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.
// 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.
In a callback function that checks if all players have built their bases or dropped out of the game, we'll do this:
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
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!
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.
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:
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!