1. Now... I wanted a 2nd player (AI) to start (just start for now, he doesn't have to do anything) but not a single structure/unit shows up...I'll answer the questions in a slightly different order (the simple ones first):
2. I would like to know how I can use the _player functions, to set certain things like starting population, morale and maybe the color too.
3. Also, I would need to know how to have cargo trucks start with metals and food in them.
4. How to use daylight stuff?
TethysGame::SetDaylightEverywhere(0);
TethysGame::SetDaylightMoves(1);
Player[0].SetWorkers(30); // give player 1 30 workers
Player[1].SetKids(200); // give player 2 200 kids
TethysGame::ForceMoraleGood(1); // this will lock Player #2 to "Good" morale (75)
TethysGame::FreeMoraleLevel(playerNum);
SDescBlock DescBlock = { Colony, 2, 12, 0 }; // Important level details
Player[1].GoEden(); // for eden
Player[1].GoPlymouth(); // for plymouth
Player[1].GoAI();
Unit truck;
TethysGame::CreateUnit(truck, (map_id)1, LOCATION(125+31, 108-1), 0, (map_id)0, 4);
truck.SetTruckCargo(truckFood, 0); // to give it a cargo of food for example
You might also want to consider using the named unit types in the map_id enum. (Check "MapIdEnum.h"). Instead of using "(map_id)1", you can use "mapCargoTruck". Same with most of your other uses of the (map_id) cast.It looks like code that was generated by the mapper -- the mapper doesn't have all the names for each of the enum values written it (it just outputs numbers). IIRC the cast was needed for certain things (for example specifying cargo in a cargo truck, you have to use values from the Truck_Cargo enum). I believe in some cases warnings would appear (by default, some MS compiler option I'm sure) as well if you used integer literals instead of the enum symbols which is why the casts are used.
The only one that really requires the cast, is for the tiger speed modification in the wreckage. If you're using the default tech tree, then you can use the TechID enum found in "OP2Helper\EnumTechId.h". (You're interested in the "techTigerSpeedModification" one). If you're not using the default tech tree then you're pretty much stuck with the way you currently have it, or making your own enum.
I'm sort of wondering if you're using some really old template code because of that actually. Or if you just happened to know enough C/C++ to do that to make the compiler stop complaining after getting an error about it.
5. How to set lights for every unit to on?
6. When I use the unit class to set the cargo to the trucks, only one of my trucks actually get filled. What should I do?
7.I tried getting the AI to deploy a mine using some code I found in the AI programming,but it doesn't work. The Surveyor seems to survey the beacon, but the miner doesn't build... in fact, it just locks itself there like a ghost, units can get through it. How to fix this?
TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(126+31, 107-1), 0, mapCommandCenter, 4);
TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(126+31, 105-1), 0, (mapStructureFactory), 4);
TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(125+31, 106-1), 0, (mapTokamak), 4);
TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(127+31, 106-1), 0, (mapTokamak), 4);
TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(124+31, 105-1), 0, (mapCommonOreSmelter), 4);
TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(124+31, 107-1), 0, (mapAgridome), 4);
TethysGame::CreateUnit(x, (mapEarthworker), LOCATION(126+31, 103-1), 0, (map_id)0, 4);
TethysGame::CreateUnit(x, (mapRoboDozer), LOCATION(125+31, 104-1), 0, (map_id)0, 4);
TethysGame::CreateUnit(RM, (mapRoboMiner), LOCATION(124+31, 103-1), 0, (map_id)0, 4);
TethysGame::CreateUnit(RS, (mapRoboSurveyor), LOCATION(123+31, 104-1), 0, (map_id)0, 4);
TethysGame::CreateUnit(Trucko, (mapCargoTruck), LOCATION(125+31, 108-1), 0, (map_id)0, 4);
TethysGame::CreateUnit(Truckm, (mapCargoTruck), LOCATION(123+31, 108-1), 0, (map_id)0, 4);
TethysGame::CreateUnit(Truckf, (mapCargoTruck), LOCATION(123+31, 106-1), 0, (map_id)0, 4);
...
ConVec.DoSetLights(1); Truckf.DoSetLights(1); Trucko.DoSetLights(1); Truckm.DoSetLights(1); RS.DoSetLights(1); RM.DoSetLights(1);
Unit unit;
PlayerVehicleEnum vehicleEnum(0); // Player 0
while(vehicleEnum.GetNext(unit) != 0)
unit.DoSetLights(true);
TethysGame::CreateUnit(Trucko, (mapCargoTruck), LOCATION(125+31, 108-1), 0, (map_id)0, 4);
TethysGame::CreateUnit(Truckm, (mapCargoTruck), LOCATION(123+31, 108-1), 0, (map_id)0, 4);
TethysGame::CreateUnit(Truckf, (mapCargoTruck), LOCATION(123+31, 106-1), 0, (map_id)0, 4);
Truckf.SetTruckCargo(truckFood, 1000);
Trucko.SetTruckCargo(truckCommonOre, 1000);
Truckm.SetTruckCargo(truckCommonMetal, 1000);
void Setup(class Unit mine, class Unit smelter, struct MAP_RECT &smelterArea);
Unit commandCenter;
TethysGame::CreateUnit(commandCenter, mapCommandCenter, LOCATION(40, 7), 0, mapNone, 0);
Player[0].CenterViewOn(45, 10);
Player[0].SetFoodStored(10000);
Player[0].SetScientists(10);
Player[0].SetOre(9000);
Unit mine, smelter, cargoTruck1, cargoTruck2;
TethysGame::CreateBeacon(mapMiningBeacon, 50, 10, 0, 0, -1); // [(50, 10), common, 3 bar, random variant]
TethysGame::CreateUnit(mine, mapCommonOreMine, LOCATION(50, 10), 0, mapNone, 0);
TethysGame::CreateUnit(smelter, mapCommonOreSmelter, LOCATION(40, 10), 0, mapNone, 0);
TethysGame::CreateUnit(cargoTruck1, mapCargoTruck, LOCATION(47, 10), 0, mapNone, 0);
TethysGame::CreateUnit(cargoTruck2, mapCargoTruck, LOCATION(44, 11), 0, mapNone, 0);
MiningGroup &mineGroup = CreateMiningGroup(Player[0]);
//mineGroup.Setup(mine, smelter, MAP_RECT(40, 9, 42, 12));
mineGroup.Setup(mine, smelter, MAP_RECT(40, 10, 43, 13));
mineGroup.TakeUnit(cargoTruck1);
mineGroup.TakeUnit(cargoTruck2);
// Make the vent appear in 1 mark (1 mark = 100 ticks)
Trigger &timeTrigger = CreateTimeTrigger(true, true, 100, "VolcanoVent");
SCRIPT_API void VolcanoVent()
{
// Make the vent appear (just changes map tiles)
// (Appearance shortly before eruption is detectable)
GameMap::SetTile(LOCATION(165+31, 154-1), 1140);
GameMap::SetTile(LOCATION(165+31, 155-1), 1150);
// Start the eruption on it's way in 1 mark
Trigger &timeTrigger = CreateTimeTrigger(true, true, 100, "Eruption");
}
SCRIPT_API void Eruption()
{
// Mark where the lava can flow
// (do this anytime before the eruption, including from InitProc)
GameMap::SetLavaPossible(LOCATION(165+31, 156-1), true);
GameMap::SetLavaPossible(LOCATION(163+31, 157-1), true);
GameMap::SetLavaPossible(LOCATION(164+31, 157-1), true);
GameMap::SetLavaPossible(LOCATION(165+31, 157-1), true);
GameMap::SetLavaPossible(LOCATION(166+31, 157-1), true);
// Make the vent appear (just changes map tiles)
// (Appearance when eruption is detectable)
GameMap::SetTile(LOCATION(165+31, 154-1), 1147);
GameMap::SetTile(LOCATION(165+31, 155-1), 1158);
// Start the lava flowing (... in about 10 marks)
// At this point, the earliest disaster warning will sound
TethysGame::SetEruption(165+31, 156-1, 150);
}
struct LavaScanline
{
int y;
int x1;
int x2;
};
void SetLavaPath(int numStructs, struct lavaScanline)
{
for (i = 0; i < numStructs; i++)
for (j = lavaScanline.x1, j <= lavaScanline.x2; j++)
GameMap::SetLavaPossible(j, lavaScanline.y, true);
}
// Defining data (might want to check if this is even valid C++ code)
LavaScanline lavaScanline[] = {
{ 40, 50, 55 },
{ 41, 49, 56 },
{ 42, 47, 56 }.
{ 43, 46, 58 },
// ...
};
// Maybe use an expression like this to calculate the size of the array,
// and possibly a macro to output both the array size and pointer,
// much like what is used in OP2Helper for the base data
int numLines = size(lavaScanline) / sizeof(lavaScanline[0]); // sizeOfArray/sizeOfElement
// Calling the function
SetLavaPath(numLines, lavaScanline);
// 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();
}
// 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);
// 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
buildGroup.RecordVehReinforceGroup(mineGroup1, 100);
// 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
buildGroup.RecordVehReinforceGroup(mineGroup2, 100);
return 1; // return 1 if OK; 0 on failure
12. If the AI has a structure kit pre-placed in its SF storage, will a ConVec take the placed one if it has to be build using the normal RecordBuilding, or will the SF build another kit and the ConVec use the newly built kit?
13. Past attempts on avoiding the RecordBuilding function to help speed up the process of building the AI base has come to a stand-still, as I can't figure out a way to make it work. I've started using the IUnits header and have got the SF to produce a basic lab kit. But then's the problem... how do I get a next ConVec that has no cargo to the dock and load the SF cargo, whilst building a new kit in the meantime.
14. How big does the reinforce group value difference hav eto be to force the VF to build something before the other things? Using the reinforceVehicleGroup to get the VF to make stuff, it won't always make both the Cargo Trucks it has to, building an Earthworker or a Repair Vehicle in the meantime...?
if (Player[1].HasTechnology(2701)) TethysGame::AddMessage(1+31,1-1,"IT IS WORKING!",0,0);
// Note I'm using some fake/nonexistent function names in here
lab.DoResearch(int techID)
{
int availableScientists, maxAssignableScientists, numScientists;
// Get available scientists
availableScientists = Player[lab.ownerNum].GetAvailableScientists();
// Get max scientists for research
maxAssignableScientists = Technology.GetMaxScientists(techNum);
numScientists = min(availableScientists, maxAssignableScientists)
lab.Research(techID, numScientists);
}
// Global AI research order array
// Note: I STRONGLY suggest using symbolic names here,
// such as those listed in OP2Helper\EnumTechID.h,
// or something equivalent for tech files that you wrote.
// (But, here is a numeric exmaple instead)
int AIResearchList[] = {
2701,
2702,
2703,
...
};
// Trigger callback function
SCRIPT_API void AIResearchCallback()
{
if (scriptGlobal.AI.researchIndex < SizeOfResearchList)
{
lab.DoResearch(AIResearchList[scriptGlobal.AI.researchIndex]);
scriptGlobal.AI.researchIndex++;
}
}
if (Player[1].Scientists()<5) if (Player[1].HasTechnology(2701)==0) bLab.Research(2701,Player[1].Scientists()); else;
else if (Player[1].HasTechnology(2701)==0) bLab.Research(2701,5);
if (Player[1].HasTechnology(2701)==0) if (Player[1].Scientists()<5) bLab.Research(2701,Player[1].Scientists());
else bLab.Research(2701,5);
if(Rectenum.GetNext(basiclab)==1)
{
numAvailableScientists = *(int*)(0x56EF1C + 3108 * playerNum + 0xA8);
if(!bLab.IsLive())
{
IUnit lab;
InRectEnumerator Rectenumbl(MAP_RECT(51+31,30-1,55+31,34-1));
if(Rectenumbl.GetNext(lab)==1)
{
if (lab.GetType() == mapBasicLab)
{
switch(lab.GetBusy())
{
case ctMoDevelop:
case ctMoUnDevelop:
case ctMoDismantle:
case ctMoResearch:
break;
default:
bLab = lab;
Trigger &basre = CreateTimeTrigger(1,0,100,"Basre");
break;
}
}
}
}
if (!Player[0].HasTechnology(2701) && re==0) {if (nAS<5) {bLab.Research(2701,nAS); re=1;}
else {bLab.Research(2701,1); re=1;}}
void Research(short techID,short numScientists);
00472D90 >/$ 83EC 04 SUB ESP,4 ; Function: Research.GetTechNum(int techID) (returns techNum or -1)
int __declspec(naked) GetTechNum(int techID)
{
__asm
{
MOV EAX, [techID]
MOV EDX, 0x472D90
PUSH EAX
MOV ECX, 0x56C230
CALL [EDX]
RETN
}
}
int __declspec(naked) GetTechNum(int techID)
{
__asm
{
MOV EAX, [ESP + 4]
MOV EDX, 0x472D90
PUSH EAX
MOV ECX, 0x56C230
CALL EDX
RETN 4
}
}
int __declspec(naked) GetTechNum(int techID)
{
__asm
{
MOV EAX, [ESP + 4]
MOV EDX, 0x472D90
PUSH EAX
MOV ECX, 0x56C230
CALL EDX
RETN 4
}
}
BEGIN_TECH "Astronomy" 02701
CATEGORY 1
[...]
COST 400
MAX_SCIENTISTS 5
LAB 1
END_TECH
Just out of curiosity, does the function return 0 or 1 for the lowest TechID value?
I'd like to ask if there's any way the Residence demand could stop affecting morale until it's researchedI don't believe so. Not any easy way at least. I think you'd need to overwrite some code to do that.
When using Records for buildings, even if you start making a kit or have one in the SF bay (probably needs to be bay 1, I suppose) the record will use that building and not make a new one. I know I asked something about this before in this thread.Say what? Do you mean the UnitRecord in the SDK? (Exported by Outpost2.exe). Or do you mean the structs used by BaseBuilder.h? What exactly is the problem? I'm not too sure what you're saying here.
if(Rectenum.GetNext(RobS)==1)
if(Rectenum.GetNext(RobS))
IUnit x,cmine,csmelter,structf,structf2,vechiclef1,vehiclef2,Truck1,Truck2,Convec,Earthw,Robos,Robom,bLab;
Unit ConVec,Earthy,Dozer,RM,RS,Truc,Truckf;
int i,a,a1,a2,lm,lms,lq,ls,lv,re,metnum;
int nAS = *(int*)(0x56EF1C + 3108 * 0 + 0xA8);
// int nAS = *(int*)(0x56EF1C + 3108 * 1 + 0xA8);
struct ScriptGlobals
{
IUnit x,cmine,csmelter,structf,structf2,vechiclef1,vehiclef2,Truck1,Truck2,Convec,Earthw,Robos,Robom,bLab;
Unit ConVec,Earthy,Dozer,RM,RS,Truc,Truckf;
int i,a,a1,a2,lm,lms,lq,ls,lv,re,metnum;
};
// Declare instance of struct
ScriptGlobals scriptGlobals;
void __cdecl GetSaveRegions(struct BufferDesc &bufDesc)
{
bufDesc.bufferStart = &scriptGlobals; // Pointer to a buffer that needs to be saved
bufDesc.length = sizeof(scriptGlobals); // sizeof(buffer)
}
int nAS = *(int*)(0x56EF1C + 3108 * 0 + 0xA8);
for (metnum=0;metnum<=6+TethysGame::GetRand(6);metnum++)
int numMeteors = 6 + TethysGame::GetRand(7);
for (metnum=0; metnum < numMeteors; metnum++)
//Groups
MiningGroup com1;
MiningGroup com2;
MiningGroup com3;
// ...
SCRIPT_API void StLab()
SCRIPT_API void Nurseri()
Warning: Content might cause head aches, severe migraines or a possible stroke to any experienced map coder!
int __declspec(naked) __stdcall GetTechNum(int techID)
{
__asm
{
MOV EAX, [ESP + 4]
MOV EDX, 0x472D90
PUSH EAX
MOV ECX, 0x56C230
CALL EDX
RETN 4
}
}
Declaring all those variables inside the struct makes them unrecognizable for the rest of the file.Easy fix. Just reference them with structVar.memberVar.
struct ScriptGlobals
{
int something;
};
// Declare instance of struct
ScriptGlobals scriptGlobals;
scriptGlobals.something
ScriptGlobal scriptGlobal;
...
scriptGlobal.roboSurveyor.DoMove(...)
if (!scriptGlobals.bLab.GetBusy() && !Player[0].HasTechnology(3004))
{
//Trigger &BasicResearchTrigger = CreateTimeTrigger(1,1,50,"BasRe");
BasRe();
}
ScriptGlobal
{
// ...
Trigger researchTrigger;
Trigger researchTimeTrigger;
};
... (somehwere in InitProc)
scriptGlobal.researchTimeTrigger = CreateTimeTrigger(true, false, 50, "StartNextResearch");
... (new function)
SCRIPT_API void StartNextResearch()
{
int bFailed = true; // Default assumption
// Determine next research topic
int techID = ...;
int techIndex = GetTechNum(techID)
// Start the next research here ...
// If failed, abort here without adjusting further settings
// (Although, you can Destroy the research trigger if you want)
if (bFailed)
return; // Will try again in 50 ticks
// Disable the Time Trigger (It is auto reset to a 50 tick delay, that starts when it's next enabled)
scriptGlobal.researchTimeTrigger.Disable();
// Set the Research Trigger
if (scriptGlobal.researchTrigger.IsInitialized()) // Only destroy it if it's already in use
scriptGlobal.researchTrigger.Destroy();
scriptGlobal.researchTrigger = CreateResearchTrigger(true, true, techID, playerNum, "ResearchComplete");
}
SCRIPT_API void ResearchComplete()
{
// Start the next topic in 50 ticks
scriptGlobal.researchTimeTrigger.Enable();
}
int researchTopicList[] =
{
2701,
2702,
// ...
};
int edenResearchTopicList[] =
{
// ...
};
int plymouthResearchTopicList[] =
{
// ...
};
ScriptGlobal
{
// ...
Trigger researchTrigger;
Trigger researchTimeTrigger;
int researchTopicIndex;
};
// In InitProc
scriptGlobal.researchTopicIndex = 0; // Initialize to start of list
SCRIPT_API void StartNextResearch()
{
int bFailed = true; // Default assumption
// Determine next research topic
int techID = researchTopicList[scriptGlobal.researchTopicIndex];
int techIndex = GetTechNum(techID)
// Start the next research here
if (techIndex != -1)
{
// Start the research (with as many scientists as possible)
int maxScientists = GetMaxScientists(techIndex);
int numAvailableScientists = GetNumAvailableScientists(playerNum);
int numScientists = Min(maxScientists, numAvailableScientists);
scriptGlobal.lab.Research(techIndex, numScientists);
bFailed = false;
}
//... Rest of function, as above
}
SCRIPT_API void ResearchComplete()
{
// Increment the topic index
scriptGlobal.researchTopicIndex++;
// Start the next topic in 50 ticks
scriptGlobal.researchTimeTrigger.Enable();
}
int __declspec(naked) __stdcall GetMaxScientists(int techIndex)
{
__asm
{
MOV EAX, [ESP + 4]
MOV ECX, [(0x56C230+4)] // Load TechInfo*[]*
MOV EDX, [ECX + EAX * 4] // Load TechInfo* (of techIndex)
MOV EAX, [EDX + 0x14] // EAX has return value of TechInfo.maxScientists
RETN 4
}
}
int researchTopicList[] = ...
int numResearchTopics = sizeof(researchTopicList)/sizeof(researchTopicList[0]);
SCRIPT_API void StartNextResearch()
{
if (scriptGlobal.researchTopicIndex >= numResearchTopics)
{
// Shutdown trigger system. All research has been completed.
if (scriptGlobal.researchTrigger.IsInitialized()) // Need to check this in case the list was empty (and good habit)
scriptGlobal.researchTrigger.Destroy(); // Don't need this anymore
scriptGlobal.researchTimeTrigger.Destroy(); // Don't need this anymore either
return; // Don't try to do anything more
}
// ... Rest of function, as above
}
struct ScriptGlobal
{
// ...
};
ScriptGlobal scriptGlobal;
int __declspec(naked) __stdcall GetMaxScientists(int techIndex)
{
__asm
{
MOV EAX, [ESP + 4]
MOV ECX, 0x56C230+4 // Load &Research.TechInfo*[]*
MOV ECX, [ECX] // Load TechInfo*[]*
MOV EDX, [ECX + EAX * 4] // Load TechInfo* (of techIndex)
MOV EAX, [EDX + 0x14] // EAX has return value of TechInfo.maxScientists
RETN 4
}
}
MOV ECX, [0x56C234]
MOV ECX, 0x56C234
Trigger &BasicResearchTrigger = CreateTimeTrigger(1,0,60,"StartNextResearch");
Trigger &StandardResearchTrigger = CreateTimeTrigger(1,1,50,"StdRe");
scriptGlobal.researchTrigger = CreateTimeTrigger((1,0,60,"StartNextResearch");