//============================================================================== // File: aiNodeCaptureCore.xs // Author: Matt Craver // // Tower Creep AI //============================================================================== extern vector gMainBaseLocation = cInvalidVector; extern int gUnitSchedule = -1; extern int gUnitScheduleIndex = 0; extern int gUnitScheduleCount = -1; extern int gCurrentUnit = -1; extern int gInfantryUnit = -1; extern int gArcheryUnit = -1; extern int gCavalryUnit = -1; extern int gMaintainPlanID = -1; // victory node arrays extern int gVictoryNodeUnitType = cUnitTypeUnitTypeCityDefenseVictoryNode; extern int gResourceNodeUnitType = cUnitTypeUnitTypeCityDefenseResourceNode; extern int gVictoryNodeCount = 0; extern int gResourceNodeCount = 0; extern int gHeroPlanID = -1; extern int gNodeCount = 0; extern int gNodeLocations = -1; extern int gNodeOwners = -1; extern int gNodeUnitIDs = -1; extern int gNodePlanIDs = -1; extern int gDefensePlanSize = 2; extern int gAttackPlanSize = 4; extern int gUnitQuery = -1; extern int gEnemyQuery = -1; extern int gBonusPlanSize = 0; extern int gDelayedHeroPlanIndex = -1; extern int gPlanInterval = 10; extern int gTimeSinceLastPlan = -60000; extern int gHeroUpdateInterval = 10; extern int gTimeSinceLastHeroUpdate = -60000; extern bool gEarlyGameGoFast = true; extern int gPopBonus = 0; extern int gUpdateTimeBonus = 0; extern int gLandReservePlan = -1; extern int gSpawnedUnitPlan = -1; extern bool gSendHeroWithSpawnedUnits = false; extern const int cEarlyGame = 0; extern const int cMidGame = 1; extern const int cLateGame = 2; extern int gGameState = cEarlyGame; extern int gHumanScore = 0; extern int gAIScore = 0; extern int gPointsToWin = -1; extern bool gIsFlipMode = false; extern vector gOffLimitsLocation = cInvalidVector; extern int gNodeIDToIgnore = -1; extern bool gNeedToEnableAI = false; extern vector gBoatDropOffLocation = cInvalidVector; extern int gTimeSinceAbility1Fired = -60000; extern int gTimeSinceAbility2Fired = -60000; extern int gAbility1Cooldown = 40; extern int gAbility2Cooldown = 60; extern int gHeroQuery = -1; // UNIT SCHEDULE RM FUNCS //============================================================================== void initializeUnitSchedule(int count = -1) { gUnitSchedule = xsArrayCreateInt(count, -1, "Unit Schedule"); gUnitScheduleCount = count; } //============================================================================== void addUnitToSchedule(int id = -1) { xsArraySetInt(gUnitSchedule, gUnitScheduleIndex, id); gUnitScheduleIndex = gUnitScheduleIndex + 1; } //============================================================================== void activateCoopStuff(int unused = -1) { gDefensePlanSize = 5; gAttackPlanSize = 8; gPopBonus = 15; kbAdjustResourceByID(cMyID, cResourceGold, 250); } //============================================================================== void setFlipMode(int flip = -1) { if(flip == 0) { gIsFlipMode = false; } else { gIsFlipMode = true; gAttackPlanSize = 6; xsSetRuleMinInterval("increasePlanSizes", 180); } } //============================================================================== void setPointsToWin(int points = 0) { gPointsToWin = points; } //============================================================================== void increaseHumanScore(int unused = -1) { gHumanScore = gHumanScore + 1; } //============================================================================== void increaseAIScore(int unused = -1) { gAIScore = gAIScore + 1; } //============================================================================== void setSendHeroWithSpawnedAttack(int send = -1) { if(send == 0) { gSendHeroWithSpawnedUnits = false; } else { gSendHeroWithSpawnedUnits = true; } } //============================================================================== void setNodeIDToIgnore(int id = -1) { gNodeIDToIgnore = id; } //============================================================================== void enableWhenHumanCapturesNode(int unused = -1) { xsDisableRule("planUpdateLoop"); xsDisableRule("disableEarlyGame"); xsDisableRule("emergencyGold"); gNeedToEnableAI = true; } //============================================================================== // heroMovement // // Gets activated to delay hero movement, to give an attack a bit of a head start. //============================================================================== rule heroMovement inactive minInterval 5 { if(xsGetTime() > gTimeSinceLastHeroUpdate + (gHeroUpdateInterval * 1000)) { if(gHeroPlanID == -1) { gHeroPlanID = aiPlanCreate("Hero Plan", cPlanDefend); aiPlanSetVariableFloat(gHeroPlanID, cDefendPlanEngageRange, 0, 10); aiPlanSetVariableFloat(gHeroPlanID, cDefendPlanGatherDistance, 0, 20); if(gIsFlipMode == true) { aiPlanAddUnitType(gHeroPlanID, kbFindUnit(cUnitTypeNPCNorthernHero01), 1, 1, 1); } else { aiPlanAddUnitType(gHeroPlanID, kbFindUnit(cUnitTypeNPCNorthernHero02), 1, 1, 1); } aiPlanAddUnitType(gHeroPlanID, gInfantryUnit, 2, 2, 2); aiPlanSetRequiresAllNeedUnits(gHeroPlanID, true); aiPlanSetDesiredPriority(gHeroPlanID, 100); aiPlanSetAllowHandleDamage(gHeroPlanID, false); aiPlanSetAllowUnderAttackResponse(gHeroPlanID, false); aiPlanSetActive(gHeroPlanID); } aiPlanSetVariableVector(gHeroPlanID, cDefendPlanDefendPoint, 0, xsArrayGetVector(gNodeLocations, gDelayedHeroPlanIndex)); gTimeSinceLastHeroUpdate = xsGetTime(); xsEnableRule("forceHeroMove"); } xsDisableSelf(); } rule forceHeroMove inactive minInterval 20 { if(gHeroQuery == -1) { gHeroQuery = kbUnitQueryCreate("Hero Query"); kbUnitQuerySetPlayerID(gHeroQuery, cMyID); kbUnitQuerySetUnitType(gHeroQuery, cUnitTypeUnitTypeScout1); kbUnitQuerySetState(gHeroQuery, cUnitStateAlive); } kbUnitQueryResetResults(gHeroQuery); int heroCount = kbUnitQueryExecute(gHeroQuery); if(heroCount > 0) { int heroID = kbUnitQueryGetResult(gHeroQuery, 0); vector heroLoc = kbUnitGetPosition(heroID); vector dest = aiPlanGetVariableVector(gHeroPlanID, cDefendPlanDefendPoint, 0); float dist = xsVectorLength(heroLoc - dest); aiEcho("AntiLeash: Hero distance from desired node " + dist); if(dist > 50) { kbUnitQuerySetPosition(gEnemyQuery, heroLoc); kbUnitQuerySetMaximumDistance(gEnemyQuery, 20); kbUnitQueryResetResults(gEnemyQuery); int enemyCount = kbUnitQueryExecute(gEnemyQuery); aiEcho("AntiLeash: Enemies around hero " + enemyCount); if(enemyCount > 0) { aiEcho("AntiLeash: leashing detected, sending supplies"); aiTaskUnitMove(heroID, dest); kbAdjustResourceByID(cMyID, cResourceWood, 1); } } } } //============================================================================== // sendHeroToLoc // // Hero now has his own plan. This function will create it if it doesn't exist // and then set the location. //============================================================================== void sendHeroToLoc(int index = -1) { gDelayedHeroPlanIndex = index; xsEnableRule("heroMovement"); } //============================================================================== // getClosestUncontrolledNode // // Find the node closest to our main base that we don't currently own. //============================================================================== int getClosestUncontrolledNode(bool resourceOnly = false, int specificPlayerID = -1, int maxUnits = -1, vector alternativeLocation = cInvalidVector) { int closestID = -1; float closestDistance = 555.0; for(i = 0; < gNodeCount) { // we only want resources if(resourceOnly == true && i < gVictoryNodeCount) continue; // we own the node or the specific player doesn't own it if(xsArrayGetInt(gNodeOwners, i) == cMyID || (specificPlayerID != -1 && xsArrayGetInt(gNodeOwners, i) != specificPlayerID)) continue; // we are supposed to ignore this node (likely the human's starting resource) if(gNodeIDToIgnore == xsArrayGetInt(gNodeUnitIDs, i)) continue; // check for units around the node if(maxUnits != -1) { kbUnitQuerySetPosition(gEnemyQuery, xsArrayGetVector(gNodeLocations, i)); kbUnitQuerySetMaximumDistance(gEnemyQuery, 45); kbUnitQueryResetResults(gEnemyQuery); int count = kbUnitQueryExecute(gEnemyQuery); if(count > maxUnits) { continue; } } // we already have a plan int plan = xsArrayGetInt(gNodePlanIDs, i); if(plan != -1 && aiPlanGetActive(plan) == true) continue; // use a specific location instead of base location if we have one vector location = gMainBaseLocation; if(alternativeLocation != cInvalidVector) { location = alternativeLocation; } // finally get the distance float dist = xsVectorLength(location - xsArrayGetVector(gNodeLocations, i)); if(dist < closestDistance) { closestID = i; closestDistance = dist; } } return (closestID); } //============================================================================== // startUsingSpawnedUnits // // Use units that were spawned via RM. //============================================================================== void startUsingSpawnedUnits(int armyID = -1) { gSpawnedUnitPlan = aiPlanCreate("Spawned Units", cPlanDefend); aiPlanSetUnitStance(gSpawnedUnitPlan, cUnitStanceDefensive); aiPlanSetVariableFloat(gSpawnedUnitPlan, cDefendPlanEngageRange, 0, 10); aiPlanSetVariableFloat(gSpawnedUnitPlan, cDefendPlanGatherDistance, 0, 20); int armyCount = kbGetUnitCountInArmy(armyID); aiPlanAddUnitType(gSpawnedUnitPlan, cUnitTypeMilitary, armyCount, armyCount, armyCount); for(i = 0; < armyCount) { aiPlanAddUnit(gSpawnedUnitPlan, kbGetUnitInArmy(armyID, i)); } int nodeID = getClosestUncontrolledNode(false, 4); aiPlanSetVariableVector(gSpawnedUnitPlan, cDefendPlanDefendPoint, 0, xsArrayGetVector(gNodeLocations, nodeID)); aiPlanSetDesiredPriority(gSpawnedUnitPlan, 100); aiPlanSetNoMoreUnits(gSpawnedUnitPlan, true); aiPlanSetActive(gSpawnedUnitPlan); if(gSendHeroWithSpawnedUnits == true) { sendHeroToLoc(nodeID); } } //============================================================================== // stopUsingSpawnedUnits // // Called via RM. Kill the plan and reset the variable. //============================================================================== void stopUsingSpawnedUnits(int unused = -1) { aiPlanSetActive(gSpawnedUnitPlan, false); gSpawnedUnitPlan = -1; } //============================================================================== // updateSpawnedArmy //============================================================================== rule updateSpawnedArmy active minInterval 10 { if(gSpawnedUnitPlan == -1 || aiPlanGetActive(gSpawnedUnitPlan) == false) { return; } vector point = aiPlanGetVariableVector(gSpawnedUnitPlan, cDefendPlanDefendPoint, 0); kbUnitQuerySetPosition(gEnemyQuery, point); kbUnitQueryResetResults(gEnemyQuery); int count = kbUnitQueryExecute(gEnemyQuery); // move to a new node if there are no enemies near the current node. if(count == 0) { int nodeID = getClosestUncontrolledNode(false, 4); if(nodeID != -1) { aiPlanSetVariableVector(gSpawnedUnitPlan, cDefendPlanDefendPoint, 0, xsArrayGetVector(gNodeLocations, nodeID)); if(gSendHeroWithSpawnedUnits == true) { sendHeroToLoc(nodeID); } } } } //============================================================================== // getUnitType // // Find the unit type we have hte most of that aren't tied up in existing plans. //============================================================================== int getUnitType(bool returnBest = true) { // make the query if(gUnitQuery == -1) { gUnitQuery = kbUnitQueryCreate("Best Unit Query"); kbUnitQuerySetPlayerID(gUnitQuery, cMyID); kbUnitQuerySetUnitType(gUnitQuery, cUnitTypeUnit); kbUnitQuerySetState(gUnitQuery, cUnitStateAlive); } int infCount = 0; int arcCount = 0; int cavCount = 0; kbUnitQueryResetResults(gUnitQuery); int count = kbUnitQueryExecute(gUnitQuery); for(i = 0; < count) { int id = kbUnitQueryGetResult(gUnitQuery, i); // make sure this unit is in the the land reserve plan if(kbUnitGetPlanID(id) == gLandReservePlan) { if(kbUnitIsType(id, cUnitTypeAbstractInfantry)) { infCount = infCount + 1; } else if(kbUnitIsType(id, cUnitTypeAbstractArcher)) { arcCount = arcCount + 1; } else if(kbUnitIsType(id, cUnitTypeAbstractCavalry)) { cavCount = cavCount + 1; } } } // add some bonus to the type we are currently training. if(gCurrentUnit == gInfantryUnit) { infCount = infCount + 2; } else if(gCurrentUnit == gArcheryUnit) { arcCount = arcCount + 2; } else if(gCurrentUnit == gCavalryUnit) { cavCount = cavCount + 2; } aiEcho("Reserve Units: Inf " + infCount + ", Arc " + arcCount + ", Cav " + cavCount); // return the unit type of the highest count if(infCount >= arcCount && infCount >= cavCount) { return(gInfantryUnit); } else if(arcCount >= infCount && arcCount >= cavCount) { return(gArcheryUnit); } else { return(gCavalryUnit); } // xs compiler ^___________^ return(-1); } //============================================================================== // updateScheduledUnit // // Update the unit we should be training. //============================================================================== rule updateScheduledUnit active minInterval 90 { if(gUnitScheduleIndex >= gUnitScheduleCount) { gUnitScheduleIndex = 0; } int id = xsArrayGetInt(gUnitSchedule, gUnitScheduleIndex); switch(id) { case 0: { aiEcho("updateScheduledUnit: Infantry"); gCurrentUnit = gInfantryUnit; break; } case 1: { aiEcho("updateScheduledUnit: Ranged"); gCurrentUnit = gArcheryUnit; break; } case 2: { aiEcho("updateScheduledUnit: Cavalry"); gCurrentUnit = gCavalryUnit; break; } } // if our maintain plan exists, change the unit type if(gMaintainPlanID != -1) { aiPlanSetVariableInt(gMaintainPlanID, cTrainPlanUnitType, 0, gCurrentUnit); } gUnitScheduleIndex = gUnitScheduleIndex + 1; } //============================================================================== // createUnitMaintainPlan //============================================================================== void createUnitMaintainPlan(int unused = -1) { gMaintainPlanID = aiPlanCreate("Maintain Plan", cPlanTrain); aiPlanSetVariableInt(gMaintainPlanID, cTrainPlanNumberToMaintain, 0, 50); aiPlanSetVariableInt(gMaintainPlanID, cTrainPlanBatchSize, 0, 1); aiPlanSetEscrowID(gMaintainPlanID, cEmergencyEscrowID); aiPlanSetActive(gMaintainPlanID); aiPlanSetVariableVector(gMaintainPlanID, cTrainPlanGatherPoint, 0, gMainBaseLocation - vector(15,0,0)); // calling this function will set the unit to train for the plan updateScheduledUnit(); } //============================================================================== // createDefensePlan //============================================================================== void createDefensePlan(int index = -1, int forceUnit = -1, int forceSize = -1) { if(index == -1) return(); int existingPlan = xsArrayGetInt(gNodePlanIDs, index); // make sure we don't already have a defense plan at this location if(existingPlan == -1 || aiPlanGetActive(existingPlan) == false) { int plan = aiPlanCreate("Node Defense " + index, cPlanDefend); xsArraySetInt(gNodePlanIDs, index, plan); aiPlanSetUnitStance(plan, cUnitStanceDefensive); aiPlanSetVariableFloat(plan, cDefendPlanEngageRange, 0, 10); aiPlanSetVariableFloat(plan, cDefendPlanGatherDistance, 0, 20); int size = gDefensePlanSize + gBonusPlanSize; if(forceSize != -1) { size = forceSize; } int unitType = getUnitType(); if(forceUnit != -1) { unitType = forceUnit; } aiPlanAddUnitType(plan, unitType, size, size, size); sendHeroToLoc(index); aiPlanSetVariableVector(plan, cDefendPlanDefendPoint, 0, xsArrayGetVector(gNodeLocations, index)); aiPlanSetRequiresAllNeedUnits(plan, true); aiPlanSetDesiredPriority(plan, 100); aiPlanAddUserVariableInt(plan, 0, "Time", 1); aiPlanSetUserVariableInt(plan, 0, 0, 0); aiPlanSetActive(plan); aiEcho("Defending at " + xsArrayGetVector(gNodeLocations, index)); } } //============================================================================== // createAttackPlan //============================================================================== void createAttackPlan(int index = -1, int forceUnit = -1, int forceSize = -1) { if(index == -1) return(); int plan = aiPlanCreate("Node Attack " + index, cPlanAttack); int size = gAttackPlanSize + gBonusPlanSize; if(forceSize != -1) { size = forceSize; } int unitType = getUnitType(); if(forceUnit != -1) { unitType = forceUnit; } aiPlanAddUnitType(plan, unitType, size, size, size + 1); aiPlanSetVariableVector(plan, cAttackPlanAttackPoint, 0, xsArrayGetVector(gNodeLocations, index)); aiPlanSetVariableInt(plan, cAttackPlanPlayerID, 0, 1); aiPlanSetDesiredPriority(plan, 75); aiPlanSetRequiresAllNeedUnits(plan, true); aiPlanAddUserVariableInt(plan, 0, "Time", 1); aiPlanSetUserVariableInt(plan, 0, 0, 0); aiPlanSetActive(plan); aiEcho("Attacking at " + xsArrayGetVector(gNodeLocations, index)); } //============================================================================== // initializeNodeArrays // // Create the arrays that track information about each node. //============================================================================== void initializeNodeArrays() { // create a query to find the victory nodes int victoryQuery = kbUnitQueryCreate("Victory Query"); kbUnitQuerySetPlayerRelation(victoryQuery, cPlayerRelationAny); kbUnitQuerySetUnitType(victoryQuery, gVictoryNodeUnitType); kbUnitQuerySetState(victoryQuery, cUnitStateAlive); kbUnitQueryResetResults(victoryQuery); gVictoryNodeCount = kbUnitQueryExecute(victoryQuery); // query for the resource nodes int resourceQuery = kbUnitQueryCreate("Resource Query"); kbUnitQuerySetPlayerRelation(resourceQuery, cPlayerRelationAny); kbUnitQuerySetUnitType(resourceQuery, gResourceNodeUnitType); kbUnitQuerySetState(resourceQuery, cUnitStateAlive); kbUnitQueryResetResults(resourceQuery); gResourceNodeCount = kbUnitQueryExecute(resourceQuery); gNodeCount = gVictoryNodeCount + gResourceNodeCount; gNodeLocations = xsArrayCreateVector(gNodeCount, cInvalidVector, "Node Locations"); gNodeOwners = xsArrayCreateInt(gNodeCount, -1, "Node Owners"); gNodeUnitIDs = xsArrayCreateInt(gNodeCount, -1, "Node Unit IDs"); gNodePlanIDs = xsArrayCreateInt(gNodeCount, -1, "Node Plan IDs"); int id = -1; for(i = 0; < gVictoryNodeCount) { id = kbUnitQueryGetResult(victoryQuery, i); // switching the queries to be PlayerRelationAny caused some weird results, so we need to break // as soon as we come across a bad ID if(id == -1) { gVictoryNodeCount = i; break; } xsArraySetInt(gNodeUnitIDs, i, id); xsArraySetInt(gNodeOwners, i, kbUnitGetPlayerID(id)); xsArraySetVector(gNodeLocations, i, kbUnitGetPosition(id)); aiEcho("VICTORY NODE: " + id + " " + kbUnitGetPosition(id)); } // using one array for both victory and resource nodes, so we have to start adding into the array // where the victory nodes left off for(i = 0; < gResourceNodeCount) { id = kbUnitQueryGetResult(resourceQuery, i); // switching the queries to be PlayerRelationAny caused some weird results, so we need to break // as soon as we come across a bad ID if(id == -1) { gResourceNodeCount = i; break; } xsArraySetInt(gNodeUnitIDs, gVictoryNodeCount + i, id); xsArraySetInt(gNodeOwners, gVictoryNodeCount + i, kbUnitGetPlayerID(id)); xsArraySetVector(gNodeLocations, gVictoryNodeCount + i, kbUnitGetPosition(id)); aiEcho("RESOURCE NODE: " + id + " " + kbUnitGetPosition(id)); } } void killPlan(int index = -1) { aiEcho("killPlan: destroying plan at node " + index); aiPlanDestroy(xsArrayGetInt(gNodePlanIDs, index)); xsArraySetInt(gNodePlanIDs, index, -1); } //============================================================================== // updateNodeData // // Check which player owns each node. //============================================================================== rule updateNodeData active minInterval 5 { int id = -1; int prevOwner = -1; int currOwner = -1; for(i = 0; < gNodeCount) { id = xsArrayGetInt(gNodeUnitIDs, i); prevOwner = xsArrayGetInt(gNodeOwners, i); currOwner = kbUnitGetPlayerID(id); xsArraySetInt(gNodeOwners, i, currOwner); string nodeType = "VICTORY"; if(i >= gVictoryNodeCount) { nodeType = "RESOURCE"; } // check if ownership has changed if(prevOwner != currOwner) { aiEcho(nodeType + " NODE " + id + ": previous owner " + prevOwner + ", new owner " + currOwner); if(i < gVictoryNodeCount && prevOwner == cMyID) { // they took our victory node aiEcho("LET'S TAKE IT BACK"); killPlan(i); createAttackPlan(i); } // human took a resource node, time to start if(gNeedToEnableAI == true && currOwner == 4) { xsEnableRule("planUpdateLoop"); xsEnableRule("disableEarlyGame"); xsEnableRule("emergencyGold"); gNeedToEnableAI = false; } } } } //============================================================================== // increasePlanSizes //============================================================================== rule increasePlanSizes active minInterval 240 { gDefensePlanSize = gDefensePlanSize + 1; gAttackPlanSize = gAttackPlanSize + 2; } //============================================================================== // cleanUpDefensePlan //============================================================================== rule cleanUpDefensePlans active minInterval 10 { for(i = 0; < gNodeCount) { int plan = xsArrayGetInt(gNodePlanIDs, i); if(plan != -1) { int time = aiPlanGetUserVariableInt(plan, 0, 0); if(time >= 180) { killPlan(i); } else { aiPlanSetUserVariableInt(plan, 0, 0, time + 10); } } } } //============================================================================== // getNumberResourceNodes // // Instead of actually checking ownership, we check for Plan IDs. //============================================================================== int getNumberResourceNodes() { int resourceCount = 0; for(i = gVictoryNodeCount; < gNodeCount) { if(xsArrayGetInt(gNodePlanIDs, i) != -1 || xsArrayGetInt(gNodeOwners, i) == cMyID) { resourceCount = resourceCount + 1; } } return(resourceCount); } rule disableEarlyGame active minInterval 60 { gEarlyGameGoFast = false; // bring the defense plan size back up gDefensePlanSize = 4; gHeroUpdateInterval = 30; xsDisableSelf(); } rule planUpdateLoop active minInterval 6 { if(gEnemyQuery == -1) { gEnemyQuery = kbUnitQueryCreate("Victory Query"); kbUnitQuerySetPlayerRelation(gEnemyQuery, cPlayerRelationEnemyNotGaia); kbUnitQuerySetUnitType(gEnemyQuery, cUnitTypeUnit); kbUnitQuerySetState(gEnemyQuery, cUnitStateAlive); } if(xsGetTime() > gTimeSinceLastPlan + (gPlanInterval * 1000)) { bool needResourceNodes = false; if(getNumberResourceNodes() < 2) { needResourceNodes = true; } int node = getClosestUncontrolledNode(needResourceNodes, -1); if(node != -1) { kbUnitQuerySetPosition(gEnemyQuery, xsArrayGetVector(gNodeLocations, node)); kbUnitQuerySetMaximumDistance(gEnemyQuery, 30); kbUnitQueryResetResults(gEnemyQuery); int count = kbUnitQueryExecute(gEnemyQuery); aiEcho("Nearby enemies: " + count); if(count > gDefensePlanSize - 3 && gEarlyGameGoFast == false) { createAttackPlan(node); int bonusNode = getClosestUncontrolledNode(false, -1, 0); if(bonusNode != -1) { aiEcho("BONUS: Sending Hero to take " + xsArrayGetVector(gNodeLocations, bonusNode)); sendHeroToLoc(bonusNode); } else { sendHeroToLoc(node); } } else { createDefensePlan(node); } gTimeSinceLastPlan = xsGetTime(); } else { aiEcho("planUpdateLoop: Tried to find node and couldn't!!!!!!!!!!!!!!!!!!"); } } if(needResourceNodes == true || gEarlyGameGoFast == true) { gPlanInterval = 30; } } //============================================================================== // activityAttacks //============================================================================== rule activityAttacks active minInterval 60 { if(xsGetTime() < 180 * 1000 || gGameState == cEarlyGame) return; int node = getClosestUncontrolledNode(false, 4); if(node != -1) { int plan = aiPlanCreate("Mini Attack", cPlanAttack); int size = 2; if(gGameState == cLateGame) { size = size + 1; } if(gIsFlipMode == true) { size = size + 1; } aiPlanAddUnitType(plan, getUnitType(), size, size, size); aiPlanSetVariableVector(plan, cAttackPlanAttackPoint, 0, xsArrayGetVector(gNodeLocations, node)); aiPlanSetVariableInt(plan, cAttackPlanPlayerID, 0, 1); aiPlanSetDesiredPriority(plan, 75); aiPlanSetRequiresAllNeedUnits(plan, true); aiPlanSetActive(plan); aiEcho("Mini-attack at " + xsArrayGetVector(gNodeLocations, node)); } } //============================================================================== // emergencyGold // // If we are low on resource nodes, give some gold. //============================================================================== rule emergencyGold active minInterval 30 { int nodes = getNumberResourceNodes(); if(nodes == 1) { kbAdjustResourceByID(cMyID, cResourceGold, 50); } else if(nodes == 0) { kbAdjustResourceByID(cMyID, cResourceGold, 100); } } //============================================================================== // updateGameState // // Check the scores and update the stage of the game. //============================================================================== rule updateGameState active minInterval 13 { if(gHumanScore + gAIScore >= (gPointsToWin * 2) - 20) { gGameState = cLateGame; } else if(gHumanScore + gAIScore >= 20) { gGameState = cMidGame; } else { gGameState = cEarlyGame; } aiEcho("updateAdaptiveFactors: Human Score: " + gHumanScore + ", AI Score: " + gAIScore); } //============================================================================== // updateAdaptiveFactors // // Adjust how well the AI plays based on number of controlled nodes. //============================================================================== rule updateAdaptiveFactors active minInterval 15 { if(gEarlyGameGoFast == true) return; int humanNodes = 0; int ourNodes = 0; for(i = 0; < gNodeCount) { if(xsArrayGetInt(gNodeOwners, i) == 4) { humanNodes = humanNodes + 1; } else if(xsArrayGetInt(gNodeOwners, i) == cMyID) { ourNodes = ourNodes + 1; } } int nodeDiff = ourNodes - humanNodes; // positive diff means the AI is doing well. if(nodeDiff >= 2) { aiEcho("updateAdaptiveFactors: AI is doing well."); if(gIsFlipMode == false) { gPlanInterval = 60 + (nodeDiff - 2) * 30; aiSetMilitaryPop(30 + gPopBonus); gBonusPlanSize = -2; xsDisableRule("activityAttacks"); } } else if(nodeDiff <= -2) { aiEcho("updateAdaptiveFactors: Humans are doing well."); aiSetMilitaryPop(50 + gPopBonus); gBonusPlanSize = -1 * nodeDiff; gUpdateTimeBonus = 0; gPlanInterval = 30; xsEnableRule("activityAttacks"); } else { aiEcho("updateAdaptiveFactors: Relatively neutral."); aiSetMilitaryPop(40 + gPopBonus); gBonusPlanSize = 0; gUpdateTimeBonus = 0; gPlanInterval = 45; xsEnableRule("activityAttacks"); } } // THIS IS TEMPORARY. REMOVE IT LATER AND USE A REAL CHILD SCRIPT void main() { kbAreaCalculate(); initializeNodeArrays(); // "preload" our units gInfantryUnit = kbFindUnit(cUnitTypeAbstractInfantry); gArcheryUnit = kbFindUnit(cUnitTypeAbstractArcher); gCavalryUnit = kbFindUnit(cUnitTypeAbstractCavalry); // find our factory to grab a main base location int query = kbUnitQueryCreate("Factory Query"); kbUnitQuerySetPlayerID(query, cMyID); kbUnitQuerySetUnitType(query, cUnitTypeNPCCityDefenseFactory); kbUnitQuerySetState(query, cUnitStateAlive); kbUnitQueryResetResults(query); kbUnitQueryExecute(query); gMainBaseLocation = kbUnitGetPosition(kbUnitQueryGetResult(query, 0)); // make a land reserve plan to pick up units gLandReservePlan = aiPlanCreate("Land Reserve Units", cPlanDefend); aiPlanAddUnitType(gLandReservePlan, cUnitTypeLogicalTypeLandPickerChoice , 0, 5, 200); // All mil units, high MAX value to suck up all excess aiPlanSetVariableVector(gLandReservePlan, cDefendPlanDefendPoint, 0, gMainBaseLocation); aiPlanSetVariableFloat(gLandReservePlan, cDefendPlanEngageRange, 0, 20); // Loose aiPlanSetVariableBool(gLandReservePlan, cDefendPlanPatrol, 0, false); aiPlanSetVariableFloat(gLandReservePlan, cDefendPlanGatherDistance, 0, 30); aiPlanSetInitialPosition(gLandReservePlan, gMainBaseLocation); aiPlanSetUnitStance(gLandReservePlan, cUnitStanceDefensive); aiPlanSetVariableInt(gLandReservePlan, cDefendPlanRefreshFrequency, 0, 5); aiPlanSetVariableInt(gLandReservePlan, cDefendPlanAttackTypeID, 0, cUnitTypeUnit); aiPlanSetDesiredPriority(gLandReservePlan, 5); // Very very low priority, gather unused units. aiPlanSetActive(gLandReservePlan); aiSetMilitaryPop(50); } //============================================================================= // CRETE SPECIFIC "FLAVOR" STUFF void setBoatDropOffLocationX(int x = -1) { gBoatDropOffLocation = xsVectorSetX(gBoatDropOffLocation, x); } void setBoatDropOffLocationZ(int z = -1) { gBoatDropOffLocation = xsVectorSetZ(gBoatDropOffLocation, z); } void whoLetTheDogsOut(int count = -1) { int node = getClosestUncontrolledNode(false, 4, -1, gBoatDropOffLocation); createDefensePlan(node, cUnitTypeNoCavWarDog, count); } //============================================================================= // ABILITY STUFF rule updateAbilities active minInterval 5 { if(gIsFlipMode == false) { xsDisableSelf(); return; } if(xsGetTime() > (gAbility1Cooldown * 1000) + gTimeSinceAbility1Fired) { if(gHeroQuery == -1) { gHeroQuery = kbUnitQueryCreate("Hero Query"); kbUnitQuerySetPlayerID(gHeroQuery, cMyID); kbUnitQuerySetUnitType(gHeroQuery, cUnitTypeUnitTypeScout1); kbUnitQuerySetState(gHeroQuery, cUnitStateAlive); } kbUnitQueryResetResults(gHeroQuery); int heroCount = kbUnitQueryExecute(gHeroQuery); if(heroCount > 0) { kbUnitQuerySetPosition(gEnemyQuery, kbUnitGetPosition(kbUnitQueryGetResult(gHeroQuery, 0))); kbUnitQuerySetMaximumDistance(gEnemyQuery, 10); kbUnitQueryResetResults(gEnemyQuery); int count = kbUnitQueryExecute(gEnemyQuery); if(count > 5) { kbAdjustResourceByID(cMyID, cResourceStone, 1); gTimeSinceAbility1Fired = xsGetTime(); } } } }