As I've not been active on modding for some time, I decided to share some of my research on AI scripting itself. Starting with transport plan, which modders including me have been struggling to get it to work correctly. (There is method to transport units using a fake attack plan though.)
However, after locating the hardcoded plan in the EXE which was created by attack plan with nameAttackXPort, I was able to create a working plan as shown below:
Based on my tests, cTransportPathTypeAreas doesn't seem to be working, and should probably always set cTransportPlanMaximizeXportMovement to true as it maximizes the water transport distance.
Here is a example for the Ceylon map to transport covered Wagon and scouts to the main Island before doing anything.
Added an plan state handler because transport often fails during the progress, mostly atcPlanStateEnter which the ship stays idle and if a 'bad' cTransportPlanDropOffPoint is used, garrisoned units ended up not being ejected on land.
And well, it's known that AI cannot repair buildings and use abilities, there are currently no easy hacks to create XS syscalls based on my research on the EXE.
Also, if anyone need help create XS syscalls or cheats with our Unhardcode Patch plug-in system, feel free to use this thread to ask questions.
NE Developer | Modding Tool Programmer
GR2 Importer for 3ds Max | Unhardcode Patcher | DDT Plugin for Photoshop
However, after locating the hardcoded plan in the EXE which was created by attack plan with name
//==============================================================================
// createSimpleTransportPlan
//==============================================================================
int createSimpleTransportPlan(int transportID = -1, vector gatherPoint = cInvalidVector, vector targetPoint = cInvalidVector, int pri = 100, bool economy = true, int baseID = -1)
{
int transportType = kbUnitGetProtoUnitID(transportID);
int planID = aiPlanCreate(kbGetUnitTypeName(transportType)+" Transport Plan", cPlanTransport);
aiPlanSetBaseID(planID, baseID);
// Priority.
aiPlanSetDesiredPriority(planID, pri);
// Mil vs Econ.
if (economy == true)
aiPlanSetMilitary(planID, false);
else
aiPlanSetMilitary(planID, true);
// Transport unit.
aiPlanSetVariableInt(planID, cTransportPlanTransportTypeID, 0, transportType);
aiPlanSetVariableInt(planID, cTransportPlanTransportID, 0, transportID);
// Gather point.
aiPlanSetVariableVector(planID, cTransportPlanGatherPoint, 0, gatherPoint);
// Target point.
aiPlanSetVariableVector(planID, cTransportPlanTargetPoint, 0, targetPoint);
aiPlanSetVariableBool(planID, cTransportPlanReturnWhenDone, 0, true);
aiPlanSetVariableBool(planID, cTransportPlanPersistent, 0, false);
aiPlanSetVariableBool(planID, cTransportPlanMaximizeXportMovement, 0, true);
aiPlanSetVariableInt(planID, cTransportPlanPathType, 0, cTransportPathTypePoints);
aiPlanSetVariableBool(planID, cTransportPlanTakeMoreUnits, 0, false);
aiPlanSetActive(planID);
return(planID);
}
Based on my tests, cTransportPathTypeAreas doesn't seem to be working, and should probably always set cTransportPlanMaximizeXportMovement to true as it maximizes the water transport distance.
Here is a example for the Ceylon map to transport covered Wagon and scouts to the main Island before doing anything.
//==============================================================================
// initCeylon
//
// migrate to main island before calling init.
//==============================================================================
extern vector gCeylonMainIslandClosestLocation = cInvalidVector;
extern int gCeylonTransportID = -1;
void initCeylon(void)
{
int areaGroupNumberAreas = -1;
vector myLocation = cInvalidVector;
int myAreaGroup = -1;
int area = 0;
int areaGroup = -1;
int i = 0;
int unit = getUnit(cUnitTypeCoveredWagon, cMyID, cUnitStateAlive);
areaGroupNumberAreas = xsArrayCreateInt(gAreaGroupCount, 0, "Area group number areas");
for (area = 0; <gAreaCount)
{
areaGroup = kbAreaGroupGetIDByPosition(kbAreaGetCenter(area));
xsArraySetInt(areaGroupNumberAreas, areaGroup, xsArrayGetInt(areaGroupNumberAreas, areaGroup) + 1);
}
myLocation = kbUnitGetPosition(unit);
myAreaGroup = kbAreaGroupGetIDByPosition(myLocation);
int closestArea = -1;
float closestAreaDistance = kbGetMapXSize();
for (area = 0; <gAreaCount)
{
if (kbAreaGetType(area) == cAreaTypeWater)
continue;
areaGroup = kbAreaGroupGetIDByPosition(kbAreaGetCenter(area));
if (xsArrayGetInt(areaGroupNumberAreas, areaGroup) - xsArrayGetInt(areaGroupNumberAreas, myAreaGroup) <= 10)
continue;
bool bordersWater = false;
int borderAreaCount = kbAreaGetNumberBorderAreas(area);
for (i = 0; <borderAreaCount)
{
if (kbAreaGetType(kbAreaGetBorderAreaID(area, i)) == cAreaTypeWater)
{
bordersWater = true;
break;
}
}
if (bordersWater == false)
continue;
float dist = xsVectorLength(kbAreaGetCenter(area) - myLocation);
if (dist <closestAreaDistance)
{
closestAreaDistance = dist;
closestArea = area;
}
}
gCeylonMainIslandClosestLocation = kbAreaGetCenter(closestArea);
gCeylonTransportID = getUnit(cUnitTypeypMarathanCatamaran, cMyID, cUnitStateAlive);
aiTaskUnitMove(gCeylonTransportID, gCeylonMainIslandClosestLocation);
xsEnableRule("initCeylonWaitForExplore");
}
rule initCeylonWaitForExplore
inactive
minInterval 3
{
if (kbLocationVisible(gCeylonMainIslandClosestLocation) == false)
{
aiTaskUnitMove(gCeylonTransportID, gCeylonMainIslandClosestLocation);
return;
}
int unit = getUnit(cUnitTypeCoveredWagon, cMyID, cUnitStateAlive);
vector location = kbUnitGetPosition(unit);
int baseID = kbBaseCreate(cMyID, "Transport gather base", location, 10.0);
kbBaseAddUnit(cMyID, baseID, unit);
int transportPlan = createSimpleTransportPlan(gCeylonTransportID, location, gCeylonMainIslandClosestLocation, 100, true, baseID);
aiPlanSetEventHandler(transportPlan, cPlanEventStateChange, "initCeylonTransportHandler");
int numberNeeded = kbUnitCount(cMyID, cUnitTypeAbstractWagon, cUnitStateAlive);
aiPlanAddUnitType(transportPlan, cUnitTypeAbstractWagon, numberNeeded, numberNeeded, numberNeeded);
numberNeeded = kbUnitCount(cMyID, cUnitTypeLogicalTypeScout, cUnitStateAlive);
aiPlanAddUnitType(transportPlan, cUnitTypeLogicalTypeScout, numberNeeded, numberNeeded, numberNeeded);
xsDisableSelf();
}
void initCeylonTransportHandler(int planID = -1)
{
static bool transporting = false;
switch(aiPlanGetState(planID))
{
case -1:
{
if (transporting == true)
{
// transport done.
aiTaskUnitMove(getUnit(cUnitTypeCoveredWagon, cMyID, cUnitStateAlive), kbGetMapCenter());
xsEnableRule("initRule");
xsSetRuleMinInterval("initRule", 10 + aiRandInt(10));
}
break;
}
case cPlanStateGather:
{
// hack drop-off point
vector targetPoint = aiPlanGetVariableVector(planID, cTransportPlanTargetPoint, 0);
vector centerPoint = kbGetMapCenter();
aiPlanSetVariableVector(planID, cTransportPlanDropOffPoint, 0, xsVectorSet(0.2 * xsVectorGetX(centerPoint) + 0.8 * xsVectorGetX(targetPoint), 0.0, 0.2 * xsVectorGetZ(centerPoint) + 0.8 * xsVectorGetZ(targetPoint)));
break;
}
case cPlanStateEnter:
{
xsEnableRule("initCeylonFailsafe");
break;
}
case cPlanStateGoto:
{
transporting = true;
break;
}
}
}
rule initCeylonFailsafe
inactive
minInterval 10
{
int transportPlan = aiPlanGetIDByTypeAndVariableType(cPlanTransport, cTransportPlanTransportTypeID, cUnitTypeypMarathanCatamaran);
switch(aiPlanGetState(transportPlan))
{
case -1:
{
xsDisableSelf();
break;
}
case cPlanStateEnter:
{
aiTaskUnitMove(gCeylonTransportID, aiPlanGetVariableVector(transportPlan, cTransportPlanGatherPoint, 0));
break;
}
}
}
Added an plan state handler because transport often fails during the progress, mostly at
And well, it's known that AI cannot repair buildings and use abilities, there are currently no easy hacks to create XS syscalls based on my research on the EXE.
Also, if anyone need help create XS syscalls or cheats with our Unhardcode Patch plug-in system, feel free to use this thread to ask questions.
GR2 Importer for 3ds Max | Unhardcode Patcher | DDT Plugin for Photoshop
[This message has been edited by kangcliff (edited 01-04-2018 @ 10:49 PM).]