Author Topic: Outpost 2 Coding 101: Week 8  (Read 8720 times)

Offline Sirbomber

  • Hero Member
  • *****
  • Posts: 3238
Outpost 2 Coding 101: Week 8
« on: March 16, 2010, 10:20:55 AM »
Week 8
Your First Land Rush - Thwarting Dynamix-Style Bases For One Tenth of a Century

Last One Standing is great and all, and Space Race can be kinda cool, but everyone loves Land Rush!

How hard can it be, right?  Since we're just setting up vehicles for players, it'll be easier than if we had to do bases!  Right?

Wrong.  This is gonna be a lot tougher than you think.  Think of some of the implications of a land rush:
-Since players can build literally anywhere, you need to provide them with plenty of suitable base locations.  I'd recommend at least 2~3 times as many base sites as the max number of players.
-Likewise, you may want more start locations than there are players, depending on your needs.
-You'll have to do some fancy stuff with the mission objectives to make them work right since nobody starts with a Command Center.

Unit Setup
Here's the standard Land Rush setup:
-5 ConVecs
-3 Cargo Trucks
-1 Earthworker
-1 Robo-Surveyor
-1 Robo-Miner
-1 Command Center Kit
-1 Structure Factory Kit
-1 Common Ore Smelter Kit
-1 Tokamak Kit
-1 Agridome Kit
-2000 Common Metals (in cargo trucks)
-1000 Food (in cargo trucks)
-? Initial Units with colony-dependent weapon (Laser or Microwave)

Note that some maps (Fractures, Flood Plain, others?) use this setup instead (for High Resources only):
-7 ConVecs
-4 Cargo Trucks
-1 Earthworker
-1 Robo-Surveyor
-1 Robo-Miner
-1 Command Center Kit
-1 Structure Factory Kit
-1 Common Ore Smelter Kit
-1 Tokamak Kit
-1 Agridome Kit
-1 Standard Lab Kit
-1 Vehicle Factory Kit
-3000 Common Metals (in cargo trucks)
-1000 Food (in cargo trucks)
-? Initial Units with colony-dependent weapon (Laser or Microwave)

Starting populations:
High:
24 Children
30 Workers
16 Scientists

Medium:
21 Children
26 Workers
13 Scientists

Low:
22 Children
22 Workers
10 Scientists

Starting techs:
High:
Health Maintenance
Offspring Enhancement
Research Training Programs
Cybernetic Teleoperation

Med:
Health Maintenance
Offspring Enhancement
Research Training Programs

Low:
Health Maintenance

Now that we've got that stuff out of the way, let's move on to actually coding this thing.

Before we go on, though, I should probably mention I've never actually made a Land Rush myself, so this will be a learning experience for all of us!  Exciting, don't you think?
 
"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 8
« Reply #1 on: March 16, 2010, 10:26:06 AM »
As usual, start with a blank template project.  We don't want old code to interfere with our Land Rush stuff, after all.

Take care of basic setup stuff.  Pick a nice map, setup code to handle morale, day/night, and disaster settings.  You can setup player resources yourself, but I'll provide the code for that later if you want to use mine instead.

Now let's get to the special Land Rush-specific stuff.  Do you remember in Week 5 when I told you about random bases, but told you they were optional?  Well now they aren't.  So go review the Randomized Bases code in Week 5 if you need to.

Done?  Good.  Go pick out a few nice-looking start locations.  Come back when you're ready.  Remember, you only need one for each player, but it'd be pretty cool if you had extra start locations to make each game a little different.

So let's do that.  This code is pretty ugly, but it gets the job done.  I'm sure somebody could write something much better though.
Note: You only need this if you have more start locations than there are players; otherwise the old randomized start location code works just fine (better, probably).

Warning: This code sucks.
Code: [Select]
// Global Variables
// Declare two arrays to hold X/Y tile info for each player's start location
// For those who don't know, arrays are like "super variables" that can hold more than one variable at a time
short plStartX[6] = {0};    // Keep track of player Z's X-tile start location
short plStartY[6] = {0};    // Keep track of player Z's Y-tile start location


