Author Topic: Outpost 2 Coding 101: Week 4  (Read 8022 times)

Offline Sirbomber

  • Hero Member
  • *****
  • Posts: 3238
Outpost 2 Coding 101: Week 4
« on: February 07, 2010, 10:12:51 PM »
Week 4
More Disasters; Victory and Defeat

Warning: Volcanic eruption detected!

Do you remember last week's optional bonus lesson, where I told you how to set up the Blight?  This week we're going to look at that in more detail, and it's mandatory.  After that we'll move on to volcanoes.

Okay, let's look at our code from last week's bonus lesson:
Code: [Select]
CreateTimeTrigger(1, 1, 1000, "Blight");

SCRIPT_API void Blight()
{
// Setup Blight
GameMap::SetVirusUL(LOCATION(8+31, 19-1), 1);  // Spawn the Blight
TethysGame::SetMicrobeSpreadSpeed(60);   // Set the Blight's spread speed

// Warning Message
TethysGame::AddMessage(1248, 576, "Microbe growth detected!", -1, 152);
}

I get the feeling most people don't want the Blight to spawn at mark 10, so you may want to change that time trigger.  I usually use a Precise Time Trigger for Blight, rather than an Interval Time Trigger.  But it's your call.  We're not here to talk about triggers again though.  Let's look at what each part of the code does.
Code: [Select]
GameMap::SetVirusUL(LOCATION(x, y), spread);
This is the function that actually spawns the Blight at the designated location.  The "spread" setting is odd.  For the longest time we thought it was spread speed, but if I remember correctly it actually determines whether the Blight spreads or not.  You'll probably want it set to 1 so it spreads, though if my memory is correct if you set it to 0 repeatedly the Blight actually starts to die off.  I've never actually tried this, though, and I may be completely wrong, so I don't know why I mentioned it.
Code: [Select]
TethysGame::SetMicrobeSpreadSpeed(number);
Fairly easy to understand, right?  Actually, it's kinda complicated.  Spread speed is scaled based on the size of the map.  You'll really have to experiment with this a lot to find good settings.  The maximum value is 8131 I think.  Basically, it infects the entire map instantly, and can spread even during the night.
If you're interested, check out this post by Hooman for ultra-detailed info about how OP2 actually calculates Blight/Lava spreading.

Volcanoes

Volcanoes are a pain for a few reasons.  They require a lot of annoying setup, and if you mess them up bad things happen.  Anyways, let's switch to a map that has terrain suitable for a volcano.  How about the Plymouth Starship map?  It's "cm01.map".

Terrian Setup:
First things first, we need to prep the map itself for the volcano by setting the appropriate tiles to "lava-possible" status.  This allows lava to flow onto those tiles, and there are a couple ways to do this: either by celltype or by tile type.  I prefer setting is by tile type because it's generally "safer" (you won't accidentally set some random sand dune to lava-possible).  Let's start with that method.
Code: [Select]
// Function for Setting All "Ancient Lava Flows" to lava-possible
void SetAllLava()
{
  int x,y,tile;
  LOCATION mapsize(512,256);                         // create a point wayoff the map

  //mapsize.Clip();                                      // clip it, so we have the mapsize

  for (y=0; y<mapsize.y; ++y)                          // run through all rows
  {
    for (x=0; x<mapsize.x; ++x)                    // check every tile on each row
    {
      tile=GameMap::GetTile(LOCATION(x,y));            // get the tile on that position
      if ( ((tile>=0x1C7) && (tile<=0x1F8))            // is this one a lavarock...
        || ((tile>=0x22F) && (tile<=0x25E)) )          // ...or an edge ?
      {
        GameMap::SetLavaPossible(LOCATION(x,y),true);  // -> then set it to LavaPossible
      }
    }
  }
}

That code sets all dark gray tiles to lava-possible.  I got that code from the old wiki.  However, I'll modify it slightly to produce an alternate method that sets lava-possible status by celltype:
Code: [Select]
// Function for Setting All S1 and S2 Celltypes to lava-possible
void SetAllLava()
{
  int x,y,tile;
  LOCATION mapsize(512,256);                         // create a point wayoff the map

  //mapsize.Clip();                                      // clip it, so we have the mapsize

  for (y=0; y<mapsize.y; ++y)                          // run through all rows
  {
    for (x=0; x<mapsize.x; ++x)                    // check every tile on each row
    {
      tile=GameMap::GetCellType(LOCATION(x,y));            // get the celltype on that position
      if ( (tile==cellSlowPassible1)
|| (tile==cellSlowPassible2) )               // If celltype = S1 or S2

      {
        GameMap::SetLavaPossible(LOCATION(x,y),true);  // -> then set it to LavaPossible
      }
    }
  }
}

By the way, I don't expect you to understand most of that code, so don't worry if you don't.  Just copy/paste it into your code like so:

Code: [Select]
void SetAllLava()
{
// code for whichever method you chose here
}

int InitProc()
{
// some other code

SetAllLava();

// maybe some more code here too
}

SetAllLava is a function, just like those disasters were earlier.  However, it wasn't a function called by a trigger, so it needs to go above InitProc.  Trigger response functions can go above or below InitProc, but convention dictates you put them below InitProc and AIProc.
Anyways, we're not done yet.  There's still a few more things we need to setup: we have to get those volcano animations working, and we need to manually set the eruption point on the side of the volcano.  Let's start with those pretty animations.

Code: [Select]
// Functions for setting up volcano animations
void SouthEastFlowAni ( int x, int y)
{
GameMap::SetTile(LOCATION(x, y), 0x489);
GameMap::SetTile(LOCATION(x, y+1), 0x4A0);
GameMap::SetTile(LOCATION(x+1, y+1), 0x4AB);
GameMap::SetTile(LOCATION(x+1, y), 0x494);
}
void SouthFlowAni( int x, int y)
{
GameMap::SetTile(LOCATION(x, y), 0x474);
GameMap::SetTile(LOCATION(x, y+1), 0x47E);
}

void SouthWestFlowAni ( int x, int y)
{
GameMap::SetTile(LOCATION(x+1, y), 0x453);
GameMap::SetTile(LOCATION(x, y), 0x447);
GameMap::SetTile(LOCATION(x, y+1), 0x45E);
GameMap::SetTile(LOCATION(x+1, y+1), 0x469);
}

// Functions for stopping volcano animations
void SouthEastFlowStopAni ( int x, int y)
{
GameMap::SetTile(LOCATION(x, y), 0x490);
GameMap::SetTile(LOCATION(x, y+1), 0x4A8);
GameMap::SetTile(LOCATION(x+1, y+1), 0x4B2);
GameMap::SetTile(LOCATION(x+1, y), 0x49C);
}
void SouthFlowStopAni( int x, int y)
{
GameMap::SetTile(LOCATION(x, y), 0x47B);
GameMap::SetTile(LOCATION(x, y+1), 0x486);
}

void SouthWestFlowStopAni ( int x, int y)
{
GameMap::SetTile(LOCATION(x+1, y), 0x45A);
GameMap::SetTile(LOCATION(x, y), 0x44F);
GameMap::SetTile(LOCATION(x, y+1), 0x465);
GameMap::SetTile(LOCATION(x+1, y+1), 0x470);
}

You don't need to worry about what they do specifically as long as you understand the results.  These functions are bit different from the ones we've been using as they have some arguments (I've been calling them settings/options so far) we need to fill in; specifically the X/Y position of where we want the animation.
Code: [Select]
int InitProc()
{
// Start a south-east volcano animation on the Plymouth Starship map
SouthWestFlowAni(113+31, 12-1);

// Code from previous lessons here
}

Also, we'll need to manually set some tiles on the volcano itself to be lava-possible.
Code: [Select]
int InitProc()
{
// Manually set some tiles to lava-possible
GameMap::SetLavaPossible(LOCATION(113+31, 13-1), true);
GameMap::SetLavaPossible(LOCATION(113+31, 14-1), true);
GameMap::SetLavaPossible(LOCATION(113+31, 15-1), true);
GameMap::SetLavaPossible(LOCATION(113+31, 16-1), true);

// Code from previous lessons here
}

And now, for the code to make the volcano erupt!
Code: [Select]
	TethysGame::SetEruption(113+31, 13-1, 45);	// X, Y, speed(?)
TethysGame::SetLavaSpeed(45);   // Set spread speed, just in case

That should be everything you need for a working volcano.  Try putting it all together yourself.  If you have trouble, download my solution below.

Time to move on to victory/defeat conditions.
"As usual, colonist opinion is split between those who think the plague is a good idea, and those who are dying from it." - Outpost Evening Star

Outpost 2 Coding 101 Tutorials

Offline Sirbomber

  • Hero Member
  • *****
  • Posts: 3238
Outpost 2 Coding 101: Week 4
« Reply #1 on: February 07, 2010, 10:13:18 PM »
Victory and Defeat - You Have Done Well.  Our Colony Is Doomed.

I've already shown you the standard LoS victory trigger, so let's just use that.

Code: [Select]
int InitProc()
{
// Code from previous lessons

Trigger trig;        // Create a trigger variable.  If you don't know what that means, don't worry about it.
trig = CreateOnePlayerLeftTrigger(1, 0, "NoResponseToTrigger");  // NoResponseToTrigger is a generic catch-all function that does nothing when invoked
CreateVictoryCondition(1, 0, trig, "Eliminate your opponents."); // Use that trigger to create a victory condition
}

Let's look at CreateVictoryCondition.
Code: [Select]
CreateVictoryCondition(1, 0, Trigger To Use, "Text");
The first option is enabled/disabled.  There's no point making a disabled victory condition, so this will always be 1.
The second option is "must always be zero".
Next is the trigger we want to use.  We created that trigger variable (trig) so OP2 could remember which trigger we wanted the victory condition to use.
Text is the text that gets displayed in the Mission Objectives list.

Creating a failure condition is the exact same thing.  The only difference is the Text portion isn't used by OP2 (but you still have to type something in, so just write whatever).  I usually write what conditions result in failure.

However, failure conditions aren't recommended for multiplayer maps for two reasons.
1) OP2 automatically fails all losing players as soon as another player or team achieves victory.
2) If only one player fails and the others are still in the game, the other games will think they "lost contact" with the defeated player, resulting in a jarring pause in the action as OP2 pauses in order to drop the "lost" player.

Something to keep in mind: OP2 places mission objectives on the list as they are created.  You probably won't need to worry about that in most cases, though.  Not yet anyways.

Here's another example (and I get to sneak in a description of more triggers, too!):
Code: [Select]
	Trigger trig;

        // Evacuation Module
        trig = CreateCountTrigger(1, 1, -1, mapEvacuationModule, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Evacuate 200 colonists to the starship.");

        // Food Cargo
        trig = CreateCountTrigger(1, 0, -1, mapFoodCargo, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Evacuate 10000 units of food to the starship.");

        // Rare Metals Cargo
        trig = CreateCountTrigger(1, 0, -1, mapRareMetalsCargo, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Evacuate 10000 units of Rare Metals to the starship.");

        // Common Metals Cargo
        trig = CreateCountTrigger(1, 0, -1, mapCommonMetalsCargo, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Evacuate 10000 units of Common Metals to the starship.");

        // Phoenix Module
        trig = CreateCountTrigger(1, 0, -1, mapPhoenixModule, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Launch the Phoenix Module.");

        // Orbital Package
        trig = CreateCountTrigger(1, 0, -1, mapOrbitalPackage, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Launch the Orbital Package.");

        // Stasis Systems
        trig = CreateCountTrigger(1, 0, -1, mapStasisSystems, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Launch the Stasis Systems.");

        // Sensor Package
        trig = CreateCountTrigger(1, 0, -1, mapSensorPackage, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Launch the Sensor Package.");

        // Habitat Ring
        trig = CreateCountTrigger(1, 0, -1, mapHabitatRing, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Launch the Habitat Ring.");

        // Command Module
        trig = CreateCountTrigger(1, 0, -1, mapCommandModule, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Launch the Command Module.");

        // Fueling Systems
        trig = CreateCountTrigger(1, 0, -1, mapFuelingSystems, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Launch the Fueling Systems.");

        // Fusion Drive
        trig = CreateCountTrigger(1, 0, -1, mapFusionDriveModule, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Launch the Fusion Drive Module.");

        // Ion Drive
        trig = CreateCountTrigger(1, 0, -1, mapIonDriveModule, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Launch the Ion Drive Module.");

        // Skydock
        trig = CreateCountTrigger(1, 0, -1, mapSkydock, mapNone, 1, cmpGreaterEqual, "NoResponseToTrigger");
        CreateVictoryCondition(1, 0, trig, "Place the Skydock in orbit.");

That's a lot of triggers and victory conditions!  But it's everything you need for a Starship or Space Race game.  Well, give or take a few triggers. (Space Race: Remove the Evacuation Module, Common Metals Cargo, Rare Metals Cargo, and Food Cargo triggers/conditions.)

Well, lemme just explain the Count Trigger real quick.
Code: [Select]
CreateCountTrigger(Enabled, One-Time, Players, Unit Type, Cargo Type, How Many, Comparison Type, "Trigger Function);
You kn whow enabled and one-time work by now.
Players: Which players can make this trigger fire.
Unit Type: The type of unit (or thing) we're looking for.  Can be a structure, vehicle, or starship component.
Cargo Type: Used to specify that the unit in question must also have a specific cargo.  Useful for objectives like "Have 4 Rail Gun Panthers" or "Have a Command Center kit in a ConVec".
How Many: How many you want the player to have.
Comparison Type: Do you want the player to have exactly 5 Cargo Trucks?  Maybe you want the player to have more than 7 Panthers.  Or maybe the AI must build no more than 4 Spaceports.  That's what this is for.
Code: [Select]
//Valid comparisons:
cmpEqual
cmpGreater
cmpLower
cmpLowerEqual
cmpGreaterEqual
Trigger Function: Please tell me you know what this is by now...

I think that's it for now.  Questions, comments, or results?  Post 'em!

Next Week: Your First Last One Standing
"As usual, colonist opinion is split between those who think the plague is a good idea, and those who are dying from it." - Outpost Evening Star

Outpost 2 Coding 101 Tutorials

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Outpost 2 Coding 101: Week 4
« Reply #2 on: February 08, 2010, 12:15:49 AM »
The SDK was updated slightly after the v2 release. The updated SetVirusUL is:
Code: [Select]
static void __fastcall SetVirusUL(LOCATION location, int bBlightPresent);

If the last parameter is true (1), then blight will be placed on the map. If it is false (0), then it will be removed from the map. At least that's what I seem to remember. It's been a long while.
 

Offline Sirbomber

  • Hero Member
  • *****
  • Posts: 3238
Outpost 2 Coding 101: Week 4
« Reply #3 on: February 08, 2010, 06:59:36 AM »
Thanks for clearing that up Hooman.
"As usual, colonist opinion is split between those who think the plague is a good idea, and those who are dying from it." - Outpost Evening Star

Outpost 2 Coding 101 Tutorials

Offline Angellus Mortis

  • Full Member
  • ***
  • Posts: 138
Outpost 2 Coding 101: Week 4
« Reply #4 on: February 08, 2010, 08:14:47 AM »
Thanks on the animation thing. I never quite figured out how to use it. :)

Offline jcj94

  • Sr. Member
  • ****
  • Posts: 407
    • http://techfusion-279.com
Outpost 2 Coding 101: Week 4
« Reply #5 on: March 14, 2011, 06:32:33 PM »
You forgot a closing bracket..

I know, I shouldn't nag.. But I program in Java for a robot so if i mess up.. someone could get hurt.. and/or I'm on Amtrak out of town.

Offline Savant_Ace

  • Full Member
  • ***
  • Posts: 102
    • http://www.spitfire-sa.com/
Outpost 2 Coding 101: Week 4
« Reply #6 on: September 27, 2011, 02:58:50 PM »
I'm probably missing a large component here (Namely my mind), But what identifier do I use for a population count (eg, Have 1000 colonists to victory)?

Offline Flashy

  • Sr. Member
  • ****
  • Posts: 391
Outpost 2 Coding 101: Week 4
« Reply #7 on: September 27, 2011, 04:39:51 PM »
Simple:
Trigger trig = CreateResourceTrigger(1, 0, resColonists, 1000, playerNum, cmpGreaterEqual, "TriggerCallback");
CreateVictoryCondition(1, 0, trig, "Have 1000 Colonists.");

You can look at the trigger creation functions in the sdk.
Praise the mighty light towers!!!

Fire Plague

  • Guest
Outpost 2 Coding 101: Week 4
« Reply #8 on: June 05, 2013, 01:26:07 AM »
Trigger vitoryTrig;
vitoryTrig = CreateCountTrigger(1, 0, 1, mapConVec, mapAny, 25, cmpGreaterEqual, "NoResponseToTrigger");
CreateVictoryCondition(1, 0, vitoryTrig, "Protect The Convoy");



So this looks identical to your victory trigger, yet it fires as soon as the game starts without fail. The condition hasn't had time to become true. (there is only one unit for player one at this point.) I've inverted the compare type and that didn't do anything and using a set trigger confused the .exe in oupost2.

is there only a set of triggers victory conditions will take seriosly?

why is it insta-winning?

thanks, fire plague.
« Last Edit: June 05, 2013, 01:42:13 AM by Fire Plague »

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Outpost 2 Coding 101: Week 4
« Reply #9 on: June 05, 2013, 04:02:23 AM »
Quote
Trigger vitoryTrig;
vitoryTrig = CreateCountTrigger(1, 0, 1, mapConVec, mapAny, 25, cmpGreaterEqual, "NoResponseToTrigger");
We normally Define the trigger the moment it's created. Your code creates the trigger, then leaves it hanging before being defined/assigned its value (game can still catch the victory condition between the two lines of code, if I recall correctly. Whatever value it's given before you assign its value in the next line may be good for the victory condition to trigger).


That's the only thing that comes to mind, try it (a.k.a. like this: Trigger vitoryTrig = CreateCountTrigger(1, 0, 1, mapConVec, mapAny, 25, cmpGreaterEqual, "NoResponseToTrigger"); ) and see.
« Last Edit: June 05, 2013, 04:04:42 AM by Hidiot »
"Nothing from nowhere, I'm no one at all"

Fire Plague

  • Guest
Outpost 2 Coding 101: Week 4
« Reply #10 on: June 05, 2013, 08:32:13 AM »
Well, I tried it and it did nothing to help (dosen't it run initproc() once and then cycle everything else?) It is also the only victory condition in the entire project. I guess it will have to have a "normal" victory condition for it to listen to me, or at least not that one. I'm going to have a vehicle count trigger next in hopes that will work.

EDIT: I discovered that the problem goes away if the condition applies to player ZERO.

aparently everything must apply to eithther player ZERO or all of them. If not, it will insta-win. A good thing to note in the tutorial don't you think?

Update:
while the victory conditions all fire when not applying in some way to player zero, I noticed that the SDK definitions of createVictoryCondition() stated that param 2 "/*must allways be 0*/" yet, in op2helper they are allways a value of 1. Test the posibilities under different conditions (victory condition parm two = 1, set on trigger for player 1, etc) and update again what you've found.
« Last Edit: June 06, 2013, 08:19:57 AM by Fire Plague »