aboutsummaryrefslogtreecommitdiff
path: root/engines/xeen/combat.cpp
diff options
context:
space:
mode:
authorPaul Gilbert2015-02-15 20:47:56 -0500
committerPaul Gilbert2015-02-15 20:47:56 -0500
commit75a070de1755fca9244eec1796ea892118afa84b (patch)
tree101d103a540d93de545bf0fc95906fd9a060093e /engines/xeen/combat.cpp
parentef2a4595c2c17260f61ad93d6c4374af7fa8a606 (diff)
downloadscummvm-rg350-75a070de1755fca9244eec1796ea892118afa84b.tar.gz
scummvm-rg350-75a070de1755fca9244eec1796ea892118afa84b.tar.bz2
scummvm-rg350-75a070de1755fca9244eec1796ea892118afa84b.zip
XEEN: Implementing combat methods
Diffstat (limited to 'engines/xeen/combat.cpp')
-rw-r--r--engines/xeen/combat.cpp401
1 files changed, 399 insertions, 2 deletions
diff --git a/engines/xeen/combat.cpp b/engines/xeen/combat.cpp
index 582444d5e5..3797c18139 100644
--- a/engines/xeen/combat.cpp
+++ b/engines/xeen/combat.cpp
@@ -20,12 +20,64 @@
*
*/
-#include "xeen/combat.h"
#include "common/algorithm.h"
-#include "common/textconsole.h"
+#include "common/rect.h"
+#include "xeen/combat.h"
+#include "xeen/interface.h"
+#include "xeen/xeen.h"
namespace Xeen {
+static const int MONSTER_GRID_X[48] = {
+ 1, 1, 1, 0, -1, -1, -1, 1, 1, 1, 0, -1,
+ -1, -1, 1, 1, 1, 0, -1, -1, -1, 1, 1, 1,
+ 0, -1, -1, -1, 1, 1, 1, 0, -1, -1, -1, 1,
+ 1, 1, 0, -1, -1, -1, 1, 1, 1, 0, -1, -1
+};
+
+static const int MONSTER_GRID_Y[48] = {
+ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0,
+ 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0
+};
+
+static const int MONSTER_GRID3[48] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ - 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1,
+ 0, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+};
+
+static const int MONSTER_GRID_BITINDEX1[48] = {
+ 1, 1, 1, 2, 3, 3, 3, 1, 1, 1, 2, 3,
+ 3, 3, 1, 1, 1, 2, 3, 3, 3, 1, 1, 1,
+ 0, 3, 3, 3, 1, 1, 1, 0, 3, 3, 3, 1,
+ 1, 1, 0, 3, 3, 3, 1, 1, 1, 0, 3, 3
+};
+
+static const int MONSTER_GRID_BITINDEX2[48] = {
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1,
+ 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const int MONSTER_GRID_BITMASK[12] = {
+ 0xC, 8, 4, 0, 0xF, 0xF000, 0xF00, 0xF0, 0xF00, 0xF0, 0x0F, 0xF000
+};
+
+static const int ATTACK_TYPE_FX[23] = {
+ 49, 18, 13, 14, 15, 17, 16, 0, 6, 1, 2, 3,
+ 4, 5, 4, 9, 27, 29, 44, 51, 53, 61, 71
+};
+
+static const int MONSTER_SHOOT_POW[7] = { 12, 14, 0, 4, 8, 10, 13 };
+
+static const int COMBAT_SHOOTING[4] = { 1, 1, 2, 3 };
+
+/*------------------------------------------------------------------------*/
+
Combat::Combat(XeenEngine *vm): _vm(vm) {
Common::fill(&_attackMonsters[0], &_attackMonsters[26], 0);
Common::fill(&_charsArray1[0], &_charsArray1[12], 0);
@@ -34,9 +86,14 @@ Combat::Combat(XeenEngine *vm): _vm(vm) {
Common::fill(&_elemPow[0], &_elemPow[12], 0);
Common::fill(&_elemScale[0], &_elemScale[12], 0);
Common::fill(&_shooting[0], &_shooting[8], 0);
+ Common::fill(&_monsterMap[0][0], &_monsterMap[32][32], 0);
+ Common::fill(&_monsterMoved[0], &_monsterMoved[MAX_NUM_MONSTERS], false);
+ Common::fill(&_rangeAttacking[0], &_rangeAttacking[MAX_NUM_MONSTERS], false);
+ Common::fill(&_gmonHit[0], &_gmonHit[36], 0);
_globalCombat = 0;
_whosTurn = -1;
_itemFlag = false;
+ _monstersAttacking = false;
}
void Combat::clear() {
@@ -51,5 +108,345 @@ void Combat::giveCharDamage(int damage, int v2, int v3) {
error("TODO: giveCharDamage");
}
+void Combat::moveMonsters() {
+ Interface &intf = *_vm->_interface;
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+
+ if (!_vm->_moveMonsters)
+ return;
+
+ intf._tillMove = 0;
+ if (intf._charsShooting)
+ return;
+
+ Common::fill(&_monsterMap[0][0], &_monsterMap[32][32], 0);
+ Common::fill(&_monsterMoved[0], &_monsterMoved[MAX_NUM_MONSTERS], false);
+ Common::fill(&_rangeAttacking[0], &_rangeAttacking[MAX_NUM_MONSTERS], false);
+ Common::fill(&_gmonHit[0], &_gmonHit[36], -1);
+ _vm->_dangerSenseAllowed = false;
+
+ for (uint idx = 0; idx < map._mobData._monsters.size(); ++idx) {
+ MazeMonster &monster = map._mobData._monsters[idx];
+ if (monster._position.y < 32) {
+ _monsterMap[monster._position.y][monster._position.x]++;
+ }
+ }
+
+ for (int loopNum = 0; loopNum < 2; ++loopNum) {
+ int arrIndex = -1;
+ for (int yDiff = 3; yDiff >= -3; --yDiff) {
+ for (int xDiff = 3; xDiff >= -3; --xDiff) {
+ Common::Point pt = party._mazePosition + Common::Point(xDiff, yDiff);
+ ++arrIndex;
+
+ for (int idx = 0; idx < (int)map._mobData._monsters.size(); ++idx) {
+ MazeMonster &monster = map._mobData._monsters[idx];
+ MonsterStruct &monsterData = map._monsterData[monster._spriteId];
+
+ if (pt == monster._position) {
+ _vm->_dangerSenseAllowed = true;
+ if ((monster._isAttacking || _vm->_mode == MODE_SLEEPING)
+ && !_monsterMoved[idx]) {
+ if (party._mazePosition.x == pt.x || party._mazePosition.y == pt.y) {
+ // Check for range attacks
+ if (monsterData._rangeAttack && !_rangeAttacking[idx]
+ && _attackMonsters[0] != idx && _attackMonsters[1] != idx
+ && _attackMonsters[2] != idx && !monster._field7) {
+ // Setup monster for attacking
+ setupMonsterAttack(monster._spriteId, pt);
+ _rangeAttacking[idx] = true;
+ }
+ }
+ }
+ }
+
+ switch (party._mazeDirection) {
+ case DIR_NORTH:
+ case DIR_SOUTH:
+ if (monsterCanMove(pt, MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX1[arrIndex]],
+ MONSTER_GRID_X[arrIndex], MONSTER_GRID_Y[arrIndex], idx)) {
+ // Move the monster
+ moveMonster(idx, Common::Point(MONSTER_GRID_X[arrIndex], MONSTER_GRID_Y[arrIndex]));
+ } else {
+ if (monsterCanMove(pt, MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX2[arrIndex]],
+ arrIndex >= 21 && arrIndex <= 27 ? MONSTER_GRID3[arrIndex] : 0,
+ arrIndex >= 21 && arrIndex <= 27 ? 0 : MONSTER_GRID3[arrIndex],
+ idx))
+ if (arrIndex >= 21 && arrIndex <= 27) {
+ moveMonster(idx, Common::Point(MONSTER_GRID3[arrIndex], 0));
+ } else {
+ moveMonster(idx, Common::Point(0, MONSTER_GRID3[arrIndex]));
+ }
+ }
+ break;
+
+ case DIR_EAST:
+ case DIR_WEST:
+ if (monsterCanMove(pt, MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX2[arrIndex]],
+ arrIndex >= 21 && arrIndex <= 27 ? MONSTER_GRID3[arrIndex] : 0,
+ arrIndex >= 21 && arrIndex <= 27 ? 0 : MONSTER_GRID3[arrIndex],
+ idx)) {
+ if (arrIndex >= 21 && arrIndex <= 27) {
+ moveMonster(idx, Common::Point(MONSTER_GRID3[arrIndex], 0));
+ } else {
+ moveMonster(idx, Common::Point(0, MONSTER_GRID3[arrIndex]));
+ }
+ } else if (monsterCanMove(pt, MONSTER_GRID_BITMASK[MONSTER_GRID_BITINDEX1[arrIndex]],
+ MONSTER_GRID_X[arrIndex], MONSTER_GRID_Y[arrIndex], idx)) {
+ moveMonster(idx, Common::Point(MONSTER_GRID_X[arrIndex], MONSTER_GRID_Y[arrIndex]));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ monsterOvercome();
+ if (_monstersAttacking)
+ monstersAttack();
+}
+
+void Combat::monstersAttack() {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+ SoundManager &sound = *_vm->_sound;
+ int powNum = -1;
+ MonsterStruct *monsterData = nullptr;
+ OutdoorDrawList &outdoorList = intf._outdoorList;
+ IndoorDrawList &indoorList = intf._indoorList;
+
+ for (int idx = 0; idx < 36; ++idx) {
+ if (_gmonHit[idx] != -1) {
+ monsterData = &map._monsterData[_gmonHit[idx]];
+ powNum = MONSTER_SHOOT_POW[monsterData->_attackType];
+ if (powNum != 12)
+ break;
+ }
+ }
+
+ _powSprites.load(Common::String::format("pow%d.icn", powNum));
+ sound.playFX(ATTACK_TYPE_FX[monsterData->_attackType]);
+
+ for (int charNum = 0; charNum < MAX_PARTY_COUNT; ++charNum) {
+ if (!_shooting[charNum])
+ continue;
+
+ if (map._isOutdoors) {
+ outdoorList._attackImgs1[charNum]._scale = 3;
+ outdoorList._attackImgs2[charNum]._scale = 7;
+ outdoorList._attackImgs3[charNum]._scale = 11;
+ outdoorList._attackImgs4[charNum]._scale = 15;
+ outdoorList._attackImgs1[charNum]._sprites = nullptr;
+ outdoorList._attackImgs2[charNum]._sprites = nullptr;
+ outdoorList._attackImgs3[charNum]._sprites = nullptr;
+ outdoorList._attackImgs4[charNum]._sprites = nullptr;
+
+ switch (_shooting[charNum]) {
+ case 1:
+ outdoorList._attackImgs1[charNum]._sprites = &_powSprites;
+ break;
+ case 2:
+ outdoorList._attackImgs2[charNum]._sprites = &_powSprites;
+ break;
+ default:
+ outdoorList._attackImgs3[charNum]._sprites = &_powSprites;
+ break;
+ }
+ } else {
+ indoorList._attackImgs1[charNum]._scale = 3;
+ indoorList._attackImgs2[charNum]._scale = 7;
+ indoorList._attackImgs3[charNum]._scale = 11;
+ indoorList._attackImgs4[charNum]._scale = 15;
+ indoorList._attackImgs1[charNum]._sprites = nullptr;
+ indoorList._attackImgs2[charNum]._sprites = nullptr;
+ indoorList._attackImgs3[charNum]._sprites = nullptr;
+ indoorList._attackImgs4[charNum]._sprites = nullptr;
+
+ switch (_shooting[charNum]) {
+ case 1:
+ indoorList._attackImgs1[charNum]._sprites = &_powSprites;
+ break;
+ case 2:
+ indoorList._attackImgs2[charNum]._sprites = &_powSprites;
+ break;
+ default:
+ indoorList._attackImgs3[charNum]._sprites = &_powSprites;
+ break;
+ }
+ }
+ }
+
+ // Wait whilst the attacking effect is done
+ do {
+ intf.draw3d(true);
+ events.pollEventsAndWait();
+ } while (!_vm->shouldQuit() && intf._isAttacking);
+
+ endAttack();
+
+ if (_vm->_mode != MODE_COMBAT) {
+ // Combat wasn't previously active, but it is now. Set up
+ // the combat party from the currently active party
+ party._combatParty.clear();
+ for (uint idx = 0; idx < party._activeParty.size(); ++idx)
+ party._combatParty.push_back(&party._activeParty[idx]);
+ }
+
+ for (int idx = 0; idx < 36; ++idx) {
+ if (_gmonHit[idx] != -1)
+ attackMonster(_gmonHit[idx]);
+ }
+
+ _monstersAttacking = false;
+
+ if (_vm->_mode != MODE_SLEEPING) {
+ for (uint charNum = 0; charNum < party._activeParty.size(); ++charNum) {
+ Condition condition = party._activeParty[charNum].worstCondition();
+
+ if (condition != ASLEEP && (condition < PARALYZED || condition == NO_CONDITION)) {
+ _vm->_mode = MODE_1;
+ break;
+ }
+ }
+ }
+}
+
+void Combat::setupMonsterAttack(int monsterDataIndex, const Common::Point &pt) {
+ Party &party = *_vm->_party;
+
+ for (int idx = 0; idx < 36; ++idx) {
+ if (_gmonHit[idx] != -1) {
+ int result = stopAttack(pt - party._mazePosition);
+ if (result) {
+ _monstersAttacking = true;
+ _gmonHit[idx] = monsterDataIndex;
+
+ if (result != 1) {
+ for (int charNum = 0; charNum < MAX_PARTY_COUNT; ++charNum) {
+ if (!_shooting[charNum]) {
+ _shooting[charNum] = COMBAT_SHOOTING[result - 1];
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+bool Combat::monsterCanMove(const Common::Point &pt, int wallShift,
+ int xDiff, int yDiff, int monsterId) {
+ Map &map = *_vm->_map;
+ MazeMonster &monster = map._mobData._monsters[monsterId];
+ MonsterStruct &monsterData = map._monsterData[monster._spriteId];
+
+ Common::Point tempPos = pt;
+ if (map._isOutdoors) {
+ tempPos += Common::Point(xDiff, yDiff);
+ wallShift = 4;
+ }
+ int v = map.mazeLookup(tempPos, wallShift);
+
+ if (!map._isOutdoors) {
+ return v <= map.mazeData()._difficulties._wallNoPass;
+ } else {
+ SurfaceType surfaceType;
+ switch (v) {
+ case 0:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ case 11:
+ case 13:
+ case 14:
+ surfaceType = (SurfaceType)map.mazeData()._surfaceTypes[map._currentSurfaceId];
+ if (surfaceType == SURFTYPE_WATER || surfaceType == SURFTYPE_DWATER) {
+ return monsterData._flying || monster._spriteId == 59;
+ } else if (surfaceType == SURFTYPE_SPACE) {
+ return monsterData._flying;
+ } else {
+ return _vm->_files->_isDarkCc || monster._spriteId != 59;
+ }
+ default:
+ return v <= map.mazeData()._difficulties._wallNoPass;
+ }
+ }
+}
+
+void Combat::moveMonster(int monsterId, const Common::Point &pt) {
+ Map &map = *_vm->_map;
+ MazeMonster &monster = map._mobData._monsters[monsterId];
+
+ if (_monsterMap[pt.y][pt.x] < 3 && !monster._field7 && _vm->_moveMonsters) {
+ ++_monsterMap[pt.y][pt.x];
+ --_monsterMap[monster._position.y][monster._position.x];
+ monster._position = pt;
+ _monsterMoved[monsterId] = true;
+ }
+}
+
+void Combat::endAttack() {
+ Interface &intf = *_vm->_interface;
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+ intf._isAttacking = false;
+ IndoorDrawList &indoorList = intf._indoorList;
+ OutdoorDrawList &outdoorList = intf._outdoorList;
+
+ for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
+ Character &c = party._activeParty[idx];
+
+ if (map._isOutdoors) {
+ outdoorList._attackImgs1[idx]._scale = 0;
+ outdoorList._attackImgs2[idx]._scale = 0;
+ outdoorList._attackImgs3[idx]._scale = 0;
+ outdoorList._attackImgs4[idx]._scale = 0;
+ outdoorList._attackImgs1[idx]._sprites = nullptr;
+ outdoorList._attackImgs2[idx]._sprites = nullptr;
+ outdoorList._attackImgs3[idx]._sprites = nullptr;
+ outdoorList._attackImgs4[idx]._sprites = nullptr;
+ } else {
+ indoorList._attackImgs1[idx]._scale = 0;
+ indoorList._attackImgs2[idx]._scale = 0;
+ indoorList._attackImgs3[idx]._scale = 0;
+ indoorList._attackImgs4[idx]._scale = 0;
+ indoorList._attackImgs1[idx]._sprites = nullptr;
+ indoorList._attackImgs2[idx]._sprites = nullptr;
+ indoorList._attackImgs3[idx]._sprites = nullptr;
+ indoorList._attackImgs4[idx]._sprites = nullptr;
+ }
+ }
+
+ Common::fill(&_shooting[0], &_shooting[MAX_PARTY_COUNT], false);
+}
+
+void Combat::monsterOvercome() {
+ Map &map = *_vm->_map;
+
+ for (uint idx = 0; idx < map._mobData._monsters.size(); ++idx) {
+ MazeMonster &monster = map._mobData._monsters[idx];
+ int dataIndex = monster._spriteId;
+
+ if (monster._field7 != 0 && monster._field7 != 13) {
+ // Do a saving throw for monster
+ if (dataIndex <= _vm->getRandomNumber(1, dataIndex + 50))
+ monster._field7 = 0;
+ }
+ }
+}
+
+void Combat::attackMonster(int monsterId) {
+
+}
+
+bool Combat::stopAttack(const Common::Point &diffPt) {
+ // TODO
+ return false;
+}
} // End of namespace Xeen