Ok, I got some working example mine code based off of CES1.dll. What they did is setup the mining using a repeated time trigger. The time trigger would fire every 97 ticks, and would check for the presence of the right kind of smelter. Once the smelter existed, they setup the mining group and destroyed the time trigger (or reused it to trigger a different even, thus essentially destroying it for it's previous purpose).
I don't think the whole trigger setup is actually necessary, but I figure I'll post code that does it that way. Mostly because I was following how CES1.dll did it, and when I got it working the code was in that form.
Also, the current form of the code uses global objects in the DLL, which will need to be saved correctly if the user Loads/Saves the level. If you didn't setup the SaveRegions properly while using global variables, they wouldn't have their values restored when you loaded the game, and who knows what would happen then. Things might just stop working, or the game might crash. At any rate, I get to demonstrate an example of that too.
One fine point to note about variables in C++ is that the language doesn't specify the order of variables in memory in relation to the order they are declared. They usually get stuck in memory in the same order they are declared, but this is not a requirement, and there are some research projects that attempt to reorder the variables in memory for various purposes. Sometimes it's to do with speed and cache locality, and sometimes it's for security reasons. There is one way to force the ordering however. You just put the variables into a struct. That way you know exactly where all the variables go, and can easily get the size of all of them. This is important for SaveRegions since we just get to save one contiguous chunk of memory, and we'll want exactly the variables we specify to be in that chunk.
The code:
// Global Script variable layout
struct ScriptGlobal
{
BuildingGroup buildGroup;
Trigger mineGroup1TimeTrigger;
Trigger mineGroup2TimeTrigger;
Unit supernovaLynx;
};
// Global Script variables
ScriptGlobal scriptGlobal;
int InitProc()
{
// Bulldoze smelter area for faster testing
int i, j;
for (j = 4; j < 14; j++)
{
for (i = 41; i < 50; i++)
{
GameMap::SetCellType(LOCATION(i, j), 21);
GameMap::SetTile(LOCATION(i, j), 1670);
}
}
// Set initial colony status
TethysGame::ForceMoraleGreat(-1);
Player[0].MarkResearchComplete(techEfficiencyEngineeringEden);
Player[0].MarkResearchComplete(techCyberneticTeleoperation);
Player[0].GoAI();
Player[0].GoEden();
Player[0].CenterViewOn(45, 10);
Player[0].SetFoodStored(10000);
Player[0].SetWorkers(10);
Player[0].SetScientists(10);
Player[0].SetOre(10000);
// Create an initial base to support the testing
Unit commandCenter, structureFactory, vehicleFactory, tokamak, commonStorage;
TethysGame::CreateUnit(commandCenter, mapCommandCenter, LOCATION(40, 7), 0, mapNone, 0);
TethysGame::CreateUnit(structureFactory, mapStructureFactory, LOCATION(36, 7), 0, mapNone, 0);
TethysGame::CreateUnit(vehicleFactory, mapVehicleFactory, LOCATION(36, 11), 0, mapNone, 0);
TethysGame::CreateUnit(tokamak, mapTokamak, LOCATION(36, 2), 0, mapNone, 0);
TethysGame::CreateUnit(commonStorage, mapCommonStorage, LOCATION(39, 10), 0, mapNone, 0);
TethysGame::CreateUnit(commonStorage, mapCommonStorage, LOCATION(41, 10), 0, mapNone, 0);
TethysGame::CreateUnit(scriptGlobal.supernovaLynx, mapLynx, LOCATION(45, 4), 0, mapSupernova, 0);
// Create a mining beacon
TethysGame::CreateBeacon(mapMiningBeacon, 50, 10, 0, 0, -1); // [(50, 10), common, 3 bar, random variant]
// Create a building group
BuildingGroup &buildGroup = CreateBuildingGroup(Player[0]);
buildGroup.SetRect(MAP_RECT(33, 5, 39, 10));
buildGroup.TakeUnit(structureFactory);
buildGroup.TakeUnit(vehicleFactory);
buildGroup.RecordBuilding(LOCATION(45, 10), mapCommonOreSmelter, mapNone);
buildGroup.SetTargCount(mapConVec, mapNone, 1);
buildGroup.RecordBuilding(LOCATION(50, 10), mapCommonOreMine, mapNone);
buildGroup.RecordBuilding(LOCATION(45, 6), mapCommonOreSmelter, mapNone);
buildGroup.RecordVehReinforceGroup(buildGroup, 1000);
// Save building group reference in global
scriptGlobal.buildGroup = buildGroup;
// Check periodically for the smelters being built
scriptGlobal.mineGroup1TimeTrigger = CreateTimeTrigger(true, false, 97, "SetupMining1");
scriptGlobal.mineGroup2TimeTrigger = CreateTimeTrigger(true, false, 97, "SetupMining2");
return 1; // return 1 if OK; 0 on failure
}
void AIProc()
{
if ((TethysGame::Tick() > 7500) && (scriptGlobal.supernovaLynx.IsLive()))
{
scriptGlobal.supernovaLynx.DoSelfDestruct();
}
}
void __cdecl GetSaveRegions(struct BufferDesc &bufDesc)
{
bufDesc.bufferStart = &scriptGlobal;
bufDesc.length = sizeof(scriptGlobal);
}
int StatusProc()
{
return 0;
}
SCRIPT_API void NoResponseToTrigger()
{
}
SCRIPT_API void SetupMining1()
{
Unit smelter;
PlayerBuildingEnum buildingEnum(0, mapCommonOreSmelter);
LOCATION smelterLoc(45, 10);
// Check for a smelter at the desired location
for (;;)
{
if (buildingEnum.GetNext(smelter) == 0) return;
LOCATION &temp = smelter.Location();
if ((temp.x == smelterLoc.x) && (temp.y == smelterLoc.y)) break;
}
// Setup a mining group, which will be later reinforced
MiningGroup &mineGroup1 = CreateMiningGroup(Player[0]);
mineGroup1.Setup(LOCATION(49, 10), LOCATION(46, 11), mapCommonOreMine, mapCommonOreSmelter, MAP_RECT(45, 10, 49, 14));
mineGroup1.SetTargCount(mapCargoTruck, mapNone, 2);
// Make the building group reinforce the mining groups
scriptGlobal.buildGroup.RecordVehReinforceGroup(mineGroup1, 100);
// Done with the trigger, make sure we don't reenter
scriptGlobal.mineGroup1TimeTrigger.Destroy();
}
SCRIPT_API void SetupMining2()
{
Unit smelter;
PlayerBuildingEnum buildingEnum(0, mapCommonOreSmelter);
LOCATION smelterLoc(45, 6);
// Check for a smelter at the desired location
for (;;)
{
if (buildingEnum.GetNext(smelter) == 0) return;
LOCATION &temp = smelter.Location();
if ((temp.x == smelterLoc.x) && (temp.y == smelterLoc.y)) break;
}
// Setup a second mining group, which will be later reinforced
MiningGroup &mineGroup2 = CreateMiningGroup(Player[0]);
mineGroup2.Setup(LOCATION(49, 10), LOCATION(46, 7), mapCommonOreMine, mapCommonOreSmelter, MAP_RECT(45, 7, 49, 11));
mineGroup2.SetTargCount(mapCargoTruck, mapNone, 2);
// Make the building group reinforce the mining groups
scriptGlobal.buildGroup.RecordVehReinforceGroup(mineGroup2, 100);
// Done with the trigger, make sure we don't reenter
scriptGlobal.mineGroup2TimeTrigger.Destroy();
}
Description:
Player 0 is set to GoAI, so you can't issue any commands to the units. You start with a base consisting of a CC, SF, VF, Tok, and two Common Stoage. I also added a nova lynx that is set to self destruct at Mark 75 (see AIProc). I added a variable to the globals section to keep the reference around for the self destruct command.
I added a BuildingGroup to setup the following. It builds the kits for two Common Ore Smelters, it builts a Robo Miner, it deploys the Robo Miner, and it builds a Convec. The Convec will pick up the kits from the SF and deploy the two Common Ore Smelters.
The two time triggers setup two MiningGroups, one for each smelter, and they share a common mine. The time trigger checks periodically for the smelter to see when it's there (initialled placed), and once it detects this, it sets up the mining groups, sets a target count of 2 cargo trucks for each group, and sets the BuildingGroup to reinforce the MiningGroups, so those cargo trucks will be built as they are needed at the VF.
The second smelter should finish building at about mark 70, and everything will be harvesting. Then at mark 75, the nova lynx explodes. This should take out the top smelter, and possibly a cargo truck or two. When this happens, the SF should start to build a new Common Ore Smelter kit, and the Convec should move to the pad to await pickup, and the VF should start to replace any lost Cargo Trucks. Once the kits is ready, the Convec will deploy it, and when it's finished building the cargo trucks will resume harvesting. This all happens automatically with the groups. Basically you just set them up and forget about them. As long as the building group is capable of replacing lost buildings and groups, it will. And as long as you have a mine and smelter operational at the correct location, mining will continue.
There is one important point about the mining Setup call. The locations you specify MUST be the EXACT coordinates of the DOCKs. These will not usually be the same as the coordinates used to build them. You must pay attention to where the docks are, or the trucks won't head to their destination. The internal code for the Groups actually does an exact compare on the coordinates it's given in Setup, and the dock location (using the internal function GetDockLocation) to see if they are an exact match.
I suspect, but have not yet tested, code that calls Setup using the overloaded location form will work even if you call setup well before the building is built. It seems like it should work that way, since when the smelter is destroyed and rebuilt, mining will continue with the new smelter. If this is the case then you can really get away without using the time triggers and without using the global variables. You just have to make sure the coordinates are the exact coordinates of the dock for each building.