diff options
38 files changed, 6724 insertions, 81 deletions
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 8039c5f282..116ffdd5a2 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -2934,6 +2934,84 @@ static const uint16 qfg3PatchChiefPriority[] = { PATCH_END }; +// There are 3 points that can't be achieved in the game. They should've been +// awarded for telling Rakeesh and Kreesha (room 285) about the Simabni +// initiation. +// However the array of posibble messages the hero can tell in that room +// (local 156) is missing the "Tell about Initiation" message (#31) which +// awards these points. +// This patch adds the message to that array, thus allowing the hero to tell +// that message (after completing the initiation) and gain the 3 points. +// A side effect of increasing the local156 array is that the next local +// array is shifted and shrinks in size from 4 words to 3. The patch changes +// the 2 locations in the script that reference that array, to point to the new +// location ($aa --> $ab). It is safe to shrink the 2nd array to 3 words +// because only the first element in it is ever used. +// +// Note: You have to re-enter the room in case a saved game was loaded from a +// previous version of ScummVM and that saved game was made inside that room. +// +// Applies to: English, French, German, Italian, Spanish and the GOG release. +// Responsible method: heap in script 285 +// Fixes bug #7086 +static const uint16 qfg3SignatureMissingPoints1[] = { + // local[$9c] = [0 -41 -76 1 -30 -77 -33 -34 -35 -36 -37 -42 -80 999] + // local[$aa] = [0 0 0 0] + SIG_UINT16(0x0000), // 0 START MARKER + SIG_MAGICDWORD, + SIG_UINT16(0xFFD7), // -41 "Greet" + SIG_UINT16(0xFFB4), // -76 "Say Good-bye" + SIG_UINT16(0x0001), // 1 "Tell about Tarna" + SIG_UINT16(0xFFE2), // -30 "Tell about Simani" + SIG_UINT16(0xFFB3), // -77 "Tell about Prisoner" + SIG_UINT16(0xFFDF), // -33 "Dispelled Leopard Lady" + SIG_UINT16(0xFFDE), // -34 "Tell about Leopard Lady" + SIG_UINT16(0xFFDD), // -35 "Tell about Leopard Lady" + SIG_UINT16(0xFFDC), // -36 "Tell about Leopard Lady" + SIG_UINT16(0xFFDB), // -37 "Tell about Village" + SIG_UINT16(0xFFD6), // -42 "Greet" + SIG_UINT16(0xFFB0), // -80 "Say Good-bye" + SIG_UINT16(0x03E7), // 999 END MARKER + SIG_ADDTOOFFSET(+2), // local[$aa][0] + SIG_END +}; + +static const uint16 qfg3PatchMissingPoints1[] = { + PATCH_ADDTOOFFSET(+14), + PATCH_UINT16(0xFFE1), // -31 "Tell about Initiation" + PATCH_UINT16(0xFFDE), // -34 "Tell about Leopard Lady" + PATCH_UINT16(0xFFDD), // -35 "Tell about Leopard Lady" + PATCH_UINT16(0xFFDC), // -36 "Tell about Leopard Lady" + PATCH_UINT16(0xFFDB), // -37 "Tell about Village" + PATCH_UINT16(0xFFD6), // -42 "Greet" + PATCH_UINT16(0xFFB0), // -80 "Say Good-bye" + PATCH_UINT16(0x03E7), // 999 END MARKER + PATCH_GETORIGINALBYTE(+28), // local[$aa][0].low + PATCH_GETORIGINALBYTE(+29), // local[$aa][0].high + PATCH_END +}; + +static const uint16 qfg3SignatureMissingPoints2a[] = { + SIG_MAGICDWORD, + 0x35, 0x00, // ldi 0 + 0xb3, 0xaa, // sali local[$aa] + SIG_END +}; + +static const uint16 qfg3SignatureMissingPoints2b[] = { + SIG_MAGICDWORD, + 0x36, // push + 0x5b, 0x02, 0xaa, // lea local[$aa] + SIG_END +}; + +static const uint16 qfg3PatchMissingPoints2[] = { + PATCH_ADDTOOFFSET(+3), + 0xab, // local[$aa] ==> local[$ab] + PATCH_END +}; + + // Partly WORKAROUND: // During combat, the game is not properly throttled. That's because the game uses // an inner loop for combat and does not iterate through the main loop. @@ -2995,14 +3073,17 @@ static const uint16 qfg3PatchCombatSpeedThrottling2[] = { // script, description, signature patch static const SciScriptPatcherEntry qfg3Signatures[] = { - { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, - { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog }, - { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt }, - { true, 52, "export character save bug", 2, qfg3SignatureExportChar, qfg3PatchExportChar }, - { true, 54, "import character from QfG1 bug", 1, qfg3SignatureImportQfG1Char, qfg3PatchImportQfG1Char }, - { true, 640, "chief in hut priority fix", 1, qfg3SignatureChiefPriority, qfg3PatchChiefPriority }, - { true, 550, "combat speed throttling script", 1, qfg3SignatureCombatSpeedThrottling1, qfg3PatchCombatSpeedThrottling1 }, - { true, 550, "combat speed throttling heap", 1, qfg3SignatureCombatSpeedThrottling2, qfg3PatchCombatSpeedThrottling2 }, + { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, + { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog }, + { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt }, + { true, 52, "export character save bug", 2, qfg3SignatureExportChar, qfg3PatchExportChar }, + { true, 54, "import character from QfG1 bug", 1, qfg3SignatureImportQfG1Char, qfg3PatchImportQfG1Char }, + { true, 640, "chief in hut priority fix", 1, qfg3SignatureChiefPriority, qfg3PatchChiefPriority }, + { true, 285, "missing points for telling about initiation heap", 1, qfg3SignatureMissingPoints1, qfg3PatchMissingPoints1 }, + { true, 285, "missing points for telling about initiation script", 1, qfg3SignatureMissingPoints2a, qfg3PatchMissingPoints2 }, + { true, 285, "missing points for telling about initiation script", 1, qfg3SignatureMissingPoints2b, qfg3PatchMissingPoints2 }, + { true, 550, "combat speed throttling script", 1, qfg3SignatureCombatSpeedThrottling1, qfg3PatchCombatSpeedThrottling1 }, + { true, 550, "combat speed throttling heap", 1, qfg3SignatureCombatSpeedThrottling2, qfg3PatchCombatSpeedThrottling2 }, SCI_SIGNATUREENTRY_TERMINATOR }; diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h index 06e6b249f6..72c11c95a1 100644 --- a/engines/scumm/he/intern_he.h +++ b/engines/scumm/he/intern_he.h @@ -247,10 +247,6 @@ public: void queueAuxEntry(int actorNum, int subIndex); void remapHEPalette(const uint8 *src, uint8 *dst); - -public: - /* Moonbase stuff */ - Moonbase *_moonbase; }; class ScummEngine_v72he : public ScummEngine_v71he { @@ -430,6 +426,7 @@ protected: class ScummEngine_v90he : public ScummEngine_v80he { friend class LogicHE; + friend class Moonbase; friend class MoviePlayer; friend class Sprite; @@ -458,6 +455,9 @@ protected: Sprite *_sprite; public: + Moonbase *_moonbase; + +public: ScummEngine_v90he(OSystem *syst, const DetectorResult &dr); ~ScummEngine_v90he(); @@ -553,8 +553,15 @@ protected: byte VAR_NUM_PALETTES; byte VAR_NUM_UNK; +public: // FIXME. TODO. Should be protected. Used by Moonbase byte VAR_U32_VERSION; byte VAR_U32_ARRAY_UNK; + byte VAR_U32_USER_VAR_A; + byte VAR_U32_USER_VAR_B; + byte VAR_U32_USER_VAR_C; + byte VAR_U32_USER_VAR_D; + byte VAR_U32_USER_VAR_E; + byte VAR_U32_USER_VAR_F; }; class ScummEngine_v99he : public ScummEngine_v90he { diff --git a/engines/scumm/he/moonbase/ai_defenseunit.cpp b/engines/scumm/he/moonbase/ai_defenseunit.cpp new file mode 100644 index 0000000000..f1f0251dc6 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_defenseunit.cpp @@ -0,0 +1,759 @@ +/* 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 "common/rect.h" +#include "common/util.h" +#include "scumm/he/intern_he.h" +#include "scumm/he/moonbase/ai_defenseunit.h" +#include "scumm/he/moonbase/ai_main.h" + +namespace Scumm { + +DefenseUnit::DefenseUnit() { + _state = DUS_ON; +} + +DefenseUnit::DefenseUnit(DefenseUnit *inUnit) { + _id = inUnit->getID(); + _pos.x = inUnit->getPosX(); + _pos.y = inUnit->getPosY(); + _distanceTo = inUnit->getDistanceTo(); + _state = inUnit->getState(); + _radius = inUnit->getRadius(); + _armor = inUnit->getArmor(); +} + +DefenseUnit::~DefenseUnit() { +} + +Common::Point *AntiAirUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + float ratio; + int radius; + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + radius = getRadius(); + + if ((distance < radius) || (getState() == DUS_OFF)) { + targetPos->x = getPosX(); + targetPos->y = getPosY(); + } else { + ratio = MAX(0, (getRadius() / distance)); + targetPos->x = getPosX() - ratio * (getPosX() - sourceX); + targetPos->y = getPosY() - ratio * (getPosY() - sourceY); + } + + break; + + case ITEM_EMP: + if (getRadius() + 215 > distance) { //emp radius + double x1 = static_cast<double>(sourceX); + double y1 = static_cast<double>(sourceY); + double x2 = static_cast<double>(getPosX()); + double y2 = static_cast<double>(getPosY()); + double r1 = static_cast<double>(215); + double r2 = static_cast<double>(getRadius() + 3); + double d = static_cast<double>(distance); + + //formulae for calculating one point of intersection of two circles + float root = sqrt((((r1 + r2) * (r1 + r2)) - (d * d)) * ((d * d) - ((r2 - r1) * (r2 - r1)))); + int x = ((x1 + x2) / 2) + ((x2 - x1) * (r1 * r1 - r2 * r2)) / (2 * d * d) + ((y2 - y1) / (2 * d * d)) * root; + int y = ((y1 + y2) / 2) + ((y2 - y1) * (r1 * r1 - r2 * r2)) / (2 * d * d) - ((x2 - x1) / (2 * d * d)) * root; + + targetPos->x = x; + targetPos->y = y; + } else { + ratio = 1 - (getRadius() / static_cast<float>(distance - 20)); + targetPos->x = sourceX + ratio * (getPosX() - sourceX); + targetPos->y = sourceY + ratio * (getPosY() - sourceY); + } + + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int AntiAirUnit::selectWeapon(int index) { + switch (index) { + case 0: + return ITEM_CLUSTER; + break; + + case 1: + return ITEM_EMP; + break; + + case 2: + if (getState() == DUS_OFF) { + if (getPlayerEnergy() > 6) { + if (!_vm->_rnd.getRandomNumber(3)) { + return ITEM_VIRUS; + } + } + + if (getPlayerEnergy() > 2) { + if (!_vm->_rnd.getRandomNumber(1)) { + return ITEM_SPIKE; + } + } + + return ITEM_BOMB; + } + + return ITEM_CLUSTER; + break; + + default: + return ITEM_CLUSTER; + break; + } +} + +Common::Point *ShieldUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + float ratio; + Common::Point *targetPos = new Common::Point; + + if (getState() == DUS_OFF) { + targetPos->x = getPosX(); + targetPos->y = getPosY(); + } else { + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + ratio = MAX(0.0, 1.0 - (static_cast<float>(getRadius()) / static_cast<float>(distance - 20))); + { + int maxX = getMaxX(); + int maxY = getMaxY(); + int thisX = (static_cast<int>(sourceX + ratio * (getPosX() - sourceX)) + maxX) % maxX; + int thisY = (static_cast<int>(sourceY + ratio * (getPosY() - sourceY)) + maxY) % maxY; + targetPos->x = thisX; + targetPos->y = thisY; + } + break; + + case ITEM_EMP: + if (getRadius() + 215 > distance) { //emp radius + double x1 = static_cast<double>(sourceX); + double y1 = static_cast<double>(sourceY); + double x2 = static_cast<double>(getPosX()); + double y2 = static_cast<double>(getPosY()); + double r1 = static_cast<double>(215); + double r2 = static_cast<double>(getRadius() + 10); + double d = static_cast<double>(distance); + + //formulae for calculating one point of intersection of two circles + float root = sqrt((((r1 + r2) * (r1 + r2)) - (d * d)) * ((d * d) - ((r2 - r1) * (r2 - r1)))); + int x = ((x1 + x2) / 2) + ((x2 - x1) * (r1 * r1 - r2 * r2)) / (2 * d * d) + ((y2 - y1) / (2 * d * d)) * root; + int y = ((y1 + y2) / 2) + ((y2 - y1) * (r1 * r1 - r2 * r2)) / (2 * d * d) - ((x2 - x1) / (2 * d * d)) * root; + + targetPos->x = x; + targetPos->y = y; + } else { + ratio = 1 - (getRadius() / static_cast<float>(distance - 20)); + targetPos->x = sourceX + ratio * (getPosX() - sourceX); + targetPos->y = sourceY + ratio * (getPosY() - sourceY); + } + + if (distance < getRadius()) { + targetPos->x = getPosX(); + targetPos->y = getPosY(); + } + + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + } + + return targetPos; +} + +int ShieldUnit::selectWeapon(int index) { + warning("Shield weapon select"); + + int myUnit = getClosestUnit(getPosX(), getPosY(), getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 0); + int dist = getDistance(getPosX(), getPosY(), getHubX(myUnit), getHubY(myUnit)); + + if ((dist < (getRadius() - 20)) && (dist > 90)) { + return ITEM_SPIKE; + } + + switch (index) { + case 0: + if (getState() == DUS_OFF) { + if (getPlayerEnergy() < 3) { + return ITEM_BOMB; + } else { + return ITEM_SPIKE; + } + } + + return ITEM_EMP; + break; + + case 1: + if (dist < getRadius() + 150) { + return ITEM_EMP; + } else { + return ITEM_CRAWLER; + } + + break; + + default: + return ITEM_EMP; + break; + } +} + +Common::Point *MineUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + float ratio; + Common::Point *targetPos = new Common::Point; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_EMP: + ratio = 1 - (getRadius() / static_cast<float>(distance - 20)); + targetPos->x = sourceX + ratio * (getPosX() - sourceX); + targetPos->y = sourceY + ratio * (getPosY() - sourceY); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int MineUnit::selectWeapon(int index) { + int myUnit = getClosestUnit(getPosX(), getPosY(), getMaxX(), getCurrentPlayer(), 1, 0, 0, 0); + int x = getPosX(); + int y = getPosY(); + + int dist = getDistance(x, y, getHubX(myUnit), getHubY(myUnit)); + + if ((getState() == DUS_ON) && (dist < 110)) { + return ITEM_EMP; + } else { + return ITEM_BOMB; + } +} + + +Common::Point *HubUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int HubUnit::selectWeapon(int index) { + warning("Hub weapon select"); + + int energy = getPlayerEnergy(); + + if (energy > 6) { + //possibly choose crawler + if (getBuildingWorth(getID()) > 21) { + return ITEM_CRAWLER; + } + } + + //choose betw/ bomb and cluster + if (getBuildingArmor(getID()) < 1.5) { + return ITEM_CLUSTER; + } + + if (energy > 2) { + if (!_vm->_rnd.getRandomNumber(3)) { + return ITEM_SPIKE; + } + + if (!_vm->_rnd.getRandomNumber(4)) { + return ITEM_GUIDED; + } + + if (!_vm->_rnd.getRandomNumber(4)) { + return ITEM_MINE; + } + + if (!_vm->_rnd.getRandomNumber(9)) { + return ITEM_EMP; + } + } + + return ITEM_BOMB; +} + + +Common::Point *TowerUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_SPIKE: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int TowerUnit::selectWeapon(int index) { + switch (index) { + case 0: + return ITEM_SPIKE; + break; + + default: + return ITEM_SPIKE; + break; + } +} + + +Common::Point *BridgeUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int BridgeUnit::selectWeapon(int index) { + switch (index) { + case 0: + return ITEM_BOMB; + break; + + case 1: + return ITEM_CLUSTER; + break; + + default: + return ITEM_BOMB; + break; + } +} + + +Common::Point *EnergyUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int EnergyUnit::selectWeapon(int index) { + warning("Energy weapon select"); + + int energy = getPlayerEnergy(); + + if (energy > 6) { + //possibly choose crawler + if (getBuildingWorth(getID()) > 21) { + return ITEM_CRAWLER; + } + } + + //choose betw/ bomb and cluster + if (getBuildingArmor(getID()) < 1.5) { + return ITEM_CLUSTER; + } + + if (energy > 2) { + if (!_vm->_rnd.getRandomNumber(3)) { + return ITEM_EMP; + } + } + + return ITEM_BOMB; +} + +Common::Point *OffenseUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int OffenseUnit::selectWeapon(int index) { + warning("Offense weapon select"); + + int energy = getPlayerEnergy(); + + if (energy > 6) { + //possibly choose crawler + if (getBuildingWorth(getID()) > 21) { + return ITEM_CRAWLER; + } + } + + //choose betw/ bomb and cluster + if (getBuildingArmor(getID()) < 1.5) { + return ITEM_CLUSTER; + } + + return ITEM_BOMB; +} + +Common::Point *CrawlerUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) + distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int CrawlerUnit::selectWeapon(int index) { + warning("Crawler weapon select"); + int myUnit = getClosestUnit(getPosX(), getPosY(), getMaxX(), getCurrentPlayer(), 1, 0, 0, 0); + int dist = getDistance(getHubX(myUnit), getHubY(myUnit), getPosX(), getPosY()); + + int x = getPosX(); + int y = getPosY(); + int energy = getPlayerEnergy(); + int terrain = getTerrain(x, y); + + if (terrain != TERRAIN_TYPE_WATER) { + if ((energy > 2) && (dist < 220)) { + return ITEM_RECLAIMER; + } else { + return ITEM_BOMB; + } + } else { + if (energy > 6) { + return ITEM_CRAWLER; + } + + if (energy > 2) { + if (_vm->_rnd.getRandomNumber(1)) { + return ITEM_MINE; + } else { + return ITEM_TIME_EXPIRED; + } + } + } + + return SKIP_TURN; +} + +AntiAirUnit::AntiAirUnit() { + setRadius(190); + setArmor(3); + setCost(1); +} + +ShieldUnit::ShieldUnit() { + setRadius(170); + setArmor(3); + setCost(7); +} + +MineUnit::MineUnit() { + setRadius(80); + setArmor(1); + setCost(3); +} + +HubUnit::HubUnit() { + setRadius(1); + setArmor(5); + setCost(7); +} + +TowerUnit::TowerUnit() { + setRadius(1); + setArmor(3); + setCost(1); +} + +BridgeUnit::BridgeUnit() { + setRadius(1); + setArmor(3); + setCost(1); +} + +EnergyUnit::EnergyUnit() { + setRadius(1); + setArmor(5); + setCost(7); +} + +OffenseUnit::OffenseUnit() { + setRadius(1); + setArmor(3); + setCost(7); +} + +CrawlerUnit::CrawlerUnit() { + setRadius(1); + setArmor(3); + setCost(7); +} + +AntiAirUnit::AntiAirUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); + +} + +ShieldUnit::ShieldUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +MineUnit::MineUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +HubUnit::HubUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +TowerUnit::TowerUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +BridgeUnit::BridgeUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +EnergyUnit::EnergyUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +OffenseUnit::OffenseUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +CrawlerUnit::CrawlerUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_defenseunit.h b/engines/scumm/he/moonbase/ai_defenseunit.h new file mode 100644 index 0000000000..e658fc31ab --- /dev/null +++ b/engines/scumm/he/moonbase/ai_defenseunit.h @@ -0,0 +1,190 @@ +/* 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. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_DEFENCEUNIT_H +#define SCUMM_HE_MOONBASE_AI_DEFENCEUNIT_H + +namespace Scumm { + +enum { + DUT_ANTI_AIR = 1, + DUT_SHIELD = 2, + DUT_MINE = 3, + DUT_HUB = 4, + DUT_TOWER = 5, + DUT_BRIDGE = 6, + DUT_ENERGY = 7, + DUT_OFFENSE = 8, + DUT_CRAWLER = 9 +}; + +enum { + DUS_ON = 1, + DUS_OFF = 2, + DUS_DESTROYED = 3 +}; + +class DefenseUnit { +private: + int _id; + Common::Point _pos; + int _distanceTo; + int _state; + int _radius; + int _armor; + int _cost; + +public: + DefenseUnit(); + DefenseUnit(DefenseUnit *inUnit); + + virtual ~DefenseUnit(); + + void setID(int id) { _id = id; } + void setDistanceTo(int distanceTo) { _distanceTo = distanceTo; } + void setState(int state) { _state = state; } + void setRadius(int radius) { _radius = radius; } + void setArmor(int armor) { _armor = armor; } + void setDamage(int damage) { _armor -= damage; } + void setPos(int x, int y) { + _pos.x = x; + _pos.y = y; + } + void setCost(int cost) { _cost = cost; } + + int getID() const { return _id; } + int getDistanceTo() const { return _distanceTo; } + int getState() const { return _state; } + int getRadius() const { return _radius; } + int getArmor() const { return _armor; } + int getPosX() const { return _pos.x; } + int getPosY() const { return _pos.y; } + int getCost() const { return _cost; } + + virtual int getType() const = 0; + + virtual Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) = 0; + virtual int selectWeapon(int index) = 0; +}; + +class AntiAirUnit : public DefenseUnit { +private: + +public: + AntiAirUnit(); + AntiAirUnit(DefenseUnit *inUnit); + int getType() const { return DUT_ANTI_AIR; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class ShieldUnit : public DefenseUnit { +private: + +public: + ShieldUnit(); + ShieldUnit(DefenseUnit *inUnit); + int getType() const { return DUT_SHIELD; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class MineUnit : public DefenseUnit { +private: + +public: + MineUnit(); + MineUnit(DefenseUnit *inUnit); + int getType() const { return DUT_MINE; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class HubUnit : public DefenseUnit { +private: + +public: + HubUnit(); + HubUnit(DefenseUnit *inUnit); + int getType() const { return DUT_HUB; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class TowerUnit : public DefenseUnit { +private: + +public: + TowerUnit(); + TowerUnit(DefenseUnit *inUnit); + int getType() const { return DUT_TOWER; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class BridgeUnit : public DefenseUnit { +private: + +public: + BridgeUnit(); + BridgeUnit(DefenseUnit *inUnit); + int getType() const { return DUT_BRIDGE; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class EnergyUnit : public DefenseUnit { +private: + +public: + EnergyUnit(); + EnergyUnit(DefenseUnit *inUnit); + int getType() const { return DUT_ENERGY; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class OffenseUnit : public DefenseUnit { +private: + +public: + OffenseUnit(); + OffenseUnit(DefenseUnit *inUnit); + int getType() const { return DUT_OFFENSE; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +class CrawlerUnit : public DefenseUnit { +private: + +public: + CrawlerUnit(); + CrawlerUnit(DefenseUnit *inUnit); + int getType() const { return DUT_CRAWLER; } + Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); + int selectWeapon(int index); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_main.cpp b/engines/scumm/he/moonbase/ai_main.cpp new file mode 100644 index 0000000000..501aab5912 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_main.cpp @@ -0,0 +1,3124 @@ +/* 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 { + +ScummEngine_v90he *_vm; + +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 +}; + +AIEntity *AItype[5]; + +int AIstate = STATE_CHOOSE_BEHAVIOR; +int behavior = 2; + +patternList *moveList[5]; + +int *storedLaunchAction[5] = {NULL}; + +const int *MCP_params; + +Common::Array<int> lastXCoord[5]; +Common::Array<int> lastYCoord[5]; + +void 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 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 setAIType(const int paramCount, const int *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 masterControlProgram(const int paramCount, const int *params) { + static Tree *myTree; + + static int index; + + MCP_params = 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 oldAIstate = 0; + + if (oldAIstate != AIstate) { + warning("<<%d>>", AIstate); + oldAIstate = AIstate; + } + + switch (AIstate) { + case STATE_CHOOSE_BEHAVIOR: + behavior = chooseBehavior(); + warning("Behavior mode: %d", behavior); + + if (_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<int>(getHubX(closestHub) + (500 * cos(degToRad(targetAngle))) + getMaxX()) % getMaxX(); + int testY = static_cast<int>(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->nukeArray(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; + + _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 = 0; + angleAdjustment = _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<float>(_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->nukeArray(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 chooseBehavior() { + static int dominantMode = 0; + + int modeValue[3]; + + const int DEFAULT_SCALE = 5; + + if (getBuildingStackPtr() < 5) + return OFFENSE_MODE; + + int currentPlayer = getCurrentPlayer(); + + int AIpersonality = AItype[currentPlayer]->getID(); + + switch (AIpersonality) { + case BRUTAKAS: + modeValue[ENERGY_MODE] = DEFAULT_SCALE; + modeValue[OFFENSE_MODE] = 2 * DEFAULT_SCALE; + modeValue[DEFENSE_MODE] = 0; + dominantMode = OFFENSE_MODE; + break; + + case AGI: + modeValue[ENERGY_MODE] = DEFAULT_SCALE; + modeValue[OFFENSE_MODE] = 0; + modeValue[DEFENSE_MODE] = 2 * DEFAULT_SCALE; + dominantMode = DEFENSE_MODE; + break; + + case EL_GATO: + modeValue[ENERGY_MODE] = 2 * DEFAULT_SCALE; + modeValue[OFFENSE_MODE] = DEFAULT_SCALE; + modeValue[DEFENSE_MODE] = 0; + dominantMode = ENERGY_MODE; + break; + + case PIXELAHT: + modeValue[ENERGY_MODE] = DEFAULT_SCALE; + modeValue[OFFENSE_MODE] = 0; + modeValue[DEFENSE_MODE] = DEFAULT_SCALE; + dominantMode = DEFENSE_MODE; + break; + + case CYBALL: + modeValue[ENERGY_MODE] = 0; + modeValue[OFFENSE_MODE] = 0; + modeValue[DEFENSE_MODE] = 0; + dominantMode = ENERGY_MODE; + break; + + case NEEP: + modeValue[ENERGY_MODE] = 0; + modeValue[OFFENSE_MODE] = DEFAULT_SCALE; + modeValue[DEFENSE_MODE] = DEFAULT_SCALE; + dominantMode = DEFENSE_MODE; + break; + + case WARCUPINE: + modeValue[ENERGY_MODE] = 5 * DEFAULT_SCALE; + modeValue[OFFENSE_MODE] = DEFAULT_SCALE; + modeValue[DEFENSE_MODE] = 0; + dominantMode = ENERGY_MODE; + break; + + case AONE: + modeValue[ENERGY_MODE] = 0; + modeValue[OFFENSE_MODE] = DEFAULT_SCALE; + modeValue[DEFENSE_MODE] = 2 * DEFAULT_SCALE; + dominantMode = DEFENSE_MODE; + break; + + case SPANDO: + modeValue[ENERGY_MODE] = DEFAULT_SCALE; + modeValue[OFFENSE_MODE] = DEFAULT_SCALE; + modeValue[DEFENSE_MODE] = DEFAULT_SCALE; + dominantMode = ENERGY_MODE; + break; + + case ORBNU_LUNATEK: + modeValue[ENERGY_MODE] = 0; + modeValue[OFFENSE_MODE] = 0; + modeValue[DEFENSE_MODE] = 0; + dominantMode = ENERGY_MODE; + break; + + case CRAWLER_CHUCKER: + modeValue[ENERGY_MODE] = 0; + modeValue[OFFENSE_MODE] = 2 * DEFAULT_SCALE; + modeValue[DEFENSE_MODE] = 0; + dominantMode = OFFENSE_MODE; + break; + + case ENERGY_HOG: + modeValue[ENERGY_MODE] = 2 * DEFAULT_SCALE; + modeValue[OFFENSE_MODE] = 0; + modeValue[DEFENSE_MODE] = 0; + 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: + modeValue[ENERGY_MODE] = 2 * DEFAULT_SCALE; + modeValue[OFFENSE_MODE] = 0; + modeValue[DEFENSE_MODE] = 0; + 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 + modeValue[ENERGY_MODE] = DEFAULT_SCALE; + modeValue[OFFENSE_MODE] = 2 * DEFAULT_SCALE; + modeValue[DEFENSE_MODE] = 0; + 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->nukeArray(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->nukeArray(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->nukeArray(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->nukeArray(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->nukeArray(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->nukeArray(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 chooseTarget(int behavior1) { + int numPools = getNumberOfPools(); + int currentPlayer = getCurrentPlayer(); + + int selection = 0; + int selectionValues[50] = {0}; + int selectionDist = 10000000; + + if (behavior1 == 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->nukeArray(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 (behavior1 == 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->nukeArray(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->nukeArray(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 (behavior1 == 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->nukeArray(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 *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)); + 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); + *retNode = myTree->aStarSearch_singlePassInit(); + + return myTree; +} + +int *approachTarget(Tree *myTree, int &xTarget, int &yTarget, Node **currentNode) { + int *retVal = NULL; + + *currentNode = NULL; + Node *retNode = myTree->aStarSearch_singlePass(currentNode); + + 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<Traveller *>(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<Traveller *>(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<int>(retTraveller->calcT()); + int temp2 = static_cast<int>(retTraveller->getValueG()); + int x = static_cast<int>(retTraveller->getPosX()); + int y = static_cast<int>(retTraveller->getPosY()); + warning("Target Coords: <%d, %d> G-value: %d T-value: %d", x, y, temp2, temp); + xTarget = x; + yTarget = y; + } + + return retVal; +} + +Tree *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(); + 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->nukeArray(unitsArray); + + if (!thisElement) { + delete myBaseTarget; + return NULL; + } + + Tree *myTree = new Tree(myBaseTarget, 4); + *retNode = myTree->aStarSearch_singlePassInit(); + + return myTree; +} + + +int *acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode) { + int currentPlayer = getCurrentPlayer(); + int *retVal = NULL; + + Node *currentNode = NULL; + Node *retNode = myTree->aStarSearch_singlePass(¤tNode); + + if (myTree->IsBaseNode(retNode)) + return acquireTarget(targetX, targetY); + + if (retNode == NULL) { + errorCode = 0; + return retVal; + } + + Sortie *thisSortie = static_cast<Sortie *>(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 *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 *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->nukeArray(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 = ((((n - 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->nukeArray(poolUnitsArray); + poolUnitsArray = 0; + return retVal; + } + } + + if (result > 0) { + _vm->nukeArray(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->nukeArray(poolUnitsArray); + poolUnitsArray = 0; + + return retVal; + } + + attempt++; + } else { + sameUnit = 0; + } + } else { + sameUnit = 0; + } + } else { + sameUnit = 0; + k++; + j = 0; + } + } else { + _vm->nukeArray(poolUnitsArray); + poolUnitsArray = 0; + return NULL; + } + + _vm->nukeArray(poolUnitsArray); + poolUnitsArray = 0; + int *retVal = new int[4]; + retVal[0] = 0; + + return retVal; +} + +int *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(); + break; + + case BUILDING_TOWER: + thisUnit = new TowerUnit(); + break; + + case BUILDING_MAIN_BASE: + thisUnit = new HubUnit(); + break; + + case BUILDING_ENERGY_COLLECTOR: + thisUnit = new EnergyUnit(); + break; + + case BUILDING_CRAWLER: + thisUnit = new CrawlerUnit(); + break; + + case BUILDING_BRIDGE: + thisUnit = new BridgeUnit(); + break; + + case BUILDING_SHIELD: + thisUnit = new ShieldUnit(); + break; + + default: + thisUnit = new HubUnit(); + 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 *defendTarget(int &targetX, int &targetY, int index) { + int *retVal = NULL; + Defender *thisDefender = new Defender; + 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 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(MCP_params[F_GET_CLOSEST_UNIT], 7, x, y, radius, player, alignment, unitType, checkUnitEnabled); + return retVal; +} + +int 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(MCP_params[F_GET_CLOSEST_UNIT], 8, x, y, radius, player, alignment, unitType, checkUnitEnabled, minDist); + return retVal; +} + +int getDistance(int originX, int originY, int endX, int endY) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_WORLD_DIST], 4, originX, originY, endX, endY); + return retVal; +} + +int calcAngle(int originX, int originY, int endX, int endY) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_WORLD_ANGLE], 5, originX, originY, endX, endY, 0); + return retVal; +} + +int calcAngle(int originX, int originY, int endX, int endY, int noWrapFlag) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_WORLD_ANGLE], 5, originX, originY, endX, endY, noWrapFlag); + return retVal; +} + +int getTerrain(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_TERRAIN_TYPE], 2, x, y); + return retVal; +} + +int estimateNextRoundEnergy(int player) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_ESTIMATE_NEXT_ROUND_ENERGY], 1, player); + return retVal / 10; +} + +int getHubX(int hub) { + assert(hub >= 0 && hub <= 500); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_HUB_X, hub); + return retVal; +} + +int getHubY(int hub) { + assert(hub >= 0 && hub <= 500); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_HUB_Y, hub); + return retVal; +} + +int getMaxX() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WORLD_X_SIZE); + return retVal; +} + +int getMaxY() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WORLD_Y_SIZE); + return retVal; +} + +int getCurrentPlayer() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_CURRENT_PLAYER); + assert(retVal != 0); + return retVal; +} + +int getMaxPower() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_MAX_POWER); + return retVal; +} + +int getMinPower() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_MIN_POWER); + return retVal; +} + +int getTerrainSquareSize() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_TERRAIN_SQUARE_SIZE); + return retVal; +} + +int getBuildingOwner(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_OWNER, building); + return retVal; +} + +int getBuildingState(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_STATE, building); + return retVal; +} + +int getBuildingType(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TYPE, building); + return retVal; +} + +int getBuildingArmor(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_ARMOR, building); + return retVal; +} + +int getBuildingWorth(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_WORTH, building); + return retVal; +} + +int getEnergyPoolsArray() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_ENERGY_POOLS_ARRAY); + return retVal; +} + +int getCoordinateVisibility(int x, int y, int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 4, D_GET_COORDINATE_VISIBILITY, x, y, playerNum); + return retVal; +} + +int getUnitVisibility(int unit, int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 3, D_GET_UNIT_VISIBILITY, unit, playerNum); + return retVal; +} + +int getEnergyPoolVisibility(int pool, int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 3, D_GET_ENERGY_POOL_VISIBILITY, pool, playerNum); + return retVal; +} + +int getNumberOfPools() { + int retVal = 0; + + if (AItype[getCurrentPlayer()]->getID() == ENERGY_HOG) { + retVal = 1; + } else { + retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_POOLS); + } + + return retVal; +} + +int getNumberOfPlayers() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_PLAYERS); + return retVal; +} + +int getPlayerEnergy() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_PLAYER_ENERGY); + return static_cast<int>(static_cast<float>(retVal) / 10.0); +} + +int getPlayerMaxTime() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_PLAYER_MAX_TIME); + return retVal; +} + +int getWindXSpeed() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED); + return retVal; +} + +int getWindYSpeed() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED); + return retVal; +} + +int getTotalWindSpeed() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_TOTAL_WIND_SPEED); + return retVal; +} + +int getWindXSpeedMax() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED_MAX); + return retVal; +} + +int getWindYSpeedMax() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED_MAX); + return retVal; +} + +int getBigXSize() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_BIG_X_SIZE); + return retVal; +} + +int getBigYSize() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_BIG_Y_SIZE); + return retVal; +} + +int getEnergyPoolWidth(int pool) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_ENERGY_POOL_WIDTH, pool); + return retVal; +} + +int getBuildingMaxArmor(int building) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_MAX_ARMOR, building); + return retVal; +} + +int getTimerValue(int timerNum) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_TIMER_VALUE, timerNum); + return retVal; +} + +int getLastAttacked(int &x, int &y) { + int currentPlayer = getCurrentPlayer(); + x = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_LAST_ATTACKED_X, currentPlayer); + y = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_LAST_ATTACKED_Y, currentPlayer); + + if (x || y) return 1; + + return 0; +} + +int getPlayerTeam(int player) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_PLAYER_TEAM, player); + return retVal; +} + +int getBuildingTeam(int building) { + assert((building >= 1) && (building <= 500)); + + if (getBuildingOwner(building) == 0) return 0; + + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TEAM, building); + return retVal; +} + +int getFOW() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_FOW); + return retVal; +} + +int getAnimSpeed() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_ANIM_SPEED); + return retVal; +} + +int getBuildingStackPtr() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_BUILDING_STACK_PTR); + return retVal; +} + +int getTurnCounter() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_TURN_COUNTER); + return retVal; +} + +int getGroundAltitude(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_GROUND_ALTITUDE], 2, x, y); + return retVal; +} + +int checkForCordOverlap(int xStart, int yStart, int affectRadius, int simulateFlag) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_CORD_OVERLAP], 4, xStart, yStart, affectRadius, simulateFlag); + return retVal; +} + +int checkForAngleOverlap(int unit, int angle) { + assert(angle > -721); + assert(angle < 721); + + if (!unit) return 0; + + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_ANGLE_OVERLAP], 2, unit, angle); + return retVal; +} + +int checkForUnitOverlap(int x, int y, int radius, int ignoredUnit) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_UNIT_OVERLAP], 4, x, y, radius, ignoredUnit); + return retVal; +} + +int checkForEnergySquare(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_ENERGY_SQUARE], 2, x, y); + return retVal; +} + +int aiChat() { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_AI_CHAT], 0); + return retVal; +} + +int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold, int olFlag) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_POWER_ANGLE_FROM_POINT], 6, originX, originY, endX, endY, threshold, olFlag); + return retVal; +} + +int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_POWER_ANGLE_FROM_POINT], 5, originX, originY, endX, endY, threshold); + return retVal; +} + +int checkIfWaterState(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_IF_WATER_STATE], 2, x, y); + return retVal; +} + +int checkIfWaterSquare(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_IF_WATER_SQUARE], 2, x, y); + return retVal; +} + +int getUnitsWithinRadius(int x, int y, int radius) { + assert(x >= 0); + assert(y >= 0); + assert(radius >= 0); + + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_UNITS_WITHIN_RADIUS], 3, x, y, radius); + return retVal; +} + +int getLandingPoint(int x, int y, int power, int angle) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_LANDING_POINT], 4, x, y, power, angle); + return retVal; +} + +int getEnemyUnitsVisible(int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_ENEMY_UNITS_VISIBLE], 1, playerNum); + return retVal; +} + +float degToRad(float degrees) { + return degrees * M_PI / 180.; +} + +void 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 energyPoolSize(int pool) { + int width = getEnergyPoolWidth(pool); + + switch (width) { + case 126: + return 115; + + case 116: + return 100; + + case 63: + return 60; + } + + return 0; +} + +int getMaxCollectors(int pool) { + int width = getEnergyPoolWidth(pool); + + switch (width) { + case 126: + return 4; + + case 116: + return 3; + + case 63: + return 2; + } + + return 0; +} + +int 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<int>(.70711 * power)) ; + sXSpeed = (static_cast<int>(cos(degToRad(angle)) * sZSpeed)) ; + sYSpeed = (static_cast<int>(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); + } + + int savedUnscaledXLoc = 0; + int savedUnscaledYLoc = 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); + + savedUnscaledXLoc = unscaledXLoc; + savedUnscaledYLoc = 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 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<int>(.70711 * power)) ; + sXSpeed = (static_cast<int>(cos(degToRad(angle)) * sZSpeed)) ; + sYSpeed = (static_cast<int>(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 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 getEnergyHogType() { + return energyHogType; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_main.h b/engines/scumm/he/moonbase/ai_main.h new file mode 100644 index 0000000000..4937eced0f --- /dev/null +++ b/engines/scumm/he/moonbase/ai_main.h @@ -0,0 +1,184 @@ +/* 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. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_MAIN_H +#define SCUMM_HE_MOONBASE_AI_MAIN_H + +#include "scumm/he/moonbase/ai_tree.h" + +namespace Scumm { + +class ScummEngine_v90he; + +extern ScummEngine_v90he *_vm; + +typedef Common::Array<int>::iterator intVecItr; + +enum { + TERRAIN_TYPE_GOOD = 0, + TERRAIN_TYPE_SLOPE = 1, + TERRAIN_TYPE_WATER = 2, + MAX_MEMORY = 3 +}; + +enum { + ITEM_BOMB = 0, + ITEM_CLUSTER = 1, + ITEM_REPAIR = 2, + ITEM_ANTIAIR = 3, + ITEM_BRIDGE = 4, + ITEM_TOWER = 5, + ITEM_GUIDED = 6, + ITEM_EMP = 7, + ITEM_SPIKE = 8, + ITEM_RECLAIMER = 9, + ITEM_BALLOON = 10, + ITEM_MINE = 11, + ITEM_CRAWLER = 12, + ITEM_VIRUS = 13, + ITEM_ENERGY = 14, + ITEM_SHIELD = 15, + ITEM_OFFENSE = 16, + ITEM_HUB = 17, + ITEM_TIME_EXPIRED = 18, + SKIP_TURN = -999 +}; + +enum BuildingTypes { + BUILDING_ENERGY_COLLECTOR = 3, + BUILDING_MAIN_BASE = 4, + BUILDING_BRIDGE = 5, + BUILDING_TOWER = 6, + BUILDING_EXPLOSIVE_MINE = 7, + BUILDING_SHIELD = 8, + BUILDING_ANTI_AIR = 9, + BUILDING_OFFENSIVE_LAUNCHER = 10, + BUILDING_BALLOON = 11, + BUILDING_CRAWLER = 12 +}; + +enum { + ENERGY_POOL_X = 45, + ENERGY_POOL_Y = 46, + ENERGY_POOL_UNITS_ON = 47, + + MIN_DIST = 108 +}; + +static int energyHogType = 0; + +void resetAI(); +void cleanUpAI(); +void setAIType(const int paramCount, const int *params); +int masterControlProgram(const int paramCount, const int *params); + +int chooseBehavior(); +int chooseTarget(int behavior); + +Tree *initApproachTarget(int targetX, int targetY, Node **retNode); +int *approachTarget(Tree *myTree, int &x, int &y, Node **currentNode); +Tree *initAcquireTarget(int targetX, int targetY, Node **retNode); +int *acquireTarget(int targetX, int targetY); +int *acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode); +int *offendTarget(int &targetX, int &targetY, int index); +int *defendTarget(int &targetX, int &targetY, int index); +int *energizeTarget(int &targetX, int &targetY, int index); + +int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled); +int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled, int minDist); + +int getDistance(int originX, int originY, int endX, int endY); +int calcAngle(int originX, int originY, int endX, int endY); +int calcAngle(int originX, int originY, int endX, int endY, int noWrapFlag); +int getTerrain(int x, int y); +int getHubX(int hub); +int getHubY(int hub); +int getMaxX(); +int getMaxY(); +int getCurrentPlayer(); +int getMaxPower(); +int getMinPower(); +int getTerrainSquareSize(); +int getBuildingOwner(int building); +int getBuildingState(int building); +int getBuildingType(int building); +int getBuildingArmor(int building); +int getBuildingWorth(int building); +int getEnergyPoolsArray(); +int getCoordinateVisibility(int x, int y, int playerNum); +int getUnitVisibility(int unit, int playerNum); +int getEnergyPoolVisibility(int pool, int playerNum); +int getNumberOfPools(); +int getNumberOfPlayers(); +int getPlayerEnergy(); +int getPlayerMaxTime(); +int getWindXSpeed(); +int getWindYSpeed(); +int getTotalWindSpeed(); +int getWindXSpeedMax(); +int getWindYSpeedMax(); +int getBigXSize(); +int getBigYSize(); +int getEnergyPoolWidth(int pool); +int getBuildingMaxArmor(int building); +int getTimerValue(int timerNum); +int getLastAttacked(int &x, int &y); +int getPlayerTeam(int player); +int getBuildingTeam(int building); +int getFOW(); +int getAnimSpeed(); +int getBuildingStackPtr(); +int getTurnCounter(); + +int getGroundAltitude(int x, int y); +int checkForCordOverlap(int xStart, int yStart, int affectRadius, int simulateFlag); +int checkForAngleOverlap(int unit, int angle); +int estimateNextRoundEnergy(int player); +int checkForUnitOverlap(int x, int y, int radius, int ignoredUnit); +int checkForEnergySquare(int x, int y); +int aiChat(); + +int simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int isEnergy); +int simulateWeaponLaunch(int x, int y, int power, int angle, int numSteps); +int fakeSimulateWeaponLaunch(int x, int y, int power, int angle); + +int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold, int olFlag); +int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold); +int checkIfWaterState(int x, int y); +int checkIfWaterSquare(int x, int y); +int getUnitsWithinRadius(int x, int y, int radius); +int getLandingPoint(int x, int y, int power, int angle); +int getEnemyUnitsVisible(int playerNum); + +float degToRad(float degrees); +void limitLocation(int &a, int &b, int c, int d); +int energyPoolSize(int pool); +int getMaxCollectors(int pool); + +int getEnergyHogType(); + +extern Common::Array<int> lastXCoord[]; +extern Common::Array<int> lastYCoord[]; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_node.cpp b/engines/scumm/he/moonbase/ai_node.cpp new file mode 100644 index 0000000000..083a156a31 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_node.cpp @@ -0,0 +1,153 @@ +/* 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/moonbase/ai_node.h" + +namespace Scumm { + +IContainedObject::IContainedObject(IContainedObject &sourceContainedObject) { + _objID = sourceContainedObject.getObjID(); + _valueG = sourceContainedObject.getG(); +} + +int Node::_nodeCount = 0; + +Node::Node() { + _parent = NULL; + _depth = 0; + _nodeCount++; + _contents = NULL; +} + +Node::Node(Node *sourceNode) { + _parent = NULL; + _children = sourceNode->getChildren(); + + _depth = sourceNode->getDepth(); + + _contents = sourceNode->getContainedObject()->duplicate(); +} + +Node::~Node() { + if (_contents != NULL) { + delete _contents; + _contents = NULL; + } + + _nodeCount--; +} + +int Node::generateChildren() { + int numChildren = _contents->numChildrenToGen(); + + int numChildrenGenerated = numChildren; + int errorCode = -1; + static int i = 0; + + while (i < numChildren) { + Node *tempNode = new Node; + _children.push_back(tempNode); + tempNode->setParent(this); + tempNode->setDepth(_depth + 1); + + int completionFlag; + + IContainedObject *thisContObj = _contents->createChildObj(i, completionFlag); + assert(!(thisContObj != NULL && completionFlag == 0)); + + if (!completionFlag) { + _children.pop_back(); + delete tempNode; + return 0; + } + + i++; + + if (thisContObj != NULL) { + tempNode->setContainedObject(thisContObj); + } else { + _children.pop_back(); + delete tempNode; + numChildrenGenerated--; + } + } + + i = 0; + + if (numChildrenGenerated > 0) + return numChildrenGenerated; + + return errorCode; +} + + +int Node::generateNextChild() { + int numChildren = _contents->numChildrenToGen(); + + static int i = 0; + + Node *tempNode = new Node; + _children.push_back(tempNode); + tempNode->setParent(this); + tempNode->setDepth(_depth + 1); + + int compFlag; + IContainedObject *thisContObj = _contents->createChildObj(i, compFlag); + + if (thisContObj != NULL) { + tempNode->setContainedObject(thisContObj); + } else { + _children.pop_back(); + delete tempNode; + } + + ++i; + + if (i > numChildren) + i = 0; + + return i; +} + +Node *Node::popChild() { + Node *temp; + + temp = _children.back(); + _children.pop_back(); + return temp; +} + +Node *Node::getFirstStep() { + Node *currentNode = this; + + if (currentNode->getParent() == NULL) + return currentNode; + + while (currentNode->getParent()->getParent() != NULL) + currentNode = currentNode->getParent(); + + assert(currentNode->getDepth() == 1); + + return currentNode; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_node.h b/engines/scumm/he/moonbase/ai_node.h new file mode 100644 index 0000000000..454b98e0b5 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_node.h @@ -0,0 +1,103 @@ +/* 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. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_NODE_H +#define SCUMM_HE_MOONBASE_AI_NODE_H + +#include "common/array.h" + +namespace Scumm { + +const float SUCCESS = -1; +const float FAILURE = 1e20; + +class IContainedObject { +private: + int _objID; + float _valueG; + +protected: + virtual float getG() const { return _valueG; } + virtual float calcH() { return 0; } + +public: + IContainedObject() { _valueG = 0; } + IContainedObject(float inG) { _valueG = inG; } + IContainedObject(IContainedObject &sourceContainedObject); + virtual ~IContainedObject() {} + + virtual IContainedObject *duplicate() = 0; + + void setValueG(float inG) { _valueG = inG; } + float getValueG() { return _valueG; } + + int getObjID() const { return _objID; } + void setObjID(int inputObjID) { _objID = inputObjID; } + + virtual int numChildrenToGen() = 0; + virtual IContainedObject *createChildObj(int index, int &completionFlag) = 0; + + virtual int checkSuccess() = 0; + virtual float calcT() { return getG(); } + + float returnG() const { return getG(); } +}; + +class Node { +private: + Node *_parent; + Common::Array<Node *> _children; + + int _depth; + static int _nodeCount; + + IContainedObject *_contents; + +public: + Node(); + Node(Node *sourceNode); + ~Node(); + + void setParent(Node *parentPtr) { _parent = parentPtr; } + Node *getParent() const { return _parent; } + + void setDepth(int depth) { _depth = depth; } + int getDepth() const { return _depth; } + + static int getNodeCount() { return _nodeCount; } + + void setContainedObject(IContainedObject *value) { _contents = value; } + IContainedObject *getContainedObject() { return _contents; } + + Common::Array<Node *> getChildren() const { return _children; } + int generateChildren(); + int generateNextChild(); + Node *popChild(); + + float getObjectT() { return _contents->calcT(); } + + Node *getFirstStep(); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_pattern.h b/engines/scumm/he/moonbase/ai_pattern.h new file mode 100644 index 0000000000..ae1fa6b55e --- /dev/null +++ b/engines/scumm/he/moonbase/ai_pattern.h @@ -0,0 +1,162 @@ +/* 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. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_PATTERN_H +#define SCUMM_HE_MOONBASE_AI_PATTERN_H + +namespace Scumm { + +const int NO_PATTERN = 0; +const int PATTERN_FOUND = 1; + +class patternInstance { +private: + int _sourceHub; + int _unit; + int _power; + int _angle; + +public: + patternInstance() { + _sourceHub = 0; + _unit = 0; + _power = 0; + _angle = 0; + } + + patternInstance(int sh, int unit, int power, int angle) { + setSourceHub(sh); + setUnit(unit); + setPower(power); + setAngle(angle); + } + + void setSourceHub(int sh) { _sourceHub = sh; } + void setUnit(int unit) { _unit = unit; } + + void setPower(int power) { + if (power < 300) + _power = 1; + else if (power < 480) + _power = 2; + else + _power = 3; + } + + void setAngle(int angle) { + int tempAngle = angle % 360; + + if ((tempAngle >= 0) && (tempAngle < 90)) + _angle = 1; + + if ((tempAngle >= 90) && (tempAngle < 180)) + _angle = 2; + + if ((tempAngle >= 180) && (tempAngle < 270)) + _angle = 3; + + if ((tempAngle >= 270)) + _angle = 4; + } + + int getSourceHub() const { return _sourceHub; } + int getUnit() const { return _unit; } + int getPowerIndex() const { return _power; } + int getAngleIndex() const { return _angle; } + + static int comparePatterns(patternInstance *p1, patternInstance *p2) { + if (p1->getSourceHub() != p2->getSourceHub()) + return 0; + + if (p1->getUnit() != p2->getUnit()) + return 0; + + if (p1->getUnit() == -999) + return 0; + + int temp = abs(p1->getPowerIndex() - p2->getPowerIndex()); + + if (temp > 1) + return 0; + + temp = abs(p1->getAngleIndex() - p2->getAngleIndex()); + + if (temp > 1 && temp < 3) + return 0; + + return 1; + } +}; + +class patternList { +private: + patternInstance *theList[10]; + int listIndex; + +public: + patternList() { + for (int i = 0; i < 10; i++) { + theList[i] = new patternInstance(); + } + + listIndex = 0; + } + ~patternList() { + for (int i = 0; i < 10; i++) { + delete theList[i]; + } + } + + void addPattern(int sh, int unit, int power, int angle) { + theList[listIndex]->setSourceHub(sh); + theList[listIndex]->setUnit(unit); + theList[listIndex]->setPower(power); + theList[listIndex]->setAngle(angle); + + listIndex++; + + if (listIndex > 9) + listIndex = 0; + } + + int evaluatePattern(int sh, int unit, int power, int angle) { + patternInstance *patternToMatch = new patternInstance(sh, unit, power, angle); + int matchCount = 0; + + for (int i = 0; i < 9; i++) { + if (patternInstance::comparePatterns(theList[i], patternToMatch)) { + matchCount++; + } + } + + delete patternToMatch; + + if (matchCount > 2) + return PATTERN_FOUND; + + return NO_PATTERN; + } +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_targetacquisition.cpp b/engines/scumm/he/moonbase/ai_targetacquisition.cpp new file mode 100644 index 0000000000..6fbd9839dd --- /dev/null +++ b/engines/scumm/he/moonbase/ai_targetacquisition.cpp @@ -0,0 +1,556 @@ +/* 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_targetacquisition.h" +#include "scumm/he/moonbase/ai_main.h" +#include "scumm/he/moonbase/ai_weapon.h" + +namespace Scumm { + +int Sortie::_sSourceX = 0; +int Sortie::_sSourceY = 0; + +int Sortie::_sTargetX = 0; +int Sortie::_sTargetY = 0; + +Sortie::~Sortie() { + for (Common::Array<DefenseUnit *>::iterator k = _enemyDefenses.begin(); k != _enemyDefenses.end(); k++) { + delete *k; + } +} + +void Sortie::setEnemyDefenses(int enemyDefensesScummArray, int defendX, int defendY) { + DefenseUnit *thisUnit; + int currentPlayer = getCurrentPlayer(); + + for (int i = 0; i < 200; i++) { + int thisElement = _vm->_moonbase->readFromArray(enemyDefensesScummArray, 0, i); + + if (thisElement) { + if (getBuildingOwner(thisElement)) { + if (getPlayerTeam(currentPlayer) != getBuildingTeam(thisElement)) { + int type = getBuildingType(thisElement); + + switch (type) { + case BUILDING_ANTI_AIR: + thisUnit = new AntiAirUnit(); + break; + + case BUILDING_SHIELD: + thisUnit = new ShieldUnit(); + break; + + case BUILDING_EXPLOSIVE_MINE: + if (getDistance(getHubX(thisElement), getHubY(thisElement), defendX, defendY) < 90) + thisUnit = new MineUnit(); + else + thisUnit = NULL; + + break; + + case BUILDING_CRAWLER: + thisUnit = NULL; + break; + + default: + thisUnit = NULL; + break; + } + + if (thisUnit != NULL) { + thisUnit->setID(thisElement); + thisUnit->setPos(getHubX(thisElement), getHubY(thisElement)); + + if (getBuildingState(thisElement)) thisUnit->setState(DUS_OFF); + + _enemyDefenses.push_back(thisUnit); + } + } + } + } else { + i = 200; + } + } +} + +int *Sortie::getShotPos() const { + int *retVal = new int[2]; + + retVal[0] = _shotPosX; + retVal[1] = _shotPosY; + + return retVal; +} + +int Sortie::numChildrenToGen() { + int retVal = MAX<uint>(_enemyDefenses.size(), 1) * NUM_SHOT_POSITIONS * NUM_WEAPONS; + return retVal; +} + +IContainedObject *Sortie::createChildObj(int index, int &completionFlag) { + float thisDamage; + Sortie *retSortie = new Sortie; + int activeDefenses = 0; + + Common::Array<DefenseUnit *> thisEnemyDefenses; + + // Copy the defensive unit list from the parent + for (Common::Array<DefenseUnit *>::iterator k = _enemyDefenses.begin(); k != _enemyDefenses.end(); k++) { + DefenseUnit *temp; + + switch ((*k)->getType()) { + case DUT_ANTI_AIR: + temp = new AntiAirUnit(*k); + break; + + case DUT_SHIELD: + temp = new ShieldUnit(*k); + break; + + case DUT_MINE: + temp = new MineUnit(*k); + break; + + case DUT_CRAWLER: + temp = new CrawlerUnit(*k); + break; + + default: + temp = new ShieldUnit(*k); + break; + } + + thisEnemyDefenses.push_back(temp); + } + + // Calculate the current target from the index + DefenseUnit *currentTarget = *(thisEnemyDefenses.begin() + static_cast<int>(index / (NUM_WEAPONS * NUM_SHOT_POSITIONS))); + + assert(currentTarget); + + // Pick correct weapon according to index + Weapon *currentWeapon = new Weapon(currentTarget->selectWeapon(index % NUM_WEAPONS)); + retSortie->setUnitType(currentWeapon->getTypeID()); + + // Calculate distance from target to source hub + int distance = getDistance(currentTarget->getPosX(), currentTarget->getPosY(), getSourcePosX(), getSourcePosY()); + + // Pick correct shot position according to index + Common::Point *targetCoords; + targetCoords = currentTarget->createTargetPos((static_cast<int>(index / NUM_WEAPONS) % NUM_SHOT_POSITIONS), distance, currentWeapon->getTypeID(), getSourcePosX(), getSourcePosY()); + retSortie->setShotPos(targetCoords->x, targetCoords->y); + + // Set the g value based on cost of the weapon + retSortie->setValueG(getG() + currentWeapon->getCost()); + + int AAcounter = 3; + + // Loop through defensive units, toggling anti-air units and deciding if this weapon will land safely + for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) { + distance = getDistance((*i)->getPosX(), (*i)->getPosY(), targetCoords->x, targetCoords->y); + + // Check to see if we're within an active defense's radius + if ((distance < (*i)->getRadius()) && ((*i)->getState() == DUS_ON)) { + activeDefenses++; + + // Turn off this anti-air and drop the coverage count + if (((*i)->getType() == DUT_ANTI_AIR)) { + (*i)->setState(DUS_OFF); + + if (currentWeapon->getTypeID() == ITEM_CLUSTER) + AAcounter--; + else + AAcounter = 0; + } + + // Essentially disable this weapon choice, due to its impact with a shield, or untriggered anti-air + if (((*i)->getType() == DUT_SHIELD) || !AAcounter) { + retSortie->setValueG(1000); + i = thisEnemyDefenses.end() - 1; + } + } else { + // Turn on any anti-airs that were off the previous turn + if (((*i)->getType() == DUT_ANTI_AIR) && ((*i)->getState() == DUS_OFF)) + (*i)->setState(DUS_ON); + } + } + + // Turn on all the non-anti-air units in preparation for emp's and the next turn + for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) { + if ((*i)->getType() != DUT_ANTI_AIR) { + (*i)->setState(DUS_ON); + } + } + + // If this weapon is still valid + if (retSortie->getValueG() < 1000) { + // Apply emp effects and damage to all units in range of weapon + for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); ) { + // Special simulated crawler detonation location used, since it walks a bit + if (currentWeapon->getTypeID() == ITEM_CRAWLER) + distance = getDistance((*i)->getPosX(), (*i)->getPosY(), currentTarget->getPosX(), currentTarget->getPosY()); + // Normal detonation location used here + else { + distance = getDistance((*i)->getPosX(), (*i)->getPosY(), targetCoords->x, targetCoords->y); + } + + if (distance < currentWeapon->getRadius()) { + // Apply damage + thisDamage = currentWeapon->getDamage(); + + if ((AAcounter != 3) && (currentWeapon->getTypeID() == ITEM_CLUSTER)) + thisDamage = 0; + + if (!_vm->_rnd.getRandomNumber(4)) + currentWeapon->setTypeID(ITEM_MINE); + + (*i)->setDamage(thisDamage); + + // Apply emp effect + if (currentWeapon->getTypeID() == ITEM_EMP) { + (*i)->setState(DUS_OFF); + } + + // Remove destroyed defenses + if ((*i)->getArmor() <= 0) { + delete *i; + i = thisEnemyDefenses.erase(i); + } else { + i++; + } + } else { + i++; + } + } + } + + retSortie->setEnemyDefenses(thisEnemyDefenses); + + delete targetCoords; + delete currentWeapon; + return retSortie; +} + +float Sortie::calcH() { + float retValue = 0; + Common::Array<DefenseUnit *> thisEnemyDefenses = getEnemyDefenses(); + + for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) { + if ((*i)->getState() == DUS_ON) { + switch ((*i)->getType()) { + case DUT_ANTI_AIR: + retValue += 1; + + case DUT_MINE: + retValue += 1; + break; + + case DUT_SHIELD: + retValue += 1; + break; + } + } + } + + return retValue; +} + +int Sortie::checkSuccess() { + if (!_enemyDefenses.size()) return SUCCESS; + + int targetX = getTargetPosX(); + int targetY = getTargetPosY(); + + int targetCheck = 0; + + for (Common::Array<DefenseUnit *>::iterator i = _enemyDefenses.begin(); i != _enemyDefenses.end(); i++) { + if (((*i)->getState() == DUS_ON) && ((*i)->getType() != DUT_HUB)) { + return 0; + } + + if (((*i)->getPosX() == targetX) && ((*i)->getPosY() == targetY)) targetCheck = 1; + } + + if (!targetCheck) + return SUCCESS; + + // If shot pos == target pos return SUCCESS; + if ((targetX == getShotPosX()) && (getTargetPosY() == getShotPosY())) { + return SUCCESS; + } + + return 0; +} + +float Sortie::calcT() { + return (checkSuccess() != SUCCESS) ? (getG() + calcH()) : SUCCESS; +} + +IContainedObject *Sortie::duplicate() { + return this; +} + + +void Sortie::printEnemyDefenses() { + for (Common::Array<DefenseUnit *>::iterator i = _enemyDefenses.begin(); i != _enemyDefenses.end(); i++) { + warning("Unit %d - Type: %d, Armor: %d, Status: %d", (*i)->getID(), (*i)->getType(), static_cast<int>((*i)->getArmor()), (*i)->getState()); + } +} + +int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) { + int currentPlayer = getCurrentPlayer(); + + //get list of near hubs + int unitsArray = getUnitsWithinRadius(targetX + 5, targetY, 480); + + const int NUM_HUBS = 10; + //Order on dist + int hubArray[NUM_HUBS] = { 0 }; + int hubIndex = 0; + + for (int i = 0; i < 200; i++) { + int thisUnit = _vm->_moonbase->readFromArray(unitsArray, 0, i); + + if (thisUnit) { + if (((getBuildingType(thisUnit) == BUILDING_MAIN_BASE) || (getBuildingType(thisUnit) == BUILDING_OFFENSIVE_LAUNCHER)) && (getBuildingOwner(thisUnit) == currentPlayer)) { + for (int j = 0; j < NUM_HUBS; j++) { + if (hubArray[j]) { + int distCurrent = getDistance(targetX, targetY, getHubX(thisUnit), getHubY(thisUnit)); + int distSaved = getDistance(targetX, targetY, getHubX(hubArray[j]), getHubY(hubArray[j])); + + if (distCurrent < distSaved) { + hubArray[hubIndex] = hubArray[j]; + hubArray[j] = thisUnit; + hubIndex++; + j = 100; + } + } else { + hubArray[j] = thisUnit; + hubIndex++; + j = 100; + } + } + } + } + + if (hubIndex >= NUM_HUBS) { + hubIndex = NUM_HUBS; + i = 200; + } + } + + _vm->nukeArray(unitsArray); + + //Check if repair is needed + int targetUnit = getClosestUnit(targetX + 5, targetY, 15, currentPlayer, 1, 0, 0, 0); + + if (targetUnit && (targetUnit != BUILDING_CRAWLER) && (getBuildingTeam(targetUnit) == getPlayerTeam(currentPlayer))) { + int armor = getBuildingArmor(targetUnit); + + if (armor < getBuildingMaxArmor(targetUnit)) { + unitsArray = getUnitsWithinRadius(targetX + 5, targetY, 170); + int defCount = 0; + + for (int i = 0; i < 200; i++) { + int thisUnit = _vm->_moonbase->readFromArray(unitsArray, 0, i); + + if (thisUnit) { + if (((getBuildingType(thisUnit) == BUILDING_SHIELD) || (getBuildingType(thisUnit) == BUILDING_ANTI_AIR)) && (getBuildingOwner(thisUnit) == currentPlayer) && (getBuildingState(thisUnit) == 0)) { + defCount++; + i = 200; + } + } + } + + _vm->nukeArray(unitsArray); + + if (defCount) { + //repair + int hubUnit = getClosestUnit(targetX, targetY, 480, currentPlayer, 1, BUILDING_MAIN_BASE, 1, 110); + + if (hubUnit && (hubUnit != targetUnit)) { + int powAngle = abs(getPowerAngleFromPoint(getHubX(hubUnit), getHubY(hubUnit), targetX, targetY, 20)); + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + setTargetX(targetX); + setTargetY(targetY); + + setSourceUnit(hubUnit); + setUnit(ITEM_REPAIR); + setPower(power); + setAngle(angle); + + return 1; + } + } + } + } + + //For each hub + for (int i = 0; i < MIN(NUM_HUBS, hubIndex); i++) { + int hubX = getHubX(hubArray[i]); + int hubY = getHubY(hubArray[i]); + //get angle to hub + int directAngleToHub = 0; + + //If this hub is the target + if ((hubX == targetX) && (hubY == targetY)) { + //make the angle seed point at the closest enemy + int enemyUnit = getClosestUnit(hubX, hubY, getMaxX(), currentPlayer, 0, 0, 0); + directAngleToHub = calcAngle(targetX, targetY, getHubX(enemyUnit), getHubY(enemyUnit)); + } else { + directAngleToHub = calcAngle(targetX, targetY, hubX, hubY); + } + + //Number of random chances to land + for (int j = 0; j < 3; j++) { + //Pick random angle and dist within semicircle (-90 to +90) and (40 to 150) + int randAngle = directAngleToHub + _vm->_rnd.getRandomNumber(179) - 90; + int randDist = _vm->_rnd.getRandomNumber(109) + 40; + + int x = targetX + randDist * cos(degToRad(randAngle)); + int y = targetY + randDist * sin(degToRad(randAngle)); + + int powAngle = getPowerAngleFromPoint(hubX, hubY, x, y, 20); + + if (powAngle < 0) + continue; + + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + int coords = 0; + coords = simulateBuildingLaunch(hubX, hubY, power, angle, 100, 0); + + //if valid, return + if (coords > 0) { + //warning("The prospective launching hub for this defensive unit is: %d", hubArray[i]); + + setSourceX(hubX); + setSourceY(hubY); + setTargetX((x + getMaxX()) % getMaxX()); + setTargetY((y + getMaxY()) % getMaxY()); + setSourceUnit(hubArray[i]); + + int unitsArray2 = getUnitsWithinRadius(targetX + 5, targetY, 200); + int shieldCount = 0; + + for (int k = 0; k < 200; k++) { + int thisUnit = _vm->_moonbase->readFromArray(unitsArray2, 0, k); + + if (thisUnit) { + if ((getBuildingType(thisUnit) == BUILDING_SHIELD) && (getBuildingOwner(thisUnit) == currentPlayer)) + shieldCount++; + + if ((getBuildingType(thisUnit) == BUILDING_BRIDGE) && (getBuildingOwner(thisUnit) == currentPlayer)) { + shieldCount--; + shieldCount = MAX(-1, shieldCount); + } + } + } + + if ((_vm->_rnd.getRandomNumber((int)pow(3, shieldCount + 1) - 1) == 0) && (getPlayerEnergy() > 6)) + setUnit(ITEM_SHIELD); + else + setUnit(ITEM_ANTIAIR); + + setPower(power); + setAngle(angle); + + _vm->nukeArray(unitsArray2); + return 1; + } + + if (coords < 0) { + //drop a bridge for the cord + int yCoord = -coords / getMaxX(); + int xCoord = -coords - (yCoord * getMaxX()); + + if (checkIfWaterState(xCoord, yCoord)) { + int terrainSquareSize = getTerrainSquareSize(); + xCoord = ((xCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + yCoord = ((yCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + + int xDist = xCoord - x; + int yDist = yCoord - y; + x = xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1))); + y = yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1))); + + setTargetX(x); + setTargetY(y); + + int nextUnit = getClosestUnit(x, y, 480, getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 120); + powAngle = getPowerAngleFromPoint(getHubX(nextUnit), getHubY(nextUnit), x, y, 15); + + powAngle = abs(powAngle); + power = powAngle / 360; + angle = powAngle - (power * 360); + + setSourceUnit(nextUnit); + setUnit(ITEM_BRIDGE); + setPower(power); + setAngle(angle); + + return 1; + } + } + } + } + + // Else create new hub + int count = 0; + int coords = 0; + + if (hubIndex == 0) return -3; + + do { + int sourceHub = hubArray[_vm->_rnd.getRandomNumber(hubIndex - 1)]; + + setSourceX(getHubX(sourceHub)); + setSourceY(getHubY(sourceHub)); + setSourceUnit(sourceHub); + setUnit(ITEM_HUB); + setPower(_vm->_rnd.getRandomNumber(299) + 200); + setAngle(_vm->_rnd.getRandomNumber(359)); + count++; + + if (count > (NUM_HUBS * 3)) break; + + coords = simulateBuildingLaunch(getSourceX(), getSourceY(), getPower(), getAngle(), 100, 0); + } while (coords <= 0); + + if (coords > 0) { + setTargetX(coords % getMaxX()); + setTargetY(coords / getMaxX()); + } else { + setTargetX(0); + setTargetY(0); + } + + return -1; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_targetacquisition.h b/engines/scumm/he/moonbase/ai_targetacquisition.h new file mode 100644 index 0000000000..cf8f295c70 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_targetacquisition.h @@ -0,0 +1,150 @@ +/* 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. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_TARGETACQUISITION_H +#define SCUMM_HE_MOONBASE_AI_TARGETACQUISITION_H + +#include "scumm/he/moonbase/ai_defenseunit.h" +#include "scumm/he/moonbase/ai_node.h" +#include "scumm/he/moonbase/ai_tree.h" + +namespace Scumm { + +const int NUM_IMPT_UNITS = 3; +const int NUM_SHOT_POSITIONS = 1; +const int NUM_WEAPONS = 3; + +class Sortie : public IContainedObject { +private: + static int _sSourceX; + static int _sSourceY; + + static int _sTargetX; + static int _sTargetY; + + int _unitType; + int _shotPosX, _shotPosY; + Common::Array<DefenseUnit *> _enemyDefenses; + + +public: + Sortie() {} + virtual ~Sortie(); + + static void setSourcePos(int x, int y) { + _sSourceX = x; + _sSourceY = y; + } + static void setTargetPos(int x, int y) { + _sTargetX = x; + _sTargetY = y; + } + + void setUnitType(int unitType) { _unitType = unitType; } + + void setShotPosX(int shotPosX) { _shotPosX = shotPosX; } + void setShotPosY(int shotPosY) { _shotPosY = shotPosY; } + void setShotPos(int shotPosX, int shotPosY) { + _shotPosX = shotPosX; + _shotPosY = shotPosY; + } + + void setEnemyDefenses(Common::Array<DefenseUnit *> enemyDefenses) { + _enemyDefenses = enemyDefenses; + } + void setEnemyDefenses(int enemyDefensesScummArray, int defendX, int defendY); + + void printEnemyDefenses(); + + static int getSourcePosX() { return _sSourceX; } + static int getSourcePosY() { return _sSourceY; } + static int getTargetPosX() { return _sTargetX; } + static int getTargetPosY() { return _sTargetY; } + + int getUnitType() const { return _unitType; } + + int getShotPosX() const { return _shotPosX; } + int getShotPosY() const { return _shotPosY; } + int *getShotPos() const; + + Common::Array<DefenseUnit *> getEnemyDefenses() const { return _enemyDefenses; } + + virtual IContainedObject *duplicate(); + + virtual int numChildrenToGen(); + virtual IContainedObject *createChildObj(int, int &completionFlag); + + + virtual float calcH(); + virtual int checkSuccess(); + virtual float calcT(); +}; + +class Defender { +private: + int _sourceX; + int _sourceY; + int _targetX; + int _targetY; + int _sourceUnit; + int _power; + int _angle; + int _unit; + +public: + void setSourceX(int sourceX) { _sourceX = sourceX; } + void setSourceY(int sourceY) { _sourceY = sourceY; } + void setTargetX(int targetX) { _targetX = targetX; } + void setTargetY(int targetY) { _targetY = targetY; } + void setSourceUnit(int sourceUnit) { _sourceUnit = sourceUnit; } + void setPower(int power) { _power = power; } + void setAngle(int angle) { _angle = angle; } + void setUnit(int unit) { _unit = unit; } + + int getSourceX() const { return _sourceX; } + int getSourceY() const { return _sourceY; } + int getTargetX() const { return _targetX; } + int getTargetY() const { return _targetY; } + int getSourceUnit() const { return _sourceUnit; } + int getPower() const { return _power; } + int getAngle() const { return _angle; } + int getUnit() const { return _unit; } + + int calculateDefenseUnitPosition(int targetX, int targetY, int index); +}; + +class defenseUnitCompare { +public: + bool operator()(DefenseUnit *x, DefenseUnit *y) { + //disabled units go at the end + if (x->getState() == DUS_OFF) { + warning("OFF"); + return 0; + } + + return x->getDistanceTo() < y->getDistanceTo(); + } +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_traveller.cpp b/engines/scumm/he/moonbase/ai_traveller.cpp new file mode 100644 index 0000000000..68cf0c0d76 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_traveller.cpp @@ -0,0 +1,259 @@ +/* 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/moonbase/ai_traveller.h" +#include "scumm/he/moonbase/ai_main.h" + +namespace Scumm { + +int Traveller::_targetPosX = 0; +int Traveller::_targetPosY = 0; +int Traveller::_maxDist = 0; + +int Traveller::_numToGen = 0; +int Traveller::_sizeAngleStep = 0; + +Traveller::Traveller() { + _waterFlag = 0; + setValueG(0); + unsetDisabled(); +} + +Traveller::Traveller(int originX, int originY) { + _waterFlag = 0; + setValueG(0); + unsetDisabled(); + + _posX = originX; + _posY = originY; +} + +void Traveller::adjustPosX(int offsetX) { + int maxX = getMaxX(); + int deltaX = _posX + offsetX; + + if (deltaX < 0) _posX = maxX + deltaX; + else if (deltaX > maxX) _posX = deltaX - maxX; + else _posX = deltaX; +} + +void Traveller::adjustPosY(int offsetY) { + int maxY = getMaxX(); + int deltaY = _posY + offsetY; + + if (deltaY < 0) _posY = maxY + deltaY; + else if (deltaY > maxY) _posY = deltaY - maxY; + else _posY = deltaY; +} + +void Traveller::adjustXY(int offsetX, int offsetY) { + adjustPosX(offsetX); + adjustPosY(offsetY); +} + +float Traveller::calcH() { + float retVal = 0; + // Calc dist from here to target + retVal = getDistance(_posX, _posY, _targetPosX, _targetPosY); + // Divide by _maxDist to get minimum number of jumps to goal + retVal /= static_cast<float>(_maxDist); + + return retVal * 2.0; +} + +int Traveller::numChildrenToGen() { + if (!_numToGen) + _numToGen = getAnimSpeed() + 2; + + return _numToGen; +} + +IContainedObject *Traveller::createChildObj(int index, int &completionFlag) { + static int nodeCount = 0; + static int completionState = 1; + + if (!index) nodeCount = 0; + + nodeCount++; + + Traveller *retTraveller = new Traveller; + + static int dir, angle, power; + + if (completionState) { + // Calculate angle between here and target + int directAngle = 0; + + if (getEnergyHogType()) + directAngle = calcAngle(_posX, _posY, _targetPosX, _targetPosY, 1); + else + directAngle = calcAngle(_posX, _posY, _targetPosX, _targetPosY); + + // Calculate the offset angle for this index + if (!_sizeAngleStep) + _sizeAngleStep = 52 - (getAnimSpeed() * 7); + + dir = _sizeAngleStep * ((static_cast<int>(index / NUM_POWER_STEPS) + 1) >> 1); + // Calculate the sign value for the offset for this index + int orientation = dir * (((static_cast<int>(index / NUM_POWER_STEPS) % 2) << 1) - 1); + // Add the offset angle to the direct angle to target + angle = orientation + directAngle; + + // Calculate power for this index + int maxPower = 0; + int directDist = getDistance(_posX, _posY, _targetPosX, _targetPosY); + + if (directDist > _maxDist + 120) + maxPower = getMaxPower(); + else + maxPower = (static_cast<float>(directDist) / static_cast<float>(_maxDist + 120)) * getMaxPower(); + + maxPower -= 70; + power = maxPower * (1 - ((index % NUM_POWER_STEPS) * SIZE_POWER_STEP)); + } + + retTraveller->setAngleTo(angle); + retTraveller->setPowerTo(power); + + // Set this object's position to the new one determined by the power and angle from above + static int lastSuccessful = 0; + int coords = 0; + + if (!(index % NUM_POWER_STEPS) || (!lastSuccessful)) { + coords = simulateBuildingLaunch(_posX, _posY, power, angle, 10, 0); + lastSuccessful = 0; + } else { + completionState = 1; + lastSuccessful = 0; + } + + if (!coords) { + completionFlag = 0; + completionState = 0; + delete retTraveller; + return NULL; + } else { + completionFlag = 1; + completionState = 1; + } + + int whoseTurn = getCurrentPlayer(); + int maxX = getMaxX(); + + // Check new position to see if landing is clear + if (coords > 0) { + int yCoord = coords / maxX; + int xCoord = coords - (yCoord * maxX); + + int terrain = getTerrain(xCoord, yCoord); + assert(terrain == TERRAIN_TYPE_GOOD); + + float pwr = getMinPower() * .3; + float cosine = cos((static_cast<float>(angle) / 360) * (2 * M_PI)); + float sine = sin((static_cast<float>(angle) / 360) * (2 * M_PI)); + int xParam = xCoord + (pwr * cosine); + int yParam = yCoord + (pwr * sine); + + if (xParam < 0) + xParam += getMaxX(); + else if (xParam > getMaxX()) + xParam -= getMaxX(); + + if (yParam < 0) + yParam += getMaxY(); + else if (yParam > getMaxY()) + yParam -= getMaxY(); + + if (checkIfWaterState(xParam, yParam)) { + delete retTraveller; + return NULL; + } + + retTraveller->setPosY(yCoord); + retTraveller->setPosX(xCoord); + + // Iterate through the previous action list, making sure this one isn't on it + for (intVecItr i = (lastXCoord[whoseTurn]).begin(), j = (lastYCoord[whoseTurn]).begin(); i != (lastXCoord[whoseTurn]).end(); i++, j++) { + // Check if this shot is the same as the last time we tried + if ((*i == retTraveller->getPosX()) && (*j == retTraveller->getPosY())) { + retTraveller->setDisabled(); + delete retTraveller; + return NULL; + } + } + + retTraveller->setValueG(getG() + 7 + (dir * DIRECTION_WEIGHT)); + lastSuccessful = 1; + } else { + int yCoord = -coords / maxX; + int xCoord = -coords - (yCoord * maxX); + + // If landing fault is because of water, add 1 extra to g and turn on water flag. Also set coords, and adjust power to water fault location + if (checkIfWaterState(xCoord, yCoord)) { + int terrainSquareSize = getTerrainSquareSize(); + xCoord = ((xCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + yCoord = ((yCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + + int xDist = xCoord - _posX; + int yDist = yCoord - _posY; + retTraveller->setPosX(xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1)))); + retTraveller->setPosY(yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1)))); + + int closestHub = getClosestUnit(retTraveller->getPosX(), retTraveller->getPosY(), getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110); + + retTraveller->setWaterSourceX(getHubX(closestHub)); + retTraveller->setWaterSourceY(getHubY(closestHub)); + retTraveller->setWaterDestX(retTraveller->getPosX()); + retTraveller->setWaterDestY(retTraveller->getPosY()); + + retTraveller->setPowerTo(power); + retTraveller->setAngleTo(angle); + + retTraveller->setValueG(getG() + 10 + (dir * DIRECTION_WEIGHT)); + retTraveller->enableWaterFlag(); + } else { + // If not, set G to highest value + retTraveller->setDisabled(); + delete retTraveller; + return NULL; + } + } + + return retTraveller; +} + +int Traveller::checkSuccess() { + if (getDistance(_posX + 1, _posY, _targetPosX, _targetPosY) < _maxDist) + return SUCCESS; + + return 0; +} + +float Traveller::calcT() { + assert(!_disabled); + + if (_disabled) return FAILURE; + + return (checkSuccess() != SUCCESS) ? (getG() + calcH()) : SUCCESS; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_traveller.h b/engines/scumm/he/moonbase/ai_traveller.h new file mode 100644 index 0000000000..63a56d77af --- /dev/null +++ b/engines/scumm/he/moonbase/ai_traveller.h @@ -0,0 +1,122 @@ +/* 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. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_TRAVELER_H +#define SCUMM_HE_MOONBASE_AI_TRAVELER_H + +#include "scumm/he/moonbase/ai_node.h" + +namespace Scumm { + +const int NUM_TO_GEN = 9; + +const int NUM_POWER_STEPS = 3; +const double SIZE_POWER_STEP = .15; +const int SIZE_ANGLE_STEP = 45; +const int VARIATION_EXTENT = 3; +const int DIRECTION_WEIGHT = 5; + +class Traveller : public IContainedObject { +private: + static int _targetPosX; + static int _targetPosY; + static int _maxDist; + + static int _numToGen; + static int _sizeAngleStep; + + int _sourceHub; + + int _posX; + int _posY; + int _angleTo; + int _powerTo; + + int _disabled; + int _waterFlag; + int _waterSourceX; + int _waterSourceY; + int _waterDestX; + int _waterDestY; + + +protected: + virtual float calcH(); + +public: + Traveller(); + Traveller(int originX, int originY); + ~Traveller() {} + + IContainedObject *duplicate() { return this; } + + static void setTargetPosX(int posX) { _targetPosX = posX; } + static void setTargetPosY(int posY) { _targetPosY = posY; } + static void setMaxDist(int maxDist) { _maxDist = maxDist; } + + void setSourceHub(int sourceHub) { _sourceHub = sourceHub; } + + void setPosX(int posX) { _posX = posX; } + void setPosY(int posY) { _posY = posY; } + void setAngleTo(int angleTo) { _angleTo = angleTo; } + void setPowerTo(int powerTo) { _powerTo = powerTo; } + + void setWaterSourceX(int waterSourceX) { _waterSourceX = waterSourceX; } + void setWaterSourceY(int waterSourceY) { _waterSourceY = waterSourceY; } + + void setWaterDestX(int waterDestX) { _waterDestX = waterDestX; } + void setWaterDestY(int waterDestY) { _waterDestY = waterDestY; } + + int getSourceHub() const { return _sourceHub; } + + int getPosX() const { return _posX; } + int getPosY() const { return _posY; } + int getAngleTo() const { return _angleTo; } + int getPowerTo() const { return _powerTo; } + + int getWaterSourceX() const { return _waterSourceX; } + int getWaterSourceY() const { return _waterSourceY; } + int getWaterDestX() const { return _waterDestX; } + int getWaterDestY() const { return _waterDestY; } + + void setDisabled() { _disabled = 1; } + void unsetDisabled() { _disabled = 0; } + int getDisabled() { return _disabled; } + + void adjustPosX(int offsetX); + void adjustPosY(int offsetY); + void adjustXY(int offsetX, int offsetY); + + void enableWaterFlag() { _waterFlag = 1; } + void disableWaterFlag() { _waterFlag = 0; } + int getWaterFlag() const { return _waterFlag; } + + virtual int numChildrenToGen(); + virtual IContainedObject *createChildObj(int, int &); + + virtual int checkSuccess(); + virtual float calcT(); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_tree.cpp b/engines/scumm/he/moonbase/ai_tree.cpp new file mode 100644 index 0000000000..2335d567e9 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_tree.cpp @@ -0,0 +1,222 @@ +/* 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/moonbase/ai_tree.h" +#include "scumm/he/moonbase/ai_main.h" + +namespace Scumm { + +Tree::Tree() { + pBaseNode = new Node; + _maxDepth = MAX_DEPTH; + _maxNodes = MAX_NODES; +} + +Tree::Tree(IContainedObject *contents) { + pBaseNode = new Node; + pBaseNode->setContainedObject(contents); + _maxDepth = MAX_DEPTH; + _maxNodes = MAX_NODES; +} + +Tree::Tree(IContainedObject *contents, int maxDepth) { + pBaseNode = new Node; + pBaseNode->setContainedObject(contents); + _maxDepth = maxDepth; + _maxNodes = MAX_NODES; +} + +Tree::Tree(IContainedObject *contents, int maxDepth, int maxNodes) { + pBaseNode = new Node; + pBaseNode->setContainedObject(contents); + _maxDepth = maxDepth; + _maxNodes = maxNodes; +} + +void Tree::duplicateTree(Node *sourceNode, Node *destNode) { + Common::Array<Node *> vUnvisited = sourceNode->getChildren(); + + while (vUnvisited.size()) { + Node *newNode = new Node(*(vUnvisited.end())); + newNode->setParent(destNode); + (destNode->getChildren()).push_back(newNode); + duplicateTree(*(vUnvisited.end()), newNode); + vUnvisited.pop_back(); + } +} + +Tree::Tree(const Tree *sourceTree) { + pBaseNode = new Node(sourceTree->getBaseNode()); + _maxDepth = sourceTree->getMaxDepth(); + _maxNodes = sourceTree->getMaxNodes(); + + duplicateTree(sourceTree->getBaseNode(), pBaseNode); +} + +Tree::~Tree() { + // Delete all nodes + Node *pNodeItr = pBaseNode; + + // Depth first traversal of nodes to delete them + while (pNodeItr != NULL) { + // If any children are left, move to one of them + if (!(pNodeItr->getChildren().empty())) { + int size = (pNodeItr->getChildren()).size(); + pNodeItr = pNodeItr->popChild(); + } else { + // Delete this node, and move up to the parent for further processing + Node *pTemp = pNodeItr; + pNodeItr = pNodeItr->getParent(); + delete pTemp; + pTemp = NULL; + } + } +} + + +Node *Tree::aStarSearch() { + return NULL; +#if 0 + fnpMMap mmfpOpen; + + Node *currentNode = NULL; + float currentT; + + Node *retNode = NULL; + + float temp = pBaseNode->getContainedObject()->calcT(); + + if (static_cast<int>(temp) != SUCCESS) { + + mmfpOpen.insert(fnpMMap::value_type(pBaseNode->getObjectT(), pBaseNode)); + + while (mmfpOpen.size() && (retNode == NULL)) { + currentNode = mmfpOpen.begin()->second; + mmfpOpen.erase(mmfpOpen.begin()); + + if ((currentNode->getDepth() < _maxDepth) && (Node::getNodeCount() < _maxNodes)) { + // Generate nodes + int numChildren = currentNode->generateChildren(); + Common::Array<Node *> vChildren = currentNode->getChildren(); + + for (Common::Array<Node *>::iterator i = vChildren.begin(); i != vChildren.end(); i++) { + IContainedObject *pTemp = (*i)->getContainedObject(); + currentT = pTemp->calcT(); + + if (currentT == SUCCESS) retNode = *i; + else mmfpOpen.insert(fnpMMap::value_type(currentT, (*i))); + } + } else { + retNode = currentNode; + } + } + } else { + retNode = pBaseNode; + } + + return retNode; +#endif +} + + +Node *Tree::aStarSearch_singlePassInit() { + Node *retNode = NULL; + + currentChildIndex = 1; + + float temp = pBaseNode->getContainedObject()->calcT(); + + if (static_cast<int>(temp) != SUCCESS) { + //_currentMap.insert(fnpMMap::value_type(pBaseNode->getObjectT(), pBaseNode)); + //assert(_currentMap.size()); + } else { + retNode = pBaseNode; + } + + return retNode; +} + +Node *Tree::aStarSearch_singlePass(Node **currentNode) { + currentNode = NULL; + float currentT; + + Node *retNode = NULL; + +#if 0 + static int maxTime = 0; + + if (currentChildIndex == 1) { + maxTime = getPlayerMaxTime(); + } + + if (currentChildIndex) { + if (!(_currentMap.size())) { + retNode = _currentNode; + return retNode; + } + + _currentNode = _currentMap.begin()->second; + _currentMap.erase(_currentMap.begin()); + } + + if ((_currentNode->getDepth() < _maxDepth) && (Node::getNodeCount() < _maxNodes) && ((!maxTime) || (getTimerValue(3) < maxTime))) { + // Generate nodes + currentChildIndex = _currentNode->generateChildren(); + + if (currentChildIndex) { + Common::Array<Node *> vChildren = _currentNode->getChildren(); + + if (!vChildren.size() && !_currentMap.size()) { + currentChildIndex = 0; + retNode = _currentNode; + } + + for (Common::Array<Node *>::iterator i = vChildren.begin(); i != vChildren.end(); i++) { + IContainedObject *pTemp = (*i)->getContainedObject(); + currentT = pTemp->calcT(); + + if (currentT == SUCCESS) { + retNode = *i; + i = vChildren.end() - 1; + } else { + _currentMap.insert(fnpMMap::value_type(currentT, (*i))); + } + } + + if (!(_currentMap.size()) && (currentT != SUCCESS)) { + assert(_currentNode != NULL); + retNode = _currentNode; + } + } + } else { + retNode = _currentNode; + } +#endif + + return retNode; +} + +int Tree::IsBaseNode(Node *thisNode) { + return (thisNode == pBaseNode); +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_tree.h b/engines/scumm/he/moonbase/ai_tree.h new file mode 100644 index 0000000000..1097665a7a --- /dev/null +++ b/engines/scumm/he/moonbase/ai_tree.h @@ -0,0 +1,75 @@ +/* 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. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_TREE_H +#define SCUMM_HE_MOONBASE_AI_TREE_H + +#include "common/hash-str.h" +#include "scumm/he/moonbase/ai_node.h" + +namespace Scumm { + +//typedef std::multimap< float, Node *, std::less<float> > fnpMMap; + +const int MAX_DEPTH = 100; +const int MAX_NODES = 1000000; + +class Tree { +private: + Node *pBaseNode; + + int _maxDepth; + int _maxNodes; + + int currentChildIndex; + + Common::StringMap _currentMap; + Node *_currentNode; + +public: + Tree(); + Tree(IContainedObject *contents); + Tree(IContainedObject *contents, int maxDepth); + Tree(IContainedObject *contents, int maxDepth, int maxNodes); + Tree(const Tree *sourceTree); + ~Tree(); + + void duplicateTree(Node *sourceNode, Node *destNode); + + Node *getBaseNode() const { return pBaseNode; } + void setMaxDepth(int maxDepth) { _maxDepth = maxDepth; } + int getMaxDepth() const { return _maxDepth; } + + void setMaxNodes(int maxNodes) { _maxNodes = maxNodes; } + int getMaxNodes() const { return _maxNodes; } + + Node *aStarSearch(); + + Node *aStarSearch_singlePassInit(); + Node *aStarSearch_singlePass(Node **currentNode); + + int IsBaseNode(Node *thisNode); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_types.cpp b/engines/scumm/he/moonbase/ai_types.cpp new file mode 100644 index 0000000000..1674e03777 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_types.cpp @@ -0,0 +1,175 @@ +/* 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 "common/textconsole.h" +#include "scumm/he/moonbase/ai_types.h" + +namespace Scumm { + +AIEntity::AIEntity(int id) { + switch (id) { + case BRUTAKAS: + warning("BRUTAKAS"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "BRUTAKAS"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case AGI: + warning("Agi"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Agi"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_MEDIUM; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_LARGE; + break; + + case EL_GATO: + warning("El Gato de la Noche"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "El Gato de la Noche"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_MEDIUM; + break; + + case PIXELAHT: + warning("Pixelaht"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Pixelaht"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_LARGE; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_SMALL; + break; + + case CYBALL: + warning("cYbaLL"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "cYbaLL"); + _behaviorVariation = AI_VAR_LARGE; + _targetVariation = AI_VAR_LARGE; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case NEEP: + warning("Neep! Neep!"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Neep! Neep!"); + _behaviorVariation = AI_VAR_MEDIUM; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_LARGE; + break; + + case WARCUPINE: + warning("WARcupine"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "WARcupine"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_LARGE; + _powerVariation = AI_VAR_MEDIUM; + break; + + case AONE: + warning("aone"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "aone"); + _behaviorVariation = AI_VAR_MEDIUM; + _targetVariation = AI_VAR_MEDIUM; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_MEDIUM; + break; + + case SPANDO: + warning("S p a n d o"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "S p a n d o"); + _behaviorVariation = AI_VAR_LARGE; + _targetVariation = AI_VAR_LARGE; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case ORBNU_LUNATEK: + warning("Bonur J Lunatek"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Bonur J Lunatek"); + _behaviorVariation = AI_VAR_HUGE; + _targetVariation = AI_VAR_HUGE; + _angleVariation = AI_VAR_HUGE; + _powerVariation = AI_VAR_HUGE; + break; + + case CRAWLER_CHUCKER: + warning("Le Chuckre des Crawlres"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Le Chuckre des Crawlres"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_MEDIUM; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_LARGE; + break; + + case ENERGY_HOG: + warning("Energy Hog"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Energy Hog\n"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case RANGER: + warning("Ranger"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Ranger\n"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + } +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_types.h b/engines/scumm/he/moonbase/ai_types.h new file mode 100644 index 0000000000..e2de87d653 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_types.h @@ -0,0 +1,97 @@ +/* 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. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_TYPES_H +#define SCUMM_HE_MOONBASE_AI_TYPES_H + +namespace Scumm { + +enum { + AGI = 1, + AONE = 2, + BRUTAKAS = 3, + CYBALL = 4, + EL_GATO = 5, + NEEP = 6, + ORBNU_LUNATEK = 7, + PIXELAHT = 8, + SPANDO = 9, + WARCUPINE = 10 +}; + +enum { + CRAWLER_CHUCKER = 11, + ENERGY_HOG = 12, + RANGER = 13 +}; + +enum { + AI_VAR_NONE = -1, + AI_VAR_SMALL = 0, + AI_VAR_MEDIUM = 1, + AI_VAR_LARGE = 2, + AI_VAR_HUGE = 5 +}; + +enum { + AI_VAR_BASE_BEHAVIOR = 10, + AI_VAR_BASE_TARGET = 10, + AI_VAR_BASE_ANGLE = 2, + AI_VAR_BASE_POWER = 5 +}; + +class AIEntity { +private: + int _id; + char *_nameString; + int _behaviorVariation; + int _targetVariation; + int _angleVariation; + int _powerVariation; + +public: + AIEntity(int id); + ~AIEntity() { + if (_nameString) { + delete _nameString; + _nameString = 0; + } + } + + int getID() const { return _id; } + char *getNameString() const { return _nameString; } + int getBehaviorVariation() const { return _behaviorVariation; } + int getTargetVariation() const { return _targetVariation; } + int getAngleVariation() const { return _angleVariation; } + int getPowerVariation() const { return _powerVariation; } + + void setID(int id) { _id = id; } + void setNameString(char *nameString) { _nameString = nameString; } + void setBehaviorVariation(int behaviorVariation) { _behaviorVariation = behaviorVariation; } + void setTargetVariation(int targetVariation) { _targetVariation = targetVariation; } + void setAngleVariation(int angleVariation) { _angleVariation = angleVariation; } + void setPowerVariation(int powerVariation) { _powerVariation = powerVariation; } +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_weapon.cpp b/engines/scumm/he/moonbase/ai_weapon.cpp new file mode 100644 index 0000000000..b13d1efebe --- /dev/null +++ b/engines/scumm/he/moonbase/ai_weapon.cpp @@ -0,0 +1,87 @@ +/* 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/moonbase/ai_weapon.h" +#include "scumm/he/moonbase/ai_main.h" + +namespace Scumm { + +Weapon::Weapon(int typeID) { //, float damage, int radius) + switch (typeID) { + case ITEM_BOMB: + becomeBomb(); + break; + + case ITEM_CLUSTER: + becomeCluster(); + break; + + case ITEM_CRAWLER: + becomeCrawler(); + break; + + case ITEM_EMP: + becomeEMP(); + break; + + case ITEM_SPIKE: + becomeSpike(); + break; + } +} + +void Weapon::becomeBomb() { + _typeID = ITEM_BOMB; + _damage = 3; + _radius = 30; + _cost = 1; +} + +void Weapon::becomeCluster() { + _typeID = ITEM_CLUSTER; + _damage = 1.5; + _radius = 20; + _cost = 1; +} + +void Weapon::becomeCrawler() { + _typeID = ITEM_CRAWLER; + _damage = 4; + _radius = 180; + _cost = 7; +} + +void Weapon::becomeEMP() { + _typeID = ITEM_EMP; + _damage = .1f; + _radius = 215; + _cost = 3; +} + +void Weapon::becomeSpike() { + _typeID = ITEM_SPIKE; + _damage = 6; + _radius = 180; + _cost = 3; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_weapon.h b/engines/scumm/he/moonbase/ai_weapon.h new file mode 100644 index 0000000000..55c710ccdf --- /dev/null +++ b/engines/scumm/he/moonbase/ai_weapon.h @@ -0,0 +1,59 @@ +/* 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. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_WEAPON_H +#define SCUMM_HE_MOONBASE_AI_WEAPON_H + +namespace Scumm { + +class Weapon { +private: + int _typeID; + float _damage; + int _radius; + int _cost; + +public: + Weapon() {} + Weapon(int typeID); + virtual ~Weapon() {} + + void setTypeID(int typeID) { _typeID = typeID; } + void setDamage(float damage) { _damage = damage; } + void setRadius(int radius) { _radius = radius; } + void setCost(int cost) { _cost = cost; } + + int getTypeID() { return _typeID; } + float getDamage() { return _damage; } + int getRadius() { return _radius; } + int getCost() { return _cost; } + + void becomeBomb(); + void becomeCluster(); + void becomeCrawler(); + void becomeEMP(); + void becomeSpike(); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/moonbase.cpp b/engines/scumm/he/moonbase/moonbase.cpp index 34d4368725..cc25c270e4 100644 --- a/engines/scumm/he/moonbase/moonbase.cpp +++ b/engines/scumm/he/moonbase/moonbase.cpp @@ -32,6 +32,28 @@ Moonbase::Moonbase(ScummEngine_v71he *vm) : _vm(vm) { Moonbase::~Moonbase() { } +int Moonbase::readFromArray(int array, int y, int x) { + _vm->VAR(((ScummEngine_v90he *)_vm)->VAR_U32_ARRAY_UNK) = array; + + return _vm->readArray(116, y, x); +} + +int Moonbase::callScummFunction(int scriptNumber, int paramCount,...) { + va_list va_params; + va_start(va_params, paramCount); + int args[25]; + + for (int i = 0; i < paramCount; i++) + args[i] = va_arg(va_params, int); + + va_end(va_params); + + _vm->runScript(scriptNumber, 0, 1, args); + + return _vm->pop(); +} + + void Moonbase::blitT14WizImage(uint8 *dst, int dstw, int dsth, int dstPitch, const Common::Rect *clipBox, uint8 *wizd, int x, int y, int rawROP, int paramROP) { bool premulAlpa = false; diff --git a/engines/scumm/he/moonbase/moonbase.h b/engines/scumm/he/moonbase/moonbase.h index e82ae0164f..7d93661dec 100644 --- a/engines/scumm/he/moonbase/moonbase.h +++ b/engines/scumm/he/moonbase/moonbase.h @@ -20,8 +20,8 @@ * */ -#ifndef SCUMM_HE_MOONBASE_H -#define SCUMM_HE_MOONBASE_H +#ifndef SCUMM_HE_MOONBASE_MOONBASE_H +#define SCUMM_HE_MOONBASE_MOONBASE_H #ifdef ENABLE_HE @@ -34,10 +34,17 @@ public: Moonbase(ScummEngine_v71he *vm); ~Moonbase(); + int readFromArray(int array, int y, int x); + int callScummFunction(int scriptNumber, int paramCount,...); + void blitT14WizImage(uint8 *dst, int dstw, int dsth, int dstPitch, const Common::Rect *clipBox, uint8 *wizd, int srcx, int srcy, int rawROP, int paramROP); // FOW Stuff + bool isFOW(int resNum, int state, uint32 conditionBits) { + return resNum == _fowSentinelImage && state == _fowSentinelState && conditionBits == _fowSentinelConditionBits; + } + void initFOW(); void releaseFOWResources(); diff --git a/engines/scumm/he/moonbase/moonbase_fow.cpp b/engines/scumm/he/moonbase/moonbase_fow.cpp index 3f3730b6f1..48c2219926 100644 --- a/engines/scumm/he/moonbase/moonbase_fow.cpp +++ b/engines/scumm/he/moonbase/moonbase_fow.cpp @@ -158,9 +158,7 @@ enum FOWElement { }; int Moonbase::readFOWVisibilityArray(int array, int y, int x) { - _vm->VAR(116) = array; - - if (_vm->readArray(116, y, x) > 0) + if (readFromArray(array, y, x) > 0) return FOW_EMPTY; return FOW_SOLID; diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp index 053d1bd44b..cd807f06b8 100644 --- a/engines/scumm/he/script_v100he.cpp +++ b/engines/scumm/he/script_v100he.cpp @@ -942,10 +942,10 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { byte subOp = fetchScriptByte(); switch (subOp) { - case 0: + case 0: // SO_INIT _curSpriteGroupId = pop(); break; - case 6: + case 6: // SO_MOVE value2 = pop(); value1 = pop(); if (!_curSpriteGroupId) @@ -953,7 +953,7 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->setGroupPosition(_curSpriteGroupId, value1, value2); break; - case 18: + case 18: // SO_CLIPPED value4 = pop(); value3 = pop(); value2 = pop(); @@ -963,10 +963,10 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->setGroupBounds(_curSpriteGroupId, value1, value2, value3, value4); break; - case 38: + case 38: // SO_GROUP type = pop() - 1; switch (type) { - case 0: + case 0: // SPRGRPOP_MOVE value2 = pop(); value1 = pop(); if (!_curSpriteGroupId) @@ -974,48 +974,48 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->moveGroupMembers(_curSpriteGroupId, value1, value2); break; - case 1: + case 1: // SPRGRPOP_ORDER value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersPriority(_curSpriteGroupId, value1); break; - case 2: + case 2: // SPRGRPOP_NEW_GROUP value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersGroup(_curSpriteGroupId, value1); break; - case 3: + case 3: // SPRGRPOP_UPDATE_TYPE value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersUpdateType(_curSpriteGroupId, value1); break; - case 4: + case 4: // SPRGRPOP_NEW if (!_curSpriteGroupId) break; _sprite->setGroupMembersResetSprite(_curSpriteGroupId); break; - case 5: + case 5: // SPRGRPOP_ANIMATION_SPEED value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersAnimationSpeed(_curSpriteGroupId, value1); break; - case 6: + case 6: // SPRGRPOP_ANIMATION_TYPE value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersAutoAnimFlag(_curSpriteGroupId, value1); break; - case 7: + case 7: // SPRGRPOP_SHADOW value1 = pop(); if (!_curSpriteGroupId) break; @@ -1026,14 +1026,14 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { error("o100_setSpriteGroupInfo subOp 38: Unknown case %d", subOp); } break; - case 40: + case 40: // SO_IMAGE value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupImage(_curSpriteGroupId, value1); break; - case 49: + case 49: // SO_AT value2 = pop(); value1 = pop(); if (!_curSpriteGroupId) @@ -1041,51 +1041,51 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->moveGroup(_curSpriteGroupId, value1, value2); break; - case 52: + case 52: // SO_NAME copyScriptString(string, sizeof(string)); break; - case 53: + case 53: // SO_NEW if (!_curSpriteGroupId) break; _sprite->resetGroup(_curSpriteGroupId); break; - case 54: + case 54: // SO_NEW_GENERAL_PROPERTY // dummy case pop(); pop(); break; - case 59: + case 59: // SO_PRIORITY value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupPriority(_curSpriteGroupId, value1); break; - case 60: + case 60: // SO_PROPERTY type = pop(); value1 = pop(); if (!_curSpriteGroupId) break; switch (type) { - case 0: + case 0: // SPRGRPPROP_XMUL _sprite->setGroupXMul(_curSpriteGroupId, value1); break; - case 1: + case 1: // SPRGRPPROP_XDIV _sprite->setGroupXDiv(_curSpriteGroupId, value1); break; - case 2: + case 2: // SPRGRPPROP_YMUL _sprite->setGroupYMul(_curSpriteGroupId, value1); break; - case 3: + case 3: // SPRGRPPROP_YDIV _sprite->setGroupYDiv(_curSpriteGroupId, value1); break; default: error("o100_setSpriteGroupInfo subOp 60: Unknown case %d", subOp); } break; - case 89: + case 89: // SO_NEVER_ZCLIP if (!_curSpriteGroupId) break; diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp index 2e0a03af7f..8670116c68 100644 --- a/engines/scumm/he/sound_he.cpp +++ b/engines/scumm/he/sound_he.cpp @@ -51,10 +51,12 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer) _heMusicTracks(0) { memset(_heChannel, 0, sizeof(_heChannel)); + _heSoundChannels = new Audio::SoundHandle[8](); } SoundHE::~SoundHE() { free(_heMusic); + delete[] _heSoundChannels; } void SoundHE::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) { diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h index 323858a7c9..e0324d0753 100644 --- a/engines/scumm/he/sound_he.h +++ b/engines/scumm/he/sound_he.h @@ -44,7 +44,7 @@ protected: HEMusic *_heMusic; int16 _heMusicTracks; - Audio::SoundHandle _heSoundChannels[8]; + Audio::SoundHandle *_heSoundChannels; public: // Used by createSound() struct { diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp index 0976a532de..0ebfe67245 100644 --- a/engines/scumm/he/wiz_he.cpp +++ b/engines/scumm/he/wiz_he.cpp @@ -1573,10 +1573,8 @@ uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int } if (_vm->_game.id == GID_MOONBASE && - resNum == _vm->_moonbase->_fowSentinelImage && - state == _vm->_moonbase->_fowSentinelState && - conditionBits == _vm->_moonbase->_fowSentinelConditionBits) { - _vm->_moonbase->renderFOW(dst, dstPitch, dstType, cw, ch, flags); + ((ScummEngine_v90he *)_vm)->_moonbase->isFOW(resNum, state, conditionBits)) { + ((ScummEngine_v90he *)_vm)->_moonbase->renderFOW(dst, dstPitch, dstType, cw, ch, flags); x1 = 0; y1 = 0; width = rScreen.width(); @@ -1816,7 +1814,7 @@ void Wiz::copy555WizImage(uint8 *dst, uint8 *wizd, int dstPitch, int dstType, uint32 compID = READ_LE_UINT32(wizd); if (compID == 0x12340102) { - _vm->_moonbase->blitT14WizImage(dst, dstw, dsth, dstPitch, clipBox, wizd, srcx, srcy, rawROP, paramROP); + ((ScummEngine_v90he *)_vm)->_moonbase->blitT14WizImage(dst, dstw, dsth, dstPitch, clipBox, wizd, srcx, srcy, rawROP, paramROP); } else if (compID == 0x12340802) { warning("Distorion codec"); } else if (compID == 0x12340902) { diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp index 12047635a0..6ef7e4d7f4 100644 --- a/engines/scumm/input.cpp +++ b/engines/scumm/input.cpp @@ -24,6 +24,7 @@ #include "common/events.h" #include "common/system.h" #include "common/translation.h" +#include "audio/mixer.h" #include "scumm/debugger.h" #include "scumm/dialogs.h" diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index c56ef7e5f4..04611ba1b1 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -139,6 +139,14 @@ MODULE_OBJS += \ he/logic/moonbase_logic.o \ he/logic/puttrace.o \ he/logic/soccer.o \ + he/moonbase/ai_defenseunit.o \ + he/moonbase/ai_main.o \ + he/moonbase/ai_node.o \ + he/moonbase/ai_targetacquisition.o \ + he/moonbase/ai_traveller.o \ + he/moonbase/ai_tree.o \ + he/moonbase/ai_types.o \ + he/moonbase/ai_weapon.o \ he/moonbase/moonbase.o \ he/moonbase/moonbase_fow.o endif diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 1f8a85b8c3..4adf0c5066 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -833,16 +833,9 @@ ScummEngine_v71he::ScummEngine_v71he(OSystem *syst, const DetectorResult &dr) _skipProcessActors = 0; VAR_WIZ_TCOLOR = 0xFF; - - /* Moonbase stuff */ - _moonbase = 0; - - if (_game.id == GID_MOONBASE) - _moonbase = new Moonbase(this); } ScummEngine_v71he::~ScummEngine_v71he() { - delete _moonbase; delete _wiz; } @@ -892,6 +885,19 @@ ScummEngine_v90he::ScummEngine_v90he(OSystem *syst, const DetectorResult &dr) VAR_U32_VERSION = 0xFF; VAR_U32_ARRAY_UNK = 0xFF; + + /* Moonbase stuff */ + _moonbase = 0; + + if (_game.id == GID_MOONBASE) + _moonbase = new Moonbase(this); + + VAR_U32_USER_VAR_A = 0xFF; + VAR_U32_USER_VAR_B = 0xFF; + VAR_U32_USER_VAR_C = 0xFF; + VAR_U32_USER_VAR_D = 0xFF; + VAR_U32_USER_VAR_E = 0xFF; + VAR_U32_USER_VAR_F = 0xFF; } ScummEngine_v90he::~ScummEngine_v90he() { @@ -903,6 +909,7 @@ ScummEngine_v90he::~ScummEngine_v90he() { if (_game.heversion >= 99) { free(_hePalettes); } + delete _moonbase; } ScummEngine_vCUPhe::ScummEngine_vCUPhe(OSystem *syst, const DetectorResult &dr) : Engine(syst){ diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 62386d3346..a3fa329728 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -700,10 +700,13 @@ protected: void ignoreScriptWord() { fetchScriptWord(); } void ignoreScriptByte() { fetchScriptByte(); } void push(int a); + +public: // TODO. FIXME should be protected. Used by Moonbase int pop(); virtual int readVar(uint var); virtual void writeVar(uint var, int value); +protected: void beginCutscene(int *args); void endCutscene(); void abortCutscene(); diff --git a/engines/scumm/scumm_v2.h b/engines/scumm/scumm_v2.h index e438008c1e..763bbc061c 100644 --- a/engines/scumm/scumm_v2.h +++ b/engines/scumm/scumm_v2.h @@ -77,9 +77,12 @@ protected: void getResultPosIndirect(); virtual void getResultPos(); + +public: // TODO. FIXME. Should be protected. Used by Moonbase virtual int readVar(uint var); virtual void writeVar(uint var, int value); +protected: virtual int getActiveObject(); void ifStateCommon(byte type); void ifNotStateCommon(byte type); diff --git a/engines/scumm/scumm_v6.h b/engines/scumm/scumm_v6.h index 73268f6f33..83b9f2f4f8 100644 --- a/engines/scumm/scumm_v6.h +++ b/engines/scumm/scumm_v6.h @@ -119,7 +119,10 @@ protected: ArrayHeader *getArray(int array); byte *defineArray(int array, int type, int dim2, int dim1); int findFreeArrayId(); +public: // FIXME. TODO void nukeArray(int array); + +protected: virtual int readArray(int array, int index, int base); virtual void writeArray(int array, int index, int base, int value); void shuffleArray(int num, int minIdx, int maxIdx); diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp index 2ca2579b54..42ee0115c7 100644 --- a/engines/scumm/smush/smush_player.cpp +++ b/engines/scumm/smush/smush_player.cpp @@ -25,6 +25,8 @@ #include "common/system.h" #include "common/util.h" +#include "audio/mixer.h" + #include "graphics/cursorman.h" #include "graphics/palette.h" @@ -242,9 +244,15 @@ SmushPlayer::SmushPlayer(ScummEngine_v7 *scumm) { _paused = false; _pauseStartTime = 0; _pauseTime = 0; + + + _IACTchannel = new Audio::SoundHandle(); + _compressedFileSoundHandle = new Audio::SoundHandle(); } SmushPlayer::~SmushPlayer() { + delete _IACTchannel; + delete _compressedFileSoundHandle; } void SmushPlayer::init(int32 speed) { @@ -271,8 +279,8 @@ void SmushPlayer::init(int32 speed) { vs->pitch = vs->w; _vm->_gdi->_numStrips = vs->w / 8; - _vm->_mixer->stopHandle(_compressedFileSoundHandle); - _vm->_mixer->stopHandle(_IACTchannel); + _vm->_mixer->stopHandle(*_compressedFileSoundHandle); + _vm->_mixer->stopHandle(*_IACTchannel); _IACTpos = 0; _vm->_smixer->stop(); } @@ -470,7 +478,7 @@ void SmushPlayer::handleIACT(int32 subSize, Common::SeekableReadStream &b) { if (!_IACTstream) { _IACTstream = Audio::makeQueuingAudioStream(22050, true); - _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_IACTchannel, _IACTstream); + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _IACTchannel, _IACTstream); } _IACTstream->queueBuffer(output_data, 0x1000, DisposeAfterUse::YES, Audio::FLAG_STEREO | Audio::FLAG_16BITS); @@ -1091,7 +1099,7 @@ void SmushPlayer::seekSan(const char *file, int32 pos, int32 contFrame) { } void SmushPlayer::tryCmpFile(const char *filename) { - _vm->_mixer->stopHandle(_compressedFileSoundHandle); + _vm->_mixer->stopHandle(*_compressedFileSoundHandle); _compressedFileMode = false; const char *i = strrchr(filename, '.'); @@ -1110,7 +1118,7 @@ void SmushPlayer::tryCmpFile(const char *filename) { strcpy(fname + (i - filename), ".ogg"); if (file->open(fname)) { _compressedFileMode = true; - _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_compressedFileSoundHandle, Audio::makeVorbisStream(file, DisposeAfterUse::YES)); + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _compressedFileSoundHandle, Audio::makeVorbisStream(file, DisposeAfterUse::YES)); return; } #endif @@ -1119,7 +1127,7 @@ void SmushPlayer::tryCmpFile(const char *filename) { strcpy(fname + (i - filename), ".mp3"); if (file->open(fname)) { _compressedFileMode = true; - _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_compressedFileSoundHandle, Audio::makeMP3Stream(file, DisposeAfterUse::YES)); + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _compressedFileSoundHandle, Audio::makeMP3Stream(file, DisposeAfterUse::YES)); return; } #endif @@ -1185,12 +1193,12 @@ void SmushPlayer::play(const char *filename, int32 speed, int32 offset, int32 st // the sound. Synt to time instead. now = _vm->_system->getMillis() - _pauseTime; elapsed = now - _startTime; - } else if (_vm->_mixer->isSoundHandleActive(_compressedFileSoundHandle)) { + } else if (_vm->_mixer->isSoundHandleActive(*_compressedFileSoundHandle)) { // Compressed SMUSH files. - elapsed = _vm->_mixer->getSoundElapsedTime(_compressedFileSoundHandle); - } else if (_vm->_mixer->isSoundHandleActive(_IACTchannel)) { + elapsed = _vm->_mixer->getSoundElapsedTime(*_compressedFileSoundHandle); + } else if (_vm->_mixer->isSoundHandleActive(*_IACTchannel)) { // Curse of Monkey Island SMUSH files. - elapsed = _vm->_mixer->getSoundElapsedTime(_IACTchannel); + elapsed = _vm->_mixer->getSoundElapsedTime(*_IACTchannel); } else { // For other SMUSH files, we don't necessarily have any // one channel to sync against, so we have to use @@ -1245,8 +1253,8 @@ void SmushPlayer::play(const char *filename, int32 speed, int32 offset, int32 st break; if (_vm->shouldQuit() || _vm->_saveLoadFlag || _vm->_smushVideoShouldFinish) { _smixer->stop(); - _vm->_mixer->stopHandle(_compressedFileSoundHandle); - _vm->_mixer->stopHandle(_IACTchannel); + _vm->_mixer->stopHandle(*_compressedFileSoundHandle); + _vm->_mixer->stopHandle(*_IACTchannel); _IACTpos = 0; break; } diff --git a/engines/scumm/smush/smush_player.h b/engines/scumm/smush/smush_player.h index b0d6e6a9f0..f1dffef7c7 100644 --- a/engines/scumm/smush/smush_player.h +++ b/engines/scumm/smush/smush_player.h @@ -24,9 +24,9 @@ #define SCUMM_SMUSH_PLAYER_H #include "common/util.h" -#include "scumm/sound.h" namespace Audio { +class SoundHandle; class QueuingAudioStream; } @@ -65,10 +65,10 @@ private: bool _skipNext; uint32 _frame; - Audio::SoundHandle _IACTchannel; + Audio::SoundHandle *_IACTchannel; Audio::QueuingAudioStream *_IACTstream; - Audio::SoundHandle _compressedFileSoundHandle; + Audio::SoundHandle *_compressedFileSoundHandle; bool _compressedFileMode; byte _IACToutput[4096]; int32 _IACTpos; diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index f66452e99c..33b7c3108d 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -97,12 +97,17 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer) _loomSteamCD.balance = 0; _isLoomSteam = _vm->_game.id == GID_LOOM && Common::File::exists("CDDA.SOU"); + + _loomSteamCDAudioHandle = new Audio::SoundHandle(); + _talkChannelHandle = new Audio::SoundHandle(); } Sound::~Sound() { stopCDTimer(); stopCD(); free(_offsetTable); + delete _loomSteamCDAudioHandle; + delete _talkChannelHandle; } void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) { @@ -425,7 +430,7 @@ void Sound::processSfxQueues() { if (_talk_sound_mode & 1) startTalkSound(_talk_sound_a1, _talk_sound_b1, 1); if (_talk_sound_mode & 2) - startTalkSound(_talk_sound_a2, _talk_sound_b2, 2, &_talkChannelHandle); + startTalkSound(_talk_sound_a2, _talk_sound_b2, 2, _talkChannelHandle); _talk_sound_mode = 0; } @@ -439,7 +444,7 @@ void Sound::processSfxQueues() { } else if (_vm->_game.heversion >= 60) { finished = !isSoundRunning(1); } else { - finished = !_mixer->isSoundHandleActive(_talkChannelHandle); + finished = !_mixer->isSoundHandleActive(*_talkChannelHandle); } if ((uint) act < 0x80 && ((_vm->_game.version == 8) || (_vm->_game.version <= 7 && !_vm->_string[0].no_talk_anim))) { @@ -675,7 +680,7 @@ void Sound::stopTalkSound() { } else if (_vm->_game.heversion >= 60) { stopSound(1); } else { - _mixer->stopHandle(_talkChannelHandle); + _mixer->stopHandle(*_talkChannelHandle); } _sfxMode &= ~2; } @@ -1060,7 +1065,7 @@ void Sound::playCDTrackInternal(int track, int numLoops, int startFrame, int dur g_system->getAudioCDManager()->play(track, numLoops, startFrame, duration); } else { // Stop any currently playing track - _mixer->stopHandle(_loomSteamCDAudioHandle); + _mixer->stopHandle(*_loomSteamCDAudioHandle); Common::File *cddaFile = new Common::File(); if (cddaFile->open("CDDA.SOU")) { @@ -1068,7 +1073,7 @@ void Sound::playCDTrackInternal(int track, int numLoops, int startFrame, int dur Audio::Timestamp end = Audio::Timestamp(0, startFrame + duration, 75); Audio::SeekableAudioStream *stream = makeCDDAStream(cddaFile, DisposeAfterUse::YES); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_loomSteamCDAudioHandle, + _mixer->playStream(Audio::Mixer::kMusicSoundType, _loomSteamCDAudioHandle, Audio::makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops)); } else { delete cddaFile; @@ -1080,14 +1085,14 @@ void Sound::stopCD() { if (!_isLoomSteam) g_system->getAudioCDManager()->stop(); else - _mixer->stopHandle(_loomSteamCDAudioHandle); + _mixer->stopHandle(*_loomSteamCDAudioHandle); } int Sound::pollCD() const { if (!_isLoomSteam) return g_system->getAudioCDManager()->isPlaying(); else - return _mixer->isSoundHandleActive(_loomSteamCDAudioHandle); + return _mixer->isSoundHandleActive(*_loomSteamCDAudioHandle); } void Sound::updateCD() { @@ -1100,7 +1105,7 @@ AudioCDManager::Status Sound::getCDStatus() { return g_system->getAudioCDManager()->getStatus(); else { AudioCDManager::Status info = _loomSteamCD; - info.playing = _mixer->isSoundHandleActive(_loomSteamCDAudioHandle); + info.playing = _mixer->isSoundHandleActive(*_loomSteamCDAudioHandle); return info; } } diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h index 8c11c7b5b2..7fdb16371c 100644 --- a/engines/scumm/sound.h +++ b/engines/scumm/sound.h @@ -26,10 +26,14 @@ #include "common/scummsys.h" #include "common/str.h" #include "audio/mididrv.h" -#include "audio/mixer.h" #include "backends/audiocd/audiocd.h" #include "scumm/saveload.h" +namespace Audio { +class Mixer; +class SoundHandle; +} + namespace Scumm { class ScummEngine; @@ -81,12 +85,12 @@ protected: int16 _currentCDSound; int16 _currentMusic; - Audio::SoundHandle _loomSteamCDAudioHandle; + Audio::SoundHandle *_loomSteamCDAudioHandle; bool _isLoomSteam; AudioCDManager::Status _loomSteamCD; public: - Audio::SoundHandle _talkChannelHandle; // Handle of mixer channel actor is talking on + Audio::SoundHandle *_talkChannelHandle; // Handle of mixer channel actor is talking on bool _soundsPaused; byte _sfxMode; diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp index 3049fbcf62..e6054918fa 100644 --- a/engines/scumm/string.cpp +++ b/engines/scumm/string.cpp @@ -23,6 +23,7 @@ #include "common/config-manager.h" +#include "audio/mixer.h" #include "scumm/actor.h" #include "scumm/charset.h" @@ -662,7 +663,7 @@ void ScummEngine::CHARSET_1() { // Special case for HE games } else if (_game.id == GID_LOOM && !ConfMan.getBool("subtitles") && (_sound->pollCD())) { // Special case for Loom (CD), since it only uses CD audio.for sound - } else if (!ConfMan.getBool("subtitles") && (!_haveActorSpeechMsg || _mixer->isSoundHandleActive(_sound->_talkChannelHandle))) { + } else if (!ConfMan.getBool("subtitles") && (!_haveActorSpeechMsg || _mixer->isSoundHandleActive(*_sound->_talkChannelHandle))) { // Subtitles are turned off, and there is a voice version // of this message -> don't print it. } else { diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp index a6be5c3f3a..5254aa4af2 100644 --- a/engines/scumm/vars.cpp +++ b/engines/scumm/vars.cpp @@ -340,6 +340,14 @@ void ScummEngine_v90he::setupScummVars() { VAR_NUM_PALETTES = 130; VAR_NUM_UNK = 131; } + if (_game.id == GID_MOONBASE) { + VAR_U32_USER_VAR_A = 108; + VAR_U32_USER_VAR_B = 109; + VAR_U32_USER_VAR_C = 110; + VAR_U32_USER_VAR_D = 111; + VAR_U32_USER_VAR_E = 112; + VAR_U32_USER_VAR_F = 113; + } } #endif |