diff options
author | Paul Gilbert | 2015-02-15 20:47:56 -0500 |
---|---|---|
committer | Paul Gilbert | 2015-02-15 20:47:56 -0500 |
commit | 75a070de1755fca9244eec1796ea892118afa84b (patch) | |
tree | 101d103a540d93de545bf0fc95906fd9a060093e /engines/xeen/combat.cpp | |
parent | ef2a4595c2c17260f61ad93d6c4374af7fa8a606 (diff) | |
download | scummvm-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.cpp | 401 |
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 |