From bac0caeb0ff712fe9002985c44c1ea651b86e018 Mon Sep 17 00:00:00 2001 From: athrxx Date: Sat, 6 Aug 2011 00:40:53 +0200 Subject: [PATCH] KYRA: (EOB) - lots of fixes towards EOB1 playability also implement some new code (EOB1 portals, burning hands spell, etc.) --- devtools/create_kyradat/create_kyradat.cpp | 9 +++ devtools/create_kyradat/create_kyradat.h | 3 + devtools/create_kyradat/games.cpp | 5 +- devtools/create_kyradat/tables.cpp | 22 ++++- dists/engine-data/kyra.dat | Bin 462993 -> 463146 bytes engines/kyra/chargen.cpp | 6 +- engines/kyra/eob1.cpp | 34 ++++++-- engines/kyra/eob1.h | 2 + engines/kyra/eob2.cpp | 4 +- engines/kyra/eobcommon.cpp | 89 ++++++++++++++++++--- engines/kyra/eobcommon.h | 12 ++- engines/kyra/gui_eob.cpp | 4 +- engines/kyra/magic_eob.cpp | 32 +++++++- engines/kyra/resource.h | 3 + engines/kyra/saveload_eob.cpp | 6 +- engines/kyra/scene_eob.cpp | 32 +++++--- engines/kyra/screen.cpp | 15 ++++ engines/kyra/screen.h | 5 ++ engines/kyra/screen_eob.cpp | 11 ++- engines/kyra/screen_eob.h | 4 +- engines/kyra/script_eob.cpp | 16 ++-- engines/kyra/sequences_eob1.cpp | 10 ++- engines/kyra/sequences_eob2.cpp | 2 + engines/kyra/sprites_eob.cpp | 19 +++-- engines/kyra/staticres_eob.cpp | 8 +- engines/kyra/timer_eob.cpp | 4 +- 26 files changed, 291 insertions(+), 66 deletions(-) diff --git a/devtools/create_kyradat/create_kyradat.cpp b/devtools/create_kyradat/create_kyradat.cpp index 582516bc9347..a646bee5b1bb 100644 --- a/devtools/create_kyradat/create_kyradat.cpp +++ b/devtools/create_kyradat/create_kyradat.cpp @@ -332,11 +332,13 @@ const ExtractFilename extractFilenames[] = { { kEobBaseDscDoorScaleMult5, kTypeRawData, false }, { kEobBaseDscDoorScaleMult6, kTypeRawData, false }, { kEobBaseDscDoorType5Offs, kTypeRawData, false }, + { kEobBaseDscDoorXE, kTypeRawData, false }, { kEobBaseDscDoorY1, kTypeRawData, false }, { kEobBaseDscDoorY3, kTypeRawData, false }, { kEobBaseDscDoorY4, kTypeRawData, false }, { kEobBaseDscDoorY5, kTypeRawData, false }, { kEobBaseDscDoorY6, kTypeRawData, false }, + { kEobBaseDscDoorY7, kTypeRawData, false }, { kEobBaseDscDoorCoordsExt, kLolTypeRaw16, false }, { kEobBaseDscItemPosIndex, kTypeRawData, false }, @@ -401,6 +403,7 @@ const ExtractFilename extractFilenames[] = { { kEobBaseSpellProperties, kTypeRawData, false }, { kEobBaseMagicFlightProps, kTypeRawData, false }, { kEobBaseTurnUndeadEffect, kTypeRawData, false }, + { kEobBaseBurningHandsDest, kTypeRawData, false }, // EYE OF THE BEHOLDER I { kEob1MainMenuStrings, kTypeStringList, true }, @@ -1516,6 +1519,8 @@ const char *getIdString(const int id) { return "kEobBaseDscDoorScaleMult6"; case kEobBaseDscDoorType5Offs: return "kEobBaseDscDoorType5Offs"; + case kEobBaseDscDoorXE: + return "kEobBaseDscDoorXE"; case kEobBaseDscDoorY1: return "kEobBaseDscDoorY1"; case kEobBaseDscDoorY3: @@ -1526,6 +1531,8 @@ const char *getIdString(const int id) { return "kEobBaseDscDoorY5"; case kEobBaseDscDoorY6: return "kEobBaseDscDoorY6"; + case kEobBaseDscDoorY7: + return "kEobBaseDscDoorY7"; case kEobBaseDscDoorCoordsExt: return "kEobBaseDscDoorCoordsExt"; case kEobBaseDscItemPosIndex: @@ -1635,6 +1642,8 @@ const char *getIdString(const int id) { return "kEobBaseMagicFlightProps"; case kEobBaseTurnUndeadEffect: return "kEobBaseTurnUndeadEffect"; + case kEobBaseBurningHandsDest: + return "kEobBaseBurningHandsDest"; case kEob1MainMenuStrings: return "kEob1MainMenuStrings"; case kEob1DoorShapeDefs: diff --git a/devtools/create_kyradat/create_kyradat.h b/devtools/create_kyradat/create_kyradat.h index ebaff503f487..a7b53b4128fd 100644 --- a/devtools/create_kyradat/create_kyradat.h +++ b/devtools/create_kyradat/create_kyradat.h @@ -332,11 +332,13 @@ enum kExtractID { kEobBaseDscDoorScaleMult5, kEobBaseDscDoorScaleMult6, kEobBaseDscDoorType5Offs, + kEobBaseDscDoorXE, kEobBaseDscDoorY1, kEobBaseDscDoorY3, kEobBaseDscDoorY4, kEobBaseDscDoorY5, kEobBaseDscDoorY6, + kEobBaseDscDoorY7, kEobBaseDscDoorCoordsExt, kEobBaseDscItemPosIndex, @@ -402,6 +404,7 @@ enum kExtractID { kEobBaseSpellProperties, kEobBaseMagicFlightProps, kEobBaseTurnUndeadEffect, + kEobBaseBurningHandsDest, kEob1MainMenuStrings, kEob1DoorShapeDefs, diff --git a/devtools/create_kyradat/games.cpp b/devtools/create_kyradat/games.cpp index 6e1503b45b38..000c350f2bac 100644 --- a/devtools/create_kyradat/games.cpp +++ b/devtools/create_kyradat/games.cpp @@ -1189,6 +1189,7 @@ const int eob1FloppyNeed[] = { kEobBaseSpellProperties, kEobBaseMagicFlightProps, kEobBaseTurnUndeadEffect, + kEobBaseBurningHandsDest, kLolEobCommonDscDoorShapeIndex, kEobBaseWllFlagPreset, @@ -1200,12 +1201,13 @@ const int eob1FloppyNeed[] = { kEobBaseDscDoorScaleMult4, kEobBaseDscDoorScaleMult5, kEobBaseDscDoorScaleMult6, + kEobBaseDscDoorXE, kEobBaseDscDoorY1, - kLolEobCommonDscDoorY2, kEobBaseDscDoorY3, kEobBaseDscDoorY4, kEobBaseDscDoorY5, kEobBaseDscDoorY6, + kEobBaseDscDoorY7, kEobBaseDscDoorCoordsExt, kEobBaseDscItemPosIndex, @@ -1408,6 +1410,7 @@ const int eob2FloppyNeed[] = { kEobBaseSpellProperties, kEobBaseMagicFlightProps, kEobBaseTurnUndeadEffect, + kEobBaseBurningHandsDest, kLolEobCommonDscDoorShapeIndex, kEobBaseWllFlagPreset, diff --git a/devtools/create_kyradat/tables.cpp b/devtools/create_kyradat/tables.cpp index e27c6edf443e..a92c09798bd5 100644 --- a/devtools/create_kyradat/tables.cpp +++ b/devtools/create_kyradat/tables.cpp @@ -2001,6 +2001,11 @@ const ExtractEntrySearchData kEobBaseDscDoorType5OffsProvider[] = { EXTRACT_END_ENTRY }; +const ExtractEntrySearchData kEobBaseDscDoorXEProvider[] = { + { UNK_LANG, kPlatformPC, { 0x00000020, 0x0000010F, { { 0x7B, 0x7D, 0x03, 0xDE, 0x33, 0x95, 0xB8, 0xFD, 0xAD, 0x72, 0x44, 0x7D, 0x47, 0xFE, 0x04, 0x3D } } } }, + EXTRACT_END_ENTRY +}; + const ExtractEntrySearchData kEobBaseDscDoorY1Provider[] = { { UNK_LANG, kPlatformPC, { 0x00000004, 0x000000D7, { { 0x25, 0xAE, 0xF4, 0x99, 0xE8, 0x97, 0x47, 0xAE, 0x75, 0xF3, 0xA9, 0x70, 0x4C, 0x70, 0xF3, 0x11 } } } }, // EOB 1 { UNK_LANG, kPlatformPC, { 0x00000004, 0x000000D8, { { 0xB4, 0xAA, 0x0D, 0x91, 0x58, 0x22, 0x16, 0xCF, 0xC5, 0x9D, 0x8D, 0xA1, 0xB4, 0x40, 0x83, 0x0E } } } }, // EOB 2 @@ -2027,6 +2032,11 @@ const ExtractEntrySearchData kEobBaseDscDoorY6Provider[] = { EXTRACT_END_ENTRY }; +const ExtractEntrySearchData kEobBaseDscDoorY7Provider[] = { + { UNK_LANG, kPlatformPC, { 0x00000004, 0x000000DA, { { 0xA9, 0x24, 0x71, 0x8A, 0x18, 0x24, 0x6D, 0x0A, 0x65, 0x12, 0xBB, 0x1F, 0xE7, 0x95, 0xC5, 0xA4 } } } }, + EXTRACT_END_ENTRY +}; + const ExtractEntrySearchData kEobBaseDscDoorCoordsExtProvider[] = { { UNK_LANG, kPlatformPC, { 0x00000048, 0x00000C8E, { { 0x2E, 0x0E, 0xB2, 0xAC, 0xE7, 0x0F, 0xDF, 0x38, 0xDF, 0x92, 0xB7, 0xB5, 0xA2, 0xFD, 0x40, 0x2D } } } }, EXTRACT_END_ENTRY @@ -2340,7 +2350,14 @@ const ExtractEntrySearchData kEobBaseMagicFlightPropsProvider[] = { }; const ExtractEntrySearchData kEobBaseTurnUndeadEffectProvider[] = { - { UNK_LANG, kPlatformUnknown, { 0x0000008C, 0x00002E8B, { { 0x96, 0x15, 0x61, 0x12, 0x43, 0xCF, 0x3A, 0x84, 0x1A, 0x89, 0xB5, 0x32, 0x0D, 0xB3, 0x20, 0x67 } } } }, + { UNK_LANG, kPlatformPC, { 0x0000008C, 0x00002E8B, { { 0x96, 0x15, 0x61, 0x12, 0x43, 0xCF, 0x3A, 0x84, 0x1A, 0x89, 0xB5, 0x32, 0x0D, 0xB3, 0x20, 0x67 } } } }, + + EXTRACT_END_ENTRY +}; + +const ExtractEntrySearchData kEobBaseBurningHandsDestProvider[] = { + { UNK_LANG, kPlatformPC, { 0x00000008, 0x0000000C, { { 0x61, 0xD7, 0xAB, 0xE1, 0x56, 0x54, 0x51, 0x5B, 0xD9, 0x59, 0x2D, 0x3D, 0xAE, 0xA4, 0x49, 0x31 } } } }, // EOB1 + { UNK_LANG, kPlatformPC, { 0x00000020, 0x0000003E, { { 0xA5, 0x8C, 0xCA, 0x13, 0xED, 0x0F, 0xB7, 0xA2, 0xD7, 0x9C, 0xCD, 0x11, 0x65, 0x11, 0x4B, 0xD8 } } } }, // EOB2 EXTRACT_END_ENTRY }; @@ -3775,11 +3792,13 @@ const ExtractEntry extractProviders[] = { { kEobBaseDscDoorScaleMult5, kEobBaseDscDoorScaleMult5Provider }, { kEobBaseDscDoorScaleMult6, kEobBaseDscDoorScaleMult6Provider }, { kEobBaseDscDoorType5Offs, kEobBaseDscDoorType5OffsProvider }, + { kEobBaseDscDoorXE, kEobBaseDscDoorXEProvider }, { kEobBaseDscDoorY1, kEobBaseDscDoorY1Provider }, { kEobBaseDscDoorY3, kEobBaseDscDoorY3Provider }, { kEobBaseDscDoorY4, kEobBaseDscDoorY4Provider }, { kEobBaseDscDoorY5, kEobBaseDscDoorY5Provider }, { kEobBaseDscDoorY6, kEobBaseDscDoorY6Provider }, + { kEobBaseDscDoorY7, kEobBaseDscDoorY7Provider }, { kEobBaseDscDoorCoordsExt, kEobBaseDscDoorCoordsExtProvider }, { kEobBaseDscItemPosIndex, kEobBaseDscItemPosIndexProvider }, { kEobBaseDscItemShpX, kEobBaseDscItemShpXProvider }, @@ -3843,6 +3862,7 @@ const ExtractEntry extractProviders[] = { { kEobBaseSpellProperties, kEobBaseSpellPropertiesProvider }, { kEobBaseMagicFlightProps, kEobBaseMagicFlightPropsProvider }, { kEobBaseTurnUndeadEffect, kEobBaseTurnUndeadEffectProvider }, + { kEobBaseBurningHandsDest, kEobBaseBurningHandsDestProvider }, { kEob1MainMenuStrings, kEob1MainMenuStringsProvider }, { kEob1DoorShapeDefs, kEob1DoorShapeDefsProvider }, diff --git a/dists/engine-data/kyra.dat b/dists/engine-data/kyra.dat index 7cbd3b6c2c153d7d9f5941acd0e0c3afa7383c3b..494a39591a81f63efb18212e7675e0c7a2f6f44c 100644 GIT binary patch delta 27901 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 delta 27789 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 ASpWb4 diff --git a/engines/kyra/chargen.cpp b/engines/kyra/chargen.cpp index c8040ed9de22..681412915df7 100644 --- a/engines/kyra/chargen.cpp +++ b/engines/kyra/chargen.cpp @@ -236,7 +236,7 @@ bool CharacterGenerator::start(EobCharacter *characters, uint8 ***faceShapes) { } void CharacterGenerator::init() { - _screen->loadEobBitmap("CHARGENA", 3, 3); + _screen->loadShapeSetBitmap("CHARGENA", 3, 3); if (_faceShapes) { for (int i = 0; i < 44; i++) delete[] _faceShapes[i]; @@ -248,8 +248,8 @@ void CharacterGenerator::init() { _faceShapes[i] = _screen->encodeShape((i % 10) << 2, (i / 10) << 5, 4, 32, true); _screen->_curPage = 0; - _screen->loadEobCpsFileToPage("CHARGEN", 0, 3, 3, 0); - _screen->loadEobBitmap("CHARGENB", 3, 3); + _screen->loadEobBitmap("CHARGEN", 0, 3, 3, 0); + _screen->loadShapeSetBitmap("CHARGENB", 3, 3); if (_chargenMagicShapes) { for (int i = 0; i < 10; i++) delete[] _chargenMagicShapes[i]; diff --git a/engines/kyra/eob1.cpp b/engines/kyra/eob1.cpp index 95052311ff08..762b2d1bd102 100644 --- a/engines/kyra/eob1.cpp +++ b/engines/kyra/eob1.cpp @@ -78,6 +78,9 @@ Common::Error EobEngine::init() { _scriptTimersCount = 1; + //_wllWallFlags[132] = 0x1f; + _wllWallFlags[133] = 1; + return Common::kNoError; } @@ -341,8 +344,25 @@ void EobEngine::replaceMonster(int unit, uint16 block, int pos, int dir, int typ } } +void EobEngine::updateScriptTimersExtra() { + int cnt = 0; + for (int i = 1; i < 30; i++) { + if (_monsters[i].hitPointsCur <= 0) + cnt++; + } + + if (!cnt) { + for (int i = 1; i < 30; i++) { + if (getBlockDistance(_monsters[i].block, _currentBlock) > 3) { + killMonster(&_monsters[i], true); + break; + } + } + } +} + void EobEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2) { - _screen->loadEobBitmap("DOOR", 5, 3); + _screen->loadShapeSetBitmap("DOOR", 5, 3); _screen->_curPage = 2; if (doorType1 != 0xff) { @@ -351,8 +371,8 @@ void EobEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int s _doorShapes[shapeId1 + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3]); enc = &_doorSwitchShapeEncodeDefs[(doorType1 * 3 + i) << 2]; _doorSwitches[shapeId1 + i].shp = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3]); - _doorSwitches[shapeId1 + i].x = _doorSwitchCoords[doorType1 << 1]; - _doorSwitches[shapeId1 + i].y = _doorSwitchCoords[(doorType1 << 1) + 1]; + _doorSwitches[shapeId1 + i].x = _doorSwitchCoords[doorType1 * 6 + i * 2]; + _doorSwitches[shapeId1 + i].y = _doorSwitchCoords[doorType1 * 6 + i * 2 + 1]; } } @@ -362,8 +382,8 @@ void EobEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int s _doorShapes[shapeId2 + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3]); enc = &_doorSwitchShapeEncodeDefs[(doorType2 * 3 + i) << 2]; _doorSwitches[shapeId2 + i].shp = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3]); - _doorSwitches[shapeId2 + i].x = _doorSwitchCoords[doorType2 << 1]; - _doorSwitches[shapeId2 + i].y = _doorSwitchCoords[(doorType2 << 1) + 1]; + _doorSwitches[shapeId2 + i].x = _doorSwitchCoords[doorType2 * 6 + i * 2]; + _doorSwitches[shapeId2 + i].y = _doorSwitchCoords[doorType2 * 6 + i * 2+ 1]; } } @@ -383,7 +403,7 @@ void EobEngine::drawDoorIntern(int type, int index, int x, int y, int w, int wal case 4: case 5: case 6: - y = _dscDoorY2[mDim] - shp[1]; + y = _dscDoorY6[mDim] - shp[1]; d1 = _dscDoorCoordsExt[index << 1] >> 3; d2 = _dscDoorCoordsExt[(index << 1) + 1] >> 3; if (_shpDmX1 > d1) @@ -403,7 +423,7 @@ void EobEngine::drawDoorIntern(int type, int index, int x, int y, int w, int wal case 7: case 8: case 9: - y = _dscDoorY3[mDim] - _doorShapes[shapeIndex + 3][1]; + y = _dscDoorY7[mDim] - _doorShapes[shapeIndex + 3][1]; d1 = x - (_doorShapes[shapeIndex + 3][2] << 2); x -= (shp[2] << 2); drawBlockObject(0, 2, _doorShapes[shapeIndex + 3], d1, y, 5); diff --git a/engines/kyra/eob1.h b/engines/kyra/eob1.h index 8b805c00e81d..21543ddfa518 100644 --- a/engines/kyra/eob1.h +++ b/engines/kyra/eob1.h @@ -81,6 +81,7 @@ friend class GUI_Eob; // Monsters void replaceMonster(int unit, uint16 block, int d, int dir, int type, int shpIndex, int mode, int h2, int randItem, int fixedItem); + void updateScriptTimersExtra(); // Level void loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2); @@ -94,6 +95,7 @@ friend class GUI_Eob; const uint8 *_dscDoorY4; const uint8 *_dscDoorY5; const uint8 *_dscDoorY6; + const uint8 *_dscDoorY7; const uint8 *_doorShapeEncodeDefs; const uint8 *_doorSwitchShapeEncodeDefs; diff --git a/engines/kyra/eob2.cpp b/engines/kyra/eob2.cpp index 2b4cf46afed4..9efc558b7fa9 100644 --- a/engines/kyra/eob2.cpp +++ b/engines/kyra/eob2.cpp @@ -159,7 +159,7 @@ void DarkMoonEngine::updateUsedCharacterHandItem(int charIndex, int slot) { void DarkMoonEngine::generateMonsterPalettes(const char *file, int16 monsterIndex) { int cp = _screen->setCurPage(2); - _screen->loadEobBitmap(file, 3, 3); + _screen->loadShapeSetBitmap(file, 3, 3); uint8 tmpPal[16]; uint8 newPal[16]; @@ -291,7 +291,7 @@ bool DarkMoonEngine::killMonsterExtra(EobMonsterInPlay *m) { } const uint8 *DarkMoonEngine::loadDoorShapes(const char *filename, int doorIndex, const uint8 *shapeDefs) { - _screen->loadEobBitmap(filename, 3, 3); + _screen->loadShapeSetBitmap(filename, 3, 3); for (int i = 0; i < 3; i++) { _doorShapes[doorIndex * 3 + i] = _screen->encodeShape(READ_LE_UINT16(shapeDefs), READ_LE_UINT16(shapeDefs + 2), READ_LE_UINT16(shapeDefs + 4), READ_LE_UINT16(shapeDefs + 6)); shapeDefs += 8; diff --git a/engines/kyra/eobcommon.cpp b/engines/kyra/eobcommon.cpp index f323dd4e17b5..3e1001d2bdc4 100644 --- a/engines/kyra/eobcommon.cpp +++ b/engines/kyra/eobcommon.cpp @@ -98,6 +98,7 @@ EobCoreEngine::EobCoreEngine(OSystem *system, const GameFlags &flags) : LolEobBa _dscDoorScaleMult2 = 0; _dscDoorScaleMult3 = 0; _dscDoorY1 = 0; + _dscDoorXE = 0; _color9 = 17; _color10 = 23; @@ -137,6 +138,7 @@ EobCoreEngine::EobCoreEngine(OSystem *system, const GameFlags &flags) : LolEobBa _spellAnimBuffer = 0; _clericSpellOffset = 0; _restPartyElapsedTime = 0; + _allowSkip = false; _rrCount = 0; memset(_rrNames, 0, 10 * sizeof(const char*)); @@ -980,7 +982,7 @@ void EobCoreEngine::neutralizePoison(int character) { } void EobCoreEngine::npcSequence(int npcIndex) { - _screen->loadEobBitmap("OUTTAKE", 5, 3); + _screen->loadShapeSetBitmap("OUTTAKE", 5, 3); _screen->copyRegion(0, 0, 0, 0, 176, 120, 0, 6, Screen::CR_NO_P_CHECK); drawNpcScene(npcIndex); @@ -1019,7 +1021,7 @@ void EobCoreEngine::initNpc(int npcIndex) { c->inventory[i] = duplicateItem(c->inventory[i]); } - _screen->loadEobBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3); + _screen->loadShapeSetBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3); _screen->_curPage = 2; c->faceShape = _screen->encodeShape(npcIndex << 2, _flags.gameID == GI_EOB2 ? 0 : 160, 4, 32, true); _screen->_curPage = 0; @@ -1257,7 +1259,7 @@ void EobCoreEngine::drawSequenceBitmap(const char *file, int destRect, int x1, i if (scumm_stricmp(_dialogueLastBitmap, file)) { if (!destRect) { if (!(flags & 1)) { - _screen->loadEobCpsFileToPage("BORDER", 0, 3, 3, 2); + _screen->loadEobBitmap("BORDER", 0, 3, 3, 2); _screen->copyRegion(0, 0, 0, 0, 184, 121, 2, page, Screen::CR_NO_P_CHECK); } else { _screen->copyRegion(0, 0, 0, 0, 184, 121, 0, page, Screen::CR_NO_P_CHECK); @@ -1267,7 +1269,7 @@ void EobCoreEngine::drawSequenceBitmap(const char *file, int destRect, int x1, i _screen->copyRegion(0, 0, 0, 0, 184, 121, 2, 6, Screen::CR_NO_P_CHECK); } - _screen->loadEobCpsFileToPage(file, 0, 3, 3, 2); + _screen->loadEobBitmap(file, 0, 3, 3, 2); strcpy(_dialogueLastBitmap, file); } @@ -1407,16 +1409,12 @@ bool EobCoreEngine::restParty_checkSpellsToLearn() { return false; } -void EobCoreEngine::restParty_npc() { - -} - bool EobCoreEngine::restParty_extraAbortCondition() { return false; } void EobCoreEngine::delay(uint32 millis, bool, bool) { - while (millis && !shouldQuit() && !skipFlag()) { + while (millis && !shouldQuit() && !(_allowSkip && skipFlag())) { updateInput(); uint32 step = MIN(millis, (_tickLength / 5)); _system->delayMillis(step); @@ -1501,7 +1499,75 @@ int EobCoreEngine::countResurrectionCandidates() { } void EobCoreEngine::seq_portal() { - //_portalSeq + releaseDoorShapes(); + releaseMonsterShapes(0, 36); + releaseDecorations(); + + uint8 *shapes1[5]; + uint8 *shapes2[5]; + uint8 *shapes3[5]; + uint8 *shape0; + + _screen->loadShapeSetBitmap("PORTALA", 5, 3); + + for (int i = 0; i < 5; i++) { + shapes1[i] = _screen->encodeShape(i * 3, 0, 3, 75); + shapes2[i] = _screen->encodeShape(i * 3, 80, 3, 75); + shapes3[i] = _screen->encodeShape(15, i * 18, 15, 18); + } + + shape0 = _screen->encodeShape(30, 0, 8, 77); + _screen->loadEobBitmap("PORTALB", 0, 5, 3, 2); + + snd_playSoundEffect(33); + snd_playSoundEffect(19); + _screen->copyRegion(24, 0, 24, 0, 144, 104, 2, 5, Screen::CR_NO_P_CHECK); + _screen->copyRegion(24, 0, 24, 0, 144, 104, 0, 2, Screen::CR_NO_P_CHECK); + _screen->drawShape(2, shapes3[0], 28, 9, 0); + _screen->drawShape(2, shapes1[0], 34, 28, 0); + _screen->drawShape(2, shapes2[0], 120, 28, 0); + _screen->drawShape(2, shape0, 56, 27, 0); + _screen->crossFadeRegion(24, 0, 24, 0, 144, 104, 2, 0); + _screen->copyRegion(24, 0, 24, 0, 144, 104, 5, 2, Screen::CR_NO_P_CHECK); + delay(30 * _tickLength); + + for (const int8 *pos = _portalSeq; *pos > -1 && !shouldQuit(); ) { + int s = *pos++; + _screen->drawShape(0, shapes3[s], 28, 9, 0); + _screen->drawShape(0, shapes1[s], 34, 28, 0); + _screen->drawShape(0, shapes2[s], 120, 28, 0); + + if ((s == 1) && (pos >= _portalSeq + 3)) { + if (*(pos - 3) == 0) { + snd_playSoundEffect(24); + snd_playSoundEffect(86); + } + } + + s = *pos++; + if (s == 0) { + _screen->drawShape(0, shape0, 56, 27, 0); + } else { + s--; + _screen->copyRegion((s % 5) << 6, s / 5 * 77, 56, 27, 64, 77, 2, 0, Screen::CR_NO_P_CHECK); + if (s == 0) + snd_playSoundEffect(31); + else if (s == 3) { + if (*(pos - 2) == 3) + snd_playSoundEffect(90); + } + } + + _screen->updateScreen(); + delay(2 * _tickLength); + } + + delete[] shape0; + for (int i = 0; i < 5; i++) { + delete[] shapes1[i]; + delete[] shapes2[i]; + delete[] shapes3[i]; + } } bool EobCoreEngine::checkPassword() { @@ -1830,10 +1896,13 @@ bool EobCoreEngine::monsterAttackHitTest(EobMonsterInPlay *m, int charIndex) { int r = rollDice(1, 20); if (r != 20) { + // Prot from evil if (_characters[charIndex].effectFlags & 0x800) r -= 2; + // blur if (_characters[charIndex].effectFlags & 0x10) r -= 2; + // prayer if (_partyEffectFlags & 0x8000) r--; } diff --git a/engines/kyra/eobcommon.h b/engines/kyra/eobcommon.h index 7ebc883f3e25..33b4a2fc2d62 100644 --- a/engines/kyra/eobcommon.h +++ b/engines/kyra/eobcommon.h @@ -508,6 +508,7 @@ friend class CharacterGenerator; void updateMonsters(int unit); void updateMonsterDest(EobMonsterInPlay *m); void updateMonsterDest2(EobMonsterInPlay *m); + void updateAllMonsterDests(); void turnFriendlyMonstersHostile(); int getNextMonsterDirection(int curBlock, int destBlock); int getNextMonsterPos(EobMonsterInPlay *m, int block); @@ -619,11 +620,12 @@ friend class CharacterGenerator; const uint8 *_dscDoorScaleMult2; const uint8 *_dscDoorScaleMult3; const uint8 *_dscDoorY1; + const uint8 *_dscDoorXE; const uint8 *_wllFlagPreset; int _wllFlagPresetSize; const uint8 *_teleporterShapeCoords; - const uint8 *_portalSeq; + const int8 *_portalSeq; // Script void runLevelScript(int block, int flags); @@ -633,6 +635,7 @@ friend class CharacterGenerator; const uint8 *initScriptTimers(const uint8 *pos); void updateScriptTimers(); + virtual void updateScriptTimersExtra() {} EobInfProcessor *_inf; int _stepCounter; @@ -803,7 +806,7 @@ friend class CharacterGenerator; int restParty_getCharacterWithLowestHp(); bool restParty_checkHealSpells(int charIndex); bool restParty_checkSpellsToLearn(); - virtual void restParty_npc(); + virtual void restParty_npc() {} virtual bool restParty_extraAbortCondition(); // misc @@ -840,6 +843,8 @@ friend class CharacterGenerator; const char *_rrNames[10]; int8 _rrId[10]; + bool _allowSkip; + Screen_Eob *_screen; GUI_Eob *_gui; @@ -881,6 +886,7 @@ friend class CharacterGenerator; int _dstMonsterIndex; bool _preventMonsterFlash; int16 _foundMonstersArray[5]; + int8 _monsterBlockPosArray[6]; // magic void useMagicBookOrSymbol(int charIndex, int type); @@ -969,6 +975,7 @@ friend class CharacterGenerator; void spellCallback_start_heal(); void spellCallback_start_layOnHands(); void spellCallback_start_turnUndead(); + bool spellCallback_end_kuotoaAttack(EobFlyingObject *fo); bool spellCallback_end_unk1Passive(EobFlyingObject *fo); bool spellCallback_end_unk2Passive(EobFlyingObject *fo); bool spellCallback_end_deathSpellPassive(EobFlyingObject *fo); @@ -1038,6 +1045,7 @@ friend class CharacterGenerator; const uint8 *_magicFlightObjectProperties; const uint8 *_turnUndeadEffect; + const uint8 *_burningHandsDest; // Menu EobMenuDef *_menuDefs; diff --git a/engines/kyra/gui_eob.cpp b/engines/kyra/gui_eob.cpp index 9dec1584c535..04b863c325aa 100644 --- a/engines/kyra/gui_eob.cpp +++ b/engines/kyra/gui_eob.cpp @@ -150,7 +150,7 @@ Button *EobCoreEngine::gui_getButton(Button *buttonList, int index) { } void EobCoreEngine::gui_drawPlayField(bool refresh) { - _screen->loadEobCpsFileToPage("PLAYFLD", 0, 5, 3, 2); + _screen->loadEobBitmap("PLAYFLD", 0, 5, 3, 2); int cp = _screen->setCurPage(2); gui_drawCompass(true); @@ -163,7 +163,7 @@ void EobCoreEngine::gui_drawPlayField(bool refresh) { if (!_loading) _screen->updateScreen(); - _screen->loadEobCpsFileToPage("INVENT", 0, 5, 3, 2); + _screen->loadEobBitmap("INVENT", 0, 5, 3, 2); } void EobCoreEngine::gui_restorePlayField() { diff --git a/engines/kyra/magic_eob.cpp b/engines/kyra/magic_eob.cpp index 3eb759141308..12d25245451f 100644 --- a/engines/kyra/magic_eob.cpp +++ b/engines/kyra/magic_eob.cpp @@ -142,7 +142,7 @@ void EobCoreEngine::usePotion(int charIndex, int weaponSlot) { } void EobCoreEngine::useWand(int charIndex, int weaponSlot) { - int v = _items[_characters[charIndex].inventory[weaponSlot]].value - 1; + int v = _items[_characters[charIndex].inventory[weaponSlot]].value; if (!v) { _txt->printMessage(_wandStrings[0]); return; @@ -535,7 +535,7 @@ bool EobCoreEngine::magicObjectDamageHit(EobFlyingObject *fo, int dcTimes, int d if (!fo->item && (_characters[c].effectFlags & 8)) { res = true; } else { - if ((_characters[c].flags & 1) && hitTest && !monsterAttackHitTest(&_monsters[0], c)) { + if ((_characters[c].flags & 1) && (!hitTest || monsterAttackHitTest(&_monsters[0], c))) { int dmg = rollDice(dcTimes, dcPips, dcOffs) * level; res = true; calcAndInflictCharacterDamage(c, 0, 0, dmg, dmgFlag, s, dmgType); @@ -638,7 +638,31 @@ void EobCoreEngine::spellCallback_start_armor() { } void EobCoreEngine::spellCallback_start_burningHands() { + static const int16 bX[] = { 0, 152, 24, 120, 56, 88 }; + static const int8 bY[] = { 64, 64, 56, 56, 56, 56 }; + for (int i = 0; i < 6; i++) + drawBlockObject(i & 1, 0, _firebeamShapes[(5 - i) >> 1], bX[i], bY[i], 0); + _screen->updateScreen(); + delay(2 * _tickLength); + + int cl = getCharacterMageLevel(_openBookChar); + int bl = calcNewBlockPosition(_currentBlock, _currentDirection); + + const int8 *pos = getMonsterBlockPositions(bl); + _preventMonsterFlash = true; + + int numDest = (_flags.gameID == GI_EOB1) ? 2 : 6; + const uint8 *d = &_burningHandsDest[_currentDirection * (_flags.gameID == GI_EOB1 ? 2 : 8)]; + + for (int i = 0; i < numDest; i++, d++) { + if (pos[*d] == -1) + continue; + calcAndInflictMonsterDamage(&_monsters[pos[*d]], 1, 3, cl << 1, 0x21, 4, 0); + } + + updateAllMonsterShapes(); + _sceneUpdateRequired = true; } void EobCoreEngine::spellCallback_start_detectMagic() { @@ -949,6 +973,10 @@ void EobCoreEngine::spellCallback_start_turnUndead() { _preventMonsterFlash = false; } +bool EobCoreEngine::spellCallback_end_kuotoaAttack(EobFlyingObject *fo) { + return magicObjectDamageHit(fo, 0, 0, 12, 1); +} + bool EobCoreEngine::spellCallback_end_unk1Passive(EobFlyingObject *fo) { bool res = false; if (_partyEffectFlags & 0x20000) { diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h index 0f1539b6100a..f80fdd91c27a 100644 --- a/engines/kyra/resource.h +++ b/engines/kyra/resource.h @@ -405,11 +405,13 @@ enum KyraResources { kEobBaseDscDoorScaleMult5, kEobBaseDscDoorScaleMult6, kEobBaseDscDoorType5Offs, + kEobBaseDscDoorXE, kEobBaseDscDoorY1, kEobBaseDscDoorY3, kEobBaseDscDoorY4, kEobBaseDscDoorY5, kEobBaseDscDoorY6, + kEobBaseDscDoorY7, kEobBaseDscDoorCoordsExt, kEobBaseDscItemPosIndex, @@ -475,6 +477,7 @@ enum KyraResources { kEobBaseSpellProperties, kEobBaseMagicFlightProps, kEobBaseTurnUndeadEffect, + kEobBaseBurningHandsDest, kEob1MainMenuStrings, kEob1DoorShapeDefs, diff --git a/engines/kyra/saveload_eob.cpp b/engines/kyra/saveload_eob.cpp index 43a230e590b9..88f0c1c64ee1 100644 --- a/engines/kyra/saveload_eob.cpp +++ b/engines/kyra/saveload_eob.cpp @@ -205,7 +205,7 @@ Common::Error EobCoreEngine::loadGameState(int slot) { setupCharacterTimers(); - _screen->loadEobBitmap("CHARGENA", 3, 3); + _screen->loadShapeSetBitmap("CHARGENA", 3, 3); for (int i = 0; i < 6; i++) { EobCharacter *c = &_characters[i]; if (!c->flags || c->portrait < 0) @@ -213,12 +213,12 @@ Common::Error EobCoreEngine::loadGameState(int slot) { c->faceShape = _screen->encodeShape((c->portrait % 10) << 2, (c->portrait / 10) << 5, 4, 32, true); } - _screen->loadEobBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3); + _screen->loadShapeSetBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3); for (int i = 0; i < 6; i++) { EobCharacter *c = &_characters[i]; if (!c->flags || c->portrait >= 0) continue; - c->faceShape = _screen->encodeShape(-(c->portrait + 1), _flags.gameID == GI_EOB2 ? 0 : 160, 4, 32, true); + c->faceShape = _screen->encodeShape((-(c->portrait + 1)) << 2, _flags.gameID == GI_EOB2 ? 0 : 160, 4, 32, true); } _screen->_curPage = 0; diff --git a/engines/kyra/scene_eob.cpp b/engines/kyra/scene_eob.cpp index e6b3c2639454..4c1bb1636f75 100644 --- a/engines/kyra/scene_eob.cpp +++ b/engines/kyra/scene_eob.cpp @@ -690,7 +690,7 @@ void EobCoreEngine::loadLevel(int level, int sub) { } loadVcnData(gfxFile.c_str(), 0); - _screen->loadEobCpsFileToPage("INVENT", 0, 5, 3, 2); + _screen->loadEobBitmap("INVENT", 0, 5, 3, 2); enableSysTimer(2); _sceneDrawPage1 = 2; @@ -734,15 +734,13 @@ Common::String EobCoreEngine::initLevelData(int sub){ pos += 13; } - //////// _screen->loadPalette(tmpStr, _screen->getPalette(0)); - if (_flags.gameID == GI_EOB1) { pos += 11; _screen->setShapeFadeMode(0, false); _screen->setShapeFadeMode(1, false); } - else - _screen->loadPalette(tmpStr.c_str(), _screen->getPalette(0)); + + _screen->loadPalette(tmpStr.c_str(), _screen->getPalette(0)); Palette backupPal(256); backupPal.copy(_screen->getPalette(0), 224, 32, 224); @@ -796,9 +794,8 @@ Common::String EobCoreEngine::initLevelData(int sub){ for (int i = 0; i < 2; i++) { if (_flags.gameID == GI_EOB1) { - if (*pos == 0xFF) - continue; - loadMonsterShapes((const char *)(pos + 1), i * 18, false, *pos * 18); + if (*pos != 0xFF) + loadMonsterShapes((const char *)(pos + 1), i * 18, false, *pos * 18); pos += 13; } else { if (*pos++ != 0xEC) @@ -880,7 +877,7 @@ void EobCoreEngine::loadBlockProperties(const char *mazFile) { } void EobCoreEngine::loadDecorations(const char *cpsFile, const char *decFile) { - _screen->loadEobBitmap(cpsFile, 3, 3); + _screen->loadShapeSetBitmap(cpsFile, 3, 3); Common::SeekableReadStream *s = _res->createReadStream(decFile); _levelDecorationDataSize = s->readUint16LE(); @@ -1142,7 +1139,10 @@ void EobCoreEngine::drawDecorations(int index) { int EobCoreEngine::calcNewBlockPositionAndTestPassability(uint16 curBlock, uint16 direction) { uint16 b = calcNewBlockPosition(curBlock, direction); int w = _levelBlockProperties[b].walls[direction ^ 2]; + int f = _wllWallFlags[w]; + if (!f) + assert (w < (_flags.gameID == GI_EOB1 ? 70 : 80)); if (w == 74 && _currentBlock == curBlock) { for (int i = 0; i < 5; i++) { @@ -1163,7 +1163,7 @@ void EobCoreEngine::notifyBlockNotPassable() { } void EobCoreEngine::moveParty(uint16 block) { - //processMonstersUnk1(); + updateAllMonsterDests(); uint16 old = _currentBlock; _currentBlock = block; @@ -1176,10 +1176,10 @@ void EobCoreEngine::moveParty(uint16 block) { runLevelScript(block, 1); - if (_levelBlockProperties[block].walls[0] == 26) + if (_flags.gameID == GI_EOB2 && _levelBlockProperties[block].walls[0] == 26) memset(_levelBlockProperties[block].walls, 0, 4); - //processMonstersUnk1(); + updateAllMonsterDests(); _stepCounter++; //_keybControlUnk = -1; _sceneUpdateRequired = true; @@ -1192,6 +1192,14 @@ int EobCoreEngine::clickedDoorSwitch(uint16 block, uint16 direction) { SpriteDecoration *d = &_doorSwitches[((v > 12 && v < 23) || v == 31) ? 3 : 0]; int x1 = d->x + _dscShapeCoords[138] - 4; int y1 = d->y - 4; + + if (_flags.gameID == GI_EOB1 && _currentLevel >= 4 && _currentLevel <= 6) { + if (v >= 30) + x1 += 4; + else + x1 += ((v - _dscDoorXE[v]) * 9); + } + if (!posWithinRect(_mouseX, _mouseY, x1, y1, x1 + (d->shp[2] << 3) + 8, y1 + d->shp[1] + 8) && (_clickedSpecialFlag == 0x40)) return clickedDoorNoPry(block, direction); diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp index 5e424b481703..79edfe9d72aa 100644 --- a/engines/kyra/screen.cpp +++ b/engines/kyra/screen.cpp @@ -2971,6 +2971,9 @@ bool Screen::loadPalette(const char *filename, Palette &pal) { } else if (_vm->gameFlags().platform == Common::kPlatformPC98 && _use16ColorMode) { numCols = stream->size() / Palette::kPC98BytesPerColor; pal.loadPC98Palette(*stream, 0, MIN(maxCols, numCols)); + } else if (_vm->gameFlags().gameID == GI_EOB1) { + numCols = stream->size() / Palette::kVGABytesPerColor; + pal.loadVGAPalette7bit(*stream, 0, MIN(maxCols, numCols)); } else { numCols = stream->size() / Palette::kVGABytesPerColor; pal.loadVGAPalette(*stream, 0, MIN(maxCols, numCols)); @@ -3438,6 +3441,18 @@ void Palette::loadVGAPalette(Common::ReadStream &stream, int startIndex, int col stream.read(_palData + startIndex * 3, colors * 3); } +void Palette::loadVGAPalette7bit(Common::ReadStream &stream, int startIndex, int colors) { + assert(startIndex + colors <= _numColors); + + stream.read(_palData + startIndex * 3, colors * 3); + uint8 *pos = _palData + startIndex * 3; + for (int i = 0 ; i < colors; i++) { + *pos++ &= 0x3f; + *pos++ &= 0x3f; + *pos++ &= 0x3f; + } +} + void Palette::loadAmigaPalette(Common::ReadStream &stream, int startIndex, int colors) { assert(startIndex + colors <= _numColors); diff --git a/engines/kyra/screen.h b/engines/kyra/screen.h index cb7d73d1c127..d4f0dc97da54 100644 --- a/engines/kyra/screen.h +++ b/engines/kyra/screen.h @@ -251,6 +251,11 @@ class Palette { */ void loadVGAPalette(Common::ReadStream &stream, int startIndex, int colors); + /** + * Load a VGA palette from the given stream masking out the upper bit. + */ + void loadVGAPalette7bit(Common::ReadStream &stream, int startIndex, int colors); + /** * Load a AMIGA palette from the given stream. */ diff --git a/engines/kyra/screen_eob.cpp b/engines/kyra/screen_eob.cpp index d3c2187177f5..ccbc2a166b43 100644 --- a/engines/kyra/screen_eob.cpp +++ b/engines/kyra/screen_eob.cpp @@ -145,12 +145,15 @@ void Screen_Eob::printShadedText(const char *string, int x, int y, int col1, int printText(string, x, y, col1, 0); } -void Screen_Eob::loadEobBitmap(const char *file, int tempPage, int destPage) { - loadEobCpsFileToPage(file, 0, tempPage, destPage, -1); +void Screen_Eob::loadShapeSetBitmap(const char *file, int tempPage, int destPage) { + loadEobBitmap(file, 0, tempPage, destPage, -1); _curPage = 2; } -void Screen_Eob::loadEobCpsFileToPage(const char *file, const uint8 *ditheringData, int tempPage, int destPage, int copyToPage) { +void Screen_Eob::loadEobBitmap(const char *file, const uint8 *ditheringData, int tempPage, int destPage, int copyToPage) { + //Common::String tmp = file; + //if (_vm->game() == GI_EOB1 && tmp.equalsIgnoreCase("spider")) + // tmp += "1"; Common::String tmp = Common::String::format("%s.CPS", file); Common::SeekableReadStream *s = _vm->resource()->createReadStream(tmp); bool loadAlternative = false; @@ -170,7 +173,7 @@ void Screen_Eob::loadEobCpsFileToPage(const char *file, const uint8 *ditheringDa tmp.setChar('X', 0); s = _vm->resource()->createReadStream(tmp); if (!s) - error("Screen_Eob::loadEobCpsFileToPage(): CPS file loading failed."); + error("Screen_Eob::loadEobBitmap(): CPS file loading failed."); s->seek(768); loadFileDataToPage(s, destPage, 64000); delete s; diff --git a/engines/kyra/screen_eob.h b/engines/kyra/screen_eob.h index 19cbdb5b4afb..909a56146292 100644 --- a/engines/kyra/screen_eob.h +++ b/engines/kyra/screen_eob.h @@ -51,8 +51,8 @@ class Screen_Eob : public Screen{ void loadFileDataToPage(Common::SeekableReadStream *s, int pageNum, uint32 size); void printShadedText(const char *string, int x, int y, int col1, int col2); - void loadEobCpsFileToPage(const char *file, const uint8 *ditheringData, int tempPage, int destPage, int copyToPage); - void loadEobBitmap(const char *file, int tempPage, int destPage); + void loadEobBitmap(const char *file, const uint8 *ditheringData, int tempPage, int destPage, int copyToPage); + void loadShapeSetBitmap(const char *file, int tempPage, int destPage); uint8 *encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool flag = false); void drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd = -1, int flags = 0, ...); diff --git a/engines/kyra/script_eob.cpp b/engines/kyra/script_eob.cpp index 715970074a1d..2b86aa8d516c 100644 --- a/engines/kyra/script_eob.cpp +++ b/engines/kyra/script_eob.cpp @@ -54,7 +54,7 @@ const uint8 *EobCoreEngine::initScriptTimers(const uint8 *pos) { while (((int16)READ_LE_UINT16(pos)) != -1) { _scriptTimers[_scriptTimersCount].func = READ_LE_UINT16(pos); pos += 2; - uint16 ticks = (int16)READ_LE_UINT16(pos) * 18; + uint16 ticks = READ_LE_UINT16(pos) * 18; _scriptTimers[_scriptTimersCount].ticks = ticks; pos += 2; _scriptTimers[_scriptTimersCount++].next = _system->getMillis() + ticks * _tickLength; @@ -64,20 +64,26 @@ const uint8 *EobCoreEngine::initScriptTimers(const uint8 *pos) { } void EobCoreEngine::updateScriptTimers() { - if ((_scriptTimersMode & 1) && _stepsUntilScriptCall && _stepCounter > _stepsUntilScriptCall) { + bool timerUpdate = false; + if ((_scriptTimersMode & 2) && _stepsUntilScriptCall && _stepCounter > _stepsUntilScriptCall) { _inf->run(0, 0x20); _stepCounter = 0; + timerUpdate = true; } - if (_scriptTimersMode & 2) { + if (_scriptTimersMode & 1) { for (int i = 0; i < _scriptTimersCount; i++) { if (_scriptTimers[i].next < _system->getMillis()) { _inf->run(_scriptTimers[i].func, _flags.gameID == GI_EOB1 ? 0x20 : 0x80); _scriptTimers[i].next = _system->getMillis() + _scriptTimers[i].ticks * _tickLength; _sceneUpdateRequired = true; + timerUpdate = true; } } } + + if (timerUpdate) + updateScriptTimersExtra(); } EobInfProcessor::EobInfProcessor(EobCoreEngine *engine, Screen_Eob *screen) : _vm(engine), _screen(screen), @@ -211,13 +217,13 @@ bool EobInfProcessor::preventRest() const { void EobInfProcessor::loadState(Common::SeekableSubReadStreamEndian &in) { _preventRest = in.readByte(); for (int i = 0; i < 18; i++) - _flagTable[i] = in.readUint16BE(); + _flagTable[i] = in.readUint32BE(); } void EobInfProcessor::saveState(Common::OutSaveFile *out) { out->writeByte(_preventRest); for (int i = 0; i < 18; i++) - out->writeUint16BE(_flagTable[i]); + out->writeUint32BE(_flagTable[i]); } const char *EobInfProcessor::getString(uint16 index) { diff --git a/engines/kyra/sequences_eob1.cpp b/engines/kyra/sequences_eob1.cpp index dcfabfeddd13..8f360c071de3 100644 --- a/engines/kyra/sequences_eob1.cpp +++ b/engines/kyra/sequences_eob1.cpp @@ -43,7 +43,7 @@ int EobEngine::mainMenu() { switch (menuChoice) { case 0: _screen->loadPalette("EOBPAL.COL", _screen->getPalette(0)); - _screen->loadEobCpsFileToPage("INTRO", 0, 5, 3, 2); + _screen->loadEobBitmap("INTRO", 0, 5, 3, 2); _screen->setScreenPalette(_screen->getPalette(0)); _screen->_curPage = 2; of = _screen->setFont(Screen::FID_6_FNT); @@ -107,6 +107,7 @@ void EobEngine::seq_playOpeningCredits() { static const char *cmpList[] = { "WESTWOOD.CMP", "AND.CMP", "SSI.CMP", "PRESENT.CMP", "DAND.CMP" }; static const uint8 frameDelay[] = { 140, 50, 100, 50, 140 }; + _allowSkip = true; _screen->loadPalette("WESTWOOD.COL", _screen->getPalette(0)); _screen->setScreenPalette(_screen->getPalette(0)); @@ -125,14 +126,21 @@ void EobEngine::seq_playOpeningCredits() { } delay(50 * _tickLength); + _allowSkip = false; } void EobEngine::seq_playIntro() { + _allowSkip = true; //_sound->playTrack(2); + + _allowSkip = false; } void EobEngine::seq_playFinale() { + _allowSkip = true; + + _allowSkip = false; } } // End of namespace Kyra diff --git a/engines/kyra/sequences_eob2.cpp b/engines/kyra/sequences_eob2.cpp index 8ff5581b9d87..bba5a7a2204a 100644 --- a/engines/kyra/sequences_eob2.cpp +++ b/engines/kyra/sequences_eob2.cpp @@ -941,6 +941,7 @@ DarkmoonSequenceHelper::DarkmoonSequenceHelper(OSystem *system, DarkMoonEngine * _system->delayMillis(150); _vm->resetSkipFlag(true); + _vm->_allowSkip = true; } DarkmoonSequenceHelper::~DarkmoonSequenceHelper() { @@ -960,6 +961,7 @@ DarkmoonSequenceHelper::~DarkmoonSequenceHelper() { _system->delayMillis(150); _vm->resetSkipFlag(true); + _vm->_allowSkip = false; } void DarkmoonSequenceHelper::loadScene(int index, int pageNum) { diff --git a/engines/kyra/sprites_eob.cpp b/engines/kyra/sprites_eob.cpp index 3447b6bc7f81..802ba4e4c86f 100644 --- a/engines/kyra/sprites_eob.cpp +++ b/engines/kyra/sprites_eob.cpp @@ -54,7 +54,11 @@ int LolEobBaseEngine::getBlockDistance(uint16 block1, uint16 block2) { namespace Kyra { void EobCoreEngine::loadMonsterShapes(const char *filename, int monsterIndex, bool hasDecorations, int encodeTableIndex) { - _screen->loadEobBitmap(filename, 3, 3); + Common::String s = _flags.gameID == GI_EOB1 && !scumm_stricmp(filename, "spider") ? "spider1" : filename; + if (GI_EOB1 && !scumm_stricmp(filename, "rust")) + s += "1"; + + _screen->loadShapeSetBitmap(s.c_str(), 3, 3); const uint16 *enc = &_encodeMonsterShpTable[encodeTableIndex << 2]; for (int i = 0; i < 6; i++, enc += 4) @@ -275,14 +279,14 @@ void EobCoreEngine::updateAttackingMonsterFlags() { } const int8 *EobCoreEngine::getMonsterBlockPositions(uint16 block) { - static int8 pos[6]; - memset(pos, -1, sizeof(pos)); + memset(_monsterBlockPosArray, -1, sizeof(_monsterBlockPosArray)); for (int8 i = 0; i < 30; i++) { if (_monsters[i].block != block) continue; - pos[_monsters[i].pos] = i; + assert(_monsters[i].pos < sizeof(_monsterBlockPosArray)); + _monsterBlockPosArray[_monsters[i].pos] = i; } - return pos; + return _monsterBlockPosArray; } int EobCoreEngine::getClosestMonsterPos(int charIndex, int block) { @@ -770,6 +774,11 @@ void EobCoreEngine::updateMonsterDest2(EobMonsterInPlay *m) { m->dest = _currentBlock; } +void EobCoreEngine::updateAllMonsterDests() { + for (int i = 0; i < 30; i++) + updateMonsterDest(&_monsters[i]); +} + void EobCoreEngine::turnFriendlyMonstersHostile() { EobMonsterInPlay *m = 0; for (int i = 0; i < 30; i++) { diff --git a/engines/kyra/staticres_eob.cpp b/engines/kyra/staticres_eob.cpp index 9b15d4aea818..f07cd1186f38 100644 --- a/engines/kyra/staticres_eob.cpp +++ b/engines/kyra/staticres_eob.cpp @@ -448,7 +448,7 @@ void EobCoreEngine::initStaticResource() { _npcPreset = _staticres->loadEobNpcData(kEobBaseNpcPresets, temp); _teleporterShapeCoords = _staticres->loadRawData(kEobBaseDscTelptrShpCoords, temp); - _portalSeq = _staticres->loadRawData(kEobBasePortalSeqData, temp); + _portalSeq = (const int8*)_staticres->loadRawData(kEobBasePortalSeqData, temp); _mnDef = _staticres->loadRawData(kEobBaseManDef, temp); _mnWord = _staticres->loadStrings(kEobBaseManWord, _mnNumWord); _mnPrompt = _staticres->loadStrings(kEobBaseManPrompt, temp); @@ -493,6 +493,7 @@ void EobCoreEngine::initStaticResource() { _dscDoorScaleMult2 = _staticres->loadRawData(kEobBaseDscDoorScaleMult2, temp); _dscDoorScaleMult3 = _staticres->loadRawData(kEobBaseDscDoorScaleMult3, temp); _dscDoorY1 = _staticres->loadRawData(kEobBaseDscDoorY1, temp); + _dscDoorXE = _staticres->loadRawData(kEobBaseDscDoorXE, temp); _dscItemPosIndex= _staticres->loadRawData(kEobBaseDscItemPosIndex, temp); _dscItemShpX = (const int16*)_staticres->loadRawDataBe16(kEobBaseDscItemShpX, temp); @@ -533,6 +534,7 @@ void EobCoreEngine::initStaticResource() { _sparkEffectOfY = _staticres->loadRawData(kEobBaseSparkOfY, temp); _magicFlightObjectProperties = _staticres->loadRawData(kEobBaseMagicFlightProps, temp); _turnUndeadEffect = _staticres->loadRawData(kEobBaseTurnUndeadEffect, temp); + _burningHandsDest = _staticres->loadRawData(kEobBaseBurningHandsDest, 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 @@ -1002,7 +1004,8 @@ void EobCoreEngine::initSpells() { ec2(empty); ec(empty); ec2(empty); - ec(unk1Passive); + ec1(kuotoaAttack); + ec2(unk1Passive); ec2(empty); ec2(unk2Passive); ec(deathSpellPassive); @@ -1064,6 +1067,7 @@ void EobEngine::initStaticResource() { _dscDoorY4 = _staticres->loadRawData(kEobBaseDscDoorY4, temp); _dscDoorY5 = _staticres->loadRawData(kEobBaseDscDoorY5, temp); _dscDoorY6 = _staticres->loadRawData(kEobBaseDscDoorY6, temp); + _dscDoorY7 = _staticres->loadRawData(kEobBaseDscDoorY7, temp); _dscDoorCoordsExt = (const int16*)_staticres->loadRawDataBe16(kEobBaseDscDoorCoordsExt, temp); _monsterDistAttType10 = _staticres->loadRawData(kEob1MonsterDistAttType10, temp); diff --git a/engines/kyra/timer_eob.cpp b/engines/kyra/timer_eob.cpp index 9e03bdebcfab..ba4d6a247568 100644 --- a/engines/kyra/timer_eob.cpp +++ b/engines/kyra/timer_eob.cpp @@ -210,7 +210,7 @@ void EobCoreEngine::advanceTimers(uint32 millis) { setupCharacterTimers(); - if (_scriptTimersMode & 2) { + if (_scriptTimersMode & 1) { for (int i = 0; i < _scriptTimersCount; i++) { if (_scriptTimers[i].next > ct) { uint32 chrt = _scriptTimers[i].next - ct; @@ -371,7 +371,7 @@ void EobCoreEngine::timerUpdateTeleporters(int timerNum) { _teleporterPulse ^= 1; for (int i = 0; i < 18; i++) { uint8 w = _visibleBlocks[i]->walls[_sceneDrawVarDown]; - if (w == 44 || w == 74) { + if ((_flags.gameID == GI_EOB1 && w == 52) || (_flags.gameID == GI_EOB2 && (w == 44 || w == 74))) { _sceneUpdateRequired = true; return; }