Initial commit
This commit is contained in:
3
engines/lure/POTFILES
Normal file
3
engines/lure/POTFILES
Normal file
@@ -0,0 +1,3 @@
|
||||
engines/lure/lure.cpp
|
||||
engines/lure/detection.cpp
|
||||
engines/lure/metaengine.cpp
|
||||
299
engines/lure/animseq.cpp
Normal file
299
engines/lure/animseq.cpp
Normal file
@@ -0,0 +1,299 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lure/animseq.h"
|
||||
#include "lure/decode.h"
|
||||
#include "lure/events.h"
|
||||
#include "lure/lure.h"
|
||||
#include "lure/palette.h"
|
||||
#include "lure/sound.h"
|
||||
#include "common/endian.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
// delay
|
||||
// Delays for a given number of milliseconds. If it returns true, it indicates that
|
||||
// Escape has been pressed, and the introduction should be aborted.
|
||||
|
||||
AnimAbortType AnimationSequence::delay(uint32 milliseconds) {
|
||||
Events &events = Events::getReference();
|
||||
uint32 delayCtr = g_system->getMillis() + milliseconds;
|
||||
|
||||
while (g_system->getMillis() < delayCtr) {
|
||||
while (events.pollEvent()) {
|
||||
if ((events.type() == Common::EVENT_CUSTOM_ENGINE_ACTION_START) && (events.event().customType != kActionNone)) {
|
||||
if (events.event().customType == kActionEscape)
|
||||
return ABORT_END_INTRO;
|
||||
else
|
||||
return ABORT_NEXT_SCENE;
|
||||
} else if (events.type() == Common::EVENT_LBUTTONDOWN) {
|
||||
return ABORT_NEXT_SCENE;
|
||||
} else if ((events.type() == Common::EVENT_QUIT) || (events.type() == Common::EVENT_RETURN_TO_LAUNCHER)) {
|
||||
return ABORT_END_INTRO;
|
||||
} else if (events.type() == Common::EVENT_MAINMENU) {
|
||||
return ABORT_NONE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint32 delayAmount = delayCtr - g_system->getMillis();
|
||||
if (delayAmount > 10) delayAmount = 10;
|
||||
g_system->delayMillis(delayAmount);
|
||||
}
|
||||
return ABORT_NONE;
|
||||
}
|
||||
|
||||
// egaDecodeFrame
|
||||
// Decodes a single frame of a EGA animation sequence
|
||||
|
||||
void AnimationSequence::egaDecodeFrame(byte *&pPixels) {
|
||||
Screen &screen = Screen::getReference();
|
||||
byte *screenData = screen.screen_raw();
|
||||
|
||||
// Skip over the list of blocks that are changed
|
||||
int numBlocks = *pPixels++;
|
||||
pPixels += numBlocks;
|
||||
|
||||
// Loop through the list of same/changed pixel ranges
|
||||
int len = *pPixels++;
|
||||
int offset = MENUBAR_Y_SIZE * FULL_SCREEN_WIDTH *
|
||||
EGA_NUM_LAYERS / EGA_PIXELS_PER_BYTE;
|
||||
while ((offset += len) < FULL_SCREEN_WIDTH * FULL_SCREEN_HEIGHT / 2) {
|
||||
|
||||
int repeatLen = *pPixels++;
|
||||
if (repeatLen > 0) {
|
||||
byte *pDest = screenData + (offset / EGA_NUM_LAYERS) * EGA_PIXELS_PER_BYTE;
|
||||
|
||||
// Copy over the following bytes - each four bytes contain the four
|
||||
// planes worth of data for 8 sequential pixels
|
||||
while (repeatLen-- > 0) {
|
||||
int planeNum = offset % EGA_NUM_LAYERS;
|
||||
byte v = *pPixels++;
|
||||
for (int bitCtr = 0; bitCtr < 8; ++bitCtr, v <<= 1) {
|
||||
if ((v & 0x80) != 0)
|
||||
*(pDest + bitCtr) |= 1 << planeNum;
|
||||
else
|
||||
*(pDest + bitCtr) &= ~(1 << planeNum);
|
||||
}
|
||||
|
||||
if ((++offset % EGA_NUM_LAYERS) == 0)
|
||||
pDest += EGA_PIXELS_PER_BYTE;
|
||||
}
|
||||
}
|
||||
|
||||
// Get next skip bytes length
|
||||
len = *pPixels++;
|
||||
}
|
||||
}
|
||||
|
||||
// vgaDecodeFrame
|
||||
// Decodes a single frame of a VGA animation sequence
|
||||
|
||||
void AnimationSequence::vgaDecodeFrame(byte *&pPixels, byte *&pLines) {
|
||||
Screen &screen = Screen::getReference();
|
||||
byte *screenData = screen.screen_raw();
|
||||
uint16 screenPos = 0;
|
||||
uint16 len;
|
||||
|
||||
while (screenPos < SCREEN_SIZE) {
|
||||
// Get line length
|
||||
len = (uint16) *pLines++;
|
||||
if (len == 0) {
|
||||
len = READ_LE_UINT16(pLines);
|
||||
pLines += 2;
|
||||
}
|
||||
|
||||
// Move the splice over
|
||||
memcpy(screenData, pPixels, len);
|
||||
screenData += len;
|
||||
screenPos += len;
|
||||
pPixels += len;
|
||||
|
||||
// Get the offset inc amount
|
||||
len = (uint16) *pLines++;
|
||||
if (len == 0) {
|
||||
len = READ_LE_UINT16(pLines);
|
||||
pLines += 2;
|
||||
}
|
||||
|
||||
screenData += len;
|
||||
screenPos += len;
|
||||
}
|
||||
}
|
||||
|
||||
AnimationSequence::AnimationSequence(uint16 screenId, Palette &palette, bool fadeIn, int frameDelay,
|
||||
const AnimSoundSequence *soundList, uint8 loops): _screenId(screenId), _palette(palette),
|
||||
_frameDelay(frameDelay), _soundList(soundList), _loops(loops) {
|
||||
Screen &screen = Screen::getReference();
|
||||
PictureDecoder decoder;
|
||||
Disk &d = Disk::getReference();
|
||||
|
||||
// Get the data and decode it. Note that VGA decompression is used
|
||||
// even if the decompressed contents is actually EGA data
|
||||
MemoryBlock *data = d.getEntry(_screenId);
|
||||
_decodedData = decoder.vgaDecode(data, MAX_ANIM_DECODER_BUFFER_SIZE);
|
||||
delete data;
|
||||
|
||||
_isEGA = LureEngine::getReference().isEGA();
|
||||
if (_isEGA) {
|
||||
// Setup for EGA animation
|
||||
_lineRefs = nullptr;
|
||||
|
||||
// Reset the palette and clear the screen for EGA decoding
|
||||
screen.setPaletteEmpty(RES_PALETTE_ENTRIES);
|
||||
screen.screen().empty();
|
||||
|
||||
byte *pSrc = _decodedData->data();
|
||||
pSrc = showInitialScreen(pSrc);
|
||||
screen.setPalette(&_palette, 0, _palette.numEntries());
|
||||
|
||||
// Set pointers for animation
|
||||
_pPixelsStart = _pPixels = pSrc;
|
||||
_pPixelsEnd = _decodedData->data() + _decodedData->size() - 1;
|
||||
_pLinesStart = _pLines = nullptr;
|
||||
_pLinesEnd = nullptr;
|
||||
|
||||
} else {
|
||||
// Setup for VGA animation
|
||||
_lineRefs = d.getEntry(_screenId + 1);
|
||||
|
||||
// Reset the palette and set the initial starting screen
|
||||
screen.setPaletteEmpty(RES_PALETTE_ENTRIES);
|
||||
showInitialScreen();
|
||||
|
||||
// Set the palette
|
||||
if (fadeIn) screen.paletteFadeIn(&_palette);
|
||||
else screen.setPalette(&_palette, 0, _palette.numEntries());
|
||||
|
||||
// Set up frame pointers
|
||||
_pPixelsStart = _pPixels = _decodedData->data() + SCREEN_SIZE;
|
||||
_pPixelsEnd = _decodedData->data() + _decodedData->size() - 1;
|
||||
_pLinesStart = _pLines = _lineRefs->data();
|
||||
_pLinesEnd = _lineRefs->data() + _lineRefs->size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
AnimationSequence::~AnimationSequence() {
|
||||
delete _lineRefs;
|
||||
delete _decodedData;
|
||||
|
||||
// Renable GMM saving/loading now that the animation is done
|
||||
LureEngine::getReference()._saveLoadAllowed = true;
|
||||
}
|
||||
|
||||
// show
|
||||
// Main method for displaying the animation
|
||||
|
||||
AnimAbortType AnimationSequence::show() {
|
||||
Screen &screen = Screen::getReference();
|
||||
AnimAbortType result;
|
||||
const AnimSoundSequence *soundFrame = _soundList;
|
||||
int frameCtr = 0;
|
||||
|
||||
// Disable GMM saving/loading whilst animation is running
|
||||
LureEngine::getReference()._saveLoadAllowed = false;
|
||||
|
||||
// Loop through displaying the animations
|
||||
while (_loops > 0) {
|
||||
if (_pPixels < _pPixelsEnd && (_isEGA || _pLines < _pLinesEnd)) {
|
||||
if ((soundFrame != nullptr) && (soundFrame->rolandSoundId != 0xFF) && (frameCtr == 0))
|
||||
Sound.musicInterface_Play(
|
||||
Sound.isRoland() ? soundFrame->rolandSoundId : soundFrame->adlibSoundId, soundFrame->music);
|
||||
|
||||
if (_isEGA)
|
||||
egaDecodeFrame(_pPixels);
|
||||
else {
|
||||
vgaDecodeFrame(_pPixels, _pLines);
|
||||
}
|
||||
|
||||
// Make the decoded frame visible
|
||||
screen.update();
|
||||
} else {
|
||||
// Animation has finished.
|
||||
_loops--;
|
||||
if (_loops > 0) {
|
||||
// Animation will be repeated, so reset
|
||||
// and show the first frame again.
|
||||
_pPixels = _pPixelsStart;
|
||||
_pLines = _pLinesStart;
|
||||
showInitialScreen(_decodedData->data());
|
||||
}
|
||||
}
|
||||
|
||||
result = delay(_frameDelay * 1000 / 50);
|
||||
if (result != ABORT_NONE) return result;
|
||||
|
||||
if ((soundFrame != nullptr) && (++frameCtr == soundFrame->numFrames)) {
|
||||
frameCtr = 0;
|
||||
++soundFrame;
|
||||
if (soundFrame->numFrames == 0) soundFrame = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return ABORT_NONE;
|
||||
}
|
||||
|
||||
bool AnimationSequence::step() {
|
||||
Screen &screen = Screen::getReference();
|
||||
if (_pPixels >= _pPixelsEnd) return false;
|
||||
|
||||
if (_isEGA)
|
||||
egaDecodeFrame(_pPixels);
|
||||
else {
|
||||
if (_pLines >= _pLinesEnd) return false;
|
||||
vgaDecodeFrame(_pPixels, _pLines);
|
||||
}
|
||||
|
||||
// Make the decoded frame visible
|
||||
screen.update();
|
||||
screen.setPalette(&_palette);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
byte *AnimationSequence::showInitialScreen(byte *pSrc) {
|
||||
Screen &screen = Screen::getReference();
|
||||
|
||||
if (_isEGA) {
|
||||
// Load the screen - each four bytes contain the four planes
|
||||
// worth of data for 8 sequential pixels
|
||||
byte *pDest = screen.screen().data().data() +
|
||||
(FULL_SCREEN_WIDTH * MENUBAR_Y_SIZE);
|
||||
|
||||
for (int ctr = 0; ctr < FULL_SCREEN_WIDTH * (FULL_SCREEN_HEIGHT -
|
||||
MENUBAR_Y_SIZE) / 8; ++ctr, pDest += EGA_PIXELS_PER_BYTE) {
|
||||
for (int planeCtr = 0; planeCtr < EGA_NUM_LAYERS; ++planeCtr, ++pSrc) {
|
||||
byte v = *pSrc;
|
||||
for (int bitCtr = 0; bitCtr < 8; ++bitCtr, v <<= 1) {
|
||||
if ((v & 0x80) != 0)
|
||||
*(pDest + bitCtr) |= 1 << planeCtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
screen.screen().data().copyFrom(_decodedData, 0, 0, FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH);
|
||||
}
|
||||
screen.update();
|
||||
|
||||
return pSrc;
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
68
engines/lure/animseq.h
Normal file
68
engines/lure/animseq.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_ANIMSEQ_H
|
||||
#define LURE_ANIMSEQ_H
|
||||
|
||||
#include "lure/screen.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
enum AnimAbortType {ABORT_NONE, ABORT_END_INTRO, ABORT_NEXT_SCENE};
|
||||
|
||||
struct AnimSoundSequence {
|
||||
uint16 numFrames;
|
||||
uint8 adlibSoundId;
|
||||
uint8 rolandSoundId;
|
||||
uint8 channelNum;
|
||||
bool music;
|
||||
};
|
||||
|
||||
class AnimationSequence {
|
||||
private:
|
||||
bool _isEGA;
|
||||
uint16 _screenId;
|
||||
Palette &_palette;
|
||||
MemoryBlock *_decodedData;
|
||||
MemoryBlock *_lineRefs;
|
||||
byte *_pPixels, *_pLines;
|
||||
byte *_pPixelsStart, *_pLinesStart;
|
||||
byte *_pPixelsEnd, *_pLinesEnd;
|
||||
const AnimSoundSequence *_soundList;
|
||||
int _frameDelay;
|
||||
uint8 _loops;
|
||||
|
||||
AnimAbortType delay(uint32 milliseconds);
|
||||
void egaDecodeFrame(byte *&pPixels);
|
||||
void vgaDecodeFrame(byte *&pPixels, byte *&pLines);
|
||||
public:
|
||||
AnimationSequence(uint16 screenId, Palette &palette, bool fadeIn, int frameDelay = 7,
|
||||
const AnimSoundSequence *soundList = NULL, uint8 loops = 1);
|
||||
~AnimationSequence();
|
||||
|
||||
AnimAbortType show();
|
||||
bool step();
|
||||
byte *showInitialScreen(byte *pSrc = 0);
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
3
engines/lure/configure.engine
Normal file
3
engines/lure/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine lure "Lure of the Temptress" yes "" "" "" "midi"
|
||||
3
engines/lure/credits.pl
Normal file
3
engines/lure/credits.pl
Normal file
@@ -0,0 +1,3 @@
|
||||
begin_section("Lure");
|
||||
add_person("Paul Gilbert", "dreammaster", "");
|
||||
end_section();
|
||||
635
engines/lure/debugger.cpp
Normal file
635
engines/lure/debugger.cpp
Normal file
@@ -0,0 +1,635 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/endian.h"
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/debugger.h"
|
||||
#include "lure/decode.h"
|
||||
#include "lure/game.h"
|
||||
#include "lure/res.h"
|
||||
#include "lure/res_struct.h"
|
||||
#include "lure/room.h"
|
||||
#include "lure/scripts.h"
|
||||
#include "lure/strings.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
Debugger::Debugger(): GUI::Debugger() {
|
||||
registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
|
||||
registerCmd("enter", WRAP_METHOD(Debugger, cmd_enterRoom));
|
||||
registerCmd("rooms", WRAP_METHOD(Debugger, cmd_listRooms));
|
||||
registerCmd("fields", WRAP_METHOD(Debugger, cmd_listFields));
|
||||
registerCmd("setfield", WRAP_METHOD(Debugger, cmd_setField));
|
||||
registerCmd("queryfield", WRAP_METHOD(Debugger, cmd_queryField));
|
||||
registerCmd("give", WRAP_METHOD(Debugger, cmd_giveItem));
|
||||
registerCmd("hotspots", WRAP_METHOD(Debugger, cmd_hotspots));
|
||||
registerCmd("hotspot", WRAP_METHOD(Debugger, cmd_hotspot));
|
||||
registerCmd("room", WRAP_METHOD(Debugger, cmd_room));
|
||||
registerCmd("showanim", WRAP_METHOD(Debugger, cmd_showAnim));
|
||||
registerCmd("strings", WRAP_METHOD(Debugger, cmd_saveStrings));
|
||||
registerCmd("debug", WRAP_METHOD(Debugger, cmd_debug));
|
||||
registerCmd("script", WRAP_METHOD(Debugger, cmd_script));
|
||||
}
|
||||
|
||||
static int strToInt(const char *s) {
|
||||
if (!*s)
|
||||
// No string at all
|
||||
return 0;
|
||||
else if (strcmp(s, "player") == 0)
|
||||
return PLAYER_ID;
|
||||
else if (strcmp(s, "ratpouch") == 0)
|
||||
return RATPOUCH_ID;
|
||||
else if (toupper(s[strlen(s) - 1]) != 'H')
|
||||
// Standard decimal string
|
||||
return atoi(s);
|
||||
|
||||
// Hexadecimal string
|
||||
int result = 0;
|
||||
const char *p = s;
|
||||
char ch;
|
||||
while ((ch = toupper(*p++)) != 'H') {
|
||||
if ((ch >= '0') && (ch <= '9'))
|
||||
result = (result << 4) + (ch - '0');
|
||||
else if ((ch >= 'A') && (ch <= 'F'))
|
||||
result = (result << 4) + (ch - 'A' + 10);
|
||||
else
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_enterRoom(int argc, const char **argv) {
|
||||
Resources &res = Resources::getReference();
|
||||
Room &room = Room::getReference();
|
||||
uint remoteFlag = 0;
|
||||
|
||||
if (argc > 1) {
|
||||
int roomNumber = strToInt(argv[1]);
|
||||
|
||||
// Validate that it's an existing room
|
||||
if (res.getRoom(roomNumber) == nullptr) {
|
||||
debugPrintf("specified number was not a valid room\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argc > 2) {
|
||||
remoteFlag = strToInt(argv[2]);
|
||||
}
|
||||
|
||||
room.leaveRoom();
|
||||
room.setRoomNumber(roomNumber);
|
||||
if (!remoteFlag)
|
||||
res.getActiveHotspot(PLAYER_ID)->setRoomNumber(roomNumber);
|
||||
|
||||
detach();
|
||||
return false;
|
||||
}
|
||||
|
||||
debugPrintf("Syntax: room <roomnum> [<remoteview>]\n");
|
||||
debugPrintf("A non-zero value for reomteview will change the room without ");
|
||||
debugPrintf("moving the player.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_listRooms(int argc, const char **argv) {
|
||||
RoomDataList &rooms = Resources::getReference().roomData();
|
||||
StringData &strings = StringData::getReference();
|
||||
char buffer[MAX_DESC_SIZE];
|
||||
int ctr = 0;
|
||||
|
||||
debugPrintf("Available rooms are:\n");
|
||||
for (RoomDataList::iterator i = rooms.begin(); i != rooms.end(); ++i) {
|
||||
RoomData const &room = **i;
|
||||
// Explicitly note the second drawbridge room as "Alt"
|
||||
if (room.roomNumber == 49) {
|
||||
strings.getString(47, buffer);
|
||||
Common::strcat_s(buffer, " (alt)");
|
||||
} else {
|
||||
strings.getString(room.roomNumber, buffer);
|
||||
}
|
||||
|
||||
debugPrintf("#%d - %s", room.roomNumber, buffer);
|
||||
|
||||
if (++ctr % 3 == 0) debugPrintf("\n");
|
||||
else {
|
||||
// Write out spaces between columns
|
||||
int numSpaces = 25 - strlen(buffer) - (room.roomNumber >= 10 ? 2 : 1);
|
||||
char *s = buffer;
|
||||
while (numSpaces-- > 0) *s++ = ' ';
|
||||
*s = '\0';
|
||||
debugPrintf("%s", buffer);
|
||||
}
|
||||
}
|
||||
debugPrintf("\n");
|
||||
debugPrintf("Current room: %d\n", Room::getReference().roomNumber());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_listFields(int argc, const char **argv) {
|
||||
ValueTableData &fields = Resources::getReference().fieldList();
|
||||
|
||||
for (int ctr = 0; ctr < fields.size(); ++ctr) {
|
||||
debugPrintf("(%-2d): %-5d", ctr, fields.getField(ctr));
|
||||
if (!((ctr + 1) % 7))
|
||||
debugPrintf("\n");
|
||||
}
|
||||
debugPrintf("\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_setField(int argc, const char **argv) {
|
||||
ValueTableData &fields = Resources::getReference().fieldList();
|
||||
|
||||
if (argc >= 3) {
|
||||
int fieldNum = strToInt(argv[1]);
|
||||
uint16 value = strToInt(argv[2]);
|
||||
|
||||
if ((fieldNum < 0) || (fieldNum >= fields.size())) {
|
||||
// Invalid field number
|
||||
debugPrintf("Invalid field number specified\n");
|
||||
} else {
|
||||
// Set the field value
|
||||
fields.setField(fieldNum, value);
|
||||
}
|
||||
} else {
|
||||
debugPrintf("Syntax: setfield <field_number> <value>\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_queryField(int argc, const char **argv) {
|
||||
ValueTableData &fields = Resources::getReference().fieldList();
|
||||
|
||||
if (argc > 1) {
|
||||
int fieldNum = strToInt(argv[1]);
|
||||
if ((fieldNum < 0) || (fieldNum >= fields.size())) {
|
||||
// Invalid field number
|
||||
debugPrintf("Invalid field number specified\n");
|
||||
} else {
|
||||
// Get the field value
|
||||
debugPrintf("Field %d is %d (%xh)\n", fieldNum,
|
||||
fields.getField(fieldNum), fields.getField(fieldNum));
|
||||
}
|
||||
} else {
|
||||
debugPrintf("Syntax: queryfield <field_num>\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_giveItem(int argc, const char **argv) {
|
||||
Resources &res = Resources::getReference();
|
||||
uint16 itemNum;
|
||||
uint16 charNum = PLAYER_ID;
|
||||
HotspotData *charHotspot, *itemHotspot;
|
||||
|
||||
if (argc >= 2) {
|
||||
itemNum = strToInt(argv[1]);
|
||||
|
||||
if (argc == 3)
|
||||
charNum = strToInt(argv[2]);
|
||||
|
||||
itemHotspot = res.getHotspot(itemNum);
|
||||
charHotspot = res.getHotspot(charNum);
|
||||
|
||||
if (itemHotspot == nullptr) {
|
||||
debugPrintf("The specified item does not exist\n");
|
||||
} else if (itemNum < 0x408) {
|
||||
debugPrintf("The specified item number is not an object\n");
|
||||
} else if ((charNum < PLAYER_ID) || (charNum >= 0x408) ||
|
||||
(charHotspot == nullptr)) {
|
||||
debugPrintf("The specified character does not exist");
|
||||
} else {
|
||||
// Set the item's room number to be the destination character
|
||||
itemHotspot->roomNumber = charNum;
|
||||
}
|
||||
} else {
|
||||
debugPrintf("Syntax: give <item_id> [<character_id>]\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_hotspots(int argc, const char **argv) {
|
||||
Resources &res = Resources::getReference();
|
||||
StringData &strings = StringData::getReference();
|
||||
Room &room = Room::getReference();
|
||||
char buffer[MAX_DESC_SIZE];
|
||||
|
||||
if (argc > 1) {
|
||||
if (strcmp(argv[1], "active") == 0) {
|
||||
// Loop for displaying active hotspots
|
||||
HotspotList::iterator i;
|
||||
for (i = res.activeHotspots().begin(); i != res.activeHotspots().end(); ++i) {
|
||||
Hotspot const &hotspot = **i;
|
||||
|
||||
if (hotspot.nameId() == 0) Common::strcpy_s(buffer, "none");
|
||||
else strings.getString(hotspot.nameId(), buffer);
|
||||
|
||||
debugPrintf("%4xh - %s pos=(%d,%d,%d)\n", hotspot.hotspotId(), buffer,
|
||||
hotspot.x(), hotspot.y(), hotspot.roomNumber());
|
||||
}
|
||||
} else {
|
||||
// Presume it's a room's hotspots
|
||||
uint16 roomNumber = (argc >= 3) ? strToInt(argv[2]) : room.roomNumber();
|
||||
|
||||
HotspotDataList::iterator i;
|
||||
for (i = res.hotspotData().begin(); i != res.hotspotData().end(); ++i) {
|
||||
HotspotData const &hotspot = **i;
|
||||
|
||||
if (hotspot.roomNumber == roomNumber) {
|
||||
if (hotspot.nameId == 0) Common::strcpy_s(buffer, "none");
|
||||
else strings.getString(hotspot.nameId, buffer);
|
||||
|
||||
debugPrintf("%4xh - %s pos=(%d,%d,%d)\n", hotspot.hotspotId, buffer,
|
||||
hotspot.startX, hotspot.startY, hotspot.roomNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
debugPrintf("Syntax: hotspots ['active' | ['room' | 'room' '<room_number>']]\n");
|
||||
debugPrintf("Gives a list of all the currently active hotspots, or the hotspots\n");
|
||||
debugPrintf("present in either the current room or a designated one\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_hotspot(int argc, const char **argv) {
|
||||
Resources &res = Resources::getReference();
|
||||
StringData &strings = StringData::getReference();
|
||||
StringList &stringList = res.stringList();
|
||||
char buffer[MAX_DESC_SIZE];
|
||||
HotspotData *hs;
|
||||
Hotspot *h;
|
||||
|
||||
if (argc < 2) {
|
||||
debugPrintf("hotspot <hotspot_id> ['paths' | 'schedule' | 'actions' | 'activate' | 'deactivate' | 'setpos']\n");
|
||||
return true;
|
||||
}
|
||||
hs = res.getHotspot(strToInt(argv[1]));
|
||||
if (!hs) {
|
||||
debugPrintf("Unknown hotspot specified\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
h = res.getActiveHotspot(hs->hotspotId);
|
||||
if (argc == 2) {
|
||||
// Show the hotspot properties
|
||||
strings.getString(hs->nameId, buffer);
|
||||
debugPrintf("name = %d - %s, descs = (%d,%d)\n", hs->nameId, buffer,
|
||||
hs->descId, hs->descId2);
|
||||
debugPrintf("actions = %xh, offset = %xh\n", hs->actions, hs->actionsOffset);
|
||||
debugPrintf("flags = %xh, layer = %d\n", hs->flags, hs->layer);
|
||||
debugPrintf("position = %d,%d,%d\n", hs->startX, hs->startY, hs->roomNumber);
|
||||
debugPrintf("size = %d,%d, alt = %d,%d, yCorrection = %d\n",
|
||||
hs->width, hs->height, hs->widthCopy, hs->heightCopy, hs->yCorrection);
|
||||
debugPrintf("Talk bubble offset = %d,%d\n", hs->talkX, hs->talkY);
|
||||
debugPrintf("load offset = %xh, script load = %d\n", hs->loadOffset, hs->scriptLoadFlag);
|
||||
debugPrintf("Animation Id = %xh, Color offset = %d\n", hs->animRecordId, hs->colorOffset);
|
||||
debugPrintf("Talk Script offset = %xh, Tick Script offset = %xh\n",
|
||||
hs->talkScriptOffset, hs->tickScriptOffset);
|
||||
debugPrintf("Tick Proc offset = %xh\n", hs->tickProcId);
|
||||
debugPrintf("Tick timeout = %d\n", hs->tickTimeout);
|
||||
debugPrintf("Character mode = %d, delay ctr = %d, pause ctr = %d\n",
|
||||
hs->characterMode, hs->delayCtr, hs->pauseCtr);
|
||||
|
||||
if (h != nullptr) {
|
||||
debugPrintf("Frame Number = %d of %d\n", h->frameNumber(), h->numFrames());
|
||||
debugPrintf("Persistent = %s\n", h->persistent() ? "true" : "false");
|
||||
}
|
||||
|
||||
} else if (strcmp(argv[2], "actions") == 0) {
|
||||
// List the action set for the character
|
||||
for (int action = GET; action <= EXAMINE; ++action) {
|
||||
uint16 offset = res.getHotspotAction(hs->actionsOffset, (Action) action);
|
||||
const char *actionStr = stringList.getString(action);
|
||||
|
||||
if (offset >= 0x8000) {
|
||||
debugPrintf("%s - Message %xh\n", actionStr, offset & 0x7ff);
|
||||
} else if (offset != 0) {
|
||||
debugPrintf("%s - Script %xh\n", actionStr, offset);
|
||||
}
|
||||
}
|
||||
} else if (strcmp(argv[2], "activate") == 0) {
|
||||
// Activate the hotspot
|
||||
res.activateHotspot(hs->hotspotId);
|
||||
hs->flags &= ~HOTSPOTFLAG_MENU_EXCLUSION;
|
||||
debugPrintf("Activated\n");
|
||||
|
||||
} else if (strcmp(argv[2], "deactivate") == 0) {
|
||||
// Deactivate the hotspot
|
||||
res.deactivateHotspot(hs->hotspotId);
|
||||
hs->flags |= HOTSPOTFLAG_MENU_EXCLUSION;
|
||||
debugPrintf("Deactivated\n");
|
||||
|
||||
} else {
|
||||
if (strcmp(argv[2], "schedule") == 0) {
|
||||
// List any current schedule for the character
|
||||
debugPrintf("%s", hs->npcSchedule.getDebugInfo().c_str());
|
||||
}
|
||||
if (!h)
|
||||
debugPrintf("The specified hotspot is not currently active\n");
|
||||
else if (strcmp(argv[2], "paths") == 0) {
|
||||
// List any paths for a charcter
|
||||
debugPrintf("%s", h->pathFinder().getDebugInfo().c_str());
|
||||
}
|
||||
else if (strcmp(argv[2], "pixels") == 0) {
|
||||
// List the pixel data for the hotspot
|
||||
HotspotAnimData &pData = h->anim();
|
||||
debugPrintf("Record Id = %xh\n", pData.animRecordId);
|
||||
debugPrintf("Flags = %d\n", pData.flags);
|
||||
debugPrintf("Frames: up=%d down=%d left=%d right=%d\n",
|
||||
pData.upFrame, pData.downFrame, pData.leftFrame, pData.rightFrame);
|
||||
debugPrintf("Current frame = %d of %d\n", h->frameNumber(), h->numFrames());
|
||||
}
|
||||
else if (strcmp(argv[2], "setpos") == 0) {
|
||||
// Set the hotspot position
|
||||
if (argc >= 5)
|
||||
h->setPosition(strToInt(argv[3]), strToInt(argv[4]));
|
||||
if (argc >= 6)
|
||||
h->setRoomNumber(strToInt(argv[5]));
|
||||
debugPrintf("Done.\n");
|
||||
}
|
||||
}
|
||||
|
||||
debugPrintf("\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *directionList[5] = {"UP", "DOWN", "LEFT", "RIGHT", "NONE"};
|
||||
|
||||
bool Debugger::cmd_room(int argc, const char **argv) {
|
||||
Resources &res = Resources::getReference();
|
||||
StringData &strings = StringData::getReference();
|
||||
char buffer[MAX_DESC_SIZE];
|
||||
|
||||
if (argc < 2) {
|
||||
debugPrintf("room <room_number>\n");
|
||||
return true;
|
||||
}
|
||||
int roomNumber = strToInt(argv[1]);
|
||||
RoomData *room = res.getRoom(roomNumber);
|
||||
if (!room) {
|
||||
debugPrintf("Unknown room specified\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Show the room details
|
||||
strings.getString(roomNumber, buffer);
|
||||
debugPrintf("room #%d - %s\n", roomNumber, buffer);
|
||||
strings.getString(room->descId, buffer);
|
||||
debugPrintf("%s\n", buffer);
|
||||
debugPrintf("Horizontal clipping = %d->%d walk area=(%d,%d)-(%d,%d)\n",
|
||||
room->clippingXStart, room->clippingXEnd,
|
||||
room->walkBounds.left, room->walkBounds.top,
|
||||
room->walkBounds.right, room->walkBounds.bottom);
|
||||
|
||||
debugPrintf("Exit hotspots:");
|
||||
RoomExitHotspotList &exits = room->exitHotspots;
|
||||
if (exits.empty())
|
||||
debugPrintf(" none\n");
|
||||
else {
|
||||
RoomExitHotspotList::iterator i;
|
||||
for (i = exits.begin(); i != exits.end(); ++i) {
|
||||
RoomExitHotspotData const &rec = **i;
|
||||
|
||||
debugPrintf("\nArea - (%d,%d)-(%d,%d) Room=%d Cursor=%d Hotspot=%xh",
|
||||
rec.xs, rec.ys, rec.xe, rec.ye, rec.destRoomNumber, rec.cursorNum, rec.hotspotId);
|
||||
}
|
||||
|
||||
debugPrintf("\n");
|
||||
}
|
||||
|
||||
debugPrintf("Room exits:");
|
||||
if (room->exits.empty())
|
||||
debugPrintf(" none\n");
|
||||
else {
|
||||
RoomExitList::iterator i2;
|
||||
for (i2 = room->exits.begin(); i2 != room->exits.end(); ++i2) {
|
||||
RoomExitData const &rec2 = **i2;
|
||||
|
||||
debugPrintf("\nExit - (%d,%d)-(%d,%d) Dest=%d,(%d,%d) Dir=%s Sequence=%xh",
|
||||
rec2.xs, rec2.ys, rec2.xe, rec2.ye, rec2.roomNumber,
|
||||
rec2.x, rec2.y, directionList[rec2.direction], rec2.sequenceOffset);
|
||||
}
|
||||
|
||||
debugPrintf("\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_showAnim(int argc, const char **argv) {
|
||||
Resources &res = Resources::getReference();
|
||||
if (argc < 2) {
|
||||
debugPrintf("showAnim animId [[frame_width frame_height] | list]\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the animation Id
|
||||
int animId = strToInt(argv[1]);
|
||||
HotspotAnimData *data = res.getAnimation(animId);
|
||||
if (data == nullptr) {
|
||||
debugPrintf("No such animation Id exists\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Figure out the total size of the animation - this will be used for guestimating
|
||||
// frame sizes, or validating that a specified frame size is correct
|
||||
MemoryBlock *src = Disk::getReference().getEntry(data->animId);
|
||||
|
||||
int numFrames = READ_LE_UINT16(src->data());
|
||||
uint16 *headerEntry = (uint16 *) (src->data() + 2);
|
||||
assert((numFrames >= 1) && (numFrames < 100));
|
||||
|
||||
// Calculate total needed size for output and create memory block to hold it
|
||||
uint32 totalSize = 0;
|
||||
for (uint16 ctr = 0; ctr < numFrames; ++ctr, ++headerEntry) {
|
||||
totalSize += (READ_LE_UINT16(headerEntry) + 31) / 32;
|
||||
}
|
||||
totalSize = (totalSize + 0x81) << 4;
|
||||
MemoryBlock *dest = Memory::allocate(totalSize);
|
||||
|
||||
uint32 srcStart = (numFrames + 1) * sizeof(uint16) + 6;
|
||||
uint32 destSize = AnimationDecoder::decode_data(src, dest, srcStart) - 0x40;
|
||||
|
||||
// Figure out the frame size
|
||||
int frameSize;
|
||||
|
||||
if ((data->flags & PIXELFLAG_HAS_TABLE) != 0) {
|
||||
// Table based animation, so get frame size from frame 1 offset
|
||||
frameSize = READ_LE_UINT16(src->data());
|
||||
} else {
|
||||
// Get frame size from dividing uncompressed size by number of frames
|
||||
frameSize = destSize / numFrames;
|
||||
}
|
||||
|
||||
// Free up the data
|
||||
delete src;
|
||||
delete dest;
|
||||
|
||||
int width, height;
|
||||
|
||||
if (argc == 4) {
|
||||
// Width and height specified
|
||||
width = strToInt(argv[2]);
|
||||
height = strToInt(argv[3]);
|
||||
|
||||
if ((width * height) != (frameSize * 2)) {
|
||||
debugPrintf("Warning: Total size = %d, Frame size (%d,%d) * %d frames = %d bytes\n",
|
||||
destSize, width, height, numFrames, width * height * numFrames / 2);
|
||||
}
|
||||
} else {
|
||||
// Guestimate a frame size
|
||||
frameSize = destSize / numFrames;
|
||||
|
||||
// Figure out the approximate starting point of a width 3/4 the frame size
|
||||
width = frameSize * 3 / 4;
|
||||
|
||||
bool descFlag = (argc == 3);
|
||||
if (descFlag) debugPrintf("Target size = %d\n", frameSize * 2);
|
||||
|
||||
while ((width > 0) && (descFlag || (((frameSize * 2) % width) != 0))) {
|
||||
if (((frameSize * 2) % width) == 0)
|
||||
debugPrintf("Frame size (%d,%d) found\n", width, frameSize * 2 / width);
|
||||
--width;
|
||||
}
|
||||
|
||||
if (argc == 3) {
|
||||
debugPrintf("Done\n");
|
||||
return true;
|
||||
} else if (width == 0) {
|
||||
debugPrintf("Total size = %d, # frames = %d, frame Size = %d - No valid frame dimensions\n",
|
||||
destSize, numFrames, frameSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
height = (frameSize * 2) / width;
|
||||
debugPrintf("# frames = %d, guestimated frame size = (%d,%d)\n",
|
||||
numFrames, width, height);
|
||||
}
|
||||
|
||||
// Bottle object is used as a handy hotspot holder that doesn't have any
|
||||
// tick proc behavior that we need to worry about
|
||||
Hotspot *hotspot = res.activateHotspot(BOTTLE_HOTSPOT_ID);
|
||||
hotspot->setLayer(0xfe);
|
||||
hotspot->setSize(width, height);
|
||||
|
||||
Hotspot *player = res.activateHotspot(PLAYER_ID);
|
||||
hotspot->setColorOffset(player->resource()->colorOffset);
|
||||
|
||||
hotspot->setAnimation(animId);
|
||||
|
||||
debugPrintf("Done\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_saveStrings(int argc, const char **argv) {
|
||||
if (argc != 2) {
|
||||
debugPrintf("strings <stringId>\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
StringData &strings = StringData::getReference();
|
||||
|
||||
char *buffer = (char *)malloc(32768);
|
||||
if (!buffer) {
|
||||
debugPrintf("Cannot allocate strings buffer\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16 id = strToInt(argv[1]);
|
||||
strings.getString(id, buffer);
|
||||
debugPrintf("%s\n", buffer);
|
||||
|
||||
/* Commented out code for saving all text strings - note that 0x1000 is chosen
|
||||
* arbitrarily, so there'll be a bunch of garbage at the end, or the game will crash
|
||||
|
||||
// Save all the strings to a text file - this
|
||||
|
||||
FILE *f = fopen("strings.txt", "w");
|
||||
|
||||
for (int index = 0; index < 0x1000; ++index) {
|
||||
strings.getString(index, buffer);
|
||||
fprintf(f, "%.4xh - %s\n", index, buffer);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
debugPrintf("Done\n");
|
||||
*/
|
||||
|
||||
free(buffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_debug(int argc, const char **argv) {
|
||||
Game &game = Game::getReference();
|
||||
Room &room = Room::getReference();
|
||||
|
||||
if ((argc == 2) && (strcmp(argv[1], "on") == 0)) {
|
||||
debugPrintf("debug keys are on\n");
|
||||
game.debugFlag() = true;
|
||||
|
||||
} else if ((argc == 2) && (strcmp(argv[1], "off") == 0)) {
|
||||
debugPrintf("debug keys are off\n");
|
||||
game.debugFlag() = false;
|
||||
room.setShowInfo(false);
|
||||
|
||||
} else {
|
||||
debugPrintf("debug [on | off]]\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmd_script(int argc, const char **argv) {
|
||||
if (argc < 2) {
|
||||
debugPrintf("script <script number> [param 1] [param 2] [param 3] [exit flag]\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
int scriptNumber = strToInt(argv[1]);
|
||||
if ((scriptNumber < 0) || (scriptNumber > 66)) {
|
||||
debugPrintf("An invalid script number was specified\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16 param1 = 0, param2 = 0, param3 = 0;
|
||||
if (argc >= 3)
|
||||
param1 = strToInt(argv[2]);
|
||||
if (argc >= 4)
|
||||
param2 = strToInt(argv[3]);
|
||||
if (argc >= 5)
|
||||
param3 = strToInt(argv[4]);
|
||||
|
||||
Script::executeMethod(scriptNumber, param1, param2, param3);
|
||||
debugPrintf("Script executed\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
53
engines/lure/debugger.h
Normal file
53
engines/lure/debugger.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_DEBUGGER_H
|
||||
#define LURE_DEBUGGER_H
|
||||
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
class Debugger : public GUI::Debugger {
|
||||
public:
|
||||
Debugger();
|
||||
|
||||
private:
|
||||
bool cmd_enterRoom(int argc, const char **argv);
|
||||
bool cmd_listRooms(int argc, const char **argv);
|
||||
bool cmd_listFields(int argc, const char **argv);
|
||||
bool cmd_setField(int argc, const char **argv);
|
||||
bool cmd_queryField(int argc, const char **argv);
|
||||
bool cmd_giveItem(int argc, const char **argv);
|
||||
bool cmd_hotspots(int argc, const char **argv);
|
||||
bool cmd_hotspot(int argc, const char **argv);
|
||||
bool cmd_room(int argc, const char **argv);
|
||||
bool cmd_showAnim(int argc, const char **argv);
|
||||
bool cmd_saveStrings(int argc, const char **argv);
|
||||
bool cmd_debug(int argc, const char **argv);
|
||||
bool cmd_script(int argc, const char **argv);
|
||||
};
|
||||
|
||||
extern const char *directionList[5];
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
521
engines/lure/decode.cpp
Normal file
521
engines/lure/decode.cpp
Normal file
@@ -0,0 +1,521 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lure/decode.h"
|
||||
#include "lure/lure.h"
|
||||
#include "lure/memory.h"
|
||||
#include "lure/luredefs.h"
|
||||
#include "common/endian.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
/* PictureDecoder class */
|
||||
/* */
|
||||
/* Provides the functionality for decoding screens */
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Write a single byte to the output buffer
|
||||
*/
|
||||
void PictureDecoder::writeByte(MemoryBlock *dest, byte v) {
|
||||
if (outputOffset == dest->size())
|
||||
error("Decoded data exceeded allocated output buffer size");
|
||||
dest->data()[outputOffset++] = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out a specified byte a given number of times to the output
|
||||
*/
|
||||
void PictureDecoder::writeBytes(MemoryBlock *dest, byte v, uint16 numBytes) {
|
||||
if (outputOffset + numBytes > dest->size())
|
||||
error("Decoded data exceeded allocated output buffer size");
|
||||
dest->setBytes(v, outputOffset, numBytes);
|
||||
outputOffset += numBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a byte from the compressed source using the first data pointer
|
||||
*/
|
||||
byte PictureDecoder::DSSI(bool incr) {
|
||||
if (dataPos > dataIn->size())
|
||||
error("PictureDecoder went beyond end of source data");
|
||||
|
||||
byte result = (dataPos == dataIn->size()) ? 0 :
|
||||
dataIn->data()[dataPos];
|
||||
if (incr) ++dataPos;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a byte from the compressed source using the second data pointer
|
||||
*/
|
||||
byte PictureDecoder::ESBX(bool incr) {
|
||||
if (dataPos2 >= dataIn->size())
|
||||
error("PictureDecoder went beyond end of source data");
|
||||
|
||||
byte result = dataIn->data()[dataPos2];
|
||||
if (incr) ++dataPos2;
|
||||
return result;
|
||||
}
|
||||
|
||||
void PictureDecoder::decrCtr() {
|
||||
--CL;
|
||||
if (CL == 0) {
|
||||
CH = ESBX();
|
||||
CL = 8;
|
||||
}
|
||||
}
|
||||
|
||||
bool PictureDecoder::shlCarry() {
|
||||
bool result = (CH & 0x80) != 0;
|
||||
CH <<= 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
// decode
|
||||
// Decodes a compressed Lure of the Temptress screen
|
||||
|
||||
MemoryBlock *PictureDecoder::decode(MemoryBlock *src, uint32 maxOutputSize) {
|
||||
bool isEGA = LureEngine::getReference().isEGA();
|
||||
return isEGA ? egaDecode(src, maxOutputSize) : vgaDecode(src, maxOutputSize);
|
||||
}
|
||||
|
||||
// egaDecode
|
||||
// Takes care of decoding a compressed EGA screen
|
||||
|
||||
#define READ_BIT_DX { bitFlag = (dx & 0x8000) != 0; dx <<= 1; if (--bitCtr == 0) { dx = (dx & 0xff00) | DSSI(); bitCtr = 8; } }
|
||||
#define READ_BITS(loops) for (int ctr = 0; ctr < loops; ++ctr) READ_BIT_DX
|
||||
|
||||
MemoryBlock *PictureDecoder::egaDecode(MemoryBlock *src, uint32 maxOutputSize) {
|
||||
MemoryBlock *dest = Memory::allocate(maxOutputSize);
|
||||
byte popTable[32 + 128];
|
||||
uint8 al;
|
||||
bool bitFlag;
|
||||
|
||||
// Set up initial states
|
||||
dataIn = src;
|
||||
dataPos = 6;
|
||||
|
||||
uint16 dx = READ_BE_UINT16(src->data() + dataPos);
|
||||
dataPos += sizeof(uint16);
|
||||
int bitCtr = 8;
|
||||
|
||||
// Decode the color popularity table
|
||||
|
||||
for (int nibbleCtr = 0; nibbleCtr < 32; ++nibbleCtr) {
|
||||
for (int byteCtr = 0; byteCtr < 128; byteCtr += 32) {
|
||||
popTable[nibbleCtr + byteCtr] = dx >> 11;
|
||||
READ_BITS(5);
|
||||
}
|
||||
}
|
||||
|
||||
// ok, on to the real thing
|
||||
|
||||
outputOffset = 0;
|
||||
al = dx >> 11;
|
||||
writeByte(dest, al);
|
||||
READ_BITS(5);
|
||||
|
||||
uint16 tableOffset = al;
|
||||
uint8 v = 0;
|
||||
|
||||
for (;;) {
|
||||
READ_BIT_DX
|
||||
|
||||
if (!bitFlag) {
|
||||
// Get the favorite color
|
||||
v = popTable[tableOffset];
|
||||
|
||||
} else {
|
||||
|
||||
READ_BIT_DX
|
||||
|
||||
if (bitFlag) {
|
||||
// Get another bit
|
||||
READ_BIT_DX
|
||||
|
||||
if (bitFlag) {
|
||||
// We have no favorite. Could this be a repeat?
|
||||
al = dx >> 11;
|
||||
READ_BITS(5);
|
||||
|
||||
if (al == popTable[tableOffset]) {
|
||||
// Repeat 16 bits
|
||||
uint16 numLoops = dx & 0xff00;
|
||||
READ_BITS(8);
|
||||
numLoops |= (dx >> 8);
|
||||
READ_BITS(8);
|
||||
|
||||
if (numLoops == 0)
|
||||
// Finished decoding
|
||||
break;
|
||||
writeBytes(dest, al, numLoops);
|
||||
continue;
|
||||
|
||||
} else if (al == popTable[tableOffset + 32]) {
|
||||
// Repeat 8 bits
|
||||
writeBytes(dest, tableOffset, dx >> 8);
|
||||
READ_BITS(8);
|
||||
continue;
|
||||
|
||||
} else if (al == popTable[tableOffset + 64]) {
|
||||
// Repeat 6 bits
|
||||
writeBytes(dest, tableOffset, dx >> 10);
|
||||
READ_BITS(6);
|
||||
continue;
|
||||
|
||||
} else if (al == popTable[tableOffset + 96]) {
|
||||
// Repeat 5 bits
|
||||
writeBytes(dest, tableOffset, dx >> 11);
|
||||
READ_BITS(5);
|
||||
continue;
|
||||
|
||||
} else {
|
||||
// It's a new color
|
||||
v = al;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Fourth favorite
|
||||
v = popTable[tableOffset + 96];
|
||||
}
|
||||
|
||||
} else {
|
||||
// Get another bit
|
||||
READ_BIT_DX
|
||||
|
||||
if (bitFlag) {
|
||||
// Third favorite
|
||||
v = popTable[tableOffset + 64];
|
||||
} else {
|
||||
// Second favorite
|
||||
v = popTable[tableOffset + 32];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tableOffset = v;
|
||||
writeByte(dest, v);
|
||||
}
|
||||
|
||||
// Resize the output to be the number of outputed bytes and return it
|
||||
if (outputOffset < dest->size()) dest->reallocate(outputOffset);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
// vgaDecode
|
||||
// Takes care of decoding a compressed vga screen
|
||||
|
||||
MemoryBlock *PictureDecoder::vgaDecode(MemoryBlock *src, uint32 maxOutputSize) {
|
||||
MemoryBlock *dest = Memory::allocate(maxOutputSize);
|
||||
|
||||
// Set up initial states
|
||||
dataIn = src;
|
||||
outputOffset = 0;
|
||||
dataPos = READ_LE_UINT32(dataIn->data() + 0x400);
|
||||
dataPos2 = 0x404;
|
||||
|
||||
CH = ESBX();
|
||||
CL = 9;
|
||||
|
||||
// Main decoding loop
|
||||
bool loopFlag = true;
|
||||
while (loopFlag) {
|
||||
AL = DSSI();
|
||||
writeByte(dest, AL);
|
||||
BP = ((uint16) AL) << 2;
|
||||
|
||||
// Inner loop
|
||||
for (;;) {
|
||||
decrCtr();
|
||||
if (shlCarry()) {
|
||||
decrCtr();
|
||||
if (shlCarry()) {
|
||||
decrCtr();
|
||||
if (shlCarry())
|
||||
break;
|
||||
|
||||
AL = dataIn->data()[BP + 3];
|
||||
} else {
|
||||
decrCtr();
|
||||
if (shlCarry())
|
||||
AL = dataIn->data()[BP + 2];
|
||||
else
|
||||
AL = dataIn->data()[BP + 1];
|
||||
}
|
||||
} else {
|
||||
decrCtr();
|
||||
if (shlCarry()) {
|
||||
AL = (byte) (BP >> 2);
|
||||
AH = DSSI();
|
||||
if (AH == 0) {
|
||||
AL = DSSI();
|
||||
if (AL == 0) {
|
||||
// Finally done
|
||||
loopFlag = false;
|
||||
break;
|
||||
} else {
|
||||
// Keep going
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Write out byte sequence
|
||||
writeBytes(dest, AL, AH);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
AL = dataIn->data()[BP];
|
||||
}
|
||||
}
|
||||
|
||||
// Write out the next byte
|
||||
writeByte(dest, AL);
|
||||
BP = ((uint16) AL) << 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Resize the output to be the number of outputed bytes and return it
|
||||
if (outputOffset < dest->size()) dest->reallocate(outputOffset);
|
||||
return dest;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
/* AnimationDecoder class */
|
||||
/* */
|
||||
/* Provides the functionality for decoding animations */
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
// The code below is responsible for decompressing the pixel data
|
||||
// for an animation. I'm not currently sure of the of the exact details
|
||||
// of the compression format - for now I've simply copied the code
|
||||
// from the executable
|
||||
|
||||
void AnimationDecoder::rcl(uint16 &value, bool &carry) {
|
||||
bool result = (value & 0x8000) != 0;
|
||||
value = (value << 1) + (carry ? 1 : 0);
|
||||
carry = result;
|
||||
}
|
||||
|
||||
#define GET_BYTE currData = (currData & 0xff00) | *pSrc++
|
||||
#define BX_VAL(x) *((byte *) (dest->data() + tableOffset + x))
|
||||
#define SET_HI_BYTE(x,v) x = (x & 0xff) | ((v) << 8);
|
||||
#define SET_LO_BYTE(x,v) x = (x & 0xff00) | (v);
|
||||
|
||||
void AnimationDecoder::decode_data_2(MemoryBlock *src, byte *&pSrc, uint16 &currData,
|
||||
uint16 &bitCtr, uint16 &dx, bool &carry) {
|
||||
SET_HI_BYTE(dx, currData >> 8);
|
||||
|
||||
for (int v = 0; v < 8; ++v) {
|
||||
rcl(currData, carry);
|
||||
if (--bitCtr == 0) {
|
||||
uint32 offset = (uint32) (pSrc - src->data());
|
||||
if (offset >= src->size())
|
||||
// Beyond end of source, so read in a 0 value
|
||||
currData &= 0xff00;
|
||||
else
|
||||
GET_BYTE;
|
||||
bitCtr = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 AnimationDecoder::decode_data(MemoryBlock *src, MemoryBlock *dest, uint32 srcPos) {
|
||||
byte *pSrc = src->data() + srcPos;
|
||||
byte *pDest = dest->data();
|
||||
uint16 v;
|
||||
bool carry = false;
|
||||
uint16 currData, bitCtr, dx;
|
||||
byte tableOffset;
|
||||
uint16 tempReg1, tempReg2;
|
||||
|
||||
// Handle splitting up 16 bytes into individual nibbles
|
||||
for (int numBytes = 0; numBytes < 16; ++numBytes, ++pDest) {
|
||||
// Split up next byte to pDest and pDest+0x10
|
||||
currData = *pSrc++;
|
||||
*(pDest + 0x10) = currData & 0xf;
|
||||
*pDest = (currData >> 4) & 0xf;
|
||||
|
||||
// Split up next byte to pDest+0x20 and pDest+0x30
|
||||
currData = *pSrc++;
|
||||
*(pDest + 0x30) = currData & 0xf;
|
||||
*(pDest + 0x20) = (currData >> 4) & 0xf;
|
||||
}
|
||||
|
||||
pDest = (byte *) (dest->data() + 0x40);
|
||||
currData = READ_BE_UINT16(pSrc);
|
||||
pSrc += sizeof(uint16);
|
||||
|
||||
bitCtr = 4;
|
||||
*pDest = (currData >> 8) & 0xf0;
|
||||
tableOffset = currData >> 12;
|
||||
currData <<= 4;
|
||||
dx = 1;
|
||||
|
||||
// Main loop
|
||||
bool loopFlag = true;
|
||||
while (loopFlag) {
|
||||
for (;;) {
|
||||
carry = false;
|
||||
rcl(currData, carry);
|
||||
if (--bitCtr == 0) {
|
||||
GET_BYTE;
|
||||
bitCtr = 8;
|
||||
}
|
||||
if (!carry) {
|
||||
tableOffset = BX_VAL(0);
|
||||
break;
|
||||
}
|
||||
|
||||
rcl(currData, carry);
|
||||
if (--bitCtr == 0) {
|
||||
GET_BYTE;
|
||||
bitCtr = 8;
|
||||
}
|
||||
if (!carry) {
|
||||
rcl(currData, carry);
|
||||
if (--bitCtr == 0) {
|
||||
GET_BYTE;
|
||||
bitCtr = 8;
|
||||
}
|
||||
|
||||
if (!carry) {
|
||||
tableOffset = BX_VAL(0x10);
|
||||
} else {
|
||||
tableOffset = BX_VAL(0x20);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
rcl(currData, carry);
|
||||
if (--bitCtr == 0) {
|
||||
GET_BYTE;
|
||||
bitCtr = 8;
|
||||
}
|
||||
if (!carry) {
|
||||
tableOffset = BX_VAL(0x30);
|
||||
break;
|
||||
}
|
||||
|
||||
SET_HI_BYTE(dx, currData >> 12);
|
||||
carry = false;
|
||||
for (int ctr = 0; ctr < 4; ++ctr) {
|
||||
rcl(currData, carry);
|
||||
if (--bitCtr == 0) {
|
||||
GET_BYTE;
|
||||
bitCtr = 8;
|
||||
}
|
||||
}
|
||||
|
||||
byte dxHigh = dx >> 8;
|
||||
if (dxHigh == BX_VAL(0)) {
|
||||
tempReg1 = bitCtr;
|
||||
tempReg2 = dx;
|
||||
decode_data_2(src, pSrc, currData, bitCtr, dx, carry);
|
||||
|
||||
SET_LO_BYTE(dx, dx >> 8);
|
||||
decode_data_2(src, pSrc, currData, bitCtr, dx, carry);
|
||||
SET_HI_BYTE(bitCtr, dx & 0xff);
|
||||
SET_LO_BYTE(bitCtr, dx >> 8);
|
||||
dx = tempReg2;
|
||||
|
||||
if (bitCtr == 0) {
|
||||
// End of decompression
|
||||
loopFlag = false;
|
||||
break;
|
||||
}
|
||||
} else if (dxHigh == BX_VAL(0x10)) {
|
||||
tempReg1 = bitCtr;
|
||||
decode_data_2(src, pSrc, currData, bitCtr, dx, carry);
|
||||
bitCtr = dx >> 8;
|
||||
|
||||
} else if (dxHigh == BX_VAL(0x20)) {
|
||||
SET_HI_BYTE(dx, currData >> 10);
|
||||
|
||||
for (v = 0; v < 6; ++v) {
|
||||
rcl(currData, carry);
|
||||
if (--bitCtr == 0) {
|
||||
GET_BYTE;
|
||||
bitCtr = 8;
|
||||
}
|
||||
}
|
||||
|
||||
tempReg1 = bitCtr;
|
||||
bitCtr = dx >> 8;
|
||||
|
||||
} else if (dxHigh == BX_VAL(0x30)) {
|
||||
SET_HI_BYTE(dx, currData >> 11);
|
||||
|
||||
for (v = 0; v < 5; ++v) {
|
||||
rcl(currData, carry);
|
||||
if (--bitCtr == 0) {
|
||||
GET_BYTE;
|
||||
bitCtr = 8;
|
||||
}
|
||||
}
|
||||
|
||||
tempReg1 = bitCtr;
|
||||
bitCtr = dx >> 8;
|
||||
|
||||
} else {
|
||||
tableOffset = dx >> 8;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((dx & 1) == 1) {
|
||||
*pDest++ |= tableOffset;
|
||||
--bitCtr;
|
||||
dx &= 0xfffe;
|
||||
}
|
||||
|
||||
SET_HI_BYTE(dx, tableOffset << 4);
|
||||
tableOffset |= dx >> 8;
|
||||
|
||||
v = bitCtr >> 1;
|
||||
while (v-- > 0) *pDest++ = tableOffset;
|
||||
|
||||
bitCtr &= 1;
|
||||
if (bitCtr != 0) {
|
||||
*pDest = tableOffset & 0xf0;
|
||||
dx |= 1; //dx.l
|
||||
}
|
||||
|
||||
bitCtr = tempReg1;
|
||||
tableOffset &= 0x0f;
|
||||
}
|
||||
|
||||
if (loopFlag) {
|
||||
dx ^= 1;
|
||||
if ((dx & 1) != 0) {
|
||||
SET_HI_BYTE(dx, tableOffset << 4);
|
||||
*pDest = dx >> 8;
|
||||
} else {
|
||||
*pDest++ |= tableOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return number of bytes written
|
||||
return pDest - dest->data();
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
63
engines/lure/decode.h
Normal file
63
engines/lure/decode.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_DECODE_H
|
||||
#define LURE_DECODE_H
|
||||
|
||||
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/memory.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
class PictureDecoder {
|
||||
private:
|
||||
MemoryBlock *dataIn;
|
||||
uint32 BP;
|
||||
uint32 dataPos, dataPos2;
|
||||
uint32 outputOffset;
|
||||
byte AL, AH;
|
||||
byte CH, CL;
|
||||
|
||||
void writeByte(MemoryBlock *dest, byte v);
|
||||
void writeBytes(MemoryBlock *dest, byte v, uint16 numBytes);
|
||||
byte DSSI(bool incr = true);
|
||||
byte ESBX(bool incr = true);
|
||||
void decrCtr();
|
||||
bool shlCarry();
|
||||
|
||||
public:
|
||||
MemoryBlock *decode(MemoryBlock *src, uint32 maxOutputSize = SCREEN_SIZE + 1);
|
||||
MemoryBlock *egaDecode(MemoryBlock *src, uint32 maxOutputSize);
|
||||
MemoryBlock *vgaDecode(MemoryBlock *src, uint32 maxOutputSize);
|
||||
};
|
||||
|
||||
class AnimationDecoder {
|
||||
public:
|
||||
static void rcl(uint16 &value, bool &carry);
|
||||
static uint32 decode_data(MemoryBlock *src, MemoryBlock *dest, uint32 srcPos);
|
||||
static void decode_data_2(MemoryBlock *src, byte *&pSrc, uint16 &currData,
|
||||
uint16 &bitCtr, uint16 &dx, bool &carry);
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
273
engines/lure/detection.cpp
Normal file
273
engines/lure/detection.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "base/plugins.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "lure/detection.h"
|
||||
#include "lure/lure.h"
|
||||
|
||||
static const PlainGameDescriptor lureGames[] = {
|
||||
{"lure", "Lure of the Temptress"},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
static const DebugChannelDef debugFlagList[] = {
|
||||
{Lure::kLureDebugScripts, "scripts", "Scripts debugging"},
|
||||
{Lure::kLureDebugAnimations, "animations", "Animations debugging"},
|
||||
{Lure::kLureDebugHotspots, "hotspots", "Hotspots debugging"},
|
||||
{Lure::kLureDebugFights, "fights", "Fights debugging"},
|
||||
{Lure::kLureDebugSounds, "sounds", "Sounds debugging"},
|
||||
{Lure::kLureDebugStrings, "strings", "Strings debugging"},
|
||||
DEBUG_CHANNEL_END
|
||||
};
|
||||
|
||||
namespace Lure {
|
||||
|
||||
static const LureGameDescription gameDescriptions[] = {
|
||||
{
|
||||
{
|
||||
"lure",
|
||||
"VGA",
|
||||
AD_ENTRY1("disk1.vga", "b2a8aa6d7865813a17a3c636e063572e"),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
#ifdef USE_TTS
|
||||
GUIO2(GAMEOPTION_TTS_NARRATOR, GAMEOPTION_COPY_PROTECTION)
|
||||
#else
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
#endif
|
||||
},
|
||||
GF_FLOPPY
|
||||
},
|
||||
|
||||
{ // Konami VGA version. Assembled 11:13:50 on 08/04/92
|
||||
// Bugreport #11441
|
||||
{
|
||||
"lure",
|
||||
"Konami VGA",
|
||||
AD_ENTRY1s("disk1.vga", "fbb0fca025579c0eda81d832d1fa5567", 615008),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
#ifdef USE_TTS
|
||||
GUIO2(GAMEOPTION_TTS_NARRATOR, GAMEOPTION_COPY_PROTECTION)
|
||||
#else
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
#endif
|
||||
},
|
||||
GF_FLOPPY | GF_KONAMI
|
||||
},
|
||||
|
||||
{ // Konami VGA version. Assembled 09:19:10 on 09/23/92
|
||||
{
|
||||
"lure",
|
||||
"Konami VGA",
|
||||
AD_ENTRY1s("disk1.vga", "fe11231363593982f76e0a64e988a284", 612352),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
#ifdef USE_TTS
|
||||
GUIO2(GAMEOPTION_TTS_NARRATOR, GAMEOPTION_COPY_PROTECTION)
|
||||
#else
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
#endif
|
||||
},
|
||||
GF_FLOPPY | GF_KONAMI
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"lure",
|
||||
"EGA",
|
||||
AD_ENTRY1("disk1.ega", "e9c9fdd8a19f7910d68e53cb84651273"),
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
#ifdef USE_TTS
|
||||
GUIO2(GAMEOPTION_TTS_NARRATOR, GAMEOPTION_COPY_PROTECTION)
|
||||
#else
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
#endif
|
||||
},
|
||||
GF_FLOPPY | GF_EGA
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"lure",
|
||||
"VGA",
|
||||
AD_ENTRY1("disk1.vga", "cf69d5ada228dd74f89046691c16aafb"),
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
},
|
||||
GF_FLOPPY
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"lure",
|
||||
"EGA",
|
||||
AD_ENTRY1("disk1.ega", "b80aced0321f64c58df2c7d3d74dfe79"),
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
},
|
||||
GF_FLOPPY | GF_EGA
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"lure",
|
||||
"",
|
||||
AD_ENTRY1("disk1.vga", "7aa19e444dab1ac7194d9f7a40ffe54a"),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
},
|
||||
GF_FLOPPY
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"lure",
|
||||
"",
|
||||
AD_ENTRY1("disk1.vga", "894a2c2caeccbad2fc2f4a79a8ee47b0"),
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
},
|
||||
GF_FLOPPY
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"lure",
|
||||
"",
|
||||
AD_ENTRY1("disk1.vga", "1c94475c1bb7e0e88c1757d3b5377e94"),
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
},
|
||||
GF_FLOPPY
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
"lure",
|
||||
"",
|
||||
AD_ENTRY1("disk1.vga", "1751145b653959f7a64fe1618d6b97ac"),
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
},
|
||||
GF_FLOPPY
|
||||
},
|
||||
|
||||
// Russian OG Edition v1.0
|
||||
{
|
||||
{
|
||||
"lure",
|
||||
"1.0",
|
||||
AD_ENTRY1("disk1.vga", "04cdcaa9f0cadca492f7aff0c8adfe06"),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
},
|
||||
GF_FLOPPY
|
||||
},
|
||||
|
||||
// Russian OG Edition v1.1
|
||||
{
|
||||
{
|
||||
"lure",
|
||||
"1.1",
|
||||
AD_ENTRY1("disk1.vga", "3f27adff8e8b279f12aaf3d808e84f02"),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO1(GAMEOPTION_COPY_PROTECTION)
|
||||
},
|
||||
GF_FLOPPY
|
||||
},
|
||||
|
||||
// Unsupported demo
|
||||
// Bugreport #11501
|
||||
{
|
||||
{
|
||||
"lure",
|
||||
MetaEngineDetection::GAME_NOT_IMPLEMENTED, // Reason for being unsupported
|
||||
AD_ENTRY1s("disk1.vga", "7a6aa0e958450c33b70b664d9f841ad1", 621984),
|
||||
Common::RU_RUS,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DEMO | ADGF_UNSUPPORTED,
|
||||
GUIO0()
|
||||
},
|
||||
GF_FLOPPY
|
||||
},
|
||||
|
||||
|
||||
{ AD_TABLE_END_MARKER, 0 }
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
class LureMetaEngineDetection : public AdvancedMetaEngineDetection<Lure::LureGameDescription> {
|
||||
public:
|
||||
LureMetaEngineDetection() : AdvancedMetaEngineDetection(Lure::gameDescriptions, lureGames) {
|
||||
_md5Bytes = 1024;
|
||||
|
||||
// Use kADFlagUseExtraAsHint to distinguish between EGA and VGA versions
|
||||
// of italian Lure when their datafiles sit in the same directory.
|
||||
_flags = kADFlagUseExtraAsHint;
|
||||
_guiOptions = GUIO1(GUIO_NOSPEECH);
|
||||
}
|
||||
|
||||
const char *getName() const override {
|
||||
return "lure";
|
||||
}
|
||||
|
||||
const char *getEngineName() const override {
|
||||
return "Lure of the Temptress";
|
||||
}
|
||||
|
||||
const char *getOriginalCopyright() const override {
|
||||
return "Lure of the Temptress (C) Revolution";
|
||||
}
|
||||
|
||||
const DebugChannelDef *getDebugChannels() const override {
|
||||
return debugFlagList;
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_PLUGIN_STATIC(LURE_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, LureMetaEngineDetection);
|
||||
49
engines/lure/detection.h
Normal file
49
engines/lure/detection.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_DETECTION_H
|
||||
#define LURE_DETECTION_H
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
enum {
|
||||
GF_FLOPPY = 1 << 0,
|
||||
GF_EGA = 1 << 1,
|
||||
GF_KONAMI = 1 << 2,
|
||||
GF_LNGUNK = 1 << 3
|
||||
};
|
||||
|
||||
struct LureGameDescription {
|
||||
AD_GAME_DESCRIPTION_HELPERS(desc);
|
||||
|
||||
ADGameDescription desc;
|
||||
|
||||
uint32 features;
|
||||
};
|
||||
|
||||
#define GAMEOPTION_TTS_NARRATOR GUIO_GAMEOPTIONS1
|
||||
#define GAMEOPTION_COPY_PROTECTION GUIO_GAMEOPTIONS2
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif // LURE_DETECTION_H
|
||||
223
engines/lure/disk.cpp
Normal file
223
engines/lure/disk.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/file.h"
|
||||
#include "common/util.h"
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "lure/disk.h"
|
||||
#include "lure/lure.h"
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/res.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
static Disk *int_disk = nullptr;
|
||||
|
||||
Disk &Disk::getReference() {
|
||||
return *int_disk;
|
||||
}
|
||||
|
||||
Disk::Disk() {
|
||||
_fileNum = 0xff;
|
||||
_fileHandle = nullptr;
|
||||
int_disk = this;
|
||||
}
|
||||
|
||||
Disk::~Disk() {
|
||||
delete _fileHandle;
|
||||
int_disk = nullptr;
|
||||
}
|
||||
|
||||
uint8 Disk::indexOf(uint16 id, bool suppressError) {
|
||||
// Make sure the correct file is open - the upper two bits of the Id give the file number. Note
|
||||
// that an extra check is done for the upper byte of the Id being 0x3f, which is the Id range
|
||||
// I use for lure.dat resources, which are resources extracted from the lure.exe executable
|
||||
uint8 entryFileNum = ((id>>8) == 0x3f) ? 0 : ((id >> 14) & 3) + 1;
|
||||
openFile(entryFileNum);
|
||||
|
||||
// Find the correct entry in the list based on the Id
|
||||
for (int entryIndex=0; entryIndex<NUM_ENTRIES_IN_HEADER; ++entryIndex) {
|
||||
if (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID) break;
|
||||
else if (_entries[entryIndex].id == id) return entryIndex;
|
||||
}
|
||||
|
||||
if (suppressError) return 0xff;
|
||||
if (_fileNum == 0)
|
||||
error("Could not find entry Id #%d in file %s", id, SUPPORT_FILENAME);
|
||||
else
|
||||
error("Could not find entry Id #%d in file disk%d.%s", id, _fileNum,
|
||||
LureEngine::getReference().isEGA() ? "ega" : "vga");
|
||||
}
|
||||
|
||||
void Disk::openFile(uint8 fileNum) {
|
||||
// Validate that the file number is correct
|
||||
bool isEGA = LureEngine::getReference().isEGA();
|
||||
if (fileNum > 4)
|
||||
error("Invalid file number specified - %d", fileNum);
|
||||
|
||||
// Only load up the new file if the current file number has changed
|
||||
if (fileNum == _fileNum) return;
|
||||
|
||||
// Delete any existing open file handle
|
||||
if (_fileNum != 0xff) delete _fileHandle;
|
||||
_fileNum = fileNum;
|
||||
|
||||
// Open up the new file
|
||||
_fileHandle = new Common::File();
|
||||
|
||||
char sFilename[10];
|
||||
if (_fileNum == 0)
|
||||
Common::strcpy_s(sFilename, SUPPORT_FILENAME);
|
||||
else
|
||||
Common::sprintf_s(sFilename, "disk%d.%s", _fileNum, isEGA ? "ega" : "vga");
|
||||
|
||||
_fileHandle->open(sFilename);
|
||||
if (!_fileHandle->isOpen())
|
||||
error("Could not open %s", sFilename);
|
||||
|
||||
char buffer[7];
|
||||
|
||||
// If it's the support file, then move to the correct language area
|
||||
|
||||
_dataOffset = 0;
|
||||
if (_fileNum == 0) {
|
||||
// Validate overall header
|
||||
_fileHandle->read(buffer, 6);
|
||||
buffer[4] = '\0';
|
||||
|
||||
if (strcmp(buffer, SUPPORT_IDENT_STRING) != 0)
|
||||
error("The file %s is not a valid Lure support file", sFilename);
|
||||
|
||||
// Scan for the correct language block
|
||||
LureLanguage language = LureEngine::getReference().getLureLanguage();
|
||||
bool foundFlag = false;
|
||||
|
||||
while (!foundFlag) {
|
||||
_fileHandle->read(buffer, 5);
|
||||
if ((byte)buffer[0] == 0xff)
|
||||
error("Could not find language data in support file");
|
||||
|
||||
if ((language == (LureLanguage)buffer[0]) || (language == LANG_UNKNOWN)) {
|
||||
foundFlag = true;
|
||||
_dataOffset = READ_LE_UINT32(&buffer[1]);
|
||||
_fileHandle->seek(_dataOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the header
|
||||
|
||||
_fileHandle->read(buffer, 6);
|
||||
buffer[6] = '\0';
|
||||
if (strcmp(buffer, HEADER_IDENT_STRING) != 0)
|
||||
error("The file %s was not a valid VGA file", sFilename);
|
||||
|
||||
uint16 fileFileNum = _fileHandle->readUint16BE();
|
||||
if ((fileFileNum != 0) && (fileFileNum != (isEGA ? _fileNum + 4 : _fileNum)))
|
||||
error("The file %s was not the correct file number", sFilename);
|
||||
|
||||
// Read in the header entries
|
||||
uint32 headerSize = sizeof(FileEntry) * NUM_ENTRIES_IN_HEADER;
|
||||
if (_fileHandle->read(_entries, headerSize) != headerSize)
|
||||
error("The file %s had a corrupted header", sFilename);
|
||||
|
||||
#ifdef SCUMM_BIG_ENDIAN
|
||||
// Process the read in header list to convert to big endian
|
||||
for (int i = 0; i < NUM_ENTRIES_IN_HEADER; ++i) {
|
||||
_entries[i].id = FROM_LE_16(_entries[i].id);
|
||||
_entries[i].size = FROM_LE_16(_entries[i].size);
|
||||
_entries[i].offset = FROM_LE_16(_entries[i].offset);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32 Disk::getEntrySize(uint16 id) {
|
||||
// Special room area check
|
||||
uint16 tempId = id & 0x3fff;
|
||||
if ((tempId == 0x120) || (tempId == 0x311) || (tempId == 8) || (tempId == 0x410)) {
|
||||
ValueTableData &fieldList = Resources::getReference().fieldList();
|
||||
if (fieldList.getField(AREA_FLAG) != 0)
|
||||
id ^= 0x8000;
|
||||
}
|
||||
|
||||
// Get the index of the resource, if necessary opening the correct file
|
||||
uint8 index = indexOf(id);
|
||||
|
||||
// Calculate the offset and size of the entry
|
||||
uint32 size = (uint32) _entries[index].size;
|
||||
if (_entries[index].sizeExtension) size += 0x10000;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
MemoryBlock *Disk::getEntry(uint16 id) {
|
||||
// Special room area check
|
||||
uint16 tempId = id & 0x3fff;
|
||||
if ((tempId == 0x120) || (tempId == 0x311) || (tempId == 8) || (tempId == 0x410)) {
|
||||
ValueTableData &fieldList = Resources::getReference().fieldList();
|
||||
if (fieldList.getField(AREA_FLAG) != 0)
|
||||
id ^= 0x8000;
|
||||
}
|
||||
|
||||
// Get the index of the resource, if necessary opening the correct file
|
||||
uint8 index = indexOf(id);
|
||||
|
||||
// Calculate the offset and size of the entry
|
||||
uint32 size = (uint32) _entries[index].size;
|
||||
if (_entries[index].sizeExtension) size += 0x10000;
|
||||
uint32 offset = (uint32) _entries[index].offset * 0x20 + _dataOffset;
|
||||
|
||||
MemoryBlock *result = Memory::allocate(size);
|
||||
_fileHandle->seek(offset, SEEK_SET);
|
||||
_fileHandle->read(result->data(), size);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Disk::exists(uint16 id) {
|
||||
// Get the index of the resource, if necessary opening the correct file
|
||||
uint8 index = indexOf(id, true);
|
||||
return (index != 0xff);
|
||||
}
|
||||
|
||||
uint8 Disk::numEntries() {
|
||||
if (_fileNum == 0)
|
||||
error("No file is currently open");
|
||||
|
||||
// Figure out how many entries there are by count until an unused entry is found
|
||||
for (byte entryIndex = 0; entryIndex < NUM_ENTRIES_IN_HEADER; ++entryIndex)
|
||||
if (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID) return entryIndex;
|
||||
|
||||
return NUM_ENTRIES_IN_HEADER;
|
||||
}
|
||||
|
||||
FileEntry *Disk::getIndex(uint8 entryIndex) {
|
||||
if (_fileNum == 0)
|
||||
error("No file is currently open");
|
||||
if ((entryIndex >= NUM_ENTRIES_IN_HEADER) || (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID))
|
||||
error("There is no entry at the specified index");
|
||||
|
||||
return &_entries[entryIndex];
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
66
engines/lure/disk.h
Normal file
66
engines/lure/disk.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_DISK_H
|
||||
#define LURE_DISK_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str.h"
|
||||
#include "lure/memory.h"
|
||||
#include "lure/res_struct.h"
|
||||
|
||||
namespace Common {
|
||||
class File;
|
||||
}
|
||||
|
||||
namespace Lure {
|
||||
|
||||
#define NUM_ENTRIES_IN_HEADER 0xBF
|
||||
#define HEADER_IDENT_STRING "heywow"
|
||||
#define SUPPORT_IDENT_STRING "lure"
|
||||
#define HEADER_ENTRY_UNUSED_ID 0xffff
|
||||
|
||||
class Disk {
|
||||
private:
|
||||
uint8 _fileNum;
|
||||
uint32 _dataOffset;
|
||||
Common::File *_fileHandle;
|
||||
FileEntry _entries[NUM_ENTRIES_IN_HEADER];
|
||||
|
||||
uint8 indexOf(uint16 id, bool suppressError = false);
|
||||
public:
|
||||
Disk();
|
||||
~Disk();
|
||||
static Disk &getReference();
|
||||
|
||||
void openFile(uint8 fileNum);
|
||||
uint32 getEntrySize(uint16 id);
|
||||
MemoryBlock *getEntry(uint16 id);
|
||||
bool exists(uint16 id);
|
||||
|
||||
uint8 numEntries();
|
||||
FileEntry *getIndex(uint8 entryIndex);
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
228
engines/lure/events.cpp
Normal file
228
engines/lure/events.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/events.h"
|
||||
|
||||
#include "graphics/cursorman.h"
|
||||
|
||||
#include "lure/events.h"
|
||||
#include "lure/lure.h"
|
||||
#include "lure/res.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
static Mouse *int_mouse = nullptr;
|
||||
|
||||
Mouse &Mouse::getReference() {
|
||||
return *int_mouse;
|
||||
}
|
||||
|
||||
Mouse::Mouse() {
|
||||
int_mouse = this;
|
||||
|
||||
_lButton = false;
|
||||
_rButton = false;
|
||||
_mButton = false;
|
||||
_cursorNum = CURSOR_ARROW;
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
setCursorNum(CURSOR_ARROW);
|
||||
}
|
||||
|
||||
Mouse::~Mouse() {
|
||||
}
|
||||
|
||||
void Mouse::handleEvent(Common::Event event) {
|
||||
_x = (int16) event.mouse.x;
|
||||
_y = (int16) event.mouse.y;
|
||||
|
||||
switch (event.type) {
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
_lButton = true;
|
||||
break;
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
_lButton = false;
|
||||
break;
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
_rButton = true;
|
||||
break;
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
_rButton = false;
|
||||
break;
|
||||
case Common::EVENT_MBUTTONDOWN:
|
||||
_mButton = true;
|
||||
break;
|
||||
case Common::EVENT_MBUTTONUP:
|
||||
_mButton = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Mouse::cursorOn() {
|
||||
CursorMan.showMouse(true);
|
||||
}
|
||||
|
||||
void Mouse::cursorOff() {
|
||||
CursorMan.showMouse(false);
|
||||
}
|
||||
|
||||
void Mouse::setCursorNum(CursorType cursorNum) {
|
||||
int hotspotX = 7, hotspotY = 7;
|
||||
if ((cursorNum == CURSOR_ARROW) || (cursorNum == CURSOR_MENUBAR)) {
|
||||
hotspotX = 0;
|
||||
hotspotY = 0;
|
||||
}
|
||||
|
||||
setCursorNum(cursorNum, hotspotX, hotspotY);
|
||||
}
|
||||
|
||||
void Mouse::setCursorNum(CursorType cursorNum, int hotspotX, int hotspotY) {
|
||||
Resources &res = Resources::getReference();
|
||||
|
||||
_cursorNum = cursorNum;
|
||||
byte *cursorAddr = res.getCursor(cursorNum);
|
||||
CursorMan.replaceCursor(cursorAddr, CURSOR_WIDTH, CURSOR_HEIGHT, hotspotX, hotspotY, 0);
|
||||
}
|
||||
|
||||
void Mouse::pushCursorNum(CursorType cursorNum) {
|
||||
int hotspotX = 7, hotspotY = 7;
|
||||
if ((cursorNum == CURSOR_ARROW) || (cursorNum == CURSOR_MENUBAR)) {
|
||||
hotspotX = 0;
|
||||
hotspotY = 0;
|
||||
}
|
||||
|
||||
pushCursorNum(cursorNum, hotspotX, hotspotY);
|
||||
}
|
||||
|
||||
void Mouse::pushCursorNum(CursorType cursorNum, int hotspotX, int hotspotY) {
|
||||
Resources &res = Resources::getReference();
|
||||
|
||||
_cursorNum = cursorNum;
|
||||
byte *cursorAddr = res.getCursor(cursorNum);
|
||||
CursorMan.pushCursor(cursorAddr, CURSOR_WIDTH, CURSOR_HEIGHT, hotspotX, hotspotY, 0);
|
||||
}
|
||||
|
||||
void Mouse::popCursor() {
|
||||
CursorMan.popCursor();
|
||||
}
|
||||
|
||||
void Mouse::setPosition(int newX, int newY) {
|
||||
g_system->warpMouse(newX, newY);
|
||||
}
|
||||
|
||||
void Mouse::waitForRelease() {
|
||||
Events &e = Events::getReference();
|
||||
LureEngine &engine = LureEngine::getReference();
|
||||
|
||||
do {
|
||||
while (e.pollEvent() && !engine.shouldQuit())
|
||||
;
|
||||
g_system->delayMillis(20);
|
||||
} while (!engine.shouldQuit() && (lButton() || rButton() || mButton()));
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
static Events *int_events = nullptr;
|
||||
|
||||
Events::Events() {
|
||||
int_events = this;
|
||||
}
|
||||
|
||||
Events &Events::getReference() {
|
||||
return *int_events;
|
||||
}
|
||||
|
||||
|
||||
bool Events::pollEvent() {
|
||||
if (!g_system->getEventManager()->pollEvent(_event)) return false;
|
||||
|
||||
// Handle keypress
|
||||
switch (_event.type) {
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
case Common::EVENT_RBUTTONUP:
|
||||
case Common::EVENT_MBUTTONDOWN:
|
||||
case Common::EVENT_MBUTTONUP:
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
case Common::EVENT_WHEELUP:
|
||||
case Common::EVENT_WHEELDOWN:
|
||||
Mouse::getReference().handleEvent(_event);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Events::waitForPress() {
|
||||
bool keyButton = false;
|
||||
while (!keyButton) {
|
||||
while (pollEvent()) {
|
||||
if ((_event.type == Common::EVENT_QUIT) || (_event.type == Common::EVENT_RETURN_TO_LAUNCHER)) return;
|
||||
else if (((_event.type == Common::EVENT_KEYDOWN) && (_event.kbd.ascii != 0)) ||
|
||||
((_event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) && (_event.customType != kActionNone)))
|
||||
keyButton = true;
|
||||
else if ((_event.type == Common::EVENT_LBUTTONDOWN) ||
|
||||
(_event.type == Common::EVENT_MBUTTONDOWN) ||
|
||||
(_event.type == Common::EVENT_RBUTTONDOWN)) {
|
||||
keyButton = true;
|
||||
Mouse::getReference().waitForRelease();
|
||||
}
|
||||
}
|
||||
g_system->delayMillis(20);
|
||||
}
|
||||
}
|
||||
|
||||
// interruptableDelay
|
||||
// Delays for a given number of milliseconds. If it returns true, it indicates that
|
||||
// the Escape has been pressed to abort whatever sequence is being displayed
|
||||
|
||||
bool Events::interruptableDelay(uint32 milliseconds) {
|
||||
Events &events = Events::getReference();
|
||||
LureEngine &engine = LureEngine::getReference();
|
||||
uint32 delayCtr = g_system->getMillis() + milliseconds;
|
||||
|
||||
while (g_system->getMillis() < delayCtr) {
|
||||
if (engine.shouldQuit()) return true;
|
||||
|
||||
if (events.pollEvent()) {
|
||||
if (((events.type() == Common::EVENT_KEYDOWN) && (events.event().kbd.ascii != 0)) ||
|
||||
((events.type() == Common::EVENT_CUSTOM_ENGINE_ACTION_START) && (events.customType() != kActionNone)) ||
|
||||
(events.type() == Common::EVENT_LBUTTONDOWN))
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 delayAmount = delayCtr - g_system->getMillis();
|
||||
if (delayAmount > 10) delayAmount = 10;
|
||||
g_system->delayMillis(delayAmount);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
79
engines/lure/events.h
Normal file
79
engines/lure/events.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_EVENTS_H
|
||||
#define LURE_EVENTS_H
|
||||
|
||||
|
||||
#include "common/events.h"
|
||||
#include "common/str.h"
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/disk.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
class Mouse {
|
||||
private:
|
||||
CursorType _cursorNum;
|
||||
int16 _x, _y;
|
||||
bool _lButton, _rButton, _mButton;
|
||||
public:
|
||||
Mouse();
|
||||
~Mouse();
|
||||
static Mouse &getReference();
|
||||
void handleEvent(Common::Event event);
|
||||
|
||||
void cursorOn();
|
||||
void cursorOff();
|
||||
void setCursorNum(CursorType cursorNum);
|
||||
void setCursorNum(CursorType cursorNum, int hotspotX, int hotspotY);
|
||||
CursorType getCursorNum() { return _cursorNum; }
|
||||
void setPosition(int x, int y);
|
||||
int16 x() { return _x; }
|
||||
int16 y() { return _y; }
|
||||
bool lButton() { return _lButton; }
|
||||
bool rButton() { return _rButton; }
|
||||
bool mButton() { return _mButton; }
|
||||
void waitForRelease();
|
||||
void pushCursorNum(CursorType cursorNum);
|
||||
void pushCursorNum(CursorType cursorNum, int hotspotX, int hotspotY);
|
||||
void popCursor();
|
||||
};
|
||||
|
||||
class Events {
|
||||
private:
|
||||
Common::Event _event;
|
||||
public:
|
||||
Events();
|
||||
static Events &getReference();
|
||||
|
||||
bool pollEvent();
|
||||
void waitForPress();
|
||||
bool interruptableDelay(uint32 milliseconds);
|
||||
|
||||
Common::Event event() { return _event; }
|
||||
Common::EventType type() { return _event.type; }
|
||||
Common::CustomEventType customType() { return _event.customType; };
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
617
engines/lure/fights.cpp
Normal file
617
engines/lure/fights.cpp
Normal file
@@ -0,0 +1,617 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lure/fights.h"
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/game.h"
|
||||
#include "lure/lure.h"
|
||||
#include "lure/res.h"
|
||||
#include "lure/room.h"
|
||||
#include "lure/sound.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
// Three records containing initial states for player, pig, and Skorl
|
||||
const FighterRecord initialFighterList[3] = {
|
||||
{0x23C, 0x440, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
|
||||
{0, 0x441, 0x1092, 0, 3, 0, 0, 0, 0xB94, 8, 0xA34, 0x8D4, 0xD06, 0,
|
||||
0, 0, 0, 0, 0xDDC, PLAYER_ID},
|
||||
{0, 0x446, 0x1092, 0, 3, 0, 0, 0, 0xB94, 8, 0xA34, 0x8D4, 0xD06, 0,
|
||||
0, 0, 0, 0, 0xDDC, PLAYER_ID}
|
||||
};
|
||||
|
||||
FightsManager *int_fights = nullptr;
|
||||
|
||||
FightsManager::FightsManager() : _rnd(LureEngine::getReference().rnd()) {
|
||||
int_fights = this;
|
||||
_fightData = nullptr;
|
||||
_mouseFlags = 0;
|
||||
_keyDown = KS_UP;
|
||||
reset();
|
||||
}
|
||||
|
||||
FightsManager::~FightsManager() {
|
||||
// Release the fight data
|
||||
delete _fightData;
|
||||
}
|
||||
|
||||
FightsManager &FightsManager::getReference() {
|
||||
return *int_fights;
|
||||
}
|
||||
|
||||
FighterRecord &FightsManager::getDetails(uint16 hotspotId) {
|
||||
if (hotspotId == PLAYER_ID) return _fighterList[0];
|
||||
else if (hotspotId == PIG_ID) return _fighterList[1];
|
||||
else if (hotspotId == SKORL_FIGHTER_ID) return _fighterList[2];
|
||||
error("Unknown NPC %d attempted to fight", hotspotId);
|
||||
}
|
||||
|
||||
// Sets up the data for the pig fight in the cave
|
||||
|
||||
void FightsManager::setupPigFight() {
|
||||
Resources &res = Resources::getReference();
|
||||
Hotspot *player = res.getActiveHotspot(PLAYER_ID);
|
||||
player->setSkipFlag(false);
|
||||
player->resource()->colorOffset = 16;
|
||||
player->setTickProc(PLAYER_FIGHT_TICK_PROC_ID);
|
||||
player->setSize(48, 53);
|
||||
player->setAnimationIndex(PLAYER_FIGHT_ANIM_INDEX);
|
||||
player->resource()->width = 48;
|
||||
player->resource()->height = 53;
|
||||
|
||||
player->setOccupied(false);
|
||||
player->setPosition(262, 94);
|
||||
FighterRecord &rec = getDetails(PLAYER_ID);
|
||||
rec.fwhits = 0;
|
||||
rec.fwtrue_x = 262;
|
||||
rec.fwtrue_y = 53;
|
||||
rec.fwseq_ad = FIGHT_PLAYER_INIT;
|
||||
rec.fwenemy_ad = PIG_ID;
|
||||
}
|
||||
|
||||
void FightsManager::setupSkorlFight() {
|
||||
Resources &res = Resources::getReference();
|
||||
Hotspot *player = res.getActiveHotspot(PLAYER_ID);
|
||||
FighterRecord &rec = getDetails(PLAYER_ID);
|
||||
|
||||
setupPigFight();
|
||||
|
||||
rec.fwenemy_ad = SKORL_FIGHTER_ID;
|
||||
rec.fwweapon = 0x445;
|
||||
rec.fwtrue_x = 282;
|
||||
rec.fwtrue_y = 136;
|
||||
player->setPosition(282, 136);
|
||||
player->resource()->colorOffset = 96;
|
||||
}
|
||||
|
||||
bool FightsManager::isFighting() {
|
||||
FighterRecord &rec = getDetails(PLAYER_ID);
|
||||
return rec.fwhits == 0;
|
||||
}
|
||||
|
||||
void FightsManager::fightLoop() {
|
||||
LureEngine &engine = LureEngine::getReference();
|
||||
Resources &res = Resources::getReference();
|
||||
Game &game = Game::getReference();
|
||||
Room &room = Room::getReference();
|
||||
FighterRecord &playerFight = getDetails(PLAYER_ID);
|
||||
uint32 timerVal = g_system->getMillis();
|
||||
|
||||
// Loop for the duration of the battle
|
||||
while (!engine.shouldQuit() && (playerFight.fwhits != GENERAL_MAGIC_ID)) {
|
||||
checkEvents();
|
||||
|
||||
if (g_system->getMillis() > timerVal + GAME_FRAME_DELAY) {
|
||||
timerVal = g_system->getMillis();
|
||||
|
||||
game.tick();
|
||||
room.update();
|
||||
res.delayList().tick();
|
||||
}
|
||||
|
||||
Screen::getReference().update();
|
||||
|
||||
g_system->delayMillis(10);
|
||||
}
|
||||
}
|
||||
|
||||
void FightsManager::saveToStream(Common::WriteStream *stream) {
|
||||
for (int fighterCtr = 0; fighterCtr < 3; ++fighterCtr) {
|
||||
FighterRecord &rec = _fighterList[fighterCtr];
|
||||
|
||||
stream->writeUint16LE(rec.fwseq_no);
|
||||
stream->writeUint16LE(rec.fwseq_ad);
|
||||
stream->writeUint16LE(rec.fwdist);
|
||||
stream->writeUint16LE(rec.fwwalk_roll);
|
||||
stream->writeUint16LE(rec.fwmove_number);
|
||||
stream->writeUint16LE(rec.fwhits);
|
||||
}
|
||||
}
|
||||
|
||||
void FightsManager::loadFromStream(Common::ReadStream *stream) {
|
||||
reset();
|
||||
|
||||
for (int fighterCtr = 0; fighterCtr < 3; ++fighterCtr) {
|
||||
FighterRecord &rec = _fighterList[fighterCtr];
|
||||
|
||||
rec.fwseq_no = stream->readUint16LE();
|
||||
rec.fwseq_ad = stream->readUint16LE();
|
||||
rec.fwdist = stream->readUint16LE();
|
||||
rec.fwwalk_roll = stream->readUint16LE();
|
||||
rec.fwmove_number = stream->readUint16LE();
|
||||
rec.fwhits = stream->readUint16LE();
|
||||
}
|
||||
}
|
||||
|
||||
void FightsManager::reset() {
|
||||
_fighterList[0] = initialFighterList[0];
|
||||
_fighterList[1] = initialFighterList[1];
|
||||
_fighterList[2] = initialFighterList[2];
|
||||
}
|
||||
|
||||
const CursorType moveList[] = {CURSOR_LEFT_ARROW, CURSOR_FIGHT_UPPER,
|
||||
CURSOR_FIGHT_MIDDLE, CURSOR_FIGHT_LOWER, CURSOR_RIGHT_ARROW};
|
||||
|
||||
struct ActionMapping {
|
||||
Common::CustomEventType customAction;
|
||||
uint8 moveNumber;
|
||||
};
|
||||
|
||||
const ActionMapping actionList[] = {
|
||||
{kActionFightMoveLeft, 10}, {kActionFightMoveRight, 14},
|
||||
{kActionFightCursorLeftTop, 11}, {kActionFightCursorLeftMiddle, 12}, {kActionFightCursorLeftBottom, 13},
|
||||
{kActionFightCursorRightTop, 6}, {kActionFightCursorRightMiddle, 7}, {kActionFightCursorRightBottom, 8},
|
||||
{kActionNone, 0}};
|
||||
|
||||
void FightsManager::checkEvents() {
|
||||
LureEngine &engine = LureEngine::getReference();
|
||||
Events &events = Events::getReference();
|
||||
Mouse &mouse = Mouse::getReference();
|
||||
FighterRecord &rec = getDetails(PLAYER_ID);
|
||||
Hotspot *player = Resources::getReference().getActiveHotspot(PLAYER_ID);
|
||||
int moveNumber = 0;
|
||||
|
||||
while ((moveNumber == 0) && events.pollEvent()) {
|
||||
if (events.type() == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
|
||||
switch (events.event().customType) {
|
||||
case kActionEscape:
|
||||
engine.quitGame();
|
||||
return;
|
||||
default:
|
||||
// Scan through the mapping list for a move for the keypress
|
||||
const ActionMapping *actionPtr = &actionList[0];
|
||||
while ((actionPtr->customAction != kActionNone) &&
|
||||
(actionPtr->customAction != events.event().customType))
|
||||
++actionPtr;
|
||||
if (actionPtr->customAction != kActionNone) {
|
||||
moveNumber = actionPtr->moveNumber;
|
||||
_keyDown = KS_KEYDOWN_1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (events.type() == Common::EVENT_CUSTOM_ENGINE_ACTION_END) {
|
||||
_keyDown = KS_UP;
|
||||
|
||||
} else if (events.type() == Common::EVENT_MOUSEMOVE) {
|
||||
Common::Point mPos = events.event().mouse;
|
||||
if (mPos.x < rec.fwtrue_x - 12)
|
||||
mouse.setCursorNum(CURSOR_LEFT_ARROW);
|
||||
else if (mPos.x > rec.fwtrue_x + player->width())
|
||||
mouse.setCursorNum(CURSOR_RIGHT_ARROW);
|
||||
else if (mPos.y < player->y() + 4)
|
||||
mouse.setCursorNum(CURSOR_FIGHT_UPPER);
|
||||
else if (mPos.y < player->y() + 38)
|
||||
mouse.setCursorNum(CURSOR_FIGHT_MIDDLE);
|
||||
else
|
||||
mouse.setCursorNum(CURSOR_FIGHT_LOWER);
|
||||
|
||||
} else if ((events.type() == Common::EVENT_LBUTTONDOWN) ||
|
||||
(events.type() == Common::EVENT_RBUTTONDOWN) ||
|
||||
(events.type() == Common::EVENT_LBUTTONUP) ||
|
||||
(events.type() == Common::EVENT_RBUTTONUP)) {
|
||||
_mouseFlags = 0;
|
||||
if (events.type() == Common::EVENT_LBUTTONDOWN) ++_mouseFlags;
|
||||
if (events.type() == Common::EVENT_RBUTTONDOWN) _mouseFlags += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (_keyDown == KS_KEYDOWN_2) return;
|
||||
|
||||
// Get the correct base index for the move
|
||||
while ((moveNumber < 5) && (moveList[moveNumber] != mouse.getCursorNum()))
|
||||
++moveNumber;
|
||||
if (moveNumber < 5) {
|
||||
if (_mouseFlags == 1)
|
||||
// Left mouse
|
||||
moveNumber += 10;
|
||||
else if (_mouseFlags == 2)
|
||||
// Right mouse
|
||||
moveNumber += 5;
|
||||
}
|
||||
|
||||
rec.fwmove_number = moveNumber;
|
||||
|
||||
if (_keyDown == KS_KEYDOWN_1)
|
||||
_keyDown = KS_KEYDOWN_2;
|
||||
|
||||
if (rec.fwmove_number >= 5)
|
||||
debugC(ERROR_INTERMEDIATE, kLureDebugFights,
|
||||
"Player fight move number=%d", rec.fwmove_number);
|
||||
}
|
||||
|
||||
void FightsManager::fighterAnimHandler(Hotspot &h) {
|
||||
FighterRecord &fighter = getDetails(h.hotspotId());
|
||||
FighterRecord &opponent = getDetails(fighter.fwenemy_ad);
|
||||
FighterRecord &player = getDetails(PLAYER_ID);
|
||||
|
||||
fetchFighterDistance(fighter, opponent);
|
||||
|
||||
if (fighter.fwseq_ad) {
|
||||
fightHandler(h, fighter.fwseq_ad);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16 seqNum = 0;
|
||||
if (fighter.fwdist != FIGHT_DISTANCE) {
|
||||
seqNum = getFighterMove(fighter, fighter.fwnot_near);
|
||||
} else {
|
||||
uint16 offset = (fighter.fwhits * fighter.fwdef_len) + fighter.fwdefend_adds + 4;
|
||||
|
||||
// Scan for the given sequence
|
||||
uint16 v = getWord(offset);
|
||||
while ((v != 0) && (v != player.fwseq_no)) {
|
||||
offset += 4;
|
||||
v = getWord(offset);
|
||||
}
|
||||
|
||||
if (v == 0) {
|
||||
// No sequence match found
|
||||
seqNum = getFighterMove(fighter, fighter.fwattack_table);
|
||||
} else {
|
||||
v = getWord(offset + 2);
|
||||
seqNum = getFighterMove(fighter, fighter.fwdefend_table);
|
||||
|
||||
if (seqNum == 0)
|
||||
seqNum = getFighterMove(fighter, fighter.fwattack_table);
|
||||
else if (seqNum == 0xff)
|
||||
seqNum = v;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the sequence and pass onto the fight handler
|
||||
fighter.fwseq_no = seqNum;
|
||||
fighter.fwseq_ad = getWord(FIGHT_TBL_1 + (seqNum << 1));
|
||||
}
|
||||
|
||||
void FightsManager::playerAnimHandler(Hotspot &h) {
|
||||
FighterRecord &fighter = getDetails(h.hotspotId());
|
||||
fightHandler(h, fighter.fwseq_ad);
|
||||
}
|
||||
|
||||
void FightsManager::fightHandler(Hotspot &h, uint16 moveOffset) {
|
||||
Resources &res = Resources::getReference();
|
||||
FighterRecord &fighter = getDetails(h.hotspotId());
|
||||
FighterRecord &opponent = getDetails(fighter.fwenemy_ad);
|
||||
|
||||
uint16 v1, v2;
|
||||
bool breakFlag = false;
|
||||
|
||||
while (!breakFlag) {
|
||||
if (moveOffset == 0) {
|
||||
// Player is doing nothing, so check the move number
|
||||
moveOffset = getWord(FIGHT_PLAYER_MOVE_TABLE + (fighter.fwmove_number << 1));
|
||||
|
||||
debugC(ERROR_DETAILED, kLureDebugFights,
|
||||
"Hotspot %xh fight move=%d, new offset=%xh",
|
||||
h.hotspotId(), fighter.fwmove_number, moveOffset);
|
||||
|
||||
if (moveOffset == 0)
|
||||
return;
|
||||
|
||||
fighter.fwseq_no = fighter.fwmove_number;
|
||||
fighter.fwseq_ad = moveOffset;
|
||||
}
|
||||
|
||||
uint16 moveValue = getWord(moveOffset);
|
||||
debugC(ERROR_DETAILED, kLureDebugFights,
|
||||
"Hotspot %xh script offset=%xh value=%xh",
|
||||
h.hotspotId(), moveOffset, moveValue);
|
||||
moveOffset += sizeof(uint16);
|
||||
|
||||
if ((moveValue & 0x8000) == 0) {
|
||||
// Set frame to specified number
|
||||
h.setFrameNumber(moveValue);
|
||||
|
||||
// Set the new fighter position
|
||||
int16 newX, newY;
|
||||
newX = h.x() + (int16)getWord(moveOffset);
|
||||
if (newX < 32) newX = 32;
|
||||
if (newX > 240) newX = 240;
|
||||
newY = h.y() + (int16)getWord(moveOffset + 2);
|
||||
h.setPosition(newX, newY);
|
||||
|
||||
if (fighter.fwweapon != 0) {
|
||||
Hotspot *weaponHotspot = res.getActiveHotspot(fighter.fwweapon);
|
||||
assert(weaponHotspot);
|
||||
|
||||
uint16 newFrameNumber = getWord(moveOffset + 4);
|
||||
int16 xChange = (int16)getWord(moveOffset + 6);
|
||||
int16 yChange = (int16)getWord(moveOffset + 8);
|
||||
|
||||
weaponHotspot->setFrameNumber(newFrameNumber);
|
||||
weaponHotspot->setPosition(h.x() + xChange, h.y() + yChange);
|
||||
}
|
||||
|
||||
moveOffset += 5 * sizeof(uint16);
|
||||
fighter.fwseq_ad = moveOffset;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (moveValue) {
|
||||
case 0xFFFA:
|
||||
// Walk left
|
||||
if ((fighter.fwmove_number == 5) || (fighter.fwmove_number == 10)) {
|
||||
if (h.x() < 32) {
|
||||
breakFlag = true;
|
||||
} else {
|
||||
h.setPosition(h.x() - 4, h.y());
|
||||
fighter.fwtrue_x = h.x();
|
||||
if (fetchFighterDistance(fighter, opponent) < FIGHT_DISTANCE) {
|
||||
h.setPosition(h.x() + 4, h.y());
|
||||
fighter.fwtrue_x += 4;
|
||||
breakFlag = true;
|
||||
} else {
|
||||
removeWeapon(fighter.fwweapon);
|
||||
fighter.fwtrue_x = h.x();
|
||||
fighter.fwtrue_y = h.y();
|
||||
|
||||
uint16 frameNum = (fighter.fwwalk_roll == 7) ? 0 :
|
||||
fighter.fwwalk_roll + 1;
|
||||
fighter.fwwalk_roll = frameNum;
|
||||
fighter.fwseq_ad = moveOffset;
|
||||
h.setFrameNumber(frameNum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Signal to start a new action
|
||||
moveOffset = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFFF9:
|
||||
// Walk right
|
||||
if ((fighter.fwmove_number == 9) || (fighter.fwmove_number == 14)) {
|
||||
if (h.x() >= 240) {
|
||||
breakFlag = true;
|
||||
} else {
|
||||
removeWeapon(fighter.fwweapon);
|
||||
h.setPosition(h.x() + 4, h.y());
|
||||
fighter.fwtrue_x = h.x();
|
||||
fighter.fwtrue_y = h.y();
|
||||
|
||||
fighter.fwwalk_roll = (fighter.fwwalk_roll == 0) ? 7 :
|
||||
fighter.fwwalk_roll - 1;
|
||||
fighter.fwseq_ad = moveOffset;
|
||||
h.setFrameNumber(fighter.fwwalk_roll);
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Signal to start a new action
|
||||
moveOffset = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFFEB:
|
||||
// Enemy right
|
||||
removeWeapon(fighter.fwweapon);
|
||||
h.setPosition(h.x() + 4, h.y());
|
||||
fighter.fwtrue_x = h.x();
|
||||
|
||||
if (fetchFighterDistance(fighter, opponent) < FIGHT_DISTANCE) {
|
||||
h.setPosition(h.x() - 4, h.y());
|
||||
fighter.fwtrue_x -= 4;
|
||||
fighter.fwseq_ad = 0;
|
||||
h.setFrameNumber(8);
|
||||
} else {
|
||||
h.setFrameNumber(getWord(moveOffset));
|
||||
moveOffset += sizeof(uint16);
|
||||
fighter.fwseq_ad = moveOffset;
|
||||
}
|
||||
return;
|
||||
|
||||
case 0xFFFB:
|
||||
// End of sequence
|
||||
breakFlag = true;
|
||||
break;
|
||||
|
||||
case 0xFFF8:
|
||||
// Set fight address
|
||||
moveOffset = getWord(moveOffset);
|
||||
break;
|
||||
|
||||
case 0xFFFF:
|
||||
case 0xFFFE:
|
||||
if (moveValue == 0xffff)
|
||||
// Set the animation record
|
||||
h.setAnimation(getWord(moveOffset));
|
||||
else
|
||||
// New set animation record
|
||||
h.setAnimation(getWord(fighter.fwheader_list + (getWord(moveOffset) << 1)));
|
||||
h.setFrameNumber(0);
|
||||
moveOffset += sizeof(uint16);
|
||||
break;
|
||||
|
||||
case 0xFFF7:
|
||||
// On hold
|
||||
if (getWord(moveOffset) == fighter.fwmove_number)
|
||||
moveOffset = getWord(moveOffset + 2);
|
||||
else
|
||||
moveOffset += 2 * sizeof(uint16);
|
||||
break;
|
||||
|
||||
case 0xFFF6:
|
||||
// Not hold
|
||||
if (getWord(moveOffset) == fighter.fwmove_number)
|
||||
moveOffset += 2 * sizeof(uint16);
|
||||
else
|
||||
moveOffset = getWord(moveOffset + 2);
|
||||
break;
|
||||
|
||||
case 0xFFF4:
|
||||
// End sequence
|
||||
fighter.fwseq_no = 0;
|
||||
break;
|
||||
|
||||
case 0xFFF2:
|
||||
// Set defend
|
||||
fighter.fwblocking = getWord(moveOffset);
|
||||
moveOffset += sizeof(uint16);
|
||||
break;
|
||||
|
||||
case 0xFFF1:
|
||||
// If blocking
|
||||
v1 = getWord(moveOffset);
|
||||
v2 = getWord(moveOffset + 2);
|
||||
moveOffset += 2 * sizeof(uint16);
|
||||
|
||||
if (v1 == opponent.fwblocking) {
|
||||
Sound.addSound(42);
|
||||
moveOffset = v2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFFF0:
|
||||
// Check hit
|
||||
v1 = getWord(moveOffset);
|
||||
moveOffset += sizeof(uint16);
|
||||
if (fighter.fwdist <= FIGHT_DISTANCE) {
|
||||
if (h.hotspotId() == PLAYER_ID) {
|
||||
// Player hits opponent
|
||||
Sound.addSound(52);
|
||||
|
||||
if (opponent.fwhits != 5) {
|
||||
opponent.fwseq_ad = v1;
|
||||
if (++opponent.fwhit_value != opponent.fwhit_rate) {
|
||||
opponent.fwhit_value = 0;
|
||||
if (++opponent.fwhits == 5)
|
||||
opponent.fwseq_ad = opponent.fwdie_seq;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Opponent hit player
|
||||
Sound.addSound(37);
|
||||
opponent.fwseq_ad = v1;
|
||||
if (++opponent.fwhits == 10) {
|
||||
// Player has been killed
|
||||
fighter.fwhits = 10;
|
||||
opponent.fwseq_ad = FIGHT_PLAYER_DIES;
|
||||
Sound.addSound(36);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xFFEF:
|
||||
// Save co-ordinates
|
||||
fighter.fwtrue_x = h.x();
|
||||
fighter.fwtrue_y = h.y();
|
||||
break;
|
||||
|
||||
case 0xFFEE:
|
||||
// Restore co-ordinates
|
||||
h.setPosition(fighter.fwtrue_x, fighter.fwtrue_y);
|
||||
break;
|
||||
|
||||
case 0xFFED:
|
||||
// End of game
|
||||
getDetails(PLAYER_ID).fwhits = GENERAL_MAGIC_ID;
|
||||
Game::getReference().setState(GS_RESTORE_RESTART);
|
||||
return;
|
||||
|
||||
case 0xFFEC:
|
||||
// Enemy has been killed
|
||||
enemyKilled();
|
||||
break;
|
||||
|
||||
case 0xFFEA:
|
||||
// Fight sound
|
||||
Sound.addSound(getWord(moveOffset) & 0xff);
|
||||
moveOffset += sizeof(uint16);
|
||||
break;
|
||||
|
||||
default:
|
||||
error("Unknown fight command %xh", moveValue);
|
||||
}
|
||||
}
|
||||
|
||||
fighter.fwseq_no = 0;
|
||||
fighter.fwseq_ad = 0;
|
||||
if (h.hotspotId() == PLAYER_ID)
|
||||
_mouseFlags = 0;
|
||||
}
|
||||
|
||||
uint16 FightsManager::fetchFighterDistance(FighterRecord &f1, FighterRecord &f2) {
|
||||
f1.fwdist = ABS(f1.fwtrue_x - f2.fwtrue_x) & 0xFFF8;
|
||||
return f1.fwdist;
|
||||
}
|
||||
|
||||
void FightsManager::enemyKilled() {
|
||||
Resources &res = Resources::getReference();
|
||||
Hotspot *playerHotspot = res.getActiveHotspot(PLAYER_ID);
|
||||
FighterRecord &playerRec = getDetails(PLAYER_ID);
|
||||
|
||||
playerHotspot->setTickProc(PLAYER_TICK_PROC_ID);
|
||||
playerRec.fwhits = GENERAL_MAGIC_ID;
|
||||
playerHotspot->resource()->colorOffset = 128;
|
||||
playerHotspot->setSize(32, 48);
|
||||
playerHotspot->resource()->width = 32;
|
||||
playerHotspot->resource()->height = 48;
|
||||
playerHotspot->setAnimationIndex(PLAYER_ANIM_INDEX);
|
||||
playerHotspot->setPosition(playerHotspot->x(), playerHotspot->y() + 5);
|
||||
playerHotspot->setDirection(LEFT);
|
||||
|
||||
if (playerHotspot->roomNumber() == 6) {
|
||||
Dialog::show(0xc9f);
|
||||
HotspotData *axeHotspot = res.getHotspot(0x2738);
|
||||
axeHotspot->roomNumber = PLAYER_ID;
|
||||
axeHotspot->flags |= HOTSPOTFLAG_FOUND;
|
||||
|
||||
// Prevent the weapon animation being drawn
|
||||
axeHotspot = res.getHotspot(0x440);
|
||||
axeHotspot->layer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Makes sure the given weapon is off-screen
|
||||
void FightsManager::removeWeapon(uint16 weaponId) {
|
||||
Hotspot *weaponHotspot = Resources::getReference().getActiveHotspot(weaponId);
|
||||
weaponHotspot->setPosition(-32, -32);
|
||||
}
|
||||
|
||||
uint16 FightsManager::getFighterMove(FighterRecord &rec, uint16 baseOffset) {
|
||||
int actionIndex = _rnd.getRandomNumber(31);
|
||||
return getByte(baseOffset + (rec.fwhits << 5) + actionIndex);
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
117
engines/lure/fights.h
Normal file
117
engines/lure/fights.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_FIGHT_H
|
||||
#define LURE_FIGHT_H
|
||||
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/hotspots.h"
|
||||
#include "lure/palette.h"
|
||||
|
||||
#include "common/singleton.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/random.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
struct FighterRecord {
|
||||
uint16 fwheader_list;
|
||||
uint16 fwweapon;
|
||||
uint16 fwdie_seq;
|
||||
uint16 fwhit_value;
|
||||
uint16 fwhit_rate;
|
||||
int16 fwtrue_x;
|
||||
int16 fwtrue_y;
|
||||
uint16 fwblocking;
|
||||
uint16 fwattack_table;
|
||||
uint16 fwdef_len;
|
||||
uint16 fwdefend_table;
|
||||
uint16 fwnot_near;
|
||||
uint16 fwdefend_adds;
|
||||
uint16 fwseq_no;
|
||||
uint16 fwdist;
|
||||
uint16 fwwalk_roll;
|
||||
uint16 fwmove_number;
|
||||
uint16 fwhits;
|
||||
uint16 fwseq_ad;
|
||||
uint16 fwenemy_ad;
|
||||
};
|
||||
|
||||
// Constant references into the fight data
|
||||
#define FIGHT_TBL_1 0x8b8
|
||||
#define FIGHT_PLAYER_MOVE_TABLE 0xDAA
|
||||
#define FIGHT_PLAYER_INIT 0xDC8
|
||||
#define FIGHT_PLAYER_DIES 0xF46
|
||||
|
||||
#define FIGHT_DISTANCE 32
|
||||
|
||||
enum KeyStatus {KS_UP, KS_KEYDOWN_1, KS_KEYDOWN_2};
|
||||
|
||||
class FightsManager {
|
||||
private:
|
||||
MemoryBlock *_fightData;
|
||||
Common::RandomSource &_rnd;
|
||||
uint8 _mouseFlags;
|
||||
KeyStatus _keyDown;
|
||||
FighterRecord _fighterList[3];
|
||||
|
||||
FighterRecord &getDetails(uint16 hotspotId);
|
||||
uint16 fetchFighterDistance(FighterRecord &f1, FighterRecord &f2);
|
||||
void removeWeapon(uint16 weaponId);
|
||||
void enemyKilled();
|
||||
uint16 getFighterMove(FighterRecord &rec, uint16 baseOffset);
|
||||
void checkEvents();
|
||||
void fightHandler(Hotspot &h, uint16 moveOffset);
|
||||
|
||||
inline uint16 getWord(uint16 offset) {
|
||||
if (!_fightData)
|
||||
_fightData = Disk::getReference().getEntry(FIGHT_DATA_RESOURCE_ID);
|
||||
if (offset >= _fightData->size() - 1) error("Invalid fight data index");
|
||||
return READ_LE_UINT16(_fightData->data() + offset);
|
||||
}
|
||||
inline uint8 getByte(uint16 offset) {
|
||||
if (!_fightData)
|
||||
_fightData = Disk::getReference().getEntry(FIGHT_DATA_RESOURCE_ID);
|
||||
if (offset >= _fightData->size()) error("Invalid fight data index");
|
||||
return _fightData->data()[offset];
|
||||
}
|
||||
public:
|
||||
FightsManager();
|
||||
~FightsManager();
|
||||
static FightsManager &getReference();
|
||||
|
||||
void setupPigFight();
|
||||
void setupSkorlFight();
|
||||
bool isFighting();
|
||||
void fightLoop();
|
||||
void saveToStream(Common::WriteStream *stream);
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
void reset();
|
||||
|
||||
void fighterAnimHandler(Hotspot &h);
|
||||
void playerAnimHandler(Hotspot &h);
|
||||
};
|
||||
|
||||
#define Fights FightsManager::getReference()
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
1128
engines/lure/game.cpp
Normal file
1128
engines/lure/game.cpp
Normal file
File diff suppressed because it is too large
Load Diff
95
engines/lure/game.h
Normal file
95
engines/lure/game.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_GAME_H
|
||||
#define LURE_GAME_H
|
||||
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "engines/engine.h"
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/menu.h"
|
||||
#include "lure/palette.h"
|
||||
#include "lure/disk.h"
|
||||
#include "lure/memory.h"
|
||||
#include "lure/screen.h"
|
||||
#include "lure/events.h"
|
||||
#include "lure/debugger.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
enum GameState {GS_ERROR = 1, GS_TICK = 2, GS_TOCK = 4, GS_PROT = 8, GS_RESTART = 0x10,
|
||||
GS_CAUGHT = 0x20, GS_RESTORE = 0x40, GS_FLOPPY = 0x80,
|
||||
GS_RESTORE_RESTART = 0x50};
|
||||
|
||||
class Game {
|
||||
private:
|
||||
bool _fastTextFlag, _soundFlag;
|
||||
uint8 _state;
|
||||
uint16 _tellCommands[MAX_TELL_COMMANDS * 3 + 1];
|
||||
int _numTellCommands;
|
||||
bool _preloadFlag;
|
||||
bool _debugFlag;
|
||||
|
||||
void handleMenuResponse(uint8 selection);
|
||||
void handleClick();
|
||||
void handleRightClickMenu();
|
||||
void handleLeftClick();
|
||||
bool GetTellActions();
|
||||
void doAction(Action action, uint16 hotspotId, uint16 usedId);
|
||||
|
||||
void playerChangeRoom();
|
||||
void displayChuteAnimation();
|
||||
void displayBarrelAnimation();
|
||||
void handleBootParam(int value);
|
||||
bool getYN();
|
||||
bool isMenuAvailable();
|
||||
public:
|
||||
Game();
|
||||
virtual ~Game();
|
||||
|
||||
static bool isCreated();
|
||||
static Game &getReference();
|
||||
void saveToStream(Common::WriteStream *stream);
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
|
||||
void tick();
|
||||
void tickCheck();
|
||||
void nextFrame();
|
||||
void execute();
|
||||
void setState(uint8 flags) { _state = flags; }
|
||||
bool &preloadFlag() { return _preloadFlag; }
|
||||
bool &debugFlag() { return _debugFlag; }
|
||||
bool fastTextFlag() { return _fastTextFlag; }
|
||||
bool soundFlag() { return _soundFlag; }
|
||||
|
||||
// Menu item support methods
|
||||
void doDebugMenu();
|
||||
void doShowCredits();
|
||||
void doQuit();
|
||||
void doRestart();
|
||||
void doTextSpeed();
|
||||
void doSound();
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
4826
engines/lure/hotspots.cpp
Normal file
4826
engines/lure/hotspots.cpp
Normal file
File diff suppressed because it is too large
Load Diff
491
engines/lure/hotspots.h
Normal file
491
engines/lure/hotspots.h
Normal file
@@ -0,0 +1,491 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_HOTSPOTS_H
|
||||
#define LURE_HOTSPOTS_H
|
||||
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/screen.h"
|
||||
#include "lure/disk.h"
|
||||
#include "lure/res_struct.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
#define MAX_NUM_IMPINGING 10
|
||||
|
||||
class Hotspot;
|
||||
class HotspotTickHandlers;
|
||||
|
||||
class Support {
|
||||
private:
|
||||
static bool changeRoomCheckBumped(Hotspot &h);
|
||||
public:
|
||||
static int findIntersectingCharacters(Hotspot &h, uint16 *charList, int16 xp = -1, int16 yp = -1, int roomNumber = -1);
|
||||
static bool checkForIntersectingCharacter(Hotspot &h, int16 xp = -1, int16 yp = -1, int roomNumber = -1);
|
||||
static bool checkRoomChange(Hotspot &h);
|
||||
static void characterChangeRoom(Hotspot &h, uint16 roomNumber,
|
||||
int16 newX, int16 newY, Direction dir);
|
||||
static bool charactersIntersecting(HotspotData *hotspot1, HotspotData *hotspot2);
|
||||
static bool isCharacterInList(uint16 *lst, int numEntries, uint16 charId);
|
||||
};
|
||||
|
||||
typedef void (HotspotTickHandlers::*HandlerMethodPtr)(Hotspot &h);
|
||||
|
||||
class HotspotTickHandlers {
|
||||
private:
|
||||
// Special variable used across multiple calls to followerAnimHandler
|
||||
int countdownCtr;
|
||||
|
||||
// Special variables used across multiple calls to talkAnimHandler
|
||||
TalkEntryData *_talkResponse;
|
||||
uint16 talkDestCharacter;
|
||||
|
||||
// Special variable used across multiple calls to barmanAnimHandler
|
||||
bool ewanXOffset;
|
||||
|
||||
// Support methods
|
||||
void npcRoomChange(Hotspot &h);
|
||||
void talkEndConversation();
|
||||
|
||||
// Handler methods
|
||||
void defaultHandler(Hotspot &h);
|
||||
void voiceBubbleAnimHandler(Hotspot &h);
|
||||
void standardAnimHandler(Hotspot &h);
|
||||
void standardAnimHandler2(Hotspot &h);
|
||||
void standardCharacterAnimHandler(Hotspot &h);
|
||||
void puzzledAnimHandler(Hotspot &h);
|
||||
void roomExitAnimHandler(Hotspot &h);
|
||||
void playerAnimHandler(Hotspot &h);
|
||||
void followerAnimHandler(Hotspot &h);
|
||||
void jailorAnimHandler(Hotspot &h);
|
||||
void sonicRatAnimHandler(Hotspot &h);
|
||||
void droppingTorchAnimHandler(Hotspot &h);
|
||||
void playerSewerExitAnimHandler(Hotspot &h);
|
||||
void fireAnimHandler(Hotspot &h);
|
||||
void sparkleAnimHandler(Hotspot &h);
|
||||
void teaAnimHandler(Hotspot &h);
|
||||
void goewinCaptiveAnimHandler(Hotspot &h);
|
||||
void prisonerAnimHandler(Hotspot &h);
|
||||
void catrionaAnimHandler(Hotspot &h);
|
||||
void morkusAnimHandler(Hotspot &h);
|
||||
void talkAnimHandler(Hotspot &h);
|
||||
void grubAnimHandler(Hotspot &h);
|
||||
void barmanAnimHandler(Hotspot &h);
|
||||
void skorlAnimHandler(Hotspot &h);
|
||||
void gargoyleAnimHandler(Hotspot &h);
|
||||
void goewinShopAnimHandler(Hotspot &h);
|
||||
void skullAnimHandler(Hotspot &h);
|
||||
void dragonFireAnimHandler(Hotspot &h);
|
||||
void castleSkorlAnimHandler(Hotspot &h);
|
||||
void rackSerfAnimHandler(Hotspot &h);
|
||||
void fighterAnimHandler(Hotspot &h);
|
||||
void playerFightAnimHandler(Hotspot &h);
|
||||
public:
|
||||
HotspotTickHandlers();
|
||||
|
||||
HandlerMethodPtr getHandler(uint16 procIndex);
|
||||
};
|
||||
|
||||
class WalkingActionEntry {
|
||||
private:
|
||||
Direction _direction;
|
||||
int _numSteps;
|
||||
public:
|
||||
WalkingActionEntry(Direction dir, int steps): _direction(dir), _numSteps(steps) {}
|
||||
Direction direction() const { return _direction; }
|
||||
int &rawSteps() { return _numSteps; }
|
||||
int numSteps() const;
|
||||
};
|
||||
|
||||
enum PathFinderResult {PF_UNFINISHED, PF_OK, PF_DEST_OCCUPIED, PF_PART_PATH, PF_NO_WALK};
|
||||
|
||||
class PathFinder {
|
||||
private:
|
||||
Hotspot *_hotspot;
|
||||
bool _inUse;
|
||||
typedef Common::List<Common::SharedPtr<WalkingActionEntry> > WalkingActionList;
|
||||
WalkingActionList _list;
|
||||
RoomPathsDecompressedData _layer;
|
||||
int _stepCtr;
|
||||
bool _inProgress;
|
||||
int _countdownCtr;
|
||||
int16 _destX, _destY;
|
||||
int16 _xPos, _yPos;
|
||||
int16 _xCurrent, _yCurrent;
|
||||
int16 _xDestPos, _yDestPos;
|
||||
int16 _xDestCurrent, _yDestCurrent;
|
||||
bool _destOccupied;
|
||||
bool _cellPopulated;
|
||||
uint16 *_pSrc, *_pDest;
|
||||
int _xChangeInc, _xChangeStart;
|
||||
int _yChangeInc, _yChangeStart;
|
||||
int _xCtr, _yCtr;
|
||||
|
||||
void initVars();
|
||||
void processCell(uint16 *p);
|
||||
void scanLine(int numScans, int changeAmount, uint16 *&pEnd, int &v);
|
||||
|
||||
void add(Direction dir, int steps) {
|
||||
_list.push_front(WalkingActionList::value_type(new WalkingActionEntry(dir, steps)));
|
||||
}
|
||||
void addBack(Direction dir, int steps) {
|
||||
_list.push_back(WalkingActionList::value_type(new WalkingActionEntry(dir, steps)));
|
||||
}
|
||||
public:
|
||||
PathFinder(Hotspot *h);
|
||||
void clear();
|
||||
void reset(RoomPathsData &src);
|
||||
PathFinderResult process();
|
||||
Common::String getDebugInfo() const;
|
||||
|
||||
void pop() { _list.erase(_list.begin()); }
|
||||
WalkingActionEntry &top() const { return **_list.begin(); }
|
||||
bool isEmpty() const { return _list.empty(); }
|
||||
int &stepCtr() { return _stepCtr; }
|
||||
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
enum HotspotPrecheckResult {PC_EXECUTE, PC_NOT_IN_ROOM, PC_FAILED, PC_WAIT, PC_EXCESS};
|
||||
|
||||
enum BarPlaceResult {BP_KEEP_TRYING, BP_GOT_THERE, BP_FAIL};
|
||||
|
||||
struct DestStructure {
|
||||
uint8 counter;
|
||||
Common::Point position;
|
||||
};
|
||||
|
||||
|
||||
#define MAX_NUM_FRAMES 16
|
||||
|
||||
class Hotspot {
|
||||
private:
|
||||
HotspotTickHandlers _tickHandlers;
|
||||
HotspotData *_data;
|
||||
uint16 _animId;
|
||||
HotspotAnimData *_anim;
|
||||
HandlerMethodPtr _tickHandler;
|
||||
Surface *_frames;
|
||||
uint16 _hotspotId;
|
||||
uint16 _originalId;
|
||||
uint16 _roomNumber;
|
||||
int16 _startX, _startY;
|
||||
uint16 _height, _width;
|
||||
uint16 _heightCopy, _widthCopy;
|
||||
uint16 _yCorrection;
|
||||
uint16 _charRectY;
|
||||
int8 _talkX, _talkY;
|
||||
uint16 _numFrames;
|
||||
uint16 _frameNumber;
|
||||
Direction _direction;
|
||||
uint8 _layer;
|
||||
uint16 _hotspotScriptOffset;
|
||||
uint8 _colorOffset;
|
||||
bool _persistent;
|
||||
HotspotOverrideData *_override;
|
||||
bool _skipFlag;
|
||||
PathFinder _pathFinder;
|
||||
uint16 _frameWidth;
|
||||
bool _frameStartsUsed;
|
||||
uint16 _frameStarts[MAX_NUM_FRAMES];
|
||||
char _nameBuffer[MAX_HOTSPOT_NAME_SIZE];
|
||||
DestStructure _tempDest;
|
||||
|
||||
// Runtime fields
|
||||
uint16 _frameCtr;
|
||||
uint8 _voiceCtr;
|
||||
int16 _destX, _destY;
|
||||
uint16 _destHotspotId;
|
||||
uint16 _blockedOffset;
|
||||
uint8 _exitCtr;
|
||||
bool _walkFlag;
|
||||
uint16 _startRoomNumber;
|
||||
uint16 _supportValue;
|
||||
|
||||
// Support methods
|
||||
uint16 getTalkId(HotspotData *charHotspot);
|
||||
void startTalk(HotspotData *charHotspot, uint16 id);
|
||||
void startTalkDialog();
|
||||
|
||||
// Action support methods
|
||||
HotspotPrecheckResult actionPrecheck(HotspotData *hotspot);
|
||||
BarPlaceResult getBarPlace();
|
||||
bool findClearBarPlace();
|
||||
bool characterWalkingCheck(uint16 id);
|
||||
void resetDirection();
|
||||
|
||||
// Action set
|
||||
void doGet(HotspotData *hotspot);
|
||||
void doOperate(HotspotData *hotspot);
|
||||
void doOpen(HotspotData *hotspot);
|
||||
void doClose(HotspotData *hotspot);
|
||||
void doLockUnlock(HotspotData *hotspot);
|
||||
void doUse(HotspotData *hotspot);
|
||||
void doGive(HotspotData *hotspot);
|
||||
void doTalkTo(HotspotData *hotspot);
|
||||
void doTell(HotspotData *hotspot);
|
||||
void doLook(HotspotData *hotspot);
|
||||
void doLookAt(HotspotData *hotspot);
|
||||
void doLookThrough(HotspotData *hotspot);
|
||||
void doAsk(HotspotData *hotspot);
|
||||
void doDrink(HotspotData *hotspot);
|
||||
void doStatus(HotspotData *hotspot);
|
||||
void doGoto(HotspotData *hotspot);
|
||||
void doReturn(HotspotData *hotspot);
|
||||
void doBribe(HotspotData *hotspot);
|
||||
void doExamine(HotspotData *hotspot);
|
||||
void npcSetRoomAndBlockedOffset(HotspotData *hotspot);
|
||||
void npcHeySir(HotspotData *hotspot);
|
||||
void npcExecScript(HotspotData *hotspot);
|
||||
void npcResetPausedList(HotspotData *hotspot);
|
||||
void npcSetRandomDest(HotspotData *hotspot);
|
||||
void npcWalkingCheck(HotspotData *hotspot);
|
||||
void npcSetSupportOffset(HotspotData *hotspot);
|
||||
void npcSupportOffsetConditional(HotspotData *hotspot);
|
||||
void npcDispatchAction(HotspotData *hotspot);
|
||||
void npcTalkNpcToNpc(HotspotData *hotspot);
|
||||
void npcPause(HotspotData *hotspot);
|
||||
void npcStartTalking(HotspotData *hotspot);
|
||||
void npcJumpAddress(HotspotData *hotspot);
|
||||
|
||||
// Auxiliaries
|
||||
void doLookAction(HotspotData *hotspot, Action action);
|
||||
public:
|
||||
Hotspot(HotspotData *res);
|
||||
Hotspot(Hotspot *character, uint16 objType);
|
||||
Hotspot();
|
||||
~Hotspot();
|
||||
|
||||
void setAnimation(uint16 newAnimId);
|
||||
void setAnimationIndex(int animIndex);
|
||||
void setAnimation(HotspotAnimData *newRecord);
|
||||
uint16 hotspotId() const { return _hotspotId; }
|
||||
uint16 originalId() const { return _originalId; }
|
||||
Surface &frames() const { return *_frames; }
|
||||
HotspotAnimData &anim() const { return *_anim; }
|
||||
HotspotData *resource() const { return _data; }
|
||||
uint16 numFrames() const { return _numFrames; }
|
||||
uint16 frameNumber() const { return _frameNumber; }
|
||||
void setFrameNumber(uint16 frameNum) {
|
||||
assert(frameNum < _numFrames);
|
||||
_frameNumber = frameNum;
|
||||
}
|
||||
void incFrameNumber();
|
||||
Direction direction() const { return _direction; }
|
||||
uint16 frameWidth() const { return _width; }
|
||||
int16 x() const { return _startX; }
|
||||
int16 y() const { return _startY; }
|
||||
int16 destX() const { return _destX; }
|
||||
int16 destY() const { return _destY; }
|
||||
int8 talkX() const { return _talkX; }
|
||||
int8 talkY() const { return _talkY; }
|
||||
uint16 destHotspotId() const { return _destHotspotId; }
|
||||
uint16 blockedOffset() const { return _blockedOffset; }
|
||||
uint8 exitCtr() const { return _exitCtr; }
|
||||
bool walkFlag() const { return _walkFlag; }
|
||||
uint16 startRoomNumber() const { return _startRoomNumber; }
|
||||
uint16 width() const { return _width; }
|
||||
uint16 height() const { return _height; }
|
||||
uint16 widthCopy() const { return _widthCopy; }
|
||||
uint16 heightCopy() const { return _heightCopy; }
|
||||
uint16 yCorrection() const { return _yCorrection; }
|
||||
uint16 charRectY() const { return _charRectY; }
|
||||
uint16 roomNumber() const { return _roomNumber; }
|
||||
uint16 talkScript() const {
|
||||
assert(_data);
|
||||
return _data->talkScriptOffset;
|
||||
}
|
||||
uint16 hotspotScript() const { return _hotspotScriptOffset; }
|
||||
uint8 layer() const { return _layer; }
|
||||
bool skipFlag() const { return _skipFlag; }
|
||||
void setTickProc(uint16 newVal);
|
||||
bool persistent() const { return _persistent; }
|
||||
void setPersistent(bool value) { _persistent = value; }
|
||||
uint8 colorOffset() const { return _colorOffset; }
|
||||
void setColorOffset(uint8 value) { _colorOffset = value; }
|
||||
void setRoomNumber(uint16 roomNum) {
|
||||
_roomNumber = roomNum;
|
||||
if (_data) _data->roomNumber = roomNum;
|
||||
}
|
||||
uint16 nameId() const;
|
||||
const char *getName();
|
||||
bool isActiveAnimation();
|
||||
void setPosition(int16 newX, int16 newY);
|
||||
void setDestPosition(int16 newX, int16 newY) { _destX = newX; _destY = newY; }
|
||||
void setDestHotspot(uint16 id) { _destHotspotId = id; }
|
||||
void setExitCtr(uint8 value) { _exitCtr = value; }
|
||||
BlockedState blockedState() const {
|
||||
assert(_data);
|
||||
return _data->blockedState;
|
||||
}
|
||||
void setBlockedState(BlockedState newState) {
|
||||
assert(_data);
|
||||
_data->blockedState = newState;
|
||||
}
|
||||
bool blockedFlag() const {
|
||||
assert(_data);
|
||||
return _data->blockedFlag;
|
||||
}
|
||||
void setBlockedFlag(bool newValue) {
|
||||
assert(_data);
|
||||
_data->blockedFlag = newValue;
|
||||
}
|
||||
void setWalkFlag(bool value) { _walkFlag = value; }
|
||||
void setStartRoomNumber(uint16 value) { _startRoomNumber = value; }
|
||||
void setSize(uint16 newWidth, uint16 newHeight);
|
||||
void setWidth(uint16 newWidth) {
|
||||
_width = newWidth;
|
||||
_frameWidth = newWidth;
|
||||
}
|
||||
void setHeight(uint16 newHeight) {
|
||||
_height = newHeight;
|
||||
}
|
||||
void setHotspotScript(uint16 offset) {
|
||||
assert(_data != NULL);
|
||||
_hotspotScriptOffset = offset;
|
||||
_data->hotspotScriptOffset = offset;
|
||||
}
|
||||
void setLayer(uint8 newLayer) {
|
||||
assert(_data != NULL);
|
||||
_layer = newLayer;
|
||||
_data->layer = newLayer;
|
||||
}
|
||||
void setActions(uint32 newActions) {
|
||||
assert(_data);
|
||||
_data->actions = newActions;
|
||||
}
|
||||
void setCharRectY(uint16 value) { _charRectY = value; }
|
||||
void setSkipFlag(bool value) { _skipFlag = value; }
|
||||
CharacterMode characterMode() const {
|
||||
assert(_data != NULL);
|
||||
return _data->characterMode;
|
||||
}
|
||||
void setCharacterMode(CharacterMode value) {
|
||||
assert(_data != NULL);
|
||||
_data->characterMode = value;
|
||||
}
|
||||
uint16 delayCtr() const {
|
||||
assert(_data);
|
||||
return _data->delayCtr;
|
||||
}
|
||||
void setDelayCtr(uint16 value) {
|
||||
assert(_data);
|
||||
_data->delayCtr = value;
|
||||
}
|
||||
uint16 pauseCtr() const {
|
||||
assert(_data);
|
||||
return _data->pauseCtr;
|
||||
}
|
||||
void setPauseCtr(uint16 value) {
|
||||
assert(_data);
|
||||
_data->pauseCtr = value;
|
||||
}
|
||||
VariantBool coveredFlag() const {
|
||||
assert(_data);
|
||||
return _data->coveredFlag;
|
||||
}
|
||||
void setCoveredFlag(VariantBool value) {
|
||||
assert(_data);
|
||||
_data->coveredFlag = value;
|
||||
}
|
||||
uint16 useHotspotId() const {
|
||||
assert(_data);
|
||||
return _data->useHotspotId;
|
||||
}
|
||||
void setUseHotspotId(uint16 value) {
|
||||
assert(_data);
|
||||
_data->useHotspotId = value;
|
||||
}
|
||||
uint16 talkGate() const {
|
||||
assert(_data);
|
||||
return _data->talkGate;
|
||||
}
|
||||
void setTalkGate(uint16 value) {
|
||||
assert(_data);
|
||||
_data->talkGate = value;
|
||||
}
|
||||
uint16 supportValue() const { return _supportValue; }
|
||||
void setSupportValue(uint16 value) { _supportValue = value; }
|
||||
|
||||
void copyTo(Surface *dest);
|
||||
bool executeScript();
|
||||
void tick();
|
||||
bool isRoomExit(uint16 id);
|
||||
|
||||
// Walking
|
||||
void walkTo(int16 endPosX, int16 endPosY, uint16 destHotspot = 0);
|
||||
void stopWalking();
|
||||
void endAction();
|
||||
void setDirection(Direction dir);
|
||||
void faceHotspot(HotspotData *hotspot);
|
||||
void faceHotspot(uint16 hotspotId);
|
||||
void setRandomDest();
|
||||
void setOccupied(bool occupiedFlag);
|
||||
bool walkingStep();
|
||||
void updateMovement();
|
||||
void updateMovement2(CharacterMode value);
|
||||
void resetPosition();
|
||||
bool doorCloseCheck(uint16 doorId);
|
||||
|
||||
void doAction();
|
||||
void doAction(Action action, HotspotData *hotspot);
|
||||
CurrentActionStack ¤tActions() const {
|
||||
assert(_data);
|
||||
return _data->npcSchedule;
|
||||
}
|
||||
PathFinder &pathFinder() { return _pathFinder; }
|
||||
DestStructure &tempDest() { return _tempDest; }
|
||||
uint16 frameCtr() const { return _frameCtr; }
|
||||
void setFrameCtr(uint16 value) { _frameCtr = value; }
|
||||
void decrFrameCtr() { if (_frameCtr > 0) --_frameCtr; }
|
||||
uint8 actionCtr() const {
|
||||
assert(_data);
|
||||
return _data->actionCtr;
|
||||
}
|
||||
void setActionCtr(uint8 v) {
|
||||
assert(_data);
|
||||
_data->actionCtr = v;
|
||||
}
|
||||
uint8 voiceCtr() const { return _voiceCtr; }
|
||||
void setVoiceCtr(uint8 v) { _voiceCtr = v; }
|
||||
|
||||
// Miscellaneous
|
||||
void doNothing(HotspotData *hotspot);
|
||||
void converse(uint16 destCharacterId, uint16 messageId, bool srcStandStill = false,
|
||||
bool destStandStill = false);
|
||||
void showMessage(uint16 messageId, uint16 destCharacterId = NOONE_ID);
|
||||
void scheduleConverse(uint16 destHotspot, uint16 messageId);
|
||||
void handleTalkDialog();
|
||||
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
class HotspotList: public Common::List<Common::SharedPtr<Hotspot> > {
|
||||
public:
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
255
engines/lure/intro.cpp
Normal file
255
engines/lure/intro.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lure/lure.h"
|
||||
#include "lure/intro.h"
|
||||
#include "lure/animseq.h"
|
||||
#include "lure/events.h"
|
||||
#include "lure/sound.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
struct AnimRecord {
|
||||
// The resource ID of the animation
|
||||
uint16 resourceId;
|
||||
// The index of the palette to use
|
||||
uint8 paletteIndex;
|
||||
// The time that should pass before starting the animation
|
||||
uint16 initialPause;
|
||||
// The time that should pass at the last screen of the animation
|
||||
uint16 endingPause;
|
||||
// The number of the sound that should play during the animation
|
||||
uint8 soundNumber;
|
||||
// True if the sound should fade out before the transition
|
||||
bool fadeOutSound;
|
||||
// The time that should pass on the last animation screen before the
|
||||
// next sound is played
|
||||
uint16 soundTransitionPause;
|
||||
// The number of the sound that should be played on the last screen
|
||||
// (0xFF for none)
|
||||
uint8 soundNumber2;
|
||||
};
|
||||
|
||||
static const uint16 start_screens[] = {0x18, 0x1A, 0x1E, 0x1C, 0};
|
||||
static const AnimRecord anim_screens[] = {
|
||||
// Note that the ending pause of the first anim is about 8 seconds in the
|
||||
// original interpreter vs about 14 seconds here. 8 seconds is quite short
|
||||
// to read the screen, so I kept it at 14. Conveniently, the first music
|
||||
// track is much longer than what was actually used in the game.
|
||||
{0x40, 0, 0x314, 0x2BE, 0x00, true, 0x1F4, 0x01}, // The kingdom was at peace
|
||||
{0x42, 1, 0, 0x5FA, 0x01, false, 0, 0xFF}, // Cliff overhang
|
||||
{0x44, 2, 0, 0, 0x02, false, 0, 0xFF}, // Siluette in moonlight
|
||||
{0x24, 3, 0, 0x62C + 0x24, 0xFF, false, 0x328, 0x03}, // Exposition of reaching town
|
||||
{0x46, 3, 0, 0, 0x03, false, 0, 0xFF}, // Skorl approaches
|
||||
{0, 0, 0, 0, 0xFF, false, 0, 0xFF}};
|
||||
|
||||
// showScreen
|
||||
// Shows a screen by loading it from the given resource, and then fading it in
|
||||
// with a palette in the following resource. Returns true if the introduction
|
||||
// should be aborted
|
||||
|
||||
bool Introduction::showScreen(uint16 screenId, uint16 paletteId, uint16 delaySize, bool fadeOut) {
|
||||
Screen &screen = Screen::getReference();
|
||||
bool isEGA = LureEngine::getReference().isEGA();
|
||||
screen.screen().loadScreen(screenId);
|
||||
screen.update();
|
||||
Palette p(paletteId);
|
||||
|
||||
if (LureEngine::getReference().shouldQuit()) return true;
|
||||
|
||||
if (isEGA) screen.setPalette(&p);
|
||||
else screen.paletteFadeIn(&p);
|
||||
|
||||
bool result = interruptableDelay(delaySize);
|
||||
if (LureEngine::getReference().shouldQuit()) return true;
|
||||
|
||||
if (fadeOut && !isEGA)
|
||||
screen.paletteFadeOut();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// interruptableDelay
|
||||
// Delays for a given number of milliseconds. If it returns true, it indicates that
|
||||
// the Escape has been pressed to abort whatever sequence is being displayed
|
||||
|
||||
bool Introduction::interruptableDelay(uint32 milliseconds) {
|
||||
Events &events = Events::getReference();
|
||||
|
||||
if (events.interruptableDelay(milliseconds)) {
|
||||
if (events.type() == Common::EVENT_CUSTOM_ENGINE_ACTION_START)
|
||||
return events.event().customType == kActionEscape;
|
||||
else if (LureEngine::getReference().shouldQuit())
|
||||
return true;
|
||||
else if (events.type() == Common::EVENT_LBUTTONDOWN)
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// show
|
||||
// Main method for the introduction sequence
|
||||
|
||||
bool Introduction::show() {
|
||||
Screen &screen = Screen::getReference();
|
||||
bool isEGA = LureEngine::getReference().isEGA();
|
||||
screen.setPaletteEmpty();
|
||||
|
||||
// Initial game company and then game screen
|
||||
|
||||
for (int ctr = 0; ctr < 3; ++ctr)
|
||||
if (showScreen(start_screens[ctr], start_screens[ctr] + 1, 5000))
|
||||
return true;
|
||||
|
||||
// Title screen
|
||||
if (showScreen(start_screens[3], start_screens[3] + 1, 5000, false))
|
||||
return true;
|
||||
|
||||
bool result = Sound.initCustomTimbres(true);
|
||||
if (result)
|
||||
return true;
|
||||
|
||||
// Fade out title screen
|
||||
if (!isEGA)
|
||||
screen.paletteFadeOut();
|
||||
|
||||
PaletteCollection coll(0x32);
|
||||
Palette EgaPalette(0x1D);
|
||||
|
||||
// Animated screens
|
||||
|
||||
AnimationSequence *anim;
|
||||
_currentSound = 0xFF;
|
||||
const AnimRecord *curr_anim = anim_screens;
|
||||
for (; curr_anim->resourceId; ++curr_anim) {
|
||||
// Handle sound selection
|
||||
playMusic(curr_anim->soundNumber, false);
|
||||
|
||||
bool fadeIn = curr_anim == anim_screens;
|
||||
anim = new AnimationSequence(curr_anim->resourceId,
|
||||
isEGA ? EgaPalette : coll.getPalette(curr_anim->paletteIndex), fadeIn,
|
||||
(curr_anim->resourceId == 0x44) ? 4 : 7);
|
||||
|
||||
if (curr_anim->initialPause != 0) {
|
||||
if (interruptableDelay(curr_anim->initialPause * 1000 / 50)) {
|
||||
delete anim;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
result = false;
|
||||
switch (anim->show()) {
|
||||
case ABORT_NONE:
|
||||
if (curr_anim->endingPause != 0) {
|
||||
if (curr_anim->soundTransitionPause != 0) {
|
||||
uint16 pause = curr_anim->soundTransitionPause * 1000 / 50;
|
||||
if (curr_anim->fadeOutSound) {
|
||||
pause -= 3500;
|
||||
}
|
||||
// Wait before transitioning to the next track
|
||||
result = interruptableDelay(pause);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
result = playMusic(curr_anim->soundNumber2, curr_anim->fadeOutSound);
|
||||
|
||||
if (!result)
|
||||
// Wait remaining time before the next animation
|
||||
result = interruptableDelay((curr_anim->endingPause - curr_anim->soundTransitionPause) * 1000 / 50);
|
||||
}
|
||||
break;
|
||||
|
||||
case ABORT_END_INTRO:
|
||||
result = true;
|
||||
break;
|
||||
|
||||
case ABORT_NEXT_SCENE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
delete anim;
|
||||
|
||||
if (result) {
|
||||
Sound.musicInterface_KillAll();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fade out last cutscene screen
|
||||
if (!isEGA)
|
||||
screen.paletteFadeOut();
|
||||
|
||||
// Show battle pictures one frame at a time
|
||||
|
||||
result = false;
|
||||
anim = new AnimationSequence(0x48, isEGA ? EgaPalette : coll.getPalette(4), false);
|
||||
do {
|
||||
result = interruptableDelay(2000);
|
||||
if (isEGA) {
|
||||
screen.empty();
|
||||
} else {
|
||||
screen.paletteFadeOut();
|
||||
}
|
||||
if (!result) result = interruptableDelay(500);
|
||||
if (result) break;
|
||||
} while (anim->step());
|
||||
delete anim;
|
||||
|
||||
if (!result) {
|
||||
// Show final introduction animation
|
||||
if (!isEGA)
|
||||
showScreen(0x22, 0x21, 33300);
|
||||
else {
|
||||
Palette finalPalette(0x21);
|
||||
anim = new AnimationSequence(0x22, finalPalette, false);
|
||||
delete anim;
|
||||
interruptableDelay(34000);
|
||||
}
|
||||
}
|
||||
|
||||
Sound.musicInterface_KillAll();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Introduction::playMusic(uint8 soundNumber, bool fadeOut) {
|
||||
bool result = false;
|
||||
|
||||
if (soundNumber != 0xFF && _currentSound != soundNumber) {
|
||||
// Stop the previous sound
|
||||
if (fadeOut) {
|
||||
result = Sound.fadeOut();
|
||||
if (!result)
|
||||
result = interruptableDelay(500);
|
||||
} else {
|
||||
Sound.musicInterface_KillAll();
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
_currentSound = soundNumber;
|
||||
Sound.musicInterface_Play(_currentSound, true);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
44
engines/lure/intro.h
Normal file
44
engines/lure/intro.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_INTRO_H
|
||||
#define LURE_INTRO_H
|
||||
|
||||
#include "lure/screen.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
class Introduction {
|
||||
public:
|
||||
Introduction() : _currentSound(0xFF) { }
|
||||
|
||||
bool show();
|
||||
private:
|
||||
uint8 _currentSound;
|
||||
|
||||
bool showScreen(uint16 screenId, uint16 paletteId, uint16 delaySize, bool fadeOut = true);
|
||||
bool interruptableDelay(uint32 milliseconds);
|
||||
bool playMusic(uint8 soundNumber, bool fadeOut);
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
308
engines/lure/lure.cpp
Normal file
308
engines/lure/lure.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "common/system.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/translation.h"
|
||||
#include "common/text-to-speech.h"
|
||||
|
||||
#include "engines/util.h"
|
||||
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/surface.h"
|
||||
#include "lure/lure.h"
|
||||
#include "lure/intro.h"
|
||||
#include "lure/game.h"
|
||||
#include "lure/sound.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
static LureEngine *int_engine = nullptr;
|
||||
|
||||
LureEngine::LureEngine(OSystem *system, const LureGameDescription *gameDesc)
|
||||
: Engine(system), _gameDescription(gameDesc), _rnd("lure") {
|
||||
}
|
||||
|
||||
Common::Error LureEngine::init() {
|
||||
int_engine = this;
|
||||
_initialized = false;
|
||||
_saveLoadAllowed = false;
|
||||
|
||||
initGraphics(FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
|
||||
|
||||
// Check the version of the lure.dat file
|
||||
Common::File f;
|
||||
VersionStructure version;
|
||||
if (!f.open(SUPPORT_FILENAME)) {
|
||||
GUIErrorMessageFormat(_("Unable to locate the '%s' engine data file."), SUPPORT_FILENAME);
|
||||
return Common::kUnknownError;
|
||||
}
|
||||
|
||||
f.seek(0xbf * 8);
|
||||
f.read(&version, sizeof(VersionStructure));
|
||||
f.close();
|
||||
|
||||
if (READ_LE_UINT16(&version.id) != 0xffff) {
|
||||
GUIErrorMessageFormat(_("The '%s' engine data file is corrupt."), SUPPORT_FILENAME);
|
||||
return Common::kUnknownError;
|
||||
} else if ((version.vMajor != LURE_DAT_MAJOR) || (version.vMinor != LURE_DAT_MINOR)) {
|
||||
GUIErrorMessageFormat(_("Incorrect version of the '%s' engine data file found. Expected %d.%d but got %d.%d."),
|
||||
SUPPORT_FILENAME, LURE_DAT_MAJOR, LURE_DAT_MINOR,
|
||||
version.vMajor, version.vMinor);
|
||||
return Common::kUnknownError;
|
||||
}
|
||||
|
||||
_disk = new Disk();
|
||||
_resources = new Resources();
|
||||
_strings = new StringData();
|
||||
_screen = new Screen(*_system);
|
||||
_mouse = new Mouse();
|
||||
_events = new Events();
|
||||
_menu = new Menu();
|
||||
Surface::initialize();
|
||||
_room = new Room();
|
||||
_fights = new FightsManager();
|
||||
|
||||
_gameToLoad = -1;
|
||||
_initialized = true;
|
||||
|
||||
// Setup mixer
|
||||
syncSoundSettings();
|
||||
|
||||
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
|
||||
if (ttsMan != nullptr)
|
||||
ttsMan->enable(ConfMan.getBool("tts_narrator"));
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
LureEngine::~LureEngine() {
|
||||
if (_initialized) {
|
||||
// Delete and deinitialize subsystems
|
||||
Surface::deinitialize();
|
||||
Sound.destroy();
|
||||
delete _fights;
|
||||
delete _room;
|
||||
delete _menu;
|
||||
delete _events;
|
||||
delete _mouse;
|
||||
delete _screen;
|
||||
delete _strings;
|
||||
delete _resources;
|
||||
delete _disk;
|
||||
}
|
||||
}
|
||||
|
||||
LureEngine &LureEngine::getReference() {
|
||||
return *int_engine;
|
||||
}
|
||||
|
||||
Common::Error LureEngine::go() {
|
||||
Game *gameInstance = new Game();
|
||||
|
||||
// If requested, load a savegame instead of showing the intro
|
||||
if (ConfMan.hasKey("save_slot")) {
|
||||
_gameToLoad = ConfMan.getInt("save_slot");
|
||||
if (_gameToLoad < 0 || _gameToLoad > 999)
|
||||
_gameToLoad = -1;
|
||||
}
|
||||
|
||||
if (_gameToLoad == -1) {
|
||||
if (ConfMan.getBool("copy_protection")) {
|
||||
CopyProtectionDialog *dialog = new CopyProtectionDialog();
|
||||
bool result = dialog->show();
|
||||
delete dialog;
|
||||
if (shouldQuit()) {
|
||||
delete gameInstance;
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
if (!result)
|
||||
error("Sorry - copy protection failed");
|
||||
}
|
||||
|
||||
if (ConfMan.getInt("boot_param") == 0) {
|
||||
// Show the introduction
|
||||
Sound.loadSection(Sound.isRoland() ? ROLAND_INTRO_SOUND_RESOURCE_ID : ADLIB_INTRO_SOUND_RESOURCE_ID);
|
||||
Introduction *intro = new Introduction();
|
||||
intro->show();
|
||||
delete intro;
|
||||
}
|
||||
}
|
||||
|
||||
// Play the game
|
||||
if (!shouldQuit()) {
|
||||
_screen->empty();
|
||||
|
||||
if (Sound.isRoland() && Sound.hasNativeMT32()) {
|
||||
// Initialize Roland MT-32 timbres
|
||||
Sound.loadSection(ROLAND_MAIN_SYSEX_RESOURCE_ID);
|
||||
Sound.initCustomTimbres();
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldQuit()) {
|
||||
// Play the game
|
||||
_saveLoadAllowed = true;
|
||||
Sound.loadSection(Sound.isRoland() ? ROLAND_MAIN_SOUND_RESOURCE_ID : ADLIB_MAIN_SOUND_RESOURCE_ID);
|
||||
gameInstance->execute();
|
||||
}
|
||||
|
||||
delete gameInstance;
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
void LureEngine::pauseEngineIntern(bool pause) {
|
||||
Engine::pauseEngineIntern(pause);
|
||||
|
||||
if (pause) {
|
||||
Sound.pause();
|
||||
} else {
|
||||
Sound.resume();
|
||||
}
|
||||
}
|
||||
|
||||
const char *LureEngine::generateSaveName(int slotNumber) {
|
||||
static char buffer[15];
|
||||
|
||||
Common::sprintf_s(buffer, "lure.%.3d", slotNumber);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool LureEngine::saveGame(uint8 slotNumber, Common::String &caption) {
|
||||
Common::WriteStream *f = this->_saveFileMan->openForSaving(
|
||||
generateSaveName(slotNumber));
|
||||
if (f == nullptr)
|
||||
return false;
|
||||
|
||||
f->write("lure", 5);
|
||||
f->writeByte(getLureLanguage());
|
||||
f->writeByte(LURE_SAVEGAME_MINOR);
|
||||
f->writeString(caption);
|
||||
f->writeByte(0); // End of string terminator
|
||||
|
||||
Resources::getReference().saveToStream(f);
|
||||
Game::getReference().saveToStream(f);
|
||||
Sound.saveToStream(f);
|
||||
Fights.saveToStream(f);
|
||||
Room::getReference().saveToStream(f);
|
||||
|
||||
delete f;
|
||||
return true;
|
||||
}
|
||||
|
||||
#define FAILED_MSG "loadGame: Failed to load slot %d"
|
||||
|
||||
bool LureEngine::loadGame(uint8 slotNumber) {
|
||||
Common::ReadStream *f = this->_saveFileMan->openForLoading(
|
||||
generateSaveName(slotNumber));
|
||||
if (f == nullptr)
|
||||
return false;
|
||||
|
||||
// Check for header
|
||||
char buffer[5];
|
||||
f->read(buffer, 5);
|
||||
if (memcmp(buffer, "lure", 5) != 0) {
|
||||
warning(FAILED_MSG, slotNumber);
|
||||
delete f;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check language version
|
||||
uint8 language = f->readByte();
|
||||
_saveVersion = f->readByte();
|
||||
if ((language != getLureLanguage()) || (_saveVersion < LURE_MIN_SAVEGAME_MINOR)) {
|
||||
warning("loadGame: Failed to load slot %d - incorrect version", slotNumber);
|
||||
delete f;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read in and discard the savegame caption
|
||||
while (f->readByte() != 0)
|
||||
;
|
||||
|
||||
// Load in the data
|
||||
Resources::getReference().loadFromStream(f);
|
||||
Game::getReference().loadFromStream(f);
|
||||
Sound.loadFromStream(f);
|
||||
Fights.loadFromStream(f);
|
||||
Room::getReference().loadFromStream(f);
|
||||
|
||||
delete f;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LureEngine::syncSoundSettings() {
|
||||
Engine::syncSoundSettings();
|
||||
|
||||
Sound.syncSounds();
|
||||
}
|
||||
|
||||
Common::String *LureEngine::detectSave(int slotNumber) {
|
||||
Common::ReadStream *f = this->_saveFileMan->openForLoading(
|
||||
generateSaveName(slotNumber));
|
||||
if (f == nullptr) return nullptr;
|
||||
Common::String *result = nullptr;
|
||||
|
||||
// Check for header
|
||||
char buffer[5];
|
||||
f->read(&buffer[0], 5);
|
||||
if (memcmp(&buffer[0], "lure", 5) == 0) {
|
||||
// Check language version
|
||||
uint8 language = f->readByte();
|
||||
uint8 version = f->readByte();
|
||||
if ((language == getLureLanguage()) && (version >= LURE_MIN_SAVEGAME_MINOR)) {
|
||||
// Read in the savegame title
|
||||
char saveName[MAX_DESC_SIZE];
|
||||
char *p = saveName;
|
||||
int decCtr = MAX_DESC_SIZE - 1;
|
||||
while ((decCtr > 0) && ((*p++ = f->readByte()) != 0)) --decCtr;
|
||||
*p = '\0';
|
||||
result = new Common::String(saveName);
|
||||
}
|
||||
}
|
||||
|
||||
delete f;
|
||||
return result;
|
||||
}
|
||||
|
||||
Common::String getSaveName(Common::InSaveFile *in) {
|
||||
// Check for header
|
||||
char saveName[MAX_DESC_SIZE];
|
||||
char buffer[5];
|
||||
in->read(&buffer[0], 5);
|
||||
if (memcmp(&buffer[0], "lure", 5) == 0) {
|
||||
// Check language version
|
||||
in->readByte();
|
||||
in->readByte();
|
||||
char *p = saveName;
|
||||
int decCtr = MAX_DESC_SIZE - 1;
|
||||
while ((decCtr > 0) && ((*p++ = in->readByte()) != 0)) --decCtr;
|
||||
*p = '\0';
|
||||
|
||||
}
|
||||
|
||||
return Common::String(saveName);
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
166
engines/lure/lure.h
Normal file
166
engines/lure/lure.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_LURE_H
|
||||
#define LURE_LURE_H
|
||||
|
||||
#include "engines/engine.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "common/rect.h"
|
||||
#include "common/file.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/util.h"
|
||||
#include "common/random.h"
|
||||
|
||||
#include "lure/disk.h"
|
||||
#include "lure/res.h"
|
||||
#include "lure/screen.h"
|
||||
#include "lure/events.h"
|
||||
#include "lure/menu.h"
|
||||
#include "lure/strings.h"
|
||||
#include "lure/room.h"
|
||||
#include "lure/fights.h"
|
||||
#include "lure/detection.h"
|
||||
|
||||
/**
|
||||
* This is the namespace of the Lure engine.
|
||||
*
|
||||
* Status of this engine: Complete
|
||||
*
|
||||
* Games using this engine:
|
||||
* - Lure of the Temptress
|
||||
*/
|
||||
namespace Lure {
|
||||
|
||||
#define RandomNumberGen LureEngine::getReference().rnd()
|
||||
|
||||
enum LureLanguage {
|
||||
LANG_IT_ITA = 10,
|
||||
LANG_FR_FRA = 6,
|
||||
LANG_DE_DEU = 7,
|
||||
LANG_ES_ESP = 17,
|
||||
LANG_EN_ANY = 3,
|
||||
LANG_RU_RUS = 5,
|
||||
LANG_EN_KONAMI = 4,
|
||||
LANG_UNKNOWN = -1
|
||||
};
|
||||
|
||||
enum LUREActions {
|
||||
kActionNone,
|
||||
kActionSaveGame,
|
||||
kActionRestoreGame,
|
||||
kActionRestartGame,
|
||||
kActionQuitGame,
|
||||
kActionEscape,
|
||||
kActionFightMoveLeft,
|
||||
kActionFightMoveRight,
|
||||
kActionFightCursorLeftTop,
|
||||
kActionFightCursorLeftMiddle,
|
||||
kActionFightCursorLeftBottom,
|
||||
kActionFightCursorRightTop,
|
||||
kActionFightCursorRightMiddle,
|
||||
kActionFightCursorRightBottom,
|
||||
kActionIndexNext,
|
||||
kActionIndexPrevious,
|
||||
kActionIndexSelect,
|
||||
kActionYes,
|
||||
kActionNo
|
||||
};
|
||||
|
||||
struct LureGameDescription;
|
||||
|
||||
class LureEngine : public Engine {
|
||||
private:
|
||||
bool _initialized;
|
||||
int _gameToLoad;
|
||||
uint8 _saveVersion;
|
||||
Disk *_disk;
|
||||
Resources *_resources;
|
||||
Screen *_screen;
|
||||
Mouse *_mouse;
|
||||
Events *_events;
|
||||
Menu *_menu;
|
||||
StringData *_strings;
|
||||
Room *_room;
|
||||
FightsManager *_fights;
|
||||
Common::RandomSource _rnd;
|
||||
|
||||
const char *generateSaveName(int slotNumber);
|
||||
|
||||
const LureGameDescription *_gameDescription;
|
||||
|
||||
public:
|
||||
LureEngine(OSystem *system, const LureGameDescription *gameDesc);
|
||||
~LureEngine() override;
|
||||
static LureEngine &getReference();
|
||||
bool _saveLoadAllowed;
|
||||
|
||||
// Engine APIs
|
||||
Common::Error init();
|
||||
Common::Error go();
|
||||
Common::Error run() override {
|
||||
Common::Error err;
|
||||
err = init();
|
||||
if (err.getCode() != Common::kNoError)
|
||||
return err;
|
||||
return go();
|
||||
}
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
void syncSoundSettings() override;
|
||||
void pauseEngineIntern(bool pause) override;
|
||||
|
||||
Disk &disk() { return *_disk; }
|
||||
|
||||
Common::RandomSource &rnd() { return _rnd; }
|
||||
int gameToLoad() { return _gameToLoad; }
|
||||
bool loadGame(uint8 slotNumber);
|
||||
bool saveGame(uint8 slotNumber, Common::String &caption);
|
||||
Common::String *detectSave(int slotNumber);
|
||||
uint8 saveVersion() { return _saveVersion; }
|
||||
|
||||
uint32 getFeatures() const;
|
||||
LureLanguage getLureLanguage() const;
|
||||
Common::Language getLanguage() const;
|
||||
Common::Platform getPlatform() const;
|
||||
bool isEGA() const { return (getFeatures() & GF_EGA) != 0; }
|
||||
bool isKonami() const { return (getFeatures() & GF_KONAMI) != 0; }
|
||||
|
||||
Common::Error loadGameState(int slot) override {
|
||||
return loadGame(slot) ? Common::kNoError : Common::kReadingFailed;
|
||||
}
|
||||
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override {
|
||||
Common::String s(desc);
|
||||
return saveGame(slot, s) ? Common::kNoError : Common::kReadingFailed;
|
||||
}
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
|
||||
return _saveLoadAllowed && !Fights.isFighting();
|
||||
}
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
|
||||
return _saveLoadAllowed && !Fights.isFighting();
|
||||
}
|
||||
};
|
||||
|
||||
Common::String getSaveName(Common::InSaveFile *in);
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
349
engines/lure/luredefs.h
Normal file
349
engines/lure/luredefs.h
Normal file
@@ -0,0 +1,349 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LUREDEFS_H
|
||||
#define LUREDEFS_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/list.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
#define SUPPORT_FILENAME "lure.dat"
|
||||
#define LURE_DAT_MAJOR 1
|
||||
#define LURE_DAT_MINOR 32
|
||||
#define LURE_MIN_SAVEGAME_MINOR 25
|
||||
#define LURE_SAVEGAME_MINOR 33
|
||||
|
||||
#define LURE_DEBUG 1
|
||||
|
||||
enum {
|
||||
kLureDebugScripts = 1,
|
||||
kLureDebugAnimations,
|
||||
kLureDebugHotspots,
|
||||
kLureDebugFights,
|
||||
kLureDebugSounds,
|
||||
kLureDebugStrings,
|
||||
};
|
||||
|
||||
#define ERROR_BASIC 1
|
||||
#define ERROR_INTERMEDIATE 2
|
||||
#define ERROR_DETAILED 3
|
||||
|
||||
enum {
|
||||
GI_LURE = 0
|
||||
};
|
||||
|
||||
enum Action {
|
||||
NONE = 0,
|
||||
GET = 1,
|
||||
DROP = 0,
|
||||
PUSH = 3,
|
||||
PULL = 4,
|
||||
OPERATE = 5,
|
||||
OPEN = 6,
|
||||
CLOSE = 7,
|
||||
LOCK = 8,
|
||||
UNLOCK = 9,
|
||||
USE = 10,
|
||||
GIVE = 11,
|
||||
TALK_TO = 12,
|
||||
TELL = 13,
|
||||
BUY = 14,
|
||||
LOOK = 15,
|
||||
LOOK_AT = 16,
|
||||
LOOK_THROUGH = 17,
|
||||
ASK = 18,
|
||||
EAT = 0,
|
||||
DRINK = 20,
|
||||
STATUS = 21,
|
||||
GO_TO = 22,
|
||||
RETURN = 23,
|
||||
BRIBE = 24,
|
||||
EXAMINE = 25,
|
||||
NPC_SET_ROOM_AND_OFFSET = 28,
|
||||
NPC_TALK_TO_PLAYER = 29,
|
||||
NPC_EXEC_SCRIPT = 30,
|
||||
NPC_RESET_PAUSED_LIST = 31,
|
||||
NPC_SET_RAND_DEST = 32,
|
||||
NPC_WALKING_CHECK = 33,
|
||||
NPC_SET_SUPPORT_OFFSET = 34,
|
||||
NPC_SUPPORT_OFFSET_COND = 35,
|
||||
NPC_DISPATCH_ACTION = 36,
|
||||
NPC_TALK_NPC_TO_NPC = 37,
|
||||
NPC_PAUSE = 38,
|
||||
NPC_START_TALKING = 39,
|
||||
NPC_JUMP_ADDRESS = 40
|
||||
};
|
||||
|
||||
// Basic game dimensions
|
||||
#define FULL_SCREEN_WIDTH 320
|
||||
#define FULL_SCREEN_HEIGHT 200
|
||||
#define GAME_COLORS 256
|
||||
#define SCREEN_SIZE (FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH)
|
||||
|
||||
// Some resources include multiple packed palettes of 64 entries each
|
||||
#define SUB_PALETTE_SIZE 64
|
||||
// Palette resources have 220 palette entries
|
||||
#define RES_PALETTE_ENTRIES 220
|
||||
// Main working palette size
|
||||
#define MAIN_PALETTE_SIZE 228
|
||||
// Palette color increment amouns for palette fade in/outs
|
||||
#define PALETTE_FADE_INC_SIZE 4
|
||||
|
||||
// EGA constants
|
||||
#define EGA_PALETTE_SIZE 16
|
||||
#define EGA_NUM_LAYERS 4
|
||||
#define EGA_PIXELS_PER_BYTE 8
|
||||
|
||||
// Palette and animation for Skorl catching player
|
||||
#define SKORL_CATCH_PALETTE_ID 0x4060
|
||||
#define SKORL_CATCH_ANIM_ID 0x4061
|
||||
// Palette and animation for chute animation
|
||||
#define CHUTE_PALETTE_ID 0x404C
|
||||
#define CHUTE_ANIM_ID 0x404D
|
||||
#define CHUTE2_ANIM_ID 0x404f
|
||||
#define CHUTE3_ANIM_ID 0x4051
|
||||
// Palette and animation for hiding in barrel
|
||||
#define BARREL_PALETTE_ID 0xE9F0
|
||||
#define BARREL_ANIM_ID 0xE9F1
|
||||
// Endgame animation constants
|
||||
#define ENDGAME_PALETTE_ID 0xFF00
|
||||
#define ENDGAME_ANIM_ID 0xFF01
|
||||
// Miscellaneous resources
|
||||
#define COPY_PROTECTION_RESOURCE_ID 0x4139
|
||||
#define CREDITS_RESOURCE_ID 0x7800
|
||||
#define RESTART_RESOURCE_ID 0x7900
|
||||
|
||||
// Specifies the maximum buffer sized allocated for decoding animation data
|
||||
#define MAX_ANIM_DECODER_BUFFER_SIZE 300000
|
||||
|
||||
#define MAX_DESC_SIZE 1024
|
||||
#define MAX_HOTSPOT_NAME_SIZE 80
|
||||
#define MAX_ACTION_NAME_SIZE 15
|
||||
|
||||
// Menubar constants
|
||||
#define MENUBAR_Y_SIZE 8
|
||||
|
||||
// Cursor definitions
|
||||
#define CURSOR_WIDTH 16
|
||||
#define CURSOR_HEIGHT 16
|
||||
#define CURSOR_SIZE 256
|
||||
#define CURSOR_RESOURCE_ID 1
|
||||
|
||||
enum CursorType {CURSOR_ARROW = 0, CURSOR_DISK = 1, CURSOR_TIME_START = 2,
|
||||
CURSOR_TIME_END = 9, CURSOR_CROSS = 10, CURSOR_UP_ARROW = 11, CURSOR_DOWN_ARROW = 12,
|
||||
CURSOR_LEFT_ARROW = 13, CURSOR_RIGHT_ARROW = 14, CURSOR_CAMERA = 15, CURSOR_TALK = 16,
|
||||
CURSOR_MENUBAR = 17, CURSOR_FIGHT_UPPER = 23, CURSOR_FIGHT_MIDDLE = 24,
|
||||
CURSOR_FIGHT_LOWER = 25};
|
||||
|
||||
// Font details
|
||||
#define FONT_RESOURCE_ID 4
|
||||
#define FONT_WIDTH 8
|
||||
#define FONT_HEIGHT 8
|
||||
|
||||
// Menu constants
|
||||
#define MENU_RESOURCE_ID 5
|
||||
#define MENUITEM_NONE 0
|
||||
#define MENUITEM_CREDITS 1
|
||||
#define MENUITEM_RESTART_GAME 2
|
||||
#define MENUITEM_SAVE_GAME 3
|
||||
#define MENUITEM_RESTORE_GAME 4
|
||||
#define MENUITEM_QUIT 5
|
||||
#define MENUITEM_TEXT_SPEED 6
|
||||
#define MENUITEM_SOUND 7
|
||||
|
||||
// Mouse change needed to change an item in a popup menu
|
||||
#define POPMENU_CHANGE_SENSITIVITY 5
|
||||
|
||||
// Dialog related defines
|
||||
#define EGA_DIALOG_TEXT_COLOR 6
|
||||
#define EGA_DIALOG_WHITE_COLOR 2
|
||||
#define VGA_DIALOG_TEXT_COLOR 0xe2
|
||||
#define VGA_DIALOG_WHITE_COLOR 0xe3
|
||||
#define EGA_DIALOG_BG_COLOR 13
|
||||
#define INFO_DIALOG_X 69
|
||||
#define INFO_DIALOG_Y 61
|
||||
#define INFO_DIALOG_WIDTH 191
|
||||
#define TALK_DIALOG_WIDTH 130
|
||||
#define TALK_DIALOG_EDGE_SIZE 3
|
||||
#define TALK_DIALOG_Y 33
|
||||
#define SAVE_DIALOG_X 69
|
||||
#define SAVE_DIALOG_Y 28
|
||||
|
||||
// Strings defines
|
||||
#define STRINGS_RESOURCE_ID 0x10
|
||||
#define STRINGS_2_RESOURCE_ID 0x11
|
||||
#define STRINGS_3_RESOURCE_ID 0x12
|
||||
#define STRING_ID_RANGE 0x7d0
|
||||
#define STRING_ID_UPPER 0xfa0
|
||||
|
||||
// Custom resources stored in lure.dat
|
||||
#define GAME_PALETTE_RESOURCE_ID 0x3f01
|
||||
#define ALT_PALETTE_RESOURCE_ID 0x3f02
|
||||
#define DIALOG_RESOURCE_ID 0x3f03
|
||||
#define TALK_DIALOG_RESOURCE_ID 0x3f04
|
||||
#define ROOM_DATA_RESOURCE_ID 0x3f05
|
||||
#define NPC_SCHEDULES_RESOURCE_ID 0x3f06
|
||||
#define HOTSPOT_DATA_RESOURCE_ID 0x3f07
|
||||
#define HOTSPOT_OVERRIDE_DATA_RESOURCE_ID 0x3f08
|
||||
#define ROOM_EXITS_RESOURCE_ID 0x3f09
|
||||
#define ROOM_EXIT_JOINS_RESOURCE_ID 0x3f0a
|
||||
#define ANIM_DATA_RESOURCE_ID 0x3f0b
|
||||
#define SCRIPT_DATA_RESOURCE_ID 0x3f0c
|
||||
#define SCRIPT2_DATA_RESOURCE_ID 0x3f0d
|
||||
#define HOTSPOT_SCRIPT_LIST_RESOURCE_ID 0x3f0e
|
||||
#define MESSAGES_LIST_RESOURCE_ID 0x3f0f
|
||||
#define ACTION_LIST_RESOURCE_ID 0x3f10
|
||||
#define TALK_HEADER_RESOURCE_ID 0x3f11
|
||||
#define TALK_DATA_RESOURCE_ID 0x3f12
|
||||
#define ROOM_PATHS_RESOURCE_ID 0x3f13
|
||||
#define EXIT_COORDINATES_RESOURCE_ID 0x3f14
|
||||
#define EXIT_HOTSPOT_ID_LIST 0x3f15
|
||||
#define FIGHT_DATA_RESOURCE_ID 0x3f16
|
||||
#define STRING_LIST_RESOURCE_ID 0x3f17
|
||||
#define SOUND_DESC_RESOURCE_ID 0x3f18
|
||||
#define STRING_DECODER_RESOURCE_ID 0x3f19
|
||||
#define AUDIO_INIT_ICON_RESOURCE_ID 0x3F1A
|
||||
|
||||
// Script constants
|
||||
#define STARTUP_SCRIPT 0x23FC
|
||||
|
||||
// Miscellaneous resources
|
||||
#define NAMES_RESOURCE_ID 9
|
||||
#define ROLAND_MAIN_SYSEX_RESOURCE_ID 0xB
|
||||
#define ROLAND_MAIN_SOUND_RESOURCE_ID 0xC
|
||||
#define ADLIB_MAIN_SOUND_RESOURCE_ID 0xD
|
||||
#define ROLAND_INTRO_SOUND_RESOURCE_ID 0x30
|
||||
#define ADLIB_INTRO_SOUND_RESOURCE_ID 0x31
|
||||
#define ROLAND_ENDGAME_SOUND_RESOURCE_ID 0xff10
|
||||
#define ADLIB_ENDGAME_SOUND_RESOURCE_ID 0xff11
|
||||
|
||||
#define NOONE_ID 0x3E7
|
||||
#define PLAYER_ID 0x3E8
|
||||
#define RATPOUCH_ID 0x3E9
|
||||
#define SKORL_ID 0x3EA
|
||||
#define BLACKSMITH_ID 0x3EB
|
||||
#define GWEN_ID 0x3EC
|
||||
#define MALLIN_ID 0x3ED
|
||||
#define MONK1_ID 0x3EE
|
||||
#define GOEWIN_ID 0x3EF
|
||||
#define MONK2_ID 0x3F0
|
||||
#define WAYNE_ID 0x3f1
|
||||
#define CASTLE_SKORL_ID 0x3F3
|
||||
#define FIRST_NONCHARACTER_ID 0x408
|
||||
#define SACK_ID 0x40D
|
||||
#define RACK_SERF_ID 0x411
|
||||
#define PRISONER_ID 0x412
|
||||
#define SID_ID 0x420
|
||||
#define OIL_BURNER_ID 0x424
|
||||
#define TRANSFORM_ID 0x425
|
||||
#define NELLIE_ID 0x429
|
||||
#define EWAN_ID 0x436
|
||||
#define PIG_ID 0x43F
|
||||
#define SKORL_FIGHTER_ID 0x444
|
||||
#define START_EXIT_ID 0x2710
|
||||
#define BOTTLE_HOTSPOT_ID 0x2710
|
||||
#define CELL_DOOR_HOTSPOT_ID 0x2712
|
||||
#define BRICKS_ID 0x2714
|
||||
#define BOOK_ID 0x2723
|
||||
#define START_NONVISUAL_HOTSPOT_ID 0x7530
|
||||
|
||||
// Milliseconds delay between game frames
|
||||
#define GAME_FRAME_DELAY 80
|
||||
|
||||
// Milliseconds delay between clock ticks in shop
|
||||
#define GAME_TICK_DELAY 1000
|
||||
|
||||
// Tick proc constants
|
||||
#define NULL_TICK_PROC_ID 1
|
||||
#define STANDARD_CHARACTER_TICK_PROC 2
|
||||
#define PLAYER_TICK_PROC_ID 3
|
||||
#define VOICE_TICK_PROC_ID 4
|
||||
#define PUZZLED_TICK_PROC_ID 5
|
||||
#define FOLLOWER_TICK_PROC_2 8
|
||||
#define JAILOR_TICK_PROC_ID 9
|
||||
#define STANDARD_ANIM_2_TICK_PROC 11
|
||||
#define STANDARD_ANIM_TICK_PROC 12
|
||||
#define GOEWIN_SHOP_TICK_PROC 27
|
||||
#define TALK_TICK_PROC_ID 37
|
||||
#define PLAYER_FIGHT_TICK_PROC_ID 39
|
||||
|
||||
// String constants
|
||||
#define TALK_MAGIC_ID 0x424
|
||||
#define TALK_RESPONSE_MAGIC_ID 0x1092
|
||||
|
||||
// Misc constants
|
||||
#define GENERAL_MAGIC_ID 42
|
||||
#define VOICE_ANIM_IDX 1
|
||||
#define PUZZLED_ANIM_IDX 2
|
||||
#define EXCLAMATION_ANIM_IDX 3
|
||||
#define DEFAULT_VOLUME 192
|
||||
|
||||
// Animation record indexes
|
||||
#define PLAYER_FIGHT_ANIM_INDEX 10
|
||||
#define VOICE_ANIM_INDEX 21
|
||||
#define BLACKSMITH_HAMMERING_ANIM_INDEX 22
|
||||
#define EWAN_ANIM_INDEX 23
|
||||
#define EWAN_ALT_ANIM_INDEX 24
|
||||
#define PLAYER_ANIM_INDEX 32
|
||||
#define SELENA_ANIM_INDEX 34
|
||||
#define BLACKSMITH_DEFAULT_ANIM_INDEX 35
|
||||
|
||||
#define CONVERSE_COUNTDOWN_SIZE 40
|
||||
#define IDLE_COUNTDOWN_SIZE 15
|
||||
#define MAX_TELL_COMMANDS 8
|
||||
#define MAX_SAVEGAME_SLOTS 10
|
||||
|
||||
#define ROOMNUM_VILLAGE_SHOP 35
|
||||
#define ROOMNUM_CAVE 38
|
||||
#define ROOMNUM_CELLAR 42
|
||||
#define ROOMNUM_DINING_HALL 45
|
||||
|
||||
// Countdown for # operations in path finder before breaking until next
|
||||
// tick - set it to 0 if you'd like all pathfinding to be done at once
|
||||
//#define PATHFIND_COUNTDOWN 4000
|
||||
#define PATHFIND_COUNTDOWN 0
|
||||
|
||||
// Pixel record flags
|
||||
#define PIXELFLAG_HAS_TABLE 4
|
||||
|
||||
// Hotspot flags
|
||||
#define HOTSPOTFLAG_FOUND 0x80
|
||||
#define HOTSPOTFLAG_SKIP 0x40
|
||||
#define HOTSPOTFLAG_MENU_EXCLUSION 0x20
|
||||
#define HOTSPOTFLAG_ROOM_SPECIFIC 0x10
|
||||
|
||||
// Constants used to reference entries in the reworked support data entry lists
|
||||
#define RETURN_SUPPORT_ID 0x400
|
||||
#define EXIT_BLOCKED_SUPPORT_ID 0x800
|
||||
#define JUMP_ADDR_2_SUPPORT_ID 0x1403
|
||||
#define GOEWIN_CAVE_SUPPORT_ID 0x1800
|
||||
#define GOEWIN_STANDARD_SUPPORT_ID 0x1C00
|
||||
|
||||
// Constants used in animation Serf on the rack
|
||||
#define RACK_SERF_SCRIPT_ID_1 0x35C
|
||||
#define RACK_SERF_SCRIPT_ID_2 0x384
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
103
engines/lure/memory.cpp
Normal file
103
engines/lure/memory.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lure/memory.h"
|
||||
#include "common/file.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
MemoryBlock *Memory::allocate(uint32 size) {
|
||||
MemoryBlock *block = new MemoryBlock(size);
|
||||
return block;
|
||||
}
|
||||
|
||||
MemoryBlock *Memory::duplicate(MemoryBlock *src) {
|
||||
MemoryBlock *block = new MemoryBlock(src);
|
||||
return block;
|
||||
}
|
||||
|
||||
void *Memory::alloc(uint32 size) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void Memory::dealloc(void *block) {
|
||||
free(block);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
MemoryBlock::MemoryBlock(uint32 size1) {
|
||||
_data = (uint8 *) malloc(size1);
|
||||
if (!_data) error ("Failed allocating memory block");
|
||||
_size = size1;
|
||||
}
|
||||
|
||||
MemoryBlock::MemoryBlock(MemoryBlock *src) {
|
||||
_size = src->size();
|
||||
_data = (uint8 *) malloc(_size);
|
||||
if (!_data) error ("Failed allocating memory block");
|
||||
memcpy(_data, src->data(), _size);
|
||||
}
|
||||
|
||||
MemoryBlock::~MemoryBlock() {
|
||||
free(_data);
|
||||
}
|
||||
|
||||
void MemoryBlock::empty() {
|
||||
::memset(_data, 0, _size);
|
||||
}
|
||||
|
||||
void MemoryBlock::setBytes(int c, size_t startIndex, size_t num) {
|
||||
byte *p = _data + startIndex;
|
||||
::memset(p, c, num);
|
||||
}
|
||||
|
||||
void MemoryBlock::copyFrom(MemoryBlock *src) {
|
||||
copyFrom(src, 0, 0, src->size());
|
||||
}
|
||||
|
||||
void MemoryBlock::copyFrom(MemoryBlock *src, uint32 srcPos, uint32 destPos, uint32 srcLen) {
|
||||
if ((srcPos + srcLen > src->size()) || (destPos + srcLen > size()))
|
||||
error("Memory block overrun in block copy");
|
||||
|
||||
uint8 *pDest = _data + destPos;
|
||||
uint8 *pSrc = src->data() + srcPos;
|
||||
memcpy(pDest, pSrc, srcLen);
|
||||
}
|
||||
|
||||
void MemoryBlock::copyFrom(const byte *src, uint32 srcPos, uint32 destPos, uint32 srcLen) {
|
||||
byte *pDest = _data + destPos;
|
||||
const byte *pSrc = src + srcPos;
|
||||
memcpy(pDest, pSrc, srcLen);
|
||||
}
|
||||
|
||||
void MemoryBlock::reallocate(uint32 size1) {
|
||||
_size = size1;
|
||||
|
||||
byte *tmp = (byte *) realloc(_data, size1);
|
||||
if (!tmp)
|
||||
error ("[MemoryBlock::reallocate] Failed reallocating memory block");
|
||||
|
||||
_data = tmp;
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
61
engines/lure/memory.h
Normal file
61
engines/lure/memory.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_MEMORY_H
|
||||
#define LURE_MEMORY_H
|
||||
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
class MemoryBlock {
|
||||
private:
|
||||
byte *_data;
|
||||
uint32 _size;
|
||||
public:
|
||||
MemoryBlock(uint32 size);
|
||||
MemoryBlock(MemoryBlock *src);
|
||||
~MemoryBlock();
|
||||
|
||||
byte *data() { return _data; }
|
||||
uint32 size() { return _size; }
|
||||
|
||||
void empty();
|
||||
void setBytes(int c, size_t startIndex, size_t num);
|
||||
void copyFrom(MemoryBlock *src);
|
||||
void copyFrom(MemoryBlock *src, uint32 srcPos, uint32 destPos, uint32 srcLen);
|
||||
void copyFrom(const byte *src, uint32 srcPos, uint32 destPos, uint32 srcLen);
|
||||
void reallocate(uint32 size);
|
||||
};
|
||||
|
||||
class Memory {
|
||||
public:
|
||||
static MemoryBlock *allocate(uint32 size);
|
||||
static MemoryBlock *duplicate(MemoryBlock *src);
|
||||
static void *alloc(uint32 size);
|
||||
static void dealloc(void *block);
|
||||
};
|
||||
|
||||
} // end of namspace Lure
|
||||
|
||||
#endif
|
||||
660
engines/lure/menu.cpp
Normal file
660
engines/lure/menu.cpp
Normal file
@@ -0,0 +1,660 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lure/menu.h"
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/decode.h"
|
||||
#include "lure/surface.h"
|
||||
#include "lure/res_struct.h"
|
||||
#include "lure/res.h"
|
||||
#include "lure/strings.h"
|
||||
#include "lure/room.h"
|
||||
#include "lure/events.h"
|
||||
#include "lure/lure.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
MenuRecord::MenuRecord(const MenuRecordBounds *bounds, int numParams, ...) {
|
||||
// Store list of pointers to strings
|
||||
va_list params;
|
||||
|
||||
_numEntries = numParams;
|
||||
_entries = (const char **) malloc(sizeof(const char *) * _numEntries);
|
||||
|
||||
va_start(params, numParams);
|
||||
for (int index = 0; index < _numEntries; ++index)
|
||||
_entries[index] = va_arg(params, const char *);
|
||||
va_end(params);
|
||||
|
||||
// Store position data
|
||||
_hsxstart = bounds->left; _hsxend = bounds->right;
|
||||
_xstart = bounds->contentsX << 3;
|
||||
_width = (bounds->contentsWidth + 3) << 3;
|
||||
}
|
||||
|
||||
MenuRecord::~MenuRecord() {
|
||||
free(_entries);
|
||||
_entries = nullptr;
|
||||
}
|
||||
|
||||
const char *MenuRecord::getEntry(uint8 index) {
|
||||
if (index >= _numEntries) error("Invalid menuitem index specified: %d", index);
|
||||
return _entries[index];
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
static Menu *int_menu = nullptr;
|
||||
|
||||
const MenuRecordLanguage menuList[] = {
|
||||
{Common::EN_ANY, {{40, 87, 3, 7}, {127, 179, 13, 12}, {224, 281, 27, 10}}},
|
||||
{Common::IT_ITA, {{40, 98, 4, 6}, {120, 195, 14, 11}, {208, 281, 24, 13}}},
|
||||
{Common::FR_FRA, {{40, 90, 3, 7}, {120, 195, 13, 11}, {232, 273, 23, 13}}},
|
||||
{Common::DE_DEU, {{44, 95, 1, 11}, {135, 178, 8, 23}, {232, 273, 22, 15}}},
|
||||
{Common::ES_ESP, {{40, 90, 3, 8}, {120, 195, 11, 13}, {208, 281, 17, 18}}},
|
||||
{Common::RU_RUS, {{40, 87, 3, 7}, {127, 179, 13, 12}, {224, 281, 27, 10}}},
|
||||
{Common::UNK_LANG, {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}}
|
||||
};
|
||||
|
||||
Menu::Menu() {
|
||||
int_menu = this;
|
||||
StringList &sl = Resources::getReference().stringList();
|
||||
Common::Language language = LureEngine::getReference().getLanguage();
|
||||
|
||||
MemoryBlock *data = Disk::getReference().getEntry(MENU_RESOURCE_ID);
|
||||
PictureDecoder decoder;
|
||||
_menu = decoder.decode(data, SCREEN_SIZE);
|
||||
delete data;
|
||||
|
||||
const MenuRecordLanguage *rec = &menuList[0];
|
||||
while ((rec->language != Common::UNK_LANG) && (rec->language != language))
|
||||
++rec;
|
||||
if (rec->language == Common::UNK_LANG)
|
||||
error("Unknown language encountered in top line handler");
|
||||
|
||||
_menus[0] = new MenuRecord(&rec->menus[0], 1, sl.getString(S_CREDITS));
|
||||
_menus[1] = new MenuRecord(&rec->menus[1], 3,
|
||||
sl.getString(S_RESTART_GAME), sl.getString(S_SAVE_GAME), sl.getString(S_RESTORE_GAME));
|
||||
_menus[2] = new MenuRecord(&rec->menus[2], 3,
|
||||
sl.getString(S_QUIT), sl.getString(S_SLOW_TEXT), sl.getString(S_SOUND_ON));
|
||||
|
||||
_selectedMenu = nullptr;
|
||||
}
|
||||
|
||||
Menu::~Menu() {
|
||||
for (int ctr=0; ctr<NUM_MENUS; ++ctr) delete _menus[ctr];
|
||||
delete _menu;
|
||||
}
|
||||
|
||||
Menu &Menu::getReference() {
|
||||
return *int_menu;
|
||||
}
|
||||
|
||||
uint8 Menu::execute() {
|
||||
OSystem &system = *g_system;
|
||||
LureEngine &engine = LureEngine::getReference();
|
||||
Mouse &mouse = Mouse::getReference();
|
||||
Events &events = Events::getReference();
|
||||
Screen &screen = Screen::getReference();
|
||||
|
||||
mouse.setCursorNum(CURSOR_ARROW);
|
||||
system.copyRectToScreen(_menu->data(), FULL_SCREEN_WIDTH, 0, 0,
|
||||
FULL_SCREEN_WIDTH, MENUBAR_Y_SIZE);
|
||||
|
||||
_selectedMenu = nullptr;
|
||||
_surfaceMenu = nullptr;
|
||||
_selectedIndex = 0;
|
||||
|
||||
while (mouse.lButton() || mouse.rButton() || g_system->hasFeature(OSystem::kFeatureTouchscreen)) {
|
||||
while (events.pollEvent()) {
|
||||
if (engine.shouldQuit()) return MENUITEM_NONE;
|
||||
|
||||
if (mouse.y() < MENUBAR_Y_SIZE) {
|
||||
MenuRecord *p = getMenuAt(mouse.x());
|
||||
|
||||
if (_selectedMenu != p) {
|
||||
// If necessary, remove prior menu
|
||||
if (_selectedMenu) {
|
||||
toggleHighlight(_selectedMenu);
|
||||
screen.updateArea(0, 0, FULL_SCREEN_WIDTH, _surfaceMenu->height() + 8);
|
||||
delete _surfaceMenu;
|
||||
_surfaceMenu = nullptr;
|
||||
_selectedIndex = 0;
|
||||
}
|
||||
|
||||
_selectedMenu = p;
|
||||
|
||||
// If a new menu is selected, show it
|
||||
if (_selectedMenu) {
|
||||
toggleHighlight(_selectedMenu);
|
||||
_surfaceMenu = Surface::newDialog(
|
||||
_selectedMenu->width(), _selectedMenu->numEntries(),
|
||||
_selectedMenu->entries(), false, DEFAULT_TEXT_COLOR, false);
|
||||
_surfaceMenu->copyToScreen(_selectedMenu->xstart(), MENUBAR_Y_SIZE);
|
||||
}
|
||||
|
||||
system.copyRectToScreen(_menu->data(), FULL_SCREEN_WIDTH, 0, 0,
|
||||
FULL_SCREEN_WIDTH, MENUBAR_Y_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for changing selected index
|
||||
uint8 index = getIndexAt(mouse.x(), mouse.y());
|
||||
if (index != _selectedIndex) {
|
||||
if (_selectedIndex != 0) toggleHighlightItem(_selectedIndex);
|
||||
_selectedIndex = index;
|
||||
if (_selectedIndex != 0) toggleHighlightItem(_selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_system->hasFeature(OSystem::kFeatureTouchscreen)) {
|
||||
// Close menu only when a sub menu is shown and
|
||||
// the user has either clicked on a selected index
|
||||
// or no index (outside the sub menu == cancelled)
|
||||
if (mouse.lButton() &&
|
||||
_surfaceMenu != nullptr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
system.updateScreen();
|
||||
system.delayMillis(10);
|
||||
}
|
||||
|
||||
delete _surfaceMenu;
|
||||
|
||||
// Deselect the currently selected menu header
|
||||
if (_selectedMenu)
|
||||
toggleHighlight(_selectedMenu);
|
||||
|
||||
// Restore the previous screen
|
||||
screen.update();
|
||||
|
||||
if ((_selectedMenu == nullptr) || (_selectedIndex == 0)) return MENUITEM_NONE;
|
||||
else if (_selectedMenu == _menus[0])
|
||||
return MENUITEM_CREDITS;
|
||||
else if (_selectedMenu == _menus[1]) {
|
||||
switch (_selectedIndex) {
|
||||
case 1:
|
||||
return MENUITEM_RESTART_GAME;
|
||||
case 2:
|
||||
return MENUITEM_SAVE_GAME;
|
||||
case 3:
|
||||
return MENUITEM_RESTORE_GAME;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (_selectedIndex) {
|
||||
case 1:
|
||||
return MENUITEM_QUIT;
|
||||
case 2:
|
||||
return MENUITEM_TEXT_SPEED;
|
||||
case 3:
|
||||
return MENUITEM_SOUND;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return MENUITEM_NONE;
|
||||
}
|
||||
|
||||
MenuRecord *Menu::getMenuAt(int x) {
|
||||
for (int ctr = 0; ctr < NUM_MENUS; ++ctr)
|
||||
if ((x >= _menus[ctr]->hsxstart()) && (x <= _menus[ctr]->hsxend()))
|
||||
return _menus[ctr];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8 Menu::getIndexAt(uint16 x, uint16 y) {
|
||||
if (!_selectedMenu) return 0;
|
||||
|
||||
int ys = MENUBAR_Y_SIZE + Surface::textY();
|
||||
int ye = MENUBAR_Y_SIZE + (_surfaceMenu->height() - Surface::textY());
|
||||
if ((y < ys) || (y > ye)) return 0;
|
||||
|
||||
uint16 yRelative = y - ys;
|
||||
uint8 index = (uint8) (yRelative / 8) + 1;
|
||||
if (index > _selectedMenu->numEntries()) index = _selectedMenu->numEntries();
|
||||
return index;
|
||||
}
|
||||
|
||||
#define MENUBAR_SELECTED_COLOR 0xf7
|
||||
|
||||
void Menu::toggleHighlight(MenuRecord *menuRec) {
|
||||
const byte colorList[4] = {4, 2, 0, 0xf7};
|
||||
const byte *colors = LureEngine::getReference().isEGA() ? &colorList[0] : &colorList[2];
|
||||
byte *addr = _menu->data();
|
||||
|
||||
for (uint16 y=0; y<MENUBAR_Y_SIZE; ++y) {
|
||||
for (uint16 x=menuRec->hsxstart(); x<=menuRec->hsxend(); ++x) {
|
||||
if (addr[x] == colors[0]) addr[x] = colors[1];
|
||||
else if (addr[x] == colors[1]) addr[x] = colors[0];
|
||||
}
|
||||
addr += FULL_SCREEN_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::toggleHighlightItem(uint8 index) {
|
||||
const byte colorList[4] = {EGA_DIALOG_TEXT_COLOR, EGA_DIALOG_WHITE_COLOR,
|
||||
VGA_DIALOG_TEXT_COLOR, VGA_DIALOG_WHITE_COLOR};
|
||||
const byte *colors = LureEngine::getReference().isEGA() ? &colorList[0] : &colorList[2];
|
||||
byte *p = _surfaceMenu->data().data() + (Surface::textY() +
|
||||
((index - 1) * FONT_HEIGHT)) * _surfaceMenu->width() + Surface::textX();
|
||||
int numBytes =_surfaceMenu->width() - Surface::textX() * 2;
|
||||
|
||||
for (int y = 0; y < FONT_HEIGHT; ++y, p += _surfaceMenu->width()) {
|
||||
byte *pTemp = p;
|
||||
|
||||
for (int x = 0; x < numBytes; ++x, ++pTemp) {
|
||||
if (*pTemp == colors[0]) *pTemp = colors[1];
|
||||
else if (*pTemp == colors[1]) *pTemp = colors[0];
|
||||
}
|
||||
}
|
||||
|
||||
_surfaceMenu->copyToScreen(_selectedMenu->xstart(), MENUBAR_Y_SIZE);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
uint16 PopupMenu::ShowInventory() {
|
||||
Resources &rsc = Resources::getReference();
|
||||
StringData &strings = StringData::getReference();
|
||||
|
||||
uint16 numItems = rsc.numInventoryItems();
|
||||
uint16 itemCtr = 0;
|
||||
char **itemNames = (char **) Memory::alloc(sizeof(char *) * numItems);
|
||||
uint16 *idList = (uint16 *) Memory::alloc(sizeof(uint16) * numItems);
|
||||
|
||||
HotspotDataList::iterator i;
|
||||
for (i = rsc.hotspotData().begin(); i != rsc.hotspotData().end(); ++i) {
|
||||
HotspotData const &hotspot = **i;
|
||||
if (hotspot.roomNumber == PLAYER_ID) {
|
||||
idList[itemCtr] = hotspot.hotspotId;
|
||||
char *hotspotName = itemNames[itemCtr++] = (char *) malloc(MAX_HOTSPOT_NAME_SIZE);
|
||||
strings.getString(hotspot.nameId, hotspotName);
|
||||
}
|
||||
}
|
||||
|
||||
uint16 result = Show(numItems, const_cast<const char **>(itemNames));
|
||||
if (result != 0xffff) result = idList[result];
|
||||
|
||||
for (itemCtr = 0; itemCtr < numItems; ++itemCtr)
|
||||
free(itemNames[itemCtr]);
|
||||
|
||||
Memory::dealloc(itemNames);
|
||||
Memory::dealloc(idList);
|
||||
return result;
|
||||
}
|
||||
|
||||
#define MAX_NUM_DISPLAY_ITEMS 20
|
||||
|
||||
uint16 PopupMenu::ShowItems(Action contextAction, uint16 roomNumber) {
|
||||
Resources &res = Resources::getReference();
|
||||
ValueTableData &fields = res.fieldList();
|
||||
RoomDataList &rooms = res.roomData();
|
||||
HotspotDataList &hotspots = res.hotspotData();
|
||||
StringData &strings = StringData::getReference();
|
||||
Room &room = Room::getReference();
|
||||
Screen &screen = Screen::getReference();
|
||||
Mouse &mouse = Mouse::getReference();
|
||||
RoomDataList::iterator ir;
|
||||
HotspotDataList::iterator ih;
|
||||
uint16 entryIds[MAX_NUM_DISPLAY_ITEMS];
|
||||
uint16 nameIds[MAX_NUM_DISPLAY_ITEMS];
|
||||
char *entryNames[MAX_NUM_DISPLAY_ITEMS];
|
||||
int numItems = 0;
|
||||
int itemCtr;
|
||||
uint32 contextBitflag = 1 << (contextAction - 1);
|
||||
|
||||
// Loop for rooms
|
||||
for (ir = rooms.begin(); ir != rooms.end(); ++ir) {
|
||||
RoomData const &roomData = **ir;
|
||||
// Pre-condition checks for whether to skip room
|
||||
if ((roomData.hdrFlags != 15) && ((roomData.hdrFlags & fields.hdrFlagMask()) == 0))
|
||||
continue;
|
||||
if (((roomData.flags & HOTSPOTFLAG_MENU_EXCLUSION) != 0) || ((roomData.flags & HOTSPOTFLAG_FOUND) == 0))
|
||||
continue;
|
||||
if ((roomData.actions & contextBitflag) == 0)
|
||||
continue;
|
||||
|
||||
// Add room to list of entries to display
|
||||
if (numItems == MAX_NUM_DISPLAY_ITEMS) error("Out of space in ask list");
|
||||
entryIds[numItems] = roomData.roomNumber;
|
||||
nameIds[numItems] = roomData.roomNumber;
|
||||
entryNames[numItems] = (char *) Memory::alloc(MAX_HOTSPOT_NAME_SIZE);
|
||||
strings.getString(roomData.roomNumber, entryNames[numItems]);
|
||||
++numItems;
|
||||
}
|
||||
|
||||
// Loop for hotspots
|
||||
for (ih = hotspots.begin(); ih != hotspots.end(); ++ih) {
|
||||
HotspotData const &hotspot = **ih;
|
||||
|
||||
if ((hotspot.headerFlags != 15) &&
|
||||
((hotspot.headerFlags & fields.hdrFlagMask()) == 0))
|
||||
continue;
|
||||
|
||||
if (((hotspot.flags & HOTSPOTFLAG_MENU_EXCLUSION) != 0) || ((hotspot.flags & HOTSPOTFLAG_FOUND) == 0))
|
||||
// Skip the current hotspot
|
||||
continue;
|
||||
|
||||
// If the hotspot is room specific, skip if the character will not be in the specified room
|
||||
if (((hotspot.flags & HOTSPOTFLAG_ROOM_SPECIFIC) != 0) &&
|
||||
(hotspot.roomNumber != roomNumber))
|
||||
continue;
|
||||
|
||||
// If hotspot does not allow action, then skip it
|
||||
if ((hotspot.actions & contextBitflag) == 0)
|
||||
continue;
|
||||
|
||||
// If a special hotspot Id, then skip displaying
|
||||
if ((hotspot.nameId == 0x17A) || (hotspot.nameId == 0x147))
|
||||
continue;
|
||||
|
||||
// Check if the hotspot's name is already used in an already set item
|
||||
itemCtr = 0;
|
||||
while ((itemCtr < numItems) && (nameIds[itemCtr] != hotspot.nameId))
|
||||
++itemCtr;
|
||||
if (itemCtr != numItems)
|
||||
// Item's name is already present - skip hotspot
|
||||
continue;
|
||||
|
||||
// Add hotspot to list of entries to display
|
||||
if (numItems == MAX_NUM_DISPLAY_ITEMS) error("Out of space in ask list");
|
||||
entryIds[numItems] = hotspot.hotspotId;
|
||||
nameIds[numItems] = hotspot.nameId;
|
||||
entryNames[numItems] = (char *) Memory::alloc(MAX_HOTSPOT_NAME_SIZE);
|
||||
strings.getString(hotspot.nameId, entryNames[numItems]);
|
||||
++numItems;
|
||||
}
|
||||
|
||||
if (numItems == 0) {
|
||||
// No items, so add a 'nothing' to the statusLine
|
||||
if (LureEngine::getReference().getLanguage() == Common::RU_RUS)
|
||||
Common::strcat_s(room.statusLine(), MAX_DESC_SIZE, "(ybxtuj ytn)");
|
||||
else
|
||||
Common::strcat_s(room.statusLine(), MAX_DESC_SIZE, "(nothing)");
|
||||
}
|
||||
|
||||
room.update();
|
||||
screen.update();
|
||||
mouse.waitForRelease();
|
||||
|
||||
if (numItems == 0)
|
||||
// Return flag for no items to ask for
|
||||
return 0xfffe;
|
||||
|
||||
// Display items
|
||||
uint16 result = Show(numItems, const_cast<const char **>(entryNames));
|
||||
if (result != 0xffff) result = entryIds[result];
|
||||
|
||||
// Deallocate display strings
|
||||
for (itemCtr = 0; itemCtr < numItems; ++itemCtr)
|
||||
Memory::dealloc(entryNames[itemCtr]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int entryCompare(const char **p1, const char **p2) {
|
||||
return strcmp(*p1, *p2);
|
||||
}
|
||||
|
||||
typedef int (*CompareMethod)(const void*, const void*);
|
||||
|
||||
Action PopupMenu::Show(uint32 actionMask) {
|
||||
StringList &stringList = Resources::getReference().stringList();
|
||||
int numEntries = 0;
|
||||
uint32 v = actionMask;
|
||||
int index;
|
||||
int currentAction;
|
||||
uint16 resultIndex;
|
||||
Action resultAction;
|
||||
|
||||
for (index = 1; index <= EXAMINE; ++index, v >>= 1) {
|
||||
if (v & 1) ++numEntries;
|
||||
}
|
||||
|
||||
const char **strList = (const char **) Memory::alloc(sizeof(char *) * numEntries);
|
||||
|
||||
int strIndex = 0;
|
||||
for (currentAction = 0; currentAction < (int)EXAMINE; ++currentAction) {
|
||||
if ((actionMask & (1 << currentAction)) != 0) {
|
||||
strList[strIndex] = stringList.getString(currentAction);
|
||||
++strIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the list
|
||||
qsort(strList, numEntries, sizeof(const char *), (CompareMethod) entryCompare);
|
||||
|
||||
// Show the entries
|
||||
resultIndex = Show(numEntries, strList);
|
||||
|
||||
resultAction = NONE;
|
||||
if (resultIndex != 0xffff) {
|
||||
// Scan through the list of actions to find the selected entry
|
||||
for (currentAction = 0; currentAction < (int)EXAMINE; ++currentAction) {
|
||||
if (strList[resultIndex] == stringList.getString(currentAction)) {
|
||||
resultAction = (Action) (currentAction + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Memory::dealloc(strList);
|
||||
return resultAction;
|
||||
}
|
||||
|
||||
Action PopupMenu::Show(int numEntries, Action *actions) {
|
||||
StringList &stringList = Resources::getReference().stringList();
|
||||
const char **strList = (const char **) Memory::alloc(sizeof(char *) * numEntries);
|
||||
Action *actionPtr = actions;
|
||||
for (int index = 0; index < numEntries; ++index)
|
||||
strList[index] = stringList.getString(*actionPtr++);
|
||||
uint16 result = Show(numEntries, strList);
|
||||
|
||||
Memory::dealloc(strList);
|
||||
if (result == 0xffff) return NONE;
|
||||
else return actions[result];
|
||||
}
|
||||
|
||||
uint16 PopupMenu::Show(int numEntries, const char *actions[]) {
|
||||
if (numEntries == 0) return 0xffff;
|
||||
LureEngine &engine = LureEngine::getReference();
|
||||
Events &e = Events::getReference();
|
||||
Mouse &mouse = Mouse::getReference();
|
||||
OSystem &system = *g_system;
|
||||
Screen &screen = Screen::getReference();
|
||||
bool isEGA = LureEngine::getReference().isEGA();
|
||||
byte textColor = isEGA ? EGA_DIALOG_TEXT_COLOR : VGA_DIALOG_TEXT_COLOR;
|
||||
byte whiteColor = isEGA ? EGA_DIALOG_WHITE_COLOR : VGA_DIALOG_WHITE_COLOR;
|
||||
|
||||
|
||||
const uint16 yMiddle = FULL_SCREEN_HEIGHT / 2;
|
||||
|
||||
uint16 numLines = 0, oldX = 0, oldY = 0;
|
||||
bool clickable_menu = g_system->hasFeature(OSystem::kFeatureTouchscreen);
|
||||
if (clickable_menu) {
|
||||
// The whole menu is shown and the items are click-selectable
|
||||
mouse.pushCursorNum(CURSOR_ARROW);
|
||||
numLines = numEntries;
|
||||
} else {
|
||||
oldX = mouse.x();
|
||||
oldY = mouse.y();
|
||||
mouse.cursorOff();
|
||||
mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
|
||||
|
||||
// Round up number of lines in dialog to next odd number
|
||||
numLines = (numEntries / 2) * 2 + 1;
|
||||
if (numLines > 5) numLines = 5;
|
||||
}
|
||||
|
||||
// Figure out the character width
|
||||
uint16 numCols = 0;
|
||||
for (int ctr = 0; ctr < numEntries; ++ctr) {
|
||||
int len = strlen(actions[ctr]);
|
||||
if (len > numCols)
|
||||
numCols = len;
|
||||
}
|
||||
|
||||
// Create the dialog surface
|
||||
Common::Point size;
|
||||
Surface::getDialogBounds(size, numCols, numLines, false);
|
||||
Surface *s = new Surface(size.x, size.y);
|
||||
s->createDialog();
|
||||
|
||||
int selectedIndex = 0;
|
||||
bool refreshFlag = true;
|
||||
|
||||
Common::Rect r;
|
||||
if (clickable_menu) {
|
||||
r.left = Surface::textX();
|
||||
r.right = s->width() - Surface::textX() + 1;
|
||||
r.top = Surface::textY();
|
||||
r.bottom = s->height() - Surface::textY() + 1;
|
||||
}
|
||||
|
||||
bool bailOut = false;
|
||||
|
||||
while (!bailOut) {
|
||||
if (refreshFlag) {
|
||||
// Set up the contents of the menu
|
||||
s->refreshDialog();
|
||||
|
||||
for (int index = 0; index < numLines; ++index) {
|
||||
int actionIndex = clickable_menu ? index : selectedIndex - (numLines / 2) + index;
|
||||
if ((actionIndex >= 0) && (actionIndex < numEntries)) {
|
||||
byte color = textColor;
|
||||
if (index == (clickable_menu ? selectedIndex : numLines / 2))
|
||||
color = whiteColor;
|
||||
s->writeString(Surface::textX(), Surface::textY() + index * FONT_HEIGHT,
|
||||
actions[actionIndex], true,
|
||||
color, false);
|
||||
}
|
||||
}
|
||||
|
||||
s->copyToScreen(0, yMiddle-(s->height() / 2));
|
||||
system.updateScreen();
|
||||
refreshFlag = false;
|
||||
}
|
||||
|
||||
while (e.pollEvent()) {
|
||||
if (engine.shouldQuit()) {
|
||||
selectedIndex = 0xffff;
|
||||
bailOut = true;
|
||||
break;
|
||||
} else if (e.type() == Common::EVENT_WHEELUP) {
|
||||
// Scroll upwards
|
||||
if (selectedIndex > 0) {
|
||||
--selectedIndex;
|
||||
refreshFlag = true;
|
||||
}
|
||||
} else if (e.type() == Common::EVENT_WHEELDOWN) {
|
||||
// Scroll downwards
|
||||
if (selectedIndex < numEntries - 1) {
|
||||
++selectedIndex;
|
||||
refreshFlag = true;
|
||||
}
|
||||
} else if (e.type() == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
|
||||
uint16 keycode = e.event().customType;
|
||||
|
||||
if ((keycode == kActionIndexPrevious) && (selectedIndex > 0)) {
|
||||
--selectedIndex;
|
||||
refreshFlag = true;
|
||||
} else if ((keycode == kActionIndexNext) &&
|
||||
(selectedIndex < numEntries-1)) {
|
||||
++selectedIndex;
|
||||
refreshFlag = true;
|
||||
} else if (keycode == kActionIndexSelect) {
|
||||
bailOut = true;
|
||||
break;
|
||||
} else if (keycode == kActionEscape) {
|
||||
selectedIndex = 0xffff;
|
||||
bailOut = true;
|
||||
break;
|
||||
}
|
||||
} else if (clickable_menu && (e.type() == Common::EVENT_LBUTTONDOWN || e.type() == Common::EVENT_MOUSEMOVE)) {
|
||||
int16 x = mouse.x();
|
||||
int16 y = mouse.y() - yMiddle + (s->height() / 2);
|
||||
refreshFlag = true;
|
||||
|
||||
if (r.contains(x, y)) {
|
||||
selectedIndex = (y - r.top) / FONT_HEIGHT;
|
||||
if (e.type() == Common::EVENT_LBUTTONDOWN) {
|
||||
bailOut = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (!clickable_menu && ((e.type() == Common::EVENT_LBUTTONDOWN) ||
|
||||
(e.type() == Common::EVENT_MBUTTONDOWN))) {
|
||||
//mouse.waitForRelease();
|
||||
bailOut = true;
|
||||
break;
|
||||
} else if (e.type() == Common::EVENT_RBUTTONDOWN) {
|
||||
mouse.waitForRelease();
|
||||
selectedIndex = 0xffff;
|
||||
bailOut = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bailOut) {
|
||||
if (!clickable_menu) {
|
||||
// Warping the mouse to "neutral" even if the top/bottom menu
|
||||
// entry has been reached has both pros and cons. It makes the
|
||||
// menu behave a bit more sensibly, but it also makes it harder
|
||||
// to move the mouse pointer out of the ScummVM window.
|
||||
|
||||
if (mouse.y() < yMiddle - POPMENU_CHANGE_SENSITIVITY) {
|
||||
if (selectedIndex > 0) {
|
||||
--selectedIndex;
|
||||
refreshFlag = true;
|
||||
}
|
||||
mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
|
||||
} else if (mouse.y() > yMiddle + POPMENU_CHANGE_SENSITIVITY) {
|
||||
if (selectedIndex < numEntries - 1) {
|
||||
++selectedIndex;
|
||||
refreshFlag = true;
|
||||
}
|
||||
mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
|
||||
}
|
||||
}
|
||||
|
||||
system.delayMillis(20);
|
||||
}
|
||||
}
|
||||
|
||||
// bailOut
|
||||
delete s;
|
||||
|
||||
if (clickable_menu) {
|
||||
mouse.popCursor();
|
||||
} else {
|
||||
mouse.setPosition(oldX, oldY);
|
||||
mouse.cursorOn();
|
||||
}
|
||||
screen.update();
|
||||
return selectedIndex;
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
97
engines/lure/menu.h
Normal file
97
engines/lure/menu.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_MENU_H
|
||||
#define LURE_MENU_H
|
||||
|
||||
|
||||
#include "common/str.h"
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/disk.h"
|
||||
#include "lure/screen.h"
|
||||
#include "lure/surface.h"
|
||||
#include "lure/events.h"
|
||||
|
||||
#define NUM_MENUS 3
|
||||
|
||||
namespace Lure {
|
||||
|
||||
struct MenuRecordBounds {
|
||||
uint16 left, right;
|
||||
uint16 contentsX, contentsWidth;
|
||||
};
|
||||
|
||||
struct MenuRecordLanguage {
|
||||
Common::Language language;
|
||||
MenuRecordBounds menus[3];
|
||||
};
|
||||
|
||||
class MenuRecord {
|
||||
private:
|
||||
uint16 _xstart, _width;
|
||||
uint16 _hsxstart, _hsxend;
|
||||
const char **_entries;
|
||||
uint8 _numEntries;
|
||||
public:
|
||||
MenuRecord(const MenuRecordBounds *bounds, int numParams, ...);
|
||||
~MenuRecord();
|
||||
|
||||
uint16 xstart() { return _xstart; }
|
||||
uint16 width() { return _width; }
|
||||
uint16 hsxstart() { return _hsxstart; }
|
||||
uint16 hsxend() { return _hsxend; }
|
||||
uint8 numEntries() { return _numEntries; }
|
||||
const char **entries() { return _entries; }
|
||||
const char *getEntry(uint8 index);
|
||||
};
|
||||
|
||||
class Menu {
|
||||
private:
|
||||
MemoryBlock *_menu;
|
||||
MenuRecord *_menus[NUM_MENUS];
|
||||
MenuRecord *_selectedMenu;
|
||||
Surface *_surfaceMenu;
|
||||
uint8 _selectedIndex;
|
||||
|
||||
MenuRecord *getMenuAt(int x);
|
||||
uint8 getIndexAt(uint16 x, uint16 y);
|
||||
void toggleHighlight(MenuRecord *menuRec);
|
||||
void toggleHighlightItem(uint8 index);
|
||||
public:
|
||||
Menu();
|
||||
~Menu();
|
||||
static Menu &getReference();
|
||||
uint8 execute();
|
||||
MenuRecord &getMenu(uint8 index) { return *_menus[index]; }
|
||||
};
|
||||
|
||||
class PopupMenu {
|
||||
public:
|
||||
static Action Show(uint32 actionMask);
|
||||
static Action Show(int numEntries, Action *actions);
|
||||
static uint16 Show(int numEntries, const char *actions[]);
|
||||
static uint16 ShowInventory();
|
||||
static uint16 ShowItems(Action contextAction, uint16 roomNumber);
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
336
engines/lure/metaengine.cpp
Normal file
336
engines/lure/metaengine.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/savefile.h"
|
||||
#include "common/system.h"
|
||||
#include "common/translation.h"
|
||||
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymapper.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
|
||||
#include "engines/advancedDetector.h"
|
||||
|
||||
#include "lure/lure.h"
|
||||
#include "lure/detection.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
static const ADExtraGuiOptionsMap optionsList[] = {
|
||||
{
|
||||
GAMEOPTION_COPY_PROTECTION,
|
||||
{
|
||||
_s("Enable copy protection"),
|
||||
_s("Enable any copy protection that would otherwise be bypassed by default."),
|
||||
"copy_protection",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
},
|
||||
},
|
||||
#ifdef USE_TTS
|
||||
{
|
||||
GAMEOPTION_TTS_NARRATOR,
|
||||
{
|
||||
_s("TTS Narrator"),
|
||||
_s("Use TTS to read the descriptions (if TTS is available)"),
|
||||
"tts_narrator",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
}
|
||||
},
|
||||
#endif
|
||||
|
||||
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
||||
};
|
||||
|
||||
uint32 LureEngine::getFeatures() const { return _gameDescription->features; }
|
||||
Common::Language LureEngine::getLanguage() const { return _gameDescription->desc.language; }
|
||||
Common::Platform LureEngine::getPlatform() const { return _gameDescription->desc.platform; }
|
||||
|
||||
LureLanguage LureEngine::getLureLanguage() const {
|
||||
switch (_gameDescription->desc.language) {
|
||||
case Common::IT_ITA: return LANG_IT_ITA;
|
||||
case Common::FR_FRA: return LANG_FR_FRA;
|
||||
case Common::DE_DEU: return LANG_DE_DEU;
|
||||
case Common::ES_ESP: return LANG_ES_ESP;
|
||||
case Common::RU_RUS: return LANG_RU_RUS;
|
||||
case Common::EN_ANY:
|
||||
return isKonami() ? LANG_EN_KONAMI : LANG_EN_ANY;
|
||||
case Common::UNK_LANG: return LANG_UNKNOWN;
|
||||
default:
|
||||
error("Unknown game language");
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
class LureMetaEngine : public AdvancedMetaEngine<Lure::LureGameDescription> {
|
||||
public:
|
||||
const char *getName() const override {
|
||||
return "lure";
|
||||
}
|
||||
|
||||
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
|
||||
return Lure::optionsList;
|
||||
}
|
||||
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine, const Lure::LureGameDescription *desc) const override;
|
||||
|
||||
SaveStateList listSaves(const char *target) const override;
|
||||
int getMaximumSaveSlot() const override;
|
||||
bool removeSaveState(const char *target, int slot) const override;
|
||||
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
};
|
||||
|
||||
bool LureMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSupportsLoadingDuringStartup) ||
|
||||
(f == kSimpleSavesNames) ||
|
||||
(f == kSupportsDeleteSave);
|
||||
}
|
||||
|
||||
bool Lure::LureEngine::hasFeature(EngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsReturnToLauncher) ||
|
||||
(f == kSupportsLoadingDuringRuntime) ||
|
||||
(f == kSupportsSavingDuringRuntime);
|
||||
}
|
||||
|
||||
Common::Error LureMetaEngine::createInstance(OSystem *syst, Engine **engine, const Lure::LureGameDescription *desc) const {
|
||||
*engine = new Lure::LureEngine(syst,desc);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
SaveStateList LureMetaEngine::listSaves(const char *target) const {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
Common::StringArray filenames;
|
||||
Common::String saveDesc;
|
||||
Common::String pattern = "lure.###";
|
||||
|
||||
filenames = saveFileMan->listSavefiles(pattern);
|
||||
|
||||
SaveStateList saveList;
|
||||
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
|
||||
// Obtain the last 3 digits of the filename, since they correspond to the save slot
|
||||
int slotNum = atoi(file->c_str() + file->size() - 3);
|
||||
|
||||
if (slotNum >= 0 && slotNum <= 999) {
|
||||
Common::InSaveFile *in = saveFileMan->openForLoading(*file);
|
||||
if (in) {
|
||||
saveDesc = Lure::getSaveName(in);
|
||||
saveList.push_back(SaveStateDescriptor(this, slotNum, saveDesc));
|
||||
delete in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort saves based on slot number.
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
|
||||
int LureMetaEngine::getMaximumSaveSlot() const { return 999; }
|
||||
|
||||
bool LureMetaEngine::removeSaveState(const char *target, int slot) const {
|
||||
Common::String filename = target;
|
||||
filename += Common::String::format(".%03d", slot);
|
||||
|
||||
return g_system->getSavefileManager()->removeSavefile(filename);
|
||||
}
|
||||
|
||||
Common::KeymapArray LureMetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
using namespace Lure;
|
||||
|
||||
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "lure-default", _("Default keymappings"));
|
||||
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
|
||||
Keymap *fightKeyMap = new Keymap(Keymap::kKeymapTypeGame, "fight-shortcut", _("Fight sequence keymappings"));
|
||||
Keymap *indexKeyMap = new Keymap(Keymap::kKeymapTypeGame, "index-shortcut", _("Index keymappings"));
|
||||
Keymap *yesNoKeyMap = new Keymap(Keymap::kKeymapTypeGame, "yesno-shortcut", _("Yes/No keymappings"));
|
||||
|
||||
Common::Action *act;
|
||||
|
||||
{
|
||||
act = new Common::Action(kStandardActionLeftClick, _("Left click"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
engineKeyMap->addAction(act);
|
||||
|
||||
act = new Common::Action(kStandardActionRightClick, _("Right click"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
engineKeyMap->addAction(act);
|
||||
}
|
||||
|
||||
{
|
||||
act = new Common::Action("SAVEGAME", _("Save game"));
|
||||
act->setCustomEngineActionEvent(kActionSaveGame);
|
||||
act->addDefaultInputMapping("F5");
|
||||
act->addDefaultInputMapping("JOY_Y");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Common::Action("RESTOREGAME", _("Restore game"));
|
||||
act->setCustomEngineActionEvent(kActionRestoreGame);
|
||||
act->addDefaultInputMapping("F7");
|
||||
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Common::Action("RESTARTGAME", _("Restart game"));
|
||||
act->setCustomEngineActionEvent(kActionRestartGame);
|
||||
act->addDefaultInputMapping("F9");
|
||||
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
|
||||
gameKeyMap->addAction(act);
|
||||
|
||||
act = new Common::Action("ESC", _("Escape"));
|
||||
act->setCustomEngineActionEvent(kActionEscape);
|
||||
act->addDefaultInputMapping("ESCAPE");
|
||||
act->addDefaultInputMapping("JOY_X");
|
||||
gameKeyMap->addAction(act);
|
||||
}
|
||||
|
||||
{
|
||||
// I18N: Move actor left during fight
|
||||
act = new Common::Action("MOVELEFT", _("Move left"));
|
||||
act->setCustomEngineActionEvent(kActionFightMoveLeft);
|
||||
act->addDefaultInputMapping("LEFT");
|
||||
act->addDefaultInputMapping("JOY_LEFT");
|
||||
fightKeyMap->addAction(act);
|
||||
|
||||
|
||||
// I18N: Move actor right during fight
|
||||
act = new Common::Action("MOVERIGHT", _("Move right"));
|
||||
act->setCustomEngineActionEvent(kActionFightMoveRight);
|
||||
act->addDefaultInputMapping("RIGHT");
|
||||
act->addDefaultInputMapping("JOY_RIGHT");
|
||||
fightKeyMap->addAction(act);
|
||||
|
||||
// I18N: Shift Cursor during fight to top left
|
||||
act = new Common::Action("CURSORLEFTTOP", _("Shift Cursor - Top left"));
|
||||
act->setCustomEngineActionEvent(kActionFightCursorLeftTop);
|
||||
act->addDefaultInputMapping("KP7");
|
||||
fightKeyMap->addAction(act);
|
||||
|
||||
// I18N: Shift Cursor during fight to middle left
|
||||
act = new Common::Action("CURSORLEFTMIDDLE", _("Shift Cursor - Middle left"));
|
||||
act->setCustomEngineActionEvent(kActionFightCursorLeftTop);
|
||||
act->addDefaultInputMapping("KP4");
|
||||
fightKeyMap->addAction(act);
|
||||
|
||||
// I18N: Shift Cursor during fight to bottom left
|
||||
act = new Common::Action("CURSORLEFTBOTTOM", _("Shift Cursor - Bottom left"));
|
||||
act->setCustomEngineActionEvent(kActionFightCursorLeftTop);
|
||||
act->addDefaultInputMapping("KP1");
|
||||
fightKeyMap->addAction(act);
|
||||
|
||||
// I18N: Shift Cursor during fight to top right
|
||||
act = new Common::Action("CURSORRIGHTTOP", _("Shift Cursor - Top right"));
|
||||
act->setCustomEngineActionEvent(kActionFightCursorLeftTop);
|
||||
act->addDefaultInputMapping("KP9");
|
||||
fightKeyMap->addAction(act);
|
||||
|
||||
// I18N: Shift Cursor during fight to middle right
|
||||
act = new Common::Action("CURSORRIGHTMIDDLE", _("Shift Cursor - Middle right"));
|
||||
act->setCustomEngineActionEvent(kActionFightCursorLeftTop);
|
||||
act->addDefaultInputMapping("KP6");
|
||||
fightKeyMap->addAction(act);
|
||||
|
||||
// I18N: Shift Cursor during fight to bottom right
|
||||
act = new Common::Action("CURSORRIGHTBOTTOM", _("Shift Cursor - Bottom right"));
|
||||
act->setCustomEngineActionEvent(kActionFightCursorLeftTop);
|
||||
act->addDefaultInputMapping("KP3");
|
||||
fightKeyMap->addAction(act);
|
||||
}
|
||||
|
||||
{
|
||||
act = new Common::Action("INDEXPREVIOUS", _("Go to next index"));
|
||||
act->setCustomEngineActionEvent(kActionIndexNext);
|
||||
act->addDefaultInputMapping("KP2");
|
||||
act->addDefaultInputMapping("DOWN");
|
||||
act->addDefaultInputMapping("JOY_DOWN");
|
||||
indexKeyMap->addAction(act);
|
||||
|
||||
act = new Common::Action("INDEXNEXT", _("Go to previous index"));
|
||||
act->setCustomEngineActionEvent(kActionIndexPrevious);
|
||||
act->addDefaultInputMapping("KP8");
|
||||
act->addDefaultInputMapping("UP");
|
||||
act->addDefaultInputMapping("JOY_UP");
|
||||
indexKeyMap->addAction(act);
|
||||
|
||||
act = new Common::Action("INDEXSELECT", _("Select index"));
|
||||
act->setCustomEngineActionEvent(kActionIndexSelect);
|
||||
act->addDefaultInputMapping("KP3");
|
||||
act->addDefaultInputMapping("JOY_CENTER");
|
||||
indexKeyMap->addAction(act);
|
||||
}
|
||||
|
||||
{
|
||||
String s = ConfMan.get("language", target);
|
||||
Language l = Common::parseLanguage(s);
|
||||
|
||||
act = new Common::Action("YES", _("Press \"Yes\" key"));
|
||||
act->setCustomEngineActionEvent(kActionYes);
|
||||
act->addDefaultInputMapping("JOY_LEFT_STICK");
|
||||
if (l == Common::FR_FRA)
|
||||
act->addDefaultInputMapping("o");
|
||||
else if ((l == Common::DE_DEU) || (l == Common::NL_NLD))
|
||||
act->addDefaultInputMapping("j");
|
||||
else if ((l == Common::ES_ESP) || (l == Common::IT_ITA))
|
||||
act->addDefaultInputMapping("s");
|
||||
else if (l == Common::RU_RUS)
|
||||
act->addDefaultInputMapping("l");
|
||||
else
|
||||
act->addDefaultInputMapping("y");
|
||||
yesNoKeyMap->addAction(act);
|
||||
|
||||
act = new Common::Action("NO", _("Press \"No\" key"));
|
||||
act->setCustomEngineActionEvent(kActionNo);
|
||||
act->addDefaultInputMapping("JOY_RIGHT_STICK");
|
||||
if (l == Common::RU_RUS)
|
||||
act->addDefaultInputMapping("y");
|
||||
else
|
||||
act->addDefaultInputMapping("n");
|
||||
yesNoKeyMap->addAction(act);
|
||||
}
|
||||
|
||||
KeymapArray keymaps(5);
|
||||
keymaps[0] = engineKeyMap;
|
||||
keymaps[1] = gameKeyMap;
|
||||
keymaps[2] = fightKeyMap;
|
||||
keymaps[3] = indexKeyMap;
|
||||
keymaps[4] = yesNoKeyMap;
|
||||
|
||||
yesNoKeyMap->setEnabled(false);
|
||||
|
||||
return keymaps;
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(LURE)
|
||||
REGISTER_PLUGIN_DYNAMIC(LURE, PLUGIN_TYPE_ENGINE, LureMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(LURE, PLUGIN_TYPE_ENGINE, LureMetaEngine);
|
||||
#endif
|
||||
36
engines/lure/module.mk
Normal file
36
engines/lure/module.mk
Normal file
@@ -0,0 +1,36 @@
|
||||
MODULE := engines/lure
|
||||
|
||||
MODULE_OBJS := \
|
||||
animseq.o \
|
||||
debugger.o \
|
||||
decode.o \
|
||||
disk.o \
|
||||
events.o \
|
||||
fights.o \
|
||||
game.o \
|
||||
hotspots.o \
|
||||
intro.o \
|
||||
lure.o \
|
||||
memory.o \
|
||||
menu.o \
|
||||
metaengine.o \
|
||||
palette.o \
|
||||
res.o \
|
||||
res_struct.o \
|
||||
room.o \
|
||||
screen.o \
|
||||
scripts.o \
|
||||
sound.o \
|
||||
strings.o \
|
||||
surface.o
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_LURE), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
226
engines/lure/palette.cpp
Normal file
226
engines/lure/palette.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lure/lure.h"
|
||||
#include "lure/palette.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
// Constructor
|
||||
// Defaults the palette to a full 256 entry palette
|
||||
|
||||
Palette::Palette() {
|
||||
_numEntries = GAME_COLORS;
|
||||
_palette = Memory::allocate(_numEntries * 4);
|
||||
_palette->empty();
|
||||
}
|
||||
|
||||
// Consructor
|
||||
// Sets up a palette with the given number of entries and a copy of the passed data
|
||||
|
||||
Palette::Palette(uint16 srcNumEntries, const byte *srcData, PaletteSource paletteSource) {
|
||||
_numEntries = srcNumEntries;
|
||||
_palette = Memory::allocate(_numEntries * 4);
|
||||
|
||||
if (srcData) {
|
||||
if (paletteSource == RGB64)
|
||||
convertRgb64Palette(srcData, _numEntries);
|
||||
else if (paletteSource == EGA) {
|
||||
assert((srcNumEntries == 16) || (srcNumEntries == 17));
|
||||
convertEGAPalette(srcData);
|
||||
} else
|
||||
_palette->copyFrom(srcData, 0, 0, _numEntries * 4);
|
||||
|
||||
} else {
|
||||
// No data provided, set a null palette
|
||||
_palette->empty();
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor
|
||||
// Makes a copy of a passed palette object
|
||||
|
||||
Palette::Palette(Palette &src) {
|
||||
_numEntries = src.numEntries();
|
||||
_palette = Memory::duplicate(src._palette);
|
||||
}
|
||||
|
||||
// Constructor
|
||||
// Loads a palette from a resource
|
||||
|
||||
Palette::Palette(uint16 resourceId, PaletteSource paletteSource) {
|
||||
Disk &disk = Disk::getReference();
|
||||
bool isEGA = LureEngine::getReference().isEGA();
|
||||
MemoryBlock *srcData = disk.getEntry(resourceId);
|
||||
|
||||
if (paletteSource == DEFAULT)
|
||||
paletteSource = isEGA ? EGA : RGB64;
|
||||
|
||||
switch (paletteSource) {
|
||||
case EGA:
|
||||
// Handle EGA palette
|
||||
if ((srcData->size() != 16) && (srcData->size() != 17))
|
||||
error("Specified resource %d is not a palette", resourceId);
|
||||
|
||||
_numEntries = 16;
|
||||
_palette = Memory::allocate(_numEntries * 4);
|
||||
convertEGAPalette(srcData->data());
|
||||
break;
|
||||
|
||||
case RGB64:
|
||||
if (((srcData->size() % 3) != 0) || ((srcData->size() / 3) > GAME_COLORS))
|
||||
error("Specified resource %d is not a palette", resourceId);
|
||||
|
||||
_numEntries = srcData->size() / 3;
|
||||
_palette = Memory::allocate(_numEntries * 4);
|
||||
convertRgb64Palette(srcData->data(), _numEntries);
|
||||
break;
|
||||
|
||||
default:
|
||||
error("Invalid palette type specified for palette resource");
|
||||
}
|
||||
|
||||
delete srcData;
|
||||
}
|
||||
|
||||
// Destructor
|
||||
|
||||
Palette::~Palette() {
|
||||
delete _palette;
|
||||
}
|
||||
|
||||
void Palette::convertRgb64Palette(const byte *srcPalette, uint16 srcNumEntries) {
|
||||
byte *pDest = _palette->data();
|
||||
const byte *pSrc = srcPalette;
|
||||
|
||||
while (srcNumEntries-- > 0) {
|
||||
*pDest++ = (pSrc[0] << 2) + (pSrc[0] >> 4);
|
||||
*pDest++ = (pSrc[1] << 2) + (pSrc[1] >> 4);
|
||||
*pDest++ = (pSrc[2] << 2) + (pSrc[2] >> 4);
|
||||
*pDest++ = 0;
|
||||
pSrc += 3;
|
||||
}
|
||||
}
|
||||
|
||||
// EGA palette definition copied from DOSBox 0.72
|
||||
static byte ega_palette[64][3] =
|
||||
{
|
||||
{0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
|
||||
{0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
|
||||
{0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
|
||||
{0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
|
||||
{0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
|
||||
{0x00,0x00,0x00}, {0x00,0x00,0x2a}, {0x00,0x2a,0x00}, {0x00,0x2a,0x2a}, {0x2a,0x00,0x00}, {0x2a,0x00,0x2a}, {0x2a,0x15,0x00}, {0x2a,0x2a,0x2a},
|
||||
{0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f},
|
||||
{0x15,0x15,0x15}, {0x15,0x15,0x3f}, {0x15,0x3f,0x15}, {0x15,0x3f,0x3f}, {0x3f,0x15,0x15}, {0x3f,0x15,0x3f}, {0x3f,0x3f,0x15}, {0x3f,0x3f,0x3f}
|
||||
};
|
||||
|
||||
void Palette::convertEGAPalette(const byte *srcPalette) {
|
||||
byte *pDest = _palette->data();
|
||||
const byte *pSrc = srcPalette;
|
||||
|
||||
for (int index = 0; index < 16; ++index, ++pSrc) {
|
||||
// Handle RGB components of entry
|
||||
assert(*pSrc < 64);
|
||||
byte *v = &ega_palette[*pSrc][0];
|
||||
*pDest++ = *v++ * 4;
|
||||
*pDest++ = *v++ * 4;
|
||||
*pDest++ = *v++ * 4;
|
||||
*pDest++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Palette::setEntry(uint8 index, uint32 value) {
|
||||
if (index >= numEntries()) error("Invalid palette index: %d", index);
|
||||
uint32 *entry = (uint32 *) (data() + index * 4);
|
||||
*entry = value;
|
||||
}
|
||||
|
||||
uint32 Palette::getEntry(uint8 index) {
|
||||
if (index >= numEntries()) error("Invalid palette index: %d", index);
|
||||
uint32 *entry = (uint32 *) (data() + index * 4);
|
||||
return *entry;
|
||||
}
|
||||
|
||||
void Palette::copyFrom(Palette *src) {
|
||||
_palette->copyFrom(src->palette());
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
PaletteCollection::PaletteCollection(uint16 resourceId) {
|
||||
Disk &d = Disk::getReference();
|
||||
MemoryBlock *resource = d.getEntry(resourceId);
|
||||
bool isEGA = LureEngine::getReference().isEGA();
|
||||
uint32 palSize;
|
||||
uint8 *data = resource->data();
|
||||
|
||||
if (isEGA) {
|
||||
// EGA Palette collection - only has 1 sub-palette
|
||||
if ((resource->size() != 16) && (resource->size() != 17))
|
||||
error("Resource #%d is not a valid palette set", resourceId);
|
||||
|
||||
_numPalettes = 1;
|
||||
_palettes = (Palette **) Memory::alloc(1 * sizeof(Palette *));
|
||||
_palettes[0] = new Palette(16, data, EGA);
|
||||
|
||||
} else {
|
||||
// VGA Palette collection
|
||||
if (resource->size() % (SUB_PALETTE_SIZE * 3) != 0)
|
||||
error("Resource #%d is not a valid palette set", resourceId);
|
||||
|
||||
palSize = SUB_PALETTE_SIZE * 3;
|
||||
_numPalettes = resource->size() / palSize;
|
||||
|
||||
_palettes = (Palette **) Memory::alloc(_numPalettes * sizeof(Palette *));
|
||||
for (uint8 paletteCtr = 0; paletteCtr < _numPalettes; ++paletteCtr, data += palSize)
|
||||
_palettes[paletteCtr] = new Palette(SUB_PALETTE_SIZE, data, RGB64);
|
||||
|
||||
// WORKAROUND Intro animation 1 VGA palette has bad entries,
|
||||
// causing the text to be all white instead of shades of grey.
|
||||
// Updating it here with the color values of the other intro
|
||||
// text screens.
|
||||
if (resourceId == 0x32 && _palettes[0]->getEntry(0x22) == 0x00FFFFFF) {
|
||||
_palettes[0]->setEntry(0x22, 0x00E3E3E3); // 38 38 38
|
||||
_palettes[0]->setEntry(0x24, 0x00C3C3C3); // 30 30 30
|
||||
_palettes[0]->setEntry(0x26, 0x00929292); // 24 24 24
|
||||
_palettes[0]->setEntry(0x27, 0x00717171); // 1C 1C 1C
|
||||
_palettes[0]->setEntry(0x28, 0x00000000); // 00 00 00
|
||||
}
|
||||
}
|
||||
|
||||
delete resource;
|
||||
}
|
||||
|
||||
PaletteCollection::~PaletteCollection() {
|
||||
for (int paletteCtr = 0; paletteCtr < _numPalettes; ++paletteCtr)
|
||||
delete _palettes[paletteCtr];
|
||||
free(_palettes);
|
||||
}
|
||||
|
||||
Palette &PaletteCollection::getPalette(uint8 paletteNum) {
|
||||
if (paletteNum >= _numPalettes)
|
||||
error("Invalid palette index specified");
|
||||
return *_palettes[paletteNum];
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
69
engines/lure/palette.h
Normal file
69
engines/lure/palette.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_PALETTE_H
|
||||
#define LURE_PALETTE_H
|
||||
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/disk.h"
|
||||
#include "lure/memory.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
enum PaletteSource {DEFAULT, RGB, RGB64, EGA};
|
||||
|
||||
class Palette {
|
||||
private:
|
||||
MemoryBlock *_palette;
|
||||
uint16 _numEntries;
|
||||
|
||||
void convertRgb64Palette(const byte *srcPalette, uint16 srcNumEntries);
|
||||
void convertEGAPalette(const byte *srcPalette);
|
||||
public:
|
||||
Palette();
|
||||
Palette(uint16 srcNumEntries, const byte *srcData, PaletteSource paletteSource);
|
||||
Palette(Palette &src);
|
||||
Palette(uint16 resourceId, PaletteSource paletteSource = DEFAULT);
|
||||
~Palette();
|
||||
|
||||
uint8 *data() { return _palette->data(); }
|
||||
MemoryBlock *palette() { return _palette; }
|
||||
uint16 numEntries() { return _palette->size() / 4; }
|
||||
void setEntry(uint8 index, uint32 value);
|
||||
uint32 getEntry(uint8 index);
|
||||
void copyFrom(Palette *src);
|
||||
};
|
||||
|
||||
class PaletteCollection {
|
||||
private:
|
||||
Palette **_palettes;
|
||||
uint8 _numPalettes;
|
||||
public:
|
||||
PaletteCollection(uint16 resourceId);
|
||||
~PaletteCollection();
|
||||
|
||||
uint8 numPalettes() { return _numPalettes; }
|
||||
Palette &getPalette(uint8 paletteNum);
|
||||
};
|
||||
|
||||
} // end of namspace Lure
|
||||
|
||||
#endif
|
||||
830
engines/lure/res.cpp
Normal file
830
engines/lure/res.cpp
Normal file
@@ -0,0 +1,830 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lure/res.h"
|
||||
#include "lure/disk.h"
|
||||
#include "lure/scripts.h"
|
||||
#include "lure/screen.h"
|
||||
#include "lure/lure.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/events.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
static Resources *int_resources = nullptr;
|
||||
|
||||
Resources &Resources::getReference() {
|
||||
return *int_resources;
|
||||
}
|
||||
|
||||
Resources::Resources() : _rnd(LureEngine::getReference().rnd()) {
|
||||
int_resources = this;
|
||||
reloadData();
|
||||
|
||||
// Load the string list
|
||||
MemoryBlock *mb = Disk::getReference().getEntry(STRING_LIST_RESOURCE_ID);
|
||||
_stringList.load(mb);
|
||||
delete mb;
|
||||
|
||||
// WORKAROUND: In Spanish the look "Obsevar" should be "Observar"
|
||||
if (!Common::String(_stringList.getString(LOOK)).compareTo("Obsevar"))
|
||||
_stringList.setString(LOOK, "Observar");
|
||||
}
|
||||
|
||||
Resources::~Resources() {
|
||||
// Free up any loaded data
|
||||
freeData();
|
||||
|
||||
// Free up constant data
|
||||
_stringList.clear();
|
||||
}
|
||||
|
||||
void Resources::freeData() {
|
||||
_activeHotspots.clear();
|
||||
_roomData.clear();
|
||||
_hotspotData.clear();
|
||||
_hotspotOverrides.clear();
|
||||
_animData.clear();
|
||||
_exitJoins.clear();
|
||||
_delayList.clear();
|
||||
_charSchedules.clear();
|
||||
_randomActions.clear();
|
||||
_indexedRoomExitHospots.clear();
|
||||
_pausedList.clear();
|
||||
_actionsList.clear();
|
||||
_coordinateList.clear();
|
||||
_talkHeaders.clear();
|
||||
_talkData.clear();
|
||||
_giveTalkIds.clear();
|
||||
|
||||
free(_hotspotScriptData);
|
||||
delete _paletteSubset;
|
||||
delete _scriptData;
|
||||
delete _script2Data;
|
||||
delete _talkDialogData;
|
||||
delete _messagesData;
|
||||
delete _cursors;
|
||||
delete[] _charOffsets;
|
||||
}
|
||||
|
||||
struct AnimRecordTemp {
|
||||
uint16 offset;
|
||||
MovementDataList *list;
|
||||
};
|
||||
|
||||
void Resources::reset() {
|
||||
freeData();
|
||||
|
||||
_fieldList.reset();
|
||||
_barmanLists.reset();
|
||||
_talkState = TALK_NONE;
|
||||
_activeTalkData = nullptr;
|
||||
|
||||
reloadData();
|
||||
}
|
||||
|
||||
void Resources::reloadData() {
|
||||
Disk &d = Disk::getReference();
|
||||
bool isEGA = LureEngine::getReference().isEGA();
|
||||
MemoryBlock *mb, *paths;
|
||||
uint16 *offset, offsetVal;
|
||||
uint16 recordId, startOffset;
|
||||
int ctr;
|
||||
uint16 *v;
|
||||
|
||||
// Get the palette subset data
|
||||
_paletteSubset = isEGA ? nullptr : new Palette(ALT_PALETTE_RESOURCE_ID);
|
||||
|
||||
// Load room data
|
||||
mb = d.getEntry(ROOM_DATA_RESOURCE_ID);
|
||||
paths = d.getEntry(ROOM_PATHS_RESOURCE_ID);
|
||||
|
||||
offset = (uint16 *) mb->data();
|
||||
while ((offsetVal = READ_LE_UINT16(offset++)) != 0xffff) {
|
||||
if (offsetVal != 0) {
|
||||
// Get room resource
|
||||
RoomResource *rec = (RoomResource *) (mb->data() + offsetVal);
|
||||
|
||||
RoomData *newEntry = new RoomData(rec, paths);
|
||||
_roomData.push_back(RoomDataList::value_type(newEntry));
|
||||
|
||||
uint8 numExits = rec->numExits;
|
||||
if (numExits > 0) {
|
||||
RoomExitResource *exitRes = (RoomExitResource *)
|
||||
(mb->data() + offsetVal + sizeof(RoomResource));
|
||||
|
||||
for (uint16 exitCtr = 0; exitCtr < numExits; ++exitCtr, ++exitRes) {
|
||||
RoomExitData *exit = new RoomExitData(exitRes);
|
||||
newEntry->exits.push_back(RoomExitList::value_type(exit));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete mb;
|
||||
delete paths;
|
||||
|
||||
// Load room exits
|
||||
mb = d.getEntry(ROOM_EXITS_RESOURCE_ID);
|
||||
ctr = 0;
|
||||
for (;;) {
|
||||
offsetVal = READ_LE_UINT16(mb->data() + (ctr * 2));
|
||||
if (offsetVal == 0xffff) break;
|
||||
|
||||
if (offsetVal != 0) {
|
||||
RoomData *room = getRoom(ctr);
|
||||
if (room) {
|
||||
RoomExitHotspotResource *re = (RoomExitHotspotResource *)
|
||||
(mb->data() + offsetVal);
|
||||
while (READ_LE_UINT16(&re->hotspotId) != 0xffff) {
|
||||
RoomExitHotspotData *newEntry = new RoomExitHotspotData(re);
|
||||
room->exitHotspots.push_back(RoomExitHotspotList::value_type(newEntry));
|
||||
++re;
|
||||
}
|
||||
}
|
||||
}
|
||||
++ctr;
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Load room joins
|
||||
mb = d.getEntry(ROOM_EXIT_JOINS_RESOURCE_ID);
|
||||
RoomExitJoinResource *joinRec = (RoomExitJoinResource *) mb->data();
|
||||
while (READ_LE_UINT16(&joinRec->hotspot1Id) != 0xffff) {
|
||||
RoomExitJoinData *newEntry = new RoomExitJoinData(joinRec);
|
||||
_exitJoins.push_back(RoomExitJoinList::value_type(newEntry));
|
||||
|
||||
GET_NEXT(joinRec, RoomExitJoinResource);
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Load the set of NPC schedules
|
||||
mb = d.getEntry(NPC_SCHEDULES_RESOURCE_ID);
|
||||
|
||||
// Load the lookup list of support data indexes used in the script engine
|
||||
numCharOffsets = 0;
|
||||
offset = (uint16 *) mb->data();
|
||||
while (READ_LE_UINT16(offset++) != 0xffff) ++numCharOffsets;
|
||||
_charOffsets = new uint16[numCharOffsets];
|
||||
offset = (uint16 *) mb->data();
|
||||
for (ctr = 0; ctr < numCharOffsets; ++ctr, ++offset)
|
||||
_charOffsets[ctr] = READ_LE_UINT16(offset);
|
||||
|
||||
// Next load up the list of random actions your follower can do in each room
|
||||
|
||||
++offset;
|
||||
while (READ_LE_UINT16(offset) != 0xffff) {
|
||||
RandomActionSet *actionSet = new RandomActionSet(offset);
|
||||
_randomActions.push_back(RandomActionList::value_type(actionSet));
|
||||
}
|
||||
|
||||
// Loop through loading the schedules
|
||||
ctr = 0;
|
||||
while ((startOffset = READ_LE_UINT16(++offset)) != 0xffff) {
|
||||
CharacterScheduleResource *res = (CharacterScheduleResource *) (mb->data() + startOffset);
|
||||
CharacterScheduleSet *newEntry = new CharacterScheduleSet(res, ++ctr);
|
||||
_charSchedules.push_back(CharacterScheduleList::value_type(newEntry));
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Load the hotspot list
|
||||
mb = d.getEntry(HOTSPOT_DATA_RESOURCE_ID);
|
||||
HotspotResource *hsRec = (HotspotResource *) mb->data();
|
||||
while (READ_LE_UINT16(&hsRec->hotspotId) != 0xffff) {
|
||||
HotspotData *newEntry = new HotspotData(hsRec);
|
||||
_hotspotData.push_back(HotspotDataList::value_type(newEntry));
|
||||
|
||||
GET_NEXT(hsRec, HotspotResource);
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Load the hotspot overrides
|
||||
mb = d.getEntry(HOTSPOT_OVERRIDE_DATA_RESOURCE_ID);
|
||||
HotspotOverrideResource *hsoRec = (HotspotOverrideResource *) mb->data();
|
||||
while (READ_LE_UINT16(&hsoRec->hotspotId) != 0xffff) {
|
||||
HotspotOverrideData *newEntry = new HotspotOverrideData(hsoRec);
|
||||
_hotspotOverrides.push_back(HotspotOverrideList::value_type(newEntry));
|
||||
++hsoRec;
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Load the animation list
|
||||
mb = d.getEntry(ANIM_DATA_RESOURCE_ID);
|
||||
HotspotAnimResource *animRec = (HotspotAnimResource *) mb->data();
|
||||
while (READ_LE_UINT16(&animRec->animRecordId) != 0xffff) {
|
||||
HotspotAnimData *newEntry = new HotspotAnimData(animRec);
|
||||
_animData.push_back(HotspotAnimList::value_type(newEntry));
|
||||
|
||||
// Handle any direction frames
|
||||
AnimRecordTemp dirEntries[4] = {
|
||||
{FROM_LE_16(animRec->leftOffset), &newEntry->leftFrames},
|
||||
{FROM_LE_16(animRec->rightOffset), &newEntry->rightFrames},
|
||||
{FROM_LE_16(animRec->upOffset), &newEntry->upFrames},
|
||||
{FROM_LE_16(animRec->downOffset), &newEntry->downFrames}};
|
||||
for (int dirCtr = 0; dirCtr < 4; ++dirCtr) {
|
||||
offsetVal = dirEntries[dirCtr].offset;
|
||||
if (offsetVal != 0) {
|
||||
MovementResource *moveRec = (MovementResource *)
|
||||
(mb->data() + offsetVal);
|
||||
while (READ_LE_UINT16(&moveRec->frameNumber) != 0xffff) {
|
||||
MovementData *newMove = new MovementData(moveRec);
|
||||
dirEntries[dirCtr].list->push_back(MovementDataList::value_type(newMove));
|
||||
++moveRec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++animRec;
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Hotspot scripts
|
||||
mb = d.getEntry(HOTSPOT_SCRIPT_LIST_RESOURCE_ID);
|
||||
uint16 numEntries = mb->size() / 2;
|
||||
uint16 *srcVal = (uint16 *) mb->data();
|
||||
uint16 *destVal = _hotspotScriptData = (uint16 *)
|
||||
Memory::alloc(numEntries * sizeof(uint16));
|
||||
for (ctr = 0; ctr < numEntries; ++ctr, ++srcVal, ++destVal) {
|
||||
*destVal = READ_LE_UINT16(srcVal);
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Handle the hotspot action lists
|
||||
mb = d.getEntry(ACTION_LIST_RESOURCE_ID);
|
||||
v = (uint16 *) mb->data();
|
||||
while ((recordId = READ_LE_UINT16(v)) != 0xffff) {
|
||||
++v;
|
||||
offsetVal = READ_LE_UINT16(v);
|
||||
++v;
|
||||
|
||||
HotspotActionList *list = new HotspotActionList(
|
||||
recordId, mb->data() + offsetVal);
|
||||
_actionsList.push_back(HotspotActionSet::value_type(list));
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Read in the talk data header
|
||||
mb = d.getEntry(TALK_HEADER_RESOURCE_ID);
|
||||
TalkHeaderResource *thHeader = (TalkHeaderResource *) mb->data();
|
||||
uint16 hotspotId;
|
||||
while ((hotspotId = READ_LE_UINT16(&thHeader->hotspotId)) != 0xffff) {
|
||||
uint16 *offsets = (uint16 *) (mb->data() + READ_LE_UINT16(&thHeader->offset));
|
||||
TalkHeaderData *newEntry = new TalkHeaderData(hotspotId, offsets);
|
||||
|
||||
_talkHeaders.push_back(TalkHeaderList::value_type(newEntry));
|
||||
++thHeader;
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Read in the talk data entries
|
||||
mb = d.getEntry(TALK_DATA_RESOURCE_ID);
|
||||
|
||||
// First get the list of give talk Ids
|
||||
v = (uint16 *) mb->data();
|
||||
|
||||
for (int talkIndex = 0; talkIndex < NUM_GIVE_TALK_IDS; ++talkIndex)
|
||||
_giveTalkIds.push_back(READ_LE_UINT16(v++));
|
||||
|
||||
// Get the following talk ata
|
||||
|
||||
byte *dataStart = (byte *) v;
|
||||
TalkDataHeaderResource *tdHeader = (TalkDataHeaderResource *) dataStart;
|
||||
|
||||
while ((recordId = READ_LE_UINT16(&tdHeader->recordId)) != 0xffff) {
|
||||
TalkData *data = new TalkData(recordId);
|
||||
|
||||
TalkDataResource *entry = (TalkDataResource *) (dataStart + READ_LE_UINT16(&tdHeader->listOffset));
|
||||
while (READ_LE_UINT16(&entry->preSequenceId) != 0xffff) {
|
||||
TalkEntryData *newEntry = new TalkEntryData(entry);
|
||||
data->entries.push_back(TalkEntryList::value_type(newEntry));
|
||||
++entry;
|
||||
}
|
||||
|
||||
entry = (TalkDataResource *) (dataStart + READ_LE_UINT16(&tdHeader->responsesOffset));
|
||||
while (READ_LE_UINT16(&entry->preSequenceId) != 0xffff) {
|
||||
TalkEntryData *newEntry = new TalkEntryData(entry);
|
||||
data->responses.push_back(TalkEntryList::value_type(newEntry));
|
||||
++entry;
|
||||
}
|
||||
|
||||
_talkData.push_back(TalkDataList::value_type(data));
|
||||
++tdHeader;
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Load in the list of room exit coordinates
|
||||
mb = d.getEntry(EXIT_COORDINATES_RESOURCE_ID);
|
||||
RoomExitCoordinateEntryResource *coordRec = (RoomExitCoordinateEntryResource *) mb->data();
|
||||
while (READ_LE_UINT16(coordRec) != 0xffff) {
|
||||
RoomExitCoordinates *newEntry = new RoomExitCoordinates(coordRec);
|
||||
_coordinateList.push_back(RoomExitCoordinatesList::value_type(newEntry));
|
||||
++coordRec;
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Load the list of room exit hotspot Ids
|
||||
mb = d.getEntry(EXIT_HOTSPOT_ID_LIST);
|
||||
RoomExitIndexedHotspotResource *indexedRec = (RoomExitIndexedHotspotResource *) mb->data();
|
||||
while (READ_LE_UINT16(indexedRec) != 0xffff) {
|
||||
_indexedRoomExitHospots.push_back(RoomExitIndexedHotspotList::value_type(new RoomExitIndexedHotspotData(indexedRec)));
|
||||
indexedRec++;
|
||||
}
|
||||
delete mb;
|
||||
|
||||
// Initialize delay list
|
||||
_delayList.clear(true);
|
||||
|
||||
// Load miscellaneous data
|
||||
_cursors = d.getEntry(CURSOR_RESOURCE_ID);
|
||||
_scriptData = d.getEntry(SCRIPT_DATA_RESOURCE_ID);
|
||||
_script2Data = d.getEntry(SCRIPT2_DATA_RESOURCE_ID);
|
||||
_messagesData = d.getEntry(MESSAGES_LIST_RESOURCE_ID);
|
||||
_talkDialogData = d.getEntry(TALK_DIALOG_RESOURCE_ID);
|
||||
|
||||
_activeTalkData = nullptr;
|
||||
_currentAction = NONE;
|
||||
_talkState = TALK_NONE;
|
||||
_talkSelection = 0;
|
||||
_talkStartEntry = 0;
|
||||
_talkDetails.active = false;
|
||||
_talkingCharacter = 0;
|
||||
}
|
||||
|
||||
RoomExitJoinData *Resources::getExitJoin(uint16 hotspotId) {
|
||||
RoomExitJoinList::iterator i;
|
||||
|
||||
for (i = _exitJoins.begin(); i != _exitJoins.end(); ++i) {
|
||||
RoomExitJoinData *rec = (*i).get();
|
||||
if ((rec->hotspots[0].hotspotId == hotspotId) || (rec->hotspots[1].hotspotId == hotspotId))
|
||||
return rec;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16 Resources::getHotspotScript(uint16 index) {
|
||||
return _hotspotScriptData[index];
|
||||
}
|
||||
|
||||
RoomData *Resources::getRoom(uint16 roomNumber) {
|
||||
RoomDataList::iterator i;
|
||||
|
||||
for (i = _roomData.begin(); i != _roomData.end(); ++i) {
|
||||
RoomData *rec = (*i).get();
|
||||
if (rec->roomNumber == roomNumber) return rec;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Resources::checkHotspotExtent(HotspotData *hotspot) {
|
||||
uint16 roomNum = hotspot->roomNumber;
|
||||
RoomData *room = getRoom(roomNum);
|
||||
return (hotspot->startX >= room->clippingXStart) && ((room->clippingXEnd == 0) ||
|
||||
(hotspot->startX + 32 < room->clippingXEnd));
|
||||
}
|
||||
|
||||
void Resources::insertPaletteSubset(Palette &p) {
|
||||
p.palette()->copyFrom(_paletteSubset->palette(), 0, 129*4, 60*4);
|
||||
p.palette()->copyFrom(_paletteSubset->palette(), 60*4, 220*4, 8*4);
|
||||
}
|
||||
|
||||
byte *Resources::getCursor(uint8 cursorNum) {
|
||||
if (!LureEngine::getReference().isEGA())
|
||||
return _cursors->data() + (cursorNum * CURSOR_SIZE);
|
||||
|
||||
Common::fill(&_cursor[0], &_cursor[0] + CURSOR_SIZE, 0);
|
||||
byte *pSrc = _cursors->data() + (cursorNum * 64);
|
||||
byte *pDest = &_cursor[0];
|
||||
|
||||
for (int y = 0; y < 16; ++y) {
|
||||
for (int x = 0; x < 2; ++x) {
|
||||
for (int planeNum = 0; planeNum < 2; ++planeNum, ++pSrc) {
|
||||
|
||||
byte v = *pSrc;
|
||||
for (int bitCtr = 0; bitCtr < 8; ++bitCtr, v <<= 1) {
|
||||
if ((v & 0x80) != 0)
|
||||
*(pDest + bitCtr) |= 1 << planeNum;
|
||||
else
|
||||
*(pDest + bitCtr) &= ~(1 << planeNum);
|
||||
}
|
||||
}
|
||||
|
||||
pDest += 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Post-process the cells to adjust the color
|
||||
for (int index = 0; index < CURSOR_SIZE; ++index) {
|
||||
if (_cursor[index] == 3) _cursor[index] = 15;
|
||||
}
|
||||
|
||||
return &_cursor[0];
|
||||
}
|
||||
|
||||
HotspotData *Resources::getHotspot(uint16 hotspotId) {
|
||||
HotspotDataList::iterator i;
|
||||
|
||||
for (i = _hotspotData.begin(); i != _hotspotData.end(); ++i) {
|
||||
HotspotData *rec = (*i).get();
|
||||
if (rec->hotspotId == hotspotId) return rec;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Hotspot *Resources::getActiveHotspot(uint16 hotspotId) {
|
||||
HotspotList::iterator i;
|
||||
|
||||
for (i = _activeHotspots.begin(); i != _activeHotspots.end(); ++i) {
|
||||
Hotspot *rec = (*i).get();
|
||||
if (rec->hotspotId() == hotspotId) return rec;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
HotspotOverrideData *Resources::getHotspotOverride(uint16 hotspotId) {
|
||||
HotspotOverrideList::iterator i;
|
||||
|
||||
for (i = _hotspotOverrides.begin(); i != _hotspotOverrides.end(); ++i) {
|
||||
HotspotOverrideData *rec = (*i).get();
|
||||
if (rec->hotspotId == hotspotId) return rec;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HotspotAnimData *Resources::getAnimation(uint16 animRecordId) {
|
||||
HotspotAnimList::iterator i;
|
||||
|
||||
for (i = _animData.begin(); i != _animData.end(); ++i) {
|
||||
HotspotAnimData *rec = (*i).get();
|
||||
if (rec->animRecordId == animRecordId) return rec;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int Resources::getAnimationIndex(HotspotAnimData *animData) {
|
||||
HotspotAnimList::iterator i;
|
||||
int index = 0;
|
||||
|
||||
for (i = _animData.begin(); i != _animData.end(); ++i, ++index) {
|
||||
HotspotAnimData *rec = (*i).get();
|
||||
if (rec == animData)
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint16 Resources::getHotspotAction(uint16 actionsOffset, Action action) {
|
||||
HotspotActionList *list = _actionsList.getActions(actionsOffset);
|
||||
uint16 offset = (!list) ? 0 : list->getActionOffset(action);
|
||||
debugC(ERROR_DETAILED, kLureDebugHotspots,
|
||||
"Resources::getHotspotAction actionsOffset=%xh result=%xh", actionsOffset, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
TalkHeaderData *Resources::getTalkHeader(uint16 hotspotId) {
|
||||
TalkHeaderList::iterator i;
|
||||
for (i = _talkHeaders.begin(); i != _talkHeaders.end(); ++i) {
|
||||
TalkHeaderData *rec = (*i).get();
|
||||
if (rec->characterId == hotspotId) return rec;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HotspotActionList *Resources::getHotspotActions(uint16 actionsOffset) {
|
||||
return _actionsList.getActions(actionsOffset);
|
||||
}
|
||||
|
||||
void Resources::setTalkingCharacter(uint16 id) {
|
||||
Resources &res = Resources::getReference();
|
||||
|
||||
if (_talkingCharacter != 0) {
|
||||
deactivateHotspot(_talkingCharacter, true);
|
||||
HotspotData *charHotspot = res.getHotspot(_talkingCharacter);
|
||||
assert(charHotspot);
|
||||
charHotspot->talkDestCharacterId = 0;
|
||||
|
||||
if (_talkingCharacter != id)
|
||||
charHotspot->talkCountdown = 0;
|
||||
}
|
||||
|
||||
_talkingCharacter = id;
|
||||
|
||||
if (_talkingCharacter != 0) {
|
||||
Hotspot *character = getActiveHotspot(id);
|
||||
assert(character);
|
||||
|
||||
// Add the special "voice" animation above the character
|
||||
Hotspot *hotspot = new Hotspot(character, VOICE_ANIM_IDX);
|
||||
addHotspot(hotspot);
|
||||
}
|
||||
}
|
||||
uint16 englishLoadOffsets[] = {0x3afe, 0x41BD, 0x7167, 0x7172, 0x8617, 0x88ac, 0};
|
||||
|
||||
Hotspot *Resources::activateHotspot(uint16 hotspotId) {
|
||||
Resources &resources = Resources::getReference();
|
||||
HotspotData *res = getHotspot(hotspotId);
|
||||
if (!res) return nullptr;
|
||||
res->roomNumber &= 0x7fff; // clear any suppression bit in room #
|
||||
|
||||
// Make sure that the hotspot isn't already active
|
||||
Hotspot *h = getActiveHotspot(hotspotId);
|
||||
if (h != nullptr)
|
||||
return h;
|
||||
|
||||
// If it's NPC with a schedule, then activate the schedule
|
||||
if ((res->npcScheduleId != 0) && (res->npcSchedule.isEmpty())) {
|
||||
CharacterScheduleEntry *entry = resources.charSchedules().getEntry(res->npcScheduleId);
|
||||
res->npcSchedule.addFront(DISPATCH_ACTION, entry, res->roomNumber);
|
||||
}
|
||||
|
||||
// Check the script load flag
|
||||
if (res->scriptLoadFlag) {
|
||||
// Execute a script rather than doing a standard load
|
||||
Script::execute(res->loadOffset);
|
||||
} else {
|
||||
// Standard load
|
||||
bool loadFlag = true;
|
||||
uint16 talkIndex;
|
||||
|
||||
switch (res->loadOffset) {
|
||||
case 1:
|
||||
// Copy protection check - since the game is freeware now, ignore it
|
||||
loadFlag = false;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Empty handler used to prevent loading hotspots that
|
||||
// are yet to be active (such as the straw fire)
|
||||
loadFlag = false;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
// Standard animation load
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Custom loader used by the notice hotspot 42ah in room #20
|
||||
talkIndex = _fieldList.getField(TALK_INDEX);
|
||||
if ((talkIndex < 8) || (talkIndex >= 14))
|
||||
// Don't load hotspot
|
||||
loadFlag = false;
|
||||
else
|
||||
// Make the notice be on-screen
|
||||
res->startY = 85;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
// Torch in room #1
|
||||
loadFlag = _fieldList.getField(TORCH_HIDE) == 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
// All others simply activate the hotspot
|
||||
warning("Hotspot %d uses unknown load offset index %d",
|
||||
res->hotspotId, res->loadOffset);
|
||||
}
|
||||
|
||||
if (loadFlag) {
|
||||
Hotspot *hotspot = addHotspot(hotspotId);
|
||||
assert(hotspot);
|
||||
|
||||
// Special post-load handling
|
||||
if (res->loadOffset == 3) hotspot->setPersistent(true);
|
||||
if (res->loadOffset == 5) hotspot->handleTalkDialog();
|
||||
if (hotspotId == CASTLE_SKORL_ID) {
|
||||
// The Castle skorl has a default room #99, so it needs to be adjusted dynamically
|
||||
res->npcSchedule.clear();
|
||||
CharacterScheduleEntry *entry = resources.charSchedules().getEntry(res->npcScheduleId);
|
||||
res->npcSchedule.addFront(DISPATCH_ACTION, entry, res->roomNumber);
|
||||
}
|
||||
if ((hotspotId == GOEWIN_ID) && (hotspot->roomNumber() == 39)) {
|
||||
// WORKAROUND: When you re-join Goewin in the caves, clear her schedule. This may prevent a
|
||||
// situation where you could close the left door, and she'd be permanently stuck trying to go
|
||||
// the next room on the left, since her old schedule still had her following your old path
|
||||
hotspot->currentActions().clear();
|
||||
|
||||
// Since she's no longer a follower, clear her start room field
|
||||
hotspot->setStartRoomNumber(0);
|
||||
}
|
||||
|
||||
// TODO: Figure out why there's a room set in the animation decode for a range of characters,
|
||||
// particularly since it doesn't seem to match what happens in-game
|
||||
/*
|
||||
if ((hotspot->hotspotId() >= RATPOUCH_ID) &&
|
||||
(hotspot->hotspotId() < FIRST_NONCHARACTER_ID) &&
|
||||
(hotspot->roomNumber() < 42)) {
|
||||
// Start wandering characters off in room 24
|
||||
hotspot->setRoomNumber(24);
|
||||
hotspot->setPosition(64, 116);
|
||||
_fieldList.wanderingCharsLoaded() = true;
|
||||
}
|
||||
*/
|
||||
|
||||
return hotspot;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Hotspot *Resources::addHotspot(uint16 hotspotId) {
|
||||
HotspotData *hData = getHotspot(hotspotId);
|
||||
assert(hData);
|
||||
Hotspot *hotspot = new Hotspot(hData);
|
||||
_activeHotspots.push_back(HotspotList::value_type(hotspot));
|
||||
|
||||
if (hotspotId < FIRST_NONCHARACTER_ID) {
|
||||
// Default characters to facing upwards until they start moving
|
||||
hotspot->setDirection(UP);
|
||||
hotspot->setCharRectY(0);
|
||||
|
||||
// When reactivating an NPC, ensure that their previous state wasn't PROCESSING_PATH, since
|
||||
// the pause has destroyed the previously decided destination position
|
||||
if (!hData->npcSchedule.isEmpty() && (hData->npcSchedule.top().action() == PROCESSING_PATH))
|
||||
hData->npcSchedule.top().setAction(DISPATCH_ACTION);
|
||||
}
|
||||
|
||||
return hotspot;
|
||||
}
|
||||
|
||||
void Resources::addHotspot(Hotspot *hotspot) {
|
||||
_activeHotspots.push_back(HotspotList::value_type(hotspot));
|
||||
}
|
||||
|
||||
void Resources::deactivateHotspot(uint16 hotspotId, bool isDestId) {
|
||||
HotspotList::iterator i = _activeHotspots.begin();
|
||||
|
||||
while (i != _activeHotspots.end()) {
|
||||
Hotspot const &h = **i;
|
||||
if ((!isDestId && (h.hotspotId() == hotspotId)) ||
|
||||
(isDestId && (h.destHotspotId() == hotspotId) && (h.hotspotId() == 0xffff))) {
|
||||
_activeHotspots.erase(i);
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void Resources::deactivateHotspot(Hotspot *hotspot) {
|
||||
HotspotList::iterator i = _activeHotspots.begin();
|
||||
|
||||
while (i != _activeHotspots.end()) {
|
||||
Hotspot *h = (*i).get();
|
||||
if (h == hotspot) {
|
||||
_activeHotspots.erase(i);
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 Resources::numInventoryItems() {
|
||||
uint16 numItems = 0;
|
||||
HotspotDataList &list = _hotspotData;
|
||||
HotspotDataList::iterator i;
|
||||
for (i = list.begin(); i != list.end(); ++i) {
|
||||
if ((*i)->roomNumber == PLAYER_ID) ++numItems;
|
||||
}
|
||||
|
||||
return numItems;
|
||||
}
|
||||
|
||||
void Resources::copyCursorTo(Surface *s, uint8 cursorNum, int16 x, int16 y) {
|
||||
byte *pSrc = getCursor(cursorNum);
|
||||
byte *pDest = s->data().data() + (y * FULL_SCREEN_WIDTH) + x;
|
||||
|
||||
for (int yP = 0; yP < CURSOR_HEIGHT; ++yP) {
|
||||
for (int xP = 0; xP < CURSOR_WIDTH; ++xP) {
|
||||
if (*pSrc != 0) *pDest = *pSrc;
|
||||
++pSrc;
|
||||
++pDest;
|
||||
}
|
||||
pDest += FULL_SCREEN_WIDTH - CURSOR_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
void Resources::setTalkData(uint16 offset) {
|
||||
if (offset == 0) {
|
||||
_activeTalkData = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
TalkDataList::iterator i;
|
||||
for (i = _talkData.begin(); i != _talkData.end(); ++i) {
|
||||
TalkData *rec = (*i).get();
|
||||
if (rec->recordId == offset) {
|
||||
_activeTalkData = rec;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
error("Unknown talk entry offset %d requested", offset);
|
||||
}
|
||||
|
||||
void Resources::saveToStream(Common::WriteStream *stream) {
|
||||
// Save basic fields
|
||||
stream->writeUint16LE(_talkingCharacter);
|
||||
|
||||
// Save out the schedule for any non-active NPCs
|
||||
HotspotDataList::iterator i;
|
||||
for (i = _hotspotData.begin(); i != _hotspotData.end(); ++i) {
|
||||
HotspotData const &rec = **i;
|
||||
if (!rec.npcSchedule.isEmpty()) {
|
||||
Hotspot *h = getActiveHotspot(rec.hotspotId);
|
||||
if (h == nullptr) {
|
||||
stream->writeUint16LE(rec.hotspotId);
|
||||
rec.npcSchedule.saveToStream(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
stream->writeUint16LE(0xffff);
|
||||
|
||||
// Save sublist data
|
||||
_hotspotData.saveToStream(stream);
|
||||
_activeHotspots.saveToStream(stream);
|
||||
_fieldList.saveToStream(stream);
|
||||
_randomActions.saveToStream(stream);
|
||||
_barmanLists.saveToStream(stream);
|
||||
_exitJoins.saveToStream(stream);
|
||||
_roomData.saveToStream(stream);
|
||||
_delayList.saveToStream(stream);
|
||||
_talkData.saveToStream(stream);
|
||||
}
|
||||
|
||||
void Resources::loadFromStream(Common::ReadStream *stream) {
|
||||
uint8 saveVersion = LureEngine::getReference().saveVersion();
|
||||
|
||||
if (saveVersion >= 26) {
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading resource data");
|
||||
_talkingCharacter = stream->readUint16LE();
|
||||
} else {
|
||||
_talkingCharacter = 0;
|
||||
}
|
||||
|
||||
_talkState = TALK_NONE;
|
||||
_activeTalkData = nullptr;
|
||||
|
||||
if (saveVersion >= 31) {
|
||||
// Load in any schedules for non-active NPCS
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading NPC schedules");
|
||||
uint16 hotspotId;
|
||||
while ((hotspotId = stream->readUint16LE()) != 0xffff) {
|
||||
HotspotData *hotspot = getHotspot(hotspotId);
|
||||
assert(hotspot);
|
||||
hotspot->npcSchedule.loadFromStream(stream);
|
||||
}
|
||||
}
|
||||
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading hotspot data");
|
||||
_hotspotData.loadFromStream(stream);
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading active hotspots");
|
||||
_activeHotspots.loadFromStream(stream);
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading fields");
|
||||
_fieldList.loadFromStream(stream);
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading random actions");
|
||||
_randomActions.loadFromStream(stream);
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading barman lists");
|
||||
_barmanLists.loadFromStream(stream);
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading room exit joins");
|
||||
_exitJoins.loadFromStream(stream);
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading walkable paths");
|
||||
_roomData.loadFromStream(stream);
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading delay list");
|
||||
_delayList.loadFromStream(stream);
|
||||
|
||||
if (saveVersion >= 32) {
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Loading talk data");
|
||||
_talkData.loadFromStream(stream);
|
||||
}
|
||||
|
||||
debugC(ERROR_DETAILED, kLureDebugScripts, "Finished loading");
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
181
engines/lure/res.h
Normal file
181
engines/lure/res.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_RES_H
|
||||
#define LURE_RES_H
|
||||
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/memory.h"
|
||||
#include "lure/res_struct.h"
|
||||
#include "lure/hotspots.h"
|
||||
#include "lure/palette.h"
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/file.h"
|
||||
#include "common/list.h"
|
||||
#include "common/random.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
enum TalkState {TALK_NONE, TALK_START, TALK_SELECT, TALK_RESPOND, TALK_RESPONSE_WAIT,
|
||||
TALK_RESPOND_2, TALK_RESPOND_3};
|
||||
|
||||
#define MAX_TALK_SELECTIONS 4
|
||||
typedef TalkEntryData *TalkSelections[MAX_TALK_SELECTIONS];
|
||||
|
||||
struct TalkDialogDetails {
|
||||
Common::Rect bounds;
|
||||
bool active;
|
||||
};
|
||||
|
||||
#define NUM_GIVE_TALK_IDS 6
|
||||
|
||||
class Resources {
|
||||
private:
|
||||
Common::RandomSource &_rnd;
|
||||
Palette *_paletteSubset;
|
||||
MemoryBlock *_cursors;
|
||||
RoomDataList _roomData;
|
||||
HotspotDataList _hotspotData;
|
||||
HotspotOverrideList _hotspotOverrides;
|
||||
HotspotAnimList _animData;
|
||||
MemoryBlock *_scriptData;
|
||||
MemoryBlock *_script2Data;
|
||||
MemoryBlock *_messagesData;
|
||||
uint16 *_hotspotScriptData;
|
||||
RoomExitJoinList _exitJoins;
|
||||
HotspotList _activeHotspots;
|
||||
ValueTableData _fieldList;
|
||||
HotspotActionSet _actionsList;
|
||||
TalkHeaderList _talkHeaders;
|
||||
TalkDataList _talkData;
|
||||
Common::Array<uint16> _giveTalkIds;
|
||||
SequenceDelayList _delayList;
|
||||
Action _currentAction;
|
||||
MemoryBlock *_talkDialogData;
|
||||
RoomExitCoordinatesList _coordinateList;
|
||||
CharacterScheduleList _charSchedules;
|
||||
RandomActionList _randomActions;
|
||||
RoomExitIndexedHotspotList _indexedRoomExitHospots;
|
||||
PausedCharacterList _pausedList;
|
||||
BarmanLists _barmanLists;
|
||||
StringList _stringList;
|
||||
|
||||
int numCharOffsets;
|
||||
uint16 *_charOffsets;
|
||||
|
||||
TalkData *_activeTalkData;
|
||||
TalkState _talkState;
|
||||
TalkSelections _talkSelections;
|
||||
TalkDialogDetails _talkDetails;
|
||||
int _talkSelection;
|
||||
int _talkStartEntry;
|
||||
uint16 _talkingCharacter;
|
||||
byte _cursor[CURSOR_WIDTH * CURSOR_HEIGHT];
|
||||
|
||||
void reloadData();
|
||||
void freeData();
|
||||
public:
|
||||
Resources();
|
||||
~Resources();
|
||||
static Resources &getReference();
|
||||
void reset();
|
||||
|
||||
RoomDataList &roomData() { return _roomData; }
|
||||
RoomData *getRoom(uint16 roomNumber);
|
||||
bool checkHotspotExtent(HotspotData *hotspot);
|
||||
void insertPaletteSubset(Palette &p);
|
||||
byte *getCursor(uint8 cursorNum);
|
||||
HotspotDataList &hotspotData() { return _hotspotData; }
|
||||
HotspotOverrideList &hotspotOverrides() { return _hotspotOverrides; }
|
||||
HotspotAnimList &animRecords() { return _animData; }
|
||||
MemoryBlock *scriptData() { return _scriptData; }
|
||||
MemoryBlock *hotspotScriptData() { return _script2Data; }
|
||||
MemoryBlock *messagesData() { return _messagesData; }
|
||||
uint16 getHotspotScript(uint16 index);
|
||||
HotspotList &activeHotspots() { return _activeHotspots; }
|
||||
uint16 getRandom() { return _rnd.getRandomNumber(0xffff); }
|
||||
HotspotData *getHotspot(uint16 hotspotId);
|
||||
Hotspot *getActiveHotspot(uint16 hotspotId);
|
||||
HotspotOverrideData *getHotspotOverride(uint16 hotspotId);
|
||||
HotspotAnimData *getAnimation(uint16 animRecordId);
|
||||
int getAnimationIndex(HotspotAnimData *animData);
|
||||
RoomExitJoinList &exitJoins() { return _exitJoins; }
|
||||
RoomExitJoinData *getExitJoin(uint16 hotspotId);
|
||||
uint16 getHotspotAction(uint16 actionsOffset, Action action);
|
||||
HotspotActionList *getHotspotActions(uint16 actionsOffset);
|
||||
TalkHeaderData *getTalkHeader(uint16 hotspotId);
|
||||
ValueTableData &fieldList() { return _fieldList; }
|
||||
SequenceDelayList &delayList() { return _delayList; }
|
||||
MemoryBlock &getTalkDialogData() { return *_talkDialogData; }
|
||||
RoomExitCoordinatesList &coordinateList() { return _coordinateList; }
|
||||
CharacterScheduleList &charSchedules() { return _charSchedules; }
|
||||
RandomActionList &randomActions() { return _randomActions; }
|
||||
RoomExitIndexedHotspotList &exitHotspots() { return _indexedRoomExitHospots; }
|
||||
PausedCharacterList &pausedList() { return _pausedList; }
|
||||
BarmanLists &barmanLists() { return _barmanLists; }
|
||||
StringList &stringList() { return _stringList; }
|
||||
uint16 getCharOffset(int index) {
|
||||
if (index >= numCharOffsets)
|
||||
error("Invalid index %d passed to script engine support data offset list", index);
|
||||
if (index == 1)
|
||||
error("support data list index #1 was referenced - special handlng TODO");
|
||||
return _charOffsets[index];
|
||||
}
|
||||
void copyCursorTo(Surface *s, uint8 cursorNum, int16 x, int16 y);
|
||||
|
||||
uint16 numInventoryItems();
|
||||
void setTalkData(uint16 offset);
|
||||
TalkData *getTalkData() { return _activeTalkData; }
|
||||
void setTalkState(TalkState state) { _talkState = state; }
|
||||
TalkState getTalkState() { return _talkState; }
|
||||
TalkSelections &getTalkSelections() { return _talkSelections; }
|
||||
TalkDialogDetails &getTalkDetails() { return _talkDetails; }
|
||||
void setTalkSelection(int index) { _talkSelection = index; }
|
||||
int getTalkSelection() { return _talkSelection; }
|
||||
void setTalkStartEntry(int index) { _talkStartEntry = index; }
|
||||
int getTalkStartEntry() { return _talkStartEntry; }
|
||||
uint16 getTalkingCharacter() { return _talkingCharacter; }
|
||||
void setTalkingCharacter(uint16 index);
|
||||
uint16 getGiveTalkId(uint16 index) {
|
||||
if (index >= _giveTalkIds.size())
|
||||
error("Invalid give talk id specified");
|
||||
return _giveTalkIds[index];
|
||||
}
|
||||
void setCurrentAction(Action action) { _currentAction = action; }
|
||||
Action getCurrentAction() { return _currentAction; }
|
||||
const char *getCurrentActionStr() {
|
||||
if (_currentAction > EXAMINE)
|
||||
error("Invalid current action %d", _currentAction);
|
||||
return _stringList.getString(_currentAction);
|
||||
}
|
||||
Hotspot *activateHotspot(uint16 hotspotId);
|
||||
Hotspot *addHotspot(uint16 hotspotId);
|
||||
void addHotspot(Hotspot *hotspot);
|
||||
void deactivateHotspot(uint16 hotspotId, bool isDestId = false);
|
||||
void deactivateHotspot(Hotspot *hotspot);
|
||||
void saveToStream(Common::WriteStream *stream);
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
1485
engines/lure/res_struct.cpp
Normal file
1485
engines/lure/res_struct.cpp
Normal file
File diff suppressed because it is too large
Load Diff
929
engines/lure/res_struct.h
Normal file
929
engines/lure/res_struct.h
Normal file
@@ -0,0 +1,929 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_RESSTRUCT_H
|
||||
#define LURE_RESSTRUCT_H
|
||||
|
||||
#include "lure/luredefs.h"
|
||||
#include "common/rect.h"
|
||||
#include "common/list.h"
|
||||
#include "common/file.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/str-array.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
/* Structure definitions */
|
||||
/* */
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* HACK/FIXME: three structs are misaligned (at least on 4-byte aligned system,
|
||||
should have more troubles with coming 64bit systems), GET_NEXT let us read
|
||||
properly sequence of struct in lure.dat hardcoding size of struct.
|
||||
*/
|
||||
|
||||
#define GET_NEXT(v, sc) v = (sc *)(((byte *)v) + kSizeOf##sc)
|
||||
|
||||
#define kSizeOfRoomExitJoinResource 13
|
||||
#define kSizeOfHotspotResource 62
|
||||
#define kSizeOfHotspotActionResource 3
|
||||
|
||||
|
||||
#include "common/pack-start.h" // START STRUCT PACKING
|
||||
|
||||
struct VersionStructure {
|
||||
uint16 id;
|
||||
byte vMajor;
|
||||
byte vMinor;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct FileEntry {
|
||||
uint16 id;
|
||||
byte unused;
|
||||
byte sizeExtension;
|
||||
uint16 size;
|
||||
uint16 offset;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct HotspotResource {
|
||||
uint16 hotspotId;
|
||||
uint16 nameId;
|
||||
uint16 descId;
|
||||
uint16 descId2;
|
||||
uint32 actions;
|
||||
uint16 actionsOffset;
|
||||
uint16 roomNumber;
|
||||
byte layer;
|
||||
byte scriptLoadFlag;
|
||||
uint16 loadOffset;
|
||||
uint16 startX;
|
||||
uint16 startY;
|
||||
uint16 width;
|
||||
uint16 height;
|
||||
uint16 widthCopy;
|
||||
uint16 heightCopy;
|
||||
uint16 yCorrection;
|
||||
int16 walkX;
|
||||
uint16 walkY;
|
||||
int8 talkX;
|
||||
int8 talkY;
|
||||
uint16 colorOffset;
|
||||
uint16 animRecordId;
|
||||
uint16 hotspotScriptOffset;
|
||||
uint16 talkScriptOffset;
|
||||
uint16 tickProcId;
|
||||
uint16 tickTimeout;
|
||||
uint16 tickScriptOffset;
|
||||
uint16 npcSchedule;
|
||||
uint16 characterMode;
|
||||
uint16 delayCtr;
|
||||
byte flags2;
|
||||
byte hdrFlags;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct HotspotAnimResource {
|
||||
uint16 animRecordId;
|
||||
uint16 animId;
|
||||
uint16 flags;
|
||||
uint16 upOffset;
|
||||
uint16 downOffset;
|
||||
uint16 leftOffset;
|
||||
uint16 rightOffset;
|
||||
uint8 upFrame;
|
||||
uint8 downFrame;
|
||||
uint8 leftFrame;
|
||||
uint8 rightFrame;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct MovementResource {
|
||||
uint16 frameNumber;
|
||||
int16 xChange;
|
||||
int16 yChange;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct RoomRect {
|
||||
int16 xs, xe;
|
||||
int16 ys, ye;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct RoomResource {
|
||||
uint16 roomNumber;
|
||||
uint8 hdrFlags;
|
||||
uint8 unused;
|
||||
uint32 actions;
|
||||
uint16 descId;
|
||||
uint16 numLayers;
|
||||
uint16 layers[4];
|
||||
uint16 sequenceOffset;
|
||||
int16 clippingXStart;
|
||||
int16 clippingXEnd;
|
||||
uint8 areaFlag;
|
||||
uint8 numExits;
|
||||
uint32 exitTime;
|
||||
RoomRect walkBounds;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct RoomExitResource {
|
||||
int16 xs, xe, ys, ye;
|
||||
uint16 sequenceOffset;
|
||||
uint8 newRoom;
|
||||
uint8 direction;
|
||||
int16 newRoomX, newRoomY;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct HotspotOverrideResource {
|
||||
uint16 hotspotId;
|
||||
int16 xs, xe, ys, ye;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct RoomExitHotspotResource {
|
||||
uint16 hotspotId;
|
||||
int16 xs, xe;
|
||||
int16 ys, ye;
|
||||
uint16 cursorNum;
|
||||
uint16 destRoomNumber;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct RoomExitJoinResource {
|
||||
uint16 hotspot1Id;
|
||||
byte h1CurrentFrame;
|
||||
byte h1DestFrame;
|
||||
uint8 h1OpenSound;
|
||||
uint8 h1CloseSound;
|
||||
uint16 hotspot2Id;
|
||||
byte h2CurrentFrame;
|
||||
byte h2DestFrame;
|
||||
uint8 h2OpenSound;
|
||||
uint8 h2CloseSound;
|
||||
byte blocked;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct HotspotActionResource {
|
||||
byte action;
|
||||
uint16 sequenceOffset;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct TalkHeaderResource {
|
||||
uint16 hotspotId;
|
||||
uint16 offset;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct TalkDataHeaderResource {
|
||||
uint16 recordId;
|
||||
uint16 listOffset;
|
||||
uint16 responsesOffset;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct TalkDataResource {
|
||||
uint16 preSequenceId;
|
||||
uint16 descId;
|
||||
uint16 postSequenceId;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct TalkResponseResource {
|
||||
uint16 sequenceId1;
|
||||
uint16 sequenceId2;
|
||||
uint16 sequenceId3;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct RoomExitCoordinateResource {
|
||||
int16 x;
|
||||
int16 y;
|
||||
uint16 roomNumber;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
#define ROOM_EXIT_COORDINATES_NUM_ENTRIES 6
|
||||
#define ROOM_EXIT_COORDINATES_NUM_ROOMS 52
|
||||
|
||||
struct RoomExitCoordinateEntryResource {
|
||||
uint8 roomIndex[ROOM_EXIT_COORDINATES_NUM_ROOMS];
|
||||
RoomExitCoordinateResource entries[ROOM_EXIT_COORDINATES_NUM_ENTRIES];
|
||||
} PACKED_STRUCT;
|
||||
|
||||
#define MAX_SCHEDULE_ENTRY_PARAMS 5
|
||||
|
||||
struct CharacterScheduleResource {
|
||||
uint16 action;
|
||||
uint16 params[MAX_SCHEDULE_ENTRY_PARAMS];
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct RoomExitIndexedHotspotResource {
|
||||
uint8 roomNumber;
|
||||
uint8 hotspotIndex;
|
||||
uint16 hotspotId;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
enum SoundDescFlags {SF_IN_USE = 1, SF_RESTORE = 2};
|
||||
|
||||
// In desc entry, numChannels: bits 0-1 # roland, bits 2-3 #adlib, bits 4-5 #internal
|
||||
|
||||
struct SoundDescResource {
|
||||
uint8 soundNumber;
|
||||
uint8 channel;
|
||||
uint8 numChannels;
|
||||
uint8 flags;
|
||||
uint8 volume;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
#include "common/pack-end.h" // END STRUCT PACKING
|
||||
|
||||
/** Enumeration used for direction facings */
|
||||
enum Direction {UP, DOWN, LEFT, RIGHT, NO_DIRECTION};
|
||||
|
||||
// Support classes to hold loaded resources
|
||||
|
||||
class RoomExitHotspotData {
|
||||
public:
|
||||
RoomExitHotspotData(RoomExitHotspotResource *rec);
|
||||
|
||||
uint16 hotspotId;
|
||||
int16 xs, xe;
|
||||
int16 ys, ye;
|
||||
uint16 cursorNum;
|
||||
uint16 destRoomNumber;
|
||||
};
|
||||
|
||||
typedef Common::List<Common::SharedPtr<RoomExitHotspotData> > RoomExitHotspotList;
|
||||
|
||||
class RoomExitData {
|
||||
public:
|
||||
RoomExitData(RoomExitResource *rec);
|
||||
bool insideRect(int16 xp, int16 yp);
|
||||
|
||||
int16 xs, xe, ys, ye;
|
||||
uint16 sequenceOffset;
|
||||
Direction direction;
|
||||
uint8 roomNumber;
|
||||
uint16 x, y;
|
||||
};
|
||||
|
||||
class RoomExitList: public Common::List<Common::SharedPtr<RoomExitData> > {
|
||||
public:
|
||||
RoomExitData *checkExits(int16 xp, int16 yp);
|
||||
};
|
||||
|
||||
#define ROOM_PATHS_WIDTH 40
|
||||
#define ROOM_PATHS_HEIGHT 24
|
||||
#define ROOM_PATHS_SIZE (ROOM_PATHS_WIDTH / 8 * ROOM_PATHS_HEIGHT)
|
||||
#define DECODED_PATHS_WIDTH 42
|
||||
#define DECODED_PATHS_HEIGHT 26
|
||||
|
||||
typedef uint16 RoomPathsDecompressedData[DECODED_PATHS_WIDTH * DECODED_PATHS_HEIGHT];
|
||||
|
||||
class RoomPathsData {
|
||||
private:
|
||||
byte _data[ROOM_PATHS_HEIGHT * ROOM_PATHS_WIDTH];
|
||||
public:
|
||||
RoomPathsData() {}
|
||||
RoomPathsData(byte *srcData) { load(srcData); }
|
||||
|
||||
void load(byte *srcData) {
|
||||
memcpy(_data, srcData, ROOM_PATHS_SIZE);
|
||||
}
|
||||
const byte *data() const { return _data; }
|
||||
bool isOccupied(int x, int y);
|
||||
bool isOccupied(int x, int y, int width);
|
||||
void setOccupied(int x, int y, int width);
|
||||
void clearOccupied(int x, int y, int width);
|
||||
void decompress(RoomPathsDecompressedData &dataOut, int characterWidth);
|
||||
};
|
||||
|
||||
#define MAX_NUM_LAYERS 4
|
||||
|
||||
class RoomData {
|
||||
public:
|
||||
RoomData(RoomResource *rec, MemoryBlock *pathData);
|
||||
|
||||
uint16 roomNumber;
|
||||
uint8 hdrFlags;
|
||||
uint8 flags;
|
||||
uint32 actions;
|
||||
uint16 descId;
|
||||
uint16 numLayers;
|
||||
uint16 layers[MAX_NUM_LAYERS];
|
||||
uint16 sequenceOffset;
|
||||
int16 clippingXStart;
|
||||
int16 clippingXEnd;
|
||||
uint8 areaFlag;
|
||||
uint32 exitTime;
|
||||
Common::Rect walkBounds;
|
||||
RoomExitHotspotList exitHotspots;
|
||||
RoomExitList exits;
|
||||
RoomPathsData paths;
|
||||
};
|
||||
|
||||
class RoomDataList: public Common::List<Common::SharedPtr<RoomData> > {
|
||||
public:
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
struct RoomExitJoinStruct {
|
||||
uint16 hotspotId;
|
||||
byte currentFrame;
|
||||
byte destFrame;
|
||||
uint8 openSound;
|
||||
uint8 closeSound;
|
||||
};
|
||||
|
||||
class RoomExitJoinData {
|
||||
public:
|
||||
RoomExitJoinData(RoomExitJoinResource *rec);
|
||||
|
||||
RoomExitJoinStruct hotspots[2];
|
||||
|
||||
byte blocked;
|
||||
};
|
||||
|
||||
class RoomExitJoinList: public Common::List<Common::SharedPtr<RoomExitJoinData> > {
|
||||
public:
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
class HotspotActionData {
|
||||
public:
|
||||
HotspotActionData(HotspotActionResource *rec);
|
||||
|
||||
Action action;
|
||||
uint16 sequenceOffset;
|
||||
};
|
||||
|
||||
class HotspotActionList: public Common::List<Common::SharedPtr<HotspotActionData> > {
|
||||
public:
|
||||
uint16 recordId;
|
||||
|
||||
HotspotActionList(uint16 id, byte *data);
|
||||
uint16 getActionOffset(Action action);
|
||||
};
|
||||
|
||||
class HotspotActionSet: public Common::List<Common::SharedPtr<HotspotActionList> > {
|
||||
public:
|
||||
HotspotActionList *getActions(uint16 recordId);
|
||||
};
|
||||
|
||||
enum CharacterMode {CHARMODE_NONE, CHARMODE_HESITATE, CHARMODE_IDLE, CHARMODE_PAUSED,
|
||||
CHARMODE_WAIT_FOR_PLAYER, CHARMODE_CONVERSING, CHARMODE_PLAYER_WAIT,
|
||||
CHARMODE_WAIT_FOR_INTERACT, CHARMODE_INTERACTING, CHARMODE_SPECIAL_PLAYER};
|
||||
|
||||
enum BlockedState {BS_NONE, BS_INITIAL, BS_FINAL};
|
||||
|
||||
enum VariantBool {VB_INITIAL, VB_FALSE, VB_TRUE};
|
||||
|
||||
enum CurrentAction {NO_ACTION, START_WALKING, DISPATCH_ACTION, EXEC_HOTSPOT_SCRIPT,
|
||||
PROCESSING_PATH, WALKING};
|
||||
|
||||
class CharacterScheduleSet;
|
||||
|
||||
class CharacterScheduleEntry {
|
||||
private:
|
||||
CharacterScheduleSet *_parent;
|
||||
Action _action;
|
||||
uint16 _params[MAX_TELL_COMMANDS * 3];
|
||||
int _numParams;
|
||||
public:
|
||||
CharacterScheduleEntry() { _action = NONE; _parent = NULL; }
|
||||
CharacterScheduleEntry(int theAction, ...);
|
||||
CharacterScheduleEntry(CharacterScheduleSet *parentSet,
|
||||
CharacterScheduleResource *&rec);
|
||||
CharacterScheduleEntry(CharacterScheduleEntry *src);
|
||||
|
||||
Action action() { return _action; }
|
||||
int numParams() { return _numParams; }
|
||||
uint16 param(int index);
|
||||
void setDetails(int theAction, ...);
|
||||
void setDetails2(Action theAction, int numParamEntries, uint16 *paramList);
|
||||
CharacterScheduleEntry *next();
|
||||
CharacterScheduleSet *parent() { return _parent; }
|
||||
uint16 id();
|
||||
};
|
||||
|
||||
class CurrentActionEntry {
|
||||
private:
|
||||
CurrentAction _action;
|
||||
CharacterScheduleEntry *_supportData;
|
||||
uint16 _roomNumber;
|
||||
bool _dynamicSupportData;
|
||||
public:
|
||||
CurrentActionEntry(CurrentAction newAction, uint16 roomNum);
|
||||
CurrentActionEntry(CurrentAction newAction, CharacterScheduleEntry *data, uint16 roomNum);
|
||||
CurrentActionEntry(Action newAction, uint16 roomNum, uint16 param1, uint16 param2);
|
||||
CurrentActionEntry(CurrentActionEntry *src);
|
||||
virtual ~CurrentActionEntry() {
|
||||
if (_dynamicSupportData) delete _supportData;
|
||||
}
|
||||
|
||||
CurrentAction action() const { return _action; }
|
||||
CharacterScheduleEntry &supportData() const {
|
||||
if (!_supportData) error("Access made to non-defined action support record");
|
||||
return *_supportData;
|
||||
}
|
||||
bool hasSupportData() const { return _supportData != NULL; }
|
||||
uint16 roomNumber() const { return _roomNumber; }
|
||||
void setAction(CurrentAction newAction) { _action = newAction; }
|
||||
void setRoomNumber(uint16 roomNum) { _roomNumber = roomNum; }
|
||||
void setSupportData(CharacterScheduleEntry *newRec) {
|
||||
assert((newRec == NULL) || (newRec->parent() != NULL));
|
||||
if (_dynamicSupportData) {
|
||||
delete _supportData;
|
||||
_dynamicSupportData = false;
|
||||
}
|
||||
_supportData = newRec;
|
||||
}
|
||||
void setSupportData(uint16 entryId);
|
||||
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
static CurrentActionEntry *loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
class CurrentActionStack {
|
||||
private:
|
||||
typedef Common::List<Common::SharedPtr<CurrentActionEntry> > ActionsList;
|
||||
ActionsList _actions;
|
||||
void validateStack() {
|
||||
if (_actions.size() > 20)
|
||||
error("NPC character got an excessive number of pending actions");
|
||||
}
|
||||
public:
|
||||
CurrentActionStack() { _actions.clear(); }
|
||||
|
||||
bool isEmpty() const { return _actions.begin() == _actions.end(); }
|
||||
void clear() { _actions.clear(); }
|
||||
CurrentActionEntry &top() { return **_actions.begin(); }
|
||||
CurrentActionEntry &bottom() {
|
||||
ActionsList::iterator i = _actions.end();
|
||||
--i;
|
||||
return **i;
|
||||
}
|
||||
CurrentAction action() { return isEmpty() ? NO_ACTION : top().action(); }
|
||||
void pop() { _actions.erase(_actions.begin()); }
|
||||
int size() const { return _actions.size(); }
|
||||
Common::String getDebugInfo() const;
|
||||
|
||||
void addBack(CurrentAction newAction, uint16 roomNum) {
|
||||
_actions.push_back(ActionsList::value_type(new CurrentActionEntry(newAction, roomNum)));
|
||||
validateStack();
|
||||
}
|
||||
void addBack(CurrentAction newAction, CharacterScheduleEntry *rec, uint16 roomNum) {
|
||||
_actions.push_back(ActionsList::value_type(new CurrentActionEntry(newAction, rec, roomNum)));
|
||||
validateStack();
|
||||
}
|
||||
void addBack(Action newAction, uint16 roomNum, uint16 param1, uint16 param2) {
|
||||
_actions.push_back(ActionsList::value_type(new CurrentActionEntry(newAction, roomNum, param1, param2)));
|
||||
validateStack();
|
||||
}
|
||||
void addFront(CurrentAction newAction, uint16 roomNum) {
|
||||
_actions.push_front(ActionsList::value_type(new CurrentActionEntry(newAction, roomNum)));
|
||||
validateStack();
|
||||
}
|
||||
void addFront(CurrentAction newAction, CharacterScheduleEntry *rec, uint16 roomNum) {
|
||||
_actions.push_front(ActionsList::value_type(new CurrentActionEntry(newAction, rec, roomNum)));
|
||||
validateStack();
|
||||
}
|
||||
void addFront(Action newAction, uint16 roomNum, uint16 param1, uint16 param2) {
|
||||
_actions.push_front(ActionsList::value_type(new CurrentActionEntry(newAction, roomNum, param1, param2)));
|
||||
validateStack();
|
||||
}
|
||||
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
void copyFrom(CurrentActionStack &stack);
|
||||
};
|
||||
|
||||
class HotspotData {
|
||||
public:
|
||||
CurrentActionStack npcSchedule;
|
||||
HotspotData(HotspotResource *rec);
|
||||
|
||||
uint16 hotspotId;
|
||||
uint16 nameId;
|
||||
uint16 descId;
|
||||
uint16 descId2;
|
||||
uint32 actions;
|
||||
uint16 actionsOffset;
|
||||
byte flags;
|
||||
uint16 roomNumber;
|
||||
byte layer;
|
||||
byte scriptLoadFlag;
|
||||
uint16 loadOffset;
|
||||
int16 startX;
|
||||
int16 startY;
|
||||
uint16 width;
|
||||
uint16 height;
|
||||
uint16 widthCopy;
|
||||
uint16 heightCopy;
|
||||
uint16 yCorrection;
|
||||
int16 walkX;
|
||||
uint16 walkY;
|
||||
int8 talkX;
|
||||
int8 talkY;
|
||||
uint16 colorOffset;
|
||||
uint16 animRecordId;
|
||||
uint16 hotspotScriptOffset;
|
||||
uint16 talkScriptOffset;
|
||||
uint16 tickProcId;
|
||||
uint16 tickTimeout;
|
||||
uint16 tickScriptOffset;
|
||||
CharacterMode characterMode;
|
||||
uint16 delayCtr;
|
||||
uint8 flags2;
|
||||
uint8 headerFlags;
|
||||
uint16 npcScheduleId;
|
||||
|
||||
// Runtime fields
|
||||
uint16 actionCtr;
|
||||
BlockedState blockedState;
|
||||
bool blockedFlag;
|
||||
VariantBool coveredFlag;
|
||||
uint16 talkMessageId;
|
||||
uint16 talkerId;
|
||||
uint16 talkDestCharacterId;
|
||||
uint16 talkCountdown;
|
||||
uint16 pauseCtr;
|
||||
uint16 useHotspotId;
|
||||
uint16 talkGate;
|
||||
uint16 actionHotspotId;
|
||||
uint16 talkOverride;
|
||||
uint16 scriptHotspotId;
|
||||
|
||||
void enable() { flags |= 0x80; }
|
||||
void disable() { flags &= 0x7F; }
|
||||
Direction nonVisualDirection() { return (Direction) scriptLoadFlag; }
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
class HotspotDataList: public Common::List<Common::SharedPtr<HotspotData> > {
|
||||
public:
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
class HotspotOverrideData {
|
||||
public:
|
||||
HotspotOverrideData(HotspotOverrideResource *rec);
|
||||
|
||||
uint16 hotspotId;
|
||||
int16 xs, xe, ys, ye;
|
||||
};
|
||||
|
||||
typedef Common::List<Common::SharedPtr<HotspotOverrideData> > HotspotOverrideList;
|
||||
|
||||
class MovementData {
|
||||
public:
|
||||
MovementData(MovementResource *);
|
||||
|
||||
uint16 frameNumber;
|
||||
int16 xChange;
|
||||
int16 yChange;
|
||||
};
|
||||
|
||||
class MovementDataList: public Common::List<Common::SharedPtr<MovementData> > {
|
||||
public:
|
||||
bool getFrame(uint16 currentFrame, int16 &xChange, int16 &yChange,
|
||||
uint16 &nextFrame);
|
||||
};
|
||||
|
||||
class HotspotAnimData {
|
||||
public:
|
||||
HotspotAnimData(HotspotAnimResource *rec);
|
||||
|
||||
uint16 animRecordId;
|
||||
uint16 animId;
|
||||
uint16 flags;
|
||||
uint8 upFrame;
|
||||
uint8 downFrame;
|
||||
uint8 leftFrame;
|
||||
uint8 rightFrame;
|
||||
|
||||
MovementDataList leftFrames, rightFrames;
|
||||
MovementDataList upFrames, downFrames;
|
||||
};
|
||||
|
||||
typedef Common::List<Common::SharedPtr<HotspotAnimData> > HotspotAnimList;
|
||||
|
||||
// Talk header list
|
||||
|
||||
class TalkHeaderData {
|
||||
private:
|
||||
uint16 *_data;
|
||||
int _numEntries;
|
||||
public:
|
||||
TalkHeaderData(uint16 charId, uint16 *entries);
|
||||
~TalkHeaderData();
|
||||
|
||||
uint16 characterId;
|
||||
uint16 getEntry(int index);
|
||||
};
|
||||
|
||||
typedef Common::List<Common::SharedPtr<TalkHeaderData> > TalkHeaderList;
|
||||
|
||||
class TalkEntryData {
|
||||
public:
|
||||
TalkEntryData(TalkDataResource *rec);
|
||||
|
||||
uint16 preSequenceId;
|
||||
uint16 descId;
|
||||
uint16 postSequenceId;
|
||||
};
|
||||
|
||||
typedef Common::List<Common::SharedPtr<TalkEntryData> > TalkEntryList;
|
||||
|
||||
class TalkData {
|
||||
public:
|
||||
TalkData(uint16 id);
|
||||
~TalkData();
|
||||
|
||||
uint16 recordId;
|
||||
TalkEntryList entries;
|
||||
TalkEntryList responses;
|
||||
|
||||
TalkEntryData *getResponse(int index);
|
||||
};
|
||||
|
||||
class TalkDataList: public Common::List<Common::SharedPtr<TalkData> > {
|
||||
public:
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
struct RoomExitCoordinateData {
|
||||
int16 x;
|
||||
int16 y;
|
||||
uint16 roomNumber;
|
||||
byte hotspotIndexId;
|
||||
};
|
||||
|
||||
class RoomExitCoordinates {
|
||||
private:
|
||||
RoomExitCoordinateData _entries[ROOM_EXIT_COORDINATES_NUM_ENTRIES];
|
||||
uint8 _roomIndex[ROOM_EXIT_COORDINATES_NUM_ROOMS];
|
||||
public:
|
||||
RoomExitCoordinates(RoomExitCoordinateEntryResource *rec);
|
||||
RoomExitCoordinateData &getData(uint16 destRoomNumber);
|
||||
};
|
||||
|
||||
class RoomExitCoordinatesList: public Common::List<Common::SharedPtr<RoomExitCoordinates> > {
|
||||
public:
|
||||
RoomExitCoordinates &getEntry(uint16 roomNumber);
|
||||
};
|
||||
|
||||
class RoomExitIndexedHotspotData {
|
||||
public:
|
||||
RoomExitIndexedHotspotData(RoomExitIndexedHotspotResource *rec);
|
||||
|
||||
uint16 roomNumber;
|
||||
uint8 hotspotIndex;
|
||||
uint16 hotspotId;
|
||||
};
|
||||
|
||||
class RoomExitIndexedHotspotList: public Common::List<Common::SharedPtr<RoomExitIndexedHotspotData> > {
|
||||
public:
|
||||
uint16 getHotspot(uint16 roomNumber, uint8 hotspotIndexId);
|
||||
};
|
||||
|
||||
// The following classes hold any sequence offsets that are being delayed
|
||||
|
||||
class SequenceDelayData {
|
||||
private:
|
||||
SequenceDelayData() {}
|
||||
public:
|
||||
SequenceDelayData(uint16 delay, uint16 seqOffset, bool canClearFlag);
|
||||
static SequenceDelayData *load(uint32 delay, uint16 seqOffset, bool canClearFlag);
|
||||
|
||||
uint32 timeoutCtr;
|
||||
uint16 sequenceOffset;
|
||||
bool canClear;
|
||||
};
|
||||
|
||||
class SequenceDelayList: public Common::List<Common::SharedPtr<SequenceDelayData> > {
|
||||
public:
|
||||
void add(uint16 delay, uint16 seqOffset, bool canClear);
|
||||
void tick();
|
||||
void clear(bool forceClear = false);
|
||||
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
// The following classes holds the data for NPC schedules
|
||||
|
||||
extern const int actionNumParams[NPC_JUMP_ADDRESS+1];
|
||||
|
||||
class CharacterScheduleSet: public Common::List<Common::SharedPtr<CharacterScheduleEntry> > {
|
||||
private:
|
||||
uint16 _id;
|
||||
public:
|
||||
CharacterScheduleSet(CharacterScheduleResource *rec, uint16 setId);
|
||||
uint16 getId(CharacterScheduleEntry *rec);
|
||||
uint16 id() { return _id; }
|
||||
};
|
||||
|
||||
class CharacterScheduleList: public Common::List<Common::SharedPtr<CharacterScheduleSet> > {
|
||||
public:
|
||||
CharacterScheduleEntry *getEntry(uint16 id, CharacterScheduleSet *currentSet = NULL);
|
||||
};
|
||||
|
||||
typedef Common::List<uint16> CharacterScheduleOffsets;
|
||||
|
||||
// The follow classes are used to store the NPC schedule Ids for the random actions a follower can do in each room
|
||||
|
||||
enum RandomActionType {REPEATABLE, REPEAT_ONCE, REPEAT_ONCE_DONE};
|
||||
|
||||
class RandomActionSet {
|
||||
private:
|
||||
uint16 _roomNumber;
|
||||
int _numActions;
|
||||
RandomActionType *_types;
|
||||
uint16 *_ids;
|
||||
public:
|
||||
RandomActionSet(uint16 *&offset);
|
||||
~RandomActionSet();
|
||||
|
||||
uint16 roomNumber() const { return _roomNumber; }
|
||||
int numActions() const { return _numActions; }
|
||||
void getEntry(int index, RandomActionType &actionType, uint16 &id) {
|
||||
assert((index >= 0) && (index < _numActions));
|
||||
actionType = _types[index];
|
||||
id = _ids[index];
|
||||
}
|
||||
void setDone(int index) {
|
||||
assert((index >= 0) && (index < _numActions));
|
||||
assert(_types[index] == REPEAT_ONCE);
|
||||
_types[index] = REPEAT_ONCE_DONE;
|
||||
}
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
class RandomActionList: public Common::List<Common::SharedPtr<RandomActionSet> > {
|
||||
public:
|
||||
RandomActionSet *getRoom(uint16 roomNumber);
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
class PausedCharacter {
|
||||
public:
|
||||
PausedCharacter(uint16 SrcCharId, uint16 DestCharId);
|
||||
|
||||
uint16 srcCharId;
|
||||
uint16 destCharId;
|
||||
uint16 counter;
|
||||
HotspotData *charHotspot;
|
||||
};
|
||||
|
||||
class Hotspot;
|
||||
|
||||
class PausedCharacterList: public Common::List<Common::SharedPtr<PausedCharacter> > {
|
||||
public:
|
||||
void reset(uint16 hotspotId);
|
||||
void countdown();
|
||||
void scan(Hotspot &h);
|
||||
int check(uint16 charId, int numImpinging, uint16 *impingingList);
|
||||
};
|
||||
|
||||
struct ServeEntry {
|
||||
uint16 hotspotId;
|
||||
uint8 serveFlags;
|
||||
};
|
||||
|
||||
#define NUM_SERVE_CUSTOMERS 4
|
||||
|
||||
enum BarmanGraphicType {BG_RANDOM = 0, BG_BEER = 1, BG_EXTRA1 = 2, BG_EXTRA2 = 3};
|
||||
|
||||
struct BarEntry {
|
||||
uint16 roomNumber;
|
||||
uint16 barmanId;
|
||||
ServeEntry customers[NUM_SERVE_CUSTOMERS];
|
||||
const uint16 *graphics[4];
|
||||
uint16 gridLine;
|
||||
ServeEntry *currentCustomer;
|
||||
};
|
||||
|
||||
class BarmanLists {
|
||||
BarEntry _barList[3];
|
||||
public:
|
||||
BarmanLists();
|
||||
|
||||
void reset();
|
||||
BarEntry &getDetails(uint16 roomNumber);
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
enum BarmanAction {WALK_AROUND = 1, POLISH_BAR = 2, WAIT = 3, WAIT_DIALOG = 4, SERVE_BEER = 5};
|
||||
|
||||
struct RoomTranslationRecord {
|
||||
uint8 srcRoom;
|
||||
uint8 destRoom;
|
||||
};
|
||||
|
||||
extern const RoomTranslationRecord roomTranslations[];
|
||||
|
||||
enum StringEnum {S_CREDITS = 25, S_RESTART_GAME = 26, S_SAVE_GAME = 27, S_RESTORE_GAME = 28,
|
||||
S_QUIT = 29, S_FAST_TEXT = 30, S_SLOW_TEXT = 31, S_SOUND_ON = 32, S_SOUND_OFF = 33,
|
||||
S_ACTION_NOTHING = 34, S_FOR = 35, S_TO = 36, S_ON = 37, S_AND_THEN = 38, S_FINISH = 39,
|
||||
S_CONFIRM_YN = 40, S_YOU_ARE_CARRYING = 41, S_INV_NOTHING = 42, S_YOU_HAVE = 43,
|
||||
S_GROAT = 44, S_GROATS = 45,
|
||||
S_ARTICLE_LIST = 46};
|
||||
|
||||
class StringList {
|
||||
private:
|
||||
Common::StringArray _entries;
|
||||
public:
|
||||
StringList() {}
|
||||
|
||||
void load(MemoryBlock *data);
|
||||
void clear();
|
||||
int count() { return _entries.size(); }
|
||||
const char *getString(int index) {
|
||||
return _entries[index].c_str();
|
||||
}
|
||||
const char *getString(Action action) { return getString((int)action - 1); }
|
||||
const char *getString(StringEnum sEnum) { return getString((int)sEnum); }
|
||||
void setString(Action action, const Common::String &s) { _entries[(int)action - 1] = s; }
|
||||
};
|
||||
|
||||
// The following class holds the field list used by the script engine as
|
||||
// well as miscellaneous fields used by the game.
|
||||
|
||||
#define NUM_VALUE_FIELDS 90
|
||||
|
||||
enum FieldName {
|
||||
ROOM_NUMBER = 0,
|
||||
CHARACTER_HOTSPOT_ID = 1,
|
||||
USE_HOTSPOT_ID = 2,
|
||||
ACTIVE_HOTSPOT_ID = 3,
|
||||
SEQUENCE_RESULT = 4,
|
||||
GENERAL = 5,
|
||||
GIVE_TALK_INDEX = 6,
|
||||
NEW_ROOM_NUMBER = 7,
|
||||
OLD_ROOM_NUMBER = 8,
|
||||
CELL_DOOR_STATE = 9,
|
||||
TORCH_HIDE = 10,
|
||||
PRISONER_DEAD = 15,
|
||||
BOTTLE_FILLED = 18,
|
||||
TALK_INDEX = 19,
|
||||
SACK_CUT = 20,
|
||||
ROOM_EXIT_ANIMATION = 76,
|
||||
AREA_FLAG = 82
|
||||
};
|
||||
|
||||
struct PlayerNewPosition {
|
||||
Common::Point position;
|
||||
uint16 roomNumber;
|
||||
};
|
||||
|
||||
class ValueTableData {
|
||||
private:
|
||||
uint16 _numGroats;
|
||||
PlayerNewPosition _playerNewPos;
|
||||
uint8 _textCtr1, _textCtr2; // originally 2 2-bit counters
|
||||
uint8 _hdrFlagMask;
|
||||
|
||||
uint16 _fieldList[NUM_VALUE_FIELDS];
|
||||
bool isKnownField(uint16 fieldIndex);
|
||||
public:
|
||||
ValueTableData();
|
||||
void reset();
|
||||
uint16 getField(uint16 fieldIndex);
|
||||
uint16 getField(FieldName fieldName);
|
||||
|
||||
void setField(uint16 fieldIndex, uint16 value);
|
||||
void setField(FieldName fieldName, uint16 value);
|
||||
int size() { return NUM_VALUE_FIELDS; }
|
||||
|
||||
uint16 &numGroats() { return _numGroats; }
|
||||
uint8 &textCtr1() { return _textCtr1; }
|
||||
uint8 &textCtr2() { return _textCtr2; }
|
||||
uint8 &hdrFlagMask() { return _hdrFlagMask; }
|
||||
PlayerNewPosition &playerNewPos() { return _playerNewPos; }
|
||||
|
||||
void saveToStream(Common::WriteStream *stream) const;
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
809
engines/lure/room.cpp
Normal file
809
engines/lure/room.cpp
Normal file
@@ -0,0 +1,809 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lure/room.h"
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/res.h"
|
||||
#include "lure/screen.h"
|
||||
#include "lure/game.h"
|
||||
#include "lure/lure.h"
|
||||
#include "lure/events.h"
|
||||
#include "lure/strings.h"
|
||||
#include "lure/scripts.h"
|
||||
#include "lure/sound.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
static Room *int_room;
|
||||
|
||||
RoomLayer::RoomLayer(uint16 screenId, bool backgroundLayer):
|
||||
Surface(FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT) {
|
||||
Disk &disk = Disk::getReference();
|
||||
byte *screenData = data().data();
|
||||
int cellY;
|
||||
int cellIndex = 0;
|
||||
|
||||
// Reset all the cells to unused
|
||||
Common::fill((uint8 *) _cells, (uint8 *) _cells + GRID_SIZE, 0xff);
|
||||
|
||||
// Load up the screen data
|
||||
MemoryBlock *rawData = disk.getEntry(screenId);
|
||||
loadScreen(rawData);
|
||||
|
||||
uint16 v = READ_BE_UINT16(rawData->data());
|
||||
bool is5Bit = (v & 0xfffe) == 0x140;
|
||||
delete rawData;
|
||||
|
||||
_paletteId = (screenId & 0xffe0) - 1;
|
||||
if (is5Bit) {
|
||||
uint16 roomNumber = Room::getReference().roomNumber();
|
||||
|
||||
if (roomNumber == 6)
|
||||
_paletteId = 0x45ff;
|
||||
else if (roomNumber == 49)
|
||||
_paletteId = 0xf1ff;
|
||||
else {
|
||||
_paletteId = 0x40ff;
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through each cell of the screen
|
||||
for (cellY = 0; cellY < NUM_VERT_RECTS; ++cellY) {
|
||||
for (int cellX = 0; cellX < NUM_HORIZ_RECTS; ++cellX) {
|
||||
bool hasPixels = false;
|
||||
|
||||
if (backgroundLayer) {
|
||||
hasPixels = true;
|
||||
} else {
|
||||
// Check the cell
|
||||
for (int yP = 0; yP < RECT_SIZE; ++yP) {
|
||||
if (hasPixels) break;
|
||||
byte *linePos = screenData + (cellY * RECT_SIZE + yP + MENUBAR_Y_SIZE)
|
||||
* FULL_SCREEN_WIDTH + (cellX * RECT_SIZE);
|
||||
|
||||
for (int xP = 0; xP < RECT_SIZE; ++xP) {
|
||||
hasPixels = *linePos++ != 0;
|
||||
if (hasPixels) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_cells[cellY + NUM_EDGE_RECTS][cellX + NUM_EDGE_RECTS] =
|
||||
hasPixels ? cellIndex++ : 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Room::Room(): _screen(Screen::getReference()) {
|
||||
int_room = this;
|
||||
|
||||
_roomData = nullptr;
|
||||
_talkDialog = nullptr;
|
||||
_hotspotId = 0;
|
||||
_hotspotName[0] = '\0';
|
||||
_statusLine[0] = '\0';
|
||||
for (int ctr = 0; ctr < MAX_NUM_LAYERS; ++ctr) _layers[ctr] = nullptr;
|
||||
_numLayers = 0;
|
||||
_showInfo = false;
|
||||
_isExit = false;
|
||||
_roomNumber = 0;
|
||||
_destRoomNumber = 0;
|
||||
_cursorState = CS_NONE;
|
||||
|
||||
//****DEBUG****
|
||||
memset(tempLayer, 0, DECODED_PATHS_WIDTH * DECODED_PATHS_HEIGHT * 2);
|
||||
}
|
||||
|
||||
Room::~Room() {
|
||||
for (int layerNum = 0; layerNum < _numLayers; ++layerNum)
|
||||
delete _layers[layerNum];
|
||||
|
||||
delete _talkDialog;
|
||||
int_room = nullptr;
|
||||
}
|
||||
|
||||
Room &Room::getReference() {
|
||||
return *int_room;
|
||||
}
|
||||
|
||||
// leaveRoom
|
||||
// Handles leaving the current room
|
||||
|
||||
void Room::leaveRoom() {
|
||||
Resources &r = Resources::getReference();
|
||||
|
||||
// Scan through the hotspot list and remove any uneeded entries
|
||||
|
||||
HotspotList &list = r.activeHotspots();
|
||||
HotspotList::iterator i = list.begin();
|
||||
while (i != list.end()) {
|
||||
Hotspot const &h = **i;
|
||||
if (!h.persistent()) {
|
||||
i = list.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Room::loadRoomHotspots() {
|
||||
Resources &r = Resources::getReference();
|
||||
HotspotDataList &list = r.hotspotData();
|
||||
|
||||
HotspotDataList::iterator i;
|
||||
for (i = list.begin(); i != list.end(); ++i) {
|
||||
HotspotData const &rec = **i;
|
||||
|
||||
if ((rec.hotspotId < 0x7530) && (rec.roomNumber == _roomNumber) &&
|
||||
(rec.layer != 0))
|
||||
r.activateHotspot(rec.hotspotId);
|
||||
}
|
||||
}
|
||||
|
||||
void Room::checkRoomHotspots() {
|
||||
uint16 rangeStart[4] = {0x408, 0x3e8, 0x7530, 0x2710};
|
||||
uint16 rangeEnd[4] = {0x270f, 0x407, 0xffff, 0x752f};
|
||||
|
||||
Mouse &m = Mouse::getReference();
|
||||
Resources &res = Resources::getReference();
|
||||
HotspotDataList &list = res.hotspotData();
|
||||
HotspotData *entry = nullptr;
|
||||
int16 currentX = m.x();
|
||||
int16 currentY = m.y();
|
||||
HotspotDataList::iterator i;
|
||||
|
||||
_destRoomNumber = 0;
|
||||
|
||||
// Loop for each range of hotspot Ids
|
||||
for (int ctr = 0; ctr < 4; ++ctr) {
|
||||
for (i = list.begin(); i != list.end(); ++i) {
|
||||
entry = (*i).get();
|
||||
if ((entry->hotspotId < rangeStart[ctr]) || (entry->hotspotId > rangeEnd[ctr]))
|
||||
// Hotspot outside range, so skip it
|
||||
continue;
|
||||
|
||||
bool skipFlag = (entry->roomNumber != _roomNumber);
|
||||
if (!skipFlag) {
|
||||
skipFlag = (((entry->flags & HOTSPOTFLAG_FOUND) == 0) &&
|
||||
((entry->flags & HOTSPOTFLAG_SKIP) != 0)) ||
|
||||
((entry->flags & HOTSPOTFLAG_MENU_EXCLUSION) != 0);
|
||||
}
|
||||
|
||||
if ((!skipFlag) && (entry->hotspotId < 0x409))
|
||||
// For character hotspots, validate they're in clipping range
|
||||
skipFlag = !res.checkHotspotExtent(entry);
|
||||
|
||||
if (!skipFlag && (entry->hotspotId >= 0x2710) && (entry->hotspotId <= 0x27ff)) {
|
||||
RoomExitJoinData *rec = res.getExitJoin(entry->hotspotId);
|
||||
if ((rec) && (!rec->blocked))
|
||||
// Hotspot is over a room exit, and it's not blocked, so don't
|
||||
// register it as an active hotspot
|
||||
skipFlag = true;
|
||||
}
|
||||
|
||||
if (!skipFlag) {
|
||||
// Check for a hotspot override
|
||||
HotspotOverrideData *hsEntry = res.getHotspotOverride(entry->hotspotId);
|
||||
|
||||
if (hsEntry) {
|
||||
// Check whether cursor is in override hotspot area
|
||||
if ((currentX >= hsEntry->xs) && (currentX <= hsEntry->xe) &&
|
||||
(currentY >= hsEntry->ys) && (currentY <= hsEntry->ye))
|
||||
// Found to be in hotspot entry
|
||||
break;
|
||||
} else {
|
||||
// Check whether cursor is in default hospot area
|
||||
if ((currentX >= entry->startX) && (currentY >= entry->startY) &&
|
||||
(currentX < entry->startX + entry->widthCopy) &&
|
||||
(currentY < entry->startY + entry->height))
|
||||
// Found hotspot entry
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i != list.end())
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == list.end()) {
|
||||
_hotspotId = 0;
|
||||
_hotspotNameId = 0;
|
||||
_hotspot = nullptr;
|
||||
} else {
|
||||
_hotspotNameId = entry->nameId;
|
||||
_hotspot = entry;
|
||||
_hotspotId = entry->hotspotId;
|
||||
_isExit = false;
|
||||
entry->flags |= HOTSPOTFLAG_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
CursorType Room::checkRoomExits() {
|
||||
Mouse &m = Mouse::getReference();
|
||||
Resources &res = Resources::getReference();
|
||||
_destRoomNumber = 0;
|
||||
|
||||
RoomExitHotspotList &exits = _roomData->exitHotspots;
|
||||
if (exits.empty()) return CURSOR_ARROW;
|
||||
RoomExitJoinData *join;
|
||||
bool skipFlag;
|
||||
|
||||
RoomExitHotspotList::iterator i;
|
||||
for (i = exits.begin(); i != exits.end(); ++i) {
|
||||
RoomExitHotspotData const &rec = **i;
|
||||
skipFlag = false;
|
||||
|
||||
if (rec.hotspotId != 0) {
|
||||
join = res.getExitJoin(rec.hotspotId);
|
||||
if ((join) && (join->blocked != 0))
|
||||
skipFlag = true;
|
||||
}
|
||||
|
||||
if (!skipFlag && (m.x() >= rec.xs) && (m.x() <= rec.xe) &&
|
||||
(m.y() >= rec.ys) && (m.y() <= rec.ye)) {
|
||||
// Cursor is within exit area
|
||||
CursorType cursorNum = (CursorType)rec.cursorNum;
|
||||
_destRoomNumber = rec.destRoomNumber;
|
||||
|
||||
// If it's a hotspotted exit, change arrow to the + arrow
|
||||
if (rec.hotspotId != 0) {
|
||||
_hotspotId = rec.hotspotId;
|
||||
_hotspot = res.getHotspot(_hotspotId);
|
||||
_hotspotNameId = _hotspot->nameId;
|
||||
_isExit = true;
|
||||
cursorNum = (CursorType)((int)cursorNum + 7);
|
||||
}
|
||||
|
||||
return cursorNum;
|
||||
}
|
||||
}
|
||||
|
||||
// No room exits found
|
||||
return CURSOR_ARROW;
|
||||
}
|
||||
|
||||
void Room::addAnimation(Hotspot &h) {
|
||||
Surface &s = _screen.screen();
|
||||
char buffer[10];
|
||||
h.copyTo(&s);
|
||||
|
||||
if (_showInfo) {
|
||||
int16 x = h.x();
|
||||
int16 y = h.y();
|
||||
if ((x >= 0) && (x < FULL_SCREEN_WIDTH) && (y >= 0) && (y < FULL_SCREEN_HEIGHT))
|
||||
Common::sprintf_s(buffer, "%xh", h.hotspotId());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Room::addLayers(Hotspot &h) {
|
||||
int16 hsX = h.x() + (NUM_EDGE_RECTS * RECT_SIZE);
|
||||
int16 hsY = h.y() + (NUM_EDGE_RECTS * RECT_SIZE) - MENUBAR_Y_SIZE;
|
||||
|
||||
int16 xStart = hsX / RECT_SIZE;
|
||||
int16 xEnd = (hsX + h.width()) / RECT_SIZE;
|
||||
int16 numX = xEnd - xStart + 1;
|
||||
int16 yStart = hsY / RECT_SIZE;
|
||||
int16 yEnd = (hsY + h.heightCopy() - 1) / RECT_SIZE;
|
||||
int16 numY = yEnd - yStart + 1;
|
||||
|
||||
if ((xStart < 0) || (yEnd < 0))
|
||||
return;
|
||||
|
||||
for (int16 xCtr = 0; xCtr < numX; ++xCtr, ++xStart) {
|
||||
int16 xs = xStart - NUM_EDGE_RECTS;
|
||||
if (xs < 0) continue;
|
||||
|
||||
// Check foreground layers for an occupied one
|
||||
|
||||
int layerNum = 1;
|
||||
while ((layerNum < 4) && (_layers[layerNum] != nullptr) &&
|
||||
(_layers[layerNum]->getCell(xStart, yEnd) == 0xff))
|
||||
++layerNum;
|
||||
if ((layerNum == 4) || (_layers[layerNum] == nullptr)) continue;
|
||||
|
||||
int16 ye = yEnd - NUM_EDGE_RECTS;
|
||||
for (int16 yCtr = 0; yCtr < numY; ++yCtr, --ye) {
|
||||
if (ye < 0) break;
|
||||
addCell(xs, ye, layerNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Room::addCell(int16 xp, int16 yp, int layerNum) {
|
||||
Surface &s = _screen.screen();
|
||||
|
||||
while ((layerNum < 4) && (_layers[layerNum] != nullptr) &&
|
||||
(_layers[layerNum]->getCell(xp + NUM_EDGE_RECTS, yp + NUM_EDGE_RECTS) >= 0xfe))
|
||||
++layerNum;
|
||||
if ((layerNum == 4) || (_layers[layerNum] == nullptr)) return;
|
||||
|
||||
RoomLayer *layer = _layers[layerNum];
|
||||
|
||||
int index = ((yp * RECT_SIZE + MENUBAR_Y_SIZE) * FULL_SCREEN_WIDTH) + (xp * RECT_SIZE);
|
||||
byte *srcPos = layer->data().data() + index;
|
||||
byte *destPos = s.data().data() + index;
|
||||
|
||||
for (int yCtr = 0; yCtr < RECT_SIZE; ++yCtr) {
|
||||
for (int xCtr = 0; xCtr < RECT_SIZE; ++xCtr, ++destPos) {
|
||||
byte pixel = *srcPos++;
|
||||
if (pixel) *destPos = pixel;
|
||||
}
|
||||
|
||||
// Move to start of next cell line
|
||||
srcPos += FULL_SCREEN_WIDTH - RECT_SIZE;
|
||||
destPos += FULL_SCREEN_WIDTH - RECT_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
void Room::blockMerge() {
|
||||
for (int layerNum1 = 0; layerNum1 < 3; ++layerNum1) {
|
||||
if (_layers[layerNum1] == nullptr) break;
|
||||
|
||||
for (int layerNum2 = layerNum1 + 1; layerNum2 < 4; ++layerNum2) {
|
||||
if (_layers[layerNum2] == nullptr) break;
|
||||
|
||||
for (int yp = 0; yp < NUM_VERT_RECTS; ++yp) {
|
||||
for (int xp = 0; xp < NUM_HORIZ_RECTS; ++xp) {
|
||||
if (_layers[layerNum1]->isOccupied(xp + NUM_EDGE_RECTS, yp + NUM_EDGE_RECTS) &&
|
||||
_layers[layerNum2]->isOccupied(xp + NUM_EDGE_RECTS, yp + NUM_EDGE_RECTS)) {
|
||||
// Copy the rect from the later layer onto the earlier layer
|
||||
int offset = (yp * RECT_SIZE + MENUBAR_Y_SIZE) * FULL_SCREEN_WIDTH + (xp * RECT_SIZE);
|
||||
byte *src = _layers[layerNum2]->data().data() + offset;
|
||||
byte *dest = _layers[layerNum1]->data().data() + offset;
|
||||
|
||||
for (int y = 0; y < RECT_SIZE; ++y) {
|
||||
for (int x = 0; x < RECT_SIZE; ++x, ++src, ++dest) {
|
||||
if (*src != 0) *dest = *src;
|
||||
}
|
||||
src += FULL_SCREEN_WIDTH - RECT_SIZE;
|
||||
dest += FULL_SCREEN_WIDTH - RECT_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Room::layersPostProcess() {
|
||||
for (int layerNum = 1; layerNum < 4; ++layerNum) {
|
||||
if (_layers[layerNum] == nullptr)
|
||||
continue;
|
||||
|
||||
// Layer optimisation
|
||||
for (int xp = NUM_EDGE_RECTS; xp < NUM_HORIZ_RECTS + NUM_EDGE_RECTS; ++xp) {
|
||||
bool priorFlag = false, nextFlag = false;
|
||||
|
||||
for (int yp = NUM_EDGE_RECTS; yp < NUM_VERT_RECTS + NUM_EDGE_RECTS; ++yp) {
|
||||
if (_layers[layerNum]->getCell(xp, yp) == 0xff) {
|
||||
priorFlag = false;
|
||||
nextFlag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (priorFlag && (_layers[layerNum]->getCell(xp - 1, yp) == 0xff))
|
||||
_layers[layerNum]->setCell(xp - 1, yp, 0xfe);
|
||||
if (nextFlag && (_layers[layerNum]->getCell(xp + 1, yp) == 0xff))
|
||||
_layers[layerNum]->setCell(xp + 1, yp, 0xfe);
|
||||
|
||||
priorFlag = _layers[layerNum]->getCell(xp - 1, yp) != 0xff;
|
||||
nextFlag = _layers[layerNum]->getCell(xp + 1, yp) != 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
// Layer extension of final row to off-screen edge rows below
|
||||
|
||||
for (int xp = NUM_EDGE_RECTS + NUM_HORIZ_RECTS - 1; xp >= NUM_EDGE_RECTS; --xp) {
|
||||
if (_layers[layerNum]->getCell(xp, NUM_EDGE_RECTS + NUM_VERT_RECTS - 1) != 0xff) {
|
||||
for (int yp = NUM_VERT_RECTS + NUM_EDGE_RECTS; yp < FULL_VERT_RECTS; ++yp)
|
||||
_layers[layerNum]->setCell(xp, yp, 0xfe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Room::update() {
|
||||
Surface &s = _screen.screen();
|
||||
Resources &res = Resources::getReference();
|
||||
HotspotList &hotspots = res.activeHotspots();
|
||||
byte white = LureEngine::getReference().isEGA() ? EGA_DIALOG_WHITE_COLOR : VGA_DIALOG_WHITE_COLOR;
|
||||
HotspotList::iterator i;
|
||||
|
||||
// Copy the background to the temporary screen surface
|
||||
_layers[0]->copyTo(&s);
|
||||
|
||||
// Handle first layer (layer 3)
|
||||
for (i = hotspots.begin(); i != hotspots.end(); ++i) {
|
||||
Hotspot &h = **i;
|
||||
|
||||
if ((h.roomNumber() == _roomNumber) && h.isActiveAnimation() && (h.layer() == 3)) {
|
||||
addAnimation(h);
|
||||
addLayers(h);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle second layer (layer 1) - do in order of Y axis
|
||||
Common::List<Hotspot *> tempList;
|
||||
Common::List<Hotspot *>::iterator iTemp;
|
||||
for (i = hotspots.begin(); i != hotspots.end(); ++i) {
|
||||
Hotspot *h = i->get();
|
||||
if ((h->layer() != 1) || (h->roomNumber() != _roomNumber) ||
|
||||
h->skipFlag() || !h->isActiveAnimation())
|
||||
continue;
|
||||
int16 endY = h->y() + h->heightCopy();
|
||||
|
||||
for (iTemp = tempList.begin(); iTemp != tempList.end(); ++iTemp) {
|
||||
Hotspot *hTemp = *iTemp;
|
||||
int16 tempY = hTemp->y() + hTemp->heightCopy();
|
||||
if (endY < tempY) break;
|
||||
}
|
||||
tempList.insert(iTemp, h);
|
||||
}
|
||||
for (iTemp = tempList.begin(); iTemp != tempList.end(); ++iTemp) {
|
||||
Hotspot &h = **iTemp;
|
||||
addAnimation(h);
|
||||
addLayers(h);
|
||||
}
|
||||
|
||||
// Handle third layer (layer 2)
|
||||
for (i = hotspots.begin(); i != hotspots.end(); ++i) {
|
||||
Hotspot &h = **i;
|
||||
|
||||
if ((h.roomNumber() == _roomNumber) && h.isActiveAnimation() && (h.layer() == 2)) {
|
||||
addAnimation(h);
|
||||
}
|
||||
}
|
||||
|
||||
// Show any active talk dialog
|
||||
if (_talkDialog) {
|
||||
// Make sure the character is still active and in the viewing room
|
||||
Hotspot *talkCharacter = res.getActiveHotspot(res.getTalkingCharacter());
|
||||
if ((talkCharacter != nullptr) && (talkCharacter->roomNumber() == _roomNumber))
|
||||
_talkDialog->copyTo(&s, _talkDialogX, _talkDialogY);
|
||||
}
|
||||
|
||||
// Handle showing the status line
|
||||
if (!*_statusLine) {
|
||||
// No current status action being display
|
||||
if (_hotspotId != 0)
|
||||
s.writeString(0, 0, _hotspotName, false);
|
||||
} else {
|
||||
// Word wrap (if necessary) the status line and display it
|
||||
Common::String statusLineCopy(_statusLine);
|
||||
char **lines;
|
||||
uint8 numLines;
|
||||
int16 yPos = 0;
|
||||
s.wordWrap(statusLineCopy.begin(), s.width(), lines, numLines);
|
||||
for (int lineNum = 0; lineNum < numLines; ++lineNum) {
|
||||
s.writeString(0, yPos, lines[lineNum], false, white);
|
||||
yPos += FONT_HEIGHT;
|
||||
}
|
||||
Memory::dealloc(lines);
|
||||
}
|
||||
|
||||
// Debug - if the bottle object is on layer 0FEh, then display it's surface
|
||||
Hotspot *displayHotspot = res.getActiveHotspot(BOTTLE_HOTSPOT_ID);
|
||||
if ((displayHotspot != nullptr) && (displayHotspot->layer() == 0xfe))
|
||||
displayHotspot->frames().copyTo(&s);
|
||||
|
||||
// If show information is turned on, show extra debugging information
|
||||
if (_showInfo) {
|
||||
char buffer[64];
|
||||
|
||||
// Temporary display of pathfinding data
|
||||
for (int yctr = 0; yctr < ROOM_PATHS_HEIGHT; ++yctr) {
|
||||
for (int xctr = 0; xctr < ROOM_PATHS_WIDTH; ++xctr) {
|
||||
/*
|
||||
if (_roomData->paths.isOccupied(xctr, yctr))
|
||||
s.fillRect(Rect(xctr * 8, yctr * 8 + 8, xctr * 8 + 7, yctr * 8 + 15), 255);
|
||||
*/
|
||||
uint16 v = tempLayer[(yctr + 1) * DECODED_PATHS_WIDTH + xctr + 1];
|
||||
if ((v != 0) && (v < 100)) {
|
||||
Common::sprintf_s(buffer, "%d", v % 10);
|
||||
s.writeString(xctr * 8, yctr * 8 + 8, buffer, true);
|
||||
// } else if (v == 0xffff) {
|
||||
} else if (_roomData->paths.isOccupied(xctr, yctr)) {
|
||||
s.fillRect(Common::Rect(xctr * 8, yctr * 8 + 8, xctr * 8 + 7, yctr * 8 + 15), 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mouse &m = Mouse::getReference();
|
||||
Common::sprintf_s(buffer, "Room %d Pos (%d,%d) @ (%d,%d)", _roomNumber, m.x(), m.y(),
|
||||
m.x() / RECT_SIZE, (m.y() - MENUBAR_Y_SIZE) / RECT_SIZE);
|
||||
s.writeString(FULL_SCREEN_WIDTH / 2, 0, buffer, false, white);
|
||||
}
|
||||
}
|
||||
|
||||
void Room::setRoomNumber(uint16 newRoomNumber, bool showOverlay) {
|
||||
Resources &res = Resources::getReference();
|
||||
Game &game = Game::getReference();
|
||||
Mouse &mouse = Mouse::getReference();
|
||||
bool isEGA = LureEngine::getReference().isEGA();
|
||||
|
||||
mouse.pushCursorNum(CURSOR_DISK);
|
||||
|
||||
_roomData = res.getRoom(newRoomNumber);
|
||||
if (!_roomData)
|
||||
error("Tried to change to non-existent room: %d", newRoomNumber);
|
||||
|
||||
bool fadeFlag = (newRoomNumber != _roomNumber) && (_roomNumber != 0);
|
||||
bool leaveFlag = _roomNumber != 999;
|
||||
|
||||
_roomNumber = _roomData->roomNumber;
|
||||
_descId = _roomData->descId;
|
||||
|
||||
if (fadeFlag) {
|
||||
if (isEGA)
|
||||
_screen.setPaletteEmpty();
|
||||
else
|
||||
// Fade out all the colors except the high index 0FFh, which is used to show the
|
||||
// disk cursor as a room changes
|
||||
_screen.paletteFadeOut(GAME_COLORS - 1);
|
||||
|
||||
// Deallocate graphical layers from the room
|
||||
for (int layerNum = 0; layerNum < _numLayers; ++layerNum) {
|
||||
if (_layers[layerNum]) {
|
||||
delete _layers[layerNum];
|
||||
_layers[layerNum] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (leaveFlag) {
|
||||
leaveRoom();
|
||||
Sound.removeSounds();
|
||||
}
|
||||
}
|
||||
|
||||
_screen.empty();
|
||||
_screen.setPaletteEmpty(RES_PALETTE_ENTRIES);
|
||||
|
||||
_numLayers = _roomData->numLayers;
|
||||
if (showOverlay) ++_numLayers;
|
||||
|
||||
for (uint8 layerNum = 0; layerNum < _numLayers; ++layerNum)
|
||||
_layers[layerNum] = new RoomLayer(_roomData->layers[layerNum],
|
||||
layerNum == 0);
|
||||
|
||||
blockMerge();
|
||||
layersPostProcess();
|
||||
|
||||
// Generate the palette for the room that will be faded in
|
||||
Palette *p;
|
||||
if (isEGA) {
|
||||
p = new Palette(_layers[0]->paletteId());
|
||||
} else {
|
||||
p = new Palette(GAME_PALETTE_RESOURCE_ID);
|
||||
Palette tempPalette(_layers[0]->paletteId());
|
||||
p->copyFrom(&tempPalette);
|
||||
res.insertPaletteSubset(*p);
|
||||
}
|
||||
|
||||
// Set the new room number
|
||||
res.fieldList().setField(ROOM_NUMBER, newRoomNumber);
|
||||
|
||||
if (_roomData->sequenceOffset != 0xffff)
|
||||
Script::execute(_roomData->sequenceOffset);
|
||||
|
||||
loadRoomHotspots();
|
||||
|
||||
if (leaveFlag) {
|
||||
// A previous room has been left - check if there are any seconds worth
|
||||
// of animations that need to be done in 'fast forward'
|
||||
if ((_roomData->exitTime != 0xffff) && (_roomData->exitTime != 0)) {
|
||||
// If time has passed, animation ticks needed before room is displayed
|
||||
int numSeconds = (g_system->getMillis() - _roomData->exitTime) / 1000;
|
||||
if (numSeconds > 300) numSeconds = 300;
|
||||
|
||||
game.preloadFlag() = true;
|
||||
while (numSeconds-- > 0)
|
||||
game.tick();
|
||||
game.preloadFlag() = false;
|
||||
}
|
||||
}
|
||||
|
||||
game.tick();
|
||||
update();
|
||||
_screen.update();
|
||||
|
||||
if (fadeFlag && !isEGA)
|
||||
_screen.paletteFadeIn(p);
|
||||
else
|
||||
_screen.setPalette(p);
|
||||
|
||||
mouse.popCursor();
|
||||
delete p;
|
||||
}
|
||||
|
||||
// checkCursor
|
||||
// Called repeatedly to check if any changes to the cursor are required
|
||||
|
||||
void Room::checkCursor() {
|
||||
Mouse &mouse = Mouse::getReference();
|
||||
Resources &res = Resources::getReference();
|
||||
uint16 oldHotspotId = _hotspotId;
|
||||
CursorType currentCursor = mouse.getCursorNum();
|
||||
CursorType newCursor = currentCursor;
|
||||
CurrentAction playerAction = res.getActiveHotspot(PLAYER_ID)->currentActions().action();
|
||||
uint16 oldRoomNumber = res.fieldList().getField(OLD_ROOM_NUMBER);
|
||||
|
||||
if ((currentCursor >= CURSOR_TIME_START) && (currentCursor <= CURSOR_TIME_END) &&
|
||||
((playerAction == START_WALKING) || (playerAction == PROCESSING_PATH))) {
|
||||
// Animate the clock when processing the player path
|
||||
newCursor = (CursorType)((int)newCursor + 1);
|
||||
if (newCursor == CURSOR_CROSS) newCursor = CURSOR_TIME_START;
|
||||
} else if (checkInTalkDialog() && (oldRoomNumber == 0)) {
|
||||
newCursor = CURSOR_TALK;
|
||||
} else if (res.getTalkData()) {
|
||||
newCursor = CURSOR_ARROW;
|
||||
} else if (_cursorState == CS_BUMPED) {
|
||||
newCursor = CURSOR_CAMERA;
|
||||
} else if (_cursorState == CS_TALKING) {
|
||||
newCursor = CURSOR_ARROW;
|
||||
} else if (mouse.y() < MENUBAR_Y_SIZE) {
|
||||
// If viewing a room remotely, then don't change to the menu cursor
|
||||
if (oldRoomNumber != 0) return;
|
||||
|
||||
newCursor = CURSOR_MENUBAR;
|
||||
} else if (_cursorState != CS_NONE) {
|
||||
// Currently in a special mode
|
||||
checkRoomHotspots();
|
||||
newCursor = CURSOR_CAMERA;
|
||||
} else {
|
||||
// Check for a highlighted hotspot
|
||||
checkRoomHotspots();
|
||||
|
||||
if (_hotspotId != 0) {
|
||||
newCursor = CURSOR_CROSS;
|
||||
} else {
|
||||
newCursor = checkRoomExits();
|
||||
}
|
||||
|
||||
if (oldHotspotId != _hotspotId)
|
||||
StringData::getReference().getString(_hotspotNameId, _hotspotName);
|
||||
}
|
||||
|
||||
if (mouse.getCursorNum() != newCursor)
|
||||
mouse.setCursorNum(newCursor);
|
||||
}
|
||||
|
||||
void Room::setTalkDialog(uint16 srcCharacterId, uint16 destCharacterId, uint16 usedId, uint16 stringId) {
|
||||
Resources &res = Resources::getReference();
|
||||
debugC(ERROR_DETAILED, kLureDebugAnimations, "Room::setTalkDialog - char=%xh string=%d",
|
||||
srcCharacterId, stringId);
|
||||
|
||||
if (_talkDialog) {
|
||||
delete _talkDialog;
|
||||
_talkDialog = nullptr;
|
||||
}
|
||||
/*
|
||||
if (res.getTalkingCharacter() != 0) {
|
||||
// Signal to any talked to character that they're no longer being talked to
|
||||
HotspotData *talkingChar = res.getHotspot(res.getTalkingCharacter());
|
||||
if ((talkingChar->talkDestCharacterId != 0) &&
|
||||
(talkingChar->talkDestCharacterId != NOONE_ID)) {
|
||||
HotspotData *destChar = res.getHotspot(talkingChar->talkDestCharacterId);
|
||||
destChar->talkerId = 0;
|
||||
}
|
||||
}
|
||||
*/
|
||||
res.setTalkingCharacter(srcCharacterId);
|
||||
|
||||
if (srcCharacterId == 0)
|
||||
return;
|
||||
|
||||
HotspotData *character = res.getHotspot(srcCharacterId);
|
||||
if (character->roomNumber != _roomNumber)
|
||||
return;
|
||||
|
||||
_talkDialog = new TalkDialog(srcCharacterId, destCharacterId, usedId, stringId);
|
||||
_talkDialogX = character->startX + (character->width / 2) - (TALK_DIALOG_WIDTH / 2);
|
||||
|
||||
if (_talkDialogX < 0) _talkDialogX = 0;
|
||||
if (_talkDialogX + TALK_DIALOG_WIDTH >= FULL_SCREEN_WIDTH - 10)
|
||||
_talkDialogX = FULL_SCREEN_WIDTH - 10 - TALK_DIALOG_WIDTH;
|
||||
|
||||
_talkDialogY = TALK_DIALOG_Y;
|
||||
debugC(ERROR_DETAILED, kLureDebugAnimations, "Room::setTalkDialog end");
|
||||
}
|
||||
|
||||
// Checks to see if a talk dialog is active, and if so if the mouse is in
|
||||
// within it
|
||||
|
||||
bool Room::checkInTalkDialog() {
|
||||
// Make sure there is a talk dialog active
|
||||
if (!_talkDialog) return false;
|
||||
|
||||
// Don't allow dialog close if it's still in progress
|
||||
if (_talkDialog->isBuilding()) return false;
|
||||
|
||||
// Only allow the dialog to be closable if it's the player talking, or
|
||||
// someone talking to the player
|
||||
Resources &res = Resources::getReference();
|
||||
uint16 talkerId = res.getTalkingCharacter();
|
||||
if ((talkerId == NOONE_ID) || (talkerId == 0))
|
||||
return false;
|
||||
|
||||
if (talkerId != PLAYER_ID) {
|
||||
HotspotData *charHotspot = res.getHotspot(talkerId);
|
||||
assert(charHotspot);
|
||||
if (charHotspot->talkDestCharacterId != PLAYER_ID)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check boundaries
|
||||
Mouse &mouse = Mouse::getReference();
|
||||
return ((mouse.x() >= _talkDialogX) && (mouse.y() >= _talkDialogY) &&
|
||||
(mouse.x() < _talkDialogX + _talkDialog->surface().width()) &&
|
||||
(mouse.y() < _talkDialogY + _talkDialog->surface().height()));
|
||||
}
|
||||
|
||||
void Room::saveToStream(Common::WriteStream *stream) {
|
||||
if (_talkDialog == nullptr)
|
||||
stream->writeUint16LE(0);
|
||||
else
|
||||
_talkDialog->saveToStream(stream);
|
||||
|
||||
stream->writeUint16LE(_roomNumber);
|
||||
stream->writeUint16LE(_destRoomNumber);
|
||||
stream->writeByte(_showInfo);
|
||||
stream->writeUint16LE(_cursorState);
|
||||
}
|
||||
|
||||
void Room::loadFromStream(Common::ReadStream *stream) {
|
||||
uint8 saveVersion = LureEngine::getReference().saveVersion();
|
||||
|
||||
if (_talkDialog) {
|
||||
delete _talkDialog;
|
||||
_talkDialog = nullptr;
|
||||
}
|
||||
|
||||
if (saveVersion >= 26)
|
||||
_talkDialog = TalkDialog::loadFromStream(stream);
|
||||
|
||||
// Clear any active hotspot
|
||||
_hotspotId = 0;
|
||||
_hotspotName[0] = '\0';
|
||||
_statusLine[0] = '\0';
|
||||
|
||||
uint16 roomNum = stream->readUint16LE();
|
||||
_roomNumber = 999; // Dummy room number so current room is faded out
|
||||
setRoomNumber(roomNum, false);
|
||||
|
||||
_destRoomNumber = stream->readUint16LE();
|
||||
_showInfo = stream->readByte() != 0;
|
||||
_cursorState = (CursorState) stream->readUint16LE();
|
||||
}
|
||||
|
||||
void Room::reset() {
|
||||
_roomNumber = 999;
|
||||
setTalkDialog(0, 0, 0, 0);
|
||||
|
||||
_hotspotId = 0;
|
||||
_hotspotName[0] = '\0';
|
||||
_statusLine[0] = '\0';
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
133
engines/lure/room.h
Normal file
133
engines/lure/room.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_ROOM_H
|
||||
#define LURE_ROOM_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "lure/disk.h"
|
||||
#include "lure/res.h"
|
||||
#include "lure/memory.h"
|
||||
#include "lure/surface.h"
|
||||
#include "lure/screen.h"
|
||||
#include "lure/hotspots.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
#define RECT_SIZE 32
|
||||
#define NUM_HORIZ_RECTS 10
|
||||
#define NUM_VERT_RECTS 6
|
||||
#define FULL_HORIZ_RECTS 18
|
||||
#define FULL_VERT_RECTS 14
|
||||
#define NUM_EDGE_RECTS 4
|
||||
#define GRID_SIZE (FULL_VERT_RECTS * FULL_HORIZ_RECTS)
|
||||
|
||||
class RoomLayer: public Surface {
|
||||
private:
|
||||
byte _cells[FULL_VERT_RECTS][FULL_HORIZ_RECTS];
|
||||
uint16 _paletteId;
|
||||
public:
|
||||
RoomLayer(uint16 screenId, bool backgroundLayer);
|
||||
bool isOccupied(byte cellX, byte cellY) {
|
||||
return _cells[cellY][cellX] < 0xfe;
|
||||
}
|
||||
uint8 getCell(byte cellX, byte cellY) {
|
||||
return _cells[cellY][cellX];
|
||||
}
|
||||
void setCell(byte cellX, byte cellY, byte value) {
|
||||
_cells[cellY][cellX] = value;
|
||||
}
|
||||
uint16 paletteId() { return _paletteId; }
|
||||
};
|
||||
|
||||
enum CursorState {CS_NONE, CS_ACTION, CS_SEQUENCE, CS_TALKING, CS_BUMPED};
|
||||
|
||||
class Room {
|
||||
private:
|
||||
RoomData *_roomData;
|
||||
Screen &_screen;
|
||||
uint16 _roomNumber;
|
||||
uint16 _descId;
|
||||
uint16 _hotspotId;
|
||||
uint16 _hotspotNameId;
|
||||
uint16 _destRoomNumber;
|
||||
bool _isExit;
|
||||
char _hotspotName[MAX_HOTSPOT_NAME_SIZE + MAX_ACTION_NAME_SIZE];
|
||||
char _statusLine[MAX_DESC_SIZE];
|
||||
HotspotData *_hotspot;
|
||||
bool _showInfo;
|
||||
uint8 _numLayers;
|
||||
RoomLayer *_layers[MAX_NUM_LAYERS];
|
||||
TalkDialog *_talkDialog;
|
||||
int16 _talkDialogX, _talkDialogY;
|
||||
CursorState _cursorState;
|
||||
|
||||
void checkRoomHotspots();
|
||||
CursorType checkRoomExits();
|
||||
void loadRoomHotspots();
|
||||
void addAnimation(Hotspot &h);
|
||||
void addLayers(Hotspot &h);
|
||||
void addCell(int16 xp, int16 yp, int layerNum);
|
||||
void blockMerge();
|
||||
void layersPostProcess();
|
||||
public:
|
||||
RoomPathsDecompressedData tempLayer;
|
||||
Room();
|
||||
~Room();
|
||||
static Room &getReference();
|
||||
|
||||
void update();
|
||||
void nextFrame();
|
||||
void checkCursor();
|
||||
uint16 roomNumber() { return _roomNumber; }
|
||||
void setRoomNumber(uint16 newRoomNumber, bool showOverlay = false);
|
||||
void leaveRoom();
|
||||
uint8 numLayers() { return _numLayers; }
|
||||
uint16 hotspotId() { return _hotspotId; }
|
||||
uint16 destRoomNumber() { return _destRoomNumber; }
|
||||
uint16 isExit() { return _isExit; }
|
||||
uint32 hotspotActions() { return _hotspot->actions & 0x10ffffff; }
|
||||
uint8 hotspotFlags() { return (_hotspot->actions >> 24) & 0xfe; }
|
||||
HotspotData &hotspot() { return *_hotspot; }
|
||||
uint16 descId() { return _descId; }
|
||||
bool showInfo() { return _showInfo; }
|
||||
CursorState cursorState() { return _cursorState; }
|
||||
void setShowInfo(bool value) { _showInfo = value; }
|
||||
void setTalkDialog(uint16 srcCharacterId, uint16 destCharacterId, uint16 usedId, uint16 stringId);
|
||||
TalkDialog *talkDialog() { return _talkDialog; }
|
||||
void setCursorState(CursorState state) { _cursorState = state; }
|
||||
bool isDialogActive() { return _talkDialog != NULL; }
|
||||
bool isDialogShowing() {
|
||||
Resources &res = Resources::getReference();
|
||||
Hotspot *talkCharacter = res.getActiveHotspot(res.getTalkingCharacter());
|
||||
return isDialogActive() && (talkCharacter != NULL) && (talkCharacter->roomNumber() == _roomNumber);
|
||||
}
|
||||
bool checkInTalkDialog();
|
||||
char *statusLine() { return _statusLine; }
|
||||
void saveToStream(Common::WriteStream *stream);
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
void reset();
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
191
engines/lure/screen.cpp
Normal file
191
engines/lure/screen.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lure/screen.h"
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/memory.h"
|
||||
#include "lure/disk.h"
|
||||
#include "lure/decode.h"
|
||||
#include "lure/events.h"
|
||||
|
||||
#include "graphics/paletteman.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
static Screen *int_disk = nullptr;
|
||||
|
||||
Screen &Screen::getReference() {
|
||||
return *int_disk;
|
||||
}
|
||||
|
||||
Screen::Screen(OSystem &system): _system(system),
|
||||
_screen(new Surface(FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT)),
|
||||
_disk(Disk::getReference()),
|
||||
_palette(new Palette(GAME_PALETTE_RESOURCE_ID, RGB64)) {
|
||||
int_disk = this;
|
||||
_screen->empty();
|
||||
_system.getPaletteManager()->setPalette(_palette->data(), 0, GAME_COLORS);
|
||||
}
|
||||
|
||||
Screen::~Screen() {
|
||||
delete _screen;
|
||||
delete _palette;
|
||||
}
|
||||
|
||||
void Screen::setSystemPalette(Palette *p, uint16 start, uint16 num) {
|
||||
byte pal[3 * 256];
|
||||
assert(start + num <= 256);
|
||||
|
||||
const byte *rawData = p->data();
|
||||
for (uint i = 0; i < num; ++i) {
|
||||
pal[i * 3 + 0] = rawData[(i + start) * 4 + 0];
|
||||
pal[i * 3 + 1] = rawData[(i + start) * 4 + 1];
|
||||
pal[i * 3 + 2] = rawData[(i + start) * 4 + 2];
|
||||
}
|
||||
|
||||
_system.getPaletteManager()->setPalette(pal, start, num);
|
||||
}
|
||||
|
||||
// setPaletteEmpty
|
||||
// Defaults the palette to an empty set
|
||||
|
||||
void Screen::setPaletteEmpty(int numEntries) {
|
||||
Palette emptyPalette(numEntries, nullptr, RGB64);
|
||||
setSystemPalette(&emptyPalette, 0, numEntries);
|
||||
_palette->copyFrom(&emptyPalette);
|
||||
/*
|
||||
delete _palette;
|
||||
_palette = new Palette();
|
||||
|
||||
_system.getPaletteManager()->setPalette(_palette->data(), 0, numEntries);
|
||||
*/
|
||||
_system.updateScreen();
|
||||
}
|
||||
|
||||
// setPalette
|
||||
// Sets the current palette to the passed palette
|
||||
|
||||
void Screen::setPalette(Palette *p) {
|
||||
_palette->copyFrom(p);
|
||||
setSystemPalette(_palette, 0, GAME_COLORS);
|
||||
_system.updateScreen();
|
||||
}
|
||||
|
||||
// setPalette
|
||||
// Variation that allows the specification of a subset of a palette passed in to be copied
|
||||
|
||||
void Screen::setPalette(Palette *p, uint16 start, uint16 num) {
|
||||
_palette->palette()->copyFrom(p->palette(), start * 4, start * 4, num * 4);
|
||||
setSystemPalette(_palette, start, num);
|
||||
_system.updateScreen();
|
||||
}
|
||||
|
||||
// paletteFadeIn
|
||||
// Fades in the palette. For proper operation, the palette should have been
|
||||
// previously set to empty
|
||||
|
||||
void Screen::paletteFadeIn(Palette *p) {
|
||||
assert(p->numEntries() <= _palette->numEntries());
|
||||
Events &events = Events::getReference();
|
||||
bool changed;
|
||||
|
||||
do {
|
||||
changed = false;
|
||||
byte *pFinal = p->data();
|
||||
byte *pCurrent = _palette->data();
|
||||
|
||||
for (int palCtr = 0; palCtr < p->numEntries() * PALETTE_FADE_INC_SIZE; ++palCtr, ++pCurrent, ++pFinal) {
|
||||
if (palCtr % PALETTE_FADE_INC_SIZE == (PALETTE_FADE_INC_SIZE - 1)) continue;
|
||||
bool isDifferent = *pCurrent < *pFinal;
|
||||
|
||||
if (isDifferent) {
|
||||
if ((*pFinal - *pCurrent) < PALETTE_FADE_INC_SIZE)
|
||||
*pCurrent = *pFinal;
|
||||
else
|
||||
*pCurrent += PALETTE_FADE_INC_SIZE;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
setSystemPalette(_palette, 0, p->numEntries());
|
||||
_system.updateScreen();
|
||||
_system.delayMillis(20);
|
||||
while (events.pollEvent())
|
||||
;
|
||||
}
|
||||
} while (changed);
|
||||
}
|
||||
|
||||
// paletteFadeOut
|
||||
// Fades the screen to black by gradually decreasing the palette colors
|
||||
|
||||
void Screen::paletteFadeOut(int numEntries) {
|
||||
assert((uint32)numEntries <= _palette->palette()->size());
|
||||
Events &events = Events::getReference();
|
||||
bool changed;
|
||||
|
||||
do {
|
||||
byte *pTemp = _palette->data();
|
||||
changed = false;
|
||||
|
||||
for (uint32 palCtr = 0; palCtr < (uint32)(numEntries * PALETTE_FADE_INC_SIZE); ++palCtr, ++pTemp) {
|
||||
if (palCtr % PALETTE_FADE_INC_SIZE == (PALETTE_FADE_INC_SIZE - 1))
|
||||
continue;
|
||||
bool isDifferent = *pTemp > 0;
|
||||
if (isDifferent) {
|
||||
if (*pTemp < PALETTE_FADE_INC_SIZE) *pTemp = 0;
|
||||
else *pTemp -= PALETTE_FADE_INC_SIZE;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
setSystemPalette(_palette, 0, numEntries);
|
||||
_system.updateScreen();
|
||||
_system.delayMillis(20);
|
||||
while (events.pollEvent())
|
||||
;
|
||||
}
|
||||
} while (changed);
|
||||
}
|
||||
|
||||
void Screen::resetPalette() {
|
||||
Palette p(GAME_PALETTE_RESOURCE_ID);
|
||||
setPalette(&p);
|
||||
}
|
||||
|
||||
void Screen::empty() {
|
||||
_screen->empty();
|
||||
update();
|
||||
}
|
||||
|
||||
void Screen::update() {
|
||||
_system.copyRectToScreen(screen_raw(), FULL_SCREEN_WIDTH, 0, 0, FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
|
||||
_system.updateScreen();
|
||||
}
|
||||
|
||||
void Screen::updateArea(uint16 x, uint16 y, uint16 w, uint16 h) {
|
||||
_system.copyRectToScreen(screen_raw(), FULL_SCREEN_WIDTH, x, y, w, h);
|
||||
_system.updateScreen();
|
||||
}
|
||||
|
||||
} // End of namespace Lure
|
||||
66
engines/lure/screen.h
Normal file
66
engines/lure/screen.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_SCREEN_H
|
||||
#define LURE_SCREEN_H
|
||||
|
||||
|
||||
#include "engines/engine.h"
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/palette.h"
|
||||
#include "lure/disk.h"
|
||||
#include "lure/memory.h"
|
||||
#include "lure/surface.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
class Screen {
|
||||
private:
|
||||
OSystem &_system;
|
||||
Disk &_disk;
|
||||
Surface *_screen;
|
||||
Palette *_palette;
|
||||
|
||||
void setSystemPalette(Palette *p, uint16 start, uint16 num);
|
||||
public:
|
||||
Screen(OSystem &system);
|
||||
~Screen();
|
||||
static Screen &getReference();
|
||||
|
||||
void setPaletteEmpty(int numEntries = RES_PALETTE_ENTRIES);
|
||||
void setPalette(Palette *p);
|
||||
void setPalette(Palette *p, uint16 start, uint16 num);
|
||||
Palette &getPalette() { return *_palette; }
|
||||
void paletteFadeIn(Palette *p);
|
||||
void paletteFadeOut(int numEntries = RES_PALETTE_ENTRIES);
|
||||
void resetPalette();
|
||||
void empty();
|
||||
void update();
|
||||
void updateArea(uint16 x, uint16 y, uint16 w, uint16 h);
|
||||
|
||||
Surface &screen() { return *_screen; }
|
||||
uint8 *screen_raw() { return _screen->data().data(); }
|
||||
uint8 *pixel_raw(uint16 x, uint16 y) { return screen_raw() + (y * FULL_SCREEN_WIDTH) + x; }
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
1361
engines/lure/scripts.cpp
Normal file
1361
engines/lure/scripts.cpp
Normal file
File diff suppressed because it is too large
Load Diff
154
engines/lure/scripts.h
Normal file
154
engines/lure/scripts.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_SCRIPTS_H
|
||||
#define LURE_SCRIPTS_H
|
||||
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/memory.h"
|
||||
#include "lure/hotspots.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
// Opcode list
|
||||
#define S_OPCODE_ABORT 0
|
||||
#define S_OPCODE_ADD 1
|
||||
#define S_OPCODE_SUBTRACT 2
|
||||
#define S_OPCODE_MULTIPLY 3
|
||||
#define S_OPCODE_DIVIDE 4
|
||||
#define S_OPCODE_EQUALS 5
|
||||
#define S_OPCODE_NOT_EQUALS 6
|
||||
#define S_OPCODE_LT 7
|
||||
#define S_OPCODE_GT 8
|
||||
#define S_OPCODE_LTE 9
|
||||
#define S_OPCODE_GTE 10
|
||||
#define S_OPCODE_AND 11
|
||||
#define S_OPCODE_OR 12
|
||||
#define S_OPCODE_LOGICAL_AND 13
|
||||
#define S_OPCODE_LOGICAL_OR 14
|
||||
#define S_OPCODE_GET_FIELD 15
|
||||
#define S_OPCODE_SET_FIELD 16
|
||||
#define S_OPCODE_PUSH 17
|
||||
#define S_OPCODE_SUBROUTINE 18
|
||||
#define S_OPCODE_EXEC 19
|
||||
#define S_OPCODE_END 20
|
||||
#define S_OPCODE_COND_JUMP 21
|
||||
#define S_OPCODE_JUMP 22
|
||||
#define S_OPCODE_ABORT2 23
|
||||
#define S_OPCODE_ABORT3 24
|
||||
#define S_OPCODE_RANDOM 25
|
||||
|
||||
#define S2_OPCODE_FRAME_CTR -1
|
||||
#define S2_OPCODE_POSITION -2
|
||||
#define S2_OPCODE_CHANGE_POS -3
|
||||
#define S2_OPCODE_UNLOAD -4
|
||||
#define S2_OPCODE_DIMENSIONS -5
|
||||
#define S2_OPCODE_JUMP -6
|
||||
#define S2_OPCODE_ANIMATION -7
|
||||
#define S2_OPCODE_PLAY_SOUND -8
|
||||
#define S2_OPCODE_STOP_SOUND -9
|
||||
#define S2_OPCODE_ACTIONS -10
|
||||
|
||||
|
||||
|
||||
class Script {
|
||||
public:
|
||||
static uint16 execute(uint16 startOffset);
|
||||
|
||||
static void executeMethod(int methodIndex, uint16 v1, uint16 v2, uint16 v3);
|
||||
static void activateHotspot(uint16 hotspotId, uint16 v2, uint16 v3);
|
||||
static void setHotspotScript(uint16 hotspotId, uint16 scriptIndex, uint16 v3);
|
||||
static void addSound2(uint16 soundIndex, uint16 v2, uint16 v3);
|
||||
static void setHotspotFlagMask(uint16 maskVal, uint16 v2, uint16 v3);
|
||||
static void clearSequenceDelayList(uint16 v1, uint16 scriptIndex, uint16 v3);
|
||||
static void deactivateHotspotSet(uint16 listIndex, uint16 v2, uint16 v3);
|
||||
static void deactivateHotspot(uint16 hotspotId, uint16 v2, uint16 v3);
|
||||
static void resetPathfinder(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void addDelayedSequence(uint16 seqOffset, uint16 delay, uint16 canClear);
|
||||
static void killSound(uint16 soundNumber, uint16 v2, uint16 v3);
|
||||
static void characterInRoom(uint16 characterId, uint16 roomNumber, uint16 v3);
|
||||
static void setDesc(uint16 hotspotId, uint16 descId, uint16 v3);
|
||||
static void setHotspotName(uint16 hotspotId, uint16 nameId, uint16 v3);
|
||||
static void addSound(uint16 soundIndex, uint16 v2, uint16 v3);
|
||||
static void endgameSequence(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void setupPigFight(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void displayDialog(uint16 stringId, uint16 v2, uint16 v3);
|
||||
static void setupSkorlFight(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void remoteRoomViewSetup(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void startSpeakingToNoone(uint16 characterId, uint16 stringId, uint16 v3);
|
||||
static void stopSound(uint16 soundIndex, uint16 v2, uint16 v3);
|
||||
static void getDoorBlocked(uint16 hotspotId, uint16 v2, uint16 v3);
|
||||
static void isSkorlInCell(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void ratpouchPushBricks(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void characterChangeRoom(uint16 y, uint16 x, uint16 roomNumber);
|
||||
static void pauseRatpouch(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void setBlockingHotspotScript(uint16 charId, uint16 scriptIndex, uint16 v3);
|
||||
static void decrInventoryItems(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void setTalking(uint16 characterId, uint16 destHotspot, uint16 messageId);
|
||||
static void setActionCtr(uint16 hotspotId, uint16 value, uint16 v3);
|
||||
static void startSpeaking(uint16 characterId, uint16 destHotspot, uint16 messageId);
|
||||
static void disableHotspot(uint16 hotspotId, uint16 v2, uint16 v3);
|
||||
static void cutSack(uint16 hotspotId, uint16 v2, uint16 v3);
|
||||
static void increaseNumGroats(uint16 characterId, uint16 numGroats, uint16 v3);
|
||||
static void enableHotspot(uint16 hotspotId, uint16 v2, uint16 v3);
|
||||
static void displayMessage2(uint16 messageId, uint16 hotspotId, uint16 v3);
|
||||
static void startOilBurner(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void transformPlayer(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void townHallClose(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void checkRoomNumber(uint16 hotspotId, uint16 roomNumber, uint16 v3);
|
||||
static void makeGoewinFollow(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void doorClose(uint16 hotspotId, uint16 v2, uint16 v3);
|
||||
static void fixGoewin(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void displayMessage(uint16 messageId, uint16 characterId, uint16 destCharacterId);
|
||||
static void doorOpen(uint16 hotspotId, uint16 v2, uint16 v3);
|
||||
static void npcWait(uint16 hotspotId, uint16 v2, uint16 v3);
|
||||
static void checkWakeBrenda(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void setNewSupportData(uint16 index, uint16 hotspotId, uint16 v3);
|
||||
static void setSupportData(uint16 hotspotId, uint16 index, uint16 v3);
|
||||
static void givePlayerItem(uint16 hotspotId, uint16 v2, uint16 v3);
|
||||
static void decreaseNumGroats(uint16 characterId, uint16 numGroats, uint16 v3);
|
||||
static void makeGoewinWork(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void moveCharacterToPlayer(uint16 characterId, uint16 v2, uint16 v3);
|
||||
static void setVillageSkorlTickProc(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void freeGoewin(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void barmanServe(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void getNumGroats(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void checkHasBook(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void enableGargoylesTalk(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void killPlayer(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void normalGoewin(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void animationLoad(uint16 hotspotId, uint16 v2, uint16 v3);
|
||||
static void addActions(uint16 hotspotId, uint16 actions, uint16 v3);
|
||||
static void randomToGeneral(uint16 maxVal, uint16 minVal, uint16 v3);
|
||||
static void checkCellDoor(uint16 v1, uint16 v2, uint16 v3);
|
||||
static void checkSound(uint16 soundNumber, uint16 v2, uint16 v3);
|
||||
};
|
||||
|
||||
class HotspotScript {
|
||||
private:
|
||||
static int16 nextVal(MemoryBlock *data, uint16 &offset);
|
||||
public:
|
||||
static bool execute(Hotspot *h);
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
1063
engines/lure/sound.cpp
Normal file
1063
engines/lure/sound.cpp
Normal file
File diff suppressed because it is too large
Load Diff
200
engines/lure/sound.h
Normal file
200
engines/lure/sound.h
Normal file
@@ -0,0 +1,200 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_SOUND_H
|
||||
#define LURE_SOUND_H
|
||||
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/disk.h"
|
||||
#include "lure/memory.h"
|
||||
|
||||
#include "common/mutex.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/ptr.h"
|
||||
|
||||
#include "audio/adlib_ms.h"
|
||||
#include "audio/mididrv.h"
|
||||
#include "audio/mt32gm.h"
|
||||
|
||||
class MidiParser;
|
||||
class MidiChannel;
|
||||
|
||||
namespace Lure {
|
||||
|
||||
#define NUM_CHANNELS 16
|
||||
#define LURE_MAX_SOURCES 10
|
||||
|
||||
class MidiMusic: public MidiDriver_BASE {
|
||||
private:
|
||||
uint8 _soundNumber;
|
||||
uint8 _numChannels;
|
||||
byte _volume;
|
||||
MemoryBlock *_decompressedSound;
|
||||
uint8 *_soundData;
|
||||
uint8 _soundSize;
|
||||
MidiDriver_Multisource *_driver;
|
||||
MidiDriver_MT32GM *_mt32Driver;
|
||||
int8 _source;
|
||||
MidiParser *_parser;
|
||||
bool _isMusic;
|
||||
bool _loop;
|
||||
bool _isPlaying;
|
||||
|
||||
void queueUpdatePos();
|
||||
uint8 randomQueuePos();
|
||||
uint32 songOffset(uint16 songNum) const;
|
||||
uint32 songLength(uint16 songNum) const;
|
||||
|
||||
public:
|
||||
MidiMusic(MidiDriver_Multisource *driver, uint8 soundNum, bool isMus, bool loop,
|
||||
int8 source, uint8 numChannels, void *soundData, uint32 size, uint8 volume);
|
||||
~MidiMusic() override;
|
||||
void setVolume(int volume);
|
||||
int getVolume() const { return _volume; }
|
||||
|
||||
void playSong(uint16 songNum);
|
||||
void stopSong() { stopMusic(); }
|
||||
void playMusic();
|
||||
void stopMusic();
|
||||
void pauseMusic();
|
||||
void resumeMusic();
|
||||
void queueTuneList(int16 tuneList);
|
||||
bool queueSong(uint16 songNum);
|
||||
void toggleVChange();
|
||||
|
||||
// MidiDriver_BASE interface implementation
|
||||
void send(uint32 b) override;
|
||||
void send(int8 source, uint32 b) override;
|
||||
void metaEvent(byte type, const byte *data, uint16 length) override;
|
||||
void metaEvent(int8 source, byte type, const byte *data, uint16 length) override;
|
||||
|
||||
void onTimer();
|
||||
|
||||
uint8 soundNumber() const { return _soundNumber; }
|
||||
int8 getSource() const { return _source; }
|
||||
bool isPlaying() const { return _isPlaying; }
|
||||
bool isMusic() const { return _isMusic; }
|
||||
};
|
||||
|
||||
class SoundManager : public Common::Singleton<SoundManager> {
|
||||
private:
|
||||
// Outer sound interface properties
|
||||
MemoryBlock *_descs;
|
||||
MemoryBlock *_soundData;
|
||||
uint8 _soundsTotal;
|
||||
int _numDescs;
|
||||
SoundDescResource *soundDescs() { return (SoundDescResource *) _descs->data(); }
|
||||
MidiDriver_Multisource *_driver;
|
||||
MidiDriver_MT32GM *_mt32Driver;
|
||||
typedef Common::List<Common::SharedPtr<SoundDescResource> > SoundList;
|
||||
typedef SoundList::iterator SoundListIterator;
|
||||
SoundList _activeSounds;
|
||||
typedef Common::List<Common::SharedPtr<MidiMusic> > MusicList;
|
||||
typedef MusicList::iterator MusicListIterator;
|
||||
MusicList _playingSounds;
|
||||
bool _sourcesInUse[LURE_MAX_SOURCES];
|
||||
bool _nativeMT32;
|
||||
bool _isRoland;
|
||||
Common::Mutex _soundMutex;
|
||||
bool _paused;
|
||||
|
||||
// Internal support methods
|
||||
void bellsBodge();
|
||||
void musicInterface_TidySounds();
|
||||
static void onTimer(void *data);
|
||||
void doTimer();
|
||||
|
||||
public:
|
||||
SoundManager();
|
||||
~SoundManager() override;
|
||||
|
||||
void saveToStream(Common::WriteStream *stream);
|
||||
void loadFromStream(Common::ReadStream *stream);
|
||||
|
||||
void loadSection(uint16 sectionId);
|
||||
bool initCustomTimbres(bool canAbort = false);
|
||||
void killSounds();
|
||||
void addSound(uint8 soundIndex, bool tidyFlag = true);
|
||||
void addSound2(uint8 soundIndex);
|
||||
void stopSound(uint8 soundIndex);
|
||||
void killSound(uint8 soundNumber);
|
||||
void setVolume(uint8 soundNumber, uint8 volume);
|
||||
void syncSounds();
|
||||
void tidySounds();
|
||||
uint8 descIndexOf(uint8 soundNumber);
|
||||
SoundDescResource *findSound(uint8 soundNumber);
|
||||
void removeSounds();
|
||||
void restoreSounds();
|
||||
bool fadeOut();
|
||||
void pause();
|
||||
void resume();
|
||||
bool getPaused() const { return _paused; }
|
||||
bool hasNativeMT32() const { return _nativeMT32; }
|
||||
bool isRoland() const { return _isRoland; }
|
||||
|
||||
// The following methods implement the external sound player module
|
||||
//void musicInterface_Initialize();
|
||||
void musicInterface_Play(uint8 soundNumber, bool isMusic = false, uint8 numChannels = 4, uint8 volume = 0x80);
|
||||
void musicInterface_Stop(uint8 soundNumber);
|
||||
bool musicInterface_CheckPlaying(uint8 soundNumber);
|
||||
void musicInterface_SetVolume(uint8 soundNumber, uint8 volume);
|
||||
void musicInterface_KillAll();
|
||||
void musicInterface_ContinuePlaying();
|
||||
void musicInterface_TrashReverb();
|
||||
};
|
||||
|
||||
// AdLib MidiDriver subclass implementing the behavior specific to Lure of the
|
||||
// Temptress.
|
||||
class MidiDriver_ADLIB_Lure : public MidiDriver_ADLIB_Multisource {
|
||||
protected:
|
||||
// Lookup array for OPL frequency (F-num) values.
|
||||
static const uint16 OPL_FREQUENCY_LOOKUP[];
|
||||
|
||||
public:
|
||||
MidiDriver_ADLIB_Lure();
|
||||
|
||||
// Channel aftertouch is used to adjust a note's volume. This is done by
|
||||
// overriding the velocity of the active note and recalculating the volume.
|
||||
void channelAftertouch(uint8 channel, uint8 pressure, uint8 source) override;
|
||||
// The MIDI data uses three custom sequencer meta events; the most important
|
||||
// one sets the instrument definition for a channel.
|
||||
void metaEvent(int8 source, byte type, const byte *data, uint16 length) override;
|
||||
|
||||
protected:
|
||||
InstrumentInfo determineInstrument(uint8 channel, uint8 source, uint8 note) override;
|
||||
uint16 calculateFrequency(uint8 channel, uint8 source, uint8 note) override;
|
||||
// Returns the number of semitones in bits 8+ and an 8 bit fraction of a
|
||||
// semitone.
|
||||
int32 calculatePitchBend(uint8 channel, uint8 source, uint16 oplFrequency) override;
|
||||
uint8 calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) override;
|
||||
|
||||
// Stores the instrument definitions set by sequencer meta events.
|
||||
OplInstrumentDefinition _instrumentDefs[LURE_MAX_SOURCES][MIDI_CHANNEL_COUNT];
|
||||
// Pitch bend sensitivity in semi-tones. This is a global setting;
|
||||
// it cannot be specified for a specific MIDI channel.
|
||||
uint8 _pitchBendSensitivity;
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#define Sound (::Lure::SoundManager::instance())
|
||||
|
||||
#endif
|
||||
258
engines/lure/strings.cpp
Normal file
258
engines/lure/strings.cpp
Normal file
@@ -0,0 +1,258 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lure/strings.h"
|
||||
#include "lure/disk.h"
|
||||
#include "lure/lure.h"
|
||||
#include "lure/res.h"
|
||||
#include "lure/room.h"
|
||||
#include "common/endian.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
StringData *int_strings = nullptr;
|
||||
|
||||
StringData::StringData() {
|
||||
int_strings = this;
|
||||
Disk &disk = Disk::getReference();
|
||||
|
||||
for (uint8 ctr = 0; ctr < MAX_NUM_CHARS; ++ctr) _chars[ctr] = nullptr;
|
||||
_numChars = 0;
|
||||
_names = Disk::getReference().getEntry(NAMES_RESOURCE_ID);
|
||||
_strings[0] = disk.getEntry(STRINGS_RESOURCE_ID);
|
||||
_strings[1] = disk.getEntry(STRINGS_2_RESOURCE_ID);
|
||||
_strings[2] = disk.getEntry(STRINGS_3_RESOURCE_ID);
|
||||
|
||||
// Add in the list of bit sequences, and what characters they represent
|
||||
MemoryBlock *decoderList = disk.getEntry(STRING_DECODER_RESOURCE_ID);
|
||||
|
||||
const char *p = (const char *) decoderList->data();
|
||||
while ((byte)*p != 0xff) {
|
||||
char ascii = *p++;
|
||||
add(p, ascii);
|
||||
p += strlen(p) + 1;
|
||||
}
|
||||
|
||||
delete decoderList;
|
||||
}
|
||||
|
||||
StringData::~StringData() {
|
||||
int_strings = nullptr;
|
||||
|
||||
for (uint8 ctr = 0; ctr < MAX_NUM_CHARS; ++ctr)
|
||||
if (_chars[ctr]) delete _chars[ctr];
|
||||
else break;
|
||||
|
||||
delete _names;
|
||||
delete _strings[0];
|
||||
delete _strings[1];
|
||||
delete _strings[2];
|
||||
}
|
||||
|
||||
StringData &StringData::getReference() {
|
||||
return *int_strings;
|
||||
}
|
||||
|
||||
void StringData::add(const char *sequence, char ascii) {
|
||||
uint32 value = 0;
|
||||
|
||||
for (uint8 index = 0; index < strlen(sequence); ++index) {
|
||||
if (sequence[index] == '1')
|
||||
value |= (1 << index);
|
||||
else if (sequence[index] != '0')
|
||||
error("Invalid character in string bit-stream sequence");
|
||||
}
|
||||
|
||||
if (_numChars == MAX_NUM_CHARS)
|
||||
error("Max characters too lower in string decoder");
|
||||
_chars[_numChars++] = new CharacterEntry(strlen(sequence), value, ascii);
|
||||
}
|
||||
|
||||
byte StringData::readBit() {
|
||||
byte result = ((*_srcPos & _bitMask) != 0) ? 1 : 0;
|
||||
_bitMask >>= 1;
|
||||
if (_bitMask == 0) {
|
||||
_bitMask = 0x80;
|
||||
++_srcPos;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool StringData::initPosition(uint16 stringId) {
|
||||
uint16 roomNumber = Room::getReference().roomNumber();
|
||||
|
||||
if ((roomNumber >= 0x2A) && (stringId >= STRING_ID_RANGE) && (stringId < STRING_ID_UPPER))
|
||||
stringId = 0x76;
|
||||
if ((roomNumber < 0x2A) && (stringId >= STRING_ID_UPPER))
|
||||
stringId = 0x76;
|
||||
|
||||
if (stringId < STRING_ID_RANGE)
|
||||
_stringTable = _strings[0]->data();
|
||||
else if (stringId < STRING_ID_RANGE*2) {
|
||||
stringId -= STRING_ID_RANGE;
|
||||
_stringTable = _strings[1]->data();
|
||||
} else {
|
||||
stringId -= STRING_ID_RANGE * 2;
|
||||
_stringTable = _strings[2]->data();
|
||||
}
|
||||
|
||||
_srcPos = _stringTable + 4;
|
||||
|
||||
uint32 total = 0;
|
||||
int numLoops = stringId >> 5;
|
||||
for (int ctr = 0; ctr < numLoops; ++ctr) {
|
||||
total += READ_LE_UINT16(_srcPos);
|
||||
_srcPos += sizeof(uint16);
|
||||
}
|
||||
|
||||
numLoops = stringId & 0x1f;
|
||||
if (numLoops!= 0) {
|
||||
byte *tempPtr = _stringTable + (stringId & 0xffe0) + READ_LE_UINT16(_stringTable);
|
||||
|
||||
for (int ctr = 0; ctr < numLoops; ++ctr) {
|
||||
byte v = *tempPtr++;
|
||||
if ((v & 0x80) == 0) {
|
||||
total += v;
|
||||
} else {
|
||||
total += (v & 0x7f) << 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_bitMask = 0x80;
|
||||
|
||||
if ((total & 3) != 0)
|
||||
_bitMask >>= (total & 3) * 2;
|
||||
|
||||
_srcPos = _stringTable + (total >> 2) + READ_LE_UINT16(_stringTable + 2);
|
||||
|
||||
// Final positioning to start of string
|
||||
for (;;) {
|
||||
if (readBit() == 0) break;
|
||||
_srcPos += 2;
|
||||
}
|
||||
return readBit() != 0;
|
||||
}
|
||||
|
||||
// readCharatcer
|
||||
// Reads the next character from the input bit stream
|
||||
|
||||
char StringData::readCharacter() {
|
||||
uint32 searchValue = 0;
|
||||
|
||||
// Loop through an increasing number of bits
|
||||
|
||||
for (uint8 numBits = 1; numBits <= 18; ++numBits) {
|
||||
searchValue |= readBit() << (numBits - 1);
|
||||
|
||||
// Scan through list for a match
|
||||
for (int index = 0; _chars[index] != nullptr; ++index) {
|
||||
if ((_chars[index]->_numBits == numBits) &&
|
||||
(_chars[index]->_sequence == searchValue))
|
||||
return _chars[index]->_ascii;
|
||||
}
|
||||
}
|
||||
|
||||
error("Unknown bit sequence encountered when decoding string");
|
||||
|
||||
return 0; // for compilers that don't support NORETURN
|
||||
}
|
||||
|
||||
void StringData::getString(uint16 stringId, char *dest, const char *hotspotName,
|
||||
const char *characterName, int hotspotArticle, int characterArticle) {
|
||||
debugC(ERROR_INTERMEDIATE, kLureDebugStrings,
|
||||
"StringData::getString stringId=%xh hotspot=%d,%s character=%d,%s",
|
||||
stringId, hotspotArticle, hotspotName, characterArticle, characterName);
|
||||
StringList &stringList = Resources::getReference().stringList();
|
||||
char ch;
|
||||
dest[0] = '\0';
|
||||
char *destPos = dest;
|
||||
stringId &= 0x1fff; // Strip off any article identifier
|
||||
if (stringId == 0) return;
|
||||
|
||||
bool includeArticles = initPosition(stringId);
|
||||
uint32 charOffset = _srcPos - _stringTable;
|
||||
uint8 charBitMask = _bitMask;
|
||||
|
||||
ch = readCharacter();
|
||||
|
||||
while (ch != '\0') {
|
||||
if (ch == '%') {
|
||||
// Copy over hotspot or action
|
||||
ch = readCharacter();
|
||||
const char *p = (ch == '1') ? hotspotName : characterName;
|
||||
int article = !includeArticles ? 0 : ((ch == '1') ? hotspotArticle : characterArticle);
|
||||
|
||||
if (p != nullptr) {
|
||||
if (article > 0) {
|
||||
Common::strcpy_s(destPos, MAX_DESC_SIZE - (destPos - dest), stringList.getString(S_ARTICLE_LIST + article - 1));
|
||||
Common::strcat_s(destPos, MAX_DESC_SIZE - (destPos - dest), p);
|
||||
} else {
|
||||
Common::strcpy_s(destPos, MAX_DESC_SIZE - (destPos - dest), p);
|
||||
}
|
||||
destPos += strlen(destPos);
|
||||
|
||||
debugC(ERROR_DETAILED, kLureDebugStrings, "String data %xh/%.2xh val=%.2xh name=%s",
|
||||
charOffset, charBitMask, (uint8)ch, p);
|
||||
}
|
||||
} else if ((uint8) ch >= 0xa0) {
|
||||
const char *p = getName((uint8) ch - 0xa0);
|
||||
Common::strcpy_s(destPos, MAX_DESC_SIZE - (destPos - dest), p);
|
||||
destPos += strlen(p);
|
||||
debugC(ERROR_DETAILED, kLureDebugStrings, "String data %xh/%.2xh val=%.2xh sequence='%s'",
|
||||
charOffset, charBitMask, (uint8)ch, p);
|
||||
} else {
|
||||
*destPos++ = ch;
|
||||
debugC(ERROR_DETAILED, kLureDebugStrings, "String data %xh/%.2xh val=%.2xh char=%c",
|
||||
charOffset, charBitMask, (uint8)ch, ch);
|
||||
}
|
||||
|
||||
charOffset = _srcPos - _stringTable;
|
||||
charBitMask = _bitMask;
|
||||
|
||||
// WORKAROUND: Italian version had an unterminated Look description for Prisoner after cutting sack
|
||||
if ((charOffset == 0x1a08) && (charBitMask == 1) &&
|
||||
(LureEngine::getReference().getLanguage() == Common::IT_ITA))
|
||||
// Hardcode for end of string
|
||||
ch = '\0';
|
||||
else
|
||||
ch = readCharacter();
|
||||
}
|
||||
|
||||
debugC(ERROR_DETAILED, kLureDebugStrings, "String data %xh/%.2xh val=%.2xh EOS",
|
||||
charOffset, charBitMask, ch);
|
||||
*destPos = '\0';
|
||||
}
|
||||
|
||||
// getName
|
||||
// Returns the name or fragment of word at the specified index in the names resource
|
||||
|
||||
char *StringData::getName(uint8 nameIndex) {
|
||||
uint16 numNames = READ_LE_UINT16(_names->data()) / 2;
|
||||
if (nameIndex >= numNames)
|
||||
error("Invalid name index was passed to getCharacterName");
|
||||
|
||||
uint16 nameStart = READ_LE_UINT16(_names->data() + (nameIndex * 2));
|
||||
return (char *) (_names->data() + nameStart);
|
||||
}
|
||||
|
||||
} // namespace Lure
|
||||
71
engines/lure/strings.h
Normal file
71
engines/lure/strings.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_STRINGS_H
|
||||
#define LURE_STRINGS_H
|
||||
|
||||
#include "lure/luredefs.h"
|
||||
#include "lure/memory.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
class CharacterEntry {
|
||||
public:
|
||||
uint8 _numBits;
|
||||
uint32 _sequence;
|
||||
char _ascii;
|
||||
|
||||
CharacterEntry(uint8 numBits, uint32 sequence, char ascii): _numBits(numBits),
|
||||
_sequence(sequence), _ascii(ascii) {}
|
||||
};
|
||||
|
||||
#define MAX_NUM_CHARS 218
|
||||
|
||||
class StringData {
|
||||
private:
|
||||
MemoryBlock *_strings[3];
|
||||
MemoryBlock *_names;
|
||||
CharacterEntry *_chars[MAX_NUM_CHARS];
|
||||
uint8 _numChars;
|
||||
byte *_srcPos;
|
||||
byte *_stringTable;
|
||||
byte _bitMask;
|
||||
|
||||
void add(const char *sequence, char ascii);
|
||||
bool initPosition(uint16 stringId);
|
||||
char readCharacter();
|
||||
byte readBit();
|
||||
public:
|
||||
StringData();
|
||||
~StringData();
|
||||
static StringData &getReference();
|
||||
|
||||
void getString(uint16 stringId, char *dest, const char *hotspotName, const char *characterName,
|
||||
int hotspotArticle = 0, int characterArticle = 0);
|
||||
void getString(uint16 stringId, char *dest) {
|
||||
getString(stringId, dest, NULL, NULL);
|
||||
}
|
||||
char *getName(uint8 nameIndex);
|
||||
};
|
||||
|
||||
} // namespace Lure
|
||||
|
||||
#endif
|
||||
1510
engines/lure/surface.cpp
Normal file
1510
engines/lure/surface.cpp
Normal file
File diff suppressed because it is too large
Load Diff
166
engines/lure/surface.h
Normal file
166
engines/lure/surface.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LURE_SURFACE_H
|
||||
#define LURE_SURFACE_H
|
||||
|
||||
|
||||
#include "common/str.h"
|
||||
#include "common/ptr.h"
|
||||
#include "lure/disk.h"
|
||||
#include "lure/luredefs.h"
|
||||
|
||||
namespace Lure {
|
||||
|
||||
#define DEFAULT_TEXT_COLOR -1
|
||||
|
||||
class Surface {
|
||||
private:
|
||||
MemoryBlock *_data;
|
||||
uint16 _width, _height;
|
||||
|
||||
void egaCreateDialog();
|
||||
void vgaCreateDialog();
|
||||
void egaRefreshDialog();
|
||||
void vgaRefreshDialog();
|
||||
public:
|
||||
Surface(MemoryBlock *src, uint16 width, uint16 height);
|
||||
Surface(uint16 width, uint16 height);
|
||||
~Surface();
|
||||
static uint16 textX();
|
||||
static uint16 textY();
|
||||
static void getDialogBounds(Common::Point &size, int charWidth, int numLines,
|
||||
bool squashedLines = true);
|
||||
|
||||
static void initialize();
|
||||
static void deinitialize();
|
||||
|
||||
uint16 width() { return _width; }
|
||||
uint16 height() { return _height; }
|
||||
MemoryBlock &data() { return *_data; }
|
||||
|
||||
void loadScreen(uint16 resourceId);
|
||||
void loadScreen(MemoryBlock *data);
|
||||
int writeChar(uint16 x, uint16 y, uint8 ascii, bool transparent, int color);
|
||||
void writeString(uint16 x, uint16 y, Common::String line, bool transparent,
|
||||
int color = DEFAULT_TEXT_COLOR, bool varLength = true);
|
||||
void writeSubstring(uint16 x, uint16 y, Common::String line, int len,
|
||||
bool transparent, int color = DEFAULT_TEXT_COLOR, bool varLength = true);
|
||||
void transparentCopyTo(Surface *dest);
|
||||
void copyTo(Surface *dest);
|
||||
void copyTo(Surface *dest, uint16 x, uint16 y);
|
||||
void copyTo(Surface *dest, const Common::Rect &srcBounds, uint16 destX, uint16 destY,
|
||||
int transparentColor = -1);
|
||||
void copyFrom(MemoryBlock *src) { _data->copyFrom(src); }
|
||||
void copyFrom(MemoryBlock *src, uint32 destOffset);
|
||||
void empty() { _data->empty(); }
|
||||
void fillRect(const Common::Rect &r, uint8 color);
|
||||
void createDialog();
|
||||
void refreshDialog();
|
||||
void copyToScreen(uint16 x, uint16 y);
|
||||
void centerOnScreen();
|
||||
|
||||
static uint16 textWidth(const char *s, int numChars = 0);
|
||||
static void wordWrap(char *text, uint16 width, char **&lines, uint8 &numLines);
|
||||
static Surface *newDialog(uint16 width, uint8 numLines, const char **lines, bool varLength = true,
|
||||
int color = DEFAULT_TEXT_COLOR, bool squashedLines = true);
|
||||
static Surface *newDialog(uint16 width, const char *lines, int color = DEFAULT_TEXT_COLOR);
|
||||
static Surface *getScreen(uint16 resourceId);
|
||||
bool getString(Common::String &line, int maxSize, bool isNumeric, bool varLength, int16 x, int16 y);
|
||||
};
|
||||
|
||||
class Dialog {
|
||||
public:
|
||||
static void show(const char *text);
|
||||
static void show(uint16 stringId, const char *hotspotName, const char *characterName);
|
||||
static void show(uint16 stringId);
|
||||
};
|
||||
|
||||
class TalkDialog {
|
||||
private:
|
||||
Surface *_surface;
|
||||
char _desc[MAX_DESC_SIZE];
|
||||
char **_lines;
|
||||
uint8 _numLines;
|
||||
int _endLine, _endIndex;
|
||||
int _wordCountdown;
|
||||
|
||||
uint16 _characterId;
|
||||
uint16 _destCharacterId;
|
||||
uint16 _activeItemId;
|
||||
uint16 _descId;
|
||||
|
||||
int getArticle(uint16 msgId, uint16 objId);
|
||||
void vgaTalkDialog(Surface *s);
|
||||
public:
|
||||
TalkDialog(uint16 characterId, uint16 destCharacterId, uint16 activeItemId, uint16 descId);
|
||||
~TalkDialog();
|
||||
|
||||
char *desc() { return _desc; }
|
||||
Surface &surface() { return *_surface; }
|
||||
void copyTo(Surface *dest, uint16 x, uint16 y);
|
||||
bool isBuilding() { return _endLine < _numLines; }
|
||||
|
||||
void saveToStream(Common::WriteStream *stream);
|
||||
static TalkDialog *loadFromStream(Common::ReadStream *stream);
|
||||
};
|
||||
|
||||
class SaveRestoreDialog {
|
||||
private:
|
||||
static void toggleHightlight(int xs, int xe, int ys, int ye);
|
||||
public:
|
||||
static bool show(bool saveDialog);
|
||||
};
|
||||
|
||||
class RestartRestoreDialog {
|
||||
public:
|
||||
static bool show();
|
||||
};
|
||||
|
||||
class CopyProtectionDialog {
|
||||
private:
|
||||
typedef Common::List<Common::SharedPtr<Hotspot> > HotspotsList;
|
||||
HotspotsList _hotspots;
|
||||
int _charIndex;
|
||||
|
||||
void chooseCharacters();
|
||||
public:
|
||||
CopyProtectionDialog();
|
||||
bool show();
|
||||
};
|
||||
|
||||
class AudioInitIcon {
|
||||
private:
|
||||
Surface *_iconSurface;
|
||||
bool _visible;
|
||||
|
||||
public:
|
||||
AudioInitIcon();
|
||||
~AudioInitIcon();
|
||||
|
||||
void toggleVisibility();
|
||||
void show();
|
||||
void hide();
|
||||
};
|
||||
|
||||
} // End of namespace Lure
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user