/* 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 "xeen/interface.h" #include "xeen/dialogs/dialogs_char_info.h" #include "xeen/dialogs/dialogs_control_panel.h" #include "xeen/dialogs/dialogs_message.h" #include "xeen/dialogs/dialogs_quick_fight.h" #include "xeen/dialogs/dialogs_info.h" #include "xeen/dialogs/dialogs_items.h" #include "xeen/dialogs/dialogs_map.h" #include "xeen/dialogs/dialogs_query.h" #include "xeen/dialogs/dialogs_quests.h" #include "xeen/dialogs/dialogs_quick_ref.h" #include "xeen/dialogs/dialogs_spells.h" #include "xeen/resources.h" #include "xeen/xeen.h" #include "xeen/dialogs/dialogs_party.h" namespace Xeen { enum { SCENE_WINDOW = 11, SCENE_WIDTH = 216, SCENE_HEIGHT = 132 }; PartyDrawer::PartyDrawer(XeenEngine *vm): _vm(vm) { _restoreSprites.load("restorex.icn"); _hpSprites.load("hpbars.icn"); _dseFace.load("dse.fac"); _hiliteChar = HILIGHT_CHAR_NONE; } void PartyDrawer::drawParty(bool updateFlag) { Combat &combat = *_vm->_combat; Party &party = *_vm->_party; Resources &res = *_vm->_resources; Windows &windows = *_vm->_windows; bool inCombat = _vm->_mode == MODE_COMBAT; _restoreSprites.draw(0, 0, Common::Point(8, 149)); // Handle drawing the party faces uint partyCount = inCombat ? combat._combatParty.size() : party._activeParty.size(); for (uint idx = 0; idx < partyCount; ++idx) { Character &c = inCombat ? *combat._combatParty[idx] : party._activeParty[idx]; Condition charCondition = c.worstCondition(); int charFrame = Res.FACE_CONDITION_FRAMES[charCondition]; SpriteResource *sprites = (charFrame > 4) ? &_dseFace : c._faceSprites; assert(sprites); if (charFrame > 4) charFrame -= 5; sprites->draw(0, charFrame, Common::Point(Res.CHAR_FACES_X[idx], 150)); } for (uint idx = 0; idx < partyCount; ++idx) { Character &c = inCombat ? *combat._combatParty[idx] : party._activeParty[idx]; // Draw the Hp bar int maxHp = c.getMaxHP(); int frame; if (c._currentHp < 1) frame = 4; else if (c._currentHp > maxHp) frame = 3; else if (c._currentHp == maxHp) frame = 0; else if (c._currentHp < (maxHp / 4)) frame = 2; else frame = 1; _hpSprites.draw(0, frame, Common::Point(Res.HP_BARS_X[idx], 182)); } if (_hiliteChar != HILIGHT_CHAR_NONE) res._globalSprites.draw(0, 8, Common::Point(Res.CHAR_FACES_X[_hiliteChar] - 1, 149)); if (updateFlag) windows[33].update(); } void PartyDrawer::highlightChar(int charId) { Resources &res = *_vm->_resources; Windows &windows = *_vm->_windows; assert(charId < MAX_ACTIVE_PARTY); if (charId != _hiliteChar && _hiliteChar != HILIGHT_CHAR_DISABLED) { // Handle deselecting any previusly selected char if (_hiliteChar != HILIGHT_CHAR_NONE) { res._globalSprites.draw(0, 9 + _hiliteChar, Common::Point(Res.CHAR_FACES_X[_hiliteChar] - 1, 149)); } // Highlight new character res._globalSprites.draw(0, 8, Common::Point(Res.CHAR_FACES_X[charId] - 1, 149)); _hiliteChar = charId; windows[33].update(); } } void PartyDrawer::highlightChar(const Character *c) { int charNum = _vm->_party->_activeParty.indexOf(*c); if (charNum != -1) highlightChar(charNum); } void PartyDrawer::unhighlightChar() { Resources &res = *_vm->_resources; Windows &windows = *_vm->_windows; if (_hiliteChar != HILIGHT_CHAR_NONE) { res._globalSprites.draw(0, _hiliteChar + 9, Common::Point(Res.CHAR_FACES_X[_hiliteChar] - 1, 149)); _hiliteChar = HILIGHT_CHAR_NONE; windows[33].update(); } } void PartyDrawer::resetHighlight() { _hiliteChar = HILIGHT_CHAR_NONE; } /*------------------------------------------------------------------------*/ Interface::Interface(XeenEngine *vm) : ButtonContainer(vm), InterfaceScene(vm), PartyDrawer(vm), _vm(vm) { _buttonsLoaded = false; _obscurity = OBSCURITY_NONE; _steppingFX = 0; _falling = FALL_NONE; _blessedUIFrame = 0; _powerShieldUIFrame = 0; _holyBonusUIFrame = 0; _heroismUIFrame = 0; _flipUIFrame = 0; _face1UIFrame = 0; _face2UIFrame = 0; _levitateUIFrame = 0; _spotDoorsUIFrame = 0; _dangerSenseUIFrame = 0; _face1State = _face2State = 0; _upDoorText = false; _tillMove = 0; _iconsMode = ICONS_STANDARD; Common::fill(&_charFX[0], &_charFX[MAX_ACTIVE_PARTY], 0); setWaitBounds(); } void Interface::setup() { _borderSprites.load("border.icn"); _spellFxSprites.load("spellfx.icn"); _fecpSprites.load("fecp.brd"); _blessSprites.load("bless.icn"); _charPowSprites.load("charpow.icn"); _uiSprites.load("inn.icn"); _stdIcons.load("main.icn"); _combatIcons.load("combat.icn"); Party &party = *_vm->_party; party.loadActiveParty(); party._newDay = party._minutes < 300; } void Interface::startup() { Resources &res = *_vm->_resources; animate3d(); if (_vm->_map->_isOutdoors) { setIndoorsMonsters(); setIndoorsObjects(); } else { setOutdoorsMonsters(); setOutdoorsObjects(); } draw3d(false); if (g_vm->getGameID() == GType_Swords) res._logoSprites.draw(1, 0, Common::Point(232, 9)); else res._globalSprites.draw(1, 5, Common::Point(232, 9)); drawParty(false); setMainButtons(); _tillMove = false; } void Interface::mainIconsPrint() { Resources &res = *_vm->_resources; Windows &windows = *_vm->_windows; windows[38].close(); windows[12].close(); res._globalSprites.draw(0, 7, Common::Point(232, 74)); drawButtons(&windows[0]); windows[34].update(); } void Interface::setMainButtons(IconsMode mode) { clearButtons(); _iconsMode = mode; SpriteResource *spr = mode == ICONS_COMBAT ? &_combatIcons : &_stdIcons; addButton(Common::Rect(235, 75, 259, 95), Common::KEYCODE_s, spr); addButton(Common::Rect(260, 75, 284, 95), Common::KEYCODE_c, spr); addButton(Common::Rect(286, 75, 310, 95), Common::KEYCODE_r, spr); addButton(Common::Rect(235, 96, 259, 116), Common::KEYCODE_b, spr); addButton(Common::Rect(260, 96, 284, 116), Common::KEYCODE_d, spr); addButton(Common::Rect(286, 96, 310, 116), Common::KEYCODE_v, spr); addButton(Common::Rect(235, 117, 259, 137), Common::KEYCODE_m, spr); addButton(Common::Rect(260, 117, 284, 137), Common::KEYCODE_i, spr); addButton(Common::Rect(286, 117, 310, 137), Common::KEYCODE_q, spr); addButton(Common::Rect(109, 137, 122, 147), Common::KEYCODE_TAB, spr); addButton(Common::Rect(235, 148, 259, 168), Common::KEYCODE_LEFT, spr); addButton(Common::Rect(260, 148, 284, 168), Common::KEYCODE_UP, spr); addButton(Common::Rect(286, 148, 310, 168), Common::KEYCODE_RIGHT, spr); addButton(Common::Rect(235, 169, 259, 189), (Common::KBD_CTRL << 16) |Common::KEYCODE_LEFT, spr); addButton(Common::Rect(260, 169, 284, 189), Common::KEYCODE_DOWN, spr); addButton(Common::Rect(286, 169, 310, 189), (Common::KBD_CTRL << 16) | Common::KEYCODE_RIGHT, spr); addButton(Common::Rect(236, 11, 308, 69), Common::KEYCODE_EQUALS); addButton(Common::Rect(239, 27, 312, 37), Common::KEYCODE_1); addButton(Common::Rect(239, 37, 312, 47), Common::KEYCODE_2); addButton(Common::Rect(239, 47, 312, 57), Common::KEYCODE_3); addPartyButtons(_vm); if (mode == ICONS_COMBAT) { _buttons[0]._value = Common::KEYCODE_f; _buttons[1]._value = Common::KEYCODE_c; _buttons[2]._value = Common::KEYCODE_a; _buttons[3]._value = Common::KEYCODE_u; _buttons[4]._value = Common::KEYCODE_r; _buttons[5]._value = Common::KEYCODE_b; _buttons[6]._value = Common::KEYCODE_o; _buttons[7]._value = Common::KEYCODE_i; _buttons[16]._value = 0; } } void Interface::perform() { Combat &combat = *_vm->_combat; EventsManager &events = *_vm->_events; Map &map = *_vm->_map; Party &party = *_vm->_party; Scripts &scripts = *_vm->_scripts; Sound &sound = *_vm->_sound; do { // Draw the next frame events.updateGameCounter(); draw3d(true); // Wait for a frame or a user event _buttonValue = 0; do { events.pollEventsAndWait(); if (g_vm->shouldExit() || g_vm->isLoadPending() || party._dead) return; checkEvents(g_vm); } while (!_buttonValue && events.timeElapsed() < 1); } while (!_buttonValue); if (_buttonValue == Common::KEYCODE_SPACE) { int lookupId = map.mazeLookup(party._mazePosition, Res.WALL_SHIFTS[party._mazeDirection][2]); bool eventsFlag = true; switch (lookupId) { case 1: if (!map._isOutdoors) { eventsFlag = !scripts.openGrate(13, 1); } break; case 6: // Open grate being closed if (!map._isOutdoors) { eventsFlag = !scripts.openGrate(9, 0); } break; case 9: // Closed grate being opened if (!map._isOutdoors) { eventsFlag = !scripts.openGrate(6, 0); } break; case 13: if (!map._isOutdoors) { eventsFlag = !scripts.openGrate(1, 1); } break; default: break; } if (eventsFlag) { scripts.checkEvents(); if (_vm->shouldExit()) return; } else { clearEvents(); } } switch (_buttonValue) { case Common::KEYCODE_TAB: // Show control panel combat._moveMonsters = false; ControlPanel::show(_vm); if (!g_vm->shouldExit() && !g_vm->_gameMode) combat._moveMonsters = true; break; case Common::KEYCODE_SPACE: case Common::KEYCODE_w: // Wait one turn chargeStep(); combat.moveMonsters(); _upDoorText = false; _flipDefaultGround = !_flipDefaultGround; _flipGround = !_flipGround; stepTime(); break; case (Common::KBD_CTRL << 16) | Common::KEYCODE_LEFT: case Common::KEYCODE_KP4: if (checkMoveDirection((Common::KBD_CTRL << 16) | Common::KEYCODE_LEFT)) { switch (party._mazeDirection) { case DIR_NORTH: --party._mazePosition.x; break; case DIR_SOUTH: ++party._mazePosition.x; break; case DIR_EAST: ++party._mazePosition.y; break; case DIR_WEST: --party._mazePosition.y; break; default: break; } chargeStep(); _isAnimReset = true; party._mazeDirection = (Direction)((int)party._mazeDirection & 3); _flipSky = !_flipSky; stepTime(); } break; case (Common::KBD_CTRL << 16) | Common::KEYCODE_RIGHT: case Common::KEYCODE_KP6: if (checkMoveDirection((Common::KBD_CTRL << 16) | Common::KEYCODE_RIGHT)) { switch (party._mazeDirection) { case DIR_NORTH: ++party._mazePosition.x; break; case DIR_SOUTH: --party._mazePosition.x; break; case DIR_EAST: --party._mazePosition.y; break; case DIR_WEST: ++party._mazePosition.y; break; default: break; } chargeStep(); _isAnimReset = true; party._mazeDirection = (Direction)((int)party._mazeDirection & 3); _flipSky = !_flipSky; stepTime(); } break; case Common::KEYCODE_LEFT: case Common::KEYCODE_KP7: party._mazeDirection = (Direction)((int)party._mazeDirection - 1); _isAnimReset = true; party._mazeDirection = (Direction)((int)party._mazeDirection & 3); _flipSky = !_flipSky; stepTime(); break; case Common::KEYCODE_RIGHT: case Common::KEYCODE_KP9: party._mazeDirection = (Direction)((int)party._mazeDirection + 1); _isAnimReset = true; party._mazeDirection = (Direction)((int)party._mazeDirection & 3); _flipSky = !_flipSky; stepTime(); break; case Common::KEYCODE_UP: case Common::KEYCODE_KP8: if (checkMoveDirection(Common::KEYCODE_UP)) { switch (party._mazeDirection) { case DIR_NORTH: ++party._mazePosition.y; break; case DIR_SOUTH: --party._mazePosition.y; break; case DIR_EAST: ++party._mazePosition.x; break; case DIR_WEST: --party._mazePosition.x; break; default: break; } chargeStep(); stepTime(); } break; case Common::KEYCODE_DOWN: case Common::KEYCODE_KP2: if (checkMoveDirection(Common::KEYCODE_DOWN)) { switch (party._mazeDirection) { case DIR_NORTH: --party._mazePosition.y; break; case DIR_SOUTH: ++party._mazePosition.y; break; case DIR_EAST: --party._mazePosition.x; break; case DIR_WEST: ++party._mazePosition.x; break; default: break; } chargeStep(); stepTime(); } break; case (Common::KBD_CTRL << 16) | Common::KEYCODE_DOWN: party._mazeDirection = (Direction)((int)party._mazeDirection ^ 2); _flipSky = !_flipSky; _isAnimReset = true; stepTime(); break; case Common::KEYCODE_F1: case Common::KEYCODE_F2: case Common::KEYCODE_F3: case Common::KEYCODE_F4: case Common::KEYCODE_F5: case Common::KEYCODE_F6: _buttonValue -= Common::KEYCODE_F1; if (_buttonValue < (int)party._activeParty.size()) { CharacterInfo::show(_vm, _buttonValue); if (party._stepped) combat.moveMonsters(); } break; case Common::KEYCODE_EQUALS: case Common::KEYCODE_KP_EQUALS: // Toggle minimap party._automapOn = !party._automapOn; break; case Common::KEYCODE_b: chargeStep(); if (map.getCell(2) < map.mazeData()._difficulties._wallNoPass && !map._isOutdoors) { switch (party._mazeDirection) { case DIR_NORTH: ++party._mazePosition.y; break; case DIR_EAST: ++party._mazePosition.x; break; case DIR_SOUTH: --party._mazePosition.y; break; case DIR_WEST: --party._mazePosition.x; break; default: break; } chargeStep(); stepTime(); } else { bash(party._mazePosition, party._mazeDirection); } break; case Common::KEYCODE_c: // Cast spell if (_tillMove) { combat.moveMonsters(); draw3d(true); } if (CastSpell::show(_vm) != -1) { chargeStep(); doStepCode(); } break; case Common::KEYCODE_i: // Show Info dialog combat._moveMonsters = false; InfoDialog::show(_vm); combat._moveMonsters = true; break; case Common::KEYCODE_m: // Show map dialog MapDialog::show(_vm); break; case Common::KEYCODE_q: // Show the quick reference dialog QuickReferenceDialog::show(_vm); break; case Common::KEYCODE_r: // Rest rest(); break; case Common::KEYCODE_s: // Shoot if (!party.canShoot()) { sound.playFX(21); } else { if (_tillMove) { combat.moveMonsters(); draw3d(true); } if (combat._attackMonsters[0] != -1 || combat._attackMonsters[1] != -1 || combat._attackMonsters[2] != -1) { if ((_vm->_mode == MODE_INTERACTIVE || _vm->_mode == MODE_SLEEPING) && !combat._monstersAttacking && !_charsShooting) { doCombat(); } } combat.shootRangedWeapon(); chargeStep(); doStepCode(); } break; case Common::KEYCODE_v: // Show the quests dialog Quests::show(_vm); break; default: break; } } void Interface::chargeStep() { if (!_vm->_party->_dead) { _vm->_party->changeTime(_vm->_map->_isOutdoors ? 10 : 1); if (_tillMove) { _vm->_combat->moveMonsters(); } _tillMove = 3; } } void Interface::stepTime() { Party &party = *_vm->_party; Sound &sound = *_vm->_sound; doStepCode(); if (++party._ctr24 == 24) party._ctr24 = 0; if (_buttonValue != Common::KEYCODE_SPACE && _buttonValue != Common::KEYCODE_w) { _steppingFX ^= 1; sound.playFX(_steppingFX + 7); } _upDoorText = false; _flipDefaultGround = !_flipDefaultGround; _flipGround = !_flipGround; } void Interface::doStepCode() { Combat &combat = *_vm->_combat; Map &map = *_vm->_map; Party &party = *_vm->_party; int damage = 0; party._stepped = true; _upDoorText = false; map.getCell(2); int surfaceId = map.mazeData()._surfaceTypes[map._currentSurfaceId]; switch (surfaceId) { case SURFTYPE_SPACE: // Wheeze.. can't breathe in space! Explosive decompression, here we come party._dead = true; break; case SURFTYPE_LAVA: // It burns, it burns! damage = 100; combat._damageType = DT_FIRE; break; case SURFTYPE_SKY: // We can fly, we can.. oh wait, we can't! damage = 100; combat._damageType = DT_PHYSICAL; _falling = FALL_IN_PROGRESS; break; case SURFTYPE_DESERT: // Without navigation skills, simulate getting lost by adding extra time if (map._isOutdoors && !party.checkSkill(NAVIGATOR)) party.addTime(170); break; case SURFTYPE_CLOUD: if (!party._levitateCount) { combat._damageType = DT_PHYSICAL; _falling = FALL_IN_PROGRESS; damage = 100; } break; default: break; } if (_vm->getGameID() != GType_Swords && _vm->_files->_ccNum && party._gameFlags[1][118]) { _falling = FALL_NONE; } else { if (_falling != FALL_NONE) startFalling(false); if ((party._mazePosition.x & 16) || (party._mazePosition.y & 16)) { if (map._isOutdoors) map.getNewMaze(); } if (damage) { _flipGround = !_flipGround; draw3d(true); int oldTarget = combat._combatTarget; combat._combatTarget = 0; // WORKAROUND: Stepping into combat whilst on lava results in damageType being lost combat._damageType = (surfaceId == SURFTYPE_LAVA) ? DT_FIRE : DT_PHYSICAL; combat.giveCharDamage(damage, combat._damageType, 0); combat._combatTarget = oldTarget; _flipGround = !_flipGround; } else if (party._dead) { draw3d(true); } } } void Interface::startFalling(bool flag) { Combat &combat = *_vm->_combat; Map &map = *_vm->_map; Party &party = *_vm->_party; int ccNum = _vm->_files->_ccNum; if (ccNum && party._gameFlags[1][118]) { _falling = FALL_NONE; return; } _falling = FALL_NONE; draw3d(true); _falling = FALL_START; draw3d(false); if (flag && (!ccNum || party._fallMaze != 0)) { party._mazeId = party._fallMaze; party._mazePosition = party._fallPosition; } else if (!ccNum) { switch (party._mazeId - 25) { case 0: case 26: case 27: case 28: case 29: party._mazeId = 24; party._mazePosition = Common::Point(11, 9); break; case 1: case 30: case 31: case 32: case 33: party._mazeId = 12; party._mazePosition = Common::Point(6, 15); break; case 2: case 34: case 35: case 36: case 37: case 51: case 52: case 53: party._mazeId = 15; party._mazePosition = Common::Point(4, 12); party._mazeDirection = DIR_SOUTH; break; case 40: case 41: party._mazeId = 14; party._mazePosition = Common::Point(8, 3); break; case 44: case 45: party._mazeId = 1; party._mazePosition = Common::Point(8, 7); party._mazeDirection = DIR_NORTH; break; case 49: party._mazeId = 12; party._mazePosition = Common::Point(11, 13); party._mazeDirection = DIR_SOUTH; break; case 57: case 58: case 59: party._mazeId = 5; party._mazePosition = Common::Point(12, 7); party._mazeDirection = DIR_NORTH; break; case 60: party._mazeId = 6; party._mazePosition = Common::Point(12, 3); party._mazeDirection = DIR_NORTH; break; default: party._mazeId = 23; party._mazePosition = Common::Point(12, 10); party._mazeDirection = DIR_NORTH; break; } } else { if (party._mazeId > 88 && party._mazeId < 114) { party._mazeId -= 88; } else { switch (party._mazeId - 25) { case 0: party._mazeId = 89; party._mazePosition = Common::Point(2, 14); break; case 1: party._mazeId = 109; party._mazePosition = Common::Point(13, 14); break; case 2: party._mazeId = 112; party._mazePosition = Common::Point(13, 3); break; case 3: party._mazeId = 92; party._mazePosition = Common::Point(2, 3); break; case 12: case 13: party._mazeId = 14; party._mazePosition = Common::Point(10, 2); break; case 16: case 17: case 18: party._mazeId = 4; party._mazePosition = Common::Point(5, 14); break; case 20: case 21: case 22: party._mazeId = 21; party._mazePosition = Common::Point(9, 11); break; case 24: case 25: case 26: party._mazeId = 1; party._mazePosition = Common::Point(10, 4); break; case 28: case 29: case 30: case 31: party._mazeId = 26; party._mazePosition = Common::Point(12, 10); break; case 32: case 33: case 34: case 35: party._mazeId = 3; party._mazePosition = Common::Point(4, 9); break; case 36: case 37: case 38: case 39: party._mazeId = 16; party._mazePosition = Common::Point(2, 7); break; case 40: case 41: case 42: case 43: party._mazeId = 23; party._mazePosition = Common::Point(10, 9); break; case 44: case 45: case 46: case 47: party._mazeId = 13; party._mazePosition = Common::Point(2, 10); break; case 103: case 104: map._loadCcNum = 0; party._mazeId = 8; party._mazePosition = Common::Point(11, 15); party._mazeDirection = DIR_NORTH; break; case 105: party._mazeId = 24; party._mazePosition = Common::Point(11, 9); break; case 106: party._mazeId = 12; party._mazePosition = Common::Point(6, 15); break; case 107: party._mazeId = 15; party._mazePosition = Common::Point(4, 12); break; default: party._mazeId = 29; party._mazePosition = Common::Point(25, 21); party._mazeDirection = DIR_NORTH; break; } } } _falling = FALL_IN_PROGRESS; map.load(party._mazeId); if (flag) { if (map._isOutdoors && ((party._mazePosition.x & 16) || (party._mazePosition.y & 16))) map.getNewMaze(); _flipGround ^= 1; draw3d(true); int oldTarget = combat._combatTarget; combat._combatTarget = 0; combat.giveCharDamage(party._fallDamage, DT_PHYSICAL, 0); combat._combatTarget = oldTarget; _flipGround ^= 1; } } bool Interface::checkMoveDirection(int key) { Debugger &debugger = *g_vm->_debugger; Map &map = *_vm->_map; Party &party = *_vm->_party; Sound &sound = *_vm->_sound; // If intangibility is turned on in the debugger, allow any movement if (debugger._intangible) return true; // For strafing or moving backwards, temporarily move to face the direction being checked, // since the call to getCell will the adjacent cell details in the direction being faced Direction dir = party._mazeDirection; switch (key) { case (Common::KBD_CTRL << 16) | Common::KEYCODE_LEFT: party._mazeDirection = (party._mazeDirection == DIR_NORTH) ? DIR_WEST : (Direction)(party._mazeDirection - 1); break; case (Common::KBD_CTRL << 16) | Common::KEYCODE_RIGHT: party._mazeDirection = (party._mazeDirection == DIR_WEST) ? DIR_NORTH : (Direction)(party._mazeDirection + 1); break; case Common::KEYCODE_DOWN: party._mazeDirection = (Direction)((int)party._mazeDirection ^ 2); break; default: break; } // Get next facing tile information map.getCell(7); int startSurfaceId = map._currentSurfaceId; int surfaceId; if (map._isOutdoors) { // Reset direction back to original facing, if it was changed for strafing checks party._mazeDirection = dir; switch (map._currentWall) { case 5: if (_vm->_files->_ccNum) goto check; // fall through case 0: case 2: case 4: case 8: case 11: case 13: case 14: surfaceId = map.mazeData()._surfaceTypes[map._currentSurfaceId]; if (surfaceId == SURFTYPE_WATER) { if (party.checkSkill(SWIMMING) || party._walkOnWaterActive) return true; } else if (surfaceId == SURFTYPE_DWATER) { if (party._walkOnWaterActive) return true; } else if (surfaceId != SURFTYPE_SPACE) { return true; } sound.playFX(21); return false; case 1: case 7: case 9: case 10: case 12: check: if (party.checkSkill(MOUNTAINEER)) return true; sound.playFX(21); return false; default: break; } } else { surfaceId = map.getCell(2); // Reset direction back to original facing, if it was changed for strafing checks party._mazeDirection = dir; if (surfaceId >= map.mazeData()._difficulties._wallNoPass) { sound.playFX(46); return false; } else { if (startSurfaceId != SURFTYPE_SWAMP || party.checkSkill(SWIMMING) || party._walkOnWaterActive) { if (_buttonValue == Common::KEYCODE_UP && _wo[107]) { _openDoor = true; sound.playFX(47); draw3d(true); _openDoor = false; } return true; } else { sound.playFX(46); return false; } } } return true; } void Interface::rest() { Map &map = *_vm->_map; Party &party = *_vm->_party; map.cellFlagLookup(party._mazePosition); if ((map._currentCantRest || (map.mazeData()._mazeFlags & RESTRICTION_REST)) && _vm->_mode != MODE_INTERACTIVE2) { ErrorScroll::show(_vm, Res.TOO_DANGEROUS_TO_REST, WT_NONFREEZED_WAIT); } else { // Check whether any character is in danger of dying bool dangerFlag = false; for (uint charIdx = 0; charIdx < party._activeParty.size(); ++charIdx) { for (int attrib = MIGHT; attrib <= LUCK; ++attrib) { if (party._activeParty[charIdx].getStat((Attribute)attrib) < 1) dangerFlag = true; } } if (dangerFlag) { if (!Confirm::show(_vm, Res.SOME_CHARS_MAY_DIE)) return; } // Mark all the players as being asleep for (uint charIdx = 0; charIdx < party._activeParty.size(); ++charIdx) { party._activeParty[charIdx]._conditions[ASLEEP] = 1; } drawParty(true); Mode oldMode = _vm->_mode; _vm->_mode = MODE_SLEEPING; if (oldMode == MODE_INTERACTIVE2) { party.changeTime(8 * 60); } else { for (int idx = 0; idx < 10; ++idx) { chargeStep(); draw3d(true); if (_vm->_mode == MODE_INTERACTIVE) { _vm->_mode = oldMode; return; } } party.changeTime(map._isOutdoors ? 380 : 470); } if (_vm->getRandomNumber(1, 20) == 1) _vm->dream(); party.resetTemps(); // Wake up the party bool starving = false; int foodConsumed = 0; for (uint charIdx = 0; charIdx < party._activeParty.size(); ++charIdx) { Character &c = party._activeParty[charIdx]; c._conditions[ASLEEP] = 0; if (party._food == 0) { starving = true; } else { party._rested = true; Condition condition = c.worstCondition(); if (condition < DEAD || condition > ERADICATED) { --party._food; ++foodConsumed; party._heroism = 0; party._holyBonus = 0; party._powerShield = 0; party._blessed = 0; c._conditions[UNCONSCIOUS] = 0; c._currentHp = c.getMaxHP(); c._currentSp = c.getMaxSP(); // WORKAROUND: Resting curing weakness only originally worked due to a bug in changeTime // resetting WEAK if party wasn't drunk. With that resolved, we have to reset WEAK here c._conditions[WEAK] = 0; } } } drawParty(true); _vm->_mode = oldMode; doStepCode(); draw3d(true); ErrorScroll::show(_vm, Common::String::format(Res.REST_COMPLETE, starving ? Res.PARTY_IS_STARVING : Res.HIT_SPELL_POINTS_RESTORED, foodConsumed)); party.checkPartyDead(); } } void Interface::bash(const Common::Point &pt, Direction direction) { EventsManager &events = *_vm->_events; Map &map = *_vm->_map; Party &party = *_vm->_party; Sound &sound = *_vm->_sound; Windows &windows = *_vm->_windows; if (map._isOutdoors) return; sound.playFX(31); uint charNum1 = 0, charNum2 = 0; for (uint charIdx = 0; charIdx < party._activeParty.size(); ++charIdx) { Character &c = party._activeParty[charIdx]; Condition condition = c.worstCondition(); if (!(condition == ASLEEP || (condition >= PARALYZED && condition <= ERADICATED))) { if (charNum1) { charNum2 = charIdx + 1; break; } else { charNum1 = charIdx + 1; } } } party._activeParty[charNum1 - 1].subtractHitPoints(2); _charPowSprites.draw(windows[0], 0, Common::Point(Res.CHAR_FACES_X[charNum1 - 1], 150)); windows[0].update(); if (charNum2) { party._activeParty[charNum2 - 1].subtractHitPoints(2); _charPowSprites.draw(windows[0], 0, Common::Point(Res.CHAR_FACES_X[charNum2 - 1], 150)); windows[0].update(); } int cell = map.mazeLookup(Common::Point(pt.x + Res.SCREEN_POSITIONING_X[direction][7], pt.y + Res.SCREEN_POSITIONING_Y[direction][7]), 0, 0xffff); if (cell != INVALID_CELL) { int v = map.getCell(2); if (v == 7) { ++_wo[207]; ++_wo[267]; ++_wo[287]; } else if (v == 14) { ++_wo[267]; ++_wo[287]; } else if (v == 15) { ++_wo[287]; } else { int might = party._activeParty[charNum1 - 1].getStat(MIGHT) + _vm->getRandomNumber(1, 30); if (charNum2) might += party._activeParty[charNum2 - 1].getStat(MIGHT); int bashThreshold = (v == 9) ? map.mazeData()._difficulties._bashGrate : map.mazeData()._difficulties._bashWall; if (might >= bashThreshold) { // Remove the wall on the current cell, and the reverse wall // on the cell we're bashing through to map.setWall(pt, direction, 3); switch (direction) { case DIR_NORTH: map.setWall(Common::Point(pt.x, pt.y + 1), DIR_SOUTH, 3); break; case DIR_EAST: map.setWall(Common::Point(pt.x + 1, pt.y), DIR_WEST, 3); break; case DIR_SOUTH: map.setWall(Common::Point(pt.x, pt.y - 1), DIR_NORTH, 3); break; case DIR_WEST: map.setWall(Common::Point(pt.x - 1, pt.y), DIR_EAST, 3); break; default: break; } } } } party.checkPartyDead(); events.ipause(2); drawParty(true); } void Interface::draw3d(bool updateFlag, bool pauseFlag) { Combat &combat = *_vm->_combat; EventsManager &events = *_vm->_events; Party &party = *_vm->_party; Scripts &scripts = *_vm->_scripts; Windows &windows = *_vm->_windows; events.timeMark5(); if (windows[SCENE_WINDOW]._enabled) return; _flipUIFrame = (_flipUIFrame + 1) % 4; if (_flipUIFrame == 0) _flipWater = !_flipWater; if (_tillMove && (_vm->_mode == MODE_INTERACTIVE || _vm->_mode == MODE_COMBAT) && !combat._monstersAttacking && combat._moveMonsters) { if (--_tillMove == 0) combat.moveMonsters(); } // Draw the game scene drawScene(); // Draw the minimap drawMinimap(); // Handle any darkness-based oscurity obscureScene(_obscurity); if (_falling == FALL_IN_PROGRESS) handleFalling(); if (_falling == FALL_START) { setupFallSurface(true); } assembleBorder(); // Draw any on-screen text if flagged to do so if (_upDoorText && combat._attackMonsters[0] == -1) { windows[3].writeString(_screenText); } if (updateFlag) { windows[1].update(); windows[3].update(); } if (combat._attackMonsters[0] != -1 || combat._attackMonsters[1] != -1 || combat._attackMonsters[2] != -1) { if ((_vm->_mode == MODE_INTERACTIVE || _vm->_mode == MODE_SLEEPING) && !combat._monstersAttacking && !_charsShooting && combat._moveMonsters) { doCombat(); if (scripts._eventSkipped) scripts.checkEvents(); } } party._stepped = false; if (pauseFlag) events.ipause5(2); } void Interface::handleFalling() { Party &party = *_vm->_party; Screen &screen = *_vm->_screen; Sound &sound = *_vm->_sound; Windows &windows = *_vm->_windows; Window &w = windows[3]; // Set the bottom half of the fall surface (area that is being fallen to) setupFallSurface(false); // Update character faces and start scream for (uint idx = 0; idx < party._activeParty.size(); ++idx) { party._activeParty[idx]._faceSprites->draw(0, 4, Common::Point(Res.CHAR_FACES_X[idx], 150)); } windows[33].update(); sound.playFX(11); sound.playSound("scream.voc"); // Fall down to the ground #define YINDEX (SCENE_HEIGHT / 2) const int Y_LIST[] = { SCENE_HEIGHT, SCENE_HEIGHT - 5, SCENE_HEIGHT, SCENE_HEIGHT - 3, SCENE_HEIGHT }; for (int idx = 1; idx < YINDEX + 5; ++idx) { fall((idx < YINDEX) ? idx * 2 : Y_LIST[idx - YINDEX]); assembleBorder(); w.update(); screen.update(); g_system->delayMillis(5); if (idx == YINDEX) { sound.stopSound(); sound.playSound("unnh.voc"); sound.playFX(31); } } shake(10); _falling = FALL_NONE; drawParty(true); } void Interface::setupFallSurface(bool isTop) { Window &w = (*g_vm->_windows)[SCENE_WINDOW]; if (_fallSurface.empty()) _fallSurface.create(SCENE_WIDTH, SCENE_HEIGHT * 2); _fallSurface.blitFrom(w, w.getBounds(), Common::Point(0, isTop ? 0 : SCENE_HEIGHT)); } void Interface::fall(int yp) { Window &w = (*g_vm->_windows)[SCENE_WINDOW]; w.blitFrom(_fallSurface, Common::Rect(0, yp, SCENE_WIDTH, yp + SCENE_HEIGHT), Common::Point(8, 8)); } void Interface::shake(int count) { Screen &screen = *g_vm->_screen; byte b; for (int idx = 0; idx < count * 2; ++idx) { for (int yp = 0; yp < screen.h; ++yp) { byte *lineP = (byte *)screen.getBasePtr(0, yp); if (idx % 2) { // Shift back right b = lineP[SCREEN_WIDTH - 1]; Common::copy_backward(lineP, lineP + SCREEN_WIDTH - 1, lineP + SCREEN_WIDTH); lineP[0] = b; } else { // Scroll left one pixel b = lineP[0]; Common::copy(lineP + 1, lineP + SCREEN_WIDTH, lineP); lineP[SCREEN_WIDTH - 1] = b; } } screen.markAllDirty(); screen.update(); g_system->delayMillis(5); } } void Interface::assembleBorder() { Combat &combat = *_vm->_combat; Resources &res = *_vm->_resources; Windows &windows = *_vm->_windows; // Draw the outer frame res._globalSprites.draw(windows[0], 0, Common::Point(8, 8)); // Draw the animating bat character on the left screen edge to indicate // that the party is being levitated _borderSprites.draw(windows[0], _vm->_party->_levitateCount ? _levitateUIFrame + 16 : 16, Common::Point(0, 82)); _levitateUIFrame = (_levitateUIFrame + 1) % 12; // Draw UI element to indicate whether can spot hidden doors _borderSprites.draw(0, (_thinWall && _vm->_party->checkSkill(SPOT_DOORS)) ? _spotDoorsUIFrame + 28 : 28, Common::Point(194, 91)); _spotDoorsUIFrame = (_spotDoorsUIFrame + 1) % 12; // Draw UI element to indicate whether can sense danger _borderSprites.draw(0, (combat._dangerPresent && _vm->_party->checkSkill(DANGER_SENSE)) ? _spotDoorsUIFrame + 40 : 40, Common::Point(107, 9)); _dangerSenseUIFrame = (_dangerSenseUIFrame + 1) % 12; // Handle the face UI elements for indicating clairvoyance status _face1UIFrame = (_face1UIFrame + 1) % 4; if (_face1State == 0) _face1UIFrame += 4; else if (_face1State == 2) _face1UIFrame = 0; _face2UIFrame = (_face2UIFrame + 1) % 4 + 12; if (_face2State == 0) _face2UIFrame -= 3; else if (_face2State == 2) _face2UIFrame = 8; if (!_vm->_party->_clairvoyanceActive) { _face1UIFrame = 0; _face2UIFrame = 8; } _borderSprites.draw(0, _face1UIFrame, Common::Point(0, 32)); _borderSprites.draw(0, windows[10]._enabled || windows[2]._enabled ? 52 : _face2UIFrame, Common::Point(215, 32)); // Draw resistence indicators if (!windows[10]._enabled && !windows[2]._enabled && !windows[38]._enabled) { _fecpSprites.draw(0, _vm->_party->_fireResistence ? 1 : 0, Common::Point(2, 2)); _fecpSprites.draw(0, _vm->_party->_electricityResistence ? 3 : 2, Common::Point(219, 2)); _fecpSprites.draw(0, _vm->_party->_coldResistence ? 5 : 4, Common::Point(2, 134)); _fecpSprites.draw(0, _vm->_party->_poisonResistence ? 7 : 6, Common::Point(219, 134)); } else { _fecpSprites.draw(0, _vm->_party->_fireResistence ? 9 : 8, Common::Point(8, 8)); _fecpSprites.draw(0, _vm->_party->_electricityResistence ? 11 : 10, Common::Point(219, 8)); _fecpSprites.draw(0, _vm->_party->_coldResistence ? 13 : 12, Common::Point(8, 134)); _fecpSprites.draw(0, _vm->_party->_poisonResistence ? 15 : 14, Common::Point(219, 134)); } // Draw UI element for blessed _blessSprites.draw(0, 16, Common::Point(33, 137)); if (_vm->_party->_blessed) { _blessedUIFrame = (_blessedUIFrame + 1) % 4; _blessSprites.draw(0, _blessedUIFrame, Common::Point(33, 137)); } // Draw UI element for power shield if (_vm->_party->_powerShield) { _powerShieldUIFrame = (_powerShieldUIFrame + 1) % 4; _blessSprites.draw(0, _powerShieldUIFrame + 4, Common::Point(55, 137)); } // Draw UI element for holy bonus if (_vm->_party->_holyBonus) { _holyBonusUIFrame = (_holyBonusUIFrame + 1) % 4; _blessSprites.draw(0, _holyBonusUIFrame + 8, Common::Point(160, 137)); } // Draw UI element for heroism if (_vm->_party->_heroism) { _heroismUIFrame = (_heroismUIFrame + 1) % 4; _blessSprites.draw(0, _heroismUIFrame + 12, Common::Point(182, 137)); } // Draw direction character if direction sense is active if (_vm->_party->checkSkill(DIRECTION_SENSE) && !_vm->_noDirectionSense) { const char *dirText = Res.DIRECTION_TEXT_UPPER[_vm->_party->_mazeDirection]; Common::String msg = Common::String::format("\x2\f08\x3""c\v139\t116%c\fd\x1", *dirText); windows[0].writeString(msg); } // Draw view frame if (windows[12]._enabled) windows[12].frame(); } void Interface::doCombat() { Combat &combat = *_vm->_combat; EventsManager &events = *_vm->_events; Map &map = *_vm->_map; Party &party = *_vm->_party; Scripts &scripts = *_vm->_scripts; Sound &sound = *_vm->_sound; Windows &windows = *_vm->_windows; bool upDoorText = _upDoorText; bool reloadMap = false; int index = 0; _upDoorText = false; combat._combatMode = COMBATMODE_2; _vm->_mode = MODE_COMBAT; // Set the combat buttons IconsMode oldMode = _iconsMode; setMainButtons(ICONS_COMBAT); mainIconsPrint(); combat._combatParty.clear(); combat.clearBlocked(); combat._pow[0]._duration = 0; combat._pow[1]._duration = 0; combat._pow[2]._duration = 0; combat._monstersAttacking = false; combat._partyRan = false; // Set up the combat party combat.setupCombatParty(); combat.setSpeedTable(); // Initialize arrays for character/monster states Common::fill(&combat._charsGone[0], &combat._charsGone[PARTY_AND_MONSTERS], 0); Common::fill(&combat._charsBlocked[0], &combat._charsBlocked[PARTY_AND_MONSTERS], false); combat._whosSpeed = -1; combat._whosTurn = -1; resetHighlight(); nextChar(); if (!party._dead) { combat.setSpeedTable(); if (_tillMove) { combat.moveMonsters(); draw3d(true); } Window &w = windows[2]; w.open(); bool breakFlag = false; while (!_vm->shouldExit() && !breakFlag && !party._dead && _vm->_mode == MODE_COMBAT) { // FIXME: I've had a rare issue where the loop starts with a non-party _whosTurn. Unfortunately, // I haven't been able to consistently replicate and diagnose the problem, so for now, // I'm simply detecting if it happens and resetting the combat round if (combat._whosTurn >= (int)party._activeParty.size()) goto new_round; highlightChar(combat._whosTurn); combat.setSpeedTable(); // Write out the description of the monsters being battled w.writeString(combat.getMonsterDescriptions()); _combatIcons.draw(0, 32, Common::Point(233, combat._attackDurationCtr * 10 + 27), SPRFLAG_800, 0); w.update(); // Wait for keypress index = 0; do { events.updateGameCounter(); draw3d(true); if (++index == 5 && combat._attackMonsters[0] != -1) { MazeMonster &monster = map._mobData._monsters[combat._monster2Attack]; MonsterStruct &monsterData = *monster._monsterData; sound.playFX(monsterData._fx); } do { events.pollEventsAndWait(); checkEvents(_vm); } while (!_vm->shouldExit() && events.timeElapsed() < 1 && !_buttonValue); } while (!_vm->shouldExit() && !_buttonValue); if (_vm->shouldExit()) goto exit; switch (_buttonValue) { case Common::KEYCODE_TAB: // Show the control panel if (ControlPanel::show(_vm) == 2) { reloadMap = true; breakFlag = true; } else { highlightChar(combat._whosTurn); } break; case Common::KEYCODE_1: case Common::KEYCODE_2: case Common::KEYCODE_3: _buttonValue -= Common::KEYCODE_1; if (combat._attackMonsters[_buttonValue] != -1) { combat._monster2Attack = combat._attackMonsters[_buttonValue]; combat._attackDurationCtr = _buttonValue; } break; case Common::KEYCODE_a: // Attack combat.attack(*combat._combatParty[combat._whosTurn], RT_SINGLE); nextChar(); break; case Common::KEYCODE_b: // Block combat.block(); nextChar(); break; case Common::KEYCODE_c: { // Cast spell if (CastSpell::show(_vm) != -1) { nextChar(); } else { highlightChar(combat._whosTurn); } break; } case Common::KEYCODE_f: // Quick Fight combat.quickFight(); nextChar(); break; case Common::KEYCODE_i: // Info dialog InfoDialog::show(_vm); highlightChar(combat._whosTurn); break; case Common::KEYCODE_o: // Quick Fight Options QuickFight::show(_vm, combat._combatParty[combat._whosTurn]); highlightChar(combat._whosTurn); break; case Common::KEYCODE_q: // Quick Reference dialog QuickReferenceDialog::show(_vm); highlightChar(combat._whosTurn); break; case Common::KEYCODE_r: // Run from combat combat.run(); nextChar(); if (_vm->_mode == MODE_INTERACTIVE) { party._treasure._gems = 0; party._treasure._gold = 0; party._treasure._hasItems = false; party.moveToRunLocation(); breakFlag = true; } break; case Common::KEYCODE_u: { int whosTurn = combat._whosTurn; ItemsDialog::show(_vm, combat._combatParty[combat._whosTurn], ITEMMODE_COMBAT); if (combat._whosTurn == whosTurn) { highlightChar(combat._whosTurn); } else { combat._whosTurn = whosTurn; nextChar(); } break; } case Common::KEYCODE_F1: case Common::KEYCODE_F2: case Common::KEYCODE_F3: case Common::KEYCODE_F4: case Common::KEYCODE_F5: case Common::KEYCODE_F6: // Show character info _buttonValue -= Common::KEYCODE_F1; if (_buttonValue < (int)combat._combatParty.size()) { CharacterInfo::show(_vm, _buttonValue); } highlightChar(combat._whosTurn); break; case Common::KEYCODE_LEFT: case Common::KEYCODE_RIGHT: // Rotate party direction left or right if (_buttonValue == Common::KEYCODE_LEFT) { party._mazeDirection = (party._mazeDirection == DIR_NORTH) ? DIR_WEST : (Direction)((int)party._mazeDirection - 1); } else { party._mazeDirection = (party._mazeDirection == DIR_WEST) ? DIR_NORTH : (Direction)((int)party._mazeDirection + 1); } _flipSky ^= 1; if (_tillMove) combat.moveMonsters(); party._stepped = true; break; } // Handling for if the combat turn is complete if (combat.allHaveGone()) { new_round: Common::fill(&combat._charsGone[0], &combat._charsGone[PARTY_AND_MONSTERS], false); combat.clearBlocked(); combat.setSpeedTable(); combat._whosTurn = -1; combat._whosSpeed = -1; nextChar(); for (uint idx = 0; idx < map._mobData._monsters.size(); ++idx) { MazeMonster &monster = map._mobData._monsters[idx]; if (monster._spriteId == 53) { // For Medusa sprites, their HP keeps getting reset MonsterStruct &monsData = map._monsterData[53]; monster._hp = monsData._hp; } } combat.moveMonsters(); setIndoorsMonsters(); party.changeTime(1); } if (combat._attackMonsters[0] == -1 && combat._attackMonsters[1] == -1 && combat._attackMonsters[2] == -1) { party.changeTime(1); draw3d(true); if (combat._attackMonsters[0] == -1 && combat._attackMonsters[1] == -1 && combat._attackMonsters[2] == -1) break; } party.checkPartyDead(); } _vm->_mode = MODE_INTERACTIVE; if (combat._partyRan && (combat._attackMonsters[0] != -1 || combat._attackMonsters[1] != -1 || combat._attackMonsters[2] != -1)) { party.checkPartyDead(); if (!party._dead) { party.moveToRunLocation(); for (uint idx = 0; idx < combat._combatParty.size(); ++idx) { Character &c = *combat._combatParty[idx]; if (c.isDisabled()) c._conditions[DEAD] = 1; } } } exit: w.close(); events.clearEvents(); _vm->_mode = MODE_COMBAT; draw3d(true); party.giveTreasure(); _vm->_mode = MODE_INTERACTIVE; party._stepped = true; unhighlightChar(); combat.setupCombatParty(); drawParty(true); } // Restore old icons setMainButtons(oldMode); mainIconsPrint(); combat._monster2Attack = -1; if (!g_vm->isLoadPending()) { if (upDoorText) { map.cellFlagLookup(party._mazePosition); if (map._currentIsEvent) scripts.checkEvents(); } if (reloadMap) { sound.playFX(51); map._loadCcNum = _vm->getGameID() != GType_WorldOfXeen ? 1 : 0; map.load(_vm->getGameID() == GType_WorldOfXeen ? 28 : 29); party._mazeDirection = _vm->getGameID() == GType_WorldOfXeen ? DIR_EAST : DIR_SOUTH; } } combat._combatMode = COMBATMODE_INTERACTIVE; } void Interface::nextChar() { Combat &combat = *_vm->_combat; Party &party = *_vm->_party; if (combat.allHaveGone()) return; if ((combat._attackMonsters[0] == -1 && combat._attackMonsters[1] == -1 && combat._attackMonsters[2] == -1) || combat._combatParty.size() == 0) { _vm->_mode = MODE_INTERACTIVE; return; } // Loop for potentially multiple monsters attacking until it's time // for one of the party's turn for (;;) { // Check if party is dead party.checkPartyDead(); if (party._dead) { _vm->_mode = MODE_INTERACTIVE; break; } int idx; for (idx = 0; idx < (int)combat._speedTable.size(); ++idx) { if (combat._whosTurn != -1) { combat._charsGone[combat._whosTurn] = true; } combat._whosSpeed = (combat._whosSpeed + 1) % combat._speedTable.size(); combat._whosTurn = combat._speedTable[combat._whosSpeed]; if (combat.allHaveGone()) { idx = -1; break; } if (combat._whosTurn < (int)combat._combatParty.size()) { // If it's a party member, only allow them to become active if // they're still conscious if (combat._combatParty[combat._whosTurn]->isDisabledOrDead()) continue; } break; } if (idx == -1) { if (!combat.charsCantAct()) return; combat.setSpeedTable(); combat._whosTurn = -1; combat._whosSpeed = -1; Common::fill(&combat._charsGone[0], &combat._charsGone[PARTY_AND_MONSTERS], false); continue; } if (combat._whosTurn < (int)combat._combatParty.size()) { // It's a party character's turn now, so highlight the character if (!combat.allHaveGone()) { highlightChar(combat._whosTurn); } break; } else { // It's a monster's turn to attack combat.doMonsterTurn(0); if (!party._dead) { party.checkPartyDead(); if (party._dead) break; } } } } void Interface::spellFX(Character *c) { Combat &combat = *_vm->_combat; EventsManager &events = *_vm->_events; Party &party = *_vm->_party; Sound &sound = *_vm->_sound; Windows &windows = *_vm->_windows; // Ensure there's no alraedy running effect for the given character uint charIndex; for (charIndex = 0; charIndex < party._activeParty.size(); ++charIndex) { if (&party._activeParty[charIndex] == c) break; } if (charIndex == party._activeParty.size() || _charFX[charIndex]) return; if (windows[12]._enabled) windows[12].close(); if (combat._combatMode == COMBATMODE_2) { for (uint idx = 0; idx < combat._combatParty.size(); ++idx) { if (combat._combatParty[idx]->_rosterId == c->_rosterId) { charIndex = idx; break; } } } int tillMove = _tillMove; _tillMove = 0; sound.playFX(20); for (int frameNum = 0; frameNum < 4; ++frameNum) { events.updateGameCounter(); _spellFxSprites.draw(0, frameNum, Common::Point( Res.CHAR_FACES_X[charIndex], 150)); if (!windows[SCENE_WINDOW]._enabled) draw3d(false); windows[0].update(); events.wait(windows[SCENE_WINDOW]._enabled ? 2 : 1,false); } drawParty(true); _tillMove = tillMove; ++_charFX[charIndex]; } void Interface::obscureScene(Obscurity obscurity) { Screen &screen = *g_vm->_screen; const byte *lookup; switch (obscurity) { case OBSCURITY_BLACK: // Totally dark (black) background screen.fillRect(Common::Rect(8, 8, 224, 140), 0); break; case OBSCURITY_1: case OBSCURITY_2: case OBSCURITY_3: lookup = &Res.DARKNESS_XLAT[obscurity - 1][0]; for (int yp = 8; yp < 140; ++yp) { byte *destP = (byte *)screen.getBasePtr(8, yp); for (int xp = 8; xp < 224; ++xp, ++destP) *destP = lookup[*destP]; } break; default: // Full daylight, so no obscurity break; } } } // End of namespace Xeen