Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

3
engines/lure/POTFILES Normal file
View File

@@ -0,0 +1,3 @@
engines/lure/lure.cpp
engines/lure/detection.cpp
engines/lure/metaengine.cpp

299
engines/lure/animseq.cpp Normal file
View 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
View 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

View 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
View File

@@ -0,0 +1,3 @@
begin_section("Lure");
add_person("Paul Gilbert", "dreammaster", "");
end_section();

635
engines/lure/debugger.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

95
engines/lure/game.h Normal file
View 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

File diff suppressed because it is too large Load Diff

491
engines/lure/hotspots.h Normal file
View 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 &currentActions() 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

929
engines/lure/res_struct.h Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

154
engines/lure/scripts.h Normal file
View 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

File diff suppressed because it is too large Load Diff

200
engines/lure/sound.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

166
engines/lure/surface.h Normal file
View 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