aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/sci/engine/script_patches.cpp97
-rw-r--r--engines/scumm/he/intern_he.h15
-rw-r--r--engines/scumm/he/moonbase/ai_defenseunit.cpp759
-rw-r--r--engines/scumm/he/moonbase/ai_defenseunit.h190
-rw-r--r--engines/scumm/he/moonbase/ai_main.cpp3124
-rw-r--r--engines/scumm/he/moonbase/ai_main.h184
-rw-r--r--engines/scumm/he/moonbase/ai_node.cpp153
-rw-r--r--engines/scumm/he/moonbase/ai_node.h103
-rw-r--r--engines/scumm/he/moonbase/ai_pattern.h162
-rw-r--r--engines/scumm/he/moonbase/ai_targetacquisition.cpp556
-rw-r--r--engines/scumm/he/moonbase/ai_targetacquisition.h150
-rw-r--r--engines/scumm/he/moonbase/ai_traveller.cpp259
-rw-r--r--engines/scumm/he/moonbase/ai_traveller.h122
-rw-r--r--engines/scumm/he/moonbase/ai_tree.cpp222
-rw-r--r--engines/scumm/he/moonbase/ai_tree.h75
-rw-r--r--engines/scumm/he/moonbase/ai_types.cpp175
-rw-r--r--engines/scumm/he/moonbase/ai_types.h97
-rw-r--r--engines/scumm/he/moonbase/ai_weapon.cpp87
-rw-r--r--engines/scumm/he/moonbase/ai_weapon.h59
-rw-r--r--engines/scumm/he/moonbase/moonbase.cpp22
-rw-r--r--engines/scumm/he/moonbase/moonbase.h11
-rw-r--r--engines/scumm/he/moonbase/moonbase_fow.cpp4
-rw-r--r--engines/scumm/he/script_v100he.cpp48
-rw-r--r--engines/scumm/he/sound_he.cpp2
-rw-r--r--engines/scumm/he/sound_he.h2
-rw-r--r--engines/scumm/he/wiz_he.cpp8
-rw-r--r--engines/scumm/input.cpp1
-rw-r--r--engines/scumm/module.mk8
-rw-r--r--engines/scumm/scumm.cpp21
-rw-r--r--engines/scumm/scumm.h3
-rw-r--r--engines/scumm/scumm_v2.h3
-rw-r--r--engines/scumm/scumm_v6.h3
-rw-r--r--engines/scumm/smush/smush_player.cpp32
-rw-r--r--engines/scumm/smush/smush_player.h6
-rw-r--r--engines/scumm/sound.cpp21
-rw-r--r--engines/scumm/sound.h10
-rw-r--r--engines/scumm/string.cpp3
-rw-r--r--engines/scumm/vars.cpp8
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, &currentNode);
+ }
+
+ 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(&currentNode);
+
+ 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