diff options
-rw-r--r-- | engines/xeen/interface.cpp | 180 | ||||
-rw-r--r-- | engines/xeen/interface.h | 10 | ||||
-rw-r--r-- | engines/xeen/interface_map.cpp | 32 | ||||
-rw-r--r-- | engines/xeen/interface_map.h | 2 | ||||
-rw-r--r-- | engines/xeen/map.cpp | 47 | ||||
-rw-r--r-- | engines/xeen/map.h | 3 | ||||
-rw-r--r-- | engines/xeen/party.cpp | 205 | ||||
-rw-r--r-- | engines/xeen/party.h | 18 | ||||
-rw-r--r-- | engines/xeen/resources.cpp | 52 | ||||
-rw-r--r-- | engines/xeen/resources.h | 4 | ||||
-rw-r--r-- | engines/xeen/scripts.cpp | 17 | ||||
-rw-r--r-- | engines/xeen/scripts.h | 15 | ||||
-rw-r--r-- | engines/xeen/sprites.cpp | 1 | ||||
-rw-r--r-- | engines/xeen/xeen.cpp | 33 | ||||
-rw-r--r-- | engines/xeen/xeen.h | 6 |
15 files changed, 499 insertions, 126 deletions
diff --git a/engines/xeen/interface.cpp b/engines/xeen/interface.cpp index 50f3ede109..e15c0574aa 100644 --- a/engines/xeen/interface.cpp +++ b/engines/xeen/interface.cpp @@ -39,7 +39,6 @@ Interface::Interface(XeenEngine *vm) : ButtonContainer(), InterfaceMap(vm), _vm( _holyBonusUIFrame = 0; _heroismUIFrame = 0; _flipUIFrame = 0; - _newDay = false; _buttonsLoaded = false; _hiliteChar = -1; _intrIndex1 = 0; @@ -49,6 +48,7 @@ Interface::Interface(XeenEngine *vm) : ButtonContainer(), InterfaceMap(vm), _vm( _tillMove = 0; _thinWall = false; _overallFrame = 0; + _upDoorText = false; Common::fill(&_combatCharIds[0], &_combatCharIds[8], 0); initDrawStructs(); @@ -91,12 +91,12 @@ void Interface::setup() { _charPowSprites.load("charpow.icn"); // Get mappings to the active characters in the party - _vm->_party._activeParty.resize(_vm->_party._partyCount); - for (int i = 0; i < _vm->_party._partyCount; ++i) { - _vm->_party._activeParty[i] = _vm->_roster[_vm->_party._partyMembers[i]]; + _vm->_party->_activeParty.resize(_vm->_party->_partyCount); + for (int i = 0; i < _vm->_party->_partyCount; ++i) { + _vm->_party->_activeParty[i] = _vm->_roster[_vm->_party->_partyMembers[i]]; } - _newDay = _vm->_party._minutes >= 300; + _vm->_party->_newDay = _vm->_party->_minutes >= 300; } void Interface::manageCharacters(bool soundPlayed) { @@ -105,7 +105,7 @@ void Interface::manageCharacters(bool soundPlayed) { bool flag = false; start: - if (_vm->_party._mazeId != 0) { + if (_vm->_party->_mazeId != 0) { _vm->_mode = MODE_0; _buttonsLoaded = true; } else { @@ -117,8 +117,8 @@ start: // Xeen only uses 24 of possible 30 character slots loadCharIcons(); - for (int i = 0; i < _vm->_party._partyCount; ++i) - _partyFaces[i] = &_charFaces[_vm->_party._partyMembers[i]]; + for (int i = 0; i < _vm->_party->_partyCount; ++i) + _partyFaces[i] = &_charFaces[_vm->_party->_partyMembers[i]]; } _vm->_mode = MODE_1; @@ -193,7 +193,7 @@ start: case Common::KEYCODE_SPACE: case Common::KEYCODE_e: case Common::KEYCODE_x: - if (_vm->_party._partyCount == 0) { + if (_vm->_party->_partyCount == 0) { ErrorScroll::show(_vm, NO_ONE_TO_ADVENTURE_WITH); } else { if (_vm->_mode != MODE_0) { @@ -208,10 +208,10 @@ start: } w.close(); - _vm->_party._realPartyCount = _vm->_party._partyCount; - _vm->_party._mazeId = _vm->_party._priorMazeId; + _vm->_party->_realPartyCount = _vm->_party->_partyCount; + _vm->_party->_mazeId = _vm->_party->_priorMazeId; - _vm->_party.copyPartyToRoster(_vm->_roster); + _vm->_party->copyPartyToRoster(_vm->_roster); _vm->_saves->writeCharFile(); breakFlag = true; break; @@ -242,7 +242,7 @@ start: case Common::KEYCODE_d: break; case Common::KEYCODE_r: - if (_vm->_party._partyCount > 0) { + if (_vm->_party->_partyCount > 0) { // TODO } break; @@ -299,8 +299,8 @@ void Interface::loadCharIcons() { } void Interface::loadPartyIcons() { - for (int i = 0; i < _vm->_party._partyCount; ++i) - _partyFaces[i] = &_charFaces[_vm->_party._partyMembers[i]]; + for (int i = 0; i < _vm->_party->_partyCount; ++i) + _partyFaces[i] = &_charFaces[_vm->_party->_partyMembers[i]]; } void Interface::setupBackground() { @@ -315,19 +315,19 @@ void Interface::assembleBorder() { _globalSprites.draw(screen._windows[0], 0, Common::Point(8, 8)); // Draw the animating bat character used to show when levitate is active - _borderSprites.draw(screen._windows[0], _vm->_party._levitateActive ? _batUIFrame + 16 : 16, + _borderSprites.draw(screen._windows[0], _vm->_party->_levitateActive ? _batUIFrame + 16 : 16, Common::Point(0, 82)); _batUIFrame = (_batUIFrame + 1) % 12; // Draw UI element to indicate whether can spot hidden doors _borderSprites.draw(screen, - (_thinWall && _vm->_party.checkSkill(SPOT_DOORS)) ? _spotDoorsUIFrame + 28 : 28, + (_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(screen, - (_vm->_dangerSenseAllowed && _vm->_party.checkSkill(DANGER_SENSE)) ? _spotDoorsUIFrame + 40 : 40, + (_vm->_dangerSenseAllowed && _vm->_party->checkSkill(DANGER_SENSE)) ? _spotDoorsUIFrame + 40 : 40, Common::Point(107, 9)); _dangerSenseUIFrame = (_dangerSenseUIFrame + 1) % 12; @@ -344,7 +344,7 @@ void Interface::assembleBorder() { else if (_vm->_face2State == 2) _face2UIFrame = 0; - if (!_vm->_party._clairvoyanceActive) { + if (!_vm->_party->_clairvoyanceActive) { _face1UIFrame = 0; _face2UIFrame = 8; } @@ -358,54 +358,54 @@ void Interface::assembleBorder() { // Draw resistence indicators if (!screen._windows[10]._enabled && !screen._windows[2]._enabled && screen._windows[38]._enabled) { - _fecpSprites.draw(screen, _vm->_party._fireResistence ? 1 : 0, + _fecpSprites.draw(screen, _vm->_party->_fireResistence ? 1 : 0, Common::Point(2, 2)); - _fecpSprites.draw(screen, _vm->_party._electricityResistence ? 3 : 2, + _fecpSprites.draw(screen, _vm->_party->_electricityResistence ? 3 : 2, Common::Point(219, 2)); - _fecpSprites.draw(screen, _vm->_party._coldResistence ? 5 : 4, + _fecpSprites.draw(screen, _vm->_party->_coldResistence ? 5 : 4, Common::Point(2, 134)); - _fecpSprites.draw(screen, _vm->_party._poisonResistence ? 7 : 6, + _fecpSprites.draw(screen, _vm->_party->_poisonResistence ? 7 : 6, Common::Point(219, 134)); } else { - _fecpSprites.draw(screen, _vm->_party._fireResistence ? 9 : 8, + _fecpSprites.draw(screen, _vm->_party->_fireResistence ? 9 : 8, Common::Point(8, 8)); - _fecpSprites.draw(screen, _vm->_party._electricityResistence ? 10 : 11, + _fecpSprites.draw(screen, _vm->_party->_electricityResistence ? 10 : 11, Common::Point(219, 8)); - _fecpSprites.draw(screen, _vm->_party._coldResistence ? 12 : 13, + _fecpSprites.draw(screen, _vm->_party->_coldResistence ? 12 : 13, Common::Point(8, 134)); - _fecpSprites.draw(screen, _vm->_party._poisonResistence ? 14 : 15, + _fecpSprites.draw(screen, _vm->_party->_poisonResistence ? 14 : 15, Common::Point(219, 134)); } // Draw UI element for blessed _blessSprites.draw(screen, 16, Common::Point(33, 137)); - if (_vm->_party._blessedActive) { + if (_vm->_party->_blessedActive) { _blessedUIFrame = (_blessedUIFrame + 1) % 4; _blessSprites.draw(screen, _blessedUIFrame, Common::Point(33, 137)); } // Draw UI element for power shield - if (_vm->_party._powerShieldActive) { + if (_vm->_party->_powerShieldActive) { _powerShieldUIFrame = (_powerShieldUIFrame + 1) % 4; _blessSprites.draw(screen, _powerShieldUIFrame + 4, Common::Point(55, 137)); } // Draw UI element for holy bonus - if (_vm->_party._holyBonusActive) { + if (_vm->_party->_holyBonusActive) { _holyBonusUIFrame = (_holyBonusUIFrame + 1) % 4; _blessSprites.draw(screen, _holyBonusUIFrame + 8, Common::Point(160, 137)); } // Draw UI element for heroism - if (_vm->_party._heroismActive) { + if (_vm->_party->_heroismActive) { _heroismUIFrame = (_heroismUIFrame + 1) % 4; _blessSprites.draw(screen, _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 = DIRECTION_TEXT[_vm->_party._mazeDirection]; + if (_vm->_party->checkSkill(DIRECTION_SENSE) && !_vm->_noDirectionSense) { + const char *dirText = DIRECTION_TEXT[_vm->_party->_mazeDirection]; Common::String msg = Common::String::format( "\002""08\003""c\013""139\011""116%c\014""d\001", *dirText); screen._windows[0].writeString(msg); @@ -426,7 +426,7 @@ void Interface::setupFaces(int charIndex, Common::Array<int> xeenSideChars, bool for (posIndex = 0; posIndex < 4; ++posIndex) { charId = xeenSideChars[charIndex]; - bool isInParty = _vm->_party.isInParty(charId); + bool isInParty = _vm->_party->isInParty(charId); if (charId == 0xff) { while ((int)_buttons.size() > (7 + posIndex)) @@ -469,10 +469,10 @@ void Interface::charIconsPrint(bool updateFlag) { _restoreSprites.draw(screen, 0, Common::Point(8, 149)); // Handle drawing the party faces - for (int idx = 0; idx < (stateFlag ? _vm->_party._combatPartyCount : - _vm->_party._partyCount); ++idx) { + for (int idx = 0; idx < (stateFlag ? _vm->_party->_combatPartyCount : + _vm->_party->_partyCount); ++idx) { int charIndex = stateFlag ? _combatCharIds[idx] : idx; - PlayerStruct &ps = _vm->_party._activeParty[charIndex]; + PlayerStruct &ps = _vm->_party->_activeParty[charIndex]; Condition charCondition = ps.worstCondition(); int charFrame = FACE_CONDITION_FRAMES[charCondition]; @@ -485,10 +485,10 @@ void Interface::charIconsPrint(bool updateFlag) { } if (!_hpSprites.empty()) { - for (int idx = 0; idx < (stateFlag ? _vm->_party._combatPartyCount : - _vm->_party._partyCount); ++idx) { + for (int idx = 0; idx < (stateFlag ? _vm->_party->_combatPartyCount : + _vm->_party->_partyCount); ++idx) { int charIndex = stateFlag ? _combatCharIds[idx] : idx; - PlayerStruct &ps = _vm->_party._activeParty[charIndex]; + PlayerStruct &ps = _vm->_party->_activeParty[charIndex]; // Draw the Hp bar int maxHp = ps.getMaxHp(); @@ -555,7 +555,7 @@ void Interface::draw3d(bool updateFlag) { } MazeObject &objObject = map._mobData._objects[_objNumber]; - Direction partyDirection = _vm->_party._mazeDirection; + Direction partyDirection = _vm->_party->_mazeDirection; int objNum = _objNumber - 1; // Loop to update the frame numbers for each maze object, applying the animation frame @@ -2312,4 +2312,102 @@ void Interface::updateAutoMap() { // TODO } +/** + * Waits for a keypress or click, whilst still allowing the game scene to + * be animated. + */ +void Interface::wait() { + EventsManager &events = *_vm->_events; + Map &map = *_vm->_map; + Party &party = *_vm->_party; + Scripts &scripts = *_vm->_scripts; + const Common::Rect waitBounds(8, 8, 224, 140); + + while (!_vm->shouldQuit()) { + events.updateGameCounter(); + draw3d(true); + + // Wait for a frame + while (!_vm->shouldQuit()) { + events.pollEventsAndWait(); + checkEvents(_vm); + } while (!_buttonValue && events.timeElapsed() < 1 && !_vm->_party->_partyDead); + + if (!_buttonValue && !_vm->_party->_partyDead) + continue; + + if (_buttonValue == Common::KEYCODE_SPACE || + (events._leftButton && waitBounds.contains(events._mousePos))) { + int lookupId = map.mazeLookup(party._mazePosition, + WALL_NUMBERS[party._mazeDirection][2]); + + bool eventsFlag = true; + switch (lookupId) { + case 1: + if (!map._isOutdoors) { + scripts.openGrate(13, 1); + eventsFlag = _buttonValue != 0; + } + + case 6: + if (!map._isOutdoors) { + scripts.openGrate(9, 0); + eventsFlag = _buttonValue != 0; + } + break; + case 9: + if (!map._isOutdoors) { + scripts.openGrate(6, 0); + eventsFlag = _buttonValue != 0; + } + break; + case 13: + if (!map._isOutdoors) { + scripts.openGrate(1, 1); + eventsFlag = _buttonValue != 0; + } + break; + default: + break; + } + if (eventsFlag) { + scripts.checkEvents(); + if (_vm->shouldQuit()) + return; + } + } + + switch (_buttonValue) { + case Common::KEYCODE_TAB: + // Stop mosters doing any movement + _vm->_moveMonsters = false; + warning("TODO: showControlPanel"); + break; + + case Common::KEYCODE_SPACE: + case Common::KEYCODE_w: + // Wait one turn + chargeStep(); + moveMonsters(); + _upDoorText = false; + _flipDefaultGround = !_flipDefaultGround; + _flipGround = !_flipGround; + break; + default: + break; + } + } +} + +void Interface::chargeStep() { + if (_vm->_party->_partyDead) { + _vm->_party->changeTime(_vm->_map->_isOutdoors ? 10 : 1); + if (!_tillMove) { + moveMonsters(); + } + + _tillMove = 3; + } +} + } // End of namespace Xeen diff --git a/engines/xeen/interface.h b/engines/xeen/interface.h index df06f5d86b..c6fee9e0d4 100644 --- a/engines/xeen/interface.h +++ b/engines/xeen/interface.h @@ -63,7 +63,6 @@ private: int _holyBonusUIFrame; int _heroismUIFrame; int _flipUIFrame; - bool _newDay; bool _buttonsLoaded; Common::String _interfaceText; int _hiliteChar; @@ -74,6 +73,7 @@ private: byte _tillMove; bool _thinWall; int _overallFrame; + bool _upDoorText; void initDrawStructs(); @@ -85,8 +85,6 @@ private: void setupFaces(int charIndex, Common::Array<int> xeenSideChars, bool updateFlag); - void charIconsPrint(bool updateFlag); - void drawViewBackground(int bgType); void moveCharacterToRoster(); @@ -100,6 +98,8 @@ private: void setMazeBits(); void updateAutoMap(); + + void chargeStep(); public: Interface(XeenEngine *vm); @@ -116,6 +116,10 @@ public: void startup(); void mainIconsPrint(); + + void charIconsPrint(bool updateFlag); + + void wait(); }; } // End of namespace Xeen diff --git a/engines/xeen/interface_map.cpp b/engines/xeen/interface_map.cpp index 93c3d9a679..1f927112b8 100644 --- a/engines/xeen/interface_map.cpp +++ b/engines/xeen/interface_map.cpp @@ -165,7 +165,7 @@ OutdoorDrawList::OutdoorDrawList() : _skySprite(_data[1]), _groundSprite(_data[2 /*------------------------------------------------------------------------*/ IndoorDrawList::IndoorDrawList() : - _sky(_data[1]), _ground(_data[2]), _horizon(_data[28]), + _sky1(_data[0]), _sky2(_data[1]), _ground(_data[2]), _horizon(_data[28]), _swl_0F1R(_data[146]), _swl_0F1L(_data[144]), _swl_1F1R(_data[134]), _swl_1F1L(_data[133]), _swl_2F2R(_data[110]), _swl_2F1R(_data[109]), _swl_2F1L(_data[108]), _swl_2F2L(_data[107]), _swl_3F1R(_data[ 78]), @@ -378,8 +378,8 @@ InterfaceMap::InterfaceMap(XeenEngine *vm): _vm(vm) { void InterfaceMap::setIndoorsMonsters() { Combat &combat = *_vm->_combat; Map &map = *_vm->_map; - Common::Point mazePos = _vm->_party._mazePosition; - Direction dir = _vm->_party._mazeDirection; + Common::Point mazePos = _vm->_party->_mazePosition; + Direction dir = _vm->_party->_mazeDirection; const int INDOOR_MONSTERS_Y[4] = { 2, 34, 53, 59 }; combat.clear(); @@ -645,8 +645,8 @@ void InterfaceMap::setMonsterSprite(DrawStruct &drawStruct, MazeMonster &monster } void InterfaceMap::setIndoorsObjects() { - Common::Point mazePos = _vm->_party._mazePosition; - Direction dir = _vm->_party._mazeDirection; + Common::Point mazePos = _vm->_party->_mazePosition; + Direction dir = _vm->_party->_mazeDirection; Common::Point pt; _objNumber = 0; @@ -871,8 +871,8 @@ void InterfaceMap::setIndoorsObjects() { void InterfaceMap::setIndoorsWallPics() { Map &map = *_vm->_map; - const Common::Point &mazePos = _vm->_party._mazePosition; - Direction dir = _vm->_party._mazeDirection; + const Common::Point &mazePos = _vm->_party->_mazePosition; + Direction dir = _vm->_party->_mazeDirection; Common::fill(&_wp[0], &_wp[20], -1); @@ -1912,7 +1912,7 @@ void InterfaceMap::drawIndoors() { _indoorList._swl_0F1R._frame = 25; } - map.cellFlagLookup(_vm->_party._mazePosition); + map.cellFlagLookup(_vm->_party->_mazePosition); // WORKAROUND: Original did an array lookup on _skySprites. // Was this a feature for multiple skys that was abandoned? @@ -1922,19 +1922,19 @@ void InterfaceMap::drawIndoors() { if (_vm->_openDoor) { Common::Point pt( - _vm->_party._mazePosition.x + SCREEN_POSITIONING_X[ - _vm->_party._mazeDirection][_vm->_party._mazePosition.x], - _vm->_party._mazePosition.y + SCREEN_POSITIONING_Y[ - _vm->_party._mazeDirection][_vm->_party._mazePosition.y] + _vm->_party->_mazePosition.x + SCREEN_POSITIONING_X[ + _vm->_party->_mazeDirection][_vm->_party->_mazePosition.x], + _vm->_party->_mazePosition.y + SCREEN_POSITIONING_Y[ + _vm->_party->_mazeDirection][_vm->_party->_mazePosition.y] ); map.cellFlagLookup(pt); - _indoorList._sky._sprites = &map._skySprites; + _indoorList._sky2._sprites = &map._skySprites; } else { - _indoorList._sky._sprites = _indoorList[0]._sprites; + _indoorList._sky2._sprites = _indoorList[0]._sprites; } - _indoorList._sky._flags = _flipSky ? SPRFLAG_HORIZ_FLIPPED : 0; + _indoorList._sky2._flags = _flipSky ? SPRFLAG_HORIZ_FLIPPED : 0; _indoorList._ground._flags = _flipDefaultGround ? SPRFLAG_HORIZ_FLIPPED : 0; _indoorList._horizon._frame = 7; @@ -1943,7 +1943,7 @@ void InterfaceMap::drawIndoors() { // Check for any character shooting _isShooting = false; - for (int i = 0; i < _vm->_party._partyCount; ++i) { + for (int i = 0; i < _vm->_party->_partyCount; ++i) { if (_vm->_combat->_shooting[i]) _isShooting = true; } diff --git a/engines/xeen/interface_map.h b/engines/xeen/interface_map.h index fd807a03d2..be17ef0024 100644 --- a/engines/xeen/interface_map.h +++ b/engines/xeen/interface_map.h @@ -50,7 +50,7 @@ public: class IndoorDrawList { public: DrawStruct _data[170]; - DrawStruct &_sky; + DrawStruct &_sky1, &_sky2; DrawStruct &_ground; DrawStruct &_horizon; DrawStruct * const _groundTiles; diff --git a/engines/xeen/map.cpp b/engines/xeen/map.cpp index 99fe5ada0b..bef0511c81 100644 --- a/engines/xeen/map.cpp +++ b/engines/xeen/map.cpp @@ -887,7 +887,7 @@ void Map::load(int mapId) { } _stepped = true; - _vm->_party._mazeId = mapId; + _vm->_party->_mazeId = mapId; _vm->_events->clearEvents(); _sideObjects = 1; @@ -970,7 +970,7 @@ void Map::load(int mapId) { if (isDarkCc && mapId == 50) mazeDataP->setAllTilesStepped(); - if (!isDarkCc && _vm->_party._gameFlags[25] && + if (!isDarkCc && _vm->_party->_gameFlags[25] && (mapId == 42 || mapId == 43 || mapId == 4)) { mazeDataP->clearCellSurfaces(); } @@ -1004,14 +1004,14 @@ void Map::load(int mapId) { _headData.synchronize(headFile); headFile.close(); - if (!isDarkCc && _vm->_party._mazeId) + if (!isDarkCc && _vm->_party->_mazeId) _mobData._monsters.clear(); if (!isDarkCc && mapId == 15) { if ((_mobData._monsters[0]._position.x > 31 || _mobData._monsters[0]._position.y > 31) && (_mobData._monsters[1]._position.x > 31 || _mobData._monsters[1]._position.y > 31) && (_mobData._monsters[2]._position.x > 31 || _mobData._monsters[2]._position.y > 31)) { - _vm->_party._gameFlags[56] = true; + _vm->_party->_gameFlags[56] = true; } } } @@ -1029,7 +1029,7 @@ void Map::load(int mapId) { // TODO: Switch setting flags that don't seem to ever be used // Reload the monster data for the main maze that we're loading - mapId = _vm->_party._mazeId; + mapId = _vm->_party->_mazeId; Common::String filename = Common::String::format("maze%c%03d.mob", (mapId >= 100) ? 'x' : '0', mapId); File mobFile(filename, *_vm->_saves); @@ -1039,10 +1039,10 @@ void Map::load(int mapId) { // Load sprites for the objects for (uint i = 0; i < _mobData._objectSprites.size(); ++i) { - if (_vm->_party._cloudsEnd && _mobData._objectSprites[i]._spriteId == 85 && + if (_vm->_party->_cloudsEnd && _mobData._objectSprites[i]._spriteId == 85 && mapId == 27 && isDarkCc) { // TODO: Flags set that don't seem to be used - } else if (mapId == 12 && _vm->_party._gameFlags[43] && + } else if (mapId == 12 && _vm->_party->_gameFlags[43] && _mobData._objectSprites[i]._spriteId == 118 && !isDarkCc) { filename = "085.obj"; _mobData._objectSprites[0]._spriteId = 85; @@ -1081,7 +1081,6 @@ void Map::load(int mapId) { if (_isOutdoors) { warning("TODO"); // Sound loading - _skySprites.load(isDarkCc ? "night.sky" : "sky.sky"); _groundSprites.load("water.out"); _tileSprites.load("outdoor.til"); outdoorList._skySprite._sprites = &_skySprites; @@ -1102,7 +1101,6 @@ void Map::load(int mapId) { } else { warning("TODO"); // Sound loading - _skySprites.load(isDarkCc ? "night.sky" : "sky.sky"); _mazeSkySprites.load(Common::String::format("%s.sky", TERRAIN_TYPES[_mazeData[0]._wallKind])); _groundSprites.load(Common::String::format("%s.gnd", @@ -1193,18 +1191,20 @@ void Map::load(int mapId) { indoorList._horizon._sprites = nullptr; } } + + loadSky(); } -int Map::mazeLookup(const Common::Point &pt, int directionLayerIndex) { +int Map::mazeLookup(const Common::Point &pt, int layerShift) { Common::Point pos = pt; - int mapId = _vm->_party._mazeId; + int mapId = _vm->_party->_mazeId; if (pt.x < -16 || pt.y < -16 || pt.x >= 32 || pt.y >= 32) error("Invalid coordinate"); // Find the correct maze data out of the set to use _mazeDataIndex = 0; - while (_mazeData[_mazeDataIndex]._mazeId != _vm->_party._mazeId) + while (_mazeData[_mazeDataIndex]._mazeId != _vm->_party->_mazeId) ++_mazeDataIndex; // Handle map changing to the north or south as necessary @@ -1259,7 +1259,7 @@ int Map::mazeLookup(const Common::Point &pt, int directionLayerIndex) { _currentSteppedOn = _mazeData[_mazeDataIndex]._steppedOnTiles[pos.y][pos.x]; } - return (_mazeData[_mazeDataIndex]._wallData[pos.y][pos.x]._data >> (directionLayerIndex * 4)) & 0xF; + return (_mazeData[_mazeDataIndex]._wallData[pos.y][pos.x]._data >> layerShift) & 0xF; } else { _currentSteppedOn = _isOutdoors; @@ -1312,7 +1312,7 @@ void Map::saveMaze() { void Map::cellFlagLookup(const Common::Point &pt) { Common::Point pos = pt; - int mapId = _vm->_party._mazeId; + int mapId = _vm->_party->_mazeId; _mazeDataIndex = 0; while (_mazeData[_mazeDataIndex]._mazeId != mapId) ++_mazeDataIndex; @@ -1364,11 +1364,11 @@ void Map::setCellSurfaceFlags(const Common::Point &pt, int bits) { int Map::getCell(int idx) { - int mapId = _vm->_party._mazeId; - Direction dir = _vm->_party._mazeDirection; + int mapId = _vm->_party->_mazeId; + Direction dir = _vm->_party->_mazeDirection; Common::Point pt( - _vm->_party._mazePosition.x + SCREEN_POSITIONING_X[_vm->_party._mazeDirection][idx], - _vm->_party._mazePosition.y + SCREEN_POSITIONING_Y[_vm->_party._mazeDirection][idx] + _vm->_party->_mazePosition.x + SCREEN_POSITIONING_X[_vm->_party->_mazeDirection][idx], + _vm->_party->_mazePosition.y + SCREEN_POSITIONING_Y[_vm->_party->_mazeDirection][idx] ); if (pt.x > 31 || pt.y > 31) { @@ -1484,10 +1484,19 @@ int Map::getCell(int idx) { _currentSurfaceId = _mazeData[_mazeDataIndex]._cells[pt.y][pt.x]._surfaceId; _currentWall = wallLayers; - return (_currentWall._data >> (WALL_NUMBERS[dir][idx * 2] * 4)) & 0xF; + return (_currentWall._data >> WALL_NUMBERS[dir][idx]) & 0xF; } return _currentWall._data; } +void Map::loadSky() { + Party &party = *_vm->_party; + + party._isNight = party._minutes < (5 * 60) || party._minutes >= (21 * 60); + _skySprites.load(((party._mazeId >= 89 && party._mazeId <= 112) || + party._mazeId == 128 || party._mazeId == 129) || !party._isNight + ? "sky.sky" : "night.sky"); +} + } // End of namespace Xeen diff --git a/engines/xeen/map.h b/engines/xeen/map.h index c97de72a23..ae8ad28efd 100644 --- a/engines/xeen/map.h +++ b/engines/xeen/map.h @@ -392,7 +392,7 @@ public: void load(int mapId); - int mazeLookup(const Common::Point &pt, int directionLayerIndex); + int mazeLookup(const Common::Point &pt, int layerShift); void cellFlagLookup(const Common::Point &pt); @@ -404,6 +404,7 @@ public: MazeData mazeData() { return _mazeData[0]; } + void loadSky(); }; } // End of namespace Xeen diff --git a/engines/xeen/party.cpp b/engines/xeen/party.cpp index ad6d2145de..6d5cc22ffa 100644 --- a/engines/xeen/party.cpp +++ b/engines/xeen/party.cpp @@ -23,9 +23,11 @@ #include "common/scummsys.h" #include "common/algorithm.h" #include "xeen/party.h" +#include "xeen/dialogs_error.h" #include "xeen/files.h" -#include "xeen/xeen.h" +#include "xeen/resources.h" #include "xeen/saves.h" +#include "xeen/xeen.h" namespace Xeen { @@ -155,6 +157,16 @@ int PlayerStruct::getMaxHp() { return 20; } +int PlayerStruct::getStat(int statNum, int v2) { + // TODO + return 10; +} + +bool PlayerStruct::charSavingThrow() { + // TODO + return false; +} + /*------------------------------------------------------------------------*/ void Roster::synchronize(Common::Serializer &s) { @@ -167,7 +179,7 @@ void Roster::synchronize(Common::Serializer &s) { /*------------------------------------------------------------------------*/ -Party::Party() { +Party::Party(XeenEngine *vm): _vm(vm) { _partyCount = 0; _realPartyCount = 0; Common::fill(&_partyMembers[0], &_partyMembers[8], 0); @@ -216,6 +228,9 @@ Party::Party() { Common::fill(&_characterFlags[i][0], &_characterFlags[i][24], false); _combatPartyCount = 0; + _partyDead = false; + _newDay = false; + _isNight = false; } void Party::synchronize(Common::Serializer &s) { @@ -343,4 +358,190 @@ void Party::copyPartyToRoster(Roster &r) { } } +/** + * Adds time to the party's playtime, taking into account the effect of any + * stat modifier changes + */ +void Party::changeTime(int numMinutes) { + bool killed = false; + + if (((_minutes + numMinutes) / 480) != (_minutes / 480)) { + for (int idx = 0; idx < _partyCount; ++idx) { + PlayerStruct &player = _activeParty[idx]; + + if (!player._conditions[DEAD] && !player._conditions[STONED] && + !player._conditions[ERADICATED]) { + for (int statNum = 0; statNum < TOTAL_STATS; ++statNum) { + int statVal = player.getStat(statNum, 0); + if (statVal < 1) + player._conditions[DEAD] = 1; + } + } + + // Handle heart broken condition becoming depression + if (player._conditions[HEART_BROKEN]) { + if (++player._conditions[HEART_BROKEN] > 10) { + player._conditions[HEART_BROKEN] = 0; + player._conditions[DEPRESSED] = 1; + } + } + + // Handle poisoning + if (!player._conditions[POISONED]) { + if (_vm->getRandomNumber(9) != 1 || !player.charSavingThrow()) + player._conditions[POISONED] *= 2; + else + // Poison wears off + player._conditions[POISONED] = 0; + } + + // Handle poisoning + if (!player._conditions[DISEASED]) { + if (_vm->getRandomNumber(9) != 1 || !player.charSavingThrow()) + player._conditions[DISEASED] *= 2; + else + // Disease wears off + player._conditions[DISEASED] = 0; + } + + // Handle insane status + if (player._conditions[INSANE]) + player._conditions[INSANE]++; + + if (player._conditions[DEAD]) { + if (++player._conditions[DEAD] == 0) + player._conditions[DEAD] = -1; + } + + if (player._conditions[STONED]) { + if (++player._conditions[STONED] == 0) + player._conditions[STONED] = -1; + } + + if (player._conditions[ERADICATED]) { + if (++player._conditions[ERADICATED] == 0) + player._conditions[ERADICATED] = -1; + } + + if (player._conditions[IN_LOVE]) { + if (++player._conditions[IN_LOVE] > 10) { + player._conditions[IN_LOVE] = 0; + player._conditions[HEART_BROKEN] = 1; + } + } + + player._conditions[WEAK] = player._conditions[DRUNK]; + player._conditions[DRUNK] = 0; + + if (player._conditions[DEPRESSED]) { + player._conditions[DEPRESSED] = (player._conditions[DEPRESSED] + 1) % 4; + } + } + } + + // Increment the time + addTime(numMinutes); + + for (int idx = 0; idx < _partyCount; ++idx) { + PlayerStruct &player = _activeParty[idx]; + + if (player._conditions[CONFUSED] && _vm->getRandomNumber(2) == 1) { + if (player.charSavingThrow()) { + player._conditions[CONFUSED] = 0; + } else { + player._conditions[CONFUSED]--; + } + } + + if (player._conditions[PARALYZED] && _vm->getRandomNumber(4) == 1) + player._conditions[PARALYZED]--; + } + + if (killed) + _vm->_interface->charIconsPrint(true); + + if (_isNight != (_minutes < (5 * 60) || _minutes >= (21 * 60))) + _vm->_map->loadSky(); +} + +void Party::addTime(int numMinutes) { + int day = _day; + _minutes += numMinutes; + + // If the total minutes has exceeded a day, move to next one + while (_minutes >= (24 * 60)) { + _minutes -= 24 * 60; + if (++_day >= 100) { + _day -= 100; + ++_year; + } + } + + if ((_day % 10) == 1 || numMinutes > (24 * 60)) { + if (_day != day) { + warning("TODO: resetBlacksmith? and giveInterest?"); + } + } + + if (_day != day) + _newDay = true; + + if (_newDay && _minutes >= 300) { + if (_vm->_mode != MODE_9 && _vm->_mode != MODE_17) { + resetTemps(); + if (_rested || _vm->_mode == MODE_5) { + _rested = false; + } else { + for (int idx = 0; idx < _partyCount; ++idx) { + if (_activeParty[idx]._conditions[WEAK] >= 0) + _activeParty[idx]._conditions[WEAK]++; + } + + ErrorScroll::show(_vm, THE_PARTY_NEEDS_REST, WT_NONFREEZED_WAIT); + } + + _vm->_interface->charIconsPrint(true); + } + + _newDay = false; + } +} + +void Party::resetTemps() { + for (int idx = 0; idx < _partyCount; ++idx) { + PlayerStruct &player = _activeParty[idx]; + + player._magicResistence._temporary = 0; + player._energyResistence._temporary = 0; + player._poisonResistence._temporary = 0; + player._electricityResistence._temporary = 0; + player._coldResistence._temporary = 0; + player._fireResistence._temporary = 0; + player._ACTemp = 0; + player._level._temporary = 0; + player._luck._temporary = 0; + player._accuracy._temporary = 0; + player._speed._temporary = 0; + player._endurance._temporary = 0; + player._personality._temporary = 0; + player._intellect._temporary = 0; + player._might._temporary = 0; + } + + _poisonResistence = 0; + _coldResistence = 0; + _electricityResistence = 0; + _fireResistence = 0; + _lightCount = 0; + _levitateActive = false; + _walkOnWaterActive = false; + _wizardEyeActive = false; + _clairvoyanceActive = false; + _heroismActive = false; + _holyBonusActive = false; + _powerShieldActive = false; + _blessedActive = false; +} + + } // End of namespace Xeen diff --git a/engines/xeen/party.h b/engines/xeen/party.h index d8fe2ab79d..75a6c2b07b 100644 --- a/engines/xeen/party.h +++ b/engines/xeen/party.h @@ -66,6 +66,7 @@ enum Condition { CURSED = 0, HEART_BROKEN = 1, WEAK = 2, POISONED = 3, #define TOTAL_CHARACTERS 30 #define XEEN_TOTAL_CHARACTERS 24 #define MAX_ACTIVE_PARTY 6 +#define TOTAL_STATS 7 class XeenEngine; @@ -133,6 +134,10 @@ public: int getAge(int partyYear, bool ignoreTemp); int getMaxHp(); + + int getStat(int statNum, int v2); + + bool charSavingThrow(); }; class Roster: public Common::Array<PlayerStruct> { @@ -143,6 +148,8 @@ public: }; class Party { +private: + XeenEngine *_vm; public: // Dynamic data that's saved int _partyCount; @@ -202,8 +209,11 @@ public: // Other party related runtime data Common::Array<PlayerStruct> _activeParty; int _combatPartyCount; + bool _partyDead; + bool _newDay; + bool _isNight; public: - Party(); + Party(XeenEngine *vm); void synchronize(Common::Serializer &s); @@ -212,6 +222,12 @@ public: bool isInParty(int charId); void copyPartyToRoster(Roster &r); + + void changeTime(int numMinutes); + + void addTime(int numMinutes); + + void resetTemps(); }; } // End of namespace Xeen diff --git a/engines/xeen/resources.cpp b/engines/xeen/resources.cpp index 51f26fe668..085f84aa73 100644 --- a/engines/xeen/resources.cpp +++ b/engines/xeen/resources.cpp @@ -66,6 +66,8 @@ const char *const OPTIONS_TITLE = "\v117Copyright (c) 1993 NWC, Inc.\n" "All Rights Reserved\x01"; +const char *const THE_PARTY_NEEDS_REST = "\x0B""012The Party needs rest!"; + const char *const TERRAIN_TYPES[6] = { "town", "cave", "towr", "cstl", "dung", "scfi" }; @@ -392,43 +394,27 @@ const int DIRECTION_ANIM_POSITIONS[4][4] = { { 0, 1, 2, 3 }, { 3, 0, 1, 2 }, { 2, 3, 0, 1 }, { 1, 2, 3, 0 } }; -const byte WALL_NUMBERS[4][96] = { +const byte WALL_NUMBERS[4][48] = { { - 3, 0, 0, 0, 3, 0, 2, 0, 3, 0, 3, 0, - 0, 0, 3, 0, 2, 0, 3, 0, 3, 0, 0, 0, - 3, 0, 0, 0, 3, 0, 2, 0, 3, 0, 2, 0, - 3, 0, 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, - 0, 0, 3, 0, 0, 0, 3, 0, 2, 0, 3, 0, - 2, 0, 3, 0, 2, 0, 3, 0, 2, 0, 3, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, - 2, 0, 2, 0, 0, 0, 0, 0, 1, 0, 1, 0 + 12, 0, 12, 8, 12, 12, 0, 12, 8, 12, 12, 0, + 12, 0, 12, 8, 12, 8, 12, 12, 0, 12, 0, 12, + 0, 12, 0, 12, 8, 12, 8, 12, 8, 12, 8, 12, + 0, 0, 0, 0, 8, 8, 8, 8, 0, 0, 4, 4 }, { - 2, 0, 3, 0, 2, 0, 1, 0, 2, 0, 2, 0, - 3, 0, 2, 0, 1, 0, 2, 0, 2, 0, 3, 0, - 2, 0, 3, 0, 2, 0, 1, 0, 2, 0, 1, 0, - 2, 0, 2, 0, 3, 0, 2, 0, 3, 0, 2, 0, - 3, 0, 2, 0, 3, 0, 2, 0, 1, 0, 2, 0, - 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, - 3, 0, 3, 0, 3, 0, 3, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 8, 12, 8, 4, 8, 8, 12, 8, 4, 8, 8, 12, + 8, 12, 8, 4, 8, 4, 8, 8, 12, 8, 12, 8, + 12, 8, 12, 8, 4, 8, 4, 8, 4, 8, 4, 8, + 12, 12, 12, 12, 4, 4, 4, 4, 0, 0, 0, 0 }, { - 1, 0, 2, 0, 1, 0, 0, 0, 1, 0, 1, 0, - 2, 0, 1, 0, 0, 0, 1, 0, 1, 0, 2, 0, - 1, 0, 2, 0, 1, 0, 0, 0, 1, 0, 0, 0, - 1, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, - 2, 0, 1, 0, 2, 0, 1, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, - 2, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0 + 4, 8, 4, 0, 4, 4, 8, 4, 0, 4, 4, 8, + 4, 8, 4, 0, 4, 0, 4, 4, 8, 4, 8, 4, + 8, 4, 8, 4, 0, 4, 0, 4, 0, 4, 0, 4, + 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 12, 12 }, { - 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, - 3, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 3, 0, 3, 0, - 3, 0, 3, 0, 0, 0, 0, 0, 2, 0, 2, 0 + 0, 4, 0, 12, 0, 0, 4, 0, 12, 0, 0, 4, + 0, 4, 0, 12, 0, 12, 0, 0, 4, 0, 4, 0, + 4, 0, 4, 0, 12, 0, 12, 0, 12, 0, 12, 0, + 4, 4, 4, 4, 12, 12, 12, 12, 0, 0, 8, 8 } }; diff --git a/engines/xeen/resources.h b/engines/xeen/resources.h index 0ef9211288..f6bcffaf4e 100644 --- a/engines/xeen/resources.h +++ b/engines/xeen/resources.h @@ -32,6 +32,8 @@ extern const char *const CREDITS; extern const char *const OPTIONS_TITLE; +extern const char *const THE_PARTY_NEEDS_REST; + extern const char *const TERRAIN_TYPES[6]; extern const char *const SURFACE_TYPE_NAMES[15]; @@ -88,7 +90,7 @@ extern const int OUTDOOR_OBJECT_Y[2][12]; extern const int DIRECTION_ANIM_POSITIONS[4][4]; -extern const byte WALL_NUMBERS[4][96]; +extern const byte WALL_NUMBERS[4][48]; extern const int DRAW_NUMBERS[25]; diff --git a/engines/xeen/scripts.cpp b/engines/xeen/scripts.cpp index 0a864aa2cb..5a36a32a7e 100644 --- a/engines/xeen/scripts.cpp +++ b/engines/xeen/scripts.cpp @@ -61,4 +61,21 @@ void MazeEvents::synchronize(XeenSerializer &s) { } } +/*------------------------------------------------------------------------*/ + +Scripts::Scripts(XeenEngine *vm) : _vm(vm) { +} + +void Scripts::checkEvents() { + // TODO +} + +void Scripts::giveTreasure() { + // TODO +} + +void Scripts::openGrate(int v1, int v2) { + // TODO +} + } // End of namespace Xeen diff --git a/engines/xeen/scripts.h b/engines/xeen/scripts.h index 727d37f681..4f49018e2b 100644 --- a/engines/xeen/scripts.h +++ b/engines/xeen/scripts.h @@ -95,6 +95,8 @@ enum Opcode { OP_PlayCD = 0x3C }; +class XeenEngine; + class MazeEvent { public: Common::Point _position; @@ -115,6 +117,19 @@ public: void synchronize(XeenSerializer &s); }; +class Scripts { +private: + XeenEngine *_vm; +public: + Scripts(XeenEngine *vm); + + void checkEvents(); + + void giveTreasure(); + + void openGrate(int v1, int v2); +}; + } // End of namespace Xeen #endif /* XEEN_SCRIPTS_H */ diff --git a/engines/xeen/sprites.cpp b/engines/xeen/sprites.cpp index 448ec0ec57..1d9a5c8abf 100644 --- a/engines/xeen/sprites.cpp +++ b/engines/xeen/sprites.cpp @@ -230,6 +230,7 @@ void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Poi void SpriteResource::draw(XSurface &dest, int frame, const Common::Point &destPos, int flags, int scale) const { + scale = 0; // ***DEBUG*** if (scale == 0) { drawOffset(dest, _index[frame]._offset1, destPos, flags); if (_index[frame]._offset2) diff --git a/engines/xeen/xeen.cpp b/engines/xeen/xeen.cpp index 6b73de6939..b8f9030c85 100644 --- a/engines/xeen/xeen.cpp +++ b/engines/xeen/xeen.cpp @@ -42,8 +42,10 @@ XeenEngine::XeenEngine(OSystem *syst, const XeenGameDescription *gameDesc) _files = nullptr; _interface = nullptr; _map = nullptr; + _party = nullptr; _saves = nullptr; _screen = nullptr; + _scripts = nullptr; _sound = nullptr; _eventData = nullptr; _loadDarkSide = 1; @@ -63,8 +65,10 @@ XeenEngine::~XeenEngine() { delete _events; delete _interface; delete _map; + delete _party; delete _saves; delete _screen; + delete _scripts; delete _sound; delete _eventData; delete _files; @@ -84,8 +88,10 @@ void XeenEngine::initialize() { _events = new EventsManager(this); _interface = new Interface(this); _map = new Map(this); - _saves = new SavesManager(this, _party, _roster); + _party = new Party(this); + _saves = new SavesManager(this, *_party, _roster); _screen = new Screen(this); + _scripts = new Scripts(this); _screen->setupWindows(); _sound = new SoundManager(this); @@ -281,13 +287,13 @@ void XeenEngine::play() { if (getGameID() != GType_WorldOfXeen && !_loadDarkSide) { _loadDarkSide = true; - _party._mazeId = 29; - _party._mazeDirection = DIR_NORTH; - _party._mazePosition.x = 25; - _party._mazePosition.y = 21; + _party->_mazeId = 29; + _party->_mazeDirection = DIR_NORTH; + _party->_mazePosition.x = 25; + _party->_mazePosition.y = 21; } - _map->load(_party._mazeId); + _map->load(_party->_mazeId); _interface->startup(); if (_mode == MODE_0) { @@ -307,9 +313,22 @@ void XeenEngine::play() { _moveMonsters = true; + gameLoop(); +} + +void XeenEngine::gameLoop() { // Main game loop while (!shouldQuit()) { - _events->pollEventsAndWait(); + _map->cellFlagLookup(_party->_mazePosition); + if (_map->_currentIsEvent) { + _scripts->checkEvents(); + if (shouldQuit()) + return; + } + _scripts->giveTreasure(); + + // Wait loop + _interface->wait(); } } diff --git a/engines/xeen/xeen.h b/engines/xeen/xeen.h index e6bcd8c0b7..59816b0d91 100644 --- a/engines/xeen/xeen.h +++ b/engines/xeen/xeen.h @@ -41,6 +41,7 @@ #include "xeen/party.h" #include "xeen/saves.h" #include "xeen/screen.h" +#include "xeen/scripts.h" #include "xeen/sound.h" /** @@ -102,6 +103,8 @@ private: void play(); void pleaseWait(); + + void gameLoop(); protected: /** * Play the game @@ -131,14 +134,15 @@ public: FileManager *_files; Interface *_interface; Map *_map; + Party *_party; SavesManager *_saves; Screen *_screen; + Scripts *_scripts; SoundManager *_sound; Mode _mode; GameEvent _gameEvent; Common::SeekableReadStream *_eventData; Roster _roster; - Party _party; int _loadDarkSide; bool _dangerSenseAllowed; int _face1State; |