warzone2100/data/multiplay/skirmish/player2.slo

4187 lines
101 KiB
Plaintext

/////////////////////////////////////////////////////////////////////
// general ai for skirmish game
/////////////////////////////////////////////////////////////////////
// Warzone2100, Pumpkin Studios,
// alex lee.98/99.
//
/////////////////////////////////////////////////////////////////////
//Tile in world units
#define TILE 128
#define NONE (-1)
// These are final rules of the lexical parser
#define R_REQUEST_HELP "help me"
#define R_REQUEST_BEACON "drop a beacon"
#define R_REPORT_SAFETY "i'm ok"
#define R_REQUEST_ALLY "ally me"
// These are our own messages - lexical parser should be able to handle them
#define M_REQUEST_HELP "help me!!"
#define M_REQUEST_BEACON "drop a beacon"
#define M_AFFIRMATIVE_OK "ok"
#define M_AFFIRMATIVE_ROGER "roger"
#define M_ANNOYED "bug off"
#define M_HELPERS_KILLED "that was all I had.."
#define M_HELP_NO_UNITS "I don't have anything"
#define MAX_PROBABILITY 100
// Base threat range in world units
#define W_BASE_THREAT_RANGE ((17 + (mapWidth + mapHeight) / 2 / 35) * TILE)
#define ALL_ALLIES -1
#define MAX_PLAYERS 8
#define BASE_DEFEND_DURATION (3 * 60)
#define BASE_DEFEND_RADIUS (15 * TILE)
// Delay before we repeat our request, in seconds
#define HELP_REQUEST_INTERVAL (3 * 60)
//in secs
#define BEACON_TIMEOUT 30
#define MAX_DROIDS 150
//range for trucks to look for more oil
#define MORE_OIL_RANGE (10 * TILE)
//don't try to build on oil if there's threat within this range
#define OIL_THREAT_RANGE (9 * TILE)
#define MAX_TRUCKS 12
#define MIN_TRUCKS 5
//Enter power saving mode when lower than this
#define LOW_POWER 250
//Target type values
#define NO_TARGET_VALUE 0
#define DROID_TARGET_VALUE 1
#define OTHER_TARGET_VALUE 2
#define DEFENSE_TARGET_VALUE 3
#define RESEARCH_TARGET_VALUE 4
#define HQ_TARGET_VALUE 5
#define OIL_TARGET_VALUE 6
#define FACTORY_TARGET_VALUE 7
#define UNLIMITED (-1)
#define AA_THREAT_RANGE (TILE * 12)
#define MAX_DEFENDERS_RADIUS (TILE * 40)
#define MAX_VTOL_DEFEND_RADIUS (TILE * 25)
// AI will remember max this number of structures
#define MAX_REBUILD_STRUCT 100
//Total number of technology branches
#define TECHS 2
public int me; // player for this instance.
public int tileExpand; // rate of exploration
public int numScouts[TECHS],maxScouts[TECHS]; // aim for...
public int numDefenders[TECHS],maxDefenders[TECHS];
public int numAttackers[TECHS],maxAttackers[TECHS];
public int numCyborgs[TECHS],maxCyborgs[TECHS];
public int branchDefault,branchVTOL,techCount[TECHS],maxVtolFacs[TECHS],maxIdleRes[TECHS],
maxVTOLs[TECHS],numVtolTargets,vtolTargetWeight[10],numRebuildStat[TECHS];
public RESEARCHSTAT tech[TECHS][30]; //technology for different research branches
public STRUCTURESTAT vtolTarget[10],rebuildStat[TECHS][2];
// structures
private int baseX,baseY,minx,miny,maxx,maxy;
public int numStructs,numIncendrys,numDefStructs,numExtraStructs[TECHS],numWallWeaps,numBaseStruct,numLightCyborgs;
public STRUCTURESTAT incendrys[8],structs[13],defStructs[26],extraStructs[TECHS][6],structChoice[5],wallWeaps[11];
public STRUCTURESTAT sensorTower,wall,cornerWall,resLab,powGen,playerHQ,lassat,factory,derrick,cybFactory,
vtolDefStruct[5],vtolPad,vtolFactory,repairFacility,uplink,baseStruct[8];
public STRUCTURESTAT powModule,facModule,resModule,vtolModule;
public int extraStruct;
// unit templates
public int numTemplates[TECHS];
public TEMPLATE tmpl[TECHS][70];
private TEMPLATE tmplChoice[5];
public TEMPLATE cybTempl[10],superCyb[4],cybMechanic,cybEngineer;
public TEMPLATE vtols[18];
public int numVtolTemplates;
public TEMPLATE sense[11];
public int numSenseTemplates;
public TEMPLATE constructor,repairUnit;
public int numRepairUnits;
//defend
private GROUP defendGroup;
private bool defendbusy;
private BASEOBJ defendObj;
public RESEARCHSTAT nexusDefence;
//build
private GROUP buildGroup;
private int buildX,buildY,buildX2,buildY2;
public FEATURESTAT oilRes;
// scout
private GROUP scoutGroup;
private int scoutX,scoutY;
private int scoutTLX,scoutTLY,scoutW,scoutH;
// attack
private GROUP attackGroup;
private BASEOBJ attackObj,allOutAttack,vtolGrAttackObj[10];
// vtols
private GROUP vtolDefendGr,vtolAttackGr[10];
// generic
private STRUCTURE structure,structure2,rebuildObj[100];
private DROID droid;
private FEATURE feature;
private BASEOBJ baseobj,baseobj2;
private int count,count2,result,result2,tempx,tempy;
private bool boolResult,boolResult2;
private bool powerSave,_DEBUG,bRunning;
private int allianceTime[8];
private int sender,x,y,beaconX[8],beaconY[8],tBeacon[8],
tLastHelpRequest,lastHelpPlayer,tHelp,tHelpTimeout,helpX,helpY;
private string message;
private int defendX,defendY,defendRadius,tDefendStart,tDefendTimeout,
defendMoveType,baseRange,curTech,numVtolAttackGroups,numAttackVtols,
numDefendVtols,rebuildStructX[MAX_REBUILD_STRUCT],rebuildStructY[MAX_REBUILD_STRUCT],countRebuildStruct;
private STRUCTURESTAT rebuildStructStat[MAX_REBUILD_STRUCT];
/////////////////////////////////////////////////////////////////////
// triggers.
#region triggers
trigger buildExpandTr (every, 600);
trigger fortifyTr (every, 1000);
trigger upgradeStructuresTr (every, 400 );
trigger conDroidsTr (every, 900); // was 1400
trigger repairDroidsTr (every, 2600);
trigger managePowerTr (every, 1500);
trigger basedetailsTr (every, 600 );
trigger buildDerrickTr (every, 80 );
trigger buildOilDefenseOrRetreatTr (every, 120 );
trigger incendryTr (every, 250 );
trigger buildPowerGeneratorsTr (every, 80 );
trigger buildBaseTr (every, 150 );
trigger finishStructsTr (every, 400 );
trigger droidBuiltTr (CALL_NEWDROID,me, ref droid,ref structure);
trigger structBuiltTr (CALL_STRUCTBUILT, me, ref droid, ref structure);
trigger droidDestroyedTr (CALL_DROID_DESTROYED, me, ref droid);
trigger structureDestroyedTr (CALL_STRUCT_DESTROYED, me, ref structure);
trigger rebuildStructureTr (every, 50);
trigger consolidateEventTr (every, 3100);
trigger factoryEventTr (every, 170 );
trigger cyborgFactoryEventTr (every, 170 );
trigger chooseScoutAreaTr (every, 200 );
trigger expandScoutAreaTr (every, 600 );
trigger scoutMainTr (every, 150 );
trigger newObjectReportTr (CALL_OBJ_SEEN, me, ref baseobj, ref baseobj2);
trigger attackStuffTr (every, 300 );
trigger allOutAttackTr (every, 2000);
trigger defendWatchTr (CALL_STRUCT_ATTACKED, me, ref structure, ref baseobj);
trigger defendReturnTr (every, 500 );
trigger doResearchTr (every, 400 );
trigger vtolDefendTr (CALL_STRUCT_ATTACKED, me, ref structure, ref baseobj);
trigger vtolStructsTr (every, 290);
trigger buildVtolsTr (every, 360);
trigger vtolAttackTr (every, 150);
trigger vtolEnablerTr (every, 700);
trigger takeoverTr (CALL_UNITTAKEOVER, ref droid);
trigger useLassatTr (every, 3000);
trigger reassignTr (CALL_PLAYERLEFT,count);
trigger formAllianceEventTr (every,170);
trigger breakAllianceEventTr (every,3000);
trigger difficultyModifierTr (every,600);
trigger humanAllianceTr (CALL_ALLIANCEOFFER,ref count, ref count2);
trigger multiMsgTr (CALL_AI_MSG, me, ref sender, ref message);
trigger beaconTr (CALL_BEACON, me, ref sender, ref x, ref y, ref message);
trigger consoleTr (CALL_CONSOLE, ref sender, ref message);
trigger watchBaseThreatTr (every, 120);
trigger manageAllyHelpTr (every, 80);
trigger everySec (every, 10);
trigger manageDefendLocationTr (every, 70);
/* Events */
event multiMsgEv;
event beaconEv;
event watchBaseThreat;
event manageAllyHelp;
event everySecEv;
event manageDefendLocationEv;
event structureDestroyed;
event rebuildStructureEv;
/* Function prototypes */
function bool haveBeacon(int _player);
function bool beaconTimeout(int _player);
function void processCommand(string _message, int _sender, bool _bBlipMessage);
function bool haveHelpers();
function bool attemptToHelp(int _playerToHelp, int _x, int _y);
function void helpPlayer(int _playerToHelp, int _helpX, int _helpY);
function bool canStopHelpingAlly();
function void stopHelpingAlly();
function bool helpingAlly();
function bool helpAllyTimeout();
function void requestHelp(int _helpX, int _helpY);
function void doRequestHelp(int _helpX, int _helpY);
function bool allyBaseAtLoc(int _ally, int _x, int _y);
function void messagePlayer(int _playerToMessage, string _message, int _probability);
function void messagePlayerAddressed(int _playerToMessage, int _playersToAddress, string _message);
function bool canSeeAllies();
function bool baseInTrouble();
function string m_affirmative();
function void defendLocation(int _defendX, int _defendY, int _tDefendTimeout, int _defendRadius, bool _bMove);
function void stopDefendingLocation();
function bool defendingLocation();
function bool defendLocationTimeout();
function bool friendlyPlayer(int _playerToCheck);
function void factoryBuildDroid(STRUCTURE _factory);
function void cybFactorBuildCyborg(STRUCTURE _factory);
function void vtolFactoryBuildVtol(STRUCTURE _factory);
function bool insideBase(int _x, int _y);
function FEATURE closestOil(int _x, int _y, bool _bAvoidThreat);
function int numAlliesInBase(bool _bVtols);
function int numEnemiesInBase(bool _bVtols);
function bool defendingOwnBase();
function int targetTypeValue(BASEOBJ _target);
function int numBitsSet(int _integer);
function int findResearch(int _searchStart, int _techTree);
function bool upgradeFactory(DROID _truck, int _maxBuilders);
function bool upgradeVtolFactory(DROID _truck, int _maxBuilders);
function bool upgradeResearch(DROID _truck, int _maxBuilders);
function bool upgradePowGen(DROID _truck, int _maxBuilders);
function void buildRearmPads();
function int numEnemyAAInRange(int _x, int _y, int _range);
function BASEOBJ chooseVtolTarget(bool bExclusiveTarget);
function int getVtolTargetWeight(BASEOBJ _target);
function bool vtolTargetAssigned(BASEOBJ _target);
function void buildTruck();
function int numBuildSameBuilding(STRUCTURESTAT _checkStat, int _x, int _y);
function void expandBase(DROID _truck);
function int totalVtols();
function bool needTank();
function void setTechBranch(int _tech);
function DROID closestIdleTruck(int _x, int _y);
function void buildOnExactLocation(DROID _truck, int _x, int _y, STRUCTURESTAT _stat);
function void rebuildStructures();
function BASEOBJ chooseVtolDefenceTarget(int _x, int _y, int _range, bool bExclusiveTarget);
function int numGroupSameOrder(GROUP _group, int _orderIndex);
function void rearrangeAttackVtols();
function int numStructBusyByType(STRUCTURESTAT _busyStructType);
function bool aiResponsibleForPlayer(int _player);
function void reassignAI();
function void shutDownAI();
function bool buildUnit(TEMPLATE _tankTemplate, STRUCTURE _factory, STRUCTURESTAT _factoryType, bool _bIdleOnly);
function STRUCTURE findIdleStructure(STRUCTURESTAT _structType, bool _bIdleOnly);
#endregion triggers
/////////////////////////////////////////////////////////////////////
// HouseKeeping
event initialisedEvent(CALL_GAMEINIT)
{
// initialise
_DEBUG = FALSE;
dbgMsgOn(me, _DEBUG);
extraStruct = 0;
numRepairUnits = 0;
allOutAttack = NULLOBJECT;
powerSave = FALSE;
tLastHelpRequest = -1; //when we requested help for the last time
lastHelpPlayer = -1; //we are not currently helping anyone
tHelp = -1; //when we started helping last time
tHelpTimeout = -1; //time when help times out
helpX = -1;
helpY = -1;
defendX = -1;
defendY = -1;
defendRadius = -1;
tDefendStart = -1;
tDefendTimeout = -1;
defendMoveType = -1; //move or scout
baseRange = 4 * TILE;
// set current research branch
setTechBranch(-1);
//setTechBranch(branchVTOL);
numVtolAttackGroups = 10;
numAttackVtols = 10; //num vtols in an attack group
numDefendVtols = 5; //num vtols in an attack group
// setup build group
//all initial droids are in buildgroup!
groupAddArea(buildGroup, me, 0, 0, (mapWidth*128), (mapHeight*128));
initGetFeature(oilRes,me,me); // use bucket = player
// note where our base is.
initIterateGroup(buildGroup); // find idle droids in build group.
droid = iterateGroup(buildGroup);
if(droid != NULLOBJECT)
{
baseX = droid.x;
baseY = droid.y;
if(aiResponsibleForPlayer(me))
{
expandBase(droid);
}
}
else
{
baseX = (128*mapWidth)/2;
baseY = (128*mapHeight)/2;
}
// defence.
defendbusy = FALSE;
// setup scouts
structure = getStructure(factory, me);
if(structure != NULLOBJECT)
{
scoutTLX = structure.x;
scoutTLY = structure.y;
}
else
{
scoutTLX = baseX;
scoutTLY = baseY;
}
scoutW = 256;
scoutH = 256;
scoutX = scoutTLX;
scoutY = scoutTLY;
// clear the alliance array...
allianceTime[0] = 0;
allianceTime[1] = 0;
allianceTime[2] = 0;
allianceTime[3] = 0;
allianceTime[4] = 0;
allianceTime[5] = 0;
allianceTime[6] = 0;
allianceTime[7] = 0;
bRunning = false;
if(aiResponsibleForPlayer(me))
{
bRunning = true;
buildTruck();
}
else
{
shutDownAI();
}
}
// decide what technology branch we will use
function void setTechBranch(int _tech)
{
local float _y2,_y1,_x2,_x1,_a,_y,_m,_rnd,_mapSize;
_mapSize = (float)((mapWidth + mapHeight) / 2);
if(_tech != -1)
{
curTech = _tech;
}
else
{
//probability to choose vtol branch for map size 90 = 0; probability for map size 200 = 45
//build a linear function: y = ((y2 - y1) / (x2 - x1)) * x + a depending on two values given (short: y = mx+a)
_x1 = 90.0; _y1 = 0.0;
_x2 = 200.0; _y2 = 45.0;
_m = ((_y2 - _y1) / (_x2 - _x1));
_a = -(_m * _x1);
//calculate probability for the current map
_y = _m * _mapSize + _a;
dbg("_m = " & _m & ", a = " & _a, me);
_rnd = (float)random(100);
if(_rnd < _y)
{
curTech = branchVTOL;
dbg("going air (" & _y & "/" & _rnd & ")", me);
}
else
{
curTech = branchDefault;
dbg("going land (" & _y & "/" & _rnd & ")", me);
}
}
}
/* returns TRUE if AI is responsible for the _player */
function bool aiResponsibleForPlayer(int _player)
{
if(not _DEBUG and ((_player == selectedPlayer) or not myResponsibility(_player)))
{
return FALSE;
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////
// initial force setup thing. plonk down a force.
event givehelp(every, 100)
{
if(multiPlayerBaseType == CAMP_WALLS)
{
// free power
addPower(1500, me);
// free droids.
/* structure = getStructure(factory, me);
if(structure != NULLOBJECT)
{
count = 0;
while(count<10)
{
tempx = baseX;
tempy = baseY;
boolResult = pickStructLocation(defStructs[0], ref tempx, ref tempy,me);
if(boolResult == TRUE)
{
droid = addDroid(tmpl[curTech][ random(6) ] , tempx, tempy, me);
}
count = count + 1;
}
} */
}
setEventTrigger(givehelp, inactive);
}
/////////////////////////////////////////////////////////////////////
// keep details about the size and postion of the ai players base
event basedetails(basedetailsTr)
{
// clear old extremities.
maxy = 0;
maxx = 0;
miny = (mapHeight*128);
minx = (mapWidth*128);
baseRange = 4 * TILE;
// now find the extremities of our vital structures.
count = 0;
while(count < numBaseStruct)
{
initEnumStruct(FALSE,baseStruct[count],me,me);
structure= enumStruct();
while(structure != NULLOBJECT)
{
if(structure.x < minx)
{
minx = structure.x;
}
if(structure.x > maxx)
{
maxx = structure.x;
}
if(structure.y < miny)
{
miny = structure.y;
}
if(structure.y > maxy)
{
maxy = structure.y;
}
result = distBetweenTwoPoints(baseX, baseY, structure.x, structure.y);
if(result > baseRange){
baseRange = result;
}
structure= enumStruct();
}
count = count + 1;
}
result = 3 * 128;
minx = minx - result;
maxx = maxx + result;
miny = miny - result;
maxy = maxy + result;
baseRange = baseRange + (4 * 128);
}
/////////////////////////////////////////////////////////////////////
// structure building rules
// build derricks on oil.
event buildDerrick(buildDerrickTr)
{
feature = getFeature(me); // find unoccupied oil resource.
if(feature != NULLOBJECT)
{
buildX = feature.x;
buildY = feature.y;
// if no more than 2 units already trying to build
initIterateGroup(buildGroup); // find all units in build group.
droid = iterateGroup(buildGroup);
count = 0;
while(droid != NULLOBJECT)
{
if((droid.orderx == buildX) and (droid.ordery == buildY))
{
count = count + 1;
}
droid = iterateGroup(buildGroup);
}
if(count < 3)
{
initIterateGroup(buildGroup); // find all units in build group.
droid = iterateGroup(buildGroup);
boolResult = FALSE; // only send 1 droid to each derrick
while( (boolResult == FALSE) and (droid != NULLOBJECT) )
{
if( (droid.order == DORDER_NONE) or (droid.order == DORDER_RTB))
{
orderDroidStatsLoc(droid, DORDER_BUILD,derrick, buildX,buildY); //build a derick
boolResult = TRUE;
}
droid = iterateGroup(buildGroup);
}
}
}
else // feature is null
{
initGetFeature(oilRes,me,me); // start again next time.
}
}
/////////////////////////////////////////////////////////////////////
// if idle and derrick in range and no defense then build defense, else ret to base .
event buildOilDefenseOrRetreat(buildOilDefenseOrRetreatTr)
{
local int _numBuilders,_maxBuilders;
_maxBuilders = 1;
// check idle.
initIterateGroup(buildGroup); // find idle droids in build group.
droid = iterateGroup(buildGroup);
while(droid != NULLOBJECT)
{
if(droid.order == DORDER_NONE)
{
// if in range of a derrick
structure = structureBuiltInRange(derrick, droid.x, droid.y, (5*128), me);
// if inside base limits then presume ok..
if( structure != NULLOBJECT)
{
if((structure.x > minx) and (structure.y > miny) and (structure.x < maxx) and (structure.y <maxy))
{
structure = NULLOBJECT;
}
}
if(structure != NULLOBJECT)
{
buildX = structure.x;
buildY = structure.y;
count = 0; // if derrick has no defense near it.
result = 0;
while(count < numDefStructs)
{
structure = structureBuiltInRange(defStructs[count], buildX, buildX,(3*128), me);
if(structure != NULLOBJECT)
{
result = result + 1; // found a defense.
}
count = count + 1;
}
// not many defenses nearby
if(result < 2)
{
count = numDefStructs - 1; //pick a struct to build..
count2 = 0;
while( (count2 < 5) and (count >= 0) )
{
if( isStructureAvailable(defStructs[count],me))
{
// don't build multiple sensors together.
if(count == 5)
{
structure = structureBuiltInRange(defStructs[count], buildX, buildY,(6*128), me);
if(structure != NULLOBJECT)
{
count = 8;
}
}
structChoice[count2] = defStructs[count];
count2 = count2 + 1;
}
count = count - 1;
}
count =0;
if(count2 > 0)
{
count = random(count2); //count = choice!
// pick a location
boolResult = pickStructLocation(structChoice[count], ref buildX, ref buildY,me);
_numBuilders = numBuildSameBuilding(NULLSTRUCTURESTAT, buildX, buildY);
if((boolResult == TRUE) and (_numBuilders < _maxBuilders))
{
// build it.
orderDroidStatsLoc(droid, DORDER_BUILD,structChoice[count], buildX,buildY);
_numBuilders++;
}
}
}
else
{
structure = structureBuiltInRange(playerHQ, droid.x, droid.y, (5*128), me);
if(structure == NULLOBJECT)
{
if(!insideBase(droid.x, droid.y))
{
orderDroid(droid,DORDER_RTB); // return to base;
}
}
}
}
else
{
structure = structureBuiltInRange(playerHQ, droid.x, droid.y, (5*128), me);
if(structure == NULLOBJECT)
{
if(!insideBase(droid.x, droid.y))
{
orderDroid(droid,DORDER_RTB); // return to base;
}
}
}
}
droid = iterateGroup(buildGroup);
}
}
/////////////////////////////////////////////////////////////////////
//mortar etc.. rules. build sensor towers and emplacements.
event incendry(incendryTr)
{
initEnumStruct(FALSE,sensorTower,me,me);
count = 0;
structure = enumStruct();
while(structure != NULLOBJECT)
{
count = count + 1;
structure = enumStruct();
}
if(count < (gameTime/4200) ) // every 7 mins
{
// if not found build a sensor tower.
// find a place to build.
buildX = 0;
buildY = 0;
initEnumStruct(FALSE,derrick,me,me);
structure= enumStruct();
while(structure != NULLOBJECT)
{
count = 0;
result = 0;
while(count < numDefStructs)
{
structure2 = structureBuiltInRange(defStructs[count], structure.x, structure.y,(4*128), me);
if(structure2 != NULLOBJECT)
{
result = result + 1;
}
count = count + 1;
}
// check for sensor nearby,
structure2 = structureBuiltInRange(sensorTower, structure.x, structure.y,(5*128), me);
if(structure2 != NULLOBJECT)
{
result = 4;
}
if(result < 3)
{
buildX = structure.x;
buildY = structure.y;
structure = NULLOBJECT;
}
else
{
structure = enumStruct();
}
}
if(buildX != 0)
{
boolResult = pickStructLocation(sensorTower, ref buildX, ref buildY,me); // pick spot.
if(boolResult == TRUE)
{
// find unit
initIterateGroup(buildGroup);
droid = iterateGroup(buildGroup);
while(droid != NULLOBJECT)
{
if(droid.order == DORDER_NONE or droid.order == DORDER_RTB)
{
orderDroidStatsLoc(droid, DORDER_BUILD,sensorTower, buildX,buildY);
droid = NULLOBJECT;
}
else
{
droid = iterateGroup(buildGroup);
}
}
}
}
}
else
{
// find a sensor tower with least incencdry structs around it..
buildX = 0;
buildY = 0;
initEnumStruct(FALSE,sensorTower,me,me);
structure= enumStruct();
count = 999;
while(structure != NULLOBJECT)
{
// count incendrys near this tower.
result = 0;
count2 = 0;
while(count2 < numIncendrys)
{
structure2 = structureBuiltInRange(incendrys[count2], structure.x, structure.y,(4*128), me);
if(structure2 != NULLOBJECT)
{
result = result + 1;
}
count2 = count2 + 1;
}
if((result < 6) and (result < count)) // lowest found yet. only sites with <6 too.
{
buildX = structure.x;
buildY = structure.y;
count = result;
}
structure = enumStruct();
}
if(buildX != 0)
{
// choose a device
count = numIncendrys - 1;
result = 99;
while(count >= 0 )
{
if(isStructureAvailable(incendrys[count],me))
{
result = count;
count = -1;
}
else
{
count = count - 1;
}
}
// find a unit and build an incendry device.
if(result != 99)
{
boolResult = pickStructLocation(incendrys[result], ref buildX, ref buildY,me); // pick spot.
if(boolResult == TRUE)
{
initIterateGroup(buildGroup);
droid = iterateGroup(buildGroup);
boolResult = (numBuildSameBuilding(incendrys[result], buildX, buildY) > 0); //anyone building there already?
while(droid != NULLOBJECT and (not boolResult))
{
if(droid.order == DORDER_NONE or droid.order == DORDER_RTB)
{
orderDroidStatsLoc(droid, DORDER_BUILD,incendrys[result], buildX,buildY);
boolResult = TRUE; //only 1 truck
}
droid = iterateGroup(buildGroup);
}
}
}
}
}
}
/////////////////////////////////////////////////////////////////////
// build a power gen for every 4 derricks. VITAL!
event buildPowerGenerators(buildPowerGeneratorsTr)
{
initEnumStruct(FALSE,derrick,me,me); // count = numderricks
structure= enumStruct();
count = 0;
while(structure != NULLOBJECT)
{
count = count + 1;
structure= enumStruct();
}
initEnumStruct(FALSE,powGen,me,me); // count2 = numpowgens
structure= enumStruct();
count2 = 0;
while(structure != NULLOBJECT)
{
count2 = count2 + 1;
structure= enumStruct();
}
if( (count2 * 4) < count ) // if we need powergen
{
buildX = baseX; // try build powergen.
buildY = baseY;
boolResult = pickStructLocation(powGen, ref buildX, ref buildY,me);
if(boolResult == TRUE)
{
initIterateGroup(buildGroup);
droid = iterateGroup(buildGroup);
while(droid != NULLOBJECT)
{
if(droid.order == DORDER_NONE or droid.order == DORDER_RTB)
{
orderDroidStatsLoc(droid, DORDER_BUILD,powGen, buildX,buildY);
}
droid = iterateGroup(buildGroup);
}
}
}
}
/////////////////////////////////////////////////////////////////////
// ensure we have everything in the vital structs list.
event buildBase(buildBaseTr)
{
local int _numBuilders,_maxBuilders;
if( idleGroup(buildGroup) >= (buildGroup.members/2) )
{
count = 0;
while(count < numStructs)
{
// check that struct.
structure = getStructure(structs[count],me);
if(structure == NULLOBJECT) // if missing build it.
{
if(isStructureAvailable(structs[count],me))
{
buildX = baseX; // pick a location
buildY = baseY;
boolResult = pickStructLocationB(structs[count], ref buildX, ref buildY,me,0);
if(boolResult == TRUE)
{
_maxBuilders = 2;
_numBuilders = numBuildSameBuilding(structs[count], buildX, buildY);
initIterateGroup(buildGroup); // find idle droids in build group.
droid = iterateGroup(buildGroup);
while((droid != NULLOBJECT) and (_numBuilders < _maxBuilders))
{
if((droid.order == DORDER_NONE) or (droid.order == DORDER_RTB))
{
orderDroidStatsLoc(droid, DORDER_BUILD,structs[count], buildX,buildY); // build it
_numBuilders++;
}
droid = iterateGroup(buildGroup);
}
}
}
}
count = count + 1;
}
}
}
/////////////////////////////////////////////////////////////////////
// build other stuff, grow the base slowly...
event buildExpand( buildExpandTr )
{
expandBase(NULLOBJECT);
}
function void expandBase(DROID _truck)
{
local int _numBuilders,_maxBuilders;
if(extraStruct == numExtraStructs[curTech]) // loop round
{
extraStruct = 0;
}
if(isStructureAvailable(extraStructs[curTech][extraStruct],me))
{
buildX = baseX; // pick a location
buildY = baseY;
boolResult = pickStructLocationB(extraStructs[curTech][extraStruct], ref buildX, ref buildY,me,0);
if(boolResult == TRUE)
{
_numBuilders = numBuildSameBuilding(extraStructs[curTech][extraStruct], buildX, buildY);
_maxBuilders = 2;
if(_truck != NULLOBJECT)
{
if((_truck.order != DORDER_BUILD) and (_truck.order != DORDER_LINEBUILD))
{
orderDroidStatsLoc(_truck, DORDER_BUILD,extraStructs[curTech][extraStruct], buildX,buildY);// build it.
_numBuilders++;
}
}
else
{
initIterateGroup(buildGroup); // find idle droids in build group.
droid = iterateGroup(buildGroup);
while((droid != NULLOBJECT) and (_numBuilders < _maxBuilders))
{
if((droid.order != DORDER_BUILD) and (droid.order != DORDER_LINEBUILD))
{
orderDroidStatsLoc(droid, DORDER_BUILD,extraStructs[curTech][extraStruct], buildX,buildY);// build it.
_numBuilders++;
}
droid = iterateGroup(buildGroup);
}
}
}
}
extraStruct = extraStruct + 1;
}
/////////////////////////////////////////////////////////////////////
// Structure (fac/res/pow) upgrades
event upgradeStructures(upgradeStructuresTr )
{
initIterateGroup(buildGroup); // find idle droids in build group.
droid = iterateGroup(buildGroup);
while(droid != NULLOBJECT)
{
if((droid.order != DORDER_BUILD) and (droid.order != DORDER_LINEBUILD))
{
boolResult = FALSE;
if(curTech == branchDefault)
{
//powergen
boolResult = upgradePowGen(droid, 2);
//factory
if(droid.order != DORDER_BUILD){
boolResult = upgradeFactory(droid, 3);
}
//research
if(droid.order != DORDER_BUILD){
boolResult = upgradeResearch(droid, 1);
}
//vtol Factory
if(droid.order != DORDER_BUILD){
boolResult = upgradeVtolFactory(droid, 1);
}
}
else if(curTech == branchVTOL)
{
//powergen
boolResult = upgradePowGen(droid, 2);
//vtol Factory
if(droid.order != DORDER_BUILD){
boolResult = upgradeVtolFactory(droid, 3);
}
//factory
if(droid.order != DORDER_BUILD){
boolResult = upgradeFactory(droid, 2);
}
//research
if(droid.order != DORDER_BUILD){
boolResult = upgradeResearch(droid, 1);
}
}
}
droid = iterateGroup(buildGroup);
}
}
function bool upgradeFactory(DROID _truck, int _maxBuilders)
{
local STRUCTURE _factory;
initEnumStruct(FALSE,factory,me,me);
_factory = enumStruct();
while(_factory != NULLOBJECT)
{
// if upgrade is available && struct is not upgraded
if( isStructureAvailable(facModule,me) and (skGetFactoryCapacity(_factory) < 2 ))
{
if((numBuildSameBuilding(facModule, _factory.x, _factory.y) +
numBuildSameBuilding(vtolFactory, _factory.x, _factory.y)) < _maxBuilders)
{
orderDroidStatsLoc(_truck, DORDER_BUILD,facModule, _factory.x,_factory.y); // upgrade it.
return TRUE;
}
}
_factory = enumStruct();
}
return FALSE;
}
function bool upgradeVtolFactory(DROID _truck, int _maxBuilders)
{
local STRUCTURE _factory;
initEnumStruct(FALSE,vtolFactory,me,me);
_factory = enumStruct();
while(_factory != NULLOBJECT)
{
// if upgrade is available && struct is not upgraded
if( isStructureAvailable(facModule,me) and (skGetFactoryCapacity(_factory) < 2 ))
{
if((numBuildSameBuilding(facModule, _factory.x, _factory.y) +
numBuildSameBuilding(factory, _factory.x, _factory.y)) < _maxBuilders)
{
orderDroidStatsLoc(_truck, DORDER_BUILD,facModule, _factory.x,_factory.y); // upgrade it.
return TRUE;
}
}
_factory = enumStruct();
}
return FALSE;
}
function bool upgradeResearch(DROID _truck, int _maxBuilders)
{
local STRUCTURE _resFac;
initEnumStruct(FALSE,resLab,me,me);
_resFac = enumStruct();
while(_resFac != NULLOBJECT)
{
// if upgrade is available && struct is not upgraded
if( isStructureAvailable(resModule,me) and (not testStructureModule(me, _resFac, 0)))
{
if((numBuildSameBuilding(resModule, _resFac.x, _resFac.y) +
numBuildSameBuilding(resLab, _resFac.x, _resFac.y)) < _maxBuilders)
{
orderDroidStatsLoc(_truck, DORDER_BUILD,resModule, _resFac.x,_resFac.y); // upgrade it.
return TRUE;
}
}
_resFac = enumStruct();
}
return FALSE;
}
function bool upgradePowGen(DROID _truck, int _maxBuilders)
{
local STRUCTURE _powGen;
initEnumStruct(FALSE,powGen,me,me);
_powGen = enumStruct();
while(_powGen != NULLOBJECT)
{
// if upgrade is available && struct is not upgraded
if( isStructureAvailable(powModule,me) and (not testStructureModule(me, _powGen, 0)))
{
if((numBuildSameBuilding(powModule, _powGen.x,_powGen.y) +
numBuildSameBuilding(powGen, _powGen.x,_powGen.y)) < _maxBuilders)
{
orderDroidStatsLoc(_truck, DORDER_BUILD,resModule, _powGen.x,_powGen.y); // upgrade it.
return TRUE;
}
}
_powGen = enumStruct();
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////
// Finish Building Part Built Structures
event finishStructs(finishStructsTr)
{
initEnumStruct(TRUE,factory,me,me);
structure= enumStruct();
while(structure != NULLOBJECT)
{
if(not structureComplete(structure))
{
initIterateGroup(buildGroup); // find idle droids in build group.
droid = iterateGroup(buildGroup);
while(droid != NULLOBJECT)
{
if((droid.order != DORDER_BUILD) and (droid.order != DORDER_LINEBUILD))
{
orderDroidObj(droid,DORDER_HELPBUILD,structure);
}
droid = iterateGroup(buildGroup);
}
}
structure= enumStruct();
}
}
/////////////////////////////////////////////////////////////////////
// fortify base by builiding defensive structs on the edge of the base.
// rewrote fortify to use scrSkDefenseLocation(baseX,baseY,me);
event newfortify(fortifyTr)
{
local int _numBuilders,_maxBuilders;
_maxBuilders = 1;
if(numGroupSameOrder(buildGroup, DORDER_LINEBUILD) >= _maxBuilders)
{
exit;
}
boolResult = FALSE;
initIterateGroup(buildGroup); // find idle an idle veh.in build group.
droid = iterateGroup(buildGroup);
while((boolResult == FALSE) and (droid != NULLOBJECT))
{
if((droid.order != DORDER_BUILD) and (droid.order != DORDER_LINEBUILD))
{
boolResult = TRUE; // dont do this again!
tempx = baseX;
tempy = baseY;
// choose a suitable turret.
count = numWallWeaps - 1;
count2 = 0;
while( (count2 < 3) and (count >= 0) )
{
if( isStructureAvailable(wallWeaps[count],me))
{
structChoice[count2] = wallWeaps[count];
count2 = count2 + 1;
}
count = count - 1;
}
count =0;
if((count2 > 0) and (_numBuilders < _maxBuilders))
{
count = random(count2);
skDefenseLocation(ref tempx,ref tempy,wall,structChoice[count],droid,me);
_numBuilders++;
}
}
droid = iterateGroup(buildGroup);
}
}
/////////////////////////////////////////////////////////////////////
// droid building rules
/////////////////////////////////////////////////////////////////////
// deal with a droid being built
event droidBuiltAssign(droidBuiltTr)
{
if(isVtol(droid))
{
if(vtolDefendGr.members < numDefendVtols)
{
groupAddDroid(vtolDefendGr, droid);
}
else
{
count = 0;
while(count < numVtolAttackGroups)
{
if(vtolAttackGr[count].members < numAttackVtols)
{
dbg("added new vtol to group " & count, me);
groupAddDroid(vtolAttackGr[count], droid);
count = numVtolAttackGroups;
}
count++;
}
}
}
else if((droid.droidType != DROID_TRANSPORTER) and (droid.droidType != DROID_COMMAND))
{
if((droid.droidType == DROID_REPAIR)
or (droid.droidType == DROID_CYBORG_REPAIR))
{
numRepairUnits = numRepairUnits + 1;
}
if((droid.droidType == DROID_CONSTRUCT)
or (droid.droidType == DROID_CYBORG_CONSTRUCT)) // if constructor droid
{
groupAddDroid(buildGroup, droid);
}
else
{
if(droid.droidType == DROID_CYBORG)
{
groupAddDroid(defendGroup, droid);
}
else
{
if(scoutGroup.members < numScouts[curTech])
{
groupAddDroid(scoutGroup, droid);
}
else if(attackGroup.members < numAttackers[curTech])
{
groupAddDroid(attackGroup, droid);
}
else if( defendGroup.members < numDefenders[curTech])
{
groupAddDroid(defendGroup, droid);
}
else
{
if(scoutGroup.members < maxScouts[curTech])
{
groupAddDroid(scoutGroup, droid);
}
else if(attackGroup.members < maxAttackers[curTech])
{
groupAddDroid(attackGroup, droid);
}
else if( defendGroup.members < maxDefenders[curTech])
{
groupAddDroid(defendGroup, droid);
}
else //make them attack
{
groupAddDroid(attackGroup, droid);
}
}
}
}
}
}
//When droid built: set droid retreat level and start building next droid
event droidBuilt(droidBuiltTr)
{
local STRUCTURE _repairFacility;
if(!isVtol(droid))
{
_repairFacility = getStructure(repairFacility,me);
if(_repairFacility == NULLOBJECT)
{
setDroidSecondary(droid,DSO_REPAIR_LEVEL, DSS_REPLEV_NEVER);
}
else
{
setDroidSecondary(droid,DSO_REPAIR_LEVEL, DSS_REPLEV_LOW);
}
}
/* Start building next droid */
if(structure != NULLOBJECT)
{
if(structure.stattype == REF_FACTORY)
{
factoryBuildDroid(structure);
}
else if(structure.stattype == REF_CYBORG_FACTORY)
{
cybFactorBuildCyborg(structure);
}
else if(structure.stattype == REF_VTOL_FACTORY)
{
vtolFactoryBuildVtol(structure);
}
}
}
/* Gets triggered when structure was built */
event structBuilt(structBuiltTr)
{
local FEATURE _oilResource;
local int _count,_count2;
if(structure == NULLOBJECT || droid == NULLOBJECT){
exit;
}
/* factory or factory module */
if(structure.stattype == REF_FACTORY)
{
if( isStructureAvailable(facModule,me) and (skGetFactoryCapacity(structure) < 2 ))
{
orderDroidStatsLoc(droid, DORDER_BUILD,facModule, structure.x,structure.y); // upgrade it.
}
}
/* vtol factory or vtol factory module */
else if(structure.stattype == REF_VTOL_FACTORY)
{
if( isStructureAvailable(facModule,me) and (skGetFactoryCapacity(structure) < 2 ))
{
orderDroidStatsLoc(droid, DORDER_BUILD,facModule, structure.x,structure.y); // upgrade it.
}
}
else if(structure.stattype == REF_RESOURCE_EXTRACTOR)
{
/* get next oil resource nearby */
_oilResource = closestOil(structure.x, structure.y, TRUE);
if(_oilResource != NULLOBJECT)
{
if(distBetweenTwoPoints(_oilResource.x, _oilResource.y, structure.x, structure.y) < MORE_OIL_RANGE) //not too far away
{
dbg("structBuilt: more oil", me);
orderDroidStatsLoc(droid, DORDER_BUILD, derrick, _oilResource.x, _oilResource.y); //build a derick
}
}
}
//see if we have just rebuilt a destroyed structure
_count = 0;
while(_count < countRebuildStruct)
{
if(structure.x == rebuildStructX[_count] and
structure.y == rebuildStructY[_count] and
( (structure.stat == rebuildStructStat[_count]) or //walls can end up as corner walls
( (structure.stat == wall or structure.stat == cornerWall) and
(rebuildStructStat[_count] == wall or rebuildStructStat[_count] == cornerWall))
))
{
dbg("finished rebuilding destroyed structure - " & _count, me);
//resort destroyed structures
_count2 = _count;
while(_count2 < (countRebuildStruct - 1))
{
rebuildStructX[_count2] = rebuildStructX[_count2 + 1];
rebuildStructY[_count2] = rebuildStructY[_count2 + 1];
rebuildStructStat[_count2] = rebuildStructStat[_count2 + 1];
_count2++;
}
//clear last entry
rebuildStructX[countRebuildStruct - 1] = 0;
rebuildStructY[countRebuildStruct - 1] = 0;
rebuildStructStat[countRebuildStruct - 1] = NULLSTRUCTURESTAT;
countRebuildStruct--; //we just built one structure
//_count = countRebuildStruct; //exit outer loop
}
_count++;
}
}
/////////////////////////////////////////////////////////////////////
// deal with attacks.
event droidDestroyed(droidDestroyedTr)
{
if(droid.droidType == DROID_REPAIR)
{
numRepairUnits = numRepairUnits - 1;
}
if(droid.droidType == DROID_CONSTRUCT) // if constructor droid
{
initEnumStruct(FALSE,factory,me,me);
structure= enumStruct(); // find factory.
if( (structure != NULLOBJECT) and (getDroidCount(me) < MAX_DROIDS) )
{
buildDroid(constructor, structure, me, 1); // build constructor
}
}
}
/////////////////////////////////////////////////////////////////////
// build more con droids.
event conDroids(conDroidsTr)
{
buildTruck();
}
/* function void buildTruck()
{
local int _maxTrucks;
local STRUCTURE _factory;
local int _numBuilding,_haveTrucks,_maxTruckFactories,_totalTrucks;
_maxTrucks = MAX_TRUCKS;
if(powerSave){
_maxTrucks = MIN_TRUCKS;
}
_maxTruckFactories = 3; //max factories to use for truck production
_haveTrucks = buildGroup.members;
// Find out how many trucks and combat engineers are already in production
_numBuilding = numTemplatesInProduction(constructor,me); //trucks
_numBuilding = _numBuilding + numTemplatesInProduction(cybEngineer,me); //engineers
_totalTrucks = _numBuilding + _haveTrucks;
initEnumStruct(FALSE,factory,me,me);
_factory = enumStruct(); // find factory.
while((_factory != NULLOBJECT) and (_numBuilding < _maxTruckFactories)
and (_totalTrucks < _maxTrucks))
{
if(structureComplete(_factory))
{
if(getDroidCount(me) < MAX_DROIDS)
{
buildDroid(constructor, _factory, me, 1); // build constructor
_numBuilding++;
_totalTrucks++;
}
}
_factory = enumStruct();
}
} */
function void buildTruck()
{
local int _maxTrucks;
local STRUCTURE _factory;
local int _numBuilding,_haveTrucks,_maxTruckFactories,_totalTrucks;
local bool _bStartedBuilding;
_maxTrucks = MAX_TRUCKS;
if(powerSave){
_maxTrucks = MIN_TRUCKS;
}
_maxTruckFactories = 3; //max factories to use for truck production
_haveTrucks = buildGroup.members;
//Find out how many trucks and combat engineers are already in production
_numBuilding = numTemplatesInProduction(constructor,me); //trucks
_numBuilding = _numBuilding + numTemplatesInProduction(cybEngineer,me); //engineers
_totalTrucks = _numBuilding + _haveTrucks;
initEnumStruct(FALSE,factory,me,me);
_factory = enumStruct();
while((_factory != NULLOBJECT) and (_numBuilding < _maxTruckFactories)
and (_totalTrucks < _maxTrucks))
{
//Try to build a truck
_bStartedBuilding = buildUnit(constructor, _factory, factory, FALSE); //build truck even if not idle
//Update statistics if started building a truck
if(_bStartedBuilding)
{
_numBuilding++;
_totalTrucks++;
}
_factory = enumStruct();
}
//build cyborg engineers if needed, no building structure limit here
initEnumStruct(FALSE,cybFactory,me,me);
_factory = enumStruct();
while((_factory != NULLOBJECT) and (_totalTrucks < _maxTrucks))
{
//Try to build a truck
if( skCanBuildTemplate(me,_factory, cybEngineer) ) //make sure we have researched cyb engineer
{
_bStartedBuilding = buildUnit(cybEngineer, _factory, cybFactory, FALSE); //build a cyb eng even if not idle
//Update statistics if started building a cyborg engineer
if(_bStartedBuilding)
{
_numBuilding++;
_totalTrucks++;
}
}
_factory = enumStruct();
}
}
//Build a droid
function bool buildUnit(TEMPLATE _tankTemplate, STRUCTURE _factory, STRUCTURESTAT _factoryType, bool _bIdleOnly)
{
//Factory was not provided, find an factory
if(_factory == NULLOBJECT)
{
_factory = findIdleStructure(_factoryType, _bIdleOnly);
}
//Build if got a factory
if(_factory != NULLOBJECT)
{
if(structureComplete(_factory) and (getDroidCount(me) < MAX_DROIDS))
{
if( !(_bIdleOnly and !structureIdle(_factory)) ) //don't build if only allowed to build whe idle and fac is not idle
{
buildDroid(_tankTemplate, _factory, me, 1); // build a tank
return TRUE; //success
}
}
}
return FALSE; //failed
}
//Returns an idle structure of the provided type or NULLOBJECT if none found
function STRUCTURE findIdleStructure(STRUCTURESTAT _structType, bool _bIdleOnly)
{
local STRUCTURE _structure;
initEnumStruct(FALSE,_structType,me,me);
_structure = enumStruct();
while(_structure != NULLOBJECT)
{
if(structureComplete(_structure))
{
if( !(_bIdleOnly and !structureIdle(_structure)) )
{
return _structure;
}
}
_structure = enumStruct();
}
return NULLOBJECT; //none found
}
/////////////////////////////////////////////////////////////////////
// build repair droids.
event repairDroids(repairDroidsTr)
{
// if we're running low on repair droids, build some..
if(numRepairUnits <3)
{
initEnumStruct(FALSE,factory,me,me);
structure= enumStruct(); // find factory.
if( (structure != NULLOBJECT) and (getDroidCount(me) < MAX_DROIDS) )
{
buildDroid(repairUnit, structure, me, 1); // build repairunit.
}
}
}
/////////////////////////////////////////////////////////////////////
event factoryEvent(factoryEventTr)
{
// for each factory....
initEnumStruct(FALSE,factory,me,me);
structure = enumStruct(); // find factory.
if(getDroidCount(me) < MAX_DROIDS)
{
while(structure != NULLOBJECT)
{
if( structureIdle(structure) )
{
factoryBuildDroid(structure);
}
structure = enumStruct();
}
}
}
function bool needTank()
{
if((defendGroup.members < maxDefenders[curTech]) or (maxDefenders[curTech] == UNLIMITED))
{
return TRUE;
}
if((scoutGroup.members < maxScouts[curTech]) or (maxScouts[curTech] == UNLIMITED))
{
return TRUE;
}
if((attackGroup.members < maxAttackers[curTech]) or (maxAttackers[curTech] == UNLIMITED))
{
return TRUE;
}
return FALSE;
}
function void factoryBuildDroid(STRUCTURE _factory)
{
local int _count,_count2;
if(_factory == NULLOBJECT){
dbg("factoryBuildDroid: factory is NULLOBJECT", me);
return;
}
if(not needTank())
{
//dbg("NEED NO TANKS!! " & maxDefenders[curTech], me);
return;
}
if( structureIdle(_factory) )
{
_count = numTemplates[curTech] - 1;
_count2 = 0;
while( (_count2 < 4) and (_count >= 0) )
{
if( skCanBuildTemplate(me,_factory, tmpl[curTech][_count]) )
{
tmplChoice[_count2] = tmpl[curTech][_count];
_count2 = _count2 + 1;
}
_count = _count - 1;
}
if(_count2 > 0)
{
buildDroid(tmplChoice[random(_count2)],_factory,me,1);
}
}
else
{
dbg("factoryBuildDroid: factory is busy", me);
}
}
/////////////////////////////////////////////////////////////////////
// put cyborg factories to work
event cyborgFactoryEvent(cyborgFactoryEventTr)
{
if(not ((defendGroup.members < maxCyborgs[curTech]) or (maxCyborgs[curTech] == UNLIMITED)))
{
exit; //we need no cyborgs
}
initEnumStruct(FALSE,cybFactory,me,me);
structure= enumStruct(); // find factory.
while(structure != NULLOBJECT)
{
if( structureIdle(structure) == TRUE)
{
cybFactorBuildCyborg(structure);
}
structure= enumStruct(); // find factory.
}
}
function void cybFactorBuildCyborg(STRUCTURE _factory)
{
if(_factory == NULLOBJECT){
dbg("cybFactorBuildCyborg: factory is NULLOBJECT", me);
return;
}
if( structureIdle(_factory) )
{
if( (defendGroup.members < maxCyborgs[curTech]) and (getDroidCount(me) < MAX_DROIDS) )
{
if(random(5) == 1)
{
buildDroid(cybMechanic,_factory,me,1);
}
else
{
count = 3;
count2 = 0;
while( count >= 0 )
{
if( skCanBuildTemplate(me,_factory, superCyb[count]) )
{
tmplChoice[count2] = superCyb[count];
count2 = count2 + 1;
}
count = count - 1;
}
if(count2 > 0)
{
buildDroid(superCyb[random(count2)],_factory,me,1);
}
else //try light cyborgs
{
count = numLightCyborgs - 1;
count2 = 0;
while( (count >= 0) and (count2 < 2) )
{
if( skCanBuildTemplate(me,_factory, cybTempl[count]) )
{
tmplChoice[count2] = cybTempl[count];
count2++;
}
count--;
}
if(count2 > 0)
{
buildDroid(cybTempl[random(count2)], _factory, me, 1);
}
}
}
}
}
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// scouting rules
// scout an area
event chooseScoutArea(chooseScoutAreaTr)
{
scoutX = scoutTLX + random(scoutW);
scoutY = scoutTLY + random(scoutH);
}
/////////////////////////////////////////////////////////////////////
// visit new places
event expandScoutArea(expandScoutAreaTr)
{
//expand the scouting area slightly
scoutTLX = scoutTLX - ((mapWidth*128)/ tileExpand);
scoutTLY = scoutTLY - ((mapHeight*128)/ tileExpand);
scoutW = scoutW + (2*((mapWidth*128)/ tileExpand));
scoutH = scoutH + (2*((mapHeight*128)/ tileExpand));
// check & restrain.
if(scoutTLX <1)
{
scoutTLX = 1;
}
if(scoutTLY <1)
{
scoutTLY = 1;
}
if(scoutTLX >(mapWidth*128))
{
scoutTLX = (mapWidth*128) - 128;
}
if(scoutTLY >(mapHeight*128))
{
scoutTLY = (128*mapHeight) - 128;
}
if( (scoutTLX + scoutW) > (128 * mapWidth) )
{
scoutW = ( (128 * mapWidth) - scoutTLX) - 128;
}
if( (scoutTLY + scoutH) > (128 *mapHeight) )
{
scoutH = ( (128*mapHeight) - scoutTLY) - 128;
}
}
/////////////////////////////////////////////////////////////////////
// order scouts
event scoutMain(scoutMainTr)
{
// find any new scouts
// if scouts aren't busy, send them to a new spot.
if( idleGroup(scoutGroup) >= (scoutGroup.members /2) )
{
orderGroupLoc(scoutGroup, DORDER_MOVE,scoutX,scoutY);
}
}
/////////////////////////////////////////////////////////////////////
// process new visibility reports
event newObjectReport(newObjectReportTr)
{
if( baseobj2 != NULLOBJECT )
{
if((baseobj2.type == OBJ_DROID) and !friendlyPlayer(baseobj.player))
{
if(targetTypeValue(baseobj) > targetTypeValue(attackObj))
{
attackObj = baseobj;// got a new unseen target from a scout.
if( attackObj.type == OBJ_STRUCTURE)
{
if(not allianceExistsBetween(attackObj.player,me)) // an enemy
{
structure = objToStructure(attackObj);
if(structure.stat == factory)
{
allOutAttack = attackObj;
}
}
}
}
}
}
}
function int targetTypeValue(BASEOBJ _target)
{
local STRUCTURE _strTarget;
if(_target == NULLOBJECT){
return NO_TARGET_VALUE;
}
if(_target.type == OBJ_DROID)
{
return DROID_TARGET_VALUE;
}
else if(_target.type == OBJ_STRUCTURE)
{
_strTarget = objToStructure(_target);
if(_strTarget.stattype == REF_DEFENSE)
{
return DEFENSE_TARGET_VALUE;
}
else if(_strTarget.stattype == REF_RESEARCH or
_strTarget.stattype == REF_POWER_GEN)
{
return RESEARCH_TARGET_VALUE;
}
else if(_strTarget.stattype == REF_HQ or
_strTarget.stattype == REF_COMMAND_CONTROL)
{
return HQ_TARGET_VALUE;
}
else if(_strTarget.stattype == REF_RESOURCE_EXTRACTOR)
{
return OIL_TARGET_VALUE;
}
else if(_strTarget.stattype == REF_FACTORY or
_strTarget.stattype == REF_CYBORG_FACTORY or
_strTarget.stattype == REF_VTOL_FACTORY)
{
return FACTORY_TARGET_VALUE;
}
else //walls, rearm pads etc
{
return OTHER_TARGET_VALUE;
}
}
return NO_TARGET_VALUE;
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// spy technologies
//event takeover( CALL_UNITTAKEOVER , ref droid )
event takeover(takeoverTr)
{
if( droid.player == me )
{
groupAddDroid( attackGroup, droid );
}
}
event takeoverDefend(takeoverTr)
{
if( droid.player != me )
{
completeResearch(nexusDefence,me);
setEventTrigger(takeoverDefend, inactive);
}
}
event useLassat(useLassatTr)
{
// find my lassat
// fire it at my attack objective.
if(allOutAttack != NULLOBJECT)
{
initEnumStruct(FALSE,lassat,me,me);
structure= enumStruct();
while(structure != NULLOBJECT)
{
if(structureComplete(structure) == TRUE)
{
skFireLassat(me,allOutAttack);
}
structure= enumStruct();
}
}
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// attack rules
event findEnemy(attackStuffTr)
{
if(attackObj == NULLOBJECT)
{
count = random(8);
count2 = 100;
while( friendlyPlayer(count) && (count2 > 0) )
{
count = random(8);
count2--;
}
if(!friendlyPlayer(count))
{
baseobj = skLocateEnemy(count);
if(baseobj != NULLOBJECT)
{
attackObj = baseobj;
allOutAttack = attackObj;
}
}
}
}
/////////////////////////////////////////////////////////////////////
// send attack team out to cause trouble near things scout found.
event attackStuff(attackStuffTr)
{
if( idleGroup(attackGroup) >= (attackGroup.members /2))
{
if( (attackObj != NULLOBJECT) and (not helpingAlly()) )
{
if (not allianceExistsBetween(me, attackObj.player))
{
if(attackGroup.members > (6+random(6)) )
{
orderGroupLoc(attackGroup, DORDER_SCOUT,attackObj.x,attackObj.y);
}
if( idleGroup(scoutGroup) >= (scoutGroup.members /2) )
{
orderGroupLoc(scoutGroup, DORDER_MOVE,scoutX,scoutY);
}
}
}
}
}
/////////////////////////////////////////////////////////////////////
event doAllOutAttack(allOutAttackTr)
{
if( (allOutAttack != NULLOBJECT) and (not helpingAlly()) )
{
if( !friendlyPlayer(allOutAttack.player) )
{
if( getDroidCount(me) > 40) // plenty of units.
{
orderGroupObj(attackGroup, DORDER_ATTACK,allOutAttack);
//orderGroupLoc(defendGroup, DORDER_SCOUT,allOutAttack.x,allOutAttack.y);
orderGroupLoc(scoutGroup, DORDER_SCOUT,allOutAttack.x,allOutAttack.y);
}
}
else
{
allOutAttack = NULLOBJECT;
}
}
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// defending rules
// defend attacked objects.
event defendWatch(defendWatchTr)
{
if(baseobj != NULLOBJECT)
{
if(!friendlyPlayer(baseobj.player))
{
if(not isHumanPlayer(baseobj.player) ) // new in wdg1 dont allout attack a pc player //TODO:is this check needed?
{
if(distBetweenTwoPoints(baseobj.x, baseobj.y, baseX, baseY) <= MAX_DEFENDERS_RADIUS) //don't go too far away from the base
{
defendObj = baseobj;
defendbusy = TRUE;
// if not too busy, attack.
if( idleGroup(defendGroup) >= (defendGroup.members / 2) )
{
orderGroupLoc(defendGroup, DORDER_MOVE,defendObj.x,defendObj.y); //cyborg mechanics can't attack (won't move)
//orderGroupObj(defendGroup, DORDER_ATTACK, defendObj);
}
if( idleGroup(scoutGroup) >= (scoutGroup.members / 2) )
{
orderGroupLoc(scoutGroup, DORDER_MOVE,scoutX,scoutY);
}
}
}
}
}
}
/////////////////////////////////////////////////////////////////////
// defenders return after they are finished.
event defendReturn(defendReturnTr)
{
if( defendbusy and (idleGroup(defendGroup) == (defendGroup.members - defendGroup.members / 12)))
{
// find a place with no structures nearby
buildX = baseX;
buildY = baseY;
pickStructLocationB(powGen, ref buildX, ref buildY, me, 0);
orderGroupLoc(defendGroup, DORDER_MOVE,buildX,buildY);
defendbusy = FALSE;
}
}
//returns number of non-idle structures of a certain type
function int numStructBusyByType(STRUCTURESTAT _busyStructType)
{
local int _result;
initEnumStruct(FALSE,_busyStructType,me,me);
structure = enumStruct();
_result = 0;
while(structure != NULLOBJECT)
{
if(structureComplete(structure))
{
if(not structureIdle(structure))
{
_result++;
}
}
structure = enumStruct();
}
return _result;
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// Research Rules Now does true research.
// do research
event doResearch(doResearchTr)
{
local int _techIndex,_numResearching;
_techIndex = 0; //research start
_numResearching = numStructBusyByType(resLab);
// for every research lab do this..
initEnumStruct(FALSE,resLab,me,me);
structure= enumStruct();
count = 0;
while(structure != NULLOBJECT)
{
boolResult = FALSE; //haven't started research for the current resFac
if(structureIdle(structure))
{
if(structureComplete(structure))
{
// first research all technologies necessary for the current research branch
while(not boolResult and _techIndex != NONE) //not started researching and still branch tech left to try
{
_techIndex = findResearch(_techIndex, curTech);
if(_techIndex > NONE)
{
boolResult = pursueResearch(structure,me,tech[curTech][_techIndex]);
_techIndex++; //try nect research next time if needed
_numResearching++;
}
}
// do common research
if(not boolResult) //didn't start branch research
{
if((maxIdleRes[curTech] == UNLIMITED) or (_numResearching < maxIdleRes[curTech]))
{
skDoResearch(structure,me,0);
_numResearching++;
}
}
}
}
structure = enumStruct();
}
}
// find next available research of our research branch
function int findResearch(int _searchStart, int _techTree)
{
local int _result;
ASSERT(_searchStart >= 0, "findResearch: _searchStart < 0", me);
ASSERT(_techTree >= 0, "findResearch: _techTree < 0", me);
_result = _searchStart;
while(_result < techCount[_techTree])
{
if((not researchFinished(tech[_techTree][_result], me)) and (not researchStarted(tech[_techTree][_result], me)))
{
return _result; //found research
}
_result++;
}
return NONE; //not found
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// Alliance Rules
// form alliances
event formAllianceEvent(formAllianceEventTr)
{
count = 0;
while(count < MAX_PLAYERS)
{
if( count != me ) // if not the only other player and rand2
{
if((getDroidCount(me) > 1) and (getDroidCount(count) > 1) ) // not dead
{
if(random(28) == 1) // bit random
{
if(not isHumanPlayer(count)) // not human
{
createAlliance(me,count);
allianceTime[count] = gameTime;
}
}
}
}
count = count + 1;
}
}
/////////////////////////////////////////////////////////////////////
// break the alliance too.
event breakAllianceEvent(breakAllianceEventTr)
{
count = 0;
while(count<multiPlayerMaxPlayers)
{
if( count != me)
{
if((getDroidCount(me) > 1) and (getDroidCount(count) > 1) ) // not dead
{
if(allianceExistsBetween(me,count) )
{
// check if we're in alliance with any other players.
if( (random(30) == 1) and ( (gameTime - allianceTime[count]) > 6000) ) // rand and more than 10 minutes.
{
allianceTime[count] = gameTime;
breakAlliance(me,count);
}
// rules for breaking alliances with humans.
// built within my base
if(numStructsInArea(count,minx,miny,maxx,maxy) > 1)
{
allianceTime[count] = gameTime;
breakAlliance(me,count);
}
// you've got lots of units in my area.
if(numDroidsInArea(count,minx,miny,maxx,maxy) > 3)
{
allianceTime[count] = gameTime;
breakAlliance(me,count);
}
// you've wiped out one of my allies ??.
}
}
}
count = count + 1;
}
}
/////////////////////////////////////////////////////////////////////
event formHumanAlliances(humanAllianceTr)
{
if(count2 == me) // offered to me.
{
result = 0;
result2 = 0;
while(result < multiPlayerMaxPlayers)
{
if(allianceExistsBetween(count,result))
{
result2 = result2 + 1;
}
result = result + 1;
}
if( result2 < ((multiPlayerMaxPlayers / 2) - 1) ) // not too many already
{
//not too soon.
if((allianceTime[count] == 0) or (gameTime - allianceTime[count] > 1200))
{
result = 0; // check forming wont end the game
result2 = 0;
while(result < multiPlayerMaxPlayers)
{
while(result2 < multiPlayerMaxPlayers)
{
if((not allianceExistsBetween(result,result2)) and (getDroidCount(result) > 0) and (getDroidCount(result2) > 0) and (result != result2) )
{
if( ((result == count and result2 == count2) or (result2 == count2 and result == count)) ) // ignore the outcome of this alliance
{
createAlliance(me,count);
allianceTime[count] = gameTime;
}
}
result2 = result2 + 1;
}
result = result + 1;
}
}
}
}
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// Consolidate Rules
// bring forces back together to consolidate attacks
event consolidateEvent(consolidateEventTr)
{
if(not helpingAlly())
{
if(random(3) == 1) // order all droids home to rejoin forces.!
{
// find a place with no structures nearby
buildX = baseX;
buildY = baseY;
pickStructLocationB(powGen, ref buildX, ref buildY, me, 0);
orderGroupLoc(scoutGroup, DORDER_MOVE,buildX,buildY);
orderGroupLoc(defendGroup, DORDER_MOVE,buildX,buildY);
}
if(attackObj != NULLOBJECT) // consolidate any ongoing attack.
{
if(!friendlyPlayer(attackObj.player))
{
orderGroupObj(attackGroup, DORDER_ATTACK,attackObj);
}
else
{
attackObj = NULLOBJECT;
}
}
else
{
// find a place with no structures nearby
buildX = baseX;
buildY = baseY;
pickStructLocationB(powGen, ref buildX, ref buildY, me, 0);
orderGroupLoc(attackGroup, DORDER_MOVE,buildX,buildY);
}
}
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
// power management.
// if running low on power put some power eating stuff on hold for a while.
event managePower(managePowerTr)
{
if( playerPower(me) < LOW_POWER ) // turn off some events.
{
powerSave = TRUE;
// setEventTrigger(fortify, inactive); // stop building defenses.
setEventTrigger(upgradeStructures, inactive); // stop building oil defenses.
// setEventTrigger(buildExpand, inactive); // stop building extra buildings.
// setEventTrigger(conDroids, inactive); // stop building more construct droids.
}
else
{
if(powerSave == TRUE) // turn events back on.
{
powerSave = FALSE;
// setEventTrigger(fortify, fortifyTr); // building defenses.
setEventTrigger(upgradeStructures, upgradeStructuresTr);// building oil defenses.
// setEventTrigger(buildExpand, buildExpandTr); // building extra buildings.
// setEventTrigger(conDroids, conDroidsTr); // building more construct droids.
}
}
}
/////////////////////////////////////////////////////////////////////
event difficultyModifier(difficultyModifierTr)
{
skDifficultyModifier(me);
}
/////////////////////////////////////////////////////////////////////
// vtols.
/////////////////////////////////////////////////////////////////////
// build vtol strucutures.
event vtolStructs(inactive)
{
local int _maxTrucks,_numTrucks,_maxBuildTrucks,_minBuildTrucks;
local int _numVtolFacs,_numRearmPads;
// got any idle trucks?
if(idleGroup(buildGroup) < 1){
exit;
}
// see how many vtol factories we already have
_numVtolFacs = getNumStructures(vtolFactory,me);
_numRearmPads = getNumStructures(vtolPad,me);
//see if we have enough rearm pads
if( _numRearmPads * 4 / 3 <= totalVtols() )
{
dbg("NEED REARM PADS", me);
buildRearmPads();
}
// calculate how many trucks we want to use for construction
_minBuildTrucks = 1;
_maxBuildTrucks = 3;
_maxTrucks = min(_maxBuildTrucks, max(_minBuildTrucks, idleGroup(buildGroup) / 2));
if(_numVtolFacs < maxVtolFacs[curTech]) // if not enough
{
// build factory
buildX = baseX; // pick a location
buildY = baseY;
boolResult = pickStructLocation(vtolFactory, ref buildX, ref buildY,me);
if(boolResult == TRUE)
{
initIterateGroup(buildGroup); // find idle droids in build group.
droid = iterateGroup(buildGroup);
_numTrucks = 0; // only send _maxTrucks to do the job
while( (_numTrucks < _maxTrucks) and (droid != NULLOBJECT))
{
if( (droid.order == DORDER_NONE) or (droid.order == DORDER_RTB))
{
orderDroidStatsLoc(droid, DORDER_BUILD,vtolFactory, buildX,buildY);
_numTrucks++;
}
droid = iterateGroup(buildGroup);
}
}
}
}
//counts vtols
function int totalVtols()
{
local int _vtolGroup,_totalVtols;
_totalVtols = 0;
_vtolGroup = 0;
while(_vtolGroup < numVtolAttackGroups)
{
_totalVtols = _totalVtols + vtolAttackGr[_vtolGroup].members;
_vtolGroup++;
}
_totalVtols = _totalVtols + vtolDefendGr.members;
return _totalVtols;
}
function void buildRearmPads()
{
local int _buildX,_buildY;
local DROID _truck;
local bool _buildOK;
if(!isStructureAvailable(vtolPad,me)){
return;
}
// build vtol rearm pads.
_buildX = baseX; // pick a location
_buildY = baseY;
_buildOK = pickStructLocation(vtolPad, ref _buildX, ref _buildY,me);
if(_buildOK)
{
initIterateGroup(buildGroup); // find idle droids in build group.
_truck = iterateGroup(buildGroup);
_buildOK = FALSE;
while(_truck != NULLOBJECT and _buildOK == FALSE)
{
if((_truck.order != DORDER_BUILD) and (_truck.order != DORDER_LINEBUILD))
{
orderDroidStatsLoc(_truck, DORDER_BUILD,vtolPad, _buildX,_buildY);
_buildOK = TRUE;
}
_truck = iterateGroup(buildGroup);
}
}
}
/////////////////////////////////////////////////////////////////////
// build vtols.
event buildVtols(inactive)
{
// got enough vtols?
if((vtolDefendGr.members >= maxVTOLs[curTech]) or (getDroidCount(me) >= MAX_DROIDS)){
dbg("CAN'T BUILD VTOLS - TOO MANY UNITS", me);
exit;
}
// build vtols
initEnumStruct(FALSE,vtolFactory,me,me);
structure = enumStruct();
while(structure != NULLOBJECT)
{
if(structureIdle(structure)) // if factory idle
{
vtolFactoryBuildVtol(structure);
}
structure = enumStruct();
}
}
function void vtolFactoryBuildVtol(STRUCTURE _factory)
{
local int _numTemplates,_bestTemplates;
if(_factory == NULLOBJECT){
return;
}
if( structureIdle(_factory) )
{
_numTemplates = numVtolTemplates - 1;
_bestTemplates = 0;
while( (_bestTemplates < 3) and (_numTemplates >= 0) )
{
if( skCanBuildTemplate(me,_factory, vtols[_numTemplates]) )
{
tmplChoice[_bestTemplates] = vtols[_numTemplates];
_bestTemplates++;
}
_numTemplates--;
}
if(_bestTemplates > 0)
{
buildDroid(tmplChoice[random(_bestTemplates)],_factory,me,1);
}
}
}
/////////////////////////////////////////////////////////////////////
// attack with vtols.
event vtolAttack(inactive)
{
local int _groupIndex,_newTargetWeight,_oldTargetWeight;
local BASEOBJ _newTarget;
local bool _bHaveDefendTarget;
local DROID _droid;
// if vtol group is not busy..
if( (idleGroup(vtolDefendGr) >= (vtolDefendGr.members / 2)) and (vtolDefendGr.members >= 2) )
{
if(attackObj != NULLOBJECT)
{
if(!friendlyPlayer(attackObj.player))
{
orderGroupObj(vtolDefendGr, DORDER_ATTACK, attackObj); // get the attack target.
}
else
{
attackObj = NULLOBJECT;
}
}
else
{
if(defendObj != NULLOBJECT)
{
if(not isHumanPlayer(defendObj.player) ) // new in wdg1 //TODO:is this check needed?
{
orderGroupObj(vtolDefendGr, DORDER_ATTACK,defendObj); // get the defend target
}
}
}
}
//make sure attack vtol groups are not cluttered
rearrangeAttackVtols();
//attack vtols
_groupIndex = 0;
while(_groupIndex < numVtolAttackGroups)
{
if(vtolAttackGr[_groupIndex].members > 0)
{
// if our base is in trouble see if we have a target in the base
// don't choose new target if already have one
_bHaveDefendTarget = FALSE;
if((vtolGrAttackObj[_groupIndex] != NULLOBJECT))
{
if(defendingOwnBase() and
(distBetweenTwoPoints(baseX, baseY, vtolGrAttackObj[_groupIndex].x, vtolGrAttackObj[_groupIndex].y) <= MAX_VTOL_DEFEND_RADIUS))
{
_bHaveDefendTarget = TRUE;
}
else //reset target if it's not worth it
{
//we don't want to attack enemy heavy tanks outside of our base
if(vtolGrAttackObj[_groupIndex].type == OBJ_DROID)
{
_droid = objToDroid(vtolGrAttackObj[_groupIndex]);
if(_droid.droidType != DROID_CONSTRUCT)
{
vtolGrAttackObj[_groupIndex] = NULLOBJECT; //reset target
_bHaveDefendTarget = FALSE;
}
}
}
}
//find target in our base if our base is in trouble
if(!_bHaveDefendTarget and defendingOwnBase())
{
vtolGrAttackObj[_groupIndex] = chooseVtolDefenceTarget(baseX, baseY, MAX_VTOL_DEFEND_RADIUS, FALSE);
if(vtolGrAttackObj[_groupIndex] != NULLOBJECT)
{
dbg("assigned new defence target for group " & _groupIndex, me);
_bHaveDefendTarget = TRUE;
}
}
//attack rules
if(!_bHaveDefendTarget and (idleGroup(vtolAttackGr[_groupIndex]) >= (vtolAttackGr[_groupIndex].members / 2)) and
(vtolAttackGr[_groupIndex].members >= (numAttackVtols * 2 / 3)))
{
_newTarget = NULLOBJECT;
//reset attack targets once in a while
if(vtolGrAttackObj[_groupIndex] != NULLOBJECT)
{
if(random(10) > 7)
{
vtolGrAttackObj[_groupIndex] = NULLOBJECT;
}
}
//now try to find an attack target if we have no defence target
//find best attack target
_newTarget = chooseVtolTarget(TRUE); //search for an exclusive target
if(_newTarget == NULLOBJECT)
{
_newTarget = chooseVtolTarget(FALSE); //search for any target
}
_newTargetWeight = getVtolTargetWeight(_newTarget);
_oldTargetWeight = getVtolTargetWeight(vtolGrAttackObj[_groupIndex]);
// new one or a much better one
if((_newTargetWeight >= (_oldTargetWeight + 20)) or
(vtolGrAttackObj[_groupIndex] == NULLOBJECT ))
{
dbg("choosing new attack object for " & _groupIndex, me);
vtolGrAttackObj[_groupIndex] = _newTarget;
}
}
//see if this group has something to attack
if(vtolGrAttackObj[_groupIndex] != NULLOBJECT)
{
if(!friendlyPlayer(vtolGrAttackObj[_groupIndex].player))
{
dbg("VTOL Group " & _groupIndex & " attacking", me);
if(_DEBUG and (_groupIndex == 0))
{
dropBeacon(getPlayerName(me), me, me, vtolGrAttackObj[_groupIndex].x, vtolGrAttackObj[_groupIndex].y, 0);
}
orderGroupObj(vtolAttackGr[_groupIndex], DORDER_ATTACK, vtolGrAttackObj[_groupIndex]);
}
else
{
vtolGrAttackObj[_groupIndex] = NULLOBJECT;
}
}
}
_groupIndex++;
}
}
//make sure vtol groups are not cluttered
function void rearrangeAttackVtols()
{
local int _emptyGr,_fillGr;
local DROID _droid;
_emptyGr = 0;
while(_emptyGr < numVtolAttackGroups)
{
if((vtolAttackGr[_emptyGr].members < numAttackVtols) and //this group is not full
(vtolAttackGr[_emptyGr].members > 0))
{
//try to make full groups by moving vtols from the last groups
_fillGr = _emptyGr + 1; //start from the next group
while((_fillGr < numVtolAttackGroups) and
(vtolAttackGr[_emptyGr].members < numAttackVtols))
{
initIterateGroup(vtolAttackGr[_fillGr]);
_droid = iterateGroup(vtolAttackGr[_fillGr]);
while((_droid != NULLOBJECT) and
(vtolAttackGr[_emptyGr].members < numAttackVtols))
{
groupAddDroid(vtolAttackGr[_emptyGr], _droid); //refill the group
_droid = iterateGroup(vtolAttackGr[_fillGr]);
}
_fillGr++;
}
}
_emptyGr++;
}
}
function BASEOBJ chooseVtolDefenceTarget(int _x, int _y, int _range, bool bExclusiveTarget)
{
local BASEOBJ _target;
_target = getClosestEnemy(_x, _y, _range, FALSE, FALSE, me);
// make sure no one else is targeting it already if we want exclusive targets
if(bExclusiveTarget and vtolTargetAssigned(_target))
{
_target = NULLOBJECT;
}
return _target;
}
function BASEOBJ chooseVtolTarget(bool bExclusiveTarget)
{
local int _structTypeIndex,_enemy;
local int _bestScore,_tempScore;
local BASEOBJ _bestTarget;
local STRUCTURE _structure;
_bestScore = 0;
_bestTarget = NULLOBJECT;
_enemy = 0;
while(_enemy < MAX_PLAYERS)
{
if(!friendlyPlayer(_enemy))
{
_structTypeIndex = 0;
while(_structTypeIndex < numVtolTargets)
{
initEnumStruct(FALSE,vtolTarget[_structTypeIndex],_enemy,me);
_structure = enumStruct();
while(_structure != NULLOBJECT)
{
// in case we don't want all groups to attack the same target
if(not (bExclusiveTarget and vtolTargetAssigned(_structure)) )
{
_tempScore = getVtolTargetWeight(_structure);
//see if this one is better
if(_tempScore > _bestScore)
{
_bestScore = _tempScore;
_bestTarget = _structure;
}
}
_structure = enumStruct();
}
_structTypeIndex++;
}
}
_enemy++;
}
return _bestTarget;
}
function int getVtolTargetWeight(BASEOBJ _target)
{
local int _AAPenalty,_numAA,_range,_targetWeight;
local int _targetType;
local STRUCTURE _structTarget;
if(_target == NULLOBJECT)
{
return 0;
}
if(_target.type != OBJ_STRUCTURE)
{
return 0;
}
_structTarget = objToStructure(_target);
_targetWeight = 0;
_AAPenalty = 9; //penalty per aa defense
_range = AA_THREAT_RANGE;
// count enemy AA
_numAA = numEnemyAAInRange(_structTarget.x, _structTarget.y, _range);
// find tyrget type from stats
_targetType = 0;
while(_targetType < numVtolTargets)
{
if(_structTarget.stat == vtolTarget[_targetType])
{
_targetWeight = vtolTargetWeight[_targetType] - (_numAA * _AAPenalty);
_targetType = numVtolTargets; //exit loop
}
_targetType++;
}
// incomplete structures get lower weight
if(!structureComplete(_structTarget))
{
_targetWeight = _targetWeight / 10;
}
return _targetWeight;
}
function int numEnemyAAInRange(int _x, int _y, int _range)
{
local int _enemy,_numAA;
_numAA = 0;
_enemy = 0;
while(_enemy < MAX_PLAYERS)
{
if(!friendlyPlayer(_enemy))
{
_numAA = _numAA + numAAinRange(_enemy, me, _x, _y, _range);
}
_enemy++;
}
return _numAA;
}
// see if a particular target is already assigned to one of the VTOL attack groups
function bool vtolTargetAssigned(BASEOBJ _target)
{
local int _groupIndex;
if(_target == NULLOBJECT)
{
return FALSE;
}
_groupIndex = 0;
while(_groupIndex < numVtolAttackGroups)
{
if(_target == vtolGrAttackObj[_groupIndex])
{
return TRUE;
}
_groupIndex++;
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////
// watch for incoming vtols
event vtolDefend(vtolDefendTr)
{
local int _numBuilders,_maxBuilders;
// boolResult2 = FALSE; // if attacked by a vtol.
if(baseobj != NULLOBJECT)
{
if(baseobj.type == OBJ_DROID)
{
if(isVtol(objToDroid(baseobj)))
{
// activate other vtol functions..
setEventTrigger(vtolStructs,vtolStructsTr);
setEventTrigger(buildVtols, buildVtolsTr);
setEventTrigger(vtolAttack, vtolAttackTr);
_numBuilders = 0;
_maxBuilders = 2;
// build defenses.
initIterateGroup(buildGroup); // find idle droids in build group.
droid = iterateGroup(buildGroup);
// while( (boolResult2 != TRUE) and (droid != NULLOBJECT))
while( droid != NULLOBJECT)
{
if( (structure != NULLOBJECT) and (droid.order != DORDER_BUILD) )
{
buildX = structure.x;
buildY = structure.y;
// if ! vtol defense already built...
//find best defense we can build.
count = 0;
count2 = -1;
while( count < 5)
{
if(isStructureAvailable(vtolDefStruct[count],me))
{
count2 = count;
}
count = count + 1;
}
if(count2 != (-1) )
{
boolResult = pickStructLocation(vtolDefStruct[count2], ref buildX, ref buildY,me);
if((boolResult == TRUE) and (_numBuilders < _maxBuilders)) // build a vtol defense near the attacked struct...
{
orderDroidStatsLoc(droid, DORDER_BUILD,vtolDefStruct[count2],buildX,buildY);
_numBuilders++;
// boolResult2 = TRUE;
}
}
}
droid = iterateGroup(buildGroup);
}
}
}
}
}
/////////////////////////////////////////////////////////////////////
event vtolEnabler(vtolEnablerTr)
{
if( skVtolEnableCheck(me) ) // check to see if we have vtol technologies.
{
setEventTrigger(vtolEnabler,inactive); // turn off this event.
setEventTrigger(vtolStructs,vtolStructsTr); // activate other vtol functions..
setEventTrigger(buildVtols, buildVtolsTr);
setEventTrigger(vtolAttack, vtolAttackTr);
dbg("----I CAN USE VTOLS----", me);
}
}
/////////////////////////////////////////////////////////////////////
// HouseKeeping
function void shutDownAI()
{
bRunning = false;
setEventTrigger(givehelp, inactive);
setEventTrigger(basedetails, inactive);
setEventTrigger(buildDerrick, inactive);
setEventTrigger(buildOilDefenseOrRetreat, inactive);
setEventTrigger(incendry, inactive);
setEventTrigger(buildPowerGenerators, inactive);
setEventTrigger(buildBase, inactive);
setEventTrigger(buildExpand,inactive);
setEventTrigger(upgradeStructures,inactive);
setEventTrigger(finishStructs, inactive);
setEventTrigger(newfortify, inactive);
setEventTrigger(droidBuilt, inactive);
setEventTrigger(structBuilt,inactive);
setEventTrigger(droidDestroyed,inactive);
setEventTrigger(conDroids,inactive);
setEventTrigger(repairDroids,inactive);
setEventTrigger(factoryEvent, inactive);
setEventTrigger(cyborgFactoryEvent, inactive);
setEventTrigger(chooseScoutArea, inactive);
setEventTrigger(expandScoutArea, inactive);
setEventTrigger(scoutMain, inactive);
setEventTrigger(newObjectReport, inactive);
setEventTrigger(takeover, inactive);
setEventTrigger(takeoverDefend, inactive);
setEventTrigger(useLassat, inactive);
setEventTrigger(findEnemy, inactive);
setEventTrigger(attackStuff, inactive);
setEventTrigger(defendWatch, inactive);
setEventTrigger(defendReturn, inactive);
setEventTrigger(doResearch, inactive);
setEventTrigger(formAllianceEvent, inactive);
setEventTrigger(breakAllianceEvent, inactive);
setEventTrigger(formHumanAlliances, inactive);
setEventTrigger(consolidateEvent, inactive);
setEventTrigger(managePower, inactive);
setEventTrigger(difficultyModifier, inactive);
setEventTrigger(vtolStructs,inactive);
setEventTrigger(buildVtols,inactive);
setEventTrigger(vtolAttack,inactive);
setEventTrigger(vtolDefend,inactive);
setEventTrigger(vtolEnabler,inactive);
setEventTrigger(beaconEv, inactive);
setEventTrigger(multiMsgEv, inactive);
setEventTrigger(manageAllyHelp, inactive);
setEventTrigger(everySecEv, inactive);
setEventTrigger(watchBaseThreat, inactive);
setEventTrigger(manageDefendLocationEv, inactive);
setEventTrigger(structureDestroyed,inactive);
setEventTrigger(rebuildStructureEv,inactive);
}
event reassignPlayers(reassignTr)
{
reassignAI();
}
function void reassignAI()
{
bRunning = true;
setEventTrigger(basedetails,basedetailsTr);
setEventTrigger(buildDerrick,buildDerrickTr);
setEventTrigger(buildOilDefenseOrRetreat,buildOilDefenseOrRetreatTr);
setEventTrigger(incendry,incendryTr);
setEventTrigger(buildPowerGenerators,buildPowerGeneratorsTr);
setEventTrigger(buildBase,buildBaseTr );
setEventTrigger(buildExpand,buildExpandTr );
setEventTrigger(upgradeStructures,upgradeStructuresTr );
setEventTrigger(finishStructs,finishStructsTr );
setEventTrigger(newfortify,fortifyTr );
setEventTrigger(droidBuilt,droidBuiltTr);
setEventTrigger(droidDestroyed,droidDestroyedTr);
setEventTrigger(conDroids,conDroidsTr);
setEventTrigger(repairDroids,repairDroidsTr);
setEventTrigger(factoryEvent,factoryEventTr);
setEventTrigger(cyborgFactoryEvent,cyborgFactoryEventTr);
setEventTrigger(chooseScoutArea,chooseScoutAreaTr);
setEventTrigger(expandScoutArea,expandScoutAreaTr);
setEventTrigger(scoutMain,scoutMainTr);
setEventTrigger(newObjectReport,newObjectReportTr);
setEventTrigger(takeover,takeoverTr);
setEventTrigger(useLassat,useLassatTr);
setEventTrigger(findEnemy,attackStuffTr);
setEventTrigger(attackStuff,attackStuffTr);
setEventTrigger(doAllOutAttack,allOutAttackTr);
setEventTrigger(defendWatch,defendWatchTr);
setEventTrigger(defendReturn,defendReturnTr);
setEventTrigger(doResearch,doResearchTr);
setEventTrigger(formAllianceEvent,formAllianceEventTr);
setEventTrigger(breakAllianceEvent,breakAllianceEventTr);
setEventTrigger(consolidateEvent,consolidateEventTr);
setEventTrigger(managePower,managePowerTr);
setEventTrigger(difficultyModifier,difficultyModifierTr);
setEventTrigger(vtolStructs,inactive);
setEventTrigger(buildVtols,inactive);
//setEventTrigger(vtolAttack,inactive);
setEventTrigger(vtolAttack, vtolAttackTr);
setEventTrigger(vtolDefend,vtolDefendTr);
setEventTrigger(vtolEnabler,vtolEnablerTr);
setEventTrigger(formHumanAlliances,humanAllianceTr);
setEventTrigger(multiMsgEv, multiMsgTr);
setEventTrigger(beaconEv, beaconTr);
setEventTrigger(watchBaseThreat, watchBaseThreatTr);
setEventTrigger(manageAllyHelp, manageAllyHelpTr);
setEventTrigger(everySecEv, everySec);
//setEventTrigger(manageDefendLocationEv, manageDefendLocationTr);
setEventTrigger(structBuilt, structBuiltTr);
setEventTrigger(structureDestroyed, structureDestroyedTr);
setEventTrigger(rebuildStructureEv, rebuildStructureTr);
}
/* Returns true if we just received a beacon from a certain player */
function bool haveBeacon(int _player)
{
if((tBeacon[_player] > 0) and (not beaconTimeout(_player)))
{
return TRUE; //have beacon for this player
}
return FALSE;
}
/* See if last beacon was placed long ago */
function bool beaconTimeout(int _player)
{
if((tBeacon[_player] > 0) and
((tBeacon[_player] + BEACON_TIMEOUT) < (gameTime / 10))) //not too long ago
{
return TRUE; //this beacon is still 'fresh'
}
return FALSE;
}
/* Deal with beacons */
event beaconEv(beaconTr)
{
local int _players;
local string _processedString;
if(_DEBUG)
debug(me & ") beaconEv: from " & sender);
ASSERT(sender >= 0 and sender < 8,
"beaconEv: sender out of bounds: " & sender , me);
beaconX[sender] = x;
beaconY[sender] = y;
tBeacon[sender] = gameTime / 10;
processCommand(message, sender, TRUE);
}
/* Deal with a chat message */
event multiMsgEv(multiMsgTr)
{
if(_DEBUG)
debug(me & ") multiMsgEv: from " & sender);
if(not allianceExistsBetween(me, sender)){
exit;
}
if(sender == me){
exit;
}
processCommand(message, sender, FALSE);
}
/* Process multiplayer messages */
function void processCommand(string _message, int _sender, bool _bBlipMessage)
{
local int _numMsgs,_curMsg,_addressedPlayers,_x,_y;
local string _msg,_processedString;
/* Extract semantic information */
_curMsg = 0;
_numMsgs = processChatMsg(_message);
debug(me & ") processCommand: '" & _message & "' from " & _sender);
dbg("processCommand: '" & _message & "' from " & _sender, me);
dbg("got " & _numMsgs & " commands", me);
/* Process all messages */
while(_curMsg < _numMsgs)
{
if(chatCmdIsPlayerAddressed(_curMsg, me))
{
dbg("i'm addressed", me);
_msg = getChatCmdDescription(_curMsg);
/* Someone requested help */
if(_msg == R_REQUEST_HELP)
{
dbg("'help' command", me);
if(haveBeacon(_sender))
{
dbg("got beacon", me);
_x = beaconX[_sender];
_y = beaconY[_sender];
if(attemptToHelp(_sender, _x, _y))
{
messagePlayer(ALL_ALLIES, m_affirmative(), MAX_PROBABILITY);
}
}
else
{
/* Ask player to drop a beacon so we would know where to help */
_addressedPlayers = setBit(0, _sender, TRUE);
messagePlayerAddressed(ALL_ALLIES, _addressedPlayers, R_REQUEST_BEACON);
}
}
/* Someone requested a beacon from us -
* did we request help and our beacon timed out??
*/
else if(_msg == M_REQUEST_BEACON)
{
/* If our base is really in trouble drop a beacon for the requester again */
if(baseInTrouble()){
dropBeacon(getPlayerName(me), _sender, me, baseX, baseY, 0);
}
}
else if(_msg == R_REPORT_SAFETY)
{
dbg("helping " & lastHelpPlayer, me);
/* Make sure we were helping him */
if(helpingAlly() and (lastHelpPlayer == _sender))
{
stopHelpingAlly();
messagePlayer(ALL_ALLIES, m_affirmative(), MAX_PROBABILITY);
}
else if(defendingOwnBase()) //if we are in trouble re-request help
{
requestHelp(baseX, baseY);
}
}
else
{
dbg("unknown message", me);
}
}
else
{
dbg("i'm not addressed", me);
}
_curMsg++;
}
}
function bool attemptToHelp(int _playerToHelp, int _x, int _y)
{
local bool _bHelpingMyself;
if(_playerToHelp < 0 or _playerToHelp >= MAX_PLAYERS){
return FALSE;
}
if(_x <= 0 or _y <= 0){
return FALSE;
}
dbg("attemptToHelp - checking", me);
_bHelpingMyself = (_playerToHelp == me);
/* Can only help allies and myself */
if(not friendlyPlayer(_playerToHelp)){
return FALSE;
}
if(_bHelpingMyself or !helpingAlly() or (lastHelpPlayer == _playerToHelp) ) //if not helping any other ally or it's me who needs help
{
dbg("not busy helping", me);
if(haveHelpers() or _DEBUG)
{
dbg("got attackers", me);
if(allyBaseAtLoc(_playerToHelp, _x, _y)) //is he just trying to misuse us?
{
helpPlayer(_playerToHelp, _x, _y);
return TRUE;
}
else
{
dbg("ally needs no help", me);
messagePlayer(ALL_ALLIES, M_ANNOYED, MAX_PROBABILITY / 2);
}
}
else
{
messagePlayer(ALL_ALLIES, M_HELP_NO_UNITS, MAX_PROBABILITY);
}
}
else if((lastHelpPlayer >= 0) and (lastHelpPlayer < MAX_PLAYERS))
{
if(!_bHelpingMyself){
messagePlayer(ALL_ALLIES, "helping " & getPlayerName(lastHelpPlayer) & " already", MAX_PROBABILITY);
}
}
return FALSE;
}
/* Start helping player */
function void helpPlayer(int _playerToHelp, int _helpX, int _helpY)
{
local int _tTravelTime;
dbg("helping " & _playerToHelp, me);
if(_DEBUG)
debug(me & ") helpPlayer: '" & _playerToHelp);
/* Move scoutes to attackers */
groupAddGroup(attackGroup, scoutGroup);
//Calculate travel time, assume ~ 150 tiles in 4 minutes
if(attackGroup.members == 0){
_tTravelTime = (int)((float)(distBetweenTwoPoints(baseX, baseY, _helpX, _helpY) / 128 ) * 1.7);
}else{
_tTravelTime = (int)((float)(distBetweenTwoPoints(attackGroup.x, attackGroup.y, _helpX, _helpY) / 128 ) * 1.7);
}
tHelp = gameTime / 10;
tHelpTimeout = (gameTime / 10) + BASE_DEFEND_DURATION + _tTravelTime;
lastHelpPlayer = _playerToHelp;
helpX = _helpX;
helpY = _helpY;
/* Scouts and attackers go to help */
defendLocation(_helpX, _helpY, tHelpTimeout, BASE_DEFEND_RADIUS, (_playerToHelp == me));
}
/* Returns a random affirmative responce */
function string m_affirmative()
{
local int _rnd;
_rnd = random(4);
if(_rnd == 3)
{
return M_AFFIRMATIVE_ROGER;
}
return M_AFFIRMATIVE_OK;
}
/* See if there are any base structures belonging to ally at a certain location */
function bool allyBaseAtLoc(int _ally, int _x, int _y)
{
local int _structIndex;
if(_x <= 0 or _y <= 0){
return FALSE;
}
if(_ally < 0 or _ally >= MAX_PLAYERS){
return FALSE;
}
_structIndex = 0;
while(_structIndex < numBaseStruct)
{
if(numStructsByStatInRange(baseStruct[_structIndex], _x, _y, (7 * 128), me, _ally) > 0 )
{
return TRUE;
}
_structIndex++;
}
return FALSE;
}
event manageAllyHelp(manageAllyHelpTr)
{
if(helpingAlly())
{
if(canStopHelpingAlly())
{
stopHelpingAlly();
}
//else
//{
// /* See if all units we sent were killed */
// if(not haveHelpers())
// {
// if(random(4) == 0)
// {
// messagePlayer(lastHelpPlayer, M_HELPERS_KILLED);
// }
// if(not attemptToHelp(lastHelpPlayer, helpX, helpY))
// {
// dbg("NO UNITS, STOPPED HELPING", me);
// stopHelpingAlly(); //don't make sense to try again
// }
// else
// {
// dbg("SENDING REINFORCEMENTS", me);
// }
// }
//}
}
}
event everySecEv(everySec)
{
/* Check if we were helping long enough */
if(helpingAlly())
{
if(helpAllyTimeout())
{
stopHelpingAlly();
}
}
if(defendingLocation())
{
if(defendLocationTimeout())
{
stopDefendingLocation();
}
}
}
/* Do we have any units we can send to help ally ? */
function bool haveHelpers()
{
if(attackGroup.members == 0){
return FALSE;
}
return TRUE;
}
function bool helpingAlly()
{
if(lastHelpPlayer >= 0){
return TRUE;
}
return FALSE;
}
/* Returns true if we were helping long enough */
function bool helpAllyTimeout()
{
if(tHelpTimeout < (gameTime / 10) ){
return TRUE;
}
return FALSE;
}
function bool canStopHelpingAlly()
{
if(lastHelpPlayer < 0)
{
ASSERT(FALSE, "canStopHelpingAlly: lastHelpPlayer < 0", me);
return TRUE;
}
/* Were helping long enough or someone's backstabbing */
if(!friendlyPlayer(lastHelpPlayer)){
return TRUE;
}
/* Nothing to defend anymore */
if(!allyBaseAtLoc(lastHelpPlayer, helpX, helpY)){
return TRUE;
}
return FALSE;
}
function void stopHelpingAlly()
{
dbg("stopped helping", me);
tHelp = -1;
tHelpTimeout = -1;
lastHelpPlayer = -1;
helpX = -1;
helpY = -1;
stopDefendingLocation();
}
/* Send a multiplayer message to an ally */
function void messagePlayer(int _playerToMessage, string _message, int _probability)
{
local int _player;
ASSERT(_playerToMessage >= -1 && _playerToMessage < MAX_PLAYERS,
"messagePlayer: player out of bounds: " & _playerToMessage, me);
// throw the dice
if( random(MAX_PROBABILITY) >= _probability ){
return;
}
_player = 0;
if(_playerToMessage == ALL_ALLIES) //everyone
{
while(_player < MAX_PLAYERS)
{
/* Send message (allies only)) */
if(allianceExistsBetween(me, _player))
{
msg(_message, me, _player);
}
_player++;
}
}
else //a certain player
{
/* Send message (allies only)) */
if(allianceExistsBetween(me, _playerToMessage))
{
msg(_message, me, _playerToMessage);
}
}
}
function int numBitsSet(int _integer)
{
local int _position,_result;
_position = 0;
_result = 0;
while(_position < 8)
{
if(getBit(_integer, _position))
{
_result++;
}
_position++;
}
return _result;
}
/* Send a multiplayer message, addressing some player(s) */
function void messagePlayerAddressed(int _playerToMessage, int _playersToAddress, string _message)
{
local int _player,_totalAddressed,_curAddressed;
local string _adrMessage;
_totalAddressed = numBitsSet(_playersToAddress);
ASSERT(_totalAddressed > 0, "messagePlayerAddressed: no players addressed", me);
_adrMessage = " ";
_player = 0;
_curAddressed = 0;
while(_player < MAX_PLAYERS)
{
if(getBit(_playersToAddress, _player))
{
_curAddressed++;
_adrMessage = _adrMessage & getPlayerName(_player);
//if(_totalAddressed == 1){ //one only
// _adrMessage = getPlayerName(_player);
//}else
if(_totalAddressed > 1)
{
if(_curAddressed == _totalAddressed){ //end
_adrMessage = _adrMessage & " and " & getPlayerName(_player);
}else{
_adrMessage = _adrMessage & ", " & getPlayerName(_player);
}
}
}
_player++;
}
_message = _adrMessage & " " & _message;
//Now send the message to all players addressed
messagePlayer(_playerToMessage, _message, MAX_PROBABILITY);
}
/* Returns true if we can see our allies on the map */
function bool canSeeAllies()
{
local STRUCTURE _uplink;
/* Can see allies when team mode is on */
if(multiPlayerAlliancesType == ALLIANCES_TEAMS)
{
return TRUE;
}
/* Can see whole map if we have uplink */
_uplink = getStructure(uplink, me);
if(_uplink != NULLOBJECT)
{
/* Make sure finished building */
if(structureComplete(_uplink))
{
return TRUE;
}
}
return FALSE;
}
function bool defendingOwnBase()
{
if(helpingAlly() && lastHelpPlayer == me){
return TRUE;
}
return FALSE;
}
/* Call for help when our base is in danger */
event watchBaseThreat(watchBaseThreatTr)
{
/* See if we can stop defending */
if(defendingOwnBase())
{
if(numEnemiesInBase(FALSE) == 0)
{
stopHelpingAlly(); //stop defending our own base
/* Let allies know we don't need their help anymore */
messagePlayer(ALL_ALLIES, R_REPORT_SAFETY, MAX_PROBABILITY);
exit;
}
}
/* See if our base is in trouble and we need help */
if(baseInTrouble())
{
if(!defendingOwnBase()) //make sure not already defending the base
{
if(_DEBUG)
debug(me & ") watchBaseThreat: base in trouble");
dbg("watchBaseThreat: base in trouble", me);
/* Bring our forces back if needed */
if(helpingAlly())
{
stopHelpingAlly();
}
/* Defend my own base */
helpPlayer(me, baseX, baseY);
}
/* Request help once in a while */
requestHelp(baseX, baseY);
exit;
}
}
function int numAlliesInBase(bool _bVtols)
{
local int _numAllies;
_numAllies = numFriendlyWeapDroidsInRange(me, baseX, baseY, W_BASE_THREAT_RANGE, _bVtols);
_numAllies = _numAllies +
numFriendlyWeapStructsInRange(me, baseX, baseY, W_BASE_THREAT_RANGE, true) / 3;
return _numAllies;
}
function int numEnemiesInBase(bool _bVtols)
{
local int _numEnemies;
_numEnemies = numEnemyWeapDroidsInRange(me, baseX, baseY, W_BASE_THREAT_RANGE, _bVtols);
_numEnemies = _numEnemies +
numEnemyWeapStructsInRange(me, baseX, baseY, W_BASE_THREAT_RANGE, true) / 4;
return _numEnemies;
}
/* Returns true if our base is in trouble */
function bool baseInTrouble()
{
local int _enemyForce,_friendlyForce;
_friendlyForce = numAlliesInBase(FALSE);
_enemyForce = numEnemiesInBase(FALSE);
/* See if we are in trouble */
if((_enemyForce > 0) && (_enemyForce >= _friendlyForce)){
dbg("baseInTrouble: " & _enemyForce & " >= " & _friendlyForce, me);
return TRUE;
}
return FALSE;
}
/* Request help from allies */
function void requestHelp(int _helpX, int _helpY)
{
/* Don't do this too frequently */
if(tLastHelpRequest + HELP_REQUEST_INTERVAL > (gameTime / 10) ){
return;
}
doRequestHelp(_helpX, _helpY);
}
function void doRequestHelp(int _helpX, int _helpY)
{
local int _ally;
/* Remember when we requested help last time */
tLastHelpRequest = gameTime / 10;
/* Drop beacon for all allies so they would know where to help */
_ally = 0;
while(_ally < MAX_PLAYERS)
{
if(allianceExistsBetween(me, _ally)){
if(_DEBUG)
debug(me & ") requestHelp: " & _ally);
dropBeacon(getPlayerName(me), _ally, me, _helpX, _helpY, 0);
}
_ally++;
}
/* Now send message with help request */
messagePlayer(ALL_ALLIES, M_REQUEST_HELP, MAX_PROBABILITY);
}
function void defendLocation(int _defendX, int _defendY, int _tDefendTimeout, int _defendRadius, bool _bMove)
{
dbg("starting defending for " & _tDefendTimeout - (gameTime / 10) & " secs, with defend radius " & _defendRadius, me);
ASSERT(_defendRadius >= TILE,
"defendLocation: defendRadius too low:" & _defendRadius, me);
defendX = _defendX;
defendY = _defendY;
defendRadius = _defendRadius;
tDefendStart = gameTime / 10;
/* Should already include travel time */
tDefendTimeout = _tDefendTimeout;
/* See if we have to move or scout */
defendMoveType = DORDER_SCOUT;
if(_bMove){
defendMoveType = DORDER_MOVE;
}
/* Send attackers */
if(attackGroup.members > 0)
{
if(distBetweenTwoPoints(attackGroup.x, attackGroup.y, _defendX, _defendY) > _defendRadius)
{
orderGroupLoc(attackGroup, defendMoveType, _defendX, _defendY);
}
}
setEventTrigger(manageDefendLocationEv, manageDefendLocationTr);
}
function void stopDefendingLocation()
{
dbg("stopped defending location", me);
defendX = -1;
defendY = -1;
defendRadius = -1;
tDefendStart = -1;
tDefendTimeout = -1;
defendMoveType = -1;
setEventTrigger(manageDefendLocationEv, inactive);
orderGroupLoc(attackGroup, DORDER_SCOUT,baseX,baseY);
}
function bool defendingLocation()
{
if(defendX > 0 and defendY > 0){
return TRUE;
}
return FALSE;
}
event manageDefendLocationEv(inactive)
{
local DROID _droid;
dbg("manageDefendLocationEv", me);
ASSERT(defendRadius >= TILE,
"manageDefendLocationEv: defendRadius too low:" & defendRadius, me);
ASSERT(defendMoveType == DORDER_MOVE || defendMoveType == DORDER_SCOUT,
"manageDefendLocationEv: wrong move order:" & defendMoveType, me);
ASSERT(defendX > 0 && defendY > 0,
"manageDefendLocationEv: x/y coordinates:" & defendX & "/" & defendY, me);
if(!(defendX > 0 && defendY > 0)){
dbg("not defending???????", me);
exit; //not defending?
}
/* Collect attackers */
initIterateGroup(attackGroup);
_droid = iterateGroup(attackGroup);
while(_droid != NULLOBJECT)
{
if(distBetweenTwoPoints(_droid.x,_droid.y,defendX,defendY) > defendRadius) //too far from defend location
{
if(distBetweenTwoPoints(_droid.orderx,_droid.ordery,defendX,defendY) > defendRadius) //not already on its way to the defend location
{
orderDroidLoc(_droid, defendMoveType, defendX, defendY);
}
}
_droid = iterateGroup(attackGroup);
}
}
function bool defendLocationTimeout()
{
if(tDefendTimeout < (gameTime / 10) ){
return TRUE;
}
return FALSE;
}
/* Returns true if player in question is my ally or if it's me */
function bool friendlyPlayer(int _playerToCheck)
{
if(allianceExistsBetween(_playerToCheck, me) or (_playerToCheck == me)){
return TRUE;
}
return FALSE;
}
function bool insideBase(int _x, int _y)
{
if(_x < minx){
return FALSE;
}
if(_x > maxx){
return FALSE;
}
if(_y < miny){
return FALSE;
}
if(_y > maxy){
return FALSE;
}
return TRUE;
}
function FEATURE closestOil(int _x, int _y, bool _bAvoidThreat)
{
local FEATURE _oil,_closestOil;
local int _bestDist,_newDist;
_bestDist = 99999;
_closestOil = NULLOBJECT;
initGetFeature(oilRes,me,me);
_oil = getFeatureB(me);
while(_oil != NULLOBJECT)
{
_newDist = distBetweenTwoPoints(_x, _y, _oil.x, _oil.y);
if(_newDist < _bestDist) //this one is closer
{
if( !(_bAvoidThreat && threatInRange(me, _oil.x, _oil.y, OIL_THREAT_RANGE, FALSE)) )
{
_bestDist = _newDist;
_closestOil = _oil;
}
}
_oil = getFeatureB(me);
}
return _closestOil;
}
event keyPressed(CALL_KEY_PRESSED, ref count, ref count2)
{
if(count == KEY_P and (count2 == KEY_RCTRL or count2 == KEY_LCTRL))
{
if(_DEBUG)
{
addPower(1000, me);
console("add power");
}
}
}
event watchMenu(everySec)
{
if(_DEBUG)
{
if(vtolGrAttackObj[0] == NULLOBJECT){
setDebugMenuEntry("0 - " & vtolAttackGr[0].members, 0);
}else{
setDebugMenuEntry("0 " & vtolAttackGr[0].members & " - " & (vtolGrAttackObj[0].x / TILE) & "-" & (vtolGrAttackObj[0].y / TILE), 0);
}
if(vtolGrAttackObj[1] == NULLOBJECT){
setDebugMenuEntry("1 - " & vtolAttackGr[1].members, 1);
}else{
setDebugMenuEntry("1 " & vtolAttackGr[1].members & " - " & (vtolGrAttackObj[1].x / TILE) & "-" & (vtolGrAttackObj[1].y / TILE), 1);
}
if(vtolGrAttackObj[2] == NULLOBJECT){
setDebugMenuEntry("2 - " & vtolAttackGr[2].members, 2);
}else{
setDebugMenuEntry("2 " & vtolAttackGr[2].members & " - " & (vtolGrAttackObj[2].x / TILE) & "-" & (vtolGrAttackObj[2].y / TILE), 2);
}
if(vtolGrAttackObj[3] == NULLOBJECT){
setDebugMenuEntry("3 - " & vtolAttackGr[3].members, 3);
}else{
setDebugMenuEntry("3 " & vtolAttackGr[3].members & " - " & (vtolGrAttackObj[3].x / TILE) & "-" & (vtolGrAttackObj[3].y / TILE), 3);
}
if(vtolGrAttackObj[4] == NULLOBJECT){
setDebugMenuEntry("4 - " & vtolAttackGr[4].members, 4);
}else{
setDebugMenuEntry("4 " & vtolAttackGr[4].members & " - " & (vtolGrAttackObj[4].x / TILE) & "-" & (vtolGrAttackObj[4].y / TILE), 4);
}
if(vtolGrAttackObj[5] == NULLOBJECT){
setDebugMenuEntry("5 - " & vtolAttackGr[5].members, 5);
}else{
setDebugMenuEntry("5 " & vtolAttackGr[5].members & " - " & (vtolGrAttackObj[5].x / TILE) & "-" & (vtolGrAttackObj[5].y / TILE), 5);
}
/*
setDebugMenuEntry("total " & countRebuildStruct, 0);
setDebugMenuEntry("x1=" & rebuildStructX[0], 1);
setDebugMenuEntry("y1=" & rebuildStructY[0], 2);
setDebugMenuEntry("x2=" & rebuildStructX[1], 3);
setDebugMenuEntry("y2=" & rebuildStructY[1], 4);
setDebugMenuEntry("x3=" & rebuildStructX[2], 5);
setDebugMenuEntry("y3=" & rebuildStructY[2], 6);
setDebugMenuEntry("x4=" & rebuildStructX[3], 7);
setDebugMenuEntry("y4=" & rebuildStructY[3], 8);*/
}
}
//---------------------------------------------------------------
//Returns how many droids are already on the way to build the
//same structure on the same spot (like helpbuild)
//---------------------------------------------------------------
function int numBuildSameBuilding(STRUCTURESTAT _checkStat, int _x, int _y)
{
local int _numSameBuilding;
local DROID _truck;
_numSameBuilding = 0;
initIterateGroup(buildGroup);
_truck = iterateGroup(buildGroup);
while(_truck != NULLOBJECT)
{
if((_truck.order == DORDER_BUILD) or (_truck.order == DORDER_HELPBUILD) or (_truck.order == DORDER_LINEBUILD))
{
if((_checkStat == NULLSTRUCTURESTAT) or (_truck.stat == _checkStat)) //Same struct type
{
//Within some range
if((_x < 0) or (distBetweenTwoPoints(_x, _y, _truck.orderx , _truck.ordery) <= TILE))
{
_numSameBuilding++;
}
}
}
_truck = iterateGroup(buildGroup);
}
return _numSameBuilding;
}
// returns number of droids in a certain group with the same order
function int numGroupSameOrder(GROUP _group, int _orderIndex)
{
local int _numDroids;
local DROID _droid;
_numDroids = 0;
initIterateGroup(_group);
_droid = iterateGroup(_group);
while(_droid != NULLOBJECT)
{
if(_droid.order == _orderIndex) //right order type
{
_numDroids++;
}
_droid = iterateGroup(_group);
}
return _numDroids;
}
// Remember certain destroyed structures so we can rebuild them later
event structureDestroyed(structureDestroyedTr)
{
local int _count;
// add certain structures to the rebuild list
_count = 0;
while(_count < numRebuildStat[curTech])
{
if(structure.stat == rebuildStat[curTech][_count])
{
if(countRebuildStruct < MAX_REBUILD_STRUCT)
{
rebuildStructX[countRebuildStruct] = structure.x;
rebuildStructY[countRebuildStruct] = structure.y;
rebuildStructStat[countRebuildStruct] = structure.stat;
countRebuildStruct++;
dbg("remembered structure (" & countRebuildStruct & ") - " & structure.x & "/" & structure.y, me);
exit;
}
}
_count++;
}
}
// Rebuild structures that were destroyed
event rebuildStructureEv(rebuildStructureTr)
{
rebuildStructures();
}
function void rebuildStructures()
{
local int _count,_threatRange,_x,_y;
local DROID _truck;
local STRUCTURESTAT _stat;
_threatRange = (TILE * 8);
_count = 0;
while(_count < countRebuildStruct)
{
if(!threatInRange(me, rebuildStructX[_count], rebuildStructY[_count], _threatRange, FALSE))
{
if(getTileStructure(_x / TILE, _y / TILE) == NULLOBJECT)
{
_stat = rebuildStructStat[_count];
_x = rebuildStructX[_count];
_y = rebuildStructY[_count];
_truck = closestIdleTruck(_x, _y);
if(_truck != NULLOBJECT)
{
if(numBuildSameBuilding(_stat, _x, _y) == 0) //make sure no one is building already
{
buildOnExactLocation(_truck, _x, _y, _stat);
}
}
}
}
_count++;
}
}
function void buildOnExactLocation(DROID _truck, int _x, int _y, STRUCTURESTAT _stat)
{
local int _newX,_newY;
if(_truck == NULLOBJECT)
{
return;
}
_newX = _x;
_newY = _y;
if(pickStructLocationB(_stat, ref _newX, ref _newY, me, -1))
{
dbg("trying to rebuild on (" & _newX & "/" & _newY & ")", me);
if((_x != _newX) or (_y != _newY))
{
return;
}
orderDroidStatsLoc(_truck, DORDER_BUILD, _stat, _x, _y);
//dbg("rebuilding structure after!! (" & _x & "/" & _y & ")", me);
}
}
// Get idle truck closest to some location
function DROID closestIdleTruck(int _x, int _y)
{
local DROID _closestTruck, _tempTruck;
local int _closestDist, _tempDist;
_closestTruck = NULLOBJECT;
_closestDist = 99999;
initIterateGroup(buildGroup);
_tempTruck = iterateGroup(buildGroup);
while(_tempTruck != NULLOBJECT)
{
if((_tempTruck.order == DORDER_NONE) or (_tempTruck.order == DORDER_RTB))
{
_tempDist = distBetweenTwoPoints(_x, _y, _tempTruck.x, _tempTruck.y);
if(_tempDist < _closestDist)
{
_closestDist = _tempDist;
_closestTruck = _tempTruck;
}
}
_tempTruck = iterateGroup(buildGroup);
}
return _closestTruck;
}
event consoleEv(consoleTr)
{
//turn on 'autogame'
if(message == "autogame on" && (sender == me))
{
if(debugModeEnabled())
{
if(myResponsibility(me))
{
if(not bRunning) //make sure current machine is responsible for this AI and it's not already active
{
console(getPlayerName(me) & " is active");
reassignAI();
}
}
}
}
//turn off 'autogames'
if(message == "autogame off" && debugModeEnabled() && (sender == me))
{
if(bRunning) //make sure this AI is active
{
console(getPlayerName(me) & " is deactivated");
shutDownAI();
}
}
if(message == "aidebug on")
{
console(getPlayerName(me) & " ai debug is on");
_DEBUG = TRUE;
dbgMsgOn(me, _DEBUG);
}
else if(message == "aidebug off")
{
console(getPlayerName(me) & " ai debug is off");
_DEBUG = FALSE;
dbgMsgOn(me, _DEBUG);
}
}