From 84900e8e50df8490fbdebc3acb25a338949f2de9 Mon Sep 17 00:00:00 2001 From: athrxx Date: Fri, 29 Jul 2011 15:57:02 +0200 Subject: [PATCH] KYRA: (EOB) - add turn undead spell, add some screen fades, fix some bugs --- devtools/create_kyradat/create_kyradat.cpp | 6 +++ devtools/create_kyradat/create_kyradat.h | 2 + devtools/create_kyradat/games.cpp | 5 ++ devtools/create_kyradat/tables.cpp | 19 +++++++- dists/engine-data/kyra.dat | Bin 458387 -> 458977 bytes engines/kyra/eob1.cpp | 51 +++++++++++++++++++-- engines/kyra/eob1.h | 6 +++ engines/kyra/eob2.cpp | 2 +- engines/kyra/eobcommon.cpp | 18 +++++--- engines/kyra/eobcommon.h | 11 ++++- engines/kyra/gui_eob.cpp | 15 +++--- engines/kyra/magic_eob.cpp | 46 ++++++++++++++++++- engines/kyra/resource.h | 3 ++ engines/kyra/saveload_eob.cpp | 7 ++- engines/kyra/scene_eob.cpp | 6 ++- engines/kyra/script_eob.cpp | 4 +- engines/kyra/sprites_eob.cpp | 15 ++++-- engines/kyra/staticres_eob.cpp | 3 ++ engines/kyra/text_eob.cpp | 2 - engines/kyra/timer_eob.cpp | 5 +- 20 files changed, 189 insertions(+), 37 deletions(-) diff --git a/devtools/create_kyradat/create_kyradat.cpp b/devtools/create_kyradat/create_kyradat.cpp index 9f749f0dfdc3..276564bc6495 100644 --- a/devtools/create_kyradat/create_kyradat.cpp +++ b/devtools/create_kyradat/create_kyradat.cpp @@ -395,6 +395,7 @@ const ExtractFilename extractFilenames[] = { { kEobBaseSparkOfY, kTypeRawData, false }, { kEobBaseSpellProperties, kTypeRawData, false }, { kEobBaseMagicFlightProps, kTypeRawData, false }, + { kEobBaseTurnUndeadEffect, kTypeRawData, false }, // EYE OF THE BEHOLDER I { kEob1MainMenuStrings, kTypeStringList, true }, @@ -406,6 +407,7 @@ const ExtractFilename extractFilenames[] = { { kEob1MonsterDistAttSfx10, kTypeRawData, false }, { kEob1MonsterDistAttType17, kTypeRawData, false }, { kEob1MonsterDistAttSfx17, kTypeRawData, false }, + { kEob1TurnUndeadString, kTypeStringList, true }, // EYE OF THE BEHOLDER II { kEob2MainMenuStrings, kTypeStringList, true }, @@ -1602,6 +1604,8 @@ const char *getIdString(const int id) { return "kEobBaseSpellProperties"; case kEobBaseMagicFlightProps: return "kEobBaseMagicFlightProps"; + case kEobBaseTurnUndeadEffect: + return "kEobBaseTurnUndeadEffect"; case kEob1MainMenuStrings: return "kEob1MainMenuStrings"; case kEob1DoorShapeDefs: @@ -1618,6 +1622,8 @@ const char *getIdString(const int id) { return "kEob1MonsterDistAttType17"; case kEob1MonsterDistAttSfx17: return "kEob1MonsterDistAttSfx17"; + case kEob1TurnUndeadString: + return "kEob1TurnUndeadString"; case kEob2MainMenuStrings: return "kEob2MainMenuStrings"; case kEob2IntroStrings: diff --git a/devtools/create_kyradat/create_kyradat.h b/devtools/create_kyradat/create_kyradat.h index 598050d80c26..a5871479743a 100644 --- a/devtools/create_kyradat/create_kyradat.h +++ b/devtools/create_kyradat/create_kyradat.h @@ -396,6 +396,7 @@ enum kExtractID { kEobBaseSpellProperties, kEobBaseMagicFlightProps, + kEobBaseTurnUndeadEffect, kEob1MainMenuStrings, kEob1DoorShapeDefs, @@ -407,6 +408,7 @@ enum kExtractID { kEob1MonsterDistAttSfx10, kEob1MonsterDistAttType17, kEob1MonsterDistAttSfx17, + kEob1TurnUndeadString, kEob2MainMenuStrings, kEob2IntroStrings, diff --git a/devtools/create_kyradat/games.cpp b/devtools/create_kyradat/games.cpp index 0496acc4b155..4ab12028c251 100644 --- a/devtools/create_kyradat/games.cpp +++ b/devtools/create_kyradat/games.cpp @@ -1063,6 +1063,7 @@ const int eob1FloppyNeed[] = { kEob1MonsterDistAttSfx10, kEob1MonsterDistAttType17, kEob1MonsterDistAttSfx17, + kEob1TurnUndeadString, kEobBasePryDoorStrings, kEobBaseWarningStrings, @@ -1077,6 +1078,8 @@ const int eob1FloppyNeed[] = { kEobBaseMagicObjectStrings, kEobBaseMagicObject5String, kEobBasePatternSuffix, + kEobBasePatternGrFix1, + kEobBasePatternGrFix2, kEobBaseValidateArmorString, kEobBaseValidateNoDropString, kEobBasePotionStrings, @@ -1169,6 +1172,7 @@ const int eob1FloppyNeed[] = { kEobBaseSpellProperties, kEobBaseMagicFlightProps, + kEobBaseTurnUndeadEffect, kLolEobCommonDscDoorShapeIndex, kEobBaseWllFlagPreset, @@ -1382,6 +1386,7 @@ const int eob2FloppyNeed[] = { kEobBaseSpellProperties, kEobBaseMagicFlightProps, + kEobBaseTurnUndeadEffect, kLolEobCommonDscDoorShapeIndex, kEobBaseWllFlagPreset, diff --git a/devtools/create_kyradat/tables.cpp b/devtools/create_kyradat/tables.cpp index c45b3d4c26ef..46239c2bee5a 100644 --- a/devtools/create_kyradat/tables.cpp +++ b/devtools/create_kyradat/tables.cpp @@ -2089,8 +2089,7 @@ const ExtractEntrySearchData kEobBaseSlotValidationFlagsProvider[] = { }; const ExtractEntrySearchData kEobBaseProjectileWeaponTypesProvider[] = { - { UNK_LANG, kPlatformPC, { 0x0000000D, 0x0000063E, { { 0xA6, 0x75, 0x6C, 0x39, 0x96, 0xCB, 0xA7, 0xC2, 0x31, 0xE0, 0x2A, 0x75, 0x30, 0x96, 0x58, 0x05 } } } }, // EOB 1 - { UNK_LANG, kPlatformPC, { 0x0000000C, 0x0000063E, { { 0x3E, 0x99, 0x6D, 0xE4, 0x6B, 0xC8, 0x49, 0x1B, 0x17, 0xD2, 0xBE, 0x9B, 0xE0, 0xCD, 0xA1, 0xC2 } } } }, // EOB 1 + { UNK_LANG, kPlatformPC, { 0x00000008, 0x0000061C, { { 0x05, 0x55, 0xA6, 0xD1, 0x3C, 0x12, 0x84, 0xDA, 0xA9, 0x33, 0xCF, 0x07, 0x05, 0x2A, 0xB2, 0x29 } } } }, // EOB 1 { UNK_LANG, kPlatformPC, { 0x0000000F, 0x00000829, { { 0x9F, 0x6A, 0x13, 0x8A, 0xA7, 0x40, 0xE8, 0x40, 0x2E, 0x87, 0x49, 0x6B, 0x67, 0xED, 0xE8, 0xCE } } } }, // EOB 2 EXTRACT_END_ENTRY }; @@ -2310,6 +2309,12 @@ const ExtractEntrySearchData kEobBaseMagicFlightPropsProvider[] = { EXTRACT_END_ENTRY }; +const ExtractEntrySearchData kEobBaseTurnUndeadEffectProvider[] = { + { UNK_LANG, kPlatformUnknown, { 0x0000008C, 0x00002E8B, { { 0x96, 0x15, 0x61, 0x12, 0x43, 0xCF, 0x3A, 0x84, 0x1A, 0x89, 0xB5, 0x32, 0x0D, 0xB3, 0x20, 0x67 } } } }, + + EXTRACT_END_ENTRY +}; + const ExtractEntrySearchData kEob1MainMenuStringsProvider[] = { { EN_ANY, kPlatformUnknown, { 0x00000037, 0x00000D79, { { 0x1D, 0x72, 0x7F, 0x8F, 0xEB, 0x4A, 0xBF, 0x82, 0xB7, 0xB5, 0x9D, 0xB0, 0x7B, 0xDA, 0xEC, 0xEE } } } }, { DE_DEU, kPlatformUnknown, { 0x00000034, 0x00000C6F, { { 0xF2, 0x5F, 0xBE, 0xFB, 0x27, 0x1C, 0x91, 0x33, 0x25, 0x09, 0xC1, 0xA0, 0x27, 0x89, 0xD7, 0xD5 } } } }, @@ -2356,6 +2361,14 @@ const ExtractEntrySearchData kEob1MonsterDistAttSfx17Provider[] = { EXTRACT_END_ENTRY }; +const ExtractEntrySearchData kEob1TurnUndeadStringProvider[] = { + { EN_ANY, kPlatformUnknown, { 0x00000027, 0x00000BF2, { { 0x43, 0x0A, 0x1E, 0xEE, 0x84, 0xD6, 0xD6, 0x87, 0x20, 0x9F, 0x15, 0x22, 0x9B, 0x65, 0x24, 0xDB } } } }, + { DE_DEU, kPlatformUnknown, { 0x00000030, 0x00000F48, { { 0xDA, 0x59, 0xEC, 0xC1, 0x9B, 0xCF, 0x90, 0x4A, 0x93, 0x3E, 0xE5, 0x26, 0x20, 0x8B, 0x74, 0x92 } } } }, + + EXTRACT_END_ENTRY +}; + + const ExtractEntrySearchData kEob2MainMenuStringsProvider[] = { { EN_ANY, kPlatformUnknown, { 0x0000005F, 0x000017BE, { { 0x77, 0x8A, 0x50, 0x9F, 0x42, 0xD8, 0x00, 0x05, 0x60, 0x2A, 0x80, 0x25, 0x00, 0xDC, 0x7C, 0x92 } } } }, { DE_DEU, kPlatformUnknown, { 0x0000005E, 0x000017F3, { { 0xD0, 0x93, 0x2E, 0x5F, 0x9D, 0xDB, 0xC4, 0xFB, 0x9E, 0x9F, 0x14, 0xD6, 0xB4, 0xBE, 0x3D, 0x0C } } } }, @@ -3698,6 +3711,7 @@ const ExtractEntry extractProviders[] = { { kEobBaseSpellProperties, kEobBaseSpellPropertiesProvider }, { kEobBaseMagicFlightProps, kEobBaseMagicFlightPropsProvider }, + { kEobBaseTurnUndeadEffect, kEobBaseTurnUndeadEffectProvider }, { kEob1MainMenuStrings, kEob1MainMenuStringsProvider }, { kEob1DoorShapeDefs, kEob1DoorShapeDefsProvider }, @@ -3709,6 +3723,7 @@ const ExtractEntry extractProviders[] = { { kEob1MonsterDistAttSfx10, kEob1MonsterDistAttSfx10Provider }, { kEob1MonsterDistAttType17, kEob1MonsterDistAttType17Provider }, { kEob1MonsterDistAttSfx17, kEob1MonsterDistAttSfx17Provider }, + { kEob1TurnUndeadString, kEob1TurnUndeadStringProvider }, { kEob2MainMenuStrings, kEob2MainMenuStringsProvider }, { kEob2IntroStrings, kEob2IntroStringsProvider }, diff --git a/dists/engine-data/kyra.dat b/dists/engine-data/kyra.dat index cd51c0b54c20d0d25bf1ed47602409725e1bbf61..2128a448b6b7ea33d936ca30bab8b904650c8226 100644 GIT binary patch delta 26515 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 zF+TmRqinventory[0]].type].extraProperties != 6 && _itemTypes[_items[c->inventory[1]].type].extraProperties != 6) + continue; + + int l = getCharacterLevelIndex(2, c->cClass); + if (l > -1) { + if (c->level[l] > _openBookCasterLevel) { + _openBookCasterLevel = c->level[l]; + _openBookChar = i; + } + } else { + l = getCharacterLevelIndex(4, c->cClass); + if (l > -1) { + if ((c->level[l] - 2) > _openBookCasterLevel) { + _openBookCasterLevel = (c->level[l] - 2); + _openBookChar = i; + } + } + } + } + + if (_openBookCasterLevel) + spellCallback_start_turnUndead(); + + _openBookChar = oc; + _openBookCasterLevel = 0; +} + +void EobEngine::turnUndeadAutoHit() { + _txt->printMessage(_turnUndeadString[0], -1, _characters[_openBookChar].name); + sparkEffectOffensive(); +} + bool EobEngine::checkPartyStatusExtra() { _screen->copyPage(0, 10); - gui_drawBox(0, 121, 319, 200, _color2_1, _color1_1, _bkgColor_1); - _screen->setScreenDim(9); + gui_drawBox(0, 121, 320, 80, _color1_1, _color2_1, _bkgColor_1); + _txt->setupField(9, false); _txt->printMessage(_menuStringsDefeat[0]); while (!shouldQuit()) { removeInputTop(); @@ -221,6 +265,7 @@ bool EobEngine::checkPartyStatusExtra() { break; } _screen->copyPage(10, 0); + _eventList.clear(); return true; } diff --git a/engines/kyra/eob1.h b/engines/kyra/eob1.h index 3460ac7591dd..8d41118e521d 100644 --- a/engines/kyra/eob1.h +++ b/engines/kyra/eob1.h @@ -84,6 +84,12 @@ friend class GUI_Eob; const uint8 *_doorSwitchShapeEncodeDefs; const uint8 *_doorSwitchCoords; + // Magic + void turnUndeadAuto(); + void turnUndeadAutoHit(); + + const char * const *_turnUndeadString; + // Misc bool checkPartyStatusExtra(); uint32 convertSpellFlagToEob2Format(uint32 flag, int ignoreInvisibility); diff --git a/engines/kyra/eob2.cpp b/engines/kyra/eob2.cpp index c3169b10bfeb..cd306863ba4d 100644 --- a/engines/kyra/eob2.cpp +++ b/engines/kyra/eob2.cpp @@ -338,7 +338,7 @@ void DarkMoonEngine::drawDoorIntern(int type, int, int x, int y, int w, int wall } void DarkMoonEngine::restParty_npc() { - + warning("DarkMoonEngine::restParty_npc(): implement!"); } bool DarkMoonEngine::restParty_extraAbortCondition() { diff --git a/engines/kyra/eobcommon.cpp b/engines/kyra/eobcommon.cpp index 07c1f0760ade..22c05190691e 100644 --- a/engines/kyra/eobcommon.cpp +++ b/engines/kyra/eobcommon.cpp @@ -46,6 +46,7 @@ EobCoreEngine::EobCoreEngine(OSystem *system, const GameFlags &flags) : LolEobBa _playFinale = false; _runFlag = true; _configMouse = true; + _loading = false; _largeItemShapes = _smallItemShapes = _thrownItemShapes = _spellShapes = _firebeamShapes = _itemIconShapes = _wallOfForceShapes = _teleporterShapes = _sparkShapes = _compassShapes = 0; @@ -82,7 +83,7 @@ EobCoreEngine::EobCoreEngine(OSystem *system, const GameFlags &flags) : LolEobBa _monsterOvl1 = _monsterOvl2 = 0; _monsters = 0; _dstMonsterIndex = 0; - _inflictMonsterDamageUnk = 0; + _preventMonsterFlash = false; _teleporterPulse = 0; @@ -124,7 +125,7 @@ EobCoreEngine::EobCoreEngine(OSystem *system, const GameFlags &flags) : LolEobBa _openBookSpellLevel = 0; _openBookSpellSelectedItem = 0; _openBookSpellListOffset = 0; - _openBookChar = _openBookCharBackup = 0; + _openBookChar = _openBookCharBackup = _openBookCasterLevel = 0; _openBookType = _openBookTypeBackup = 0; _openBookSpellList = 0; _openBookAvailableSpells = 0; @@ -415,7 +416,7 @@ void EobCoreEngine::writeSettings() { void EobCoreEngine::startupNew() { gui_setPlayFieldButtons(); _screen->_curPage = 0; - gui_drawPlayField(0); + gui_drawPlayField(false); _screen->_curPage = 0; gui_drawAllCharPortraitsWithStats(); drawScene(1); @@ -452,8 +453,7 @@ void EobCoreEngine::runLoop() { _envAudioTimer = _system->getMillis() + (rollDice(1, 10, 3) * 18 * _tickLength); snd_processEnvironmentalSoundEffect(_flags.gameID == GI_EOB1 ? 30 : (rollDice(1, 2, -1) ? 27 : 28), _currentBlock + rollDice(1, 12, -1)); updateEnvironmentalSfx(0); - //TODO - //EOB1__level_2_7__turnUndead(); + turnUndeadAuto(); } } @@ -1540,7 +1540,11 @@ int EobCoreEngine::thrownAttack(int charIndex, int slotIndex, Item item) { int EobCoreEngine::projectileWeaponAttack(int charIndex, Item item) { int tp = _items[item].type; - int t = _projectileWeaponAmmoTypes[_flags.gameID == GI_EOB1 ? tp - 2 : tp]; + + if (_flags.gameID == GI_EOB1) + assert(tp >= 7); + + int t = _projectileWeaponAmmoTypes[_flags.gameID == GI_EOB1 ? tp - 7 : tp]; Item ammoItem = 0; if (t == 16) { @@ -1588,7 +1592,7 @@ void EobCoreEngine::inflictMonsterDamage(EobMonsterInPlay *m, int damage, bool g } else { if (checkSceneUpdateNeed(m->block)) { m->flags |= 2; - if (_inflictMonsterDamageUnk) + if (_preventMonsterFlash) return; flashMonsterShape(m); } diff --git a/engines/kyra/eobcommon.h b/engines/kyra/eobcommon.h index 39919ca08646..6463f839c864 100644 --- a/engines/kyra/eobcommon.h +++ b/engines/kyra/eobcommon.h @@ -406,6 +406,7 @@ friend class CharacterGenerator; const EobCharacter *_npcPreset; bool _partyResting; + bool _loading; // Items void loadItemDefs(); @@ -634,7 +635,7 @@ friend class CharacterGenerator; uint8 _scriptTimersMode; // Gui - void gui_drawPlayField(int pageNum); + void gui_drawPlayField(bool refresh); void gui_restorePlayField(); void gui_drawAllCharPortraitsWithStats(); void gui_drawCharPortraitWithStats(int index); @@ -858,7 +859,7 @@ friend class CharacterGenerator; void explodeMonster(EobMonsterInPlay *m); int _dstMonsterIndex; - int _inflictMonsterDamageUnk; + bool _preventMonsterFlash; int16 _foundMonstersArray[5]; // magic @@ -867,6 +868,9 @@ friend class CharacterGenerator; void usePotion(int charIndex, int weaponSlot); void useWand(int charIndex, int weaponSlot); + virtual void turnUndeadAuto() {}; + virtual void turnUndeadAutoHit() {}; + void castSpell(int spell, int weaponSlot); void removeCharacterEffect(int spell, int charIndex, int showWarning); void removeAllCharacterEffects(int charIndex); @@ -880,6 +884,7 @@ friend class CharacterGenerator; bool magicObjectDamageHit(EobFlyingObject *fo, int dcTimes, int dcPips, int dcOffs, int level); bool magicObjectStatusHit(EobMonsterInPlay *m, int type, bool tryEvade, int mod); + bool turnUndeadHit(EobMonsterInPlay *m, int hitChance, int casterLevel); void printWarning(const char* str); void printNoEffectWarning(); @@ -958,6 +963,7 @@ friend class CharacterGenerator; uint8 _openBookType; uint8 _openBookCharBackup; uint8 _openBookTypeBackup; + uint8 _openBookCasterLevel; const char *const *_openBookSpellList; int8 *_openBookAvailableSpells; uint8 _activeSpellCaster; @@ -1011,6 +1017,7 @@ friend class CharacterGenerator; const uint8 *_sparkEffectOfY; const uint8 *_magicFlightObjectProperties; + const uint8 *_turnUndeadEffect; // Menu EobMenuDef *_menuDefs; diff --git a/engines/kyra/gui_eob.cpp b/engines/kyra/gui_eob.cpp index c7972299d178..ce5f3ea53fe5 100644 --- a/engines/kyra/gui_eob.cpp +++ b/engines/kyra/gui_eob.cpp @@ -149,17 +149,19 @@ Button *EobCoreEngine::gui_getButton(Button *buttonList, int index) { return 0; } -void EobCoreEngine::gui_drawPlayField(int pageNum) { +void EobCoreEngine::gui_drawPlayField(bool refresh) { _screen->loadEobCpsFileToPage("PLAYFLD", 0, 5, 3, 2); int cp = _screen->setCurPage(2); gui_drawCompass(true); - if (pageNum && !_sceneDrawPage2) + if (refresh && !_sceneDrawPage2) drawScene(0); _screen->setCurPage(cp); _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); - _screen->updateScreen(); + + if (!_loading) + _screen->updateScreen(); _screen->loadEobCpsFileToPage("INVENT", 0, 5, 3, 2); } @@ -167,7 +169,7 @@ void EobCoreEngine::gui_drawPlayField(int pageNum) { void EobCoreEngine::gui_restorePlayField() { loadVcnData(0, 0); _screen->_curPage = 0; - gui_drawPlayField(1); + gui_drawPlayField(true); gui_drawAllCharPortraitsWithStats(); } @@ -706,7 +708,8 @@ void EobCoreEngine::gui_drawSpellbook() { _screen->setCurPage(0); _screen->copyRegion(64, 121, 64, 121, 112, 56, 2, 0, Screen::CR_NO_P_CHECK); - _screen->updateScreen(); + if (!_loading) + _screen->updateScreen(); } void EobCoreEngine::gui_drawSpellbookScrollArrow(int x, int y, int direction) { @@ -2245,7 +2248,7 @@ void GUI_Eob::runCampMenu() { if (cnt > 4) { _vm->dropCharacter(selectCharacterDialogue(53)); - _vm->gui_drawPlayField(0); + _vm->gui_drawPlayField(false); res = true; _screen->copyRegion(0, 120, 0, 0, 176, 24, 0, 14, Screen::CR_NO_P_CHECK); _screen->setFont(Screen::FID_6_FNT); diff --git a/engines/kyra/magic_eob.cpp b/engines/kyra/magic_eob.cpp index 593ef63544e6..f828625dbba3 100644 --- a/engines/kyra/magic_eob.cpp +++ b/engines/kyra/magic_eob.cpp @@ -80,6 +80,9 @@ void EobCoreEngine::usePotion(int charIndex, int weaponSlot) { int val = deleteInventoryItem(charIndex, weaponSlot); snd_playSoundEffect(10); + if (_flags.gameID == GI_EOB1) + val--; + switch (val) { case 0: sparkEffectDefensive(charIndex); @@ -496,7 +499,7 @@ bool EobCoreEngine::magicObjectDamageHit(EobFlyingObject *fo, int dcTimes, int d level = 1; if ((_levelBlockProperties[fo->curBlock].flags & 7) && (fo->attackerId >= 0 || ignoreAttackerId)) { - _inflictMonsterDamageUnk = 1; + _preventMonsterFlash = true; for (const int16 *m = findBlockMonsters(fo->curBlock, fo->curPos, fo->direction, blockDamage, singleTargetCheckAdjacent); *m != -1; m++) { int dmg = rollDice(dcTimes, dcPips, dcOffs) * level; @@ -604,6 +607,23 @@ bool EobCoreEngine::magicObjectStatusHit(EobMonsterInPlay *m, int type, bool try return true; } +bool EobCoreEngine::turnUndeadHit(EobMonsterInPlay *m, int hitChance, int casterLevel) { + uint8 e = _turnUndeadEffect[_monsterProps[m->type].tuResist * 14 + MIN(casterLevel, 14)]; + + if (e == 0xff) { + calcAndInflictMonsterDamage(m, 0, 0, 500, 0x200, 5, 3); + } else if (hitChance < e) { + return false; + } else { + m->mode = 0; + m->flags |= 8; + m->spellStatusLeft = 40; + m->dir = (getNextMonsterDirection(m->block, _currentBlock) ^ 4) >> 1; + } + + return true; +} + void EobCoreEngine::printWarning(const char* str) { _txt->printMessage(str); snd_playSoundEffect(79); @@ -900,11 +920,33 @@ void EobCoreEngine::spellCallback_start_heal() { } void EobCoreEngine::spellCallback_start_layOnHands() { - + modifyCharacterHitpoints(_activeSpellCaster, _characters[_openBookChar].level[0] << 1); } void EobCoreEngine::spellCallback_start_turnUndead() { + uint16 bl = calcNewBlockPosition(_currentBlock, _currentDirection); + if (!(_levelBlockProperties[bl].flags & 7)) + return; + + int cl = _openBookCasterLevel ? _openBookCasterLevel : getCharacterClericPaladinLevel(_openBookChar); + int r = rollDice(1, 20); + bool hit = false; + + for (const int16 *m = findBlockMonsters(bl, 4, 4, 1, 1); *m != -1; m++) { + if ((_monsterProps[_monsters[*m].type].typeFlags & 4) && !(_monsters[*m].flags & 0x10)) { + _preventMonsterFlash = true; + _monsters[*m].flags |= 0x10; + hit |= turnUndeadHit(&_monsters[*m], r, cl); + } + } + + if (hit) { + turnUndeadAutoHit(); + snd_playSoundEffect(95); + updateAllMonsterShapes(); + } + _preventMonsterFlash = false; } bool EobCoreEngine::spellCallback_end_unk1Passive(EobFlyingObject *fo) { diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h index 02ee384f5c3e..5328aeff35c0 100644 --- a/engines/kyra/resource.h +++ b/engines/kyra/resource.h @@ -469,6 +469,7 @@ enum KyraResources { kEobBaseSpellProperties, kEobBaseMagicFlightProps, + kEobBaseTurnUndeadEffect, kEob1MainMenuStrings, kEob1DoorShapeDefs, @@ -481,6 +482,8 @@ enum KyraResources { kEob1MonsterDistAttType17, kEob1MonsterDistAttSfx17, + kEob1TurnUndeadString, + kEob2MainMenuStrings, kEob2IntroStrings, kEob2IntroCPSFiles, diff --git a/engines/kyra/saveload_eob.cpp b/engines/kyra/saveload_eob.cpp index 1fa26c26b18f..43a230e590b9 100644 --- a/engines/kyra/saveload_eob.cpp +++ b/engines/kyra/saveload_eob.cpp @@ -149,6 +149,8 @@ Common::Error EobCoreEngine::loadGameState(int slot) { return Common::Error(Common::kReadingFailed); Common::SeekableSubReadStreamEndian in(saveFile, saveFile->pos(), saveFile->size(), !header.originalSave, DisposeAfterUse::YES); + _loading = true; + _screen->fadeToBlack(10); for (int i = 0; i < 6; i++) { EobCharacter *c = &_characters[i]; @@ -356,7 +358,7 @@ Common::Error EobCoreEngine::loadGameState(int slot) { _screen->setFont(Screen::FID_6_FNT); _screen->setCurPage(0); - gui_drawPlayField(0); + gui_drawPlayField(false); if (_currentControlMode) _screen->copyRegion(176, 0, 0, 0, 144, 168, 0, 5, Screen::CR_NO_P_CHECK); @@ -378,6 +380,9 @@ Common::Error EobCoreEngine::loadGameState(int slot) { while (!_screen->isMouseVisible()) _screen->showMouse(); + _loading = false; + _screen->fadeFromBlack(20); + return Common::kNoError; } diff --git a/engines/kyra/scene_eob.cpp b/engines/kyra/scene_eob.cpp index 36584d462b27..e6b3c2639454 100644 --- a/engines/kyra/scene_eob.cpp +++ b/engines/kyra/scene_eob.cpp @@ -997,7 +997,9 @@ void EobCoreEngine::drawScene(int refresh) { if (refresh) _screen->fillRect(0, 0, 176, 120, 12); - _screen->setScreenPalette(_screen->getPalette(0)); + if (!_loading) + _screen->setScreenPalette(_screen->getPalette(0)); + _sceneDrawPage2 = 0; } @@ -1025,7 +1027,7 @@ void EobCoreEngine::drawScene(int refresh) { if (!_dialogueField && refresh && !_updateFlags) gui_drawCompass(false); - if (refresh && !_partyResting) + if (refresh && !_partyResting && !_loading) _screen->updateScreen(); if (_sceneDefaultUpdate) { diff --git a/engines/kyra/script_eob.cpp b/engines/kyra/script_eob.cpp index 9e18a14fb2c5..833413c69be4 100644 --- a/engines/kyra/script_eob.cpp +++ b/engines/kyra/script_eob.cpp @@ -1397,12 +1397,12 @@ int EobInfProcessor::oeob_sequence(int8 *data) { case -2: // portal sequence - pos=pos; + error("EobInfProcessor::oeob_sequence(): unimplemented cmd -2"); break; case -1: // copy protection - pos=pos; + error("EobInfProcessor::oeob_sequence(): unimplemented cmd -1"); break; default: diff --git a/engines/kyra/sprites_eob.cpp b/engines/kyra/sprites_eob.cpp index baa7a0bfda5d..0874de82e3f8 100644 --- a/engines/kyra/sprites_eob.cpp +++ b/engines/kyra/sprites_eob.cpp @@ -140,6 +140,12 @@ const uint8 *EobCoreEngine::loadActiveMonsterData(const uint8 *data, int level) _timer->setCountdown(0x21 + (p << 1), v); } + uint32 ct = _system->getMillis(); + for (int i = 0x20; i < 0x24; i++) { + int32 del = _timer->getDelay(i); + _timer->setNextRun(i, (i & 1) ? ct + (del >> 1) : ct + del); + } + if (_hasTempDataFlags & (1 << (level - 1))) return data + 420; @@ -302,12 +308,12 @@ bool EobCoreEngine::isMonsterOnPos(EobMonsterInPlay *m, uint16 block, int pos, i const int16 *EobCoreEngine::findBlockMonsters(uint16 block, int pos, int dir, int blockDamage, int singleTargetCheckAdjacent) { static const uint8 cpos4[] = { 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1 }; - int checkPos4 = (pos <= 4) ? cpos4[(dir << 2) + pos] : 1; + int include4 = (pos < 4) ? cpos4[(dir << 2) + pos] : 1; int16 *dst = _foundMonstersArray; if (blockDamage) { for (int i = 0; i < 30; i++) { - if (_monsters[i].block == block && (_monsters[i].pos != 4 || checkPos4)) + if (_monsters[i].block == block && (_monsters[i].pos != 4 || include4)) *dst++ = i; } @@ -338,7 +344,7 @@ const int16 *EobCoreEngine::findBlockMonsters(uint16 block, int pos, int dir, in } else { for (int i = 0; i < 30; i++) { - if (isMonsterOnPos(&_monsters[i], block, pos, checkPos4)) + if (isMonsterOnPos(&_monsters[i], block, pos, include4)) *dst++ = i; } } @@ -395,7 +401,7 @@ void EobCoreEngine::updateAllMonsterShapes() { } else { _sceneUpdateRequired = false; } - _inflictMonsterDamageUnk = 0; + _preventMonsterFlash = false; } void EobCoreEngine::drawBlockItems(int index) { @@ -684,7 +690,6 @@ void EobCoreEngine::drawTeleporter(int index) { void EobCoreEngine::updateMonsters(int unit) { for (int i = 0; i < 30; i++) { EobMonsterInPlay *m = &_monsters[i]; - if (m->unit == unit) { if (m->hitPointsCur <= 0 || m->flags & 0x20) continue; diff --git a/engines/kyra/staticres_eob.cpp b/engines/kyra/staticres_eob.cpp index 19e2512da59d..1cf78eef5c95 100644 --- a/engines/kyra/staticres_eob.cpp +++ b/engines/kyra/staticres_eob.cpp @@ -528,6 +528,7 @@ void EobCoreEngine::initStaticResource() { _sparkEffectOfX = _staticres->loadRawData(kEobBaseSparkOfX, temp); _sparkEffectOfY = _staticres->loadRawData(kEobBaseSparkOfY, temp); _magicFlightObjectProperties = _staticres->loadRawData(kEobBaseMagicFlightProps, temp); + _turnUndeadEffect = _staticres->loadRawData(kEobBaseTurnUndeadEffect, temp); // Hard code the following strings, since EOB I doesn't have them in the original. // EOB I doesn't have load and save menus, because there is only one single @@ -1065,6 +1066,8 @@ void EobEngine::initStaticResource() { _monsterDistAttType17 = _staticres->loadRawData(kEob1MonsterDistAttType17, temp); _monsterDistAttSfx17 = _staticres->loadRawData(kEob1MonsterDistAttSfx17, temp); + _turnUndeadString = _staticres->loadStrings(kEob1TurnUndeadString, temp); + const uint8 *ps = _staticres->loadRawData(kEob1MonsterProperties, temp); temp /= 27; _monsterProps = new EobMonsterProperty[temp]; diff --git a/engines/kyra/text_eob.cpp b/engines/kyra/text_eob.cpp index c3506662261a..63d893d2cc81 100644 --- a/engines/kyra/text_eob.cpp +++ b/engines/kyra/text_eob.cpp @@ -70,8 +70,6 @@ void TextDisplayer_Eob::setupField(int dim, bool mode) { clearCurDim(); else resetDimTextPositions(dim); - - //_textPageBreakFunc = textPageBreakMore; + 0x25 } void TextDisplayer_Eob::resetDimTextPositions(int dim) { diff --git a/engines/kyra/timer_eob.cpp b/engines/kyra/timer_eob.cpp index 903dec2102d0..9e03bdebcfab 100644 --- a/engines/kyra/timer_eob.cpp +++ b/engines/kyra/timer_eob.cpp @@ -101,6 +101,7 @@ void EobCoreEngine::setupTimers() { _timer->addTimer(0x21, TimerV2(timerProcessMonsters), 20, true); _timer->addTimer(0x22, TimerV2(timerProcessMonsters), 20, true); _timer->addTimer(0x23, TimerV2(timerProcessMonsters), 20, true); + _timer->setNextRun(0x20, _system->getMillis()); _timer->setNextRun(0x21, _system->getMillis() + 7 * _tickLength); _timer->setNextRun(0x22, _system->getMillis() + 14 * _tickLength); _timer->setNextRun(0x23, _system->getMillis() + 14 * _tickLength); @@ -274,7 +275,6 @@ void EobCoreEngine::timerProcessMonsters(int timerNum) { updateMonsters(timerNum & 0x0f); } - void EobCoreEngine::timerSpecialCharacterUpdate(int timerNum) { int charIndex = timerNum & 0x0f; EobCharacter *c = &_characters[charIndex]; @@ -346,7 +346,8 @@ void EobCoreEngine::timerSpecialCharacterUpdate(int timerNum) { case 12: c->effectFlags &= ~0x1000; - _txt->printMessage(_characterStatusStrings12[0], -1, c->name); + if (_characterStatusStrings12) + _txt->printMessage(_characterStatusStrings12[0], -1, c->name); break; default: