Ok, good news for people looking to add some AI in future levels. I've run a few successful experiments with getting the computer to build/rebuild a base. It is also possible to get your own vehicles to automatically build your own base. I might add that this makes testing a little nicer since you can see what's going on.
BuildingGroup
----------------
- The building group must contain the ConVecs used to build the buildings. They can be added using BuildingGroup.TakeUnit(unit) or one of the other methods to add units to a group.
- A Structure Factory can also be added to a building group if you want/need the kits to be built first. This can also be added using TakeUnit. If the ConVecs added to the group do not have the kits carried as cargo then this is the way it needs to be done.
- Construction of one building will not start until the construction of the last building has already started. So if you have two ConVecs already loaded with structure kits, one will move to where the building needs to be built and start building. Once it has started building, the other ConVec will then move to its build site and start building. The ConVecs will not move to the build site until the previous building has started construction. If the structure factory needs to build a kit, it will not start building the kit until the building before that kit has started construction.
- When a ConVec has nothing to do, (sometimes the initial startup for a few seconds, when waiting for a structure kit to finish building, or the list of buildings is complete) it will move to a predefined rectangle and wait. This rectangle is set using BuildingGroup.SetRect(MAP_RECT);
- The MAP_RECT set above should probably be set out front of the structure factory. It's very important to set this or the default is (0,0,0,0) so vehicles will move to the top left of the map otherwise. The rectangle should be at least 2x2 or the vehicles will move about continuously and never come to a full stop.
- To specify the list of buildings to be built, and their location, use BuildingGroup.RecordBuilding(LOCATION, buildingType, weaponType /*for turrets*/);
- The order you specify the buildings with RecordBuilding matters. They will be built in the order they are added to the group.
- If a building is dismantled/destroyed, it will automatically be rebuilt, in the order they were originally listed.
- If you use BuildingGroup.SetDeleteWhenEmpty(1); then the ConVecs will self destruct after they are done building (eventually). Watch out as this will damage nearby buildings.
- This WILL build buildings even if you have NOT done the research yet! Although, for a vehicle factory, you won't be able to build anything at it until you do the research.
Finally, here is come sample code:
void BaseLayout(int player)
{
Unit u1, u2, u3, u4, u5, u6;
TethysGame::CreateUnit(u1, map_Command_Center, LOCATION(62, 205), player, map_Default, 0);
TethysGame::CreateUnit(u2, map_Structure_Factory, LOCATION(58, 205), player, map_Default, 0);
TethysGame::CreateUnit(u3, map_Common_Ore_Smelter, LOCATION(53, 205), player, map_Default, 0);
TethysGame::CreateUnit(u4, map_Tokamak, LOCATION(60, 197), player, map_Default, 0);
TethysGame::CreateUnit(u5, map_ConVec, LOCATION(60, 207), player, map_Agridome, 0);
TethysGame::CreateUnit(u6, map_ConVec, LOCATION(60, 207), player, map_Standard_Lab, 0);
BuildingGroup &bldGrp = CreateBuildingGroup(Player[player]);
bldGrp.TakeUnit(u2);
bldGrp.TakeUnit(u5);
bldGrp.TakeUnit(u6);
bldGrp.RecordBuilding(LOCATION(62, 202), map_Agridome, map_Default);
bldGrp.RecordBuilding(LOCATION(62, 208), map_Standard_Lab, map_Default);
bldGrp.RecordBuilding(LOCATION(48, 205), map_Vehicle_Factory, map_Default);
bldGrp.SetRect(MAP_RECT(58, 207, 59, 208));
bldGrp.SetDeleteWhenEmpty(1);
}
Next up, FightGroup and Pinwheel. After that we can probably do most things the computer is able to do in the regular DLLs. If someone wants to spend enough time with this, we may be able to make a comp stomp mission soon.
GetSaveRegions
-------------------
While chatting on IRC, Hacker asked if I knew what this did so we both looked into it. I think we probably know what it does now. Here's the conclusion I've come to.
When your DLL is loaded, Outpost2.exe calls GetSaveRegions (before InitProc) and passes it what appears to be a pointer to a struct that describes a buffer. The struct appears to have two fields, a pointer to the start of a buffer and the length of the buffer. I haven't yet got code to work on this but I suspect it should look something like this:
struct BufferDesc
#pragma pack(1)
struct BufferDesc
{
void *bufferStart;
int length;
};
#pragma pack()
SCRIPT_API void GetSaveRegions(struct BufferDesc *buffDesc)
{
buffDesc->bufferStart = &DLLGlobalVars;
buffDesc->length = sizeof(DLLGlobalVars);
}
The #pragma might not be necessary but I've had the C++ compiler insert extra space for alignment into structures before and really mess things up. Note that the above code doesn't quite work. For some reason, when it returns it seems to pop 4 bytes off the stack thus throwing away the return address and trying to return to the buffDesc in the code section. Needless to say, this isn't good. It might be the calling convection, I'll look into it tomorrow.
Also, note that you only get one buffer and it's length is fixed. So all your data must be stored contiguously in memory. Although the C++ compiler tends to keep variables in memory in the order in which they are declared, this is not part of the C++ specification for it's probably best to store the variables in a struct. Also, this is only really useful for variables that are global to the DLL (which is exactly what is needed).
Oh, and when tested on a regular DLL, the save game files do contain the length of the buffer followed by the buffer contents.
Mines.txt
----------
I've posted this else where but I thought I'd keep this info in the programming thread.
YIELD: LOW (1 bar), MED (2 bar), HIGH (3 bar) refers to the "bar" of the mine
VARIANT: Each type of mine has 3 variants which have characteristic Initial, Peak and Min (long run) yields. They also specify how fast the yield moves between these values.
INITIAL_YIELD_%, PEAK_YIELD_%, and MIN_YIELD_% specify the percentage of ore (out of 1000) that each cargo truck carries for the mine's initial, peak, and min yields. The actual yield varies linearly between these values with each load
PEAK_TRUCK, and MIN_TRUCK specify how fast the yield of the mine changes when moving to the corresponding yield. It specifies the number of truck loads to move from one to the next. So for:
High 1 50 14 60 50 30
we have a 3 bar mine, this is the first variant of it, the initial ore yield is 500, the yield increases linearly over a period of 14 truck loads from 500 to a peak of 600, and then it decreases linearly over a period of 50 truck loads from 600 down to 300 at which it remains stable.
The last parameter in the function CreateBeacon specifies the variant. Note that the mines.txt files specifies the variants as 1,2,3 but the function uses values 0, 1, 2. (If a greater value is passes to the function, it seems to use it modulo 3).
static int __fastcall CreateBeacon(map_id beaconType, int x, int y, int type, int yield, int variant);