From 06b7eb220e6f7a98007f6eb011a23b04812506b1 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Fri, 23 Jan 2015 23:47:05 -0500 Subject: XEEN: Implemented If script opcode --- engines/xeen/interface.cpp | 2 +- engines/xeen/party.cpp | 31 +++- engines/xeen/party.h | 19 ++- engines/xeen/resources.cpp | 19 +++ engines/xeen/resources.h | 2 + engines/xeen/scripts.cpp | 414 ++++++++++++++++++++++++++++++++++++++++++++- engines/xeen/scripts.h | 6 +- 7 files changed, 474 insertions(+), 19 deletions(-) (limited to 'engines') diff --git a/engines/xeen/interface.cpp b/engines/xeen/interface.cpp index 307027bacc..a23cee69e5 100644 --- a/engines/xeen/interface.cpp +++ b/engines/xeen/interface.cpp @@ -355,7 +355,7 @@ void Interface::charIconsPrint(bool updateFlag) { PlayerStruct &ps = _vm->_party->_activeParty[charIndex]; // Draw the Hp bar - int maxHp = ps.getMaxHp(); + int maxHp = ps.getMaxHP(); int frame; if (ps._currentHp < 1) frame = 4; diff --git a/engines/xeen/party.cpp b/engines/xeen/party.cpp index 8d59e686fd..43c2ecabab 100644 --- a/engines/xeen/party.cpp +++ b/engines/xeen/party.cpp @@ -158,17 +158,22 @@ Condition PlayerStruct::worstCondition() const { return NO_CONDITION; } -int PlayerStruct::getAge(int partyYear, bool ignoreTemp) { - int year = MIN(partyYear - _ybDay, 254); +int PlayerStruct::getAge(bool ignoreTemp) const { + int year = MIN(Party::_vm->_party->_year - _ybDay, 254); return ignoreTemp ? year : year + _tempAge; } -int PlayerStruct::getMaxHp() { +int PlayerStruct::getMaxHP() const { warning("TODO: getMaxHp"); return 20; } +int PlayerStruct::getMaxSP() const { + warning("TODO: getMaxSP"); + return 20; +} + int PlayerStruct::getStat(int statNum, int v2) { // TODO return 10; @@ -194,7 +199,7 @@ void PlayerStruct::setAward(int awardId, bool value) { _awards[v] = value; } -bool PlayerStruct::hasAward(int awardId) { +bool PlayerStruct::hasAward(int awardId) const { int v = awardId; if (awardId == 73) v = 126; @@ -204,6 +209,15 @@ bool PlayerStruct::hasAward(int awardId) { return _awards[v]; } +int PlayerStruct::getArmorClass(bool baseOnly) const { + // TODO + return 1; +} + +int PlayerStruct::getThievery() const { + // TODO + return 1; +} /*------------------------------------------------------------------------*/ @@ -217,7 +231,10 @@ void Roster::synchronize(Common::Serializer &s) { /*------------------------------------------------------------------------*/ -Party::Party(XeenEngine *vm): _vm(vm) { +XeenEngine *Party::_vm; + +Party::Party(XeenEngine *vm) { + _vm = vm; _partyCount = 0; _realPartyCount = 0; Common::fill(&_partyMembers[0], &_partyMembers[8], 0); @@ -258,7 +275,7 @@ Party::Party(XeenEngine *vm): _vm(vm) { _rested = false; Common::fill(&_gameFlags[0], &_gameFlags[512], false); - Common::fill(&_autoNotes[0], &_autoNotes[128], false); + Common::fill(&_worldFlags[0], &_worldFlags[128], false); Common::fill(&_quests[0], &_quests[64], false); Common::fill(&_questItems[0], &_questItems[85], 0); @@ -337,7 +354,7 @@ void Party::synchronize(Common::Serializer &s) { s.syncAsUint32LE(_totalTime); s.syncAsByte(_rested); SavesManager::syncBitFlags(s, &_gameFlags[0], &_gameFlags[512]); - SavesManager::syncBitFlags(s, &_autoNotes[0], &_autoNotes[128]); + SavesManager::syncBitFlags(s, &_worldFlags[0], &_worldFlags[128]); SavesManager::syncBitFlags(s, &_quests[0], &_quests[64]); for (int i = 0; i < 85; ++i) diff --git a/engines/xeen/party.h b/engines/xeen/party.h index 3962ba45c0..2677813228 100644 --- a/engines/xeen/party.h +++ b/engines/xeen/party.h @@ -132,9 +132,11 @@ public: Condition worstCondition() const; - int getAge(int partyYear, bool ignoreTemp); + int getAge(bool ignoreTemp) const; - int getMaxHp(); + int getMaxHP() const; + + int getMaxSP() const; int getStat(int statNum, int v2); @@ -142,9 +144,13 @@ public: bool noActions(); - bool hasAward(int awardId); - void setAward(int awardId, bool value); + + bool hasAward(int awardId) const; + + int getArmorClass(bool baseOnly) const; + + int getThievery() const; }; class Roster: public Common::Array { @@ -155,8 +161,9 @@ public: }; class Party { + friend class PlayerStruct; private: - XeenEngine *_vm; + static XeenEngine *_vm; public: // Dynamic data that's saved int _partyCount; @@ -204,7 +211,7 @@ public: int _totalTime; bool _rested; bool _gameFlags[512]; - bool _autoNotes[128]; + bool _worldFlags[128]; bool _quests[64]; int _questItems[85]; XeenItem _blacksmithWeapons2[ITEMS_COUNT]; diff --git a/engines/xeen/resources.cpp b/engines/xeen/resources.cpp index c167bdbb48..837ff15d36 100644 --- a/engines/xeen/resources.cpp +++ b/engines/xeen/resources.cpp @@ -471,4 +471,23 @@ const int MONSTER_EFFECT_FLAGS[15][8] = { { 0x108, 0x108, 0x108, 0x108, 0x108, 0x108, 0x108, 0x108 } }; +const int SPELLS_ALLOWED[3][40] = { + { + 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, + 12, 14, 16, 23, 26, 27, 28, 30, 31, 32, + 33, 42, 46, 48, 49, 50, 52, 55, 56, 58, + 59, 62, 64, 65, 67, 68, 71, 73, 74, 76 + }, { + 1, 4, 11, 13, 15, 17, 18, 19, 20, 21, + 22, 24, 25, 29, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 47, 51, 53, 54, + 57, 60, 61, 63, 66, 69, 70, 72, 75, 76 + }, { + 0, 1, 2, 3, 4, 5, 7, 9, 10, 20, + 25, 26, 27, 28, 30, 31, 34, 38, 40, 41, + 42, 43, 44, 45, 49, 50, 52, 53, 55, 59, + 60, 61, 62, 67, 68, 72, 73, 74, 75, 76 + } +}; + } // End of namespace Xeen diff --git a/engines/xeen/resources.h b/engines/xeen/resources.h index e62454eafd..36ac6b2a21 100644 --- a/engines/xeen/resources.h +++ b/engines/xeen/resources.h @@ -110,6 +110,8 @@ extern const int COMBAT_FLOAT_Y[8]; extern const int MONSTER_EFFECT_FLAGS[15][8]; +extern const int SPELLS_ALLOWED[3][40]; + } // End of namespace Xeen #endif /* XEEN_RESOURCES_H */ diff --git a/engines/xeen/scripts.cpp b/engines/xeen/scripts.cpp index 6a08286148..d59346ef7e 100644 --- a/engines/xeen/scripts.cpp +++ b/engines/xeen/scripts.cpp @@ -23,6 +23,7 @@ #include "xeen/scripts.h" #include "xeen/dialogs_string_input.h" #include "xeen/dialogs_whowill.h" +#include "xeen/dialogs_yesno.h" #include "xeen/party.h" #include "xeen/resources.h" #include "xeen/xeen.h" @@ -171,7 +172,7 @@ void Scripts::doOpcode(MazeEvent &event) { &Scripts::cmdRemove, &Scripts::cmdSetChar, &Scripts::cmdSpawn, &Scripts::cmdDoTownEvent, &Scripts::cmdExit, &Scripts::cmdAlterMap, &Scripts::cmdGiveExtended, &Scripts::cmdConfirmWord, &Scripts::cmdDamage, - &Scripts::cmdJumpRnd, &Scripts::cmdAfterEvent, &Scripts::cmdCallEvent, + &Scripts::cmdJumpRnd, &Scripts::cmdAlterEvent, &Scripts::cmdCallEvent, &Scripts::cmdReturn, &Scripts::cmdSetVar, &Scripts::cmdTakeOrGive, &Scripts::cmdTakeOrGive, &Scripts::cmdCutsceneEndClouds, &Scripts::cmdTeleport, &Scripts::cmdWhoWill, @@ -378,21 +379,47 @@ void Scripts::cmdAlterMap(Common::Array ¶ms) { } void Scripts::cmdGiveExtended(Common::Array ¶ms) { + Party &party = *_vm->_party; + uint32 mask; + int newLineNum; + bool result; + switch (params[0]) { case 16: case 34: case 100: - // TODO + mask = (params[4] << 24) | params[3] | (params[2] << 8) | (params[1] << 16); + newLineNum = params[5]; break; case 25: case 35: case 101: case 106: - // TODO + mask = (params[2] << 8) | params[1]; + newLineNum = params[3]; break; default: + mask = params[1]; + newLineNum = params[2]; break; } + + if ((_charIndex != 0 & _charIndex != 8) || params[0] == 44) { + result = ifProc(params[0], mask, _event->_opcode - OP_If1, _charIndex - 1); + } else { + result = false; + for (int idx = 0; idx < party._partyCount && !result; ++idx) { + if (_charIndex == 0 || (_charIndex == 8 && _v2 != idx)) { + result = ifProc(params[0], mask, _event->_opcode - OP_If1, idx); + } + } + } + + if (result) + _lineNum = newLineNum - 1; + + _var4F = true; + cmdNoAction(params); } void Scripts::cmdConfirmWord(Common::Array ¶ms) { @@ -634,5 +661,386 @@ void Scripts::doEnding(const Common::String &endStr, int v2) { warning("TODO: doEnding"); } +/** + * This monstrosity handles doing the various types of If checks on various data + */ +bool Scripts::ifProc(int action, uint32 mask, int mode, int charIndex) { + Party &party = *_vm->_party; + PlayerStruct &ps = party._activeParty[charIndex]; + uint v = 0; + + switch (action) { + case 3: + // Player sex + v = (uint)ps._sex; + break; + case 4: + // Player race + v = (uint)ps._race; + break; + case 5: + // Player class + v = (uint)ps._class; + break; + case 8: + // Current health points + v = (uint)ps._currentHp; + break; + case 9: + // Current spell points + v = (uint)ps._currentSp; + break; + case 10: + // Get armor class + v = (uint)ps.getArmorClass(false); + break; + case 11: + // Level bonus (extra beyond base) + v = ps._level._temporary; + break; + case 12: + // Current age, including unnatural aging + v = ps.getAge(false); + break; + case 13: + assert(mask < 18); + if (ps._skills[mask]) + v = mask; + break; + case 15: + // Award + assert(mask < 128); + if (ps.hasAward(mask)) + v = mask; + break; + case 16: + // Experience + v = ps._experience; + break; + case 17: + // Party poison resistence + v = party._poisonResistence; + break; + case 18: + // Condition + assert(mask < 16); + if (!ps._conditions[mask] && !(mask & 0x10)) + v = mask; + break; + case 19: { + // Can player cast a given spell + + // Get the type of character + int category; + switch (ps._class) { + case CLASS_KNIGHT: + case CLASS_ARCHER: + category = 0; + break; + case CLASS_PALADIN: + case CLASS_CLERIC: + category = 1; + break; + case CLASS_BARBARIAN: + case CLASS_DRUID: + category = 2; + break; + default: + category = 0; + break; + } + + // Check if the character class can cast the particular spell + for (int idx = 0; idx < 39; ++idx) { + if (SPELLS_ALLOWED[mode][idx] == mask) { + // Can cast it. Check if the player has it in their spellbook + if (ps._spells[idx]) + v = mask; + break; + } + } + break; + } + case 20: + if (_vm->_files->_isDarkCc) + mask += 0x100; + assert(mask < 0x200); + if (party._gameFlags[mask]) + v = mask; + break; + case 21: + // Scans inventories for given item number + v = 0xFFFFFFFF; + if (mask < 82) { + for (int idx = 0; idx < 9; ++idx) { + if (mask == 35) { + if (ps._weapons[idx]._name == mask) { + v = mask; + break; + } + } else if (mask < 49) { + if (ps._armor[idx]._name == (mask - 35)) { + v = mask; + break; + } + } else if (mask < 60) { + if (ps._accessories[idx]._name == (mask - 49)) { + v = mask; + break; + } + } else { + if (ps._misc[idx]._name == (mask - 60)) { + v = mask; + break; + } + } + } + } else { + int baseFlag = 8 * (6 + mask); + for (int idx = 0; idx < 8; ++idx) { + if (party._gameFlags[baseFlag + idx]) { + v = mask; + break; + } + } + } + break; + case 25: + // Returns number of minutes elapsed in the day (0-1440) + v = party._minutes; + break; + case 34: + // Current party gold + v = party._gold; + break; + case 35: + // Current party gems + v = party._gems; + break; + case 37: + // Might bonus (extra beond base) + v = ps._might._temporary; + break; + case 38: + // Intellect bonus (extra beyond base) + v = ps._intellect._temporary; + break; + case 39: + // Personality bonus (extra beyond base) + v = ps._personality._temporary; + break; + case 40: + // Endurance bonus (extra beyond base) + v = ps._endurance._temporary; + break; + case 41: + // Speed bonus (extra beyond base) + v = ps._speed._temporary; + break; + case 42: + // Accuracy bonus (extra beyond base) + v = ps._accuracy._temporary; + break; + case 43: + // Luck bonus (extra beyond base) + v = ps._luck._temporary; + break; + case 44: + v = YesNo::show(_vm, mask, 0); + if (!mask && v) + v = 0; + break; + case 45: + // Might base (before bonus) + v = ps._might._permanent; + break; + case 46: + // Intellect base (before bonus) + v = ps._intellect._permanent; + break; + case 47: + // Personality base (before bonus) + v = ps._personality._permanent; + break; + case 48: + // Endurance base (before bonus) + v = ps._endurance._permanent; + break; + case 49: + // Speed base (before bonus) + v = ps._speed._permanent; + break; + case 50: + // Accuracy base (before bonus) + v = ps._accuracy._permanent; + break; + case 51: + // Luck base (before bonus) + v = ps._luck._permanent; + break; + case 52: + // Fire resistence (before bonus) + v = ps._fireResistence._permanent; + break; + case 53: + // Elecricity resistence (before bonus) + v = ps._electricityResistence._permanent; + break; + case 54: + // Cold resistence (before bonus) + v = ps._coldResistence._permanent; + break; + case 55: + // Poison resistence (before bonus) + v = ps._poisonResistence._permanent; + break; + case 56: + // Energy reistence (before bonus) + v = ps._energyResistence._permanent; + break; + case 57: + // Energy resistence (before bonus) + v = ps._magicResistence._permanent; + break; + case 58: + // Fire resistence (extra beyond base) + v = ps._fireResistence._temporary; + break; + case 59: + // Electricity resistence (extra beyond base) + v = ps._electricityResistence._temporary; + break; + case 60: + // Cold resistence (extra beyond base) + v = ps._coldResistence._temporary; + break; + case 61: + // Poison resistence (extra beyod base) + v = ps._poisonResistence._temporary; + break; + case 62: + // Energy resistence (extra beyond base) + v = ps._energyResistence._temporary; + break; + case 63: + // Magic resistence (extra beyond base) + v = ps._magicResistence._temporary; + break; + case 64: + // Level (before bonus) + v = ps._level._permanent; + break; + case 65: + // Total party food + v = party._food; + break; + case 69: + // Test for Levitate being active + v = party._levitateActive ? 1 : 0; + break; + case 70: + // Amount of light + v = party._lightCount; + break; + case 71: + // Party magical fire resistence + v = party._fireResistence; + break; + case 72: + // Party magical electricity resistence + v = party._electricityResistence; + break; + case 73: + // Party magical cold resistence + v = party._coldResistence; + break; + case 76: + // Day of the year (100 per year) + v = party._day; + break; + case 77: + // Armor class (extra beyond base) + v = ps._ACTemp; + break; + case 78: + // Test whether current Hp is equal to or exceeds the max HP + v = ps._currentHp >= ps.getMaxHP() ? 1 : 0; + break; + case 79: + // Test for Wizard Eye being active + v = party._wizardEyeActive ? 1 : 0; + break; + case 81: + // Test whether current Sp is equal to or exceeds the max SP + v = ps._currentSp >= ps.getMaxSP() ? 1 : 0; + break; + case 84: + // Current facing direction + v = (uint)party._mazeDirection; + break; + case 85: + // Current game year since start + v = party._year; + break; + case 86: + case 87: + case 88: + case 89: + case 90: + case 91: + case 92: + // Get a player stat + v = ps.getStat(action - 86, 0); + break; + case 93: + // Current day of the week (10 days per week) + v = party._day / 10; + break; + case 94: + // Test whether Walk on Water is currently active + v = party._walkOnWaterActive ? 1 : 0; + break; + case 99: + // Party skills check + if (party.checkSkill((Skill)mask)) + v = mask; + break; + case 102: + // Thievery skill + v = ps.getThievery(); + break; + case 103: + // Get value of world flag + if (party._worldFlags[mask]) + v = mask; + break; + case 104: + // Get value of quest flag + if (party._quests[mask + (_vm->_files->_isDarkCc ? 30 : 0)]) + v = mask; + break; + case 105: + // Test number of Megacredits in party. Only used by King's Engineer in Castle Burlock + v = party._questItems[26]; + break; + case 107: + // Get value of character flag + error("Unused"); + break; + default: + break; + } + + switch (mode) { + case 0: + return mask >= v; + case 1: + return mask == v; + case 2: + return mask <= v; + default: + return false; + } +} } // End of namespace Xeen diff --git a/engines/xeen/scripts.h b/engines/xeen/scripts.h index 862c54d229..fb415cac8d 100644 --- a/engines/xeen/scripts.h +++ b/engines/xeen/scripts.h @@ -41,8 +41,8 @@ enum Opcode { OP_NPC = 0x05, OP_PlayFX = 0x06, OP_TeleportAndExit = 0x07, - OP_If_1 = 0x08, - OP_If_2 = 0x09, + OP_If1 = 0x08, + OP_If2 = 0x09, OP_If3 = 0x0A, OP_MoveObj = 0x0B, OP_TakeOrGive = 0x0C, @@ -207,6 +207,8 @@ private: void doWorldEnd(); void doEnding(const Common::String &endStr, int v2); + + bool ifProc(int action, uint32 mask, int mode, int charIndex); public: int _animCounter; bool _eventSkipped; -- cgit v1.2.3