scummvm/engines/xeen/interface.cpp
2018-07-26 18:54:17 -07:00

1912 lines
47 KiB
C++

/* 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;
party._damageType = DT_FIRE;
break;
case SURFTYPE_SKY:
// We can fly, we can.. oh wait, we can't!
damage = 100;
party._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) {
party._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;
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