/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "scumm/he/intern_he.h" #include "scumm/he/moonbase/moonbase.h" #include "scumm/he/moonbase/ai_main.h" #include "scumm/he/moonbase/ai_traveller.h" #include "scumm/he/moonbase/ai_targetacquisition.h" #include "scumm/he/moonbase/ai_types.h" #include "scumm/he/moonbase/ai_pattern.h" namespace Scumm { enum { F_GET_SCUMM_DATA = 0, F_GET_WORLD_DIST = 1, F_GET_WORLD_ANGLE = 2, F_GET_TERRAIN_TYPE = 3, F_GET_CLOSEST_UNIT = 4, F_SIMULATE_BUILDING_LAUNCH = 5, F_GET_POWER_ANGLE_FROM_POINT = 6, F_CHECK_IF_WATER_STATE = 7, F_GET_UNITS_WITHIN_RADIUS = 8, F_GET_LANDING_POINT = 9, F_GET_ENEMY_UNITS_VISIBLE = 10, F_CHECK_IF_WATER_SQUARE = 11, F_GET_GROUND_ALTITUDE = 12, F_CHECK_FOR_CORD_OVERLAP = 13, F_CHECK_FOR_ANGLE_OVERLAP = 14, F_ESTIMATE_NEXT_ROUND_ENERGY = 15, F_CHECK_FOR_UNIT_OVERLAP = 16, F_GET_COORDINATE_VISIBILITY = 17, F_CHECK_FOR_ENERGY_SQUARE = 18, F_AI_CHAT = 19 }; enum { D_GET_HUB_X = 1, D_GET_HUB_Y = 2, D_GET_WORLD_X_SIZE = 3, D_GET_WORLD_Y_SIZE = 4, D_GET_CURRENT_PLAYER = 5, D_GET_MAX_POWER = 6, D_GET_MIN_POWER = 7, D_GET_TERRAIN_SQUARE_SIZE = 8, D_GET_BUILDING_OWNER = 9, D_GET_BUILDING_STATE = 10, D_GET_BUILDING_TYPE = 11, D_DEBUG_BREAK = 12, D_GET_ENERGY_POOLS_ARRAY = 13, D_GET_COORDINATE_VISIBILITY = 14, D_GET_UNIT_VISIBILITY = 15, D_GET_ENERGY_POOL_VISIBILITY = 16, D_GET_NUMBER_OF_POOLS = 17, D_GET_NUMBER_OF_PLAYERS = 18, D_GET_BUILDING_ARMOR = 19, D_GET_BUILDING_WORTH = 20, D_GET_PLAYER_ENERGY = 21, D_GET_PLAYER_MAX_TIME = 22, D_GET_WIND_X_SPEED = 23, D_GET_WIND_Y_SPEED = 24, D_GET_TOTAL_WIND_SPEED = 25, D_GET_WIND_X_SPEED_MAX = 26, D_GET_WIND_Y_SPEED_MAX = 27, D_GET_BIG_X_SIZE = 28, D_GET_BIG_Y_SIZE = 29, D_GET_ENERGY_POOL_WIDTH = 30, D_GET_BUILDING_MAX_ARMOR = 31, D_GET_TIMER_VALUE = 32, D_GET_LAST_ATTACKED_X = 33, D_GET_LAST_ATTACKED_Y = 34, D_PRINT_DEBUG_TIMER = 35, D_GET_PLAYER_TEAM = 36, D_GET_BUILDING_TEAM = 37, D_GET_FOW = 38, D_GET_ANIM_SPEED = 39, D_GET_BUILDING_STACK_PTR = 40, D_GET_TURN_COUNTER = 41 }; enum { AI_TYPE_PLAYER_NUM = 0, AI_TYPE_TYPE = 1 }; enum { ENERGY_MODE = 0, OFFENSE_MODE = 1, DEFENSE_MODE = 2 }; enum { LAUNCH_SOURCE_HUB = 0, LAUNCH_UNIT = 1, LAUNCH_ANGLE = 2, LAUNCH_POWER = 3, MAX_LAUNCH_POWER = 560, MAX_FIRING_DISTANCE = 560 }; enum { SCALE_X = 50, SCALE_Y = 50, SCALE_Z = 50, GRAVITY_CONSTANT = (MAX_LAUNCH_POWER *MAX_LAUNCH_POWER) / MAX_FIRING_DISTANCE, HEIGHT_LOW = 20, NODE_RADIUS = 7, NODE_DIAMETER = NODE_RADIUS * 2 + 2, NODE_DETECT_RADIUS = NODE_RADIUS - 1, BUILDING_HUB_RADIUS = 16 }; enum { STATE_CHOOSE_BEHAVIOR = 0, STATE_CHOOSE_TARGET = 1, STATE_ATTEMPT_SEARCH = 2, STATE_INIT_APPROACH_TARGET = 3, STATE_APPROACH_TARGET = 4, STATE_INIT_ACQUIRE_TARGET = 5, STATE_ACQUIRE_TARGET = 6, STATE_ENERGIZE_TARGET = 7, STATE_OFFEND_TARGET = 8, STATE_DEFEND_TARGET = 9, STATE_LAUNCH = 10, STATE_CRAWLER_DECISION = 11, TREE_DEPTH = 2 }; AI::AI(ScummEngine_v100he *vm) : _vm(vm) { memset(_aiType, 0, sizeof(_aiType)); _aiState = STATE_CHOOSE_BEHAVIOR; _behavior = 2; _energyHogType = 0; memset(_moveList, 0, sizeof(_moveList)); _mcpParams = 0; } void AI::resetAI() { _aiState = STATE_CHOOSE_BEHAVIOR; warning("----------------------> Resetting AI"); for (int i = 1; i != 5; i++) { if (_aiType[i]) { delete _aiType[i]; _aiType[i] = NULL; } _aiType[i] = new AIEntity(BRUTAKAS); } for (int i = 1; i != 5; i++) { if (_moveList[i]) { delete _moveList[i]; _moveList[i] = NULL; } _moveList[i] = new patternList; } } void AI::cleanUpAI() { warning("----------------------> Cleaning Up AI"); for (int i = 1; i != 5; i++) { if (_aiType[i]) { delete _aiType[i]; _aiType[i] = NULL; } } for (int i = 1; i != 5; i++) { if (_moveList[i]) { delete _moveList[i]; _moveList[i] = NULL; } } } void AI::setAIType(const int paramCount, const int32 *params) { if (_aiType[params[AI_TYPE_PLAYER_NUM]]) { delete _aiType[params[AI_TYPE_PLAYER_NUM]]; _aiType[params[AI_TYPE_PLAYER_NUM]] = NULL; } _aiType[params[AI_TYPE_PLAYER_NUM]] = new AIEntity(params[AI_TYPE_TYPE]); if (params[AI_TYPE_TYPE] == ENERGY_HOG) { _energyHogType = 1; } else { _energyHogType = 0; } warning("AI for player %d is %s", params[AI_TYPE_PLAYER_NUM], _aiType[params[AI_TYPE_PLAYER_NUM]]->getNameString()); } int AI::masterControlProgram(const int paramCount, const int32 *params) { static Tree *myTree; static int index; _mcpParams = params; static int lastSource[5]; static int lastAngle[5]; static int lastPower[5]; static int sourceHub; static int target; static int targetX; static int targetY; static int _acquireTarget = 0; static int *launchAction = NULL; static int *currentLaunchAction = NULL; static int OLflag = 0; static int TAflag = 0; Node *retNode; static int retNodeFlag; // Memory cleanup in case of quit during game if (_vm->readVar(_vm->VAR_U32_USER_VAR_F)) { if (myTree != NULL) { delete myTree; myTree = NULL; } if (launchAction != NULL) { delete launchAction; launchAction = NULL; } if (currentLaunchAction != NULL) { delete currentLaunchAction; currentLaunchAction = NULL; } return 1; } int currentPlayer = getCurrentPlayer(); int maxTime = getPlayerMaxTime(); int timerValue = getTimerValue(3); // If timer has run out if ((_aiState > STATE_CHOOSE_BEHAVIOR) && ((maxTime) && (timerValue > maxTime))) { if (myTree != NULL) { delete myTree; myTree = NULL; } if (launchAction != NULL) { delete launchAction; launchAction = NULL; } launchAction = new int[4]; if (currentLaunchAction != NULL) { launchAction[LAUNCH_SOURCE_HUB] = currentLaunchAction[LAUNCH_SOURCE_HUB]; launchAction[LAUNCH_UNIT] = currentLaunchAction[LAUNCH_UNIT]; launchAction[LAUNCH_ANGLE] = currentLaunchAction[LAUNCH_ANGLE]; launchAction[LAUNCH_POWER] = currentLaunchAction[LAUNCH_POWER]; delete currentLaunchAction; currentLaunchAction = NULL; } else { if (!_vm->_rnd.getRandomNumber(1)) launchAction[1] = ITEM_TIME_EXPIRED; else launchAction[1] = SKIP_TURN; } _aiState = STATE_LAUNCH; } static int old_aiState = 0; if (old_aiState != _aiState) { warning("<<%d>>", _aiState); old_aiState = _aiState; } switch (_aiState) { case STATE_CHOOSE_BEHAVIOR: _behavior = chooseBehavior(); warning("Behavior mode: %d", _behavior); if ((int)_vm->_rnd.getRandomNumber(99) < _aiType[getCurrentPlayer()]->getBehaviorVariation() * AI_VAR_BASE_BEHAVIOR + 1) { if (_vm->_rnd.getRandomNumber(1)) { _behavior--; if (_behavior < ENERGY_MODE) _behavior = DEFENSE_MODE; } else { _behavior++; if (_behavior > DEFENSE_MODE) _behavior = ENERGY_MODE; } warning("Alternative behavior: %d", _behavior); } if (_behavior == ENERGY_MODE) if (!getNumberOfPools()) _behavior = OFFENSE_MODE; if (_aiType[getCurrentPlayer()]->getID() == CRAWLER_CHUCKER) _behavior = OFFENSE_MODE; if (launchAction != NULL) { delete launchAction; launchAction = NULL; } index = 0; _aiState = STATE_CHOOSE_TARGET; break; case STATE_CHOOSE_TARGET: target = chooseTarget(_behavior); if (!target) target = chooseTarget(OFFENSE_MODE); if (_behavior == ENERGY_MODE) { int energyPoolScummArray = getEnergyPoolsArray(); targetX = _vm->_moonbase->readFromArray(energyPoolScummArray, target, ENERGY_POOL_X); targetY = _vm->_moonbase->readFromArray(energyPoolScummArray, target, ENERGY_POOL_Y); } else { targetX = getHubX(target); targetY = getHubY(target); } warning("Target (%d, %d) id: %d", targetX, targetY, target); if (getFOW()) _aiState = STATE_ATTEMPT_SEARCH; else _aiState = STATE_INIT_APPROACH_TARGET; break; case STATE_ATTEMPT_SEARCH: if (!getCoordinateVisibility(targetX, targetY, currentPlayer)) { int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 0); int targetAngle = calcAngle(getHubX(closestHub), getHubY(closestHub), targetX, targetY); int testX = static_cast(getHubX(closestHub) + (500 * cos(degToRad(targetAngle))) + getMaxX()) % getMaxX(); int testY = static_cast(getHubY(closestHub) + (500 * sin(degToRad(targetAngle))) + getMaxY()) % getMaxY(); int balloonFlag = 0; int unitsArray = getUnitsWithinRadius(testX, testY, 500); int unitsCounter = 0; //see if any balloons are already in the area int nextBuilding = _vm->_moonbase->readFromArray(unitsArray, 0, unitsCounter); while (nextBuilding) { if (((getBuildingType(nextBuilding) == BUILDING_BALLOON) || (getBuildingType(nextBuilding) == BUILDING_TOWER)) && (getBuildingTeam(nextBuilding) == getPlayerTeam(currentPlayer))) { balloonFlag = 1; nextBuilding = 0; continue; } unitsCounter++; nextBuilding = _vm->_moonbase->readFromArray(unitsArray, 0, unitsCounter); } _vm->_moonbase->deallocateArray(unitsArray); if (!balloonFlag) { launchAction = new int[4]; launchAction[LAUNCH_SOURCE_HUB] = closestHub; if (getPlayerEnergy() > 3) { launchAction[LAUNCH_UNIT] = ITEM_BALLOON; launchAction[LAUNCH_POWER] = getMaxPower(); } else { launchAction[LAUNCH_UNIT] = ITEM_TOWER; launchAction[LAUNCH_POWER] = getMinPower(); } launchAction[LAUNCH_ANGLE] = targetAngle + (_vm->_rnd.getRandomNumber(89) - 45); int newTargetPos = abs(fakeSimulateWeaponLaunch(getHubX(closestHub), getHubY(closestHub), launchAction[LAUNCH_POWER], launchAction[LAUNCH_ANGLE])); targetX = newTargetPos % getMaxX(); targetY = newTargetPos / getMaxY(); _aiState = STATE_INIT_ACQUIRE_TARGET; break; } } _aiState = STATE_INIT_APPROACH_TARGET; break; case STATE_INIT_APPROACH_TARGET: { int closestOL = getClosestUnit(targetX, targetY, 900, currentPlayer, 1, BUILDING_OFFENSIVE_LAUNCHER, 1); if (closestOL && (_behavior == OFFENSE_MODE)) { _aiState = STATE_OFFEND_TARGET; break; } } // get closest hub...if attack mode and almost close enough, maybe throw an offense if ((_behavior == OFFENSE_MODE) && (getPlayerEnergy() > 6)) { if (!_vm->_rnd.getRandomNumber(2)) { int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1); int dist = getDistance(targetX, targetY, getHubX(closestHub), getHubY(closestHub)); if ((dist > 470) && (dist < 900)) { int closestOL = getClosestUnit(targetX, targetY, 900, currentPlayer, 1, BUILDING_OFFENSIVE_LAUNCHER, 0); if (!closestOL) { // Launch an OL OLflag = 1; targetX = getHubX(closestHub); targetY = getHubY(closestHub); _aiState = STATE_DEFEND_TARGET; break; } } } } if ((_behavior == OFFENSE_MODE) && (_aiType[currentPlayer]->getID() == RANGER) && (getPlayerEnergy() > 2)) { int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1); int dist = getDistance(targetX, targetY, getHubX(closestHub), getHubY(closestHub)); if (dist < 750) { _aiState = STATE_OFFEND_TARGET; break; } } myTree = initApproachTarget(targetX, targetY, &retNode); // If no need to approach, apply appropriate behavior if (retNode == myTree->getBaseNode()) { switch (_behavior) { case 0: _aiState = STATE_ENERGIZE_TARGET; break; case 1: _aiState = STATE_OFFEND_TARGET; break; case 2: _aiState = STATE_DEFEND_TARGET; break; case -1: _aiState = STATE_LAUNCH; break; } delete myTree; myTree = NULL; break; } delete retNode; retNode = NULL; if (getPlayerEnergy() < 7) { if (!_vm->_rnd.getRandomNumber(3)) { _behavior = DEFENSE_MODE; _aiState = STATE_CHOOSE_TARGET; } else { if (launchAction == NULL) { launchAction = new int[4]; } if (!_vm->_rnd.getRandomNumber(2)) { launchAction[1] = ITEM_TIME_EXPIRED; } else { launchAction[1] = SKIP_TURN; } _aiState = STATE_LAUNCH; } delete myTree; myTree = NULL; break; } _aiState = STATE_CRAWLER_DECISION; break; // If behavior is offense, possibly just chuck a crawler case STATE_CRAWLER_DECISION: { // Brace just here to scope throwCrawler int throwCrawler = 0; if (_behavior == OFFENSE_MODE) { if (getPlayerEnergy() > 6) { int crawlerTest = _vm->_rnd.getRandomNumber(9); if (!crawlerTest) throwCrawler = 1; } } if (_aiType[getCurrentPlayer()]->getID() == CRAWLER_CHUCKER) { if (getPlayerEnergy() > 6) { throwCrawler = 1; } else { launchAction = new int[4]; if (!_vm->_rnd.getRandomNumber(1)) launchAction[1] = ITEM_TIME_EXPIRED; else launchAction[1] = SKIP_TURN; _aiState = STATE_LAUNCH; delete myTree; myTree = NULL; } } if (throwCrawler) { sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1); int powAngle = getPowerAngleFromPoint(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY, 15); powAngle = abs(powAngle); int power = powAngle / 360; int angle = powAngle - (power * 360); launchAction = new int[4]; launchAction[0] = sourceHub; launchAction[1] = ITEM_CRAWLER; warning("CRAWLER DECISION is launching a crawler"); launchAction[2] = angle; launchAction[3] = power; retNodeFlag = 0; // Need to update target so acquire can work int targetCoords = fakeSimulateWeaponLaunch(getHubX(launchAction[LAUNCH_SOURCE_HUB]), getHubY(launchAction[LAUNCH_SOURCE_HUB]), launchAction[LAUNCH_POWER], launchAction[LAUNCH_ANGLE]); targetX = targetCoords % getMaxX(); targetY = targetCoords / getMaxX(); targetX = (targetX + getMaxX()) % getMaxX(); targetY = (targetY + getMaxY()) % getMaxY(); _aiState = STATE_INIT_ACQUIRE_TARGET; delete myTree; myTree = NULL; } else { _aiState = STATE_APPROACH_TARGET; } break; } // ApproachTarget returns NULL if target is already reachable case STATE_APPROACH_TARGET: { int x, y; Node *currentNode = NULL; launchAction = approachTarget(myTree, x, y, ¤tNode); } if (launchAction != NULL) { if (launchAction[0] == -1) { warning("Creating fake target approach hub"); TAflag = 1; int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1); targetX = getHubX(closestHub); targetY = getHubY(closestHub); delete[] launchAction; launchAction = NULL; _aiState = STATE_DEFEND_TARGET; delete myTree; myTree = NULL; break; } // Need to update target so acquire can work int targetCoords = fakeSimulateWeaponLaunch(getHubX(launchAction[LAUNCH_SOURCE_HUB]), getHubY(launchAction[LAUNCH_SOURCE_HUB]), launchAction[LAUNCH_POWER], launchAction[LAUNCH_ANGLE]); targetX = targetCoords % getMaxX(); targetY = targetCoords / getMaxX(); targetX = (targetX + getMaxX()) % getMaxX(); targetY = (targetY + getMaxY()) % getMaxY(); _aiState = STATE_INIT_ACQUIRE_TARGET; _behavior = -1; delete myTree; myTree = NULL; } break; case STATE_ENERGIZE_TARGET: launchAction = energizeTarget(targetX, targetY, index); if (launchAction != NULL) { if (launchAction[0]) { assert(launchAction[0] > 0); if (launchAction[1] == ITEM_HUB) { index = 0; retNodeFlag = 0; _aiState = STATE_INIT_ACQUIRE_TARGET; } else { index = 0; _aiState = STATE_INIT_ACQUIRE_TARGET; } } else { index++; delete[] launchAction; launchAction = NULL; } } else { _behavior = DEFENSE_MODE; retNodeFlag = 0; index = 0; _aiState = STATE_CHOOSE_TARGET; } break; case STATE_OFFEND_TARGET: launchAction = offendTarget(targetX, targetY, index); if (launchAction != NULL) { if (launchAction[0]) { retNodeFlag = 0; _aiState = STATE_INIT_ACQUIRE_TARGET; } else { index++; delete[] launchAction; launchAction = NULL; } } break; case STATE_DEFEND_TARGET: launchAction = defendTarget(targetX, targetY, index); if (launchAction != NULL) { if (launchAction[0]) { retNodeFlag = 0; _aiState = STATE_INIT_ACQUIRE_TARGET; if (launchAction[LAUNCH_UNIT] != ITEM_BRIDGE) { if (OLflag) { OLflag = 0; launchAction[LAUNCH_UNIT] = ITEM_OFFENSE; } if (TAflag) { TAflag = 0; warning("replacing defense unit %d with a hub", launchAction[LAUNCH_UNIT]); launchAction[LAUNCH_UNIT] = ITEM_HUB; } } } else { index++; delete[] launchAction; launchAction = NULL; } } break; case STATE_INIT_ACQUIRE_TARGET: myTree = initAcquireTarget(targetX, targetY, &retNode); if (myTree == NULL) { _aiState = STATE_LAUNCH; break; } // This next line is a questionable fix if (retNode == myTree->getBaseNode()) retNodeFlag = 1; _acquireTarget = 0; _aiState = STATE_ACQUIRE_TARGET; break; case STATE_ACQUIRE_TARGET: { // Here to scope tempLaunchAction int *tempLaunchAction = NULL; int errCod = 0; _acquireTarget++; if (!retNodeFlag) { tempLaunchAction = acquireTarget(targetX, targetY, myTree, errCod); } else { warning("NOT acquiring target!!!!!!!"); _acquireTarget = 101; } if (tempLaunchAction != NULL) { if (launchAction != NULL) { delete launchAction; launchAction = NULL; } launchAction = tempLaunchAction; } // If no hubs are available for launching...turn must be skipped if (launchAction != NULL) { if (launchAction[LAUNCH_SOURCE_HUB] == 0) { launchAction[LAUNCH_UNIT] = SKIP_TURN; } } if ((tempLaunchAction != NULL) || (errCod == 1) || (_acquireTarget > 100)) { if (tempLaunchAction == NULL) { warning("\nABORTING acquire target!!!!!"); } assert(launchAction != NULL); delete myTree; myTree = NULL; _aiState = STATE_LAUNCH; } } break; default: break; } if (_aiState == STATE_LAUNCH) { static float randomAttenuation = 1; if (((launchAction[LAUNCH_UNIT] == ITEM_REPAIR) || (launchAction[LAUNCH_UNIT] == ITEM_ANTIAIR) || (launchAction[LAUNCH_UNIT] == ITEM_BRIDGE) || (launchAction[LAUNCH_UNIT] == ITEM_TOWER) || (launchAction[LAUNCH_UNIT] == ITEM_RECLAIMER) || (launchAction[LAUNCH_UNIT] == ITEM_BALLOON) || (launchAction[LAUNCH_UNIT] == ITEM_MINE) || (launchAction[LAUNCH_UNIT] == ITEM_ENERGY) || (launchAction[LAUNCH_UNIT] == ITEM_SHIELD) || (launchAction[LAUNCH_UNIT] == ITEM_OFFENSE) || (launchAction[LAUNCH_UNIT] == ITEM_HUB)) && (getBuildingType(launchAction[LAUNCH_SOURCE_HUB]) == BUILDING_OFFENSIVE_LAUNCHER)) { if (getPlayerEnergy() > 2) { launchAction[LAUNCH_UNIT] = ITEM_GUIDED; } else { launchAction[LAUNCH_UNIT] = ITEM_BOMB; } } if ((lastSource[currentPlayer] == launchAction[LAUNCH_SOURCE_HUB]) && (lastAngle[currentPlayer] == launchAction[LAUNCH_ANGLE]) && (lastPower[currentPlayer] == launchAction[LAUNCH_POWER])) { randomAttenuation -= .2f; randomAttenuation = MAX(randomAttenuation, 0.0f); warning("Attenuating..."); } else { randomAttenuation = 1; } lastSource[currentPlayer] = launchAction[LAUNCH_SOURCE_HUB]; lastAngle[currentPlayer] = launchAction[LAUNCH_ANGLE]; lastPower[currentPlayer] = launchAction[LAUNCH_POWER]; _vm->writeVar(_vm->VAR_U32_USER_VAR_A, launchAction[LAUNCH_SOURCE_HUB]); int energy = getPlayerEnergy(); warning("Computer's energy: %d", energy); // Check if there's enough energy to launch this item int n = (launchAction[1] / 6); int energyRequired = (1 + n * n + n); if (((energy - energyRequired) < 0) || (!launchAction[LAUNCH_SOURCE_HUB])) { _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); } else { assert((launchAction[LAUNCH_UNIT] >= 0) && (launchAction[LAUNCH_UNIT] <= 18)); if ((launchAction[LAUNCH_UNIT] < 0) || (launchAction[LAUNCH_UNIT] > 18)) launchAction[LAUNCH_UNIT] = 0; _vm->writeVar(_vm->VAR_U32_USER_VAR_B, launchAction[LAUNCH_UNIT]); } if (launchAction[LAUNCH_UNIT] == ITEM_BOMB) { if (energy > 2) { if (!_vm->_rnd.getRandomNumber(4)) { launchAction[LAUNCH_UNIT] = ITEM_GUIDED; } } } { // ANGLE setting int angleAdjustment = (int)(_vm->_rnd.getRandomNumber(_aiType[getCurrentPlayer()]->getAngleVariation() * AI_VAR_BASE_ANGLE) * 3.6); //pos or neg choice angleAdjustment *= ((_vm->_rnd.getRandomNumber(1) * 2) - 1); angleAdjustment *= randomAttenuation; int safeAngle = 0; int lu = launchAction[LAUNCH_UNIT]; if ((lu == ITEM_ANTIAIR) || (lu == ITEM_TOWER) || (lu == ITEM_ENERGY) || (lu == ITEM_SHIELD) || (lu == ITEM_OFFENSE) || (lu == ITEM_HUB)) { for (int i = 1; i < 90; i++) { assert((launchAction[LAUNCH_ANGLE] < 1000) && (angleAdjustment < 360)); if (checkForAngleOverlap(launchAction[LAUNCH_SOURCE_HUB], launchAction[LAUNCH_ANGLE] + angleAdjustment)) { angleAdjustment += (i % 2 ? i : -i); } else { safeAngle = 1; i = 90; } } } else { safeAngle = 1; } if (!safeAngle) angleAdjustment = 0; warning("Angle adjustment = %d", angleAdjustment); _vm->writeVar(_vm->VAR_U32_USER_VAR_C, launchAction[LAUNCH_ANGLE] + angleAdjustment); } { // POWER setting int powerRangeFactor = (getMaxPower() - getMinPower()) / 100; int powerAdjustment = static_cast(_vm->_rnd.getRandomNumber(_aiType[getCurrentPlayer()]->getPowerVariation() * AI_VAR_BASE_POWER)) * powerRangeFactor; //pos or neg choice powerAdjustment *= ((_vm->_rnd.getRandomNumber(1) * 2) - 1); powerAdjustment *= randomAttenuation; warning("Power adjustment = %d", powerAdjustment); int newPower = MIN(getMaxPower(), launchAction[LAUNCH_POWER] + powerAdjustment); newPower = MAX(getMinPower(), launchAction[LAUNCH_POWER] + powerAdjustment); _vm->writeVar(_vm->VAR_U32_USER_VAR_D, newPower); assert(_vm->readVar(_vm->VAR_U32_USER_VAR_B) != -1); if (launchAction[LAUNCH_UNIT] != SKIP_TURN) { if ((launchAction[LAUNCH_SOURCE_HUB] > 0) && (launchAction[LAUNCH_SOURCE_HUB] <= 500)) { if (getBuildingState(launchAction[LAUNCH_SOURCE_HUB]) != 0) { _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); } } else { _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); } } if ((launchAction[LAUNCH_UNIT] == SKIP_TURN) || (launchAction[LAUNCH_POWER] == 0)) { _vm->writeVar(_vm->VAR_U32_USER_VAR_D, -1); } } if ((launchAction[LAUNCH_SOURCE_HUB] > 0) && (launchAction[LAUNCH_SOURCE_HUB] <= 500)) { int nearbyOpponents = getUnitsWithinRadius(getHubX(launchAction[LAUNCH_SOURCE_HUB]), getHubY(launchAction[LAUNCH_SOURCE_HUB]), 180); int opponentCounter = 0; int opponentBuilding = _vm->_moonbase->readFromArray(nearbyOpponents, 0, opponentCounter); int defenseOn = 0; int defenseOff = 0; while (opponentBuilding) { if (getBuildingOwner(opponentBuilding)) { if ((getBuildingType(opponentBuilding) == BUILDING_ANTI_AIR) && (getBuildingTeam(opponentBuilding) != getPlayerTeam(currentPlayer))) { if (getBuildingState(opponentBuilding) == 0) { defenseOn = 1; } else { defenseOff = 1; } } } opponentCounter++; opponentBuilding = _vm->_moonbase->readFromArray(nearbyOpponents, 0, opponentCounter); } _vm->_moonbase->deallocateArray(nearbyOpponents); if (defenseOn && defenseOff) { if (!_vm->_rnd.getRandomNumber(1)) { _vm->writeVar(_vm->VAR_U32_USER_VAR_B, ITEM_TIME_EXPIRED); } else { _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); } } else { if (defenseOn) { _vm->writeVar(_vm->VAR_U32_USER_VAR_B, ITEM_CLUSTER); int temp = _vm->_rnd.getRandomNumber(2); int tempPower = 0; switch (temp) { case 0: tempPower = getMinPower(); break; case 1: tempPower = getMaxPower(); break; default: tempPower = launchAction[LAUNCH_POWER]; } _vm->writeVar(_vm->VAR_U32_USER_VAR_D, tempPower); } } } delete[] launchAction; launchAction = NULL; _aiState = STATE_CHOOSE_BEHAVIOR; int rSh, rU, rP, rA = 0; rSh = _vm->readVar(_vm->VAR_U32_USER_VAR_A); rU = _vm->readVar(_vm->VAR_U32_USER_VAR_B); rP = _vm->readVar(_vm->VAR_U32_USER_VAR_D); rA = _vm->readVar(_vm->VAR_U32_USER_VAR_C); warning("su: %d unit: %d power: %d angle: %d", rSh, rU, rP, rA); { // Checking for patterns if ((_aiType[currentPlayer]->getID() != CRAWLER_CHUCKER) && (_aiType[currentPlayer]->getID() != ENERGY_HOG) && (getBuildingStackPtr() > 5)) _moveList[currentPlayer]->addPattern(rSh, rU, rP, rA); int patternFound = _moveList[currentPlayer]->evaluatePattern(rSh, rU, rP, rA); if (!_vm->_rnd.getRandomNumber(9)) patternFound = 0; if (patternFound) { warning("------------------------------------------>Eliminating pattern"); if (_vm->_rnd.getRandomNumber(1)) { _behavior--; if (_behavior < ENERGY_MODE) _behavior = DEFENSE_MODE; } else { _behavior++; if (_behavior > DEFENSE_MODE) _behavior = ENERGY_MODE; } if (_behavior == ENERGY_MODE) if (!getNumberOfPools()) _behavior = OFFENSE_MODE; _vm->writeVar(_vm->VAR_U32_USER_VAR_A, 0); _vm->writeVar(_vm->VAR_U32_USER_VAR_B, 0); _vm->writeVar(_vm->VAR_U32_USER_VAR_C, 0); _vm->writeVar(_vm->VAR_U32_USER_VAR_D, 0); _aiState = STATE_CHOOSE_TARGET; } } if ((rSh > 0) && (rSh < 501)) { if ((rU == ITEM_ANTIAIR) || (rU == ITEM_TOWER) || (rU == ITEM_ENERGY) || (rU == ITEM_SHIELD) || (rU == ITEM_OFFENSE) || (rU == ITEM_HUB)) { int tryCount = 0; while (checkForAngleOverlap(rSh, rA) && tryCount < 25) { rA = _vm->_rnd.getRandomNumber(359); tryCount++; } if (tryCount < 25) { _vm->writeVar(_vm->VAR_U32_USER_VAR_C, rA); } else { _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); } } } } else { _vm->writeVar(_vm->VAR_U32_USER_VAR_A, 0); _vm->writeVar(_vm->VAR_U32_USER_VAR_B, 0); _vm->writeVar(_vm->VAR_U32_USER_VAR_C, 0); _vm->writeVar(_vm->VAR_U32_USER_VAR_D, 0); } // Sending behavior to SCUMM side for profiling int selectedUnit = _vm->readVar(_vm->VAR_U32_USER_VAR_B); if (selectedUnit) { if (selectedUnit > 0) _vm->writeVar(_vm->VAR_U32_USER_VAR_E, _behavior); else _vm->writeVar(_vm->VAR_U32_USER_VAR_E, -999); } return 1; } int AI::chooseBehavior() { static int dominantMode = 0; if (getBuildingStackPtr() < 5) return OFFENSE_MODE; int currentPlayer = getCurrentPlayer(); int AIpersonality = _aiType[currentPlayer]->getID(); switch (AIpersonality) { case BRUTAKAS: dominantMode = OFFENSE_MODE; break; case AGI: dominantMode = DEFENSE_MODE; break; case EL_GATO: dominantMode = ENERGY_MODE; break; case PIXELAHT: dominantMode = DEFENSE_MODE; break; case CYBALL: dominantMode = ENERGY_MODE; break; case NEEP: dominantMode = DEFENSE_MODE; break; case WARCUPINE: dominantMode = ENERGY_MODE; break; case AONE: dominantMode = DEFENSE_MODE; break; case SPANDO: dominantMode = ENERGY_MODE; break; case ORBNU_LUNATEK: dominantMode = ENERGY_MODE; break; case CRAWLER_CHUCKER: dominantMode = OFFENSE_MODE; break; case ENERGY_HOG: dominantMode = ENERGY_MODE; { int breakFlag = 0; for (int i = 500; i > 0; i--) if ((getBuildingOwner(i) == currentPlayer) && (getBuildingType(i) == BUILDING_ENERGY_COLLECTOR)) breakFlag = 1; if (!breakFlag) return ENERGY_MODE; } break; case RANGER: dominantMode = OFFENSE_MODE; //random chance of defense if really early in game, otherwise offense if (_vm->_rnd.getRandomNumber(1) || getTurnCounter() > 4) return OFFENSE_MODE; return DEFENSE_MODE; break; default: //BRUTAKAS dominantMode = OFFENSE_MODE; break; } // get a list of all the visible enemy units int eneCon = 0; int offCon = 0; int defCon = 0; // energy mode { warning("Starting Energy Behavior Selection"); int minEnergy = 8; int maxEnergy = 14; if (dominantMode == ENERGY_MODE) { eneCon = 3; maxEnergy = 21; } else { eneCon = 5; } // loop through energy pool array int energyPoolScummArray = getEnergyPoolsArray(); int numPools = getNumberOfPools(); // Prevent energy from being chosen if not enough energy to create int energyAmount = getPlayerEnergy(); if ((energyAmount < 7)) numPools = 0; for (int i = 1; i <= numPools; i++) { int poolX = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_X); int poolY = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_Y); int radius = energyPoolSize(i) / 2; int poolMaxCount = getMaxCollectors(i); int energyCount = 0; int energyUnits = getUnitsWithinRadius(poolX, poolY, radius + 30); int energyCounter = 0; int energyBuilding = _vm->_moonbase->readFromArray(energyUnits, 0, energyCounter); while (energyBuilding) { energyCounter++; if ((getBuildingType(energyBuilding) == BUILDING_ENERGY_COLLECTOR) && (getBuildingOwner(energyBuilding) != currentPlayer)) energyCount = poolMaxCount; if ((getBuildingType(energyBuilding) == BUILDING_ENERGY_COLLECTOR) && (getBuildingOwner(energyBuilding) == currentPlayer)) energyCount++; energyBuilding = _vm->_moonbase->readFromArray(energyUnits, 0, energyCounter); } _vm->_moonbase->deallocateArray(energyUnits); if (energyCount < poolMaxCount) { int closestHub = getClosestUnit(poolX, poolY, 300, currentPlayer, 1, BUILDING_MAIN_BASE, 1); if (closestHub) { eneCon = MIN(1, eneCon); } else { int secondClosestHub = getClosestUnit(poolX, poolY, 480, currentPlayer, 1, BUILDING_MAIN_BASE, 1); if (secondClosestHub) eneCon = MIN(2, eneCon); else eneCon = MIN(3, eneCon); } } } int totalEnergy = estimateNextRoundEnergy(currentPlayer); if (totalEnergy < minEnergy) eneCon--; if (totalEnergy > maxEnergy) eneCon += 2; if ((totalEnergy > 34) || (!numPools)) eneCon = 10; if (dominantMode == ENERGY_MODE) eneCon--; } // offense mode { warning("Starting Offense Behavior Selection"); if (dominantMode == OFFENSE_MODE) offCon = 3; else offCon = 5; int enemyArray = getEnemyUnitsVisible(currentPlayer); int enemyX = 0; int enemyY = 0; int hubX = 0; int hubY = 0; int nearEnemyHub = 0; // cycle through the array of buildings for (int i = 0; i < 500; i++) { if (int thisBuilding = _vm->_moonbase->readFromArray(enemyArray, i, 0)) { enemyX = getHubX(thisBuilding); enemyY = getHubY(thisBuilding); int closestHub = getClosestUnit(enemyX, enemyY, 970, currentPlayer, 1, BUILDING_MAIN_BASE, 1); int closestOL = getClosestUnit(enemyX, enemyY, 970, currentPlayer, 1, BUILDING_MAIN_BASE, 1); int dist = 0; if (closestOL) { hubX = getHubX(closestOL); hubY = getHubY(closestOL); nearEnemyHub = 1; } if (closestHub) { hubX = getHubX(closestHub); hubY = getHubY(closestHub); dist = getDistance(hubX, hubY, enemyX, enemyY); if (dist < 480) nearEnemyHub = 1; } if (closestHub || closestOL) { int numDefenders = 0; int defArray = getUnitsWithinRadius(enemyX, enemyY, 170); int defCounter = 0; int defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); while (defenseBuilding) { defCounter++; if (((getBuildingType(defenseBuilding) == BUILDING_ANTI_AIR) || (getBuildingType(defenseBuilding) == BUILDING_SHIELD)) && (getBuildingOwner(defenseBuilding) != currentPlayer)) { if (getBuildingState(defenseBuilding) == 0) numDefenders++; } defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); } _vm->_moonbase->deallocateArray(defArray); if (!numDefenders) { int defArray2 = getUnitsWithinRadius(hubX, hubY, 170); defCounter = 0; int defenseBuilding2 = _vm->_moonbase->readFromArray(defArray2, 0, defCounter); while (defenseBuilding2) { defCounter++; if (((getBuildingType(defenseBuilding2) == BUILDING_ANTI_AIR) || (getBuildingType(defenseBuilding2) == BUILDING_SHIELD)) && (getBuildingOwner(defenseBuilding2) != currentPlayer)) if (getBuildingState(defenseBuilding2) == 0) numDefenders++; defenseBuilding2 = _vm->_moonbase->readFromArray(defArray, 0, defCounter); } _vm->_moonbase->deallocateArray(defArray2); } if ((!numDefenders) && (nearEnemyHub)) { if (thisBuilding > 495) offCon = 1 + _vm->_rnd.getRandomNumber(1); else offCon = MIN(offCon, 2); } else { if (thisBuilding > 495) { if (nearEnemyHub) { offCon = MIN(offCon, 2); break; } else { offCon = MIN(offCon, 3); break; } } else { offCon = MIN(offCon, 4); } } } if (getBuildingType(thisBuilding) == BUILDING_CRAWLER) { if (getClosestUnit(enemyX, enemyY, 350, currentPlayer, 1, 0, 0)) { int closestHub1 = getClosestUnit(enemyX, enemyY, 460, currentPlayer, 1, BUILDING_MAIN_BASE, 1); int closestMine = getClosestUnit(enemyX, enemyY, 80, 0, 0, BUILDING_EXPLOSIVE_MINE, 1); if (closestHub1 && !closestMine) offCon = -1; } } } else { break; } } _vm->_moonbase->deallocateArray(enemyArray); offCon--; } // defense mode { warning("Starting Defense Behavior Selection"); if (dominantMode == DEFENSE_MODE) defCon = 3; else defCon = 5; int numDefenders = 0; int openFlag = 0; int mainHubX = getHubX(0); int mainHubY = getHubY(0); int mainHub = getClosestUnit(mainHubX + 10, mainHubY, 20, currentPlayer, 1, BUILDING_MAIN_BASE, 0); int damageFlag = 0; // cycle through the array of buildings for (int i = 500; i >= 1; i--) { if ((i < 497) && (defCon < 3)) break; if (getBuildingOwner(i) == currentPlayer) { int type = getBuildingType(i); int hubX = getHubX(i); int hubY = getHubY(i); if (type == BUILDING_MAIN_BASE || type == BUILDING_ENERGY_COLLECTOR || type == BUILDING_OFFENSIVE_LAUNCHER) { int nearEnemy = 0; int closeBuildingsArray = getUnitsWithinRadius(hubX, hubY, 480); int closeBuildingCounter = 0; int closeBuilding = _vm->_moonbase->readFromArray(closeBuildingsArray, 0, closeBuildingCounter); while (closeBuilding) { closeBuildingCounter++; if ((getBuildingOwner(closeBuilding) != currentPlayer) && (getBuildingType(closeBuilding) == BUILDING_MAIN_BASE)) { nearEnemy = 1; break; } closeBuilding = _vm->_moonbase->readFromArray(closeBuildingsArray, 0, closeBuildingCounter); } _vm->_moonbase->deallocateArray(closeBuildingsArray); int defCounter = 0; int defArray = getUnitsWithinRadius(hubX, hubY, 170); int defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); numDefenders = 0; while (defenseBuilding) { defCounter++; if (((getBuildingType(defenseBuilding) == BUILDING_ANTI_AIR) || (getBuildingType(defenseBuilding) == BUILDING_SHIELD)) && (getBuildingOwner(defenseBuilding) == currentPlayer)) { //main base has enemies near, but is defended if (getBuildingState(defenseBuilding) == 0) numDefenders++; } defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); } _vm->_moonbase->deallocateArray(defArray); if (numDefenders > 2) defCon++; if (numDefenders < 2) if (dominantMode == DEFENSE_MODE) openFlag = 1; if (!numDefenders) { if (nearEnemy) { if (i == mainHub) { defCon = 1; break; } else { defCon = MIN(defCon, 2); } } else { if (i == mainHub) defCon = MIN(defCon, 3); else defCon = MIN(defCon, 4); } } if (getBuildingArmor(i) < getBuildingMaxArmor(i)) damageFlag = 1; } } } if (damageFlag && (defCon > 1)) defCon--; if (!openFlag && defCon == 3) defCon += 2; } warning("%s-------------------------------> Energy: %d Offense: %d Defense: %d", _aiType[currentPlayer]->getNameString(), eneCon, offCon, defCon); if (dominantMode == DEFENSE_MODE) if ((defCon <= offCon) && (defCon <= eneCon)) return DEFENSE_MODE; if (dominantMode == OFFENSE_MODE) if ((offCon <= eneCon) && (offCon <= defCon)) return OFFENSE_MODE; if (dominantMode == ENERGY_MODE) if ((eneCon <= offCon) && (eneCon <= defCon)) return ENERGY_MODE; if ((defCon <= offCon) && (defCon <= eneCon)) return DEFENSE_MODE; if ((offCon <= eneCon) && (offCon <= defCon)) return OFFENSE_MODE; if ((eneCon <= offCon) && (eneCon <= defCon)) return ENERGY_MODE; return -1; } int AI::chooseTarget(int behavior) { int numPools = getNumberOfPools(); int currentPlayer = getCurrentPlayer(); int selection = 0; int selectionValues[50] = {0}; int selectionDist = 10000000; if (behavior == ENERGY_MODE) { // loop through energy pool array int energyPoolScummArray = getEnergyPoolsArray(); for (int i = 1; i <= numPools; i++) { // check # units on pool int numPoolSpots = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_UNITS_ON); if (numPoolSpots == 0) { // put this one in the middle warning("Empty pool #%d", i); selectionValues[i] = 2; } else { // get units w/in radius of pool int poolUnitsArray = getUnitsWithinRadius(_vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_X), _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_Y), 50); int enemyPool = 0; int j = 1; int thisPoolUnit = _vm->_moonbase->readFromArray(poolUnitsArray, 0, j); while (thisPoolUnit) { if (getBuildingOwner(thisPoolUnit) != currentPlayer) enemyPool = 1; j++; thisPoolUnit = _vm->_moonbase->readFromArray(poolUnitsArray, 0, j); } _vm->_moonbase->deallocateArray(poolUnitsArray); // if enemy collector, put at bottom if (enemyPool) { selectionValues[i] = 1; } else if (numPoolSpots < getMaxCollectors(i)) { selectionValues[i] = 3; } else { // this pool is filled selectionValues[i] = 0; } } if (selectionValues[i] > selectionValues[selection]) { selection = i; } else if (selectionValues[i] == selectionValues[selection]) { int poolX = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_X); int poolY = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_Y); int closestHub = getClosestUnit(poolX, poolY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 100); int thisDist = getDistance(poolX, poolY, getHubX(closestHub), getHubY(closestHub)); if (thisDist < selectionDist) { selection = i; selectionDist = thisDist; } } } warning("Pool selected: %d dist: %d", selection, selectionDist); return selection; } if (behavior == OFFENSE_MODE) { int returnBuilding = 0; int attackableArray[500]; int nearAttackableArray[500]; int attackableIndex = 0; int nearAttackableIndex = 0; int enemyArray = getEnemyUnitsVisible(currentPlayer); for (int i = 500; i >= 1; i--) { int thisBuilding = _vm->_moonbase->readFromArray(enemyArray, i - 1, 0); if (thisBuilding) { if (getBuildingType(thisBuilding) == BUILDING_CRAWLER) { if ((getTerrain(getHubX(thisBuilding), getHubY(thisBuilding)) != TERRAIN_TYPE_WATER) || (getPlayerEnergy() > 6)) { if (getClosestUnit(getHubX(thisBuilding), getHubY(thisBuilding), 380, currentPlayer, 1, BUILDING_MAIN_BASE, 1)) { returnBuilding = thisBuilding; _vm->_moonbase->deallocateArray(enemyArray); return returnBuilding; } } else { continue; } } int enemyX = getHubX(thisBuilding); int enemyY = getHubY(thisBuilding); int closestHub = getClosestUnit(enemyX, enemyY, 930, currentPlayer, 1, BUILDING_MAIN_BASE, 1); int dist = getDistance(enemyX, enemyY, getHubX(closestHub), getHubY(closestHub)); if (getBuildingType(thisBuilding) != BUILDING_BALLOON) { if (dist < 470) { attackableArray[attackableIndex] = thisBuilding; attackableIndex++; } else { nearAttackableArray[nearAttackableIndex] = thisBuilding; nearAttackableIndex++; } } } } _vm->_moonbase->deallocateArray(enemyArray); if (attackableIndex) { int thisWorth = 0; int savedWorth = 1; int closestSavedShield = 0; int closestSavedAntiAir = 0; for (int i = 0; i < attackableIndex; i++) { thisWorth = getBuildingWorth(attackableArray[i]); if (thisWorth == savedWorth) { int closestShield = getClosestUnit(getHubX(attackableArray[i]), getHubY(attackableArray[i]), 180, currentPlayer, 0, BUILDING_SHIELD, 1); int closestAntiAir = getClosestUnit(getHubX(attackableArray[i]), getHubY(attackableArray[i]), 180, currentPlayer, 0, BUILDING_ANTI_AIR, 1); if (closestSavedShield > closestShield) { savedWorth = thisWorth; closestSavedShield = closestShield; closestSavedAntiAir = closestAntiAir; returnBuilding = attackableArray[i]; } else { if (closestSavedAntiAir > closestAntiAir) { savedWorth = thisWorth; closestSavedShield = closestShield; closestSavedAntiAir = closestAntiAir; returnBuilding = attackableArray[i]; } } } if (thisWorth > savedWorth) { savedWorth = thisWorth; returnBuilding = attackableArray[i]; } } } else { if (nearAttackableIndex) { int thisWorth = 0; int savedWorth = 1; int closestSavedShield = 0; int closestSavedAntiAir = 0; for (int i = 0; i < nearAttackableIndex; i++) { thisWorth = getBuildingWorth(nearAttackableArray[i]); if (thisWorth == savedWorth) { int closestShield = getClosestUnit(getHubX(nearAttackableArray[i]), getHubY(nearAttackableArray[i]), 180, currentPlayer, 0, BUILDING_SHIELD, 1); int closestAntiAir = getClosestUnit(getHubX(nearAttackableArray[i]), getHubY(nearAttackableArray[i]), 180, currentPlayer, 0, BUILDING_ANTI_AIR, 1); if (closestSavedShield > closestShield) { savedWorth = thisWorth; closestSavedShield = closestShield; closestSavedAntiAir = closestAntiAir; returnBuilding = nearAttackableArray[i]; } else { if (closestSavedAntiAir > closestAntiAir) { savedWorth = thisWorth; closestSavedShield = closestShield; closestSavedAntiAir = closestAntiAir; returnBuilding = nearAttackableArray[i]; } } } if (thisWorth > savedWorth) { savedWorth = thisWorth; returnBuilding = nearAttackableArray[i]; } } } } if (!returnBuilding) { for (int i = 500; i > 496; i--) { if (getBuildingOwner(i)) { if (getBuildingTeam(i) != getPlayerTeam(currentPlayer)) { returnBuilding = i; i = 0; } } } } warning("Attack target: %d", returnBuilding); assert(returnBuilding); return returnBuilding; } if (behavior == DEFENSE_MODE) { int returnBuilding = 0; int savedTally = 0; int savedDamage; float savedNumDefenses = 0; int savedWorth = 0; float numDefenses = 0; int tally = 0; int attackable = 0; int attacked = 0; int damage = 0; int type = 0; int worth = 0; int attackedX = 0; int attackedY = 0; int attackedUnit = 0; if (getLastAttacked(attackedX, attackedY)) { (void)getClosestUnit(attackedX + 10, attackedY, 50, currentPlayer, 1, 0, 0); // Unused? } // loop through own units for (int i = 1; i <= 500; i++) { numDefenses = 0; attackable = 0; attacked = 0; damage = 0; type = 0; worth = 0; int owner = getBuildingOwner(i); if (owner == currentPlayer) { type = getBuildingType(i); // if current unit in [hub, offense, energy, tower] if ((type == BUILDING_MAIN_BASE) || (type == BUILDING_ENERGY_COLLECTOR) || (type == BUILDING_OFFENSIVE_LAUNCHER) || (type == BUILDING_TOWER)) { worth = getBuildingWorth(i); // Calculate current defenses int x = getHubX(i); int y = getHubY(i); assert(x >= 0); assert(y >= 0); int defenseArray = getUnitsWithinRadius(x, y, 180); int j = 0; // cycle through the defense units close to the target building int defenseBuilding = _vm->_moonbase->readFromArray(defenseArray, 0, j); // loop on all defenses w/in 180 while (defenseBuilding != 0) { int defenseType = getBuildingType(defenseBuilding); // sub for each if ((defenseType == BUILDING_ANTI_AIR) || (defenseType == BUILDING_SHIELD)) { if (getBuildingState(defenseBuilding) == 0) numDefenses += 1; else numDefenses += .5; } j++; defenseBuilding = _vm->_moonbase->readFromArray(defenseArray, 0, j); } _vm->_moonbase->deallocateArray(defenseArray); // Calculate if this unit is attackable // get dist to nearest enemy hub, offense int closestHub = getClosestUnit(x, y, getMaxX(), getCurrentPlayer(), 0, BUILDING_MAIN_BASE, 0); int numStridesToHub = getDistance(getHubX(closestHub), getHubY(closestHub), x, y) / 480; closestHub = getClosestUnit(x, y, getMaxX(), getCurrentPlayer(), 0, BUILDING_OFFENSIVE_LAUNCHER, 0); int numStridesToOL = getDistance(getHubX(closestHub), getHubY(closestHub), x, y) / 800; // sub for each stride away if (!numStridesToOL || !numStridesToHub) attackable = 1; // Check if this unit was just attacked if (attackedUnit == i) attacked = 1; if (!numDefenses) { tally = 1; if (attackable) { tally = 4; if (attacked) { tally = 5; } } } else { if (attackable) { tally = 2; if (attacked) { tally = 3; } } } // Check if this unit is damaged int saveFlag = 0; if (tally > savedTally) { saveFlag = 1; } else { if (tally == savedTally) { if (worth > savedWorth) { saveFlag = 1; if (numDefenses > savedNumDefenses) { saveFlag = 0; } } if (damage > savedDamage) { saveFlag = 1; if (numDefenses > savedNumDefenses) { saveFlag = 0; } } if (numDefenses < savedNumDefenses) { saveFlag = 1; } if (numDefenses >= 3) { saveFlag = 0; } } } if (saveFlag) { savedTally = tally; savedWorth = worth; savedDamage = damage; savedNumDefenses = numDefenses; returnBuilding = i; } } } } return returnBuilding; } return 0; } Tree *AI::initApproachTarget(int targetX, int targetY, Node **retNode) { int sourceHub = 0; if (_behavior == 2) sourceHub = getClosestUnit(targetX + 10, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1); else sourceHub = getClosestUnit(targetX + 10, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, MIN_DIST); Traveller *myTraveller = new Traveller(getHubX(sourceHub), getHubY(sourceHub), this); myTraveller->setSourceHub(sourceHub); //target adjustment so that room is allowed for the appropriate shot int tempAngle = calcAngle(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); int adjX = -120 * cos(degToRad(tempAngle)); int adjY = -120 * sin(degToRad(tempAngle)); Traveller::setTargetPosX(targetX + adjX); Traveller::setTargetPosY(targetY + adjY); Traveller::setMaxDist(340); Tree *myTree = new Tree(myTraveller, TREE_DEPTH, this); *retNode = myTree->aStarSearch_singlePassInit(); return myTree; } int *AI::approachTarget(Tree *myTree, int &xTarget, int &yTarget, Node **currentNode) { int *retVal = NULL; *currentNode = NULL; Node *retNode = myTree->aStarSearch_singlePass(); if (*currentNode != NULL) warning("########################################### Got a possible solution"); if (myTree->IsBaseNode(retNode)) { warning("########################################### Returning Base Node"); retVal = new int[4]; retVal[0] = -1; return retVal; } if (retNode == NULL) { return retVal; } else { retVal = new int[4]; Traveller *retTraveller = reinterpret_cast(retNode->getFirstStep()->getContainedObject()); // set launching hub, item to launch, angle of launch, power of launch // if water flag is set, launch bridge instead of hub retVal[0] = static_cast(myTree->getBaseNode()->getContainedObject())->getSourceHub(); if (retTraveller->getWaterFlag()) { int powAngle = getPowerAngleFromPoint(retTraveller->getWaterSourceX(), retTraveller->getWaterSourceY(), retTraveller->getWaterDestX(), retTraveller->getWaterDestY(), 15); powAngle = abs(powAngle); int power = powAngle / 360; int angle = powAngle - (power * 360); retVal[0] = getClosestUnit(retTraveller->getWaterSourceX() + 10, retTraveller->getWaterSourceY(), getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 0); retVal[1] = ITEM_BRIDGE; retVal[2] = angle; retVal[3] = power; warning("Target Bridge Coords: <%d, %d> ", retTraveller->getWaterDestX(), retTraveller->getWaterDestY()); } else { retVal[1] = ITEM_HUB; retVal[2] = retTraveller->getAngleTo(); retVal[3] = retTraveller->getPowerTo(); } int whoseTurn = getCurrentPlayer(); if ((_lastXCoord[whoseTurn]).size() >= MAX_MEMORY) { (_lastXCoord[whoseTurn]).erase((_lastXCoord[whoseTurn]).begin()); (_lastYCoord[whoseTurn]).erase((_lastYCoord[whoseTurn]).begin()); } (_lastXCoord[whoseTurn]).push_back(retTraveller->getPosX()); (_lastYCoord[whoseTurn]).push_back(retTraveller->getPosY()); int temp = static_cast(retTraveller->calcT()); int temp2 = static_cast(retTraveller->getValueG()); int x = static_cast(retTraveller->getPosX()); int y = static_cast(retTraveller->getPosY()); warning("Target Coords: <%d, %d> G-value: %d T-value: %d", x, y, temp2, temp); xTarget = x; yTarget = y; } return retVal; } Tree *AI::initAcquireTarget(int targetX, int targetY, Node **retNode) { int sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, MIN_DIST); warning("My coords (%d): %d %d", sourceHub, getHubX(sourceHub), getHubY(sourceHub)); Sortie::setSourcePos(getHubX(sourceHub), getHubY(sourceHub)); Sortie::setTargetPos(targetX, targetY); Sortie *myBaseTarget = new Sortie(this); myBaseTarget->setValueG(0); myBaseTarget->setUnitType(ITEM_BOMB); myBaseTarget->setShotPos(-1, -1); int unitsArray = getUnitsWithinRadius(targetX + 7, targetY, 211); warning("Target Coords: <%d, %d> Source Coords: <%d, %d>", targetX, targetY, getHubX(sourceHub) , getHubY(sourceHub)); myBaseTarget->setEnemyDefenses(unitsArray, targetX, targetY); int thisElement = _vm->_moonbase->readFromArray(unitsArray, 0, 0); _vm->_moonbase->deallocateArray(unitsArray); if (!thisElement) { delete myBaseTarget; return NULL; } Tree *myTree = new Tree(myBaseTarget, 4, this); *retNode = myTree->aStarSearch_singlePassInit(); return myTree; } int *AI::acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode) { int currentPlayer = getCurrentPlayer(); int *retVal = NULL; Node *retNode = myTree->aStarSearch_singlePass(); if (myTree->IsBaseNode(retNode)) return acquireTarget(targetX, targetY); if (retNode == NULL) { errorCode = 0; return retVal; } Sortie *thisSortie = static_cast(retNode->getFirstStep()->getContainedObject()); int unitToShoot = thisSortie->getUnitType(); if (unitToShoot < 0) { errorCode = 1; return retVal; } if (unitToShoot == ITEM_CRAWLER) { warning("target acquisition is launching a crawler"); } int shotTargetX = thisSortie->getShotPosX(); int shotTargetY = thisSortie->getShotPosY(); int theTarget = getClosestUnit(shotTargetX + 5, shotTargetY, getMaxX(), 0, 0, 0, 0, 0); int sourceOL = 0; int sourceX = thisSortie->getSourcePosX(); int sourceY = thisSortie->getSourcePosY(); int sourceHub = getClosestUnit(sourceX + 5, sourceY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 0); sourceOL = getClosestUnit(sourceX, sourceY, 900, currentPlayer, 1, BUILDING_OFFENSIVE_LAUNCHER, 1, 110); if (sourceOL) { sourceHub = sourceOL; sourceX = getHubX(sourceOL); sourceY = getHubY(sourceOL); } if (!sourceHub) sourceHub = getClosestUnit(sourceX + 5, sourceY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 0); int powAngle = getPowerAngleFromPoint(sourceX, sourceY, shotTargetX, shotTargetY, 15, sourceOL); warning("The source (%d: <%d, %d>) The target (%d: <%d, %d>)", sourceHub, sourceX, sourceY, theTarget, shotTargetX, shotTargetY); powAngle = abs(powAngle); int power = powAngle / 360; int angle = powAngle - (power * 360); retVal = new int[4]; retVal[0] = sourceHub; retVal[1] = unitToShoot; retVal[2] = angle; retVal[3] = power; warning("Unit to shoot: %d", unitToShoot); return retVal; } int *AI::acquireTarget(int targetX, int targetY) { int *retVal = new int[4]; int sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110); if (!sourceHub) sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 0); int directAngle = calcAngle(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); int directDistance = getDistance(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); retVal[0] = sourceHub; retVal[1] = ITEM_OFFENSE; retVal[2] = directAngle; retVal[3] = MAX(MIN(getMaxPower() * directDistance / 500, getMaxPower()), getMinPower()); return retVal; } int *AI::energizeTarget(int &targetX, int &targetY, int index) { int n = 10; static int currentPlayer = 0; static int pool = 0; static int radius = 0; static int poolUnitsArray = 0; static int j = 0; static int k = 0; static int sameUnit = 0; static int nextUnit = 0; static int attempt = 0; if (!index) { warning("index is 0!"); currentPlayer = getCurrentPlayer(); pool = 0; // get the pool that's closest to the target coords for (int i = 1; i <= getNumberOfPools(); i++) { int poolX = _vm->_moonbase->readFromArray(getEnergyPoolsArray(), i, ENERGY_POOL_X); int poolY = _vm->_moonbase->readFromArray(getEnergyPoolsArray(), i, ENERGY_POOL_Y); if ((poolX == targetX) && (poolY == targetY)) { pool = i; } } // calculate the appropriate radius radius = energyPoolSize(pool) / 2; // create test points k = 0; j = 0; nextUnit = 0; sameUnit = 0; attempt = 0; } if (poolUnitsArray) _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = getUnitsWithinRadius(targetX, targetY, 450); assert(poolUnitsArray); // 0 is for energy collectors, 1 is for circumnavigating hubs if (k < 2) { if (!sameUnit) { sameUnit = 1; attempt = 0; nextUnit = _vm->_moonbase->readFromArray(poolUnitsArray, 0, j); j++; } if (nextUnit) { if ((getBuildingType(nextUnit) == BUILDING_MAIN_BASE) && (getBuildingOwner(nextUnit) == currentPlayer)) { int minAngle = 0; int hubToPoolAngle = 0; int testAngle = 0; int testDist = 0; static int xPos = 0; static int yPos = 0; static int newAttempt = 1; if (sameUnit) { if (k == 0) { int poolToHubAngle = calcAngle(targetX, targetY, getHubX(nextUnit), getHubY(nextUnit)); minAngle = poolToHubAngle - 45; } else { hubToPoolAngle = calcAngle(getHubX(nextUnit), getHubY(nextUnit), targetX, targetY); } } if (attempt < n) { static int power = 0; static int angle = 0; if (newAttempt) { newAttempt = 0; if (k == 0) { testAngle = (_vm->_rnd.getRandomNumber(89) + minAngle) % 360; testDist = radius; xPos = targetX + testDist * cos(degToRad(testAngle)); yPos = targetY + testDist * sin(degToRad(testAngle)); } else { switch (_vm->_rnd.getRandomNumber(1)) { case 0: testAngle = (hubToPoolAngle + (45 + _vm->_rnd.getRandomNumber(19))) % 360; break; default: testAngle = (hubToPoolAngle + (315 - _vm->_rnd.getRandomNumber(19))) % 360; break; } testDist = (((((double)n - (double)attempt) / n) * .5) + .5) * (getDistance(getHubX(nextUnit), getHubY(nextUnit), targetX, targetY) / .8); xPos = getHubX(nextUnit) + testDist * cos(degToRad(testAngle)); yPos = getHubY(nextUnit) + testDist * sin(degToRad(testAngle)); } // check if points are good int powAngle = getPowerAngleFromPoint(getHubX(nextUnit), getHubY(nextUnit), xPos, yPos, 15); powAngle = abs(powAngle); power = powAngle / 360; angle = powAngle - (power * 360); } int result = 0; result = simulateBuildingLaunch(getHubX(nextUnit), getHubY(nextUnit), power, angle, 10, 1); if (result) { newAttempt = 1; if (result > 0) { xPos = (xPos + getMaxX()) % getMaxX(); yPos = (yPos + getMaxY()) % getMaxY(); result = 1; } else { // Drop a bridge for the cord int yCoord = -result / getMaxX(); int xCoord = -result - (yCoord * getMaxX()); if (checkIfWaterState(xCoord, yCoord)) { int terrainSquareSize = getTerrainSquareSize(); xCoord = ((xCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); yCoord = ((yCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); int xDist = xCoord - xPos; int yDist = yCoord - yPos; xPos = xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1))); yPos = yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1))); nextUnit = getClosestUnit(xPos, yPos, 480, getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 120); int powAngle = getPowerAngleFromPoint(getHubX(nextUnit), getHubY(nextUnit), xPos, yPos, 15); powAngle = abs(powAngle); power = powAngle / 360; angle = powAngle - (power * 360); int *retVal = new int[4]; retVal[0] = nextUnit; retVal[1] = ITEM_BRIDGE; retVal[2] = angle; retVal[3] = power; if (nextUnit <= 0) retVal[0] = 0; _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = 0; return retVal; } } if (result > 0) { _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = 0; targetX = xPos; targetY = yPos; int *retVal = new int[4]; retVal[0] = nextUnit; if (k == 0) { retVal[1] = ITEM_ENERGY; } else { retVal[1] = ITEM_HUB; } retVal[2] = angle; retVal[3] = power; return retVal; } } else { int *retVal = new int[4]; retVal[0] = 0; _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = 0; return retVal; } attempt++; } else { sameUnit = 0; } } else { sameUnit = 0; } } else { sameUnit = 0; k++; j = 0; } } else { _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = 0; return NULL; } _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = 0; int *retVal = new int[4]; retVal[0] = 0; return retVal; } int *AI::offendTarget(int &targetX, int &targetY, int index) { int *retVal = NULL; int target = getClosestUnit(targetX + 10, targetY, 20, 0, 0, 0, 0); if (!target) target = getClosestUnit(targetX + 10, targetY, 0, 0, 0, 0, 0); warning("The target inside the offendTarget routine is: %d", target); int type = getBuildingType(target); int unit = 0; DefenseUnit *thisUnit; switch (type) { case BUILDING_OFFENSIVE_LAUNCHER: thisUnit = new OffenseUnit(this); break; case BUILDING_TOWER: thisUnit = new TowerUnit(this); break; case BUILDING_MAIN_BASE: thisUnit = new HubUnit(this); break; case BUILDING_ENERGY_COLLECTOR: thisUnit = new EnergyUnit(this); break; case BUILDING_CRAWLER: thisUnit = new CrawlerUnit(this); break; case BUILDING_BRIDGE: thisUnit = new BridgeUnit(this); break; case BUILDING_SHIELD: thisUnit = new ShieldUnit(this); break; default: thisUnit = new HubUnit(this); break; } thisUnit->setPos(targetX, targetY); thisUnit->setID(target); int sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110); int sourceOL = 0; sourceOL = getClosestUnit(targetX, targetY, 900, getCurrentPlayer(), 1, BUILDING_OFFENSIVE_LAUNCHER, 1, 110); unit = thisUnit->selectWeapon(_vm->_rnd.getRandomNumber(4)); if (sourceOL) { if ((unit == ITEM_BOMB) || (unit == ITEM_CLUSTER) || (unit == ITEM_GUIDED) || (unit == ITEM_EMP) || (unit == ITEM_SPIKE) || (unit == ITEM_CRAWLER) || (unit == ITEM_VIRUS)) { sourceHub = sourceOL; } } if (!sourceHub) { retVal = new int[4]; retVal[1] = SKIP_TURN; return retVal; } if ((thisUnit->getType() == BUILDING_CRAWLER) && (unit == SKIP_TURN)) { retVal = new int[4]; retVal[1] = unit; delete thisUnit; return retVal; } if (unit == ITEM_CRAWLER) { warning("******** offense is launching a crawler ********"); warning("The defensive unit is: %d", unit); } Common::Point *targetCoords; int dist = getDistance(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); targetCoords = thisUnit->createTargetPos(0, dist, unit, getHubX(sourceHub), getHubY(sourceHub)); int powAngle = getPowerAngleFromPoint(getHubX(sourceHub), getHubY(sourceHub), targetCoords->x, targetCoords->y, 15, sourceOL); powAngle = abs(powAngle); int power = powAngle / 360; int angle = powAngle % 360; if (unit == ITEM_MINE) power -= 30; targetX = targetCoords->x; targetY = targetCoords->y; if (targetX < 0) targetX = (targetX + getMaxX()) % getMaxX(); if (targetY < 0) targetY = (targetY + getMaxY()) % getMaxY(); assert(targetX >= 0 && targetY >= 0); delete targetCoords; delete thisUnit; retVal = new int[4]; retVal[0] = sourceHub; retVal[1] = unit; retVal[2] = angle; retVal[3] = power; return retVal; } int *AI::defendTarget(int &targetX, int &targetY, int index) { int *retVal = NULL; Defender *thisDefender = new Defender(this); int defStatus = thisDefender->calculateDefenseUnitPosition(targetX, targetY, index); if (defStatus > 0) { targetX = thisDefender->getTargetX(); targetY = thisDefender->getTargetY(); retVal = new int[4]; retVal[0] = thisDefender->getSourceUnit(); retVal[1] = thisDefender->getUnit(); retVal[2] = thisDefender->getAngle(); retVal[3] = thisDefender->getPower(); } if (defStatus == 0) { retVal = new int[4]; retVal[0] = 0; } if (defStatus == -1) { if (thisDefender->getTargetX() || thisDefender->getTargetY()) { targetX = thisDefender->getTargetX(); targetY = thisDefender->getTargetY(); } retVal = new int[4]; retVal[0] = thisDefender->getSourceUnit(); retVal[1] = thisDefender->getUnit(); retVal[2] = thisDefender->getAngle(); retVal[3] = thisDefender->getPower(); } if (defStatus == -3) { retVal = new int[4]; retVal[0] = 0; retVal[1] = SKIP_TURN; retVal[2] = 0; retVal[3] = 0; } assert(targetX >= 0 && targetY >= 0); if (retVal[1] == ITEM_CRAWLER) { warning("defend target is launching a crawler"); } delete thisDefender; return retVal; } int AI::getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled) { assert((unitType >= 0) && (unitType <= 12)); int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_CLOSEST_UNIT], 7, x, y, radius, player, alignment, unitType, checkUnitEnabled); return retVal; } int AI::getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled, int minDist) { assert((unitType >= 0) && (unitType <= 12)); int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_CLOSEST_UNIT], 8, x, y, radius, player, alignment, unitType, checkUnitEnabled, minDist); return retVal; } int AI::getDistance(int originX, int originY, int endX, int endY) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_WORLD_DIST], 4, originX, originY, endX, endY); return retVal; } int AI::calcAngle(int originX, int originY, int endX, int endY) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_WORLD_ANGLE], 5, originX, originY, endX, endY, 0); return retVal; } int AI::calcAngle(int originX, int originY, int endX, int endY, int noWrapFlag) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_WORLD_ANGLE], 5, originX, originY, endX, endY, noWrapFlag); return retVal; } int AI::getTerrain(int x, int y) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_TERRAIN_TYPE], 2, x, y); return retVal; } int AI::estimateNextRoundEnergy(int player) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_ESTIMATE_NEXT_ROUND_ENERGY], 1, player); return retVal / 10; } int AI::getHubX(int hub) { assert(hub >= 0 && hub <= 500); int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_HUB_X, hub); return retVal; } int AI::getHubY(int hub) { assert(hub >= 0 && hub <= 500); int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_HUB_Y, hub); return retVal; } int AI::getMaxX() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WORLD_X_SIZE); return retVal; } int AI::getMaxY() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WORLD_Y_SIZE); return retVal; } int AI::getCurrentPlayer() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_CURRENT_PLAYER); assert(retVal != 0); return retVal; } int AI::getMaxPower() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_MAX_POWER); return retVal; } int AI::getMinPower() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_MIN_POWER); return retVal; } int AI::getTerrainSquareSize() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_TERRAIN_SQUARE_SIZE); return retVal; } int AI::getBuildingOwner(int building) { assert((building > 0) && (building < 501)); int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_OWNER, building); return retVal; } int AI::getBuildingState(int building) { assert((building > 0) && (building < 501)); int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_STATE, building); return retVal; } int AI::getBuildingType(int building) { assert((building > 0) && (building < 501)); int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TYPE, building); return retVal; } int AI::getBuildingArmor(int building) { assert((building > 0) && (building < 501)); int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_ARMOR, building); return retVal; } int AI::getBuildingWorth(int building) { assert((building > 0) && (building < 501)); int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_WORTH, building); return retVal; } int AI::getEnergyPoolsArray() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_ENERGY_POOLS_ARRAY); return retVal; } int AI::getCoordinateVisibility(int x, int y, int playerNum) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 4, D_GET_COORDINATE_VISIBILITY, x, y, playerNum); return retVal; } int AI::getUnitVisibility(int unit, int playerNum) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 3, D_GET_UNIT_VISIBILITY, unit, playerNum); return retVal; } int AI::getEnergyPoolVisibility(int pool, int playerNum) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 3, D_GET_ENERGY_POOL_VISIBILITY, pool, playerNum); return retVal; } int AI::getNumberOfPools() { int retVal = 0; if (_aiType[getCurrentPlayer()]->getID() == ENERGY_HOG) { retVal = 1; } else { retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_POOLS); } return retVal; } int AI::getNumberOfPlayers() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_PLAYERS); return retVal; } int AI::getPlayerEnergy() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_PLAYER_ENERGY); return static_cast(static_cast(retVal) / 10.0); } int AI::getPlayerMaxTime() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_PLAYER_MAX_TIME); return retVal; } int AI::getWindXSpeed() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED); return retVal; } int AI::getWindYSpeed() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED); return retVal; } int AI::getTotalWindSpeed() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_TOTAL_WIND_SPEED); return retVal; } int AI::getWindXSpeedMax() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED_MAX); return retVal; } int AI::getWindYSpeedMax() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED_MAX); return retVal; } int AI::getBigXSize() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_BIG_X_SIZE); return retVal; } int AI::getBigYSize() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_BIG_Y_SIZE); return retVal; } int AI::getEnergyPoolWidth(int pool) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_ENERGY_POOL_WIDTH, pool); return retVal; } int AI::getBuildingMaxArmor(int building) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_MAX_ARMOR, building); return retVal; } int AI::getTimerValue(int timerNum) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_TIMER_VALUE, timerNum); return retVal; } int AI::getLastAttacked(int &x, int &y) { int currentPlayer = getCurrentPlayer(); x = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_LAST_ATTACKED_X, currentPlayer); y = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_LAST_ATTACKED_Y, currentPlayer); if (x || y) return 1; return 0; } int AI::getPlayerTeam(int player) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_PLAYER_TEAM, player); return retVal; } int AI::getBuildingTeam(int building) { assert((building >= 1) && (building <= 500)); if (getBuildingOwner(building) == 0) return 0; int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TEAM, building); return retVal; } int AI::getFOW() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_FOW); return retVal; } int AI::getAnimSpeed() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_ANIM_SPEED); return retVal; } int AI::getBuildingStackPtr() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_BUILDING_STACK_PTR); return retVal; } int AI::getTurnCounter() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_TURN_COUNTER); return retVal; } int AI::getGroundAltitude(int x, int y) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_GROUND_ALTITUDE], 2, x, y); return retVal; } int AI::checkForCordOverlap(int xStart, int yStart, int affectRadius, int simulateFlag) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_FOR_CORD_OVERLAP], 4, xStart, yStart, affectRadius, simulateFlag); return retVal; } int AI::checkForAngleOverlap(int unit, int angle) { assert(angle > -721); assert(angle < 721); if (!unit) return 0; int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_FOR_ANGLE_OVERLAP], 2, unit, angle); return retVal; } int AI::checkForUnitOverlap(int x, int y, int radius, int ignoredUnit) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_FOR_UNIT_OVERLAP], 4, x, y, radius, ignoredUnit); return retVal; } int AI::checkForEnergySquare(int x, int y) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_FOR_ENERGY_SQUARE], 2, x, y); return retVal; } int AI::aiChat() { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_AI_CHAT], 0); return retVal; } int AI::getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold, int olFlag) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_POWER_ANGLE_FROM_POINT], 6, originX, originY, endX, endY, threshold, olFlag); return retVal; } int AI::getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_POWER_ANGLE_FROM_POINT], 5, originX, originY, endX, endY, threshold); return retVal; } int AI::checkIfWaterState(int x, int y) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_IF_WATER_STATE], 2, x, y); return retVal; } int AI::checkIfWaterSquare(int x, int y) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_IF_WATER_SQUARE], 2, x, y); return retVal; } int AI::getUnitsWithinRadius(int x, int y, int radius) { assert(x >= 0); assert(y >= 0); assert(radius >= 0); debug(0, "getUnitsWithinRadius(%d, %d, %d)", x, y, radius); int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_UNITS_WITHIN_RADIUS], 3, x, y, radius); return retVal; } int AI::getLandingPoint(int x, int y, int power, int angle) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_LANDING_POINT], 4, x, y, power, angle); return retVal; } int AI::getEnemyUnitsVisible(int playerNum) { int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_ENEMY_UNITS_VISIBLE], 1, playerNum); return retVal; } float AI::degToRad(float degrees) { return degrees * M_PI / 180.; } void AI::limitLocation(int &a, int &b, int c, int d) { if (a >= 0) { a = (a % c); } else { a = (c - (abs(a) % c)); } if (b >= 0) { b = (b % d); } else { b = (d - (abs(b) % d)); } } int AI::energyPoolSize(int pool) { int width = getEnergyPoolWidth(pool); switch (width) { case 126: return 115; case 116: return 100; case 63: return 60; } return 0; } int AI::getMaxCollectors(int pool) { int width = getEnergyPoolWidth(pool); switch (width) { case 126: return 4; case 116: return 3; case 63: return 2; } return 0; } int AI::simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int isEnergy) { static int sXSpeed = 0; static int sYSpeed = 0; static int sZSpeed = 0; static int sXLoc = 0; static int sYLoc = 0; static int sZLoc = 0; static int sFrictionCount = 0; static int sWhichRadius = 0; static int sWhichUnit = 0; int gWindXSpeed = getWindXSpeed(); int gWindYSpeed = getWindYSpeed(); int gTotalWindSpeed = getTotalWindSpeed(); int gWindXSpeedMax = getWindXSpeedMax(); int gWindYSpeedMax = getWindYSpeedMax(); int bigXSize = getBigXSize(); int bigYSize = getBigYSize(); int groundAltitude = 0; int totalSpeed = 0; int resultingPoint = 0; int unscaledXLoc = 0; int unscaledYLoc = 0; int terrainType = 0; int passedBeyondUnit = 0; int currentDist = 0; if (!numSteps) numSteps = 1; if (!sXSpeed && !sYSpeed) { sZSpeed = (static_cast(.70711 * power)) ; sXSpeed = (static_cast(cos(degToRad(angle)) * sZSpeed)) ; sYSpeed = (static_cast(sin(degToRad(angle)) * sZSpeed)) ; sZSpeed *= SCALE_Z; sZLoc = (getGroundAltitude(x, y) + HEIGHT_LOW + 10) * SCALE_Z; sXLoc = x * SCALE_X; sYLoc = y * SCALE_Y; sFrictionCount = 0; sWhichRadius = NODE_DETECT_RADIUS + 1; sWhichUnit = getClosestUnit(x + 10, y, 30, getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 0, 0); } for (int i = 1; i <= numSteps; i++) { unscaledXLoc = sXLoc / SCALE_X; unscaledYLoc = sYLoc / SCALE_Y; groundAltitude = getGroundAltitude(unscaledXLoc, unscaledYLoc); groundAltitude *= SCALE_Z; sZLoc += sZSpeed / SCALE_Z; resultingPoint = MAX(1, unscaledXLoc + unscaledYLoc * getMaxX()); if (sZLoc <= groundAltitude) { terrainType = getTerrain(unscaledXLoc, unscaledYLoc); sXSpeed = 0; sYSpeed = 0; sFrictionCount = 0; if (terrainType == TERRAIN_TYPE_GOOD) return resultingPoint; else return 0 - resultingPoint; } else { if (checkIfWaterState(unscaledXLoc, unscaledYLoc)) { sXSpeed = 0; sYSpeed = 0; sFrictionCount = 0; return 0 - resultingPoint; } else { int cfco = 0; int cfuo = 0; int cfes = 0; int cfao = 0; cfao = checkForAngleOverlap(sWhichUnit, angle); cfco = checkForCordOverlap(unscaledXLoc, unscaledYLoc, sWhichRadius, 1); cfuo = checkForUnitOverlap(unscaledXLoc, unscaledYLoc, sWhichRadius, sWhichUnit); if (!isEnergy) cfes = checkForEnergySquare(unscaledXLoc, unscaledYLoc); if (cfco || cfuo || cfes || cfao) { sXSpeed = 0; sYSpeed = 0; sFrictionCount = 0; return 0 - resultingPoint; } else { sFrictionCount++; if (sFrictionCount == 10) { sFrictionCount = 0; if (!gWindXSpeed) sXSpeed = sXSpeed * .95; if (!gWindYSpeed) sYSpeed = sYSpeed * .95; } if (passedBeyondUnit) { totalSpeed = getDistance(0, 0, sXSpeed, sYSpeed); if (totalSpeed > gTotalWindSpeed) { if (gWindXSpeed > 0) { if (sXSpeed < gWindXSpeedMax) sXSpeed += gWindXSpeed; } else { if (sXSpeed > gWindXSpeedMax) sXSpeed += gWindXSpeed; } if (gWindYSpeed > 0) { if (sYSpeed < gWindYSpeedMax) sYSpeed += gWindYSpeed; } else { if (sYSpeed > gWindYSpeedMax) sYSpeed += gWindYSpeed; } } } else { currentDist = getDistance(unscaledXLoc, unscaledYLoc, x, y); if (currentDist > BUILDING_HUB_RADIUS + NODE_DIAMETER) passedBeyondUnit = 1; } sXLoc += sXSpeed; sYLoc += sYSpeed; limitLocation(sXLoc, sYLoc, bigXSize, bigYSize); sZSpeed -= GRAVITY_CONSTANT; } } } } return 0; } int AI::simulateWeaponLaunch(int x, int y, int power, int angle, int numSteps) { static int sXSpeed = 0; static int sYSpeed = 0; static int sZSpeed = 0; static int sXLoc = 0; static int sYLoc = 0; static int sZLoc = 0; static int sFrictionCount = 0; int gWindXSpeed = getWindXSpeed(); int gWindYSpeed = getWindYSpeed(); int gTotalWindSpeed = getTotalWindSpeed(); int gWindXSpeedMax = getWindXSpeedMax(); int gWindYSpeedMax = getWindYSpeedMax(); int bigXSize = getBigXSize(); int bigYSize = getBigYSize(); int groundAltitude = 0; int totalSpeed = 0; int resultingPoint = 0; int unscaledXLoc = 0; int unscaledYLoc = 0; int terrainType = 0; int passedBeyondUnit = 0; int currentDist = 0; if (!numSteps) numSteps = 1; if (!sXSpeed && !sYSpeed) { sZSpeed = (static_cast(.70711 * power)) ; sXSpeed = (static_cast(cos(degToRad(angle)) * sZSpeed)) ; sYSpeed = (static_cast(sin(degToRad(angle)) * sZSpeed)) ; sZSpeed *= SCALE_Z; sZLoc = (getGroundAltitude(x, y) + HEIGHT_LOW + 10) * SCALE_Z; sXLoc = x * SCALE_X; sYLoc = y * SCALE_Y; sFrictionCount = 0; } for (int i = 1; i <= numSteps; i++) { unscaledXLoc = sXLoc / SCALE_X; unscaledYLoc = sYLoc / SCALE_Y; groundAltitude = getGroundAltitude(unscaledXLoc, unscaledYLoc); groundAltitude *= SCALE_Z; sZLoc += sZSpeed / SCALE_Z; resultingPoint = MAX(1, unscaledXLoc + unscaledYLoc * getMaxX()); if (sZLoc <= groundAltitude) { terrainType = getTerrain(unscaledXLoc, unscaledYLoc); sXSpeed = 0; sYSpeed = 0; sFrictionCount = 0; if (terrainType == TERRAIN_TYPE_GOOD) return resultingPoint; else return 0 - resultingPoint; } else { sFrictionCount++; if (sFrictionCount == 10) { sFrictionCount = 0; if (!gWindXSpeed) sXSpeed = sXSpeed * .95; if (!gWindYSpeed) sYSpeed = sYSpeed * .95; } if (passedBeyondUnit) { totalSpeed = getDistance(0, 0, sXSpeed, sYSpeed); if (totalSpeed > gTotalWindSpeed) { if (gWindXSpeed > 0) { if (sXSpeed < gWindXSpeedMax) sXSpeed += gWindXSpeed; } else { if (sXSpeed > gWindXSpeedMax) sXSpeed += gWindXSpeed; } if (gWindYSpeed > 0) { if (sYSpeed < gWindYSpeedMax) sYSpeed += gWindYSpeed; } else { if (sYSpeed > gWindYSpeedMax) sYSpeed += gWindYSpeed; } } } else { currentDist = getDistance(unscaledXLoc, unscaledYLoc, x, y); if (currentDist > BUILDING_HUB_RADIUS + NODE_DIAMETER) passedBeyondUnit = 1; } sXLoc += sXSpeed; sYLoc += sYSpeed; limitLocation(sXLoc, sYLoc, bigXSize, bigYSize); sZSpeed -= GRAVITY_CONSTANT; } } return 0; } int AI::fakeSimulateWeaponLaunch(int x, int y, int power, int angle) { int distance = power * 480 / getMaxPower(); float radAngle = degToRad(angle); int maxX = getMaxX(); int maxY = getMaxY(); x += distance * cos(radAngle); y += distance * sin(radAngle); x = (x + maxX) % maxX; y = (y + maxY) % maxY; return MAX(1, x + y * maxX); } int AI::getEnergyHogType() { return _energyHogType; } } // End of namespace Scumm