// --------------------------------------------------------
// Randomly assign start locations to the players
// Note that there's probably a better way to do this, but I don't care.
void RandomizeStartLocations()
{
    // Randomize the list of all possible start locations
    int allStarts[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    RandomizeList(autosize(allStarts) );

    // Go through each active player
    for (short s = 0; s < TethysGame::NoPlayers(); s++)
    {
        // Assign them their corresponding start location from the list
        switch (allStarts[s])
        {
            case 0:
                plStartX[s] = 48 + 31;
                plStartY[s] = 43 -  1;
                break;

            case 1:
                plStartX[s] = 80 + 31;
                plStartY[s] = 68 -  1;
                break;

            case 2:
                plStartX[s] = 56 + 31;
                plStartY[s] = 97 -  1;
                break;

            case 3:
                plStartX[s] = 95  + 31;
                plStartY[s] = 120 -  1;
                break;

            case 4:
                plStartX[s] = 64  + 31;
                plStartY[s] = 167 -  1;
                break;

            case 5:
                plStartX[s] = 79  + 31;
                plStartY[s] = 202 -  1;
                break;

            case 6:
                plStartX[s] = 14  + 31;
                plStartY[s] = 205 -  1;
                break;

            case 7:
                plStartX[s] = 69  + 31;
                plStartY[s] = 234 -  1;
                break;

            case 8:
                plStartX[s] = 113 + 31;
                plStartY[s] = 152 -  1;
                break;

            case 9:
                plStartX[s] = 69 + 31;
                plStartY[s] = 14 -  1;
                break;

            // We shouldn't get to the default case, but better safe than sorry.
            default:
                plStartX[s] = 6 + 31;
                plStartY[s] = 111 -  1;
                break;
        }

    }
}

// Create player objects
void SetupPlayerLandRush(short s)
{
        // Center view on start location
        Player[s].CenterViewOn(plStartX[s], plStartY[s]);

        Unit Unit1;

        // High Resources setup
        if (Player[s].Difficulty() == 0)
        {
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s],   plStartY[s]  ), s, mapCommandCenter,    6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s]+1, plStartY[s]  ), s, mapStructureFactory, 6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s]+2, plStartY[s]  ), s, mapCommonOreSmelter, 6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s]+3, plStartY[s]  ), s, mapTokamak,          6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s]+4, plStartY[s]  ), s, mapAgridome,         6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s],   plStartY[s]+1), s, mapStandardLab,      6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s]+1, plStartY[s]+1), s, mapVehicleFactory,   6);
                Unit1.DoSetLights(1);

            TethysGame::CreateUnit(Unit1, mapCargoTruck, LOCATION(plStartX[s],   plStartY[s]+2), s, mapNone, 6);
                Unit1.DoSetLights(1);
                Unit1.SetTruckCargo(truckCommonMetal, 1000);
            TethysGame::CreateUnit(Unit1, mapCargoTruck, LOCATION(plStartX[s]+1, plStartY[s]+2), s, mapNone, 6);
                Unit1.DoSetLights(1);
                Unit1.SetTruckCargo(truckCommonMetal, 1000);
            TethysGame::CreateUnit(Unit1, mapCargoTruck, LOCATION(plStartX[s]+2, plStartY[s]+2), s, mapNone, 6);
                Unit1.DoSetLights(1);
                Unit1.SetTruckCargo(truckCommonMetal, 1000);
            TethysGame::CreateUnit(Unit1, mapCargoTruck, LOCATION(plStartX[s]+3, plStartY[s]+2), s, mapNone, 6);
                Unit1.DoSetLights(1);
                Unit1.SetTruckCargo(truckFood, 1000);

            TethysGame::CreateUnit(Unit1, mapRoboMiner,    LOCATION(plStartX[s]+2, plStartY[s]+1), s, mapNone, 6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapRoboSurveyor, LOCATION(plStartX[s]+3, plStartY[s]+1), s, mapNone, 6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapEarthworker,  LOCATION(plStartX[s]+4, plStartY[s]+1), s, mapNone, 6);
                Unit1.DoSetLights(1);

            // Sloppy Initial Vehicles Code
            map_id Weapon1 = mapMicrowave;
            if (Player[s].IsEden() )
            {
                Weapon1 = mapLaser;
            }

            switch (TethysGame::InitialUnits() )
            {
                case 12:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+4, plStartY[s]+2), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 11:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s],   plStartY[s]+3), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 10:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+1, plStartY[s]+3), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case  9:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+2, plStartY[s]+3), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 8:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+3, plStartY[s]+3), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 7:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+4, plStartY[s]+3), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 6:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s],   plStartY[s]+4), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 5:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+1, plStartY[s]+4), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 4:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+2, plStartY[s]+4), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 3:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+3, plStartY[s]+4), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 2:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+4, plStartY[s]+4), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 1:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s],   plStartY[s]+5), s, Weapon1, 6);
                        Unit1.DoSetLights(1);

                case 0:
                default:
                    break;
            } // Ugh, I'm ashamed that I actually wrote that...  Too tired to code.

        } // end huge if statement

        // Med/Low Res Setup
        else
        {
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s],   plStartY[s]  ), s, mapCommandCenter,    6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s]+1, plStartY[s]  ), s, mapStructureFactory, 6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s]+2, plStartY[s]  ), s, mapCommonOreSmelter, 6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s]+3, plStartY[s]  ), s, mapTokamak,          6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapConVec, LOCATION(plStartX[s]+4, plStartY[s]  ), s, mapAgridome,         6);
                Unit1.DoSetLights(1);

            TethysGame::CreateUnit(Unit1, mapCargoTruck, LOCATION(plStartX[s],   plStartY[s]+1), s, mapNone, 6);
                Unit1.DoSetLights(1);
                Unit1.SetTruckCargo(truckCommonMetal, 1000);
            TethysGame::CreateUnit(Unit1, mapCargoTruck, LOCATION(plStartX[s]+1, plStartY[s]+1), s, mapNone, 6);
                Unit1.DoSetLights(1);
                Unit1.SetTruckCargo(truckCommonMetal, 1000);
            TethysGame::CreateUnit(Unit1, mapCargoTruck, LOCATION(plStartX[s]+2, plStartY[s]+1), s, mapNone, 6);
                Unit1.DoSetLights(1);
                Unit1.SetTruckCargo(truckFood, 1000);

            TethysGame::CreateUnit(Unit1, mapRoboMiner,    LOCATION(plStartX[s]+3, plStartY[s]+1), s, mapNone, 6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapRoboSurveyor, LOCATION(plStartX[s]+4, plStartY[s]+1), s, mapNone, 6);
                Unit1.DoSetLights(1);
            TethysGame::CreateUnit(Unit1, mapEarthworker,  LOCATION(plStartX[s],   plStartY[s]+2), s, mapNone, 6);
                Unit1.DoSetLights(1);

            // Sloppy Initial Vehicles Code
            map_id Weapon1 = mapMicrowave;
            if (Player[s].IsEden() )
            {
                Weapon1 = mapLaser;
            }

            switch (TethysGame::InitialUnits() )
            {
                case 12:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+1, plStartY[s]+2), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 11:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+2, plStartY[s]+2), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 10:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+3, plStartY[s]+2), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case  9:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+4, plStartY[s]+2), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 8:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s],   plStartY[s]+3), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 7:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+1, plStartY[s]+3), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 6:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+2, plStartY[s]+3), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 5:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+3, plStartY[s]+3), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 4:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+4, plStartY[s]+3), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 3:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s],   plStartY[s]+4), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 2:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+1, plStartY[s]+4), s, Weapon1, 6);
                        Unit1.DoSetLights(1);
                case 1:
                    TethysGame::CreateUnit(Unit1, mapLynx, LOCATION(plStartX[s]+2, plStartY[s]+4), s, Weapon1, 6);
                        Unit1.DoSetLights(1);

                case 0:
                default:
                    break;
            } // Still ashamed I wrote that and then copy/pasted it.

        } // end huge else


} // end SetupPlayerLandRush

