4187 lines
101 KiB
Plaintext
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);
|
|
}
|
|
}
|