// --------------------------------------------------------

int InitProc()
{
    // ...

    // Determine player start locations
    RandomizeStartLocations();

    // Setup player units
    for (int i = 0; i < TethysGame::NoPlayers(); i++)
    {
        SetupPlayerLandRush(i);
    }

    // ...
}

Basically, what this does is randomize a list that corresponds to some possible start locations.  Each player is then assigned one of these values, and their units are created at the location.  The first unit is created at and the player's view focused on the start location.  The second unit is created at (sLoc x+1), (sLoc y); the third at (sLoc x+2), (sLoc y); ...

Time to modify our morale code!  In Dynamix's Land Rush games, even if you set Morale to Unsteady, it's steadied at "Fair" (50) until all players have completed their bases.  How generous!  Let's do the same.

Code: [Select]
	TethysGame::ForceMoraleOK(-1);            // Temporarily set morale to Fair (50) if MS is off
if (TethysGame::UsesMorale() == 1)
{
     TethysGame::ForceMoraleGood(-1);        // Lock morale at 75 for all players if Morale Steady on
}

In a callback function that checks if all players have built their bases or dropped out of the game, we'll do this:
Code: [Select]
SCRIPT_API void SomeTriggerCallbackFunction()
{
    // If Morale is supposed to be unsteady, disable steady morale
    if (TethysGame::UsesMorale() )
    {
        TethysGame::FreeMoraleLevel(-1);
    }

This presents our first big problem: How are we going to tell when everyone is ready?  We can't just do a "has CC" function; what if a player quits, or all of his/her/its vehicles get destroyed?  Do we use a Set Trigger?

Nope!  Rememeber AIProc?  We're using that.  In case you forgot or skipped the lesson, AIProc is a function OP2 calls every 4 ticks.  If you stick something in AIProc, OP2 will run it.  If used properly, it gives you a lot more control over the game than you would otherwise have.  So let's get to it!

Code: [Select]
void AIProc()
{
    // Don't enter this loop if we're already ready
    if (DefaultLOS.IsEnabled() == false)
    {
        // Keep track of how many players are ready
        short numReady = 0;

        // Check each active player for active CC/no units left
        for (short i = 0; i < TethysGame::NoPlayers(); i++)
        {
            // Create a vehicle enumerator so we can check if the player has at least one unit
            Unit tempUnit;
            PlayerVehicleEnum countUnits(i);

            // If player has a CC or has no units left, mark him/her as ready
            if ( (Player[i].hasActiveCommand() ) || !(countUnits.GetNext(tempUnit) ) )
            {
                // Add this player to the "total ready" count
                numReady++;
            } // end if player ready

        } // end for all players

        // Now check if all players are ready
        if (numReady == TethysGame::NoPlayers() )
        {
            // Create a time trigger that will fire in 1 tick
            CreateTimeTrigger(1, 1, 1, "PlayersReady");
        } // end if all ready

    } // end if trigger not already enabled

} // end AIProc

I think the code is already fairly well documented, but I'll explain it anyways.  In InitProc, we've already created a (disabled) victory condition and assigned it to a trigger variable named DefaultLOS so we can keep track of it.  Before we do anything else in AIProc, we check if this trigger has already been enabled.  If it has, we skip the rest of the code in AIProc as we don't need to do anything else here.  We then go through each player and create a Vehicle Enumerator.  Enumerators are usually used to go through each of a specific type of a player's unit, but here we just need to check for a single vehicle of any type.  More on enumerators later.  Note that we've only created the enumerator, but have yet to do anything with it.

In the next line (the if statement), we're checking if the current player has an active Command Center OR if the current player DOES NOT have at least one unit.  Let's look at the second condition since it might be a bit difficult to read.
Code: [Select]
!(countUnits.GetNext(tempUnit) ) 
We're telling our unit enumerator (countUnits) to try and find (GetNext) a unit (tempUnit) for the current player.  The ! in front of it all inverts it, so the if statement becomes true only if a vehicle is not found.

Notice that the trigger calls the "PlayersReady" trigger we created earlier to deal with unsteadying morale.  Here's the complete version of that function:
Code: [Select]
SCRIPT_API void PlayersReady()
{
    // If Morale is supposed to be unsteady, disable steady morale
    if (TethysGame::UsesMorale() )
    {
        TethysGame::FreeMoraleLevel(-1);
    }

    // Enable our victory condition
    DefaultLOS.Enable();

    // Play "new mission objective" notification
    TethysGame::AddMessage(-1, -1, "New objective.", 0, sndSavant30);
} // end PlayersReady

Okay, I think that should be it!  It's a week and a few days late, but things have been rough lately.  To make it up for you, I've attached the code I used to make this.  Take care everyone and see you next time!

Next week: Intro to AI
"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 8
« Reply #2 on: March 20, 2010, 08:53:29 PM »
Just a minor point, but you're using "short" variables in a few places where either a char or an int might be better. The reason is perhaps a bit subtle, so let me explain. Why Shorts Aren't Typically Used.


Also, your case statement is highly regular. It looks like a copy/paste job, with a few numbers changed. Usually that means you can collapse it down to a table lookup. In fact, I see some switch/case statements in there that very closely follow the ideal pattern. Converting Switch Statements To Table Lookup.
« Last Edit: March 20, 2010, 08:53:53 PM by Hooman »

Offline Sirbomber

  • Hero Member
  • *****
  • Posts: 3238
Outpost 2 Coding 101: Week 8
« Reply #3 on: March 20, 2010, 09:45:13 PM »
Last I heard, int is deprecated (or is in the process of being/will soon be deprecated?).  Dunno how much truth is in this statement, though.
"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 8
« Reply #4 on: March 20, 2010, 10:17:34 PM »
Hmm, that's news to me. If you don't like int, use long.

Considering how much code uses int, I can't imagine compilers dropping it anytime soon.
 

Offline Sirbomber

  • Hero Member
  • *****
  • Posts: 3238
Outpost 2 Coding 101: Week 8
« Reply #5 on: March 21, 2010, 12:05:12 AM »
Well, it's not "deprecated" so much as "the compiler arbitrarily decides to treat all ints as shorts or longs instead".  Then again, this was "heard from a friend, who heard it from a friend, who ..." so yeah.

Edit: Anyways, this isn't really relevant to the tutorial...
« Last Edit: March 21, 2010, 12:05:29 AM by Sirbomber »
"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 8
« Reply #6 on: March 21, 2010, 07:44:55 PM »
Ahh yes. The int data type is of an unspecified size, which is platform dependent. On a 32-bit machine, it will be at least 32 bits. On a 64 bit machine, it might be 64 bits. I'm not so sure about going smaller to 16 bits though. I believe the idea was to have it follow that native machine register size, with a guarantee of some minimum size.

But yeah, off topic. Although, the switch case code might be easier to understand if it was written in a more compact form.
 

Offline Sirbomber

  • Hero Member
  • *****
  • Posts: 3238
Outpost 2 Coding 101: Week 8
« Reply #7 on: March 21, 2010, 07:53:22 PM »
Oh, I don't disagree with you about that.  If you look, you'll notice I actually apologize for it on several occasions.  Ideally, I'd be able to do something entirely different that doesn't involve switch statements at all, but I don't know how...
"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

Fire Plague

  • Guest
Re: Outpost 2 Coding 101: Week 8
« Reply #8 on: December 22, 2015, 03:18:43 AM »
so I was reviewing this code, trying to understand it better
Quote
void AIProc()
{
    // Don't enter this loop if we're already ready
    if (DefaultLOS.IsEnabled() == false)
    {
        // Keep track of how many players are ready
        short numReady = 0;

        // Check each active player for active CC/no units left
        for (short i = 0; i < TethysGame::NoPlayers(); i++)
        {
            // Create a vehicle enumerator so we can check if the player has at least one unit
            Unit tempUnit;
            PlayerVehicleEnum countUnits(i);

            // If player has a CC or has no units left, mark him/her as ready
            if ( (Player.hasActiveCommand() ) || !(countUnits.GetNext(tempUnit) ) )
            {
                // Add this player to the "total ready" count
                numReady++;
            } // end if player ready

        } // end for all players

        // Now check if all players are ready
        if (numReady == TethysGame::NoPlayers() )
        {
            // Create a time trigger that will fire in 1 tick
            CreateTimeTrigger(1, 1, 1, "PlayersReady");
        } // end if all ready

    } // end if trigger not already enabled

} // end AIProc
and I notice that PlayerVehicleEnum countUnits(i); should create a trainload of them being in AIProc. Yet, the only thing it seems to be doing
Quote
if ( (Player.hasActiveCommand() ) || !(countUnits.GetNext(tempUnit) ) )
is returning a variable or constant to control an if statement, (therefore returning only the last Unit that is scanned in it's own internal processes as well, I think, by other things that I've read)

All I am trying to accomplish is some way to "Tab" through the list of units and repair the damaged critical buildings without the use of IUnit class. (which I don't have access to anyway)

The plan was to set up a time trigger that fired every ten ticks and repaired 1 health per execution until the specified structures were in full health. Sound possible?

Offline Sirbomber

  • Hero Member
  • *****
  • Posts: 3238
Re: Outpost 2 Coding 101: Week 8
« Reply #9 on: December 22, 2015, 04:04:55 PM »
Enumerators go through each unit, one at a time, through the use of the GetNext method.  The key here is one at a time.  If you want to get every unit, you need to call this in a while loop.  I'll leave it up to you to figure out why it's only necessary for me to call it once in this sample code.
"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