aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.common6
-rw-r--r--base/plugins.cpp3
-rwxr-xr-xconfigure13
-rw-r--r--lure/animseq.cpp148
-rw-r--r--lure/animseq.h56
-rw-r--r--lure/debug-input.cpp133
-rw-r--r--lure/debug-input.h42
-rw-r--r--lure/debug-methods.cpp132
-rw-r--r--lure/debug-methods.h39
-rw-r--r--lure/decode.cpp360
-rw-r--r--lure/decode.h62
-rw-r--r--lure/disk.cpp174
-rw-r--r--lure/disk.h66
-rw-r--r--lure/events.cpp159
-rw-r--r--lure/events.h78
-rw-r--r--lure/game.cpp420
-rw-r--r--lure/game.h64
-rw-r--r--lure/hotspots.cpp806
-rw-r--r--lure/hotspots.h137
-rw-r--r--lure/intro.cpp151
-rw-r--r--lure/intro.h45
-rw-r--r--lure/lure.cpp306
-rw-r--r--lure/lure.h73
-rw-r--r--lure/luredefs.h183
-rw-r--r--lure/memory.cpp107
-rw-r--r--lure/memory.h63
-rw-r--r--lure/menu.cpp415
-rw-r--r--lure/menu.h92
-rw-r--r--lure/module.mk36
-rw-r--r--lure/palette.cpp142
-rw-r--r--lure/palette.h68
-rw-r--r--lure/res.cpp399
-rw-r--r--lure/res.h94
-rw-r--r--lure/res_struct.cpp309
-rw-r--r--lure/res_struct.h401
-rw-r--r--lure/room.cpp485
-rw-r--r--lure/room.h107
-rw-r--r--lure/screen.cpp152
-rw-r--r--lure/screen.h64
-rw-r--r--lure/scripts.cpp576
-rw-r--r--lure/scripts.h117
-rw-r--r--lure/strings.cpp300
-rw-r--r--lure/strings.h67
-rw-r--r--lure/surface.cpp456
-rw-r--r--lure/surface.h83
-rw-r--r--lure/system.cpp41
-rw-r--r--lure/system.h40
47 files changed, 8270 insertions, 0 deletions
diff --git a/Makefile.common b/Makefile.common
index ce76cc8385..bef603ee24 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -96,6 +96,12 @@ else
MODULES += gob
endif
+ifdef DISABLE_LURE
+DEFINES += -DDISABLE_LURE
+else
+MODULES += lure
+endif
+
# After the game specific modules follow the shared modules
MODULES += \
gui \
diff --git a/base/plugins.cpp b/base/plugins.cpp
index c789f9e1d9..4da156f339 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -329,6 +329,9 @@ void PluginManager::loadPlugins() {
#ifndef DISABLE_GOB
LINK_PLUGIN(GOB)
#endif
+ #ifndef DISABLE_LURE
+ LINK_PLUGIN(LURE)
+ #endif
#endif
}
diff --git a/configure b/configure
index 7900fb41d0..3e43d4bcd9 100755
--- a/configure
+++ b/configure
@@ -56,6 +56,7 @@ _build_queen=yes
_build_saga=yes
_build_gob=yes
_build_kyra=yes
+_build_lure=yes
_need_memalign=no
_build_plugins=no
_nasm=auto
@@ -301,6 +302,7 @@ Optional Features:
--disable-saga don't build the SAGA engine
--disable-gob don't build the Gobli*ns engine
--disable-kyra don't build the Legend of Kyrandia engine
+ --disable-lure don't build the Lure of the Temptress engine
--enable-plugins build engines as loadable modules instead of
static linking them
--disable-mt32emu don't enable the integrated MT-32 emulator
@@ -366,6 +368,7 @@ for ac_option in $@; do
--disable-saga) _build_saga=no ;;
--disable-gob) _build_gob=no ;;
--disable-kyra) _build_kyra=no ;;
+ --disable-lure) _build_lure=no ;;
--disable-hq-scalers) _build_hq_scalers=no ;;
--disable-scalers) _build_scalers=no ;;
--enable-alsa) _alsa=yes ;;
@@ -677,6 +680,12 @@ else
_mak_gob='# DISABLE_GOB = 1'
fi
+if test "$_build_lure" = no ; then
+ _mak_lure='DISABLE_LURE = 1'
+else
+ _mak_lure='# DISABLE_LURE = 1'
+fi
+
if test "$_build_hq_scalers" = no ; then
_mak_hq_scalers='DISABLE_HQ_SCALERS = 1'
else
@@ -1242,6 +1251,9 @@ fi
if test "$_build_gob" = yes ; then
echo " Gobli*ns"
fi
+if test "$_build_lure" = yes ; then
+ echo " Lure of the Temptress"
+fi
echo
@@ -1379,6 +1391,7 @@ $_mak_queen
$_mak_kyra
$_mak_saga
$_mak_gob
+$_mak_lure
$_mak_mt32emu
$_mak_hq_scalers
diff --git a/lure/animseq.cpp b/lure/animseq.cpp
new file mode 100644
index 0000000000..18a27297ed
--- /dev/null
+++ b/lure/animseq.cpp
@@ -0,0 +1,148 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/animseq.h"
+#include "lure/palette.h"
+#include "lure/decode.h"
+#include "lure/events.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) {
+ uint32 delayCtr = _system.getMillis() + milliseconds;
+ Events &events = Events::getReference();
+
+ while (_system.getMillis() < delayCtr) {
+ while (events.pollEvent()) {
+ if (events.type() == OSystem::EVENT_KEYDOWN) {
+ if (events.event().kbd.keycode == 27) return ABORT_END_INTRO;
+ else return ABORT_NEXT_SCENE;
+ } else if (events.type() == OSystem::EVENT_LBUTTONDOWN)
+ return ABORT_NEXT_SCENE;
+ else if (events.type() == OSystem::EVENT_QUIT)
+ return ABORT_END_INTRO;
+ }
+
+ uint32 delayAmount = delayCtr - _system.getMillis();
+ if (delayAmount > 10) delayAmount = 10;
+ _system.delayMillis(delayAmount);
+ }
+ return ABORT_NONE;
+}
+
+// decodeFrame
+// Decodes a single frame of the animation sequence
+
+void AnimationSequence::decodeFrame(byte *&pPixels, byte *&pLines) {
+ byte *screen = _screen.screen_raw();
+ uint16 screenPos = 0;
+ uint16 len;
+
+ while (screenPos < SCREEN_SIZE) {
+ // Get line length
+ len = (uint16) *pLines++;
+ if (len == 0) {
+ len = *((uint16 *) pLines);
+ pLines += 2;
+ }
+
+ // Move the splice over
+ memcpy(screen, pPixels, len);
+ screen += len;
+ screenPos += len;
+ pPixels += len;
+
+ // Get the offset inc amount
+ len = (uint16) *pLines++;
+ if (len == 0) {
+ len = *((uint16 *) pLines);
+ pLines += 2;
+ }
+
+ screen += len;
+ screenPos += len;
+ }
+
+ // Make the decoded frame visible
+ _screen.update();
+}
+
+AnimationSequence::AnimationSequence(Screen &screen, OSystem &system, uint16 screenId, Palette &palette,
+ bool fadeIn): _screen(screen), _system(system), _screenId(screenId), _palette(palette) {
+ PictureDecoder decoder;
+ Disk &d = Disk::getReference();
+ MemoryBlock *data = d.getEntry(_screenId);
+ _decodedData = decoder.decode(data, MAX_ANIM_DECODER_BUFFER_SIZE);
+ delete data;
+
+ _lineRefs = d.getEntry(_screenId + 1);
+
+ // Show the screen that preceeds the start of the animation data
+ _screen.setPaletteEmpty();
+ _screen.screen().data().copyFrom(_decodedData, 0, 0, FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH);
+ _screen.update();
+
+ // Set the palette
+ if (fadeIn) _screen.paletteFadeIn(&_palette);
+ else _screen.setPalette(&_palette);
+
+ // Set up frame poitners
+ _pPixels = _decodedData->data() + SCREEN_SIZE;
+ _pLines = _lineRefs->data();
+ _pPixelsEnd = _decodedData->data() + _decodedData->size() - 1;
+ _pLinesEnd = _lineRefs->data() + _lineRefs->size() - 1;
+}
+
+AnimationSequence::~AnimationSequence() {
+ delete _lineRefs;
+ delete _decodedData;
+}
+
+// show
+// Main method for displaying the animation
+
+AnimAbortType AnimationSequence::show() {
+ AnimAbortType result;
+
+ // Loop through displaying the animations
+ while ((_pPixels < _pPixelsEnd) && (_pLines < _pLinesEnd)) {
+ decodeFrame(_pPixels, _pLines);
+
+ result = delay(130);
+ if (result != ABORT_NONE) return result;
+ }
+
+ return ABORT_NONE;
+}
+
+bool AnimationSequence::step() {
+ if ((_pPixels >= _pPixelsEnd) || (_pLines >= _pLinesEnd)) return false;
+ decodeFrame(_pPixels, _pLines);
+ _screen.setPalette(&_palette);
+ return true;
+}
+
+} // end of namespace Lure
diff --git a/lure/animseq.h b/lure/animseq.h
new file mode 100644
index 0000000000..fb6387a85d
--- /dev/null
+++ b/lure/animseq.h
@@ -0,0 +1,56 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_animseq_h__
+#define __lure_animseq_h__
+
+#include "lure/screen.h"
+
+namespace Lure {
+
+enum AnimAbortType {ABORT_NONE, ABORT_END_INTRO, ABORT_NEXT_SCENE};
+
+class AnimationSequence {
+private:
+ Screen &_screen;
+ OSystem &_system;
+ uint16 _screenId;
+ Palette &_palette;
+ MemoryBlock *_decodedData;
+ MemoryBlock *_lineRefs;
+ byte *_pPixels, *_pLines;
+ byte *_pPixelsEnd, *_pLinesEnd;
+
+ AnimAbortType delay(uint32 milliseconds);
+ void decodeFrame(byte *&pPixels, byte *&pLines);
+public:
+ AnimationSequence(Screen &screen, OSystem &system, uint16 screenId, Palette &palette,
+ bool fadeIn);
+ ~AnimationSequence();
+
+ AnimAbortType show();
+ bool step();
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/debug-input.cpp b/lure/debug-input.cpp
new file mode 100644
index 0000000000..9c6d7bf707
--- /dev/null
+++ b/lure/debug-input.cpp
@@ -0,0 +1,133 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/debug-input.h"
+#include "lure/luredefs.h"
+#include "lure/events.h"
+#include "lure/surface.h"
+#include "lure/screen.h"
+
+#ifdef LURE_DEBUG
+
+namespace Lure {
+
+bool get_string(char *buffer, uint32 maxSize, bool isNumeric, uint16 x, uint16 y) {
+ Events &e = Events::getReference();
+ buffer[0] = '\0';
+
+ // Create surface for holding entered text
+ Surface *s = new Surface((maxSize + 1) * FONT_WIDTH, FONT_HEIGHT);
+
+ bool abortFlag = false;
+ bool refreshFlag = true;
+
+ while (!e.quitFlag && !abortFlag) {
+ // Check for refreshing display of text
+ if (refreshFlag) {
+ uint16 strWidth = Surface::textWidth(buffer);
+ s->empty();
+ s->writeString(0, 0, buffer, false, DIALOG_TEXT_COLOUR);
+ s->writeChar(strWidth, 0, '_', false, DIALOG_TEXT_COLOUR);
+ s->copyToScreen(x, y);
+
+ refreshFlag = false;
+ }
+
+ if (e.pollEvent()) {
+ if (e.type() == OSystem::EVENT_KEYDOWN) {
+ char ch = e.event().kbd.ascii;
+ uint16 keycode = e.event().kbd.keycode;
+
+ if ((ch == 13) || (keycode == 0x10f))
+ break;
+ else if (ch == 27)
+ abortFlag = true;
+ else if (ch == 8) {
+ if (*buffer != '\0') {
+ *((char *) buffer + strlen(buffer) - 1) = '\0';
+ refreshFlag = true;
+ }
+ } else if ((ch >= ' ') && (strlen(buffer) < maxSize)) {
+ if (((ch >= '0') && (ch <= '9')) || !isNumeric) {
+ char *p = buffer + strlen(buffer);
+ *p++ = ch;
+ *p++ = '\0';
+ refreshFlag = true;
+ }
+ }
+ }
+ }
+ }
+
+ delete s;
+ if (e.quitFlag) abortFlag = true;
+ return !abortFlag;
+}
+
+bool input_integer(Common::String desc, uint32 &value)
+{
+ const int MAX_SIZE = 5;
+ char buffer[MAX_SIZE + 1];
+
+ uint16 width = DIALOG_EDGE_SIZE + Surface::textWidth(desc.c_str()) + FONT_WIDTH;
+ uint16 totalWidth = width + FONT_WIDTH * (MAX_SIZE + 1) + DIALOG_EDGE_SIZE;
+ uint16 totalHeight = FONT_HEIGHT + DIALOG_EDGE_SIZE * 2;
+
+ Surface *s = new Surface(totalWidth, totalHeight);
+ s->createDialog(true);
+ s->writeString(DIALOG_EDGE_SIZE + 3, DIALOG_EDGE_SIZE, desc, false);
+
+ uint16 xs = (FULL_SCREEN_WIDTH-totalWidth) / 2;
+ uint16 ys = (FULL_SCREEN_HEIGHT-totalHeight) / 2;
+ s->copyToScreen(xs, ys);
+
+ bool result = get_string(&buffer[0], MAX_SIZE, true, xs+width, ys+DIALOG_EDGE_SIZE);
+ Screen::getReference().update();
+ if (!result || (buffer[0] == '\0'))
+ return false;
+
+ value = atoi(buffer);
+ return true;
+}
+
+bool input_string(Common::String desc, char *buffer, uint32 maxSize)
+{
+ uint16 width = Surface::textWidth(desc.c_str());
+ if (width < FONT_WIDTH * maxSize) width = FONT_WIDTH * maxSize;
+
+ Surface *s = new Surface(width + 2 * DIALOG_EDGE_SIZE, 2 * FONT_HEIGHT + 2 * DIALOG_EDGE_SIZE);
+ s->createDialog();
+ s->writeString(DIALOG_EDGE_SIZE, DIALOG_EDGE_SIZE, desc, false, DIALOG_TEXT_COLOUR);
+
+ uint16 xs = (FULL_SCREEN_WIDTH-s->width()) / 2;
+ uint16 ys = (FULL_SCREEN_HEIGHT-s->height()) / 2;
+
+ s->copyToScreen(xs, ys);
+ bool result = get_string(buffer, maxSize, true, xs + width, ys + DIALOG_EDGE_SIZE);
+
+ Screen::getReference().update();
+ return result;
+}
+
+} // end of namespace Lure
+
+#endif
diff --git a/lure/debug-input.h b/lure/debug-input.h
new file mode 100644
index 0000000000..ec820d6745
--- /dev/null
+++ b/lure/debug-input.h
@@ -0,0 +1,42 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifdef LURE_DEBUG
+#ifndef __lure_input_h__
+#define __lure_input_h__
+
+#include "common/stdafx.h"
+#include "common/str.h"
+#include "lure/surface.h"
+
+namespace Lure {
+
+bool get_string(char *buffer, uint32 maxSize, bool isNumeric, uint16 x, uint16 y);
+
+bool input_integer(Common::String desc, uint32 &value);
+
+bool input_string(Common::String desc, char *buffer, uint32 maxSize);
+
+} // End of namespace Lure
+
+#endif
+#endif
diff --git a/lure/debug-methods.cpp b/lure/debug-methods.cpp
new file mode 100644
index 0000000000..2fda7a57dc
--- /dev/null
+++ b/lure/debug-methods.cpp
@@ -0,0 +1,132 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/debug-methods.h"
+#include "lure/luredefs.h"
+
+#include "lure/events.h"
+#include "lure/surface.h"
+#include "lure/screen.h"
+#include "lure/res.h"
+#include "lure/strings.h"
+#include "lure/room.h"
+
+#ifdef LURE_DEBUG
+
+namespace Lure {
+
+void showActiveHotspots() {
+ char buffer[16384];
+ char *lines[100];
+ char *s = buffer;
+ int numLines = 0;
+ lines[0] = s;
+ *s = '\0';
+
+ Resources &resources = Resources::getReference();
+ Mouse &mouse = Mouse::getReference();
+ Events &events = Events::getReference();
+ Screen &screen = Screen::getReference();
+
+ HotspotList::iterator i = resources.activeHotspots().begin();
+ for (; i != resources.activeHotspots().end(); ++i) {
+ Hotspot &h = *i.operator*();
+ lines[numLines++] = s;
+
+ if (numLines == 16) {
+ strcpy(s, "..more..");
+ break;
+ }
+
+ sprintf(s, "%x", h.hotspotId());
+ s += strlen(s);
+
+ sprintf(s, "h pos=(%d,%d,%d) size=(%d,%d) - ",
+ h.resource().roomNumber, h.x(), h.y(), h.width(), h.height());
+ s += strlen(s);
+
+ uint16 nameId = h.resource().nameId;
+ if (nameId != 0) {
+ StringData::getReference().getString(nameId, s, NULL, NULL);
+ s += strlen(s);
+ }
+ ++s;
+ }
+
+ Surface *surface = Surface::newDialog(300, numLines, lines);
+ mouse.cursorOff();
+ surface->copyToScreen(10, 40);
+ events.waitForPress();
+ screen.update();
+ mouse.cursorOn();
+ delete surface;
+}
+
+void showRoomHotspots() {
+ char buffer[16384];
+ char *lines[100];
+ char *s = buffer;
+ int numLines = 0;
+ lines[0] = s;
+ *s = '\0';
+
+ Resources &resources = Resources::getReference();
+ Mouse &mouse = Mouse::getReference();
+ Events &events = Events::getReference();
+ Screen &screen = Screen::getReference();
+ uint16 roomNumber = Room::getReference().roomNumber();
+
+ HotspotDataList::iterator i = resources.hotspotData().begin();
+ for (; i != resources.hotspotData().end(); ++i) {
+ HotspotData &h = *i.operator*();
+ if (h.roomNumber == roomNumber) {
+ lines[numLines++] = s;
+
+ sprintf(s, "%x", h.hotspotId);
+ s += strlen(s);
+
+ sprintf(s, "h pos=(%d,%d) size=(%d,%d) - ",
+ h.startX, h.startY, h.width, h.height);
+ s += strlen(s);
+
+ uint16 nameId = h.nameId;
+ if (nameId != 0) {
+ StringData::getReference().getString(nameId, s, NULL, NULL);
+ s += strlen(s);
+ }
+ ++s;
+ }
+ }
+
+ Surface *surface = Surface::newDialog(300, numLines, lines);
+ mouse.cursorOff();
+ surface->copyToScreen(10, 40);
+ events.waitForPress();
+ screen.update();
+ mouse.cursorOn();
+ delete surface;
+}
+
+
+} // end of namespace Lure
+
+#endif
diff --git a/lure/debug-methods.h b/lure/debug-methods.h
new file mode 100644
index 0000000000..72e31b571b
--- /dev/null
+++ b/lure/debug-methods.h
@@ -0,0 +1,39 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifdef LURE_DEBUG
+#ifndef __lure_debugprocs_h__
+#define __lure_debugprocs_h__
+
+#include "common/stdafx.h"
+#include "lure/surface.h"
+
+namespace Lure {
+
+void showActiveHotspots();
+
+void showRoomHotspots();
+
+} // End of namespace Lure
+
+#endif
+#endif
diff --git a/lure/decode.cpp b/lure/decode.cpp
new file mode 100644
index 0000000000..c2691d461e
--- /dev/null
+++ b/lure/decode.cpp
@@ -0,0 +1,360 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/decode.h"
+#include "lure/memory.h"
+#include "lure/luredefs.h"
+
+namespace Lure {
+
+/*--------------------------------------------------------------------------*/
+/* PictureDecoder class */
+/* */
+/* Provides the functionality for decoding screens */
+/*--------------------------------------------------------------------------*/
+
+void PictureDecoder::writeByte(MemoryBlock *dest, byte v) {
+ if (outputOffset == dest->size())
+ error("Decoded data exceeded allocated output buffer size");
+ dest->data()[outputOffset++] = v;
+}
+
+void PictureDecoder::writeBytes(MemoryBlock *dest, byte v, uint16 numBytes) {
+ if (outputOffset + numBytes > dest->size())
+ error("Decoded data exceeded allocated output buffer size");
+ dest->memset(v, outputOffset, numBytes);
+ outputOffset += numBytes;
+}
+
+byte PictureDecoder::DSSI(bool incr) {
+ byte result = dataIn[dataPos];
+ if (incr) ++dataPos;
+ return result;
+}
+
+byte PictureDecoder::ESBX(bool incr) {
+ byte result = dataIn[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;
+}
+
+void PictureDecoder::swap(uint16 &v1, uint16 &v2) {
+ uint16 vTemp;
+ vTemp = v1;
+ v1 = v2;
+ v2 = vTemp;
+}
+
+// decode_data
+// Takes care of decoding compressed Lure of the Temptress data
+
+MemoryBlock *PictureDecoder::decode(MemoryBlock *src, uint32 maxOutputSize) {
+ MemoryBlock *dest = Memory::allocate(maxOutputSize);
+
+ // Set up initial states
+ dataIn = src->data();
+ outputOffset = 0;
+ dataPos = READ_LE_UINT32(dataIn + 0x400);
+ dataPos2 = 0x404;
+
+ CH = ESBX();
+ CL = 9;
+
+Loc754:
+ AL = DSSI();
+ writeByte(dest, AL);
+ BP = ((uint16) AL) << 2;
+
+Loc755:
+ decrCtr();
+ if (shlCarry()) goto Loc761;
+ decrCtr();
+ if (shlCarry()) goto Loc759;
+ AL = dataIn[BP];
+
+Loc758:
+ writeByte(dest, AL);
+ BP = ((uint16) AL) << 2;
+ goto Loc755;
+
+Loc759:
+ AL = (byte) (BP >> 2);
+ AH = DSSI();
+ if (AH == 0) goto Loc768;
+
+ writeBytes(dest, AL, AH);
+ goto Loc755;
+
+Loc761:
+ decrCtr();
+ if (shlCarry()) goto Loc765;
+ decrCtr();
+
+ if (shlCarry()) goto Loc764;
+ AL = dataIn[BP+1];
+ goto Loc758;
+
+Loc764:
+ AL = dataIn[BP+2];
+ goto Loc758;
+
+Loc765:
+ decrCtr();
+ if (shlCarry()) goto Loc767;
+ AL = dataIn[BP+3];
+ goto Loc758;
+
+Loc767:
+ goto Loc754;
+
+Loc768:
+ AL = DSSI();
+ if (AL != 0) goto Loc755;
+
+ // 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(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) {
+ GET_BYTE;
+ bitCtr = 8;
+ }
+ }
+}
+
+uint32 AnimationDecoder::decode_data(MemoryBlock *src, MemoryBlock *dest, uint32 srcPos) {
+ byte *pSrc = src->data() + srcPos;
+ byte *pDest = dest->data();
+ byte 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;
+
+ for (;;) {
+ carry = false;
+ rcl(currData, carry);
+ if (--bitCtr == 0) {
+ GET_BYTE;
+ bitCtr = 8;
+ }
+ if (carry) goto loc_1441;
+ tableOffset = BX_VAL(0);
+
+loc_1439:
+ dx ^= 1;
+ if ((dx & 1) != 0) {
+ SET_HI_BYTE(dx, tableOffset << 4);
+ *pDest = dx >> 8;
+ } else {
+ *pDest++ |= tableOffset;
+ }
+ continue;
+
+loc_1441:
+ 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);
+ }
+ goto loc_1439;
+ }
+
+ rcl(currData, carry);
+ if (--bitCtr == 0) {
+ GET_BYTE;
+ bitCtr = 8;
+ }
+ if (!carry) {
+ tableOffset = BX_VAL(0x30);
+ goto loc_1439;
+ }
+
+ 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(pSrc, currData, bitCtr, dx, carry);
+
+ SET_LO_BYTE(dx, dx >> 8);
+ decode_data_2(pSrc, currData, bitCtr, dx, carry);
+ SET_HI_BYTE(bitCtr, dx & 0xff);
+ SET_LO_BYTE(bitCtr, dx >> 8);
+ dx = tempReg2;
+
+ if (bitCtr == 0)
+ // Exit out of infinite loop
+ break;
+
+ } else if (dxHigh == BX_VAL(0x10)) {
+ tempReg1 = bitCtr;
+ decode_data_2(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;
+ goto loc_1439;
+ }
+
+ 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;
+ }
+
+ // Return number of bytes written
+ return pDest - dest->data();
+}
+
+} // end of namespace Lure
diff --git a/lure/decode.h b/lure/decode.h
new file mode 100644
index 0000000000..4f2994d424
--- /dev/null
+++ b/lure/decode.h
@@ -0,0 +1,62 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_decode_h__
+#define __lure_decode_h__
+
+#include "common/stdafx.h"
+#include "lure/luredefs.h"
+#include "lure/memory.h"
+
+namespace Lure {
+
+class PictureDecoder {
+private:
+ byte *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();
+ void swap(uint16 &v1, uint16 &v2);
+public:
+ MemoryBlock *decode(MemoryBlock *src, uint32 maxOutputSize = SCREEN_SIZE);
+};
+
+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(byte *&pSrc, uint16 &currData, uint16 &bitCtr,
+ uint16 &dx, bool &carry);
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/disk.cpp b/lure/disk.cpp
new file mode 100644
index 0000000000..e996ff6ecc
--- /dev/null
+++ b/lure/disk.cpp
@@ -0,0 +1,174 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "common/util.h"
+#include "common/scummsys.h"
+
+#include "lure/disk.h"
+#include "lure/luredefs.h"
+
+namespace Lure {
+
+static Disk *int_disk = NULL;
+
+Disk &Disk::getReference() {
+ return *int_disk;
+}
+
+Disk::Disk(const Common::String &gameDataPath) {
+ _gameDataPath = gameDataPath;
+ _fileNum = 0xff;
+ _fileHandle = NULL;
+ int_disk = this;
+}
+
+Disk::~Disk() {
+ if (_fileHandle) delete _fileHandle;
+ int_disk = NULL;
+}
+
+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;
+ error("Could not find entry Id #%d in file %sdisk%d.vga", id, _gameDataPath.c_str(), _fileNum);
+}
+
+void Disk::openFile(uint8 fileNum) {
+ // Validate that the file number is correct
+ 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 the new file
+ _fileHandle = new Common::File();
+
+ char sFilename[10];
+ if (_fileNum == 0)
+ strcpy(sFilename, SUPPORT_FILENAME);
+ else
+ sprintf(sFilename, "disk%d.vga", _fileNum);
+
+ _fileHandle->open(sFilename);
+ if (!_fileHandle->isOpen())
+ error("Could not open %s%s", _gameDataPath.c_str(), sFilename);
+
+ // Validate the header
+ char buffer[7];
+ uint32 bytesRead;
+
+ bytesRead = _fileHandle->read(buffer, 6);
+ buffer[6] = '\0';
+ if (strcmp(buffer, HEADER_IDENT_STRING) != 0)
+ error("The file %s%s was not a valid VGA file", _gameDataPath.c_str(), sFilename);
+
+ uint16 fileFileNum = _fileHandle->readUint16BE();
+ if (fileFileNum != _fileNum)
+ error("The file %s%s was not the correct file number", _gameDataPath.c_str(), sFilename);
+
+ // Read in the header entries
+ uint32 headerSize = sizeof(FileEntry) * NUM_ENTRIES_IN_HEADER;
+ if (_fileHandle->read(_entries, headerSize) != headerSize)
+ error("The file %s%s had a corrupted header", _gameDataPath.c_str(), 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) {
+ // 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)
+{
+ // 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;
+
+ 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
diff --git a/lure/disk.h b/lure/disk.h
new file mode 100644
index 0000000000..9ce1c977ae
--- /dev/null
+++ b/lure/disk.h
@@ -0,0 +1,66 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_disk_h__
+#define __lure_disk_h__
+
+#include "common/stdafx.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 HEADER_ENTRY_UNUSED_ID 0xffff
+
+class Disk {
+private:
+ Common::String _gameDataPath;
+ uint8 _fileNum;
+ Common::File *_fileHandle;
+ FileEntry _entries[NUM_ENTRIES_IN_HEADER];
+
+ uint8 indexOf(uint16 id, bool suppressError = false);
+public:
+ Disk(const Common::String &gameDataPath);
+ ~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
diff --git a/lure/events.cpp b/lure/events.cpp
new file mode 100644
index 0000000000..2a920965b0
--- /dev/null
+++ b/lure/events.cpp
@@ -0,0 +1,159 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/events.h"
+
+namespace Lure {
+
+static Mouse *int_mouse = NULL;
+
+Mouse &Mouse::getReference() {
+ return *int_mouse;
+}
+
+Mouse::Mouse(OSystem &system): _system(system), _cursors(Disk::getReference().getEntry(CURSOR_RESOURCE_ID)) {
+ int_mouse = this;
+
+ _lButton = false;
+ _rButton = false;
+ _cursorNum = 0;
+ setCursorNum(0);
+}
+
+Mouse::~Mouse() {
+ delete _cursors;
+}
+
+void Mouse::handleEvent(OSystem::Event event) {
+ _x = (int16) event.mouse.x;
+ _y = (int16) event.mouse.y;
+
+ switch (event.type) {
+ case OSystem::EVENT_LBUTTONDOWN:
+ _lButton = true;
+ break;
+ case OSystem::EVENT_LBUTTONUP:
+ _lButton = false;
+ break;
+ case OSystem::EVENT_RBUTTONDOWN:
+ _rButton = true;
+ break;
+ case OSystem::EVENT_RBUTTONUP:
+ _rButton = false;
+ break;
+ default:
+ break;
+ }
+}
+
+
+void Mouse::cursorOn() {
+ _system.showMouse(true);
+}
+
+void Mouse::cursorOff() {
+ _system.showMouse(false);
+}
+
+void Mouse::setCursorNum(uint8 cursorNum) {
+ int hotspotX = 7, hotspotY = 7;
+ if ((cursorNum == CURSOR_ARROW) || (cursorNum == CURSOR_MENUBAR)) {
+ hotspotX = 0;
+ hotspotY = 0;
+ }
+
+ setCursorNum(cursorNum, hotspotX, hotspotY);
+}
+
+void Mouse::setCursorNum(uint8 cursorNum, int hotspotX, int hotspotY) {
+ _cursorNum = cursorNum;
+ byte *cursorAddr = _cursors->data() + (cursorNum * CURSOR_SIZE);
+ _system.setMouseCursor(cursorAddr, CURSOR_WIDTH, CURSOR_HEIGHT, hotspotX, hotspotY, 0);
+}
+
+void Mouse::setPosition(int newX, int newY) {
+ _system.warpMouse(newX, newY);
+}
+
+void Mouse::waitForRelease() {
+ Events &e = Events::getReference();
+
+ do {
+ e.pollEvent();
+ } while (!e.quitFlag && (lButton() || rButton()));
+}
+
+/*--------------------------------------------------------------------------*/
+
+static Events *int_events = NULL;
+
+Events::Events(OSystem &system, Mouse &mouse): _system(system), _mouse(mouse), quitFlag(false) {
+ int_events = this;
+}
+
+Events &Events::getReference() {
+ return *int_events;
+}
+
+
+bool Events::pollEvent() {
+ if (!_system.pollEvent(_event)) return false;
+
+ // Handle keypress
+ switch (_event.type) {
+ case OSystem::EVENT_QUIT:
+ quitFlag = true;
+ break;
+
+ case OSystem::EVENT_LBUTTONDOWN:
+ case OSystem::EVENT_LBUTTONUP:
+ case OSystem::EVENT_RBUTTONDOWN:
+ case OSystem::EVENT_RBUTTONUP:
+ case OSystem::EVENT_MOUSEMOVE:
+ case OSystem::EVENT_WHEELUP:
+ case OSystem::EVENT_WHEELDOWN:
+ _mouse.handleEvent(_event);
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+void Events::waitForPress() {
+ bool keyButton = false;
+ while (!keyButton) {
+ if (pollEvent()) {
+ if (_event.type == OSystem::EVENT_QUIT) return;
+ else if (_event.type == OSystem::EVENT_KEYDOWN) keyButton = true;
+ else if ((_event.type == OSystem::EVENT_LBUTTONDOWN) ||
+ (_event.type == OSystem::EVENT_RBUTTONDOWN)) {
+ keyButton = true;
+ _mouse.waitForRelease();
+ }
+ }
+ }
+}
+
+} // end of namespace Lure
diff --git a/lure/events.h b/lure/events.h
new file mode 100644
index 0000000000..9e6e9c540c
--- /dev/null
+++ b/lure/events.h
@@ -0,0 +1,78 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_events_h__
+#define __lure_events_h__
+
+#include "common/stdafx.h"
+#include "common/str.h"
+#include "lure/luredefs.h"
+#include "lure/disk.h"
+
+namespace Lure {
+
+class Mouse {
+private:
+ OSystem &_system;
+ MemoryBlock *_cursors;
+ uint8 _cursorNum;
+ int16 _x, _y;
+ bool _lButton, _rButton;
+public:
+ Mouse(OSystem &system);
+ ~Mouse();
+ static Mouse &getReference();
+ void handleEvent(OSystem::Event event);
+
+ void cursorOn();
+ void cursorOff();
+ void setCursorNum(uint8 cursorNum);
+ void setCursorNum(uint8 cursorNum, int hotspotX, int hotspotY);
+ uint8 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; }
+ void waitForRelease();
+};
+
+class Events {
+private:
+ OSystem &_system;
+ Mouse &_mouse;
+ OSystem::Event _event;
+public:
+ bool quitFlag;
+
+ Events(OSystem &system, Mouse &mouse);
+ static Events &getReference();
+
+ bool pollEvent();
+ void waitForPress();
+ OSystem::Event event() { return _event; }
+ OSystem::EventType type() { return _event.type; }
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/game.cpp b/lure/game.cpp
new file mode 100644
index 0000000000..d86b4a7bb3
--- /dev/null
+++ b/lure/game.cpp
@@ -0,0 +1,420 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/game.h"
+#include "lure/strings.h"
+#include "lure/room.h"
+#include "lure/system.h"
+#include "lure/debug-input.h"
+#include "lure/debug-methods.h"
+#include "lure/scripts.h"
+#include "lure/res_struct.h"
+
+namespace Lure {
+
+static Game *int_game = NULL;
+
+Game &Game::getReference() {
+ return *int_game;
+}
+
+Game::Game() {
+ int_game = this;
+ _slowSpeedFlag = true;
+ _soundFlag = true;
+ _remoteView = false;
+}
+
+void Game::nextFrame() {
+ Resources &r = Resources::getReference();
+ HotspotList::iterator i = r.activeHotspots().begin();
+ HotspotList::iterator iTemp;
+
+ // Note the somewhat more complicated loop style as a hotspot tick handler may
+ // unload the hotspot and accompanying record
+ for (; i != r.activeHotspots().end(); i = iTemp) {
+ iTemp = i;
+ ++iTemp;
+ Hotspot &h = *i.operator*();
+ h.tick();
+ }
+}
+
+void Game::execute() {
+ OSystem &system = System::getReference();
+ Room &r = Room::getReference();
+ Resources &res = Resources::getReference();
+ Events &events = Events::getReference();
+ Mouse &mouse = Mouse::getReference();
+ Screen &screen = Screen::getReference();
+ Menu &menu = Menu::getReference();
+ ValueTableData &fields = res.fieldList();
+
+ uint32 timerVal = system.getMillis();
+
+ screen.empty();
+ //_screen.resetPalette();
+ screen.setPaletteEmpty();
+
+ Script::execute(STARTUP_SCRIPT);
+
+ // Load the first room
+ r.setRoomNumber(1);
+
+ // Set the player direction
+ res.getActiveHotspot(PLAYER_ID)->setDirection(UP);
+
+ r.update();
+ mouse.setCursorNum(CURSOR_ARROW);
+ mouse.cursorOn();
+
+ while (!events.quitFlag) {
+ // If time for next frame, allow everything to update
+ if (system.getMillis() > timerVal + GAME_FRAME_DELAY) {
+ timerVal = system.getMillis();
+ nextFrame();
+ }
+ res.delayList().tick();
+ r.update();
+
+ if (events.pollEvent()) {
+ if (events.type() == OSystem::EVENT_KEYDOWN) {
+ uint16 roomNum = r.roomNumber();
+
+#ifdef LURE_DEBUG
+ if (events.event().kbd.keycode == 282) {
+ doDebugMenu();
+ continue;
+ }
+#endif
+
+ switch (events.event().kbd.ascii) {
+ case 27:
+ events.quitFlag = true;
+ break;
+
+#ifdef LURE_DEBUG
+ case '+':
+ while (++roomNum <= 51)
+ if (res.getRoom(roomNum) != NULL) break;
+ if (roomNum == 52) roomNum = 1;
+
+ r.leaveRoom();
+ r.setRoomNumber(roomNum);
+ break;
+
+ case '-':
+ if (roomNum == 1) roomNum = 55;
+ while (res.getRoom(--roomNum) == NULL) ;
+
+ r.leaveRoom();
+ r.setRoomNumber(roomNum);
+ break;
+
+ case '*':
+ res.getActiveHotspot(PLAYER_ID)->setRoomNumber(
+ r.roomNumber());
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
+ if (mouse.y() < MENUBAR_Y_SIZE)
+ {
+ if (mouse.getCursorNum() != CURSOR_MENUBAR) mouse.setCursorNum(CURSOR_MENUBAR);
+ if ((mouse.getCursorNum() == CURSOR_MENUBAR) && mouse.lButton())
+ {
+ uint8 responseId = menu.execute();
+ mouse.setCursorNum((mouse.y() < MENUBAR_Y_SIZE) ? CURSOR_MENUBAR : CURSOR_ARROW);
+ if (responseId != MENUITEM_NONE)
+ handleMenuResponse(responseId);
+ }
+ } else {
+ if (mouse.getCursorNum() == CURSOR_MENUBAR) mouse.setCursorNum(CURSOR_ARROW);
+
+ if (events.type() == OSystem::EVENT_MOUSEMOVE)
+ r.cursorMoved();
+
+ if (mouse.rButton()) handleRightClickMenu();
+ else if (mouse.lButton()) handleLeftClick();
+ }
+ }
+
+ uint16 destRoom = fields.getField(NEW_ROOM_NUMBER);
+ if (_remoteView && (destRoom != 0)) {
+ // Show a remote view of the specified room
+ uint16 currentRoom = r.roomNumber();
+ r.setRoomNumber(destRoom, true);
+
+ // This code eventually needs to be moved into the main loop so that,
+ // amongst other things, the tick handlers controlling animation can work
+ while (!events.quitFlag && !mouse.lButton() && !mouse.rButton()) {
+ if (events.pollEvent()) {
+ if ((events.type() == OSystem::EVENT_KEYDOWN) &&
+ (events.event().kbd.ascii == 27))
+ events.quitFlag = true;
+ if (events.type() == OSystem::EVENT_MOUSEMOVE)
+ r.cursorMoved();
+ }
+
+ if (system.getMillis() > timerVal + GAME_FRAME_DELAY) {
+ timerVal = system.getMillis();
+ nextFrame();
+ }
+ res.delayList().tick();
+ r.update();
+ }
+
+ fields.setField(NEW_ROOM_NUMBER, 0);
+ Hotspot *player = res.getActiveHotspot(PLAYER_ID);
+ player->setTickProc(0x5e44); // reattach player handler
+ _remoteView = false;
+ r.setRoomNumber(currentRoom);
+ }
+ }
+
+ r.leaveRoom();
+}
+
+#ifdef LURE_DEBUG
+
+#define NUM_DEBUG_ITEMS 4
+const char *debugItems[NUM_DEBUG_ITEMS] =
+ {"Toggle Info", "Set Room", "Show Active HS", "Show Room HS"};
+
+void Game::doDebugMenu() {
+ uint16 index = PopupMenu::Show(NUM_DEBUG_ITEMS, debugItems);
+ Room &r = Room::getReference();
+ Resources &res = Resources::getReference();
+
+ switch (index) {
+ case 0:
+ // Toggle co-ordinates
+ r.setShowInfo(!r.showInfo());
+ break;
+
+ case 1:
+ // Set room number:
+ uint32 roomNumber;
+ if (!input_integer("Enter room number:", roomNumber)) return;
+ if (res.getRoom(roomNumber))
+ r.setRoomNumber(roomNumber);
+ else
+ Dialog::show("The room does not exist");
+ break;
+
+ case 2:
+ // Show active hotspots
+ showActiveHotspots();
+ break;
+
+ case 3:
+ // Show hotspots in room
+ showRoomHotspots();
+ break;
+
+ default:
+ break;
+ }
+}
+
+#endif
+
+void Game::handleMenuResponse(uint8 selection) {
+ switch (selection) {
+ case MENUITEM_CREDITS:
+ doShowCredits();
+ break;
+
+ case MENUITEM_RESTART_GAME:
+ case MENUITEM_SAVE_GAME:
+ case MENUITEM_RESTORE_GAME:
+ break;
+
+ case MENUITEM_QUIT:
+ doQuit();
+ break;
+
+ case MENUITEM_TEXT_SPEED:
+ doTextSpeed();
+ break;
+
+ case MENUITEM_SOUND:
+ doSound();
+ }
+}
+
+void Game::handleRightClickMenu() {
+ Room &r = Room::getReference();
+ Resources &res = Resources::getReference();
+ ValueTableData &fields = Resources::getReference().fieldList();
+ Hotspot *player = res.getActiveHotspot(PLAYER_ID);
+ HotspotData *hotspot;
+ Action action;
+ uint32 actions;
+ uint16 itemId;
+
+ if (r.hotspotId() != 0) {
+ // Get hotspot actions
+ actions = r.hotspotActions();
+ } else {
+ // Standard actions - drink, examine, look, status
+ actions = 0x1184000;
+ }
+
+ // If no inventory items remove entries that require them
+ if (res.numInventoryItems() == 0)
+ actions &= 0xFEF3F9FD;
+
+ action = NONE;
+ hotspot = NULL;
+
+ bool breakFlag = false;
+ while (!breakFlag) {
+ action = PopupMenu::Show(actions);
+
+ switch (action) {
+ case LOOK:
+ case STATUS:
+ breakFlag = true;
+ break;
+
+ case GIVE:
+ case USE:
+ case EXAMINE:
+ case DRINK:
+ if (action != DRINK)
+ hotspot = res.getHotspot(r.hotspotId());
+ itemId = PopupMenu::ShowInventory();
+ breakFlag = (itemId != 0xffff);
+ if (breakFlag)
+ fields.setField(USE_HOTSPOT_ID, itemId);
+ break;
+
+ default:
+ hotspot = res.getHotspot(r.hotspotId());
+ breakFlag = true;
+ break;
+ }
+ }
+
+ // Set fields used by the script interpreter
+ fields.setField(CHARACTER_HOTSPOT_ID, PLAYER_ID);
+ if (hotspot) {
+ fields.setField(ACTIVE_HOTSPOT_ID, hotspot->hotspotId);
+ if ((action != USE) && (action != GIVE)) {
+ fields.setField(USE_HOTSPOT_ID, hotspot->hotspotId);
+ }
+ }
+
+ if (action != NONE)
+ player->doAction(action, hotspot);
+}
+
+void Game::handleLeftClick() {
+ Room &room = Room::getReference();
+ Mouse &mouse = Mouse::getReference();
+ Resources &resources = Resources::getReference();
+
+ if (room.hotspotId()) {
+ // Handle look at hotspot
+ HotspotData *hs = resources.getHotspot(room.hotspotId());
+ Hotspot *player = resources.getActiveHotspot(PLAYER_ID);
+ room.setAction(LOOK_AT);
+ room.update();
+ player->doAction(LOOK_AT, hs);
+ room.setAction(NONE);
+ } else {
+ // Walk to mouse click. TODO: still need to recognise other actions,
+ // such as to room exits or closing an on-screen floating dialog
+ Hotspot *hs = resources.getActiveHotspot(PLAYER_ID);
+ hs->walkTo(mouse.x(), mouse.y(), 0);
+ }
+}
+
+void Game::doShowCredits() {
+ Events &events = Events::getReference();
+ Mouse &mouse = Mouse::getReference();
+ Screen &screen = Screen::getReference();
+
+ mouse.cursorOff();
+ Palette p(CREDITS_RESOURCE_ID - 1);
+ Surface *s = Surface::getScreen(CREDITS_RESOURCE_ID);
+ screen.setPalette(&p);
+ s->copyToScreen(0, 0);
+ delete s;
+
+ events.waitForPress();
+
+ screen.resetPalette();
+ screen.update();
+ mouse.cursorOn();
+}
+
+void Game::doQuit() {
+ Mouse &mouse = Mouse::getReference();
+ Events &events = Events::getReference();
+ Screen &screen = Screen::getReference();
+
+ mouse.cursorOff();
+ Surface *s = Surface::newDialog(190, "Are you sure (y/n)?");
+ s->centerOnScreen();
+ delete s;
+
+ char key = '\0';
+ do {
+ if (events.pollEvent()) {
+ if (events.event().type == OSystem::EVENT_KEYDOWN) {
+ key = events.event().kbd.ascii;
+ if ((key >= 'A') && (key <= 'Z')) key += 'a' - 'A';
+ }
+ }
+ } while (((uint8) key != 27) && (key != 'y') && (key != 'n'));
+
+ events.quitFlag = key == 'y';
+ if (!events.quitFlag) {
+ screen.update();
+ mouse.cursorOn();
+ }
+}
+
+void Game::doTextSpeed() {
+ Menu &menu = Menu::getReference();
+
+ _slowSpeedFlag = !_slowSpeedFlag;
+ const char *pSrc = _slowSpeedFlag ? "Slow" : "Fast";
+ char *pDest = menu.getMenu(2).getEntry(1);
+ memcpy(pDest, pSrc, 4);
+}
+
+void Game::doSound() {
+ Menu &menu = Menu::getReference();
+
+ _soundFlag = !_soundFlag;
+ const char *pSrc = _soundFlag ? "on " : "off";
+ char *pDest = menu.getMenu(2).getEntry(2) + 6;
+ memcpy(pDest, pSrc, 3);
+}
+
+} // end of namespace Lure
diff --git a/lure/game.h b/lure/game.h
new file mode 100644
index 0000000000..d1957a6201
--- /dev/null
+++ b/lure/game.h
@@ -0,0 +1,64 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_game_h__
+#define __lure_game_h__
+
+#include "common/stdafx.h"
+#include "base/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"
+
+namespace Lure {
+
+class Game {
+private:
+ bool _slowSpeedFlag, _soundFlag;
+ bool _remoteView;
+
+ void handleMenuResponse(uint8 selection);
+ void handleRightClickMenu();
+ void handleLeftClick();
+public:
+ Game();
+ static Game &getReference();
+
+ void nextFrame();
+ void execute();
+ void setRemoteView() { _remoteView = true; }
+
+ // Menu item support methods
+ void doDebugMenu();
+ void doShowCredits();
+ void doQuit();
+ void doTextSpeed();
+ void doSound();
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/hotspots.cpp b/lure/hotspots.cpp
new file mode 100644
index 0000000000..6c158b929f
--- /dev/null
+++ b/lure/hotspots.cpp
@@ -0,0 +1,806 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/hotspots.h"
+#include "lure/decode.h"
+#include "lure/palette.h"
+#include "lure/disk.h"
+#include "lure/res.h"
+#include "lure/scripts.h"
+#include "lure/room.h"
+#include "lure/strings.h"
+#include "lure/res_struct.h"
+#include "lure/events.h"
+
+namespace Lure {
+
+Hotspot::Hotspot(HotspotData *res) {
+ _data = res;
+ _anim = NULL;
+ _frames = NULL;
+ _numFrames = 0;
+ _persistant = false;
+
+ _startX = res->startX;
+ _startY = res->startY;
+ _destX = res->startX;
+ _destY = res->startY;
+ _destHotspotId = 0;
+ _width = res->width;
+ _height = res->height;
+ _tickCtr = res->tickTimeout;
+
+ // Check for a hotspot override
+ HotspotOverrideData *hor = Resources::getReference().getHotspotOverride(res->hotspotId);
+
+ if (hor) {
+ _startX = hor->xs;
+ _startY = hor->ys;
+ if (hor->xe < hor->xs) _width = 0;
+ else _width = hor->xe - hor->xs + 1;
+ if (hor->ye < hor->ys) _height = 0;
+ else _height = hor->ye - hor->ys + 1;
+ }
+
+ if (_data->animRecordId != 0)
+ setAnimation(_data->animRecordId);
+
+ _tickHandler = HotspotTickHandlers::getHandler(_data->tickProcOffset);
+}
+
+Hotspot::~Hotspot() {
+ if (_frames) delete _frames;
+}
+
+void Hotspot::setAnimation(uint16 newAnimId) {
+ Resources &r = Resources::getReference();
+ HotspotAnimData *tempAnim;
+ if (newAnimId == 0) tempAnim = NULL;
+ else tempAnim = r.getAnimation(newAnimId);
+
+ setAnimation(tempAnim);
+}
+
+void Hotspot::setAnimation(HotspotAnimData *newRecord) {
+ Disk &r = Disk::getReference();
+ if (_frames) {
+ delete _frames;
+ _frames = NULL;
+ }
+ _anim = NULL;
+ _numFrames = 0;
+ _frameNumber = 0;
+ if (!newRecord) return;
+ if (!r.exists(newRecord->animId)) return;
+
+ _anim = newRecord;
+ MemoryBlock *src = Disk::getReference().getEntry(_anim->animId);
+
+ uint16 *numEntries = (uint16 *) src->data();
+ uint16 *headerEntry = (uint16 *) (src->data() + 2);
+
+ if ((*numEntries > 99) || (*numEntries == 0)) {
+ // Wobbly, likely something wrong with the resoure
+ _width = 1;
+ _numFrames = 1;
+ _frameNumber = 0;
+ _frames = new Surface(1, 1);
+ _frames->data().memset(_data->colourOffset, 0, 1);
+ return;
+ }
+
+ // Calculate total needed size for output and create memory block to hold it
+ uint32 totalSize = 0;
+ for (uint16 ctr = 0; ctr < *numEntries; ++ctr, ++headerEntry) {
+ totalSize += (*headerEntry + 31) / 32;
+ }
+ totalSize = (totalSize + 0x81) << 4;
+ MemoryBlock *dest = Memory::allocate(totalSize);
+
+ uint32 srcStart = (*numEntries + 1) * sizeof(uint16) + 6;
+ AnimationDecoder::decode_data(src, dest, srcStart);
+
+ _numFrames = *numEntries;
+ _frameNumber = 0;
+
+ _frames = new Surface(_data->width * _numFrames, _data->height);
+
+ _frames->data().memset(_data->colourOffset, 0, _frames->data().size());
+
+ byte *pSrc = dest->data() + 0x40;
+ byte *pDest;
+ headerEntry = (uint16 *) (src->data() + 2);
+ MemoryBlock &mDest = _frames->data();
+
+ for (uint16 frameCtr = 0; frameCtr < _numFrames; ++frameCtr, ++headerEntry) {
+
+ // Copy over the frame, applying the colour offset to each nibble
+ for (uint16 yPos = 0; yPos < _data->height; ++yPos) {
+ pDest = mDest.data() + (yPos * _numFrames + frameCtr) * _data->width;
+
+ for (uint16 ctr = 0; ctr < _data->width / 2; ++ctr) {
+ *pDest++ = _data->colourOffset + (*pSrc >> 4);
+ *pDest++ = _data->colourOffset + (*pSrc & 0xf);
+ ++pSrc;
+ }
+ }
+ }
+
+ delete src;
+ delete dest;
+}
+
+void Hotspot::copyTo(Surface *dest) {
+/*
+ int16 xPos = x();
+ int16 yPos = y();
+ uint16 hWidth = width();
+ uint16 hHeight = height();
+*/
+ int16 xPos = _data->startX;
+ int16 yPos = _data->startY;
+ uint16 hWidth = _data->width;
+ uint16 hHeight = _data->height;
+
+ Rect r(_frameNumber * hWidth, 0, (_frameNumber + 1) * hWidth - 1,
+ hHeight - 1);
+
+ if (yPos < 0) {
+ if (yPos + hHeight <= 0)
+ // Completely off screen, so don't display
+ return;
+
+ // Reduce the source rectangle to only the on-screen portion
+ r.top = -yPos;
+ yPos = 0;
+ }
+
+ if (xPos < 0) {
+ if (xPos + hWidth <= 0)
+ // Completely off screen, so don't display
+ return;
+
+ // Reduce the source rectangle to only the on-screen portion
+ r.left = -xPos;
+ xPos = 0;
+ }
+
+ if (xPos >= FULL_SCREEN_WIDTH)
+ return;
+ else if (xPos + hWidth > FULL_SCREEN_WIDTH)
+ r.right = (_frameNumber * hWidth) + (FULL_SCREEN_WIDTH - xPos) - 1;
+ if (yPos >= FULL_SCREEN_HEIGHT)
+ return;
+ else if (yPos + hHeight > FULL_SCREEN_HEIGHT)
+ r.bottom = FULL_SCREEN_HEIGHT - yPos - 1;
+
+ _frames->copyTo(dest, r, (uint16) xPos, (uint16) yPos, _data->colourOffset);
+}
+
+void Hotspot::incFrameNumber() {
+ ++_frameNumber;
+ if (_frameNumber >= _numFrames)
+ _frameNumber = 0;
+}
+
+bool Hotspot::isActiveAnimation() {
+ return ((_numFrames != 0) && (_data->layer != 0));
+}
+
+void Hotspot::setPosition(int16 newX, int16 newY) {
+ _startX = newX;
+ _startY = newY;
+ _data->startX = newX;
+ _data->startY = newY;
+}
+
+void Hotspot::setSize(uint16 newWidth, uint16 newHeight) {
+ _width = newWidth;
+ _height = newHeight;
+}
+
+bool Hotspot::executeScript() {
+ if (_data->sequenceOffset == 0)
+ return false;
+ else
+ return HotspotScript::execute(this);
+}
+
+void Hotspot::tick() {
+ _tickHandler(*this);
+}
+
+void Hotspot::setTickProc(uint16 newVal) {
+ _data->tickProcOffset = newVal;
+ _tickHandler = HotspotTickHandlers::getHandler(newVal);
+}
+
+
+void Hotspot::walkTo(int16 endPosX, int16 endPosY, uint16 destHotspot, bool immediate) {
+ _destX = endPosX;
+ _destY = endPosY - _data->height;
+
+ _destHotspotId = destHotspot;
+ if (immediate)
+ setPosition(_destX, _destY);
+}
+
+void Hotspot::setDirection(Direction dir) {
+ switch (dir) {
+ case UP:
+ setFrameNumber(_anim->upFrame);
+ break;
+ case DOWN:
+ setFrameNumber(_anim->downFrame);
+ break;
+ case LEFT:
+ setFrameNumber(_anim->leftFrame);
+ break;
+ case RIGHT:
+ setFrameNumber(_anim->rightFrame);
+ break;
+ default:
+ break;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+/* Hotspot action handling */
+/* */
+/*-------------------------------------------------------------------------*/
+
+uint16 validRoomExitHotspots[] = {0x2711, 0x2712, 0x2714, 0x2715, 0x2716, 0x2717,
+ 0x2718, 0x2719, 0x271A, 0x271E, 0x271F, 0x2720, 0x2721, 0x2722, 0x2725, 0x2726,
+ 0x2729, 0x272A, 0x272B, 0x272C, 0x272D, 0x272E, 0x272F, 0};
+
+bool Hotspot::isRoomExit(uint16 id) {
+ for (uint16 *p = &validRoomExitHotspots[0]; *p != 0; ++p)
+ if (*p == id) return true;
+ return false;
+}
+
+void Hotspot::doAction(Action action, HotspotData *hotspot) {
+ switch (action) {
+ case GET:
+ doGet(hotspot);
+ break;
+ case PUSH:
+ case PULL:
+ case OPERATE:
+ doOperate(hotspot, action);
+ break;
+ case OPEN:
+ doOpen(hotspot);
+ break;
+ case CLOSE:
+ doClose(hotspot);
+ break;
+ case LOCK:
+ doSimple(hotspot, LOCK);
+ break;
+ case UNLOCK:
+ doSimple(hotspot, UNLOCK);
+ break;
+ case USE:
+ doUse(hotspot);
+ break;
+ case GIVE:
+ doGive(hotspot);
+ break;
+ case TALK_TO:
+ doTalkTo(hotspot);
+ break;
+ case TELL:
+ doTell(hotspot);
+ break;
+ case LOOK:
+ doLook();
+ break;
+ case LOOK_AT:
+ doLookAt(hotspot);
+ break;
+ case LOOK_THROUGH:
+ doSimple(hotspot, LOOK_THROUGH);
+ break;
+ case ASK:
+ doAsk(hotspot);
+ break;
+ case DRINK:
+ doDrink();
+ break;
+ case STATUS:
+ doStatus();
+ break;
+ case BRIBE:
+ doBribe(hotspot);
+ break;
+ case EXAMINE:
+ doExamine();
+ break;
+ default:
+ doSimple(hotspot, action);
+ break;
+ }
+}
+
+void Hotspot::doGet(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, GET);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ return;
+ }
+
+ if (sequenceOffset != 0) {
+ uint16 result = Script::execute(sequenceOffset);
+
+ if (result == 1) return;
+ else if (result != 0) {
+ Dialog::showMessage(result, hotspotId());
+ return;
+ }
+ }
+
+ // Move hotspot into characters's inventory
+ hotspot->roomNumber = hotspotId();
+
+ if (hotspot->hotspotId < START_NONVISUAL_HOTSPOT_ID) {
+ // Deactive hotspot animation
+ Resources::getReference().deactivateHotspot(hotspot->hotspotId);
+ // Remove any 'on the ground' description for the hotspot
+ hotspot->descId2 = 0;
+ }
+}
+
+void Hotspot::doOperate(HotspotData *hotspot, Action action) {
+ Resources &res = Resources::getReference();
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, action);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset != 0) {
+ uint16 result = Script::execute(sequenceOffset);
+ if (result > 1)
+ Dialog::showMessage(result, hotspotId());
+ }
+}
+
+void Hotspot::doOpen(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+ RoomExitJoinData *joinRec;
+
+ if (isRoomExit(hotspot->hotspotId)) {
+ joinRec = res.getExitJoin(hotspot->hotspotId);
+ if (!joinRec->blocked) {
+ // Room exit is already open
+ Dialog::showMessage(4, hotspotId());
+ // TODO: jmp loc_1102
+ return;
+ }
+ }
+
+ // TODO: Call to sub_107 and checking the results, then sub_110
+
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, OPEN);
+ if (sequenceOffset >= 0x8000) {
+ // Message to display
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset != 0) {
+ // Otherwise handle script
+ uint16 result = Script::execute(sequenceOffset);
+
+ if (result == 0) {
+ joinRec = res.getExitJoin(hotspot->hotspotId);
+ if (joinRec->blocked) {
+ joinRec->blocked = 0;
+
+ if (hotspotId() != PLAYER_ID) {
+ // TODO: HS[44h]=3, HS[42h]W = 4
+ }
+ }
+ } else if (result != 1) {
+ // TODO: Figure out: if Hotspot-rec[60h] != 0, then set = 4
+ Dialog::showMessage(result, hotspotId());
+ }
+ }
+}
+
+void Hotspot::doClose(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+ RoomExitJoinData *joinRec;
+
+ if (isRoomExit(hotspot->hotspotId)) {
+ joinRec = res.getExitJoin(hotspot->hotspotId);
+ if (joinRec->blocked) {
+ // Room exit is already closed/blocked
+ Dialog::showMessage(3, hotspotId());
+ // TODO: jmp sub_129
+ return;
+ }
+ }
+
+ // TODO: Call to sub_107 and checking the results, then sub_110
+
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, CLOSE);
+
+ if (sequenceOffset >= 0x8000) {
+ // Message to display
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset != 0) {
+ // Otherwise handle script
+ uint16 result = Script::execute(sequenceOffset);
+
+ if (result != 0) {
+ Dialog::showMessage(result, hotspotId());
+ } else {
+ joinRec = res.getExitJoin(hotspot->hotspotId);
+ if (!joinRec->blocked) {
+ // Close the door
+ // TODO: Decode sub_183 - does check to see if door is 'jammed', but
+ // a cursory inspection seems to indicate that the routine is more
+ // concerned with checking if any character is blocking the door
+// if (!sub183(joinRec->0Dh) || !sub183(joinRec->0Fh)) {
+// Dialog::showMessage(2, hotspotId());
+// } else {
+ joinRec->blocked = 1;
+// }
+ }
+ }
+ }
+}
+
+void Hotspot::doUse(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+// uint16 usedId = res.fieldList().getField(USE_HOTSPOT_ID);
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, USE);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset == 0) {
+ Dialog::showMessage(17, hotspotId());
+ } else {
+ uint16 result = Script::execute(sequenceOffset);
+ if (result != 0)
+ Dialog::showMessage(result, hotspotId());
+ }
+}
+
+void Hotspot::doGive(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+ uint16 usedId = res.fieldList().getField(USE_HOTSPOT_ID);
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, GIVE);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else {
+ uint16 result = Script::execute(sequenceOffset);
+ if (result == 0x3E7) {
+ // TODO
+ } else if (result == 0) {
+ // Move item into character's inventory
+ HotspotData *usedItem = res.getHotspot(usedId);
+ usedItem->roomNumber = hotspotId();
+ } else if (result > 1) {
+ // TODO
+ }
+ }
+}
+
+void Hotspot::doTalkTo(HotspotData *hotspot) {
+ // TODO: extra checking at start
+ Resources &res = Resources::getReference();
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, TALK_TO);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset != 0) {
+ uint16 result = Script::execute(sequenceOffset);
+
+ if (result == 0) {
+ // Do talking with character
+ // TODO
+ Dialog::show("Still need to figure out talking");
+ }
+ }
+}
+
+void Hotspot::doTell(HotspotData *hotspot) {
+ // TODO
+}
+
+void Hotspot::doLook() {
+ Dialog::show(Room::getReference().descId());
+}
+
+void Hotspot::doLookAt(HotspotData *hotspot) {
+ Resources &res = Resources::getReference();
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, LOOK_AT);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else {
+ if (sequenceOffset != 0)
+ sequenceOffset = Script::execute(sequenceOffset);
+
+ if (sequenceOffset == 0) {
+ uint16 descId = (hotspot->descId2 != 0) ? hotspot->descId2 : hotspot->descId;
+ Dialog::show(descId);
+ }
+ }
+}
+
+void Hotspot::doAsk(HotspotData *hotspot) {
+ // TODO
+}
+
+void Hotspot::doDrink() {
+ Resources &res = Resources::getReference();
+ uint16 usedId = res.fieldList().getField(USE_HOTSPOT_ID);
+ HotspotData *hotspot = res.getHotspot(usedId);
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, DRINK);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset == 0) {
+ Dialog::showMessage(22, hotspotId());
+ } else {
+ uint16 result = Script::execute(sequenceOffset);
+ if (result == 0) {
+ // Item has been drunk, so remove item from game
+ hotspot->roomNumber = 0;
+ } else if (result != 1) {
+ Dialog::showMessage(result, hotspotId());
+ }
+ }
+}
+
+// doStatus
+// Handle the status window
+
+void Hotspot::doStatus() {
+ char buffer[MAX_DESC_SIZE];
+ uint16 numItems = 0;
+ StringData &strings = StringData::getReference();
+ Resources &resources = Resources::getReference();
+ Room &room = Room::getReference();
+
+ strings.getString(room.roomNumber(), buffer, NULL, NULL);
+ strcat(buffer, "\n\nYou are carrying ");
+
+ // Scan through the list and add in any items assigned to the player
+ HotspotDataList &list = resources.hotspotData();
+ HotspotDataList::iterator i;
+ for (i = list.begin(); i != list.end(); ++i) {
+ HotspotData *rec = *i;
+
+ if (rec->roomNumber == PLAYER_ID) {
+ if (numItems++ == 0) strcat(buffer, ": ");
+ else strcat(buffer, ", ");
+ strings.getString(rec->nameId, buffer + strlen(buffer), NULL, NULL);
+ }
+ }
+
+ // If there were no items, add in the word 'nothing'
+ if (numItems == 0) strcat(buffer, "nothing.");
+
+ // If the player has money, add it in
+ // TODO
+
+ // Display the dialog
+ Screen &screen = Screen::getReference();
+ Mouse &mouse = Mouse::getReference();
+ mouse.cursorOff();
+
+ Surface *s = Surface::newDialog(INFO_DIALOG_WIDTH, buffer);
+ s->copyToScreen(INFO_DIALOG_X, (FULL_SCREEN_HEIGHT-s->height())/2);
+
+ Events::getReference().waitForPress();
+ screen.update();
+ mouse.cursorOn();
+}
+
+void Hotspot::doBribe(HotspotData *hotspot) {
+ // TODO
+}
+
+void Hotspot::doExamine() {
+ Resources &res = Resources::getReference();
+ uint16 usedId = res.fieldList().getField(USE_HOTSPOT_ID);
+ HotspotData *hotspot = res.getHotspot(usedId);
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, EXAMINE);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else {
+ if (sequenceOffset != 0)
+ sequenceOffset = Script::execute(sequenceOffset);
+
+ if (sequenceOffset == 0) {
+ Dialog::show(hotspot->descId);
+ }
+ }
+}
+
+void Hotspot::doSimple(HotspotData *hotspot, Action action) {
+ Resources &res = Resources::getReference();
+ uint16 sequenceOffset = res.getHotspotAction(hotspot->actionsOffset, action);
+
+ if (sequenceOffset >= 0x8000) {
+ Dialog::showMessage(sequenceOffset, hotspotId());
+ } else if (sequenceOffset != 0) {
+ Script::execute(sequenceOffset);
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+HandlerMethodPtr HotspotTickHandlers::getHandler(uint16 procOffset) {
+ switch (procOffset) {
+ case 0x7F3A:
+ return standardAnimHandler;
+ case 0x7207:
+ return roomExitAnimHandler;
+ case 0x5e44:
+ return playerAnimHandler;
+ case 0x7F69:
+ return droppingTorchAnimHandler;
+ case 0x8009:
+ return fireAnimHandler;
+ case 0x8241:
+ return headAnimationHandler;
+ default:
+ return defaultHandler;
+ }
+}
+
+void HotspotTickHandlers::defaultHandler(Hotspot &h) {
+ // No handling done
+}
+
+void HotspotTickHandlers::standardAnimHandler(Hotspot &h) {
+ if (h.tickCtr() > 0)
+ h.setTickCtr(h.tickCtr() - 1);
+ else
+ h.executeScript();
+}
+
+void HotspotTickHandlers::roomExitAnimHandler(Hotspot &h) {
+ RoomExitJoinData *rec = Resources::getReference().getExitJoin(h.hotspotId());
+ if (!rec) return;
+ byte *currentFrame, *destFrame;
+
+ if (rec->hotspot1Id == h.hotspotId()) {
+ currentFrame = &rec->h1CurrentFrame;
+ destFrame = &rec->h1DestFrame;
+ } else {
+ currentFrame = &rec->h2CurrentFrame;
+ destFrame = &rec->h2DestFrame;
+ }
+
+ if ((rec->blocked != 0) && (*currentFrame != *destFrame)) {
+ // sub_178
+
+ ++*currentFrame;
+ if (*currentFrame != *destFrame) {
+ // cx=1 => sub_184
+ }
+ } else if ((rec->blocked == 0) && (*currentFrame != 0)) {
+ // sub_179
+ if (*currentFrame == *destFrame) {
+ // sub_184 and other stuff TODO
+ }
+ --*currentFrame;
+ }
+
+ h.setFrameNumber(*currentFrame);
+}
+
+void HotspotTickHandlers::playerAnimHandler(Hotspot &h) {
+ int16 xPos = h.x();
+ int16 yPos = h.y();
+ if ((xPos == h.destX()) && (yPos == h.destY())) return;
+ HotspotAnimData &anim = h.anim();
+ int16 xDiff = h.destX() - h.x();
+ int16 yDiff = h.destY() - h.y();
+
+ int16 xChange, yChange;
+ uint16 nextFrame;
+ MovementDataList *moves;
+
+ if ((yDiff < 0) && (xDiff <= 0)) moves = &anim.upFrames;
+ else if (xDiff < 0) moves = &anim.leftFrames;
+ else if (yDiff > 0) moves = &anim.downFrames;
+ else moves = &anim.rightFrames;
+
+ // Get movement amount and next frame number
+ moves->getFrame(h.frameNumber(), xChange, yChange, nextFrame);
+ xPos += xChange; yPos += yChange;
+
+ // Make sure that the move amount doesn't overstep the destination X/Y
+ if ((yDiff < 0) && (yPos < h.destY())) yPos = h.destY();
+ else if ((xDiff < 0) && (xPos < h.destX())) xPos = h.destX();
+ else if ((yDiff > 0) && (yPos > h.destY())) yPos = h.destY();
+ else if ((xDiff > 0) && (xPos > h.destX())) xPos = h.destX();
+
+ // Check to see if player has entered an exit area
+ RoomData *roomData = Resources::getReference().getRoom(h.roomNumber());
+ Room &room = Room::getReference();
+ bool charInRoom = room.roomNumber() == h.roomNumber();
+ RoomExitData *exitRec = roomData->exits.checkExits(xPos, yPos + h.height());
+
+ if (!exitRec) {
+ h.setPosition(xPos, yPos);
+ h.setFrameNumber(nextFrame);
+ } else {
+ h.setRoomNumber(exitRec->roomNumber);
+ h.walkTo(exitRec->x, exitRec->y, 0, true);
+ if (exitRec->direction != NO_DIRECTION)
+ h.setDirection(exitRec->direction);
+ if (charInRoom)
+ room.setRoomNumber(exitRec->roomNumber, false);
+ }
+}
+
+void HotspotTickHandlers::droppingTorchAnimHandler(Hotspot &h) {
+ if (h.tickCtr() > 0)
+ h.setTickCtr(h.tickCtr() - 1);
+ else {
+ bool result = h.executeScript();
+ if (result) {
+ // Changeover to the fire on the straw
+ Resources &res = Resources::getReference();
+ res.deactivateHotspot(h.hotspotId());
+ res.activateHotspot(0x41C);
+
+ // Enable the fire and activate it's animation
+ HotspotData *fire = res.getHotspot(0x418);
+ fire->flags |= 0x80;
+ fire->loadOffset = 0x7172;
+ res.activateHotspot(0x418);
+ }
+ }
+}
+
+void HotspotTickHandlers::fireAnimHandler(Hotspot &h) {
+ standardAnimHandler(h);
+ // TODO: figure out remainder of method
+}
+
+void HotspotTickHandlers::headAnimationHandler(Hotspot &h) {
+ Resources &res = Resources::getReference();
+ Hotspot *character = res.getActiveHotspot(PLAYER_ID);
+ uint16 frameNumber = 0;
+
+ if (character->y() < 79) {
+ //character = res.getActiveHotspot(RATPOUCH_ID);
+ frameNumber = 1;
+ } else {
+ if (character->x() < 72) frameNumber = 0;
+ else if (character->x() < 172) frameNumber = 1;
+ else frameNumber = 2;
+ }
+
+ h.setFrameNumber(frameNumber);
+}
+
+} // end of namespace Lure
diff --git a/lure/hotspots.h b/lure/hotspots.h
new file mode 100644
index 0000000000..3483522c96
--- /dev/null
+++ b/lure/hotspots.h
@@ -0,0 +1,137 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#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 {
+
+class Hotspot;
+
+typedef void(*HandlerMethodPtr)(Hotspot &h);
+
+class HotspotTickHandlers {
+private:
+ static void defaultHandler(Hotspot &h);
+ static void standardAnimHandler(Hotspot &h);
+ static void roomExitAnimHandler(Hotspot &h);
+ static void playerAnimHandler(Hotspot &h);
+ static void droppingTorchAnimHandler(Hotspot &h);
+ static void fireAnimHandler(Hotspot &h);
+ static void headAnimationHandler(Hotspot &h);
+
+public:
+ static HandlerMethodPtr getHandler(uint16 procOffset);
+};
+
+
+class Hotspot {
+private:
+ HotspotData *_data;
+ HotspotAnimData *_anim;
+ HandlerMethodPtr _tickHandler;
+ Surface *_frames;
+ int16 _startX, _startY;
+ uint16 _height, _width;
+ uint16 _numFrames;
+ uint16 _frameNumber;
+ uint16 _tickCtr;
+ bool _persistant;
+
+ int16 _destX, _destY;
+ uint16 _destHotspotId;
+public:
+ Hotspot(HotspotData *res);
+ ~Hotspot();
+
+ void setAnimation(uint16 newAnimId);
+ void setAnimation(HotspotAnimData *newRecord);
+ uint16 hotspotId() { return _data->hotspotId; }
+ Surface &frames() { return *_frames; }
+ HotspotAnimData &anim() { return *_anim; }
+ HotspotData &resource() { return *_data; }
+ uint16 numFrames() { return _numFrames; }
+ uint16 frameNumber() { return _frameNumber; }
+ void setFrameNumber(uint16 v) { _frameNumber = v; }
+ void incFrameNumber();
+ uint16 frameWidth() { return _width; }
+ int16 x() { return _startX; }
+ int16 y() { return _startY; }
+ int16 destX() { return _destX; }
+ int16 destY() { return _destY; }
+ uint16 destHotspotId() { return _destHotspotId; }
+ uint16 width() { return _width; }
+ uint16 height() { return _height; }
+ uint16 roomNumber() { return _data->roomNumber; }
+ uint16 script() { return _data->sequenceOffset; }
+ uint8 layer() { return _data->layer; }
+ uint16 tickCtr() { return _tickCtr; }
+ void setTickCtr(uint16 newVal) { _tickCtr = newVal; }
+ void setTickProc(uint16 newVal);
+ bool persistant() { return _persistant; }
+ void setPersistant(bool value) { _persistant = value; }
+ void setRoomNumber(uint16 roomNum) { _data->roomNumber = roomNum; }
+ bool isActiveAnimation();
+ void setPosition(int16 newX, int16 newY);
+ void setDestPosition(int16 newX, int16 newY) { _destX = newX; _destY = newY; }
+ void setSize(uint16 newWidth, uint16 newHeight);
+ void setScript(uint16 offset) { _data->sequenceOffset = offset; }
+ void setActions(uint32 newActions) { _data->actions = newActions; }
+
+ void copyTo(Surface *dest);
+ bool executeScript();
+ void tick();
+ void walkTo(int16 endPosX, int16 endPosY, uint16 destHotspot = 0, bool immediate = false);
+ void setDirection(Direction dir);
+
+ // Action set
+ void doAction(Action action, HotspotData *hotspot);
+ bool isRoomExit(uint16 id);
+ void doGet(HotspotData *hotspot);
+ void doOperate(HotspotData *hotspot, Action action);
+ 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();
+ void doLookAt(HotspotData *hotspot);
+ void doAsk(HotspotData *hotspot);
+ void doDrink();
+ void doStatus();
+ void doBribe(HotspotData *hotspot);
+ void doExamine();
+ void doSimple(HotspotData *hotspot, Action action);
+};
+
+typedef ManagedList<Hotspot *> HotspotList;
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/intro.cpp b/lure/intro.cpp
new file mode 100644
index 0000000000..58bca80239
--- /dev/null
+++ b/lure/intro.cpp
@@ -0,0 +1,151 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/intro.h"
+#include "lure/animseq.h"
+#include "lure/events.h"
+
+namespace Lure {
+
+struct AnimRecord {
+ uint16 resourceId;
+ uint8 paletteIndex;
+ bool initialPause;
+ bool endingPause;
+};
+
+static const uint16 start_screens[] = {0x18, 0x1A, 0x1E, 0x1C, 0};
+static const AnimRecord anim_screens[] = {{0x40, 0, true, true}, {0x42, 1, false, true},
+ {0x44, 2, false, false}, {0x24, 3, false, true}, {0x46, 3, false, false},
+ {0, 0, false, false}};
+
+// 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) {
+ _screen.screen().loadScreen(screenId);
+ _screen.update();
+ Palette p(paletteId);
+ _screen.paletteFadeIn(&p);
+
+ bool result = delay(delaySize);
+ if (Events::getReference().quitFlag) return true;
+
+ _screen.paletteFadeOut();
+ return result;
+}
+
+// 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.
+
+bool Introduction::delay(uint32 milliseconds) {
+ Events &events = Events::getReference();
+ uint32 delayCtr = _system.getMillis() + milliseconds;
+
+ while (_system.getMillis() < delayCtr) {
+ if (events.quitFlag) return true;
+
+ if (events.pollEvent()) {
+ if (events.type() == OSystem::EVENT_KEYDOWN)
+ return events.event().kbd.keycode == 27;
+ else if (events.type() == OSystem::EVENT_LBUTTONDOWN)
+ return false;
+ }
+
+ uint32 delayAmount = delayCtr - _system.getMillis();
+ if (delayAmount > 10) delayAmount = 10;
+ _system.delayMillis(delayAmount);
+ }
+ return false;
+}
+
+// show
+// Main method for the introduction sequence
+
+bool Introduction::show() {
+ _screen.setPaletteEmpty();
+
+ // Initial game company and then game screen
+
+ for (int ctr = 0; start_screens[ctr]; ++ctr)
+ if (showScreen(start_screens[ctr], start_screens[ctr] + 1, 5000))
+ return true;
+
+ AnimationSequence *anim;
+ bool result;
+
+ // Animated screens
+
+ PaletteCollection coll(0x32);
+ const AnimRecord *curr_anim = anim_screens;
+ for (; curr_anim->resourceId; ++curr_anim)
+ {
+ bool fadeIn = curr_anim == anim_screens;
+ anim = new AnimationSequence(_screen, _system, curr_anim->resourceId,
+ coll.getPalette(curr_anim->paletteIndex), fadeIn);
+ if (curr_anim->initialPause)
+ if (delay(12000)) return true;
+
+ result = false;
+ switch (anim->show()) {
+ case ABORT_NONE:
+ if (curr_anim->endingPause) {
+ result = delay(12000);
+ }
+ break;
+
+ case ABORT_END_INTRO:
+ result = true;
+ break;
+
+ case ABORT_NEXT_SCENE:
+ break;
+ }
+ delete anim;
+
+ if (result) return true;
+ }
+
+ // Show battle pictures one frame at a time
+
+ result = false;
+ anim = new AnimationSequence(_screen, _system, 0x48, coll.getPalette(4), false);
+ do {
+ result = delay(2000);
+ _screen.paletteFadeOut();
+ if (!result) result = delay(500);
+ if (result) break;
+ } while (anim->step());
+ delete anim;
+ if (result) return true;
+
+ // Show final introduction screen
+
+ showScreen(0x22, 0x21, 10000);
+
+ return false;
+}
+
+} // end of namespace Lure
diff --git a/lure/intro.h b/lure/intro.h
new file mode 100644
index 0000000000..d7934d938a
--- /dev/null
+++ b/lure/intro.h
@@ -0,0 +1,45 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_intro_h__
+#define __lure_intro_h__
+
+#include "lure/screen.h"
+
+namespace Lure {
+
+class Introduction {
+private:
+ Screen &_screen;
+ OSystem &_system;
+
+ bool showScreen(uint16 screenId, uint16 paletteId, uint16 delaySize);
+ bool delay(uint32 milliseconds);
+public:
+ Introduction(Screen &screen, OSystem &system): _screen(screen), _system(system) {};
+
+ bool show();
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/lure.cpp b/lure/lure.cpp
new file mode 100644
index 0000000000..3adc27eea9
--- /dev/null
+++ b/lure/lure.cpp
@@ -0,0 +1,306 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "backends/fs/fs.h"
+
+#include "base/gameDetector.h"
+#include "base/plugins.h"
+
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "common/md5.h"
+
+#include "sound/mixer.h"
+#include "sound/mididrv.h"
+#include "sound/audiostream.h"
+
+#include "lure/luredefs.h"
+#include "lure/surface.h"
+#include "lure/lure.h"
+#include "lure/intro.h"
+#include "lure/game.h"
+#include "lure/system.h"
+
+using namespace Lure;
+
+enum {
+ // We only compute MD5 of the first megabyte of our data files.
+ kMD5FileSizeLimit = 1024 * 1024
+};
+
+struct LureGameSettings {
+ const char *name;
+ const char *description;
+ byte id;
+ uint32 features;
+ const char *md5sum;
+ const char *checkFile;
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { name, description, features };
+ return dummy;
+ }
+};
+
+//
+static const LureGameSettings lure_games[] = {
+ { "lure", "Lure of the Temptress (Floppy, English)", GI_LURE, GF_ENGLISH | GF_FLOPPY,
+ "e45ea5d279a268c7d3c6524c2f63a2d2", "disk1.vga" },
+ { 0, 0, 0, 0, 0, 0 }
+};
+
+// Keep list of different supported games
+
+struct LureGameList {
+ const char *name;
+ const char *description;
+ uint32 features;
+ GameSettings toGameSettings() const {
+ GameSettings dummy = { name, description, features };
+ return dummy;
+ }
+};
+
+static const LureGameList lure_list[] = {
+ { "lure", "Lure of the Temptress", 0 },
+ { 0, 0, 0 }
+};
+
+GameList Engine_LURE_gameList() {
+ GameList games;
+ const LureGameList *g = lure_list;
+
+ while (g->name) {
+ games.push_back(g->toGameSettings());
+ g++;
+ }
+ return games;
+}
+
+DetectedGameList Engine_LURE_detectGames(const FSList &fslist) {
+ DetectedGameList detectedGames;
+ const LureGameSettings *g;
+ FSList::const_iterator file;
+
+ // Iterate over all files in the given directory
+ bool isFound = false;
+ for (file = fslist.begin(); file != fslist.end(); file++) {
+ if (file->isDirectory())
+ continue;
+
+ for (g = lure_games; g->name; g++) {
+ if (scumm_stricmp(file->displayName().c_str(), g->checkFile) == 0)
+ isFound = true;
+ }
+ if (isFound)
+ break;
+ }
+
+ if (file == fslist.end())
+ return detectedGames;
+
+ uint8 md5sum[16];
+ char md5str[32 + 1];
+
+ if (Common::md5_file(file->path().c_str(), md5sum, NULL, kMD5FileSizeLimit)) {
+ for (int i = 0; i < 16; i++) {
+ sprintf(md5str + i * 2, "%02x", (int)md5sum[i]);
+ }
+ for (g = lure_games; g->name; g++) {
+ if (strcmp(g->md5sum, (char *)md5str) == 0) {
+ detectedGames.push_back(g->toGameSettings());
+ }
+ }
+ if (detectedGames.isEmpty()) {
+ debug("Unknown MD5 (%s)! Please report the details (language, platform, etc.) of this game to the ScummVM team\n", md5str);
+
+ const LureGameList *g1 = lure_list;
+ while (g1->name) {
+ detectedGames.push_back(g1->toGameSettings());
+ g1++;
+ }
+ }
+ }
+ return detectedGames;
+}
+
+Engine *Engine_LURE_create(GameDetector *detector, OSystem *system) {
+ return new LureEngine(detector, system);
+}
+
+REGISTER_PLUGIN(LURE, "Lure of the Temptress Engine")
+
+namespace Lure {
+
+LureEngine::LureEngine(GameDetector *detector, OSystem *system): Engine(system) {
+ // Setup mixer
+/*
+ if (!_mixer->isReady()) {
+ warning("Sound initialization failed.");
+ }
+
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
+ _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
+*/
+ _features = 0;
+ _game = 0;
+}
+
+void LureEngine::detectGame() {
+ // Make sure all the needed files are present
+
+ if (!Common::File::exists(SUPPORT_FILENAME))
+ error("Missing %s - this is a custom file containing resources from the\n"
+ "Lure of the Temptress executable. See the documentation for creating it.",
+ SUPPORT_FILENAME);
+
+ for (uint8 fileNum = 1; fileNum <= 4; ++fileNum)
+ {
+ char sFilename[10];
+ sprintf(sFilename, "disk%d.vga", fileNum);
+
+ if (!Common::File::exists(sFilename))
+ error("Missing disk%d.vga", fileNum);
+ }
+
+ // Check the version of the lure.dat file
+ Common::File f;
+ if (!f.open(SUPPORT_FILENAME)) {
+ error("Error opening %s for validation", SUPPORT_FILENAME);
+ } else {
+ f.seek(0xbf * 8);
+ VersionStructure version;
+ f.read(&version, sizeof(VersionStructure));
+ f.close();
+
+ if (READ_LE_UINT16(&version.id) != 0xffff)
+ error("Error validating %s - file is invalid or out of date", SUPPORT_FILENAME);
+ else if ((version.vMajor != LURE_DAT_MAJOR) || (version.vMinor != LURE_DAT_MINOR))
+ error("Incorrect version of %s file - expected %d.%d but got %d.%d",
+ SUPPORT_FILENAME, LURE_DAT_MAJOR, LURE_DAT_MINOR,
+ version.vMajor, version.vMinor);
+ }
+
+ // Do an md5 check
+
+ uint8 md5sum[16];
+ char md5str[32 + 1];
+ const LureGameSettings *g;
+ bool found = false;
+
+ *md5str = 0;
+
+ for (g = lure_games; g->name; g++) {
+ if (!Common::File::exists(g->checkFile))
+ continue;
+
+ if (Common::md5_file(g->checkFile, md5sum, ConfMan.get("path").c_str(), kMD5FileSizeLimit)) {
+ for (int j = 0; j < 16; j++) {
+ sprintf(md5str + j * 2, "%02x", (int)md5sum[j]);
+ }
+ } else
+ continue;
+
+ if (strcmp(g->md5sum, (char *)md5str) == 0) {
+ _features = g->features;
+ _game = g->id;
+
+ if (g->description)
+ g_system->setWindowCaption(g->description);
+
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ debug("Unknown MD5 (%s)! Please report the details (language, platform, etc.) of this game to the ScummVM team", md5str);
+ _features = GF_LNGUNK || GF_FLOPPY;
+ _game = GI_LURE;
+ }
+}
+
+int LureEngine::init(GameDetector &detector) {
+ _system->beginGFXTransaction();
+ initCommonGFX(detector);
+ _system->initSize(FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
+ _system->endGFXTransaction();
+
+ detectGame();
+
+ _sys = new System(_system);
+ _disk = new Disk(_gameDataPath);
+ _resources = new Resources();
+ _strings = new StringData();
+ _screen = new Screen(*_system);
+ _mouse = new Mouse(*_system);
+ _events = new Events(*_system, *_mouse);
+ _menu = new Menu(*_system);
+ Surface::initialise();
+ _room = new Room();
+
+ return 0;
+}
+
+LureEngine::~LureEngine() {
+ Surface::deinitialise();
+ delete _room;
+ delete _menu;
+ delete _events;
+ delete _mouse;
+ delete _screen;
+ delete _strings;
+ delete _resources;
+ delete _disk;
+ delete _sys;
+}
+
+int LureEngine::go() {
+ // Show the introduction
+ Introduction *intro = new Introduction(*_screen, *_system);
+ intro->show();
+ delete intro;
+
+ // Play the game
+ if (!_events->quitFlag) {
+ // Play the game
+ Game *gameInstance = new Game();
+ gameInstance->execute();
+ delete gameInstance;
+ }
+
+ //quitGame();
+ return 0;
+}
+
+void LureEngine::errorString(const char *buf1, char *buf2) {
+ strcpy(buf2, buf1);
+}
+
+void LureEngine::quitGame() {
+ _system->quit();
+}
+
+} // End of namespace Lure
diff --git a/lure/lure.h b/lure/lure.h
new file mode 100644
index 0000000000..0a923cb72b
--- /dev/null
+++ b/lure/lure.h
@@ -0,0 +1,73 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __LURE_H__
+#define __LURE_H__
+
+#include "base/engine.h"
+#include "common/rect.h"
+#include "sound/mixer.h"
+#include "common/file.h"
+
+#include "lure/disk.h"
+#include "lure/res.h"
+#include "lure/screen.h"
+#include "lure/events.h"
+#include "lure/menu.h"
+#include "lure/system.h"
+#include "lure/strings.h"
+#include "lure/room.h"
+
+namespace Lure {
+
+class LureEngine : public Engine {
+private:
+ uint32 _features;
+ uint8 _game;
+ Disk *_disk;
+ Resources *_resources;
+ Screen *_screen;
+ Mouse *_mouse;
+ Events *_events;
+ Menu *_menu;
+ System *_sys;
+ StringData *_strings;
+ Room *_room;
+
+ void detectGame();
+public:
+ LureEngine(GameDetector *detector, OSystem *system);
+ ~LureEngine();
+
+ virtual int init(GameDetector &detector);
+ virtual int go();
+ virtual void errorString(const char *buf_input, char *buf_output);
+ void quitGame();
+
+ uint32 features() { return _features; }
+ uint8 game() { return _game; }
+ Disk &disk() { return *_disk; }
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/luredefs.h b/lure/luredefs.h
new file mode 100644
index 0000000000..d4175ce863
--- /dev/null
+++ b/lure/luredefs.h
@@ -0,0 +1,183 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __luredefs_h__
+#define __luredefs_h__
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+#include "common/list.h"
+
+namespace Lure {
+
+#define LURE_DEBUG 1
+
+#define READ_LE_INT16(x) (int16) READ_LE_UINT16(x)
+#define READ_LE_INT32(x) (int32) READ_LE_UINT32(x)
+
+enum {
+ GF_FLOPPY = 1 << 0,
+ GF_ENGLISH = 1 << 1,
+ GF_LNGUNK = 1 << 15
+};
+
+enum {
+ GI_LURE = 0
+};
+
+enum Action {
+ 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,
+ NONE = 0xffff
+};
+
+// Basic game dimensions
+#define FULL_SCREEN_WIDTH 320
+#define FULL_SCREEN_HEIGHT 200
+#define GAME_COLOURS 256
+#define SCREEN_SIZE (FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH)
+
+#define SUPPORT_FILENAME "lure.dat"
+#define LURE_DAT_MAJOR 1
+#define LURE_DAT_MINOR 1
+
+// 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
+// Palette colour increment amouns for palette fade in/outs
+#define PALETTE_FADE_INC_SIZE 4
+
+// Specifies the maximum buffer sized allocated for decoding animation data
+#define MAX_ANIM_DECODER_BUFFER_SIZE 200000
+
+#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
+#define CURSOR_ARROW 0
+#define CURSOR_DISK 1
+#define CURSOR_TIME_START 2
+#define CURSOR_TIME_END 9
+#define CURSOR_CROSS 10
+#define CURSOR_MENUBAR 17
+
+// Font details
+#define FONT_RESOURCE_ID 4
+#define NUM_CHARS_IN_FONT 122
+#define FONT_WIDTH 8
+#define FONT_HEIGHT 8
+
+// Menu constants
+#define MENUBAR_SELECTED_COLOUR 0xf7
+#define MENU_UNSELECTED_COLOUR 0xe2
+#define MENU_SELECTED_COLOUR 0xe3
+#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 DIALOG_EDGE_SIZE 9
+#define DIALOG_TEXT_COLOUR 0xe2
+#define DIALOG_WHITE_COLOUR 0xe3
+#define INFO_DIALOG_X 69
+#define INFO_DIALOG_Y 61
+#define INFO_DIALOG_WIDTH 191
+
+// 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 ROOM_DATA_RESOURCE_ID 0x3f04
+#define HOTSPOT_DATA_RESOURCE_ID 0x3f05
+#define HOTSPOT_OVERRIDE_DATA_RESOURCE_ID 0x3f06
+#define ROOM_EXITS_RESOURCE_ID 0x3f07
+#define ROOM_EXIT_JOINS_RESOURCE_ID 0x3f08
+#define ANIM_DATA_RESOURCE_ID 0x3f09
+#define SCRIPT_DATA_RESOURCE_ID 0x3f0a
+#define SCRIPT2_DATA_RESOURCE_ID 0x3f0b
+#define HOTSPOT_SCRIPT_LIST_RESOURCE_ID 0x3f0c
+#define MESSAGES_LIST_RESOURCE_ID 0x3f0d
+#define ACTION_LIST_RESOURCE_ID 0x3f0e
+
+// Script constants
+#define STARTUP_SCRIPT 0x23FC
+
+// Miscellaneous resources
+#define CREDITS_RESOURCE_ID 0x7800
+#define NAMES_RESOURCE_ID 9
+#define PLAYER_ID 0x3E8
+#define RATPOUCH_ID 0x3E9
+#define START_NONVISUAL_HOTSPOT_ID 0x7530
+
+// Milliseconds delay between game frames
+#define GAME_FRAME_DELAY 100
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/memory.cpp b/lure/memory.cpp
new file mode 100644
index 0000000000..a83858dce0
--- /dev/null
+++ b/lure/memory.cpp
@@ -0,0 +1,107 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/memory.h"
+#include "common/file.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;
+}
+
+uint8 *Memory::alloc(uint32 size) {
+ return (uint8 *) malloc(size);
+}
+
+void Memory::dealloc(uint8 *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::memset(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;
+ _data = (byte *) realloc(_data, size1);
+ if (!_data) error ("Failed reallocating memory block");
+}
+
+void MemoryBlock::saveToFile(const Common::String &filename) {
+ Common::File *f = new Common::File();
+ f->open(filename.c_str(), Common::File::kFileWriteMode);
+ f->write(_data, _size);
+ f->close();
+ delete f;
+}
+
+} // end of namespace Lure
diff --git a/lure/memory.h b/lure/memory.h
new file mode 100644
index 0000000000..e34f28893c
--- /dev/null
+++ b/lure/memory.h
@@ -0,0 +1,63 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_memory_h__
+#define __lure_memory_h__
+
+#include "common/stdafx.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 memset(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);
+ void saveToFile(const Common::String &filename);
+};
+
+class Memory {
+public:
+ static MemoryBlock *allocate(uint32 size);
+ static MemoryBlock *duplicate(MemoryBlock *src);
+ static uint8 *alloc(uint32 size);
+ static void dealloc(uint8 *block);
+};
+
+} // end of namspace Lure
+
+#endif
diff --git a/lure/menu.cpp b/lure/menu.cpp
new file mode 100644
index 0000000000..b1fbd4a5b4
--- /dev/null
+++ b/lure/menu.cpp
@@ -0,0 +1,415 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/menu.h"
+#include "lure/luredefs.h"
+#include "lure/decode.h"
+#include "lure/surface.h"
+#include "lure/system.h"
+#include "lure/res_struct.h"
+#include "lure/res.h"
+#include "lure/strings.h"
+
+namespace Lure {
+
+MenuRecord::MenuRecord(uint16 hsxstartVal, uint16 hsxendVal, uint16 xstartVal,
+ uint16 widthVal, const char *strings) {
+ _xstart = xstartVal; _width = widthVal;
+ _hsxstart = hsxstartVal; _hsxend = hsxendVal;
+
+ // Figure out the number of entries
+ const char *sPtr = strings;
+ _numEntries = 1;
+ while ((sPtr = strchr(sPtr, ',')) != NULL) {
+ ++_numEntries;
+ ++sPtr;
+ }
+
+ // Set up the list of entries
+ char *sCopy = strdup(strings);
+ char *s;
+ _entries = (char **) malloc(sizeof(char *) * _numEntries);
+ uint8 index = 0;
+ s = sCopy;
+ while (s != NULL) {
+ _entries[index++] = s;
+ s = strchr(s, ',');
+ if (s != NULL) *s++ = '\0'; // replace comma with NULL
+ }
+}
+
+MenuRecord::~MenuRecord() {
+ delete _entries[0]; // Delete string data for all the menu items
+ free(_entries); // Free the list
+}
+
+char *MenuRecord::getEntry(uint8 index) {
+ if (index >= _numEntries) error("Invalid menuitem index specified: %d", index);
+ return _entries[index];
+}
+
+/*--------------------------------------------------------------------------*/
+
+static Menu *int_menu = NULL;
+
+Menu::Menu(OSystem &system): _system(system), _screen(Screen::getReference()),
+ _events(Events::getReference()), _mouse(Mouse::getReference()) {
+ int_menu = this;
+
+ MemoryBlock *res = Disk::getReference().getEntry(5);
+ PictureDecoder decoder;
+ _menu = decoder.decode(res, SCREEN_SIZE);
+ delete res;
+
+ _menus[0] = new MenuRecord(40, 87, 20, 80, "Credits");
+ _menus[1] = new MenuRecord(127, 179, 100, 120, "Restart game,Save game,Restore game");
+ _menus[2] = new MenuRecord(224, 281, 210, 105, "Quit,Slow Text\x8b,Sound on ");
+ _selectedMenu = NULL;
+}
+
+Menu::~Menu() {
+ for (int ctr=0; ctr<NUM_MENUS; ++ctr) delete _menus[ctr];
+ delete _menu;
+}
+
+Menu &Menu::getReference() {
+ return *int_menu;
+}
+
+uint8 Menu::execute() {
+ _mouse.setCursorNum(CURSOR_ARROW);
+ _system.copyRectToScreen(_menu->data(), FULL_SCREEN_WIDTH, 0, 0, FULL_SCREEN_WIDTH, MENUBAR_Y_SIZE);
+ _system.updateScreen();
+
+ _selectedMenu = NULL;
+ _surfaceMenu = NULL;
+ _selectedIndex = 0;
+
+ while (_mouse.lButton()) {
+ if (_events.pollEvent()) {
+ // handle events
+ }
+
+ 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(_selectedMenu->xstart(), MENUBAR_Y_SIZE,
+ _surfaceMenu->width(), _surfaceMenu->height());
+ delete _surfaceMenu;
+ _surfaceMenu = NULL;
+ _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, MENU_UNSELECTED_COLOUR);
+ _surfaceMenu->copyToScreen(_selectedMenu->xstart(), MENUBAR_Y_SIZE);
+ }
+
+ _system.copyRectToScreen(_menu->data(), FULL_SCREEN_WIDTH, 0, 0, FULL_SCREEN_WIDTH, MENUBAR_Y_SIZE);
+ _system.updateScreen();
+ }
+ }
+
+ // 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 (_surfaceMenu) delete _surfaceMenu;
+
+ // Deselect the currently selected menu header
+ if (_selectedMenu)
+ toggleHighlight(_selectedMenu);
+
+ // Restore the previous screen
+ _screen.update();
+
+ if (_selectedMenu == NULL) 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;
+ }
+ } else {
+ switch (_selectedIndex) {
+ case 1: return MENUITEM_QUIT;
+ case 2: return MENUITEM_TEXT_SPEED;
+ case 3: return MENUITEM_SOUND;
+ }
+ }
+ 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 NULL;
+}
+
+uint8 Menu::getIndexAt(uint16 x, uint16 y) {
+ if (!_selectedMenu) return 0;
+
+ int ys = MENUBAR_Y_SIZE + DIALOG_EDGE_SIZE + 3;
+ int ye = MENUBAR_Y_SIZE + _surfaceMenu->height() - DIALOG_EDGE_SIZE - 3;
+ 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;
+}
+
+void Menu::toggleHighlight(MenuRecord *menuRec) {
+ 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] == MENUBAR_SELECTED_COLOUR) addr[x] = 0;
+ else if (addr[x] == 0) addr[x] = MENUBAR_SELECTED_COLOUR;
+ }
+ addr += FULL_SCREEN_WIDTH;
+ }
+}
+
+void Menu::toggleHighlightItem(uint8 index) {
+ byte *p = _surfaceMenu->data().data() + (DIALOG_EDGE_SIZE + 3 +
+ ((index - 1) * 8)) * _surfaceMenu->width();
+ uint32 numBytes = 8 * _surfaceMenu->width();
+
+ while (numBytes-- > 0) {
+ if (*p == MENU_UNSELECTED_COLOUR) *p = MENU_SELECTED_COLOUR;
+ else if (*p == MENU_SELECTED_COLOUR) *p = MENU_UNSELECTED_COLOUR;
+ ++p;
+ }
+
+ _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 *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, NULL, NULL);
+ }
+ }
+
+ uint16 result = Show(numItems, (const char **) itemNames);
+ if (result != 0xffff) result = idList[result];
+
+ for (itemCtr = 0; itemCtr < numItems; ++itemCtr)
+ free(itemNames[itemCtr]);
+
+ delete itemNames;
+ delete idList;
+ return result;
+}
+
+Action PopupMenu::Show(uint32 actionMask) {
+ int numEntries = 0;
+ uint32 v = actionMask;
+ int index;
+
+ for (index = 1; index <= EXAMINE; ++index, v >>= 1) {
+ if (v & 1) ++numEntries;
+ }
+
+ const char **strList = (const char **) Memory::alloc(sizeof(char *) * numEntries);
+
+ v = actionMask;
+ int strIndex = 0;
+ for (index=1; index<=EXAMINE; ++index, v >>= 1) {
+ if (v & 1)
+ strList[strIndex++] = actionList[index];
+ }
+
+ uint16 result = Show(numEntries, strList);
+
+ if (result == 0xffff) return NONE;
+
+ v = actionMask;
+ for (index = 1; index <= EXAMINE; ++index, v >>= 1) {
+ if (v & 1)
+ if (result-- == 0) return (Action) index;
+ }
+
+ delete strList;
+ return NONE;
+}
+
+Action PopupMenu::Show(int numEntries, Action *actions) {
+ const char **strList = (const char **) Memory::alloc(sizeof(char *) * numEntries);
+ Action *actionPtr = actions;
+ for (int index = 0; index < numEntries; ++index)
+ strList[index] = actionList[*actionPtr++];
+ uint16 result = Show(numEntries, strList);
+
+ delete strList;
+ if (result == 0xffff) return NONE;
+ else return actions[result];
+}
+
+uint16 PopupMenu::Show(int numEntries, const char *actions[]) {
+ if (numEntries == 0) return 0xffff;
+ Events &e = Events::getReference();
+ Mouse &mouse = Mouse::getReference();
+ OSystem &system = System::getReference();
+ Screen &screen = Screen::getReference();
+ Rect r;
+
+ mouse.cursorOff();
+ uint16 oldX = mouse.x();
+ uint16 oldY = mouse.y();
+ const uint16 yMiddle = FULL_SCREEN_HEIGHT / 2;
+ mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
+
+ // Round up number of lines in dialog to next odd number
+ uint16 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
+ Surface *s = new Surface(DIALOG_EDGE_SIZE * 2 + numCols * FONT_WIDTH,
+ DIALOG_EDGE_SIZE * 2 + numLines * FONT_HEIGHT);
+ s->createDialog();
+
+ int selectedIndex = 0;
+ bool refreshFlag = true;
+ r.left = DIALOG_EDGE_SIZE;
+ r.right = s->width() - DIALOG_EDGE_SIZE - 1;
+ r.top = DIALOG_EDGE_SIZE;
+ r.bottom = s->height() - DIALOG_EDGE_SIZE - 1;
+
+ for (;;) {
+ if (refreshFlag) {
+ // Set up the contents of the menu
+ s->fillRect(r, 0);
+
+ for (int index = 0; index < numLines; ++index) {
+ int actionIndex = selectedIndex - (numEntries / 2) + index;
+ if ((actionIndex >= 0) && (actionIndex < numEntries)) {
+ s->writeString(DIALOG_EDGE_SIZE, DIALOG_EDGE_SIZE + index * FONT_HEIGHT,
+ actions[actionIndex], true,
+ (index == (numLines / 2)) ? MENU_SELECTED_COLOUR : MENU_UNSELECTED_COLOUR,
+ false);
+ }
+ }
+
+ s->copyToScreen(0, yMiddle-(s->height() / 2));
+ system.updateScreen();
+ refreshFlag = false;
+ }
+
+ if (e.pollEvent()) {
+ if (e.quitFlag) {
+ selectedIndex = 0xffff;
+ break;
+ }
+
+ if (e.type() == OSystem::EVENT_KEYDOWN) {
+ byte ch = e.event().kbd.ascii;
+ uint16 keycode = e.event().kbd.keycode;
+
+ if (((keycode == 0x108) || (keycode == 0x111)) && (selectedIndex > 0)) {
+ --selectedIndex;
+ refreshFlag = true;
+ } else if (((keycode == 0x102) || (keycode == 0x112)) &&
+ (selectedIndex < numEntries-1)) {
+ ++selectedIndex;
+ refreshFlag = true;
+ } else if ((ch == '\xd') || (keycode == 0x10f)) {
+ break;
+ } else if (ch == '\x1b') {
+ selectedIndex = 0xffff;
+ break;
+ }
+
+ } else if (e.type() == OSystem::EVENT_MOUSEMOVE) {
+ if ((mouse.y() < yMiddle) && (selectedIndex > 0) &&
+ (yMiddle-mouse.y() >= POPMENU_CHANGE_SENSITIVITY)) {
+ --selectedIndex;
+ mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle);
+ refreshFlag = true;
+ } else if ((mouse.y() > yMiddle) && (selectedIndex < numEntries - 1) &&
+ (mouse.y()-yMiddle >= POPMENU_CHANGE_SENSITIVITY)) {
+ ++selectedIndex;
+ mouse.setPosition(FULL_SCREEN_WIDTH/2, yMiddle);
+ refreshFlag = true;
+ }
+
+ } else if (e.type() == OSystem::EVENT_LBUTTONDOWN) {
+ mouse.waitForRelease();
+ break;
+
+ } else if (e.type() == OSystem::EVENT_RBUTTONDOWN) {
+ mouse.waitForRelease();
+ selectedIndex = 0xffff;
+ break;
+ }
+ }
+ }
+
+ mouse.setPosition(oldX, oldY);
+ mouse.cursorOn();
+ screen.update();
+ return selectedIndex;
+}
+
+} // end of namespace Lure
diff --git a/lure/menu.h b/lure/menu.h
new file mode 100644
index 0000000000..96c6a1b2fd
--- /dev/null
+++ b/lure/menu.h
@@ -0,0 +1,92 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_menu_h__
+#define __lure_menu_h__
+
+#include "common/stdafx.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 {
+
+class MenuRecord {
+private:
+ uint16 _xstart, _width;
+ uint16 _hsxstart, _hsxend;
+ char **_entries;
+ uint8 _numEntries;
+public:
+ MenuRecord(uint16 hsxstartVal, uint16 hsxendVal, uint16 xstartVal,
+ uint16 widthVal, const char *strings);
+ ~MenuRecord();
+
+ uint16 xstart() { return _xstart; }
+ uint16 width() { return _width; }
+ uint16 hsxstart() { return _hsxstart; }
+ uint16 hsxend() { return _hsxend; }
+ uint8 numEntries() { return _numEntries; }
+ char **entries() { return _entries; }
+ char *getEntry(uint8 index);
+};
+
+class Menu {
+private:
+ OSystem &_system;
+ Screen &_screen;
+ Events &_events;
+ Mouse &_mouse;
+ 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(OSystem &system);
+ ~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();
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/module.mk b/lure/module.mk
new file mode 100644
index 0000000000..da1025ee6a
--- /dev/null
+++ b/lure/module.mk
@@ -0,0 +1,36 @@
+MODULE := lure
+
+MODULE_OBJS := \
+ lure/animseq.o \
+ lure/debug-input.o \
+ lure/debug-methods.o \
+ lure/decode.o \
+ lure/disk.o \
+ lure/events.o \
+ lure/game.o \
+ lure/hotspots.o \
+ lure/intro.o \
+ lure/lure.o \
+ lure/memory.o \
+ lure/menu.o \
+ lure/palette.o \
+ lure/res.o \
+ lure/res_struct.o \
+ lure/room.o \
+ lure/screen.o \
+ lure/scripts.o \
+ lure/strings.o \
+ lure/surface.o \
+ lure/system.o
+
+MODULE_DIRS += \
+ lure
+
+# This module can be built as a plugin
+ifdef BUILD_PLUGINS
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/common.rules
+
diff --git a/lure/palette.cpp b/lure/palette.cpp
new file mode 100644
index 0000000000..e4d8db0a41
--- /dev/null
+++ b/lure/palette.cpp
@@ -0,0 +1,142 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/palette.h"
+#include "common/util.h"
+
+namespace Lure {
+
+// Constructor
+// Defaults the palette to a full 256 entry palette
+
+Palette::Palette() {
+ _numEntries = GAME_COLOURS;
+ _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(uint8 numEntries1, const byte *data1, PaletteSource paletteSource) {
+ _numEntries = numEntries1;
+ _palette = Memory::allocate(_numEntries * 4);
+
+ if (data1) {
+ if (paletteSource == RGB64)
+ convertPalette(data1, _numEntries);
+ else
+ _palette->copyFrom(data1, 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) {
+ Disk &d = Disk::getReference();
+
+ MemoryBlock *srcData = d.getEntry(resourceId);
+ if (((srcData->size() % 3) != 0) || ((srcData->size() / 3) > GAME_COLOURS))
+ error("Specified resource %d is not a palette", resourceId);
+
+ _numEntries = srcData->size() / 3;
+ _palette = Memory::allocate(_numEntries * 4);
+ convertPalette(srcData->data(), _numEntries);
+ delete srcData;
+}
+
+void Palette::convertPalette(const byte *palette1, uint16 numEntries1) {
+ byte *pDest = _palette->data();
+ const byte *pSrc = palette1;
+
+ while (numEntries1-- > 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;
+ }
+}
+
+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);
+ uint32 palSize;
+ uint8 *data = resource->data();
+
+ 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);
+
+ 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
diff --git a/lure/palette.h b/lure/palette.h
new file mode 100644
index 0000000000..6fa2d9a1b7
--- /dev/null
+++ b/lure/palette.h
@@ -0,0 +1,68 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_palette_h__
+#define __lure_palette_h__
+
+#include "lure/luredefs.h"
+#include "lure/disk.h"
+#include "lure/memory.h"
+
+namespace Lure {
+
+enum PaletteSource {RGB, RGB64};
+
+class Palette {
+private:
+ MemoryBlock *_palette;
+ uint16 _numEntries;
+
+ void convertPalette(const byte *palette, uint16 numEntries);
+public:
+ Palette();
+ Palette(uint8 numEntries, const byte *data, PaletteSource paletteSource);
+ Palette(Palette &src);
+ Palette(uint16 resourceId);
+
+ 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
diff --git a/lure/res.cpp b/lure/res.cpp
new file mode 100644
index 0000000000..929b983f68
--- /dev/null
+++ b/lure/res.cpp
@@ -0,0 +1,399 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/res.h"
+#include "lure/disk.h"
+#include "lure/scripts.h"
+#include "lure/screen.h"
+
+namespace Lure {
+
+static Resources *int_resources = NULL;
+
+Resources &Resources::getReference() {
+ return *int_resources;
+}
+
+Resources::Resources() {
+ int_resources = this;
+ reloadData();
+}
+
+Resources::~Resources() {
+ // Delete any unremoved active hotspots
+ freeData();
+}
+
+void Resources::freeData() {
+ _activeHotspots.clear();
+ _roomData.clear();
+ _hotspotData.clear();
+ _hotspotOverrides.clear();
+ _animData.clear();
+ _exitJoins.clear();
+ _delayList.clear();
+
+ delete _paletteSubset;
+ delete _scriptData;
+ delete _script2Data;
+ delete _hotspotScriptData;
+ delete _messagesData;
+}
+
+struct AnimRecordTemp {
+ uint16 *offset;
+ MovementDataList *list;
+};
+
+void Resources::reloadData() {
+ Disk &d = Disk::getReference();
+ MemoryBlock *mb;
+ uint16 *offset, offsetVal;
+ int ctr;
+
+ // Get the palette subset data
+ _paletteSubset = new Palette(ALT_PALETTE_RESOURCE_ID);
+
+ // Load room data
+ mb = d.getEntry(ROOM_DATA_RESOURCE_ID);
+ offset = (uint16 *) mb->data();
+ for (ctr = 0; READ_LE_UINT16(offset) != 0xffff; ++ctr, ++offset) {
+ offsetVal = READ_LE_UINT16(offset);
+ if (offsetVal != 0) {
+ // Get room resource
+ RoomResource *rec = (RoomResource *) (mb->data() + offsetVal);
+ RoomData *newEntry = new RoomData(rec);
+ _roomData.push_back(newEntry);
+
+ if (rec->numExits > 0) {
+ RoomExitResource *exitRes = (RoomExitResource *)
+ (mb->data() + offsetVal + sizeof(RoomResource));
+
+ for (uint16 exitCtr = 0; exitCtr < rec->numExits; ++exitCtr, ++exitRes) {
+ RoomExitData *exit = new RoomExitData(exitRes);
+ newEntry->exits.push_back(exit);
+ }
+ }
+ }
+ }
+ delete mb;
+
+ // 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) {
+ RoomExitHotspotRecord *re = (RoomExitHotspotRecord *)
+ (mb->data() + offsetVal);
+ while (READ_LE_UINT16(&re->hotspotId) != 0xffff) {
+ RoomExitHotspotData *newEntry = new RoomExitHotspotData(re);
+ room->exitHotspots.push_back(newEntry);
+ ++re;
+ }
+ }
+ }
+ ++ctr;
+ }
+ delete mb;
+
+ // Load room joins
+ mb = d.getEntry(ROOM_EXIT_JOINS_RESOURCE_ID);
+ RoomExitJoinRecord *joinRec = (RoomExitJoinRecord *) mb->data();
+ while (READ_LE_UINT16(&joinRec->hotspot1Id) != 0xffff) {
+ RoomExitJoinData *newEntry = new RoomExitJoinData(joinRec);
+ _exitJoins.push_back(newEntry);
+ ++joinRec;
+ }
+ 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(newEntry);
+ ++hsRec;
+ }
+ 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(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(newEntry);
+
+ // Handle any direction frames
+ AnimRecordTemp dirEntries[4] = {
+ {&animRec->leftOffset, &newEntry->leftFrames},
+ {&animRec->rightOffset, &newEntry->rightFrames},
+ {&animRec->upOffset, &newEntry->upFrames},
+ {&animRec->downOffset, &newEntry->downFrames}};
+ for (int dirCtr = 0; dirCtr < 4; ++dirCtr) {
+ offsetVal = READ_LE_UINT16(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(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);
+ uint16 *v = (uint16 *) mb->data();
+ uint16 recordId;
+ 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(list);
+ }
+ delete mb;
+
+ _delayList.clear();
+
+ // Load miscellaneous data
+ _scriptData = d.getEntry(SCRIPT_DATA_RESOURCE_ID);
+ _script2Data = d.getEntry(SCRIPT2_DATA_RESOURCE_ID);
+ _messagesData = d.getEntry(MESSAGES_LIST_RESOURCE_ID);
+}
+
+RoomExitJoinData *Resources::getExitJoin(uint16 hotspotId) {
+ RoomExitJoinList::iterator i;
+
+ for (i = _exitJoins.begin(); i != _exitJoins.end(); ++i) {
+ RoomExitJoinData *rec = *i;
+ if ((rec->hotspot1Id == hotspotId) || (rec->hotspot2Id == hotspotId))
+ return rec;
+ }
+
+ return NULL;
+}
+
+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;
+ if (rec->roomNumber == roomNumber) return rec;
+ ++rec;
+ }
+
+ return NULL;
+}
+
+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);
+}
+
+HotspotData *Resources::getHotspot(uint16 hotspotId) {
+ HotspotDataList::iterator i;
+
+ for (i = _hotspotData.begin(); i != _hotspotData.end(); ++i) {
+ HotspotData *rec = *i;
+ if (rec->hotspotId == hotspotId) return rec;
+ }
+
+ return NULL;
+}
+
+Hotspot *Resources::getActiveHotspot(uint16 hotspotId) {
+ HotspotList::iterator i;
+
+ for (i = _activeHotspots.begin(); i != _activeHotspots.end(); ++i) {
+ Hotspot *rec = *i;
+ if (rec->hotspotId() == hotspotId) return rec;
+ }
+
+ return NULL;
+}
+
+
+HotspotOverrideData *Resources::getHotspotOverride(uint16 hotspotId) {
+ HotspotOverrideList::iterator i;
+
+ for (i = _hotspotOverrides.begin(); i != _hotspotOverrides.end(); ++i) {
+ HotspotOverrideData *rec = *i;
+ if (rec->hotspotId == hotspotId) return rec;
+ }
+
+ return NULL;
+}
+
+HotspotAnimData *Resources::getAnimation(uint16 animRecordId) {
+ HotspotAnimList::iterator i;
+
+ for (i = _animData.begin(); i != _animData.end(); ++i) {
+ HotspotAnimData *rec = *i;
+ if (rec->animRecordId == animRecordId) return rec;
+ }
+
+ return NULL;
+}
+
+uint16 Resources::getHotspotAction(uint16 actionsOffset, Action action) {
+ HotspotActionList *list = _actionsList.getActions(actionsOffset);
+ if (!list) return 0;
+ return list->getActionOffset(action);
+}
+
+HotspotActionList *Resources::getHotspotActions(uint16 actionsOffset) {
+ return _actionsList.getActions(actionsOffset);
+}
+
+void Resources::activateHotspot(uint16 hotspotId) {
+ HotspotData *res = getHotspot(hotspotId);
+ if (!res) return;
+ res->roomNumber &= 0x7fff; // clear any suppression bit in room #
+
+ // Make sure that the hotspot isn't already active
+ HotspotList::iterator i = _activeHotspots.begin();
+ bool found = false;
+
+ for (; i != _activeHotspots.end(); ++i) {
+ Hotspot &h = *i.operator*();
+ if (h.hotspotId() == res->hotspotId) {
+ found = true;
+ break;
+ }
+ }
+ if (found) return;
+
+ // 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;
+
+ switch (res->loadOffset) {
+ case 0x3afe:
+ // Copy protection check - since the game is freeware now,
+ // don't bother with it
+ loadFlag = false;
+ break;
+
+ case 0x41BD:
+ // Empty handler used to prevent loading hotspots that
+ // are yet to be active (such as the straw fire)
+ loadFlag = false;
+ break;
+
+ case 0x7172:
+ case 0x7167:
+ // Standard animation load
+ break;
+
+ case 0x88ac:
+ // 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 proc %d",
+ res->hotspotId, res->loadOffset);
+ }
+
+ if (loadFlag) {
+ Hotspot *hotspot = addHotspot(hotspotId);
+// if (res->loadOffset == 0x7167) hotspot->setPersistant(true);
+ // DEBUG - for now only keep certain hotspots active
+ hotspot->setPersistant((res->hotspotId >= 0x3e8) && (res->hotspotId <= 0x3ea));
+ }
+ }
+}
+
+Hotspot *Resources::addHotspot(uint16 hotspotId) {
+ Hotspot *hotspot = new Hotspot(getHotspot(hotspotId));
+ _activeHotspots.push_back(hotspot);
+ return hotspot;
+}
+
+void Resources::deactivateHotspot(uint16 hotspotId) {
+ HotspotList::iterator i = _activeHotspots.begin();
+
+ while (i != _activeHotspots.end()) {
+ Hotspot *h = *i;
+ if (h->hotspotId() == hotspotId)
+ i = _activeHotspots.erase(i);
+ else
+ i++;
+ }
+}
+
+uint16 Resources::numInventoryItems() {
+ uint16 numItems = 0;
+ HotspotDataList &list = _hotspotData;
+ HotspotDataList::iterator i;
+ for (i = list.begin(); i != list.end(); ++i) {
+ HotspotData *rec = *i;
+ if (rec->roomNumber == PLAYER_ID) ++numItems;
+ }
+
+ return numItems;
+}
+
+} // end of namespace Lure
diff --git a/lure/res.h b/lure/res.h
new file mode 100644
index 0000000000..66af55170f
--- /dev/null
+++ b/lure/res.h
@@ -0,0 +1,94 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_res_h__
+#define __lure_res_h__
+
+#include "lure/luredefs.h"
+#include "lure/memory.h"
+#include "common/list.h"
+#include "lure/res_struct.h"
+#include "lure/hotspots.h"
+#include "lure/palette.h"
+
+namespace Lure {
+
+class Resources {
+private:
+ Common::RandomSource _rnd;
+ Palette *_paletteSubset;
+ RoomDataList _roomData;
+ HotspotDataList _hotspotData;
+ HotspotOverrideList _hotspotOverrides;
+ HotspotAnimList _animData;
+ MemoryBlock *_scriptData;
+ MemoryBlock *_script2Data;
+ MemoryBlock *_messagesData;
+ uint16 *_hotspotScriptData;
+ RoomExitJoinList _exitJoins;
+ HotspotList _activeHotspots;
+ ValueTableData _fieldList;
+ HotspotActionSet _actionsList;
+ SequenceDelayList _delayList;
+
+ void freeData();
+public:
+ Resources();
+ ~Resources();
+ static Resources &getReference();
+ void reloadData();
+
+ byte *getResource(uint16 resId);
+ RoomDataList &roomData() { return _roomData; }
+ RoomData *getRoom(uint16 roomNumber);
+ void insertPaletteSubset(Palette &p);
+
+ 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 random() { return _rnd.getRandomNumber(65536) & 0xffff; }
+ HotspotData *getHotspot(uint16 hotspotId);
+ Hotspot *getActiveHotspot(uint16 hotspotId);
+ HotspotOverrideData *getHotspotOverride(uint16 hotspotId);
+ HotspotAnimData *getAnimation(uint16 animRecordId);
+ RoomExitJoinList &exitJoins() { return _exitJoins; }
+ RoomExitJoinData *getExitJoin(uint16 hotspotId);
+ uint16 getHotspotAction(uint16 actionsOffset, Action action);
+ HotspotActionList *getHotspotActions(uint16 actionsOffset);
+ ValueTableData &fieldList() { return _fieldList; }
+ SequenceDelayList &delayList() { return _delayList; }
+ uint16 numInventoryItems();
+
+ void activateHotspot(uint16 hotspotId);
+ Hotspot *addHotspot(uint16 hotspotId);
+ void deactivateHotspot(uint16 hotspotId);
+
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/res_struct.cpp b/lure/res_struct.cpp
new file mode 100644
index 0000000000..a90e86d0bc
--- /dev/null
+++ b/lure/res_struct.cpp
@@ -0,0 +1,309 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/res.h"
+#include "lure/disk.h"
+#include "lure/scripts.h"
+#include "lure/system.h"
+
+namespace Lure {
+
+const char *actionList[] = {NULL, "Get", NULL, "Push", "Pull", "Operate", "Open",
+ "Close", "Lock", "Unlock", "Use", "Give", "Talk to", "Tell", "Buy",
+ "Look", "Look at", "Look through", "Ask", NULL, "Drink", "Status",
+ "Go to", "Return", "Bribe", "Examine"};
+
+// Room data holding class
+
+RoomData::RoomData(RoomResource *rec) {
+ roomNumber = READ_LE_UINT16(&rec->roomNumber);
+ descId = READ_LE_UINT16(&rec->descId);
+ sequenceOffset = READ_LE_UINT16(&rec->sequenceOffset);
+ numLayers = READ_LE_UINT16(&rec->numLayers);
+
+ for (int ctr = 0; ctr < 4; ++ctr)
+ layers[ctr] = READ_LE_UINT16(&rec->layers[ctr]);
+}
+
+// Room exit hotspot area holding class
+
+RoomExitHotspotData::RoomExitHotspotData(RoomExitHotspotRecord *rec) {
+ hotspotId = READ_LE_UINT16(&rec->hotspotId);
+ xs = READ_LE_INT16(&rec->xs);
+ ys = READ_LE_INT16(&rec->ys);
+ xe = READ_LE_INT16(&rec->xe);
+ ye = READ_LE_INT16(&rec->ye);
+ cursorNum = rec->cursorNum;
+ destRoomNumber = READ_LE_UINT16(&rec->destRoomNumber);
+}
+
+// Room exit class
+
+RoomExitData::RoomExitData(RoomExitResource *rec) {
+ xs = rec->xs;
+ ys = rec->ys;
+ xe = rec->xe;
+ ye = rec->ye;
+ sequenceOffset = rec->sequenceOffset;
+ roomNumber = rec->newRoom;
+ x = rec->newRoomX;
+ y = rec->newRoomY;
+
+ switch (rec->direction) {
+ case 0x80:
+ direction = UP;
+ break;
+ case 0x40:
+ direction = DOWN;
+ break;
+ case 0x20:
+ direction = LEFT;
+ break;
+ case 0x10:
+ direction = RIGHT;
+ break;
+ default:
+ direction = NO_DIRECTION;
+ break;
+ }
+}
+
+bool RoomExitData::insideRect(int16 xp, int16 yp) {
+ return ((xp >= xs) && (xp <= xe) && (yp >= ys) && (yp <= ye));
+}
+
+RoomExitData *RoomExitList::checkExits(int16 xp, int16 yp) {
+ iterator i;
+ for (i = begin(); i != end(); i++) {
+ RoomExitData *rec = *i;
+ if (rec->insideRect(xp, yp)) return rec;
+ }
+ return NULL;
+}
+
+// Room exit joins class
+
+RoomExitJoinData::RoomExitJoinData(RoomExitJoinRecord *rec) {
+ hotspot1Id = READ_LE_UINT16(&rec->hotspot1Id);
+ h1CurrentFrame = rec->h1CurrentFrame;
+ h1DestFrame = rec->h1DestFrame;
+ h1Unknown = READ_LE_UINT16(&rec->h1Unknown);
+ hotspot2Id = READ_LE_UINT16(&rec->hotspot2Id);
+ h2CurrentFrame = rec->h2CurrentFrame;
+ h2DestFrame = rec->h2DestFrame;
+ h2Unknown = READ_LE_UINT16(&rec->h2Unknown);
+ blocked = rec->blocked;
+ unknown = rec->unknown;
+}
+
+// Hotspot action record
+
+HotspotActionData::HotspotActionData(HotspotActionRecord *rec) {
+ action = (Action) rec->action;
+ sequenceOffset = READ_LE_UINT16(&rec->sequenceOffset);
+}
+
+uint16 HotspotActionList::getActionOffset(Action action) {
+ iterator i;
+ for (i = begin(); i != end(); ++i) {
+ HotspotActionData *rec = *i;
+ if (rec->action == action) return rec->sequenceOffset;
+ }
+
+ return 0;
+}
+
+
+// Hotspot data
+
+HotspotData::HotspotData(HotspotResource *rec) {
+ hotspotId = READ_LE_UINT16(&rec->hotspotId);
+ nameId = READ_LE_UINT16(&rec->nameId);
+ descId = READ_LE_UINT16(&rec->descId);
+ descId2 = READ_LE_UINT16(&rec->descId2);
+ actions = READ_LE_UINT32(&rec->actions);
+ actionsOffset = READ_LE_UINT16(&rec->actionsOffset);
+ flags = (byte) (actions >> 24) & 0xf0;
+ actions &= 0xfffffff;
+
+ roomNumber = READ_LE_UINT16(&rec->roomNumber);
+ layer = rec->layer;
+ scriptLoadFlag = rec->scriptLoadFlag;
+ loadOffset = READ_LE_UINT16(&rec->loadOffset);
+ startX = READ_LE_INT16(&rec->startX);
+ startY = READ_LE_INT16(&rec->startY);
+ width = READ_LE_UINT16(&rec->width);
+ height = READ_LE_UINT16(&rec->height);
+ colourOffset = READ_LE_UINT16(&rec->colourOffset);
+ animRecordId = READ_LE_UINT16(&rec->animRecordId);
+ sequenceOffset = READ_LE_UINT16(&rec->sequenceOffset);
+ tickProcOffset = READ_LE_UINT16(&rec->tickProcOffset);
+ tickTimeout = READ_LE_UINT16(&rec->tickTimeout);
+}
+
+// Hotspot override data
+
+HotspotOverrideData::HotspotOverrideData(HotspotOverrideResource *rec) {
+ hotspotId = READ_LE_UINT16(&rec->hotspotId);
+ xs = READ_LE_INT16(&rec->xs);
+ ys = READ_LE_INT16(&rec->ys);
+ xe = READ_LE_INT16(&rec->xe);
+ ye = READ_LE_INT16(&rec->ye);
+}
+
+// Hotspot animation movement frame
+
+MovementData::MovementData(MovementResource *rec) {
+ frameNumber = READ_LE_UINT16(&rec->frameNumber);
+ xChange = READ_LE_INT16(&rec->xChange);
+ yChange = READ_LE_INT16(&rec->yChange);
+}
+
+// List of movement frames
+
+bool MovementDataList::getFrame(uint16 currentFrame, int16 &xChange,
+ int16 &yChange, uint16 &nextFrame) {
+ if (isEmpty()) return false;
+ bool foundFlag = false;
+ iterator i;
+
+ for (i = begin(); i != end(); ++i) {
+ MovementData *rec = *i;
+ if (foundFlag || (i == begin())) {
+ xChange = rec->xChange;
+ yChange = rec->yChange;
+ nextFrame = rec->frameNumber;
+ if (foundFlag) return true;
+ }
+ if (rec->frameNumber == currentFrame) foundFlag = true;
+ }
+
+ return true;
+}
+
+
+// Hotspot animation data
+
+HotspotAnimData::HotspotAnimData(HotspotAnimResource *rec) {
+ animRecordId = READ_LE_UINT16(&rec->animRecordId);
+ animId = READ_LE_UINT16(&rec->animId);
+ flags = READ_LE_UINT16(&rec->flags);
+
+ upFrame = rec->upFrame;
+ downFrame = rec->downFrame;
+ leftFrame = rec->leftFrame;
+ rightFrame = rec->rightFrame;
+}
+
+// Hotspot action lists
+
+HotspotActionList::HotspotActionList(uint16 id, byte *data) {
+ recordId = id;
+ uint16 numItems = READ_LE_UINT16(data);
+ data += 2;
+
+ HotspotActionRecord *actionRec = (HotspotActionRecord *) data;
+
+ for (int actionCtr = 0; actionCtr < numItems; ++actionCtr, ++actionRec) {
+ HotspotActionData *actionEntry = new HotspotActionData(actionRec);
+ push_back(actionEntry);
+ }
+}
+
+HotspotActionList *HotspotActionSet::getActions(uint16 recordId) {
+ HotspotActionSet::iterator i;
+ for (i = begin(); i != end(); ++i) {
+ HotspotActionList *list = *i;
+ if (list->recordId == recordId) return list;
+ }
+
+ return NULL;
+}
+
+// The following classes hold any sequence offsets that are being delayed
+
+SequenceDelayData::SequenceDelayData(uint16 delay, uint16 seqOffset) {
+ OSystem &system = System::getReference();
+
+ _timeoutCtr = system.getMillis() + delay;
+ _sequenceOffset = seqOffset;
+}
+
+void SequenceDelayList::addSequence(uint16 delay, uint16 seqOffset) {
+ SequenceDelayData *entry = new SequenceDelayData(delay, seqOffset);
+ push_back(entry);
+}
+
+void SequenceDelayList::tick() {
+ uint32 currTime = System::getReference().getMillis();
+ SequenceDelayList::iterator i;
+
+ for (i = begin(); i != end(); i++) {
+ SequenceDelayData *entry = *i;
+ if (entry->_timeoutCtr >= currTime) {
+ uint16 seqOffset = entry->_sequenceOffset;
+ erase(i);
+ Script::execute(seqOffset);
+ return;
+ }
+ }
+}
+
+// Field list and miscellaneous variables
+
+ValueTableData::ValueTableData() {
+ _numGroats = 0;
+
+ for (uint16 index = 0; index < NUM_VALUE_FIELDS; ++index)
+ _fieldList[index] = 0;
+}
+
+bool ValueTableData::isKnownField(uint16 fieldIndex) {
+ return (fieldIndex <= 8) || (fieldIndex == 10) || (fieldIndex == 15) ||
+ (fieldIndex == 18) || (fieldIndex == 20);
+}
+
+uint16 ValueTableData::getField(uint16 fieldIndex) {
+ if (fieldIndex > NUM_VALUE_FIELDS)
+ error("Invalid field index specified %d", fieldIndex);
+ if (!isKnownField(fieldIndex))
+ warning("Unknown field index %d in GET_FIELD opcode", fieldIndex);
+ return _fieldList[fieldIndex];
+}
+
+uint16 ValueTableData::getField(FieldName fieldName) {
+ return getField((uint16) fieldName);
+}
+
+void ValueTableData::setField(uint16 fieldIndex, uint16 value) {
+ if (fieldIndex > NUM_VALUE_FIELDS)
+ error("Invalid field index specified %d", fieldIndex);
+ _fieldList[fieldIndex] = value;
+ if (!isKnownField(fieldIndex))
+ warning("Unknown field index %d in SET_FIELD opcode", fieldIndex);
+}
+
+void ValueTableData::setField(FieldName fieldName, uint16 value) {
+ setField((uint16) fieldName, value);
+}
+
+} // end of namespace Lure
diff --git a/lure/res_struct.h b/lure/res_struct.h
new file mode 100644
index 0000000000..418fb9fbdd
--- /dev/null
+++ b/lure/res_struct.h
@@ -0,0 +1,401 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_resstruct_h__
+#define __lure_resstruct_h__
+
+#include "lure/luredefs.h"
+#include "common/list.h"
+
+namespace Lure {
+
+extern const char *actionList[];
+
+/*-------------------------------------------------------------------------*/
+/* Structure definitions */
+/* */
+/*-------------------------------------------------------------------------*/
+
+#if !defined(__GNUC__)
+#pragma START_PACK_STRUCTS
+#endif
+
+struct HotspotResource {
+ uint16 hotspotId;
+ uint16 nameId;
+ uint16 descId;
+ uint16 descId2;
+ uint32 actions;
+ uint16 actionsOffset;
+ uint16 roomNumber;
+ byte layer;
+ byte scriptLoadFlag;
+ uint16 loadOffset;
+ int16 startX;
+ int16 startY;
+ uint16 width;
+ uint16 height;
+ uint16 colourOffset;
+ uint16 animRecordId;
+ uint16 sequenceOffset;
+ uint16 tickProcOffset;
+ uint16 tickTimeout;
+} GCC_PACK;
+
+struct HotspotAnimResource {
+ uint16 animRecordId;
+ uint16 animId;
+ uint16 flags;
+ uint16 upOffset;
+ uint16 downOffset;
+ uint16 leftOffset;
+ uint16 rightOffset;
+ uint8 upFrame;
+ uint8 downFrame;
+ uint8 leftFrame;
+ uint8 rightFrame;
+} GCC_PACK;
+
+struct MovementResource {
+ uint16 frameNumber;
+ int16 xChange;
+ int16 yChange;
+} GCC_PACK;
+
+
+struct RoomResource {
+ uint16 roomNumber;
+ uint16 descId;
+ uint16 numLayers;
+ uint16 layers[4];
+ uint16 sequenceOffset;
+ uint16 numExits;
+} GCC_PACK;
+
+struct RoomExitResource {
+ int16 xs, xe, ys, ye;
+ uint16 sequenceOffset;
+ uint8 newRoom;
+ uint8 direction;
+ int16 newRoomX, newRoomY;
+} GCC_PACK;
+
+struct HotspotOverrideResource {
+ uint16 hotspotId;
+ int16 xs, xe, ys, ye;
+} GCC_PACK;
+
+struct RoomExitHotspotRecord {
+ uint16 hotspotId;
+ int16 xs, xe;
+ int16 ys, ye;
+ uint16 cursorNum;
+ uint16 destRoomNumber;
+} GCC_PACK;
+
+struct RoomExitJoinRecord {
+ uint16 hotspot1Id;
+ byte h1CurrentFrame;
+ byte h1DestFrame;
+ uint16 h1Unknown;
+ uint16 hotspot2Id;
+ byte h2CurrentFrame;
+ byte h2DestFrame;
+ uint16 h2Unknown;
+ byte blocked;
+ uint32 unknown;
+} GCC_PACK;
+
+struct HotspotActionRecord {
+ byte action;
+ uint16 sequenceOffset;
+} GCC_PACK;
+
+struct FileEntry {
+ uint16 id;
+ byte unused;
+ byte sizeExtension;
+ uint16 size;
+ uint16 offset;
+} GCC_PACK;
+
+struct VersionStructure {
+ uint16 id;
+ byte vMajor;
+ byte vMinor;
+} GCC_PACK;
+
+#if !defined(__GNUC__)
+#pragma END_PACK_STRUCTS
+#endif
+
+// Class template for a derived list that destroys the contained
+// object when the record containing it is destroyed. It's not
+// perfect, since the underlying list doesn't have virtual
+// methods, but it's sufficient for my usage
+
+template <class T>
+class ManagedList: public Common::List<T> {
+public:
+ ~ManagedList() {
+ clear();
+ }
+
+ void clear() {
+ typename Common::List<T>::iterator i;
+ for (i = Common::List<T>::begin(); i != Common::List<T>::end(); ++i)
+ delete *i;
+ Common::List<T>::clear();
+ }
+
+ typename Common::List<T>::iterator erase(typename Common::List<T>::iterator pos) {
+ delete *pos;
+ return Common::List<T>::erase(pos);
+ }
+
+ typename Common::List<T>::iterator erase(typename Common::List<T>::iterator first,
+ typename Common::List<T>::iterator last) {
+ typename Common::List<T>::iterator i;
+ for (i = first; i != last; ++i)
+ delete *i;
+ return Common::List<T>::erase(first, last);
+ }
+};
+
+// Enumeration used for direction facings
+
+enum Direction {UP, DOWN, LEFT, RIGHT, NO_DIRECTION};
+
+// Support classes to hold loaded resources
+
+class RoomExitHotspotData {
+public:
+ RoomExitHotspotData(RoomExitHotspotRecord *rec);
+
+ uint16 hotspotId;
+ int16 xs, xe;
+ int16 ys, ye;
+ uint16 cursorNum;
+ uint16 destRoomNumber;
+};
+
+typedef ManagedList<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 ManagedList<RoomExitData *> {
+public:
+ RoomExitData *checkExits(int16 xp, int16 yp);
+};
+
+#define MAX_NUM_LAYERS 4
+
+class RoomData {
+public:
+ RoomData(RoomResource *rec);
+
+ uint16 roomNumber;
+ uint16 descId;
+ uint16 numLayers;
+ uint16 layers[MAX_NUM_LAYERS];
+ uint16 sequenceOffset;
+ RoomExitHotspotList exitHotspots;
+ RoomExitList exits;
+};
+
+typedef ManagedList<RoomData *> RoomDataList;
+
+class RoomExitJoinData {
+public:
+ RoomExitJoinData(RoomExitJoinRecord *rec);
+
+ uint16 hotspot1Id;
+ byte h1CurrentFrame;
+ byte h1DestFrame;
+ uint16 h1Unknown;
+ uint16 hotspot2Id;
+ byte h2CurrentFrame;
+ byte h2DestFrame;
+ uint16 h2Unknown;
+ byte blocked;
+ uint32 unknown;
+};
+
+typedef ManagedList<RoomExitJoinData *> RoomExitJoinList;
+
+class HotspotActionData {
+public:
+ HotspotActionData(HotspotActionRecord *rec);
+
+ Action action;
+ uint16 sequenceOffset;
+};
+
+class HotspotActionList: public ManagedList<HotspotActionData *> {
+public:
+ uint16 recordId;
+
+ HotspotActionList(uint16 id, byte *data);
+ uint16 getActionOffset(Action action);
+};
+
+class HotspotActionSet: public ManagedList<HotspotActionList *> {
+public:
+ HotspotActionList *getActions(uint16 recordId);
+};
+
+class HotspotData {
+public:
+ 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 colourOffset;
+ uint16 animRecordId;
+ uint16 sequenceOffset;
+ uint16 tickProcOffset;
+ uint16 tickTimeout;
+};
+
+typedef ManagedList<HotspotData *> HotspotDataList;
+
+class HotspotOverrideData {
+public:
+ HotspotOverrideData(HotspotOverrideResource *rec);
+
+ uint16 hotspotId;
+ int16 xs, xe, ys, ye;
+};
+
+typedef ManagedList<HotspotOverrideData *> HotspotOverrideList;
+
+class MovementData {
+public:
+ MovementData(MovementResource *);
+
+ uint16 frameNumber;
+ int16 xChange;
+ int16 yChange;
+};
+
+class MovementDataList: public ManagedList<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 ManagedList<HotspotAnimData *> HotspotAnimList;
+
+// The following classes hold any sequence offsets that are being delayed
+
+class SequenceDelayData {
+ friend class SequenceDelayList;
+private:
+ uint32 _timeoutCtr;
+ uint16 _sequenceOffset;
+public:
+ SequenceDelayData(uint16 delay, uint16 seqOffset);
+};
+
+class SequenceDelayList: public ManagedList<SequenceDelayData *> {
+public:
+ void addSequence(uint16 delay, uint16 seqOffset);
+ void tick();
+};
+
+// 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 85
+
+enum FieldName {
+ ROOM_NUMBER = 0,
+ CHARACTER_HOTSPOT_ID = 1,
+ USE_HOTSPOT_ID = 2,
+ ACTIVE_HOTSPOT_ID = 3,
+ SEQUENCE_RESULT = 4,
+ GENERAL = 5,
+ NEW_ROOM_NUMBER = 7,
+ GENERAL_STATUS = 8,
+ TORCH_HIDE = 10,
+ PRISONER_DEAD = 15,
+ BOTTLE_FILLED = 18,
+ SACK_CUT = 20
+};
+
+class ValueTableData {
+private:
+ uint16 _numGroats;
+ uint16 _fieldList[NUM_VALUE_FIELDS];
+ bool isKnownField(uint16 fieldIndex);
+public:
+ ValueTableData();
+ uint16 getField(uint16 fieldIndex);
+ uint16 getField(FieldName fieldName);
+
+ void setField(uint16 fieldIndex, uint16 value);
+ void setField(FieldName fieldName, uint16 value);
+ uint16 &numGroats() { return _numGroats; }
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/room.cpp b/lure/room.cpp
new file mode 100644
index 0000000000..748ffccd28
--- /dev/null
+++ b/lure/room.cpp
@@ -0,0 +1,485 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/room.h"
+#include "lure/luredefs.h"
+#include "lure/res.h"
+#include "lure/screen.h"
+#include "lure/events.h"
+#include "lure/strings.h"
+#include "lure/scripts.h"
+
+namespace Lure {
+
+static Room *int_room;
+
+RoomLayer::RoomLayer(uint16 screenId, bool backgroundLayer):
+ Surface(FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT) {
+ loadScreen(screenId);
+ byte cellIndex = 0;
+ byte *screenData = data().data();
+
+ memset(_cells, 0xff, FULL_HORIZ_RECTS*FULL_VERT_RECTS);
+
+ // Loop through each cell of the screen
+ for (int 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 + 8)
+ * 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) * FULL_HORIZ_RECTS + NUM_EDGE_RECTS +
+ cellX] = !hasPixels ? 0xff : cellIndex++;
+ }
+ }
+}
+
+Room::Room(): _screen(Screen::getReference()) {
+ int_room = this;
+
+ _roomData = NULL;
+ _hotspotName[0] = '\0';
+ for (int ctr = 0; ctr < MAX_NUM_LAYERS; ++ctr) _layers[ctr] = NULL;
+ _numLayers = 0;
+ _showInfo = false;
+ _currentAction = NONE;
+}
+
+Room::~Room() {
+ for (int layerNum = 0; layerNum < _numLayers; ++layerNum)
+ if (_layers[layerNum])
+ delete _layers[layerNum];
+
+ int_room = NULL;
+}
+
+Room &Room::getReference() {
+ return *int_room;
+}
+
+// leaveRoom
+// Handles leaving the current room
+
+void Room::leaveRoom() {
+ Resources &r = Resources::getReference();
+
+ // Deallocate graphical layers from the room
+ for (int layerNum = 0; layerNum < _numLayers; ++layerNum)
+ if (_layers[layerNum]) {
+ delete _layers[layerNum];
+ _layers[layerNum] = NULL;
+ }
+
+ // Scan through the hotspot list and remove any uneeded entries
+//r.activeHotspots().clear();
+ HotspotList &list = r.activeHotspots();
+ HotspotList::iterator i = list.begin();
+ while (i != list.end()) {
+ Hotspot *h = i.operator*();
+ if (!h->persistant()) {
+ 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 *rec = *i;
+
+ if ((rec->hotspotId < 0x7530) && (rec->roomNumber == _roomNumber) &&
+ (rec->layer != 0))
+ r.activateHotspot(rec->hotspotId);
+ }
+}
+
+void Room::checkRoomHotspots() {
+ Mouse &m = Mouse::getReference();
+ Resources &r = Resources::getReference();
+ HotspotDataList &list = r.hotspotData();
+ HotspotData *entry = NULL;
+ int16 currentX = m.x();
+ int16 currentY = m.y();
+
+ HotspotDataList::iterator i;
+ for (i = list.begin(); i != list.end(); ++i) {
+ entry = *i;
+
+ bool skipFlag = (entry->roomNumber != _roomNumber);
+ if (!skipFlag) {
+ skipFlag = (((entry->flags & 0x80) == 0) &&
+ ((entry->flags & 0x40) != 0)) ||
+ ((entry->flags & 0x20) != 0);
+ }
+
+ if ((!skipFlag) && (entry->hotspotId < 0x409))
+ skipFlag = sub_112();
+
+ if (!skipFlag && (entry->hotspotId >= 0x2710) && (entry->hotspotId <= 0x27ff)) {
+ RoomExitJoinData *rec = r.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 = r.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) && (currentX < entry->startX + entry->width) &&
+ (currentY >= entry->startY) && (currentY < entry->startY + entry->height))
+ // Found hotspot entry
+ break;
+ }
+ }
+ }
+
+ if (i == list.end()) {
+ _hotspotId = 0;
+ _hotspotNameId = 0;
+ _hotspot = NULL;
+ } else {
+ _hotspotNameId = entry->nameId;
+ _hotspot = entry;
+ _hotspotId = entry->hotspotId;
+ }
+}
+
+uint8 Room::checkRoomExits() {
+ Mouse &m = Mouse::getReference();
+ Resources &r = Resources::getReference();
+
+ RoomExitHotspotList &exits = _roomData->exitHotspots;
+ if (exits.isEmpty()) return CURSOR_ARROW;
+ RoomExitJoinData *join;
+ bool skipFlag;
+
+ RoomExitHotspotList::iterator i;
+ for (i = exits.begin(); i != exits.end(); ++i) {
+ RoomExitHotspotData *rec = *i;
+ skipFlag = false;
+
+ if (rec->hotspotId != 0) {
+ join = r.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
+ uint8 cursorNum = rec->cursorNum;
+
+ // If it's a hotspotted exit, change arrow to the + arrow
+ if (rec->hotspotId != 0) cursorNum += 7;
+
+ return cursorNum;
+ }
+ }
+
+ // No room exits found
+ return CURSOR_ARROW;
+}
+
+void Room::flagCoveredCells(Hotspot &h) {
+ int16 yStart = (h.y() - MENUBAR_Y_SIZE) / RECT_SIZE;
+ int16 yEnd = (h.y() + h.height() - 1 - MENUBAR_Y_SIZE) / RECT_SIZE;
+ int16 numY = yEnd - yStart + 1;
+ int16 xStart = h.x() / RECT_SIZE;
+ int16 xEnd = (h.x() + h.width() - 1) / RECT_SIZE;
+ int16 numX = xEnd - xStart + 1;
+
+ int index = yStart * NUM_HORIZ_RECTS + xStart;
+
+ for (int16 yP = 0; yP < numY; ++yP) {
+ for (int16 xP = 0; xP < numX; ++xP) {
+ int indexPos = index + xP;
+ if ((indexPos < 0) || (indexPos >= NUM_HORIZ_RECTS*NUM_VERT_RECTS))
+ continue;
+ _cells[index+xP] |= 0x81;
+ _cells2[index+xP] |= 1;
+ }
+ index += NUM_HORIZ_RECTS;
+ }
+}
+
+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 <= 319) && (y >= 0) && (y <= 200)) {
+ sprintf(buffer, "%x", h.resource().hotspotId);
+ strcat(buffer, "h");
+ s.writeString(h.x(), h.y(), buffer, false);
+ }
+ }
+}
+
+void Room::addLayers(Hotspot &h) {
+ int16 hsX = h.x() + (4 * RECT_SIZE);
+ int16 hsY = h.y() + (4 * 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.height() - 1) / RECT_SIZE;
+ int16 numY = yEnd - yStart + 1;
+
+ for (int16 xCtr = 0; xCtr < numX; ++xCtr, ++xStart) {
+ int16 xs = xStart - 4;
+ if (xs < 0) continue;
+
+ // Check foreground layers for an occupied one
+/* DEBUG
+ int layerNum = 1;
+ while ((layerNum < _numLayers) &&
+ !_layers[layerNum]->isOccupied(xStart, yEnd))
+ ++layerNum;
+ if (layerNum == _numLayers) continue;
+*/
+ int layerNum = _numLayers - 1;
+ while ((layerNum > 0) &&
+ !_layers[layerNum]->isOccupied(xStart, yEnd))
+ --layerNum;
+ if (layerNum == 0) continue;
+
+ int16 ye = yEnd - 4;
+ 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 > 0) && !_layers[layerNum]->isOccupied(xp+4, yp+4))
+ --layerNum;
+ if (layerNum == 0) return;
+/* DEBUG
+ while ((layerNum < _numLayers) && !_layers[layerNum]->isOccupied(xp+4, yp+4))
+ ++layerNum;
+ if (layerNum == _numLayers) return;
+*/
+ RoomLayer *layer = _layers[layerNum];
+
+ int index = ((yp * RECT_SIZE + 8) * 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;
+ }
+
+ // Note: old version of screen layers load compresses loaded layers down to
+ // only a set of the non-empty rects. Since modern memory allows me to load
+ // all the layers completely, I'm bypassing the need to use cell index values
+}
+
+void Room::update() {
+ Surface &s = _screen.screen();
+ Resources &r = Resources::getReference();
+ HotspotList &hotspots = r.activeHotspots();
+ HotspotList::iterator i;
+
+ memset(_cells, 0x81, NUM_HORIZ_RECTS*NUM_VERT_RECTS);
+ memset(_cells2, 0x81, NUM_HORIZ_RECTS*NUM_VERT_RECTS);
+
+ _layers[0]->copyTo(&s);
+ for (int ctr = 1; ctr < _numLayers; ++ctr)
+ _layers[ctr]->transparentCopyTo(&s);
+
+ // Handle first layer (layer 3)
+ for (i = hotspots.begin(); i != hotspots.end(); ++i) {
+ Hotspot &h = *i.operator*();
+ if ((h.roomNumber() == _roomNumber) && h.isActiveAnimation() && (h.layer() == 3)) {
+ flagCoveredCells(h);
+ addAnimation(h);
+ addLayers(h);
+ }
+ }
+
+ // Handle second layer (layer 1) - do in order of Y axis
+ List<Hotspot *> tempList;
+ List<Hotspot *>::iterator iTemp;
+ for (i = hotspots.begin(); i != hotspots.end(); ++i) {
+ Hotspot *h = i.operator*();
+ if ((h->roomNumber() != _roomNumber) || !h->isActiveAnimation()
+ || (h->layer() != 1))
+ continue;
+ int16 endY = h->y() + h->height();
+
+ for (iTemp = tempList.begin(); iTemp != tempList.end(); ++iTemp) {
+ Hotspot *hTemp = iTemp.operator*();
+ int16 tempY = hTemp->y() + hTemp->height();
+ if (endY < tempY) {
+ if (iTemp != tempList.begin()) --iTemp;
+ break;
+ }
+ }
+ tempList.insert(iTemp, h);
+ }
+ for (iTemp = tempList.begin(); iTemp != tempList.end(); ++iTemp) {
+ Hotspot &h = *iTemp.operator*();
+ flagCoveredCells(h);
+ addAnimation(h);
+ addLayers(h);
+ }
+
+ // Handle third layer (layer 2)
+ for (i = hotspots.begin(); i != hotspots.end(); ++i) {
+ Hotspot &h = *i.operator*();
+ if ((h.roomNumber() == _roomNumber) && h.isActiveAnimation() && (h.layer() == 2)) {
+ flagCoveredCells(h);
+ addAnimation(h);
+ }
+ }
+
+ // Handle showing name of highlighted hotspot
+ if (_hotspotName[0] != '\0') {
+ if (_currentAction == NONE) {
+ s.writeString(0, 0, _hotspotName, false, DIALOG_TEXT_COLOUR);
+ } else {
+ char buffer[MAX_ACTION_NAME_SIZE + MAX_HOTSPOT_NAME_SIZE];
+ strcpy(buffer, actionList[_currentAction]);
+ strcat(buffer, " ");
+ strcat(buffer, _hotspotName);
+ s.writeString(0, 0, buffer, false, DIALOG_WHITE_COLOUR);
+ }
+ }
+
+ // If show information is turned on, show room and position
+ if (_showInfo) {
+ char buffer[64];
+ Mouse &m = Mouse::getReference();
+ sprintf(buffer, "Room %d Pos (%d,%d)", _roomNumber, m.x(), m.y());
+ s.writeString(FULL_SCREEN_WIDTH / 2, 0, buffer, false, DIALOG_TEXT_COLOUR);
+ }
+
+ _screen.update();
+}
+
+void Room::setRoomNumber(uint16 newRoomNumber, bool showOverlay) {
+ Resources &r = Resources::getReference();
+ _roomData = r.getRoom(newRoomNumber);
+ if (!_roomData)
+ error("Tried to change to non-existant room: %d", newRoomNumber);
+
+ _roomNumber = _roomData->roomNumber;
+ _descId = _roomData->descId;
+
+ _screen.empty();
+ _screen.resetPalette();
+
+ if (_layers[0]) leaveRoom();
+
+ _numLayers = _roomData->numLayers;
+ if (showOverlay) ++_numLayers;
+
+ uint16 paletteId = (_roomData->layers[0] & 0xffe0) - 1;
+
+ for (uint8 layerNum = 0; layerNum < _numLayers; ++layerNum)
+ _layers[layerNum] = new RoomLayer(_roomData->layers[layerNum],
+ layerNum == 0);
+
+ // Load in the palette, add in the two replacements segments, and then
+ // set to the system palette
+ Palette p(228, NULL, RGB64);
+ Palette tempPalette(paletteId);
+ p.copyFrom(&tempPalette);
+ r.insertPaletteSubset(p);
+ _screen.setPalette(&p);
+
+ if (_roomData->sequenceOffset != 0xffff)
+ Script::execute(_roomData->sequenceOffset);
+ loadRoomHotspots();
+ cursorMoved();
+
+ update();
+}
+
+// cursorMoved
+// Called as the cursor moves to handle any changes that must occur
+
+void Room::cursorMoved() {
+ uint16 cursorNew = CURSOR_ARROW;
+ uint16 oldHotspotId = _hotspotId;
+
+ Mouse &m = Mouse::getReference();
+ checkRoomHotspots();
+
+ if (_hotspotId != 0) {
+ cursorNew = CURSOR_CROSS;
+
+ if (oldHotspotId != _hotspotId)
+ StringData::getReference().getString(_hotspotNameId, _hotspotName, NULL, NULL);
+ } else {
+ _hotspotName[0] = '\0';
+ cursorNew = checkRoomExits();
+ }
+
+ if (m.getCursorNum() != cursorNew)
+ m.setCursorNum(cursorNew);
+}
+
+} // end of namespace Lure
diff --git a/lure/room.h b/lure/room.h
new file mode 100644
index 0000000000..8553722cda
--- /dev/null
+++ b/lure/room.h
@@ -0,0 +1,107 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_room_h__
+#define __lure_room_h__
+
+#include "common/stdafx.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
+
+class RoomLayer: public Surface {
+private:
+ byte _cells[FULL_HORIZ_RECTS*FULL_VERT_RECTS];
+public:
+ RoomLayer(uint16 screenId, bool backgroundLayer);
+ byte cellVal(byte cellX, byte cellY) {
+ return _cells[FULL_HORIZ_RECTS*cellY + cellX];
+ }
+ bool isOccupied(byte cellX, byte cellY) {
+ return cellVal(cellX, cellY) != 0xff;
+ }
+};
+
+class Room {
+private:
+ RoomData *_roomData;
+ Screen &_screen;
+ uint16 _roomNumber;
+ uint16 _descId;
+ uint16 _hotspotId;
+ uint16 _hotspotNameId;
+ Action _currentAction;
+ char _hotspotName[MAX_HOTSPOT_NAME_SIZE + MAX_ACTION_NAME_SIZE];
+ HotspotData *_hotspot;
+ bool _showInfo;
+ uint8 _numLayers;
+ RoomLayer *_layers[MAX_NUM_LAYERS];
+ byte _cells[NUM_HORIZ_RECTS*NUM_VERT_RECTS];
+ byte _cells2[NUM_HORIZ_RECTS*NUM_VERT_RECTS];
+
+ void checkRoomHotspots();
+ uint8 checkRoomExits();
+ void loadRoomHotspots();
+ bool sub_112() { return false; } // not yet implemented
+ void flagCoveredCells(Hotspot &h);
+ void addAnimation(Hotspot &h);
+ void addLayers(Hotspot &h);
+ void addCell(int16 xp, int16 yp, int layerNum);
+public:
+ Room();
+ ~Room();
+ static Room &getReference();
+
+ void update();
+ void nextFrame();
+ void cursorMoved();
+ uint16 roomNumber() { return _roomNumber; }
+ void setRoomNumber(uint16 newRoomNumber, bool showOverlay = false);
+ void leaveRoom();
+ void setAction(Action action) { _currentAction = action; }
+ Action getCurrentAction() { return _currentAction; }
+ uint16 hotspotId() { return _hotspotId; }
+ 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; }
+ void setShowInfo(bool value) { _showInfo = value; }
+ uint32 xyzzy() { return (uint32) _layers[3]; }
+};
+
+} // end of namespace Lure
+
+#endif
diff --git a/lure/screen.cpp b/lure/screen.cpp
new file mode 100644
index 0000000000..ad84c9e838
--- /dev/null
+++ b/lure/screen.cpp
@@ -0,0 +1,152 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/screen.h"
+#include "lure/luredefs.h"
+#include "lure/memory.h"
+#include "lure/disk.h"
+#include "lure/decode.h"
+
+namespace Lure {
+
+static Screen *int_disk = NULL;
+
+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)) {
+ int_disk = this;
+ _screen->empty();
+ _system.setPalette(_palette->data(), 0, GAME_COLOURS);
+}
+
+Screen::~Screen() {
+ delete _screen;
+ delete _palette;
+}
+
+// setPaletteEmpty
+// Defaults the palette to an empty set
+
+void Screen::setPaletteEmpty() {
+ delete _palette;
+ _palette = new Palette();
+
+ _system.setPalette(_palette->data(), 0, GAME_COLOURS);
+ _system.updateScreen();
+}
+
+// setPalette
+// Sets the current palette to the passed palette
+
+void Screen::setPalette(Palette *p) {
+ _palette->copyFrom(p);
+ _system.setPalette(_palette->data(), 0, GAME_COLOURS);
+ _system.updateScreen();
+}
+
+// paletteFadeIn
+// Fades in the palette. For proper operation, the palette should have been
+// previously set to empty
+
+void Screen::paletteFadeIn(Palette *p) {
+ bool changed;
+ byte *const pDest = p->data();
+ byte *const pTemp = _palette->data();
+
+ do {
+ changed = false;
+
+ for (int palCtr = 0; palCtr < p->numEntries() * 4; ++palCtr)
+ {
+ if (palCtr % PALETTE_FADE_INC_SIZE == (PALETTE_FADE_INC_SIZE - 1)) continue;
+ bool isDifferent = pTemp[palCtr] < pDest[palCtr];
+ if (isDifferent) {
+ if (pDest[palCtr] - pTemp[palCtr] < PALETTE_FADE_INC_SIZE)
+ pTemp[palCtr] = pDest[palCtr];
+ else pTemp[palCtr] += PALETTE_FADE_INC_SIZE;
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ _system.setPalette(_palette->data(), 0, GAME_COLOURS);
+ _system.updateScreen();
+ _system.delayMillis(20);
+ }
+ } while (changed);
+}
+
+// paletteFadeOut
+// Fades the screen to black by gradually decreasing the palette colours
+
+void Screen::paletteFadeOut() {
+ bool changed;
+
+ do {
+ byte *pTemp = _palette->data();
+ changed = false;
+
+ for (uint32 palCtr = 0; palCtr < _palette->palette()->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) {
+ _system.setPalette(_palette->data(), 0, GAME_COLOURS);
+ _system.updateScreen();
+ _system.delayMillis(20);
+ }
+ } 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
diff --git a/lure/screen.h b/lure/screen.h
new file mode 100644
index 0000000000..064fec0e96
--- /dev/null
+++ b/lure/screen.h
@@ -0,0 +1,64 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_screen_h__
+#define __lure_screen_h__
+
+#include "common/stdafx.h"
+#include "base/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;
+public:
+ Screen(OSystem &system);
+ ~Screen();
+ static Screen &getReference();
+
+ void setPaletteEmpty();
+ void setPalette(Palette *p);
+ Palette &getPalette() { return *_palette; }
+ void paletteFadeIn(Palette *p);
+ void paletteFadeOut();
+ 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
diff --git a/lure/scripts.cpp b/lure/scripts.cpp
new file mode 100644
index 0000000000..073753a93f
--- /dev/null
+++ b/lure/scripts.cpp
@@ -0,0 +1,576 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/scripts.h"
+#include "lure/res.h"
+#include "lure/game.h"
+#include "common/stack.h"
+
+namespace Lure {
+
+/*------------------------------------------------------------------------*/
+/*- Script Method List -*/
+/*- -*/
+/*------------------------------------------------------------------------*/
+
+// activateHotspot
+// Activates a hotspot entry for active use
+
+void Script::activateHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources::getReference().activateHotspot(hotspotId);
+}
+
+// setHotspotScript
+// Sets a hotspot's animation script offset from a master table of offsets
+
+void Script::setHotspotScript(uint16 hotspotId, uint16 scriptIndex, uint16 v3) {
+ Resources &r = Resources::getReference();
+ uint16 offset = r.getHotspotScript(scriptIndex);
+ HotspotData *rsc = r.getHotspot(hotspotId);
+ rsc->sequenceOffset = offset;
+}
+
+// Clears the sequence delay list
+
+void Script::clearSequenceDelayList(uint16 v1, uint16 scriptIndex, uint16 v3) {
+ Resources::getReference().delayList().clear();
+}
+
+// deactivates the specified hotspot from active animation
+
+void Script::deactivateHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources &rsc = Resources::getReference();
+ if (hotspotId < START_NONVISUAL_HOTSPOT_ID)
+ rsc.deactivateHotspot(hotspotId);
+ HotspotData *hs = rsc.getHotspot(hotspotId);
+ hs->flags |= 0x20;
+ if (hotspotId < START_NONVISUAL_HOTSPOT_ID)
+ hs->layer = 0xff;
+}
+
+// Sets the offset for the table of action sequence offsets for the given
+// hotspot
+
+void Script::setActionsOffset(uint16 hotspotId, uint16 offset, uint16 v3) {
+ Resources &res = Resources::getReference();
+ HotspotData *hotspot = res.getHotspot(hotspotId);
+
+ if (!res.getHotspotActions(offset))
+ warning("Hotspot %d set to invalid actions offset %d",
+ hotspotId, offset);
+
+ hotspot->actionsOffset = offset;
+}
+
+// Add a sequence to be executed after a specified delay
+
+void Script::addDelayedSequence(uint16 seqOffset, uint16 delay, uint16 v3) {
+ SequenceDelayList &list = Resources::getReference().delayList();
+ list.addSequence(delay, seqOffset);
+}
+
+// Checks whether the given character is in the specified room, and stores
+// the result in the general value field
+
+void Script::characterInRoom(uint16 characterId, uint16 roomNumber, uint16 v3) {
+ Resources &res = Resources::getReference();
+ uint16 result = 0;
+ if (characterId >= PLAYER_ID) {
+ HotspotData *hotspot = res.getHotspot(characterId);
+ if (hotspot->roomNumber == roomNumber)
+ result = 1;
+ }
+
+ res.fieldList().setField(GENERAL, result);
+}
+
+// Changes the given hotspot's name to a new name
+
+void Script::setHotspotName(uint16 hotspotId, uint16 nameId, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->nameId = nameId;
+}
+
+// Displays the given string resource Id in a dialog
+
+void Script::displayDialog(uint16 stringId, uint16 v2, uint16 v3) {
+ Dialog::show(stringId);
+}
+
+// Flags for remotely viewing a room
+
+void Script::remoteRoomViewSetup(uint16 v1, uint16 v2, uint16 v3) {
+ Hotspot *player = Resources::getReference().getActiveHotspot(PLAYER_ID);
+ player->setTickProc(0); // disable player actions
+ Game::getReference().setRemoteView();
+}
+
+// Gets the current blocked state for the given door and stores it in the
+// general value field
+
+void Script::getDoorBlocked(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources &res = Resources::getReference();
+ RoomExitJoinData *joinRec = res.getExitJoin(hotspotId);
+ res.fieldList().setField(GENERAL, joinRec->blocked);
+}
+
+// Decrements the number of inventory itemst he player has
+
+void Script::decrInventoryItems(uint16 v1, uint16 v2, uint16 v3) {
+ // module currently doesn't use a static counter for the number of
+ // inventory items, so don't do anything
+}
+
+// Sets the current frame number for the given hotspot
+
+void Script::setFrameNumber(uint16 hotspotId, uint16 frameNumber, uint16 v3) {
+ Hotspot *hotspot = Resources::getReference().getActiveHotspot(hotspotId);
+ hotspot->setFrameNumber(frameNumber);
+}
+
+// Disables the given hotspot from being highlighted by the cursor
+
+void Script::disableHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->flags |= 0x20;
+}
+
+// Increase the player's number by the specified amount
+
+void Script::increaseNumGroats(uint16 v1, uint16 numGroats, uint16 v3) {
+ ValueTableData &fields = Resources::getReference().fieldList();
+ fields.numGroats() += numGroats;
+}
+
+// Enables the flags for the given hotspot for it to be actively highlighted
+
+void Script::enableHotspot(uint16 hotspotId, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ // Clear flag 0x20 and add flag 0x80
+ hotspot->flags = (hotspot->flags & 0xdf) | 0x80;
+}
+
+// Marks the door in room 14 for closing
+
+void Script::room14DoorClose(uint16 v1, uint16 v2, uint16 v3) {
+ RoomExitJoinData *joinRec = Resources::getReference().getExitJoin(0x2719);
+ joinRec->blocked = 1;
+}
+
+// Sets the sequence result to 1 if the given secondary description for a
+// hotspot is empty (for inventory items, this gives the description before
+// the item is initially picked up)
+
+void Script::checkDroppedDesc(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources &res = Resources::getReference();
+ HotspotData *hotspot = res.getHotspot(hotspotId);
+ uint16 seqResult = (hotspot->descId2 == 0) ? 1 : 0;
+ res.fieldList().setField(SEQUENCE_RESULT, seqResult);
+}
+
+// Marks the given door hotspot for closing
+
+void Script::doorClose(uint16 hotspotId, uint16 v2, uint16 v3) {
+ RoomExitJoinData *joinRec = Resources::getReference().getExitJoin(hotspotId);
+ if (!joinRec) error("Tried to close a non-door");
+ joinRec->blocked = 1;
+}
+
+// Marks the given door hotspot for opening
+
+void Script::doorOpen(uint16 hotspotId, uint16 v2, uint16 v3) {
+ RoomExitJoinData *joinRec = Resources::getReference().getExitJoin(hotspotId);
+ if (!joinRec) error("Tried to close a non-door");
+ joinRec->blocked = 0;
+}
+
+// Lookup the given message Id for the specified character and display in a dialog
+
+void Script::displayMessage(uint16 messageId, uint16 characterId, uint16 unknownVal) {
+ Dialog::showMessage(messageId, characterId);
+}
+
+// Assign the given hotspot item to the player's inventory
+
+void Script::givePlayerItem(uint16 hotspotId, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->roomNumber = PLAYER_ID;
+ hotspot->flags |= 0x80;
+}
+
+// Decrease the number of graots the player has
+
+void Script::decreaseNumGroats(uint16 characterId, uint16 numGroats, uint16 v3) {
+ ValueTableData &fields = Resources::getReference().fieldList();
+ fields.numGroats() -= numGroats;
+}
+
+// Sets the tick handler for the village Skorl to an alternate handler
+
+void Script::setVillageSkorlTickProc(uint16 v1, uint16 v2, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(0x3F1);
+ hotspot->tickProcOffset = 0x7efa;
+}
+
+// Stores the current number of groats in the general field
+
+void Script::getNumGroats(uint16 v1, uint16 v2, uint16 v3) {
+ ValueTableData fields = Resources::getReference().fieldList();
+ fields.setField(GENERAL, fields.numGroats());
+}
+
+// Loads the specified animation, completely bypassing the standard process
+// of checking for a load proc/sequence
+
+void Script::animationLoad(uint16 hotspotId, uint16 v2, uint16 v3) {
+ Resources::getReference().addHotspot(hotspotId);
+}
+
+// Adds the passed actions to the available actions for the given hotspot
+
+void Script::addActions(uint16 hotspotId, uint16 actions, uint16 v3) {
+ HotspotData *hotspot = Resources::getReference().getHotspot(hotspotId);
+ hotspot->actions |= actions;
+}
+
+// Checks the status of the cell door, and starts music depending on it's state
+
+void Script::checkCellDoor(uint16 v1, uint16 v2, uint16 v3) {
+ // In the original game, this method checks to see if the cell door
+ // is currently open, if it is, starts a music sequence. I'll
+ // implement this method properly when I get around to implementing
+ // the in-game music
+}
+
+typedef void(*SequenceMethodPtr)(uint16, uint16, uint16);
+
+struct SequenceMethodRecord {
+ uint8 methodIndex;
+ SequenceMethodPtr proc;
+};
+
+SequenceMethodRecord scriptMethods[] = {
+ {0, Script::activateHotspot},
+ {1, Script::setHotspotScript},
+ {4, Script::clearSequenceDelayList},
+ {6, Script::deactivateHotspot},
+ {8, Script::addDelayedSequence},
+ {10, Script::characterInRoom},
+ {11, Script::setActionsOffset},
+ {12, Script::setHotspotName},
+ {16, Script::displayDialog},
+ {18, Script::remoteRoomViewSetup},
+ {20, Script::checkCellDoor},
+ {22, Script::getDoorBlocked},
+ {28, Script::decrInventoryItems},
+ {30, Script::setFrameNumber},
+ {32, Script::disableHotspot},
+ {34, Script::increaseNumGroats},
+ {35, Script::enableHotspot},
+ {39, Script::room14DoorClose},
+ {40, Script::checkDroppedDesc},
+ {42, Script::doorClose},
+ {44, Script::doorOpen},
+ {47, Script::displayMessage},
+ {50, Script::givePlayerItem},
+ {51, Script::decreaseNumGroats},
+ {54, Script::setVillageSkorlTickProc},
+ {57, Script::getNumGroats},
+ {62, Script::animationLoad},
+ {63, Script::addActions},
+ {65, Script::checkCellDoor},
+ {0xff, NULL}};
+
+/*------------------------------------------------------------------------*/
+/*- Script Execution -*/
+/*- -*/
+/*------------------------------------------------------------------------*/
+
+uint16 Script::execute(uint16 startOffset) {
+ Resources &r = Resources::getReference();
+ ValueTableData &fields = r.fieldList();
+ MemoryBlock *scriptData = r.scriptData();
+ byte *scripts = scriptData->data();
+ Common::Stack<uint16> stack;
+ Common::Stack<uint16> methodStack;
+ byte opcode;
+ uint16 param, v1, v2;
+ uint16 param1, param2, param3;
+ uint16 fieldNum;
+ uint32 tempVal;
+ SequenceMethodPtr ptr;
+ SequenceMethodRecord *rec;
+
+ uint16 offset = startOffset;
+ bool breakFlag = false;
+ param = 0;
+ fields.setField(SEQUENCE_RESULT, 0);
+
+ while (!breakFlag) {
+ if (offset >= scriptData->size())
+ error("Script failure in script %d - invalid offset %d", startOffset, offset);
+
+ opcode = scripts[offset++];
+ if ((opcode & 1) != 0) {
+ // Flag to read next two bytes as active parameter
+ if (offset >= scriptData->size()-2)
+ error("Script failure in script %d - invalid offset %d", startOffset, offset);
+
+ param = READ_LE_UINT16(scripts + offset);
+ offset += 2;
+ }
+ opcode >>= 1; // Discard param bit from opcode byte
+
+ switch (opcode) {
+ case S_OPCODE_ABORT:
+ case S_OPCODE_ABORT2:
+ case S_OPCODE_ABORT3:
+ methodStack.clear();
+ break;
+
+ case S_OPCODE_ADD:
+ stack.push(stack.pop() + stack.pop());
+ break;
+
+ case S_OPCODE_SUBTRACT:
+ v1 = stack.pop();
+ v2 = stack.pop();
+ stack.push(v2 - v1);
+ break;
+
+ case S_OPCODE_MULTIPLY:
+ tempVal = stack.pop() * stack.pop();
+ stack.push(tempVal & 0xffff);
+ param = (uint16) (tempVal >> 16);
+ break;
+
+ case S_OPCODE_DIVIDE:
+ v1 = stack.pop();
+ v2 = stack.pop();
+ stack.push(v2 / v1);
+ param = v2 % v1; // remainder
+ break;
+
+ case S_OPCODE_NOT_EQUALS:
+ stack.push((stack.pop() != stack.pop()) ? 0 : 1);
+ break;
+
+ case S_OPCODE_EQUALS:
+ stack.push((stack.pop() == stack.pop()) ? 0 : 1);
+ break;
+
+ case S_OPCODE_GT:
+ stack.push((stack.pop() > stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_LT:
+ stack.push((stack.pop() < stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_LT2:
+ stack.push((stack.pop() < stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_GT2:
+ stack.push((stack.pop() > stack.pop()) ? 1 : 0);
+ break;
+
+ case S_OPCODE_AND:
+ stack.push(stack.pop() & stack.pop());
+ break;
+
+ case S_OPCODE_OR:
+ stack.push(stack.pop() | stack.pop());
+ break;
+
+ case S_OPCODE_LOGICAL_AND:
+ stack.push(((stack.pop() != 0) && (stack.pop() != 0)) ? 1 : 0);
+ break;
+
+ case S_OPCODE_LOGICAL_OR:
+ stack.push(((stack.pop() != 0) || (stack.pop() != 0)) ? 1 : 0);
+ break;
+
+ case S_OPCODE_GET_FIELD:
+ // Opcode not yet fully implemented
+ fieldNum = param >> 1;
+ v1 = fields.getField(fieldNum);
+ stack.push(v1);
+ break;
+
+ case S_OPCODE_SET_FIELD:
+ // Opcode not yet fully implemented
+ fieldNum = param >> 1;
+ v1 = stack.pop();
+ fields.setField(fieldNum, v1);
+ break;
+
+ case S_OPCODE_PUSH:
+ stack.push(param);
+ break;
+
+ case S_OPCODE_SUBROUTINE:
+ methodStack.push(offset);
+ offset = param;
+ break;
+
+ case S_OPCODE_EXEC:
+ param1 = 0; param2 = 0; param3 = 0;
+ if (!stack.empty()) param1 = stack.pop();
+ if (!stack.empty()) param2 = stack.pop();
+ if (!stack.empty()) param3 = stack.pop();
+
+ rec = &scriptMethods[0];
+ while ((rec->methodIndex != 0xff) && (rec->methodIndex != param))
+ ++rec;
+
+ if (rec->methodIndex == 0xff)
+ warning("Undefined script method %d", param);
+ else {
+ ptr = rec->proc;
+ ptr(param1, param2, param3);
+ }
+ break;
+
+ case S_OPCODE_COND_JUMP:
+ v1 = stack.pop();
+ if (v1 == 0) offset += (int16) param;
+ break;
+
+ case S_OPCODE_JUMP:
+ offset += (int16) param;
+ break;
+
+ case S_OPCODE_RANDOM:
+ param = r.random() >> 8; // make number between 0 to 255
+ break;
+
+ case S_OPCODE_END:
+ // Signal to end the execution
+ if (!methodStack.empty())
+ offset = methodStack.pop();
+ else
+ breakFlag = true;
+ break;
+
+ default:
+ error("Unknown script opcode %d", opcode);
+ break;
+ }
+ }
+
+ return fields.getField(SEQUENCE_RESULT);
+}
+
+/*------------------------------------------------------------------------*/
+/*- Hotspot Script Handler -*/
+/*- -*/
+/*------------------------------------------------------------------------*/
+
+int16 HotspotScript::nextVal(MemoryBlock *data, uint16 &offset) {
+ if (offset >= data->size() - 1)
+ error("Script failure - invalid offset");
+ int16 value = READ_LE_UINT16(data->data() + offset);
+ offset += 2;
+ return value;
+}
+
+bool HotspotScript::execute(Hotspot *h)
+{
+ Resources &r = Resources::getReference();
+ MemoryBlock *scriptData = r.hotspotScriptData();
+ uint16 offset = h->script();
+ int16 opcode = 0;
+ int16 param1, param2;
+ bool breakFlag = false;
+
+ while (!breakFlag) {
+ opcode = nextVal(scriptData, offset);
+ switch (opcode) {
+ case S2_OPCODE_TIMEOUT:
+ param1 = nextVal(scriptData, offset);
+ h->setTickCtr(param1);
+ h->setScript(offset);
+ breakFlag = true;
+ break;
+
+ case S2_OPCODE_POSITION:
+ param1 = nextVal(scriptData, offset);
+ param2 = nextVal(scriptData, offset);
+ h->setPosition(param1 - 0x80, param2 - 0x80);
+ break;
+
+ case S2_OPCODE_CHANGE_POS:
+ param1 = nextVal(scriptData, offset);
+ param2 = nextVal(scriptData, offset);
+ h->setPosition(h->x() + param1, h->y() + param2);
+ break;
+
+ case S2_OPCODE_UNLOAD:
+ breakFlag = true;
+ break;
+
+ case S2_OPCODE_DIMENSIONS:
+ param1 = nextVal(scriptData, offset) << 4;
+ param2 = nextVal(scriptData, offset);
+ h->setSize((uint16) param1, (uint16) param2);
+ break;
+
+ case S2_OPCODE_JUMP:
+ offset = (uint16) nextVal(scriptData, offset);
+ break;
+
+ case S2_OPCODE_ANIMATION:
+ param1 = nextVal(scriptData, offset);
+ h->setAnimation(param1);
+ break;
+
+ case S2_OPCODE_UNKNOWN_247:
+ param1 = nextVal(scriptData, offset);
+ param2 = nextVal(scriptData, offset);
+// warning("UNKNOWN_247 stub called");
+ break;
+
+ case S2_OPCODE_UNKNOWN_258:
+ param1 = nextVal(scriptData, offset);
+// warning("UNKNOWN_258 stub called");
+ break;
+
+ case S2_OPCODE_ACTIONS:
+ param1 = nextVal(scriptData, offset) << 4;
+ param2 = nextVal(scriptData, offset);
+ h->setActions((uint32) param1 | ((uint32) param2 << 16));
+ break;
+
+ default:
+ // Set the animation frame number
+ h->setFrameNumber(opcode);
+ h->setScript(offset);
+ breakFlag = true;
+ break;
+ }
+ }
+
+ return (opcode == S2_OPCODE_UNLOAD);
+}
+
+} // end of namespace Lure
diff --git a/lure/scripts.h b/lure/scripts.h
new file mode 100644
index 0000000000..b8ed59b15d
--- /dev/null
+++ b/lure/scripts.h
@@ -0,0 +1,117 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#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_NOT_EQUALS 5
+#define S_OPCODE_EQUALS 6
+#define S_OPCODE_GT 7
+#define S_OPCODE_LT 8
+#define S_OPCODE_LT2 9
+#define S_OPCODE_GT2 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_TIMEOUT -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_UNKNOWN_247 -8
+#define S2_OPCODE_UNKNOWN_258 -9
+#define S2_OPCODE_ACTIONS -10
+
+
+
+class Script {
+public:
+ static uint16 execute(uint16 startOffset);
+
+ static void activateHotspot(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void setHotspotScript(uint16 hotspotId, uint16 scriptIndex, uint16 v3);
+ static void clearSequenceDelayList(uint16 v1, uint16 scriptIndex, uint16 v3);
+ static void method2(uint16 v1, uint16 v2, uint16 v3);
+ static void deactivateHotspot(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void setActionsOffset(uint16 hotspotId, uint16 offset, uint16 v3);
+ static void addDelayedSequence(uint16 seqOffset, uint16 delay, uint16 v3);
+ static void characterInRoom(uint16 characterId, uint16 roomNumber, uint16 v3);
+ static void setHotspotName(uint16 hotspotId, uint16 nameId, uint16 v3);
+ static void displayDialog(uint16 stringId, uint16 v2, uint16 v3);
+ static void remoteRoomViewSetup(uint16 v1, uint16 v2, uint16 v3);
+ static void getDoorBlocked(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void decrInventoryItems(uint16 v1, uint16 v2, uint16 v3);
+ static void setFrameNumber(uint16 hotspotId, uint16 offset, uint16 v3);
+ static void disableHotspot(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 room14DoorClose(uint16 v1, uint16 v2, uint16 v3);
+ static void checkDroppedDesc(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void doorClose(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void displayMessage(uint16 messageId, uint16 characterId, uint16 unknownVal);
+ static void doorOpen(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void givePlayerItem(uint16 hotspotId, uint16 v2, uint16 v3);
+ static void decreaseNumGroats(uint16 characterId, uint16 numGroats, uint16 v3);
+ static void setVillageSkorlTickProc(uint16 v1, uint16 v2, uint16 v3);
+ static void getNumGroats(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 checkCellDoor(uint16 v1, 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
diff --git a/lure/strings.cpp b/lure/strings.cpp
new file mode 100644
index 0000000000..d509e3842d
--- /dev/null
+++ b/lure/strings.cpp
@@ -0,0 +1,300 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/strings.h"
+#include "lure/disk.h"
+#include "lure/room.h"
+
+namespace Lure {
+
+StringData *int_strings = NULL;
+
+StringData::StringData() {
+ int_strings = this;
+
+ for (uint8 ctr = 0; ctr < MAX_NUM_CHARS; ++ctr) _chars[ctr] = NULL;
+ _numChars = 0;
+ _names = Disk::getReference().getEntry(NAMES_RESOURCE_ID);
+ _strings[0] = Disk::getReference().getEntry(STRINGS_RESOURCE_ID);
+ _strings[1] = Disk::getReference().getEntry(STRINGS_2_RESOURCE_ID);
+ _strings[2] = Disk::getReference().getEntry(STRINGS_3_RESOURCE_ID);
+
+ // Add in the list of bit sequences, and what characters they represent
+ add("00", ' ');
+ add("0100", 'e');
+ add("0101", 'o');
+ add("0110", 't');
+ add("01110", 'a');
+ add("01111", 'n');
+ add("1000", 's');
+ add("1001", 'i');
+ add("1010", 'r');
+ add("10110", 'h');
+ add("101110", 'u');
+ add("1011110", 'l');
+ add("1011111", 'd');
+ add("11000", 'y');
+ add("110010", 'g');
+ add("110011", '\0');
+ add("110100", 'w');
+ add("110101", 'c');
+ add("110110", 'f');
+ add("1101110", '.');
+ add("1101111", 'm');
+ add("111000", 'p');
+ add("111001", 'b');
+ add("1110100", ',');
+ add("1110101", 'k');
+ add("1110110", '\'');
+ add("11101110", 'I');
+ add("11101111", 'v');
+ add("1111000", '!');
+ add("1111001", '\xb4');
+ add("11110100", 'T');
+ add("11110101", '\xb5');
+ add("11110110", '?');
+ add("111101110", '\xb2');
+ add("111101111", '\xb3');
+ add("11111000", 'W');
+ add("111110010", 'H');
+ add("111110011", 'A');
+ add("111110100", '\xb1');
+ add("111110101", 'S');
+ add("111110110", 'Y');
+ add("1111101110", 'G');
+ add("11111011110", 'M');
+ add("11111011111", 'N');
+ add("111111000", 'O');
+ add("1111110010", 'E');
+ add("1111110011", 'L');
+ add("1111110100", '-');
+ add("1111110101", 'R');
+ add("1111110110", 'B');
+ add("11111101110", 'D');
+ add("11111101111", '\xa6');
+ add("1111111000", 'C');
+ add("11111110010", 'x');
+ add("11111110011", 'j');
+ add("1111111010", '\xac');
+ add("11111110110", '\xa3');
+ add("111111101110", 'P');
+ add("111111101111", 'U');
+ add("11111111000", 'q');
+ add("11111111001", '\xad');
+ add("111111110100", 'F');
+ add("111111110101", '1');
+ add("111111110110", '\xaf');
+ add("1111111101110", ';');
+ add("1111111101111", 'z');
+ add("111111111000", '\xa5');
+ add("1111111110010", '2');
+ add("1111111110011", '\xb0');
+ add("111111111010", 'K');
+ add("1111111110110", '%');
+ add("11111111101110", '\xa2');
+ add("11111111101111", '5');
+ add("1111111111000", ':');
+ add("1111111111001", 'J');
+ add("1111111111010", 'V');
+ add("11111111110110", '6');
+ add("11111111110111", '3');
+ add("1111111111100", '\xab');
+ add("11111111111010", '\xae');
+ add("111111111110110", '0');
+ add("111111111110111", '4');
+ add("11111111111100", '7');
+ add("111111111111010", '9');
+ add("111111111111011", '"');
+ add("111111111111100", '8');
+ add("111111111111101", '\xa7');
+ add("1111111111111100", '/');
+ add("1111111111111101", 'Q');
+ add("11111111111111100", '\xa8');
+ add("11111111111111101", '(');
+ add("111111111111111100", ')');
+ add("111111111111111101", '\x99');
+ add("11111111111111111", '\xa9');
+}
+
+StringData::~StringData() {
+ int_strings = NULL;
+
+ 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;
+}
+
+void StringData::initPosition(uint16 stringId) {
+ uint16 roomNumber = Room::getReference().roomNumber();
+ byte *stringTable;
+
+ 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;
+ }
+ readBit();
+}
+
+// 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] != NULL; ++index) {
+ if ((_chars[index]->_numBits == numBits) &&
+ (_chars[index]->_sequence == searchValue))
+ return _chars[index]->_ascii;
+ }
+ }
+
+ error("Unknown bit sequence encountered when decoding string");
+}
+
+void StringData::getString(uint16 stringId, char *dest, const char *hotspotName,
+ const char *actionName) {
+ char ch;
+ char *destPos = dest;
+ initPosition(stringId);
+
+ ch = readCharacter();
+ while (ch != '\0') {
+ if (ch == '%') {
+ // Copy over hotspot or action
+ ch = readCharacter();
+ const char *p = (ch == '1') ? hotspotName : actionName;
+ strcpy(destPos, p);
+ destPos += strlen(p);
+ } else if ((uint8) ch >= 0xa0) {
+ const char *p = getName((uint8) ch - 0xa0);
+ strcpy(destPos, p);
+ destPos += strlen(p);
+ } else {
+ *destPos++ = ch;
+ }
+
+ ch = readCharacter();
+ }
+
+ *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 = *((uint16 *) _names->data()) / 2;
+ if (nameIndex >= numNames)
+ error("Invalid name index was passed to getCharacterName");
+
+ uint16 nameStart = *((uint16 *) (_names->data() + (nameIndex * 2)));
+ return (char *) (_names->data() + nameStart);
+}
+
+} // namespace Lure
diff --git a/lure/strings.h b/lure/strings.h
new file mode 100644
index 0000000000..03c1da2440
--- /dev/null
+++ b/lure/strings.h
@@ -0,0 +1,67 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#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 _bitMask;
+
+ void add(const char *sequence, char ascii);
+ void initPosition(uint16 stringId);
+ char readCharacter();
+ byte readBit();
+public:
+ StringData();
+ ~StringData();
+ static StringData &getReference();
+
+ void getString(uint16 stringId, char *dest, const char *hotspotName, const char *actionName);
+ char *getName(uint8 nameIndex);
+};
+
+} // namespace Lure
+
+#endif
diff --git a/lure/surface.cpp b/lure/surface.cpp
new file mode 100644
index 0000000000..e254cfe501
--- /dev/null
+++ b/lure/surface.cpp
@@ -0,0 +1,456 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/surface.h"
+#include "lure/decode.h"
+#include "lure/system.h"
+#include "lure/events.h"
+#include "lure/screen.h"
+#include "lure/room.h"
+#include "lure/strings.h"
+
+namespace Lure {
+
+// These variables hold resources commonly used by the Surfaces, and must be initialised and freed
+// by the static Surface methods initialise and deinitailse
+
+static MemoryBlock *int_font = NULL;
+static MemoryBlock *int_dialog_frame = NULL;
+static uint8 fontSize[NUM_CHARS_IN_FONT];
+
+void Surface::initialise() {
+ int_font = Disk::getReference().getEntry(FONT_RESOURCE_ID);
+ int_dialog_frame = Disk::getReference().getEntry(DIALOG_RESOURCE_ID);
+
+ // Calculate the size of each font character
+ for (int ctr = 0; ctr < NUM_CHARS_IN_FONT; ++ctr) {
+ byte *pChar = int_font->data() + (ctr * 8);
+ fontSize[ctr] = 0;
+
+ for (int yp = 0; yp < FONT_HEIGHT; ++yp)
+ {
+ byte v = *pChar++;
+
+ for (int xp = 0; xp < FONT_WIDTH; ++xp) {
+ if ((v & 0x80) && (xp > fontSize[ctr]))
+ fontSize[ctr] = xp;
+ v = (v << 1) & 0xff;
+ }
+ }
+
+ // If character is empty, like for a space, give a default size
+ if (fontSize[ctr] == 0) fontSize[ctr] = 2;
+ }
+}
+
+void Surface::deinitialise() {
+ delete int_font;
+ delete int_dialog_frame;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Surface::Surface(MemoryBlock *src, uint16 wdth, uint16 hght): _data(src),
+ _width(wdth), _height(hght) {
+ if ((uint32) (wdth * hght) != src->size())
+ error("Surface dimensions do not match size of passed data");
+}
+
+Surface::Surface(uint16 wdth, uint16 hght): _data(Memory::allocate(wdth*hght)),
+ _width(wdth), _height(hght) {
+}
+
+Surface::~Surface() {
+ delete _data;
+}
+
+void Surface::loadScreen(uint16 resourceId) {
+ MemoryBlock *rawData = Disk::getReference().getEntry(resourceId);
+ PictureDecoder decoder;
+ MemoryBlock *tmpScreen = decoder.decode(rawData, FULL_SCREEN_HEIGHT * FULL_SCREEN_WIDTH);
+ delete rawData;
+ empty();
+ copyFrom(tmpScreen, MENUBAR_Y_SIZE * FULL_SCREEN_WIDTH);
+
+ delete tmpScreen;
+}
+
+void Surface::writeChar(uint16 x, uint16 y, uint8 ascii, bool transparent, uint8 colour) {
+ byte *const addr = _data->data() + (y * _width) + x;
+
+ if ((ascii < 32) || (ascii >= 32 + NUM_CHARS_IN_FONT))
+ error("Invalid ascii character passed for display '%d'", ascii);
+
+ uint8 v;
+ byte *pFont = int_font->data() + ((ascii - 32) * 8);
+ byte *pDest;
+ uint8 charWidth = 0;
+
+ for (int y1 = 0; y1 < 8; ++y1) {
+ v = *pFont++;
+ pDest = addr + (y1 * _width);
+
+ for (int x1 = 0; x1 < 8; ++x1, ++pDest) {
+ if (v & 0x80) {
+ *pDest = colour;
+ if (x1+1 > charWidth) charWidth = x1 + 1;
+ }
+ else if (!transparent) *pDest = 0;
+ v = (v << 1) & 0xff;
+ }
+ }
+}
+
+void Surface::writeString(uint16 x, uint16 y, Common::String line, bool transparent,
+ uint8 colour, bool varLength) {
+ const char *sPtr = line.c_str();
+
+ while (*sPtr) {
+ writeChar(x, y, (uint8) *sPtr, transparent, colour);
+
+ // Move to after the character in preparation for the next character
+ if (!varLength) x += FONT_WIDTH;
+ else x += fontSize[*sPtr - ' '] + 2;
+
+ ++sPtr; // Move to next character
+ }
+}
+
+void Surface::transparentCopyTo(Surface *dest) {
+ if (dest->width() != _width)
+ error("Incompatible surface sizes for transparent copy");
+
+ byte *pSrc = _data->data();
+ byte *pDest = dest->data().data();
+ uint16 numBytes = MIN(_height,dest->height()) * FULL_SCREEN_WIDTH;
+
+ while (numBytes-- > 0) {
+ if (*pSrc) *pDest = *pSrc;
+
+ ++pSrc;
+ ++pDest;
+ }
+}
+
+void Surface::copyTo(Surface *dest)
+{
+ copyTo(dest, 0, 0);
+}
+
+void Surface::copyTo(Surface *dest, uint16 x, uint16 y)
+{
+ if ((x == 0) && (dest->width() == _width)) {
+ // Use fast data transfer
+ uint32 dataSize = dest->data().size() - (y * _width);
+ if (dataSize > _data->size()) dataSize = _data->size();
+ dest->data().copyFrom(_data, 0, y * _width, dataSize);
+ } else {
+ // Use slower transfer
+ Rect rect;
+ rect.left = 0; rect.top = 0;
+ rect.right = _width-1; rect.bottom = _height-1;
+ copyTo(dest, rect, x, y);
+ }
+}
+
+void Surface::copyTo(Surface *dest, const Rect &srcBounds,
+ uint16 destX, uint16 destY, int transparentColour) {
+ for (uint16 y=0; y<=(srcBounds.bottom-srcBounds.top); ++y) {
+ const uint32 srcPos = (srcBounds.top + y) * _width + srcBounds.left;
+ const uint32 destPos = (destY+y) * dest->width() + destX;
+
+ uint16 numBytes = srcBounds.right-srcBounds.left+1;
+ if (transparentColour == -1) {
+ // No trnnsparent colour, so copy all the bytes of the line
+ dest->data().copyFrom(_data, srcPos, destPos, numBytes);
+ } else {
+ byte *pSrc = _data->data() + srcPos;
+ byte *pDest = dest->data().data() + destPos;
+
+ while (numBytes-- > 0) {
+ if (*pSrc != (uint8) transparentColour)
+ *pDest = *pSrc;
+ ++pSrc;
+ ++pDest;
+ }
+ }
+ }
+}
+
+void Surface::copyFrom(MemoryBlock *src, uint32 destOffset) {
+ uint32 size = _data->size() - destOffset;
+ if (src->size() > size) size = src->size();
+ _data->copyFrom(src, 0, destOffset, size);
+}
+
+// fillRect
+// Fills a rectangular area with a colour
+
+void Surface::fillRect(const Rect &r, uint8 colour) {
+ for (int yp = r.top; yp <= r.bottom; ++yp) {
+ byte *const addr = _data->data() + (yp * _width) + r.left;
+ memset(addr, colour, r.width());
+ }
+}
+
+// createDialog
+// Forms a dialog encompassing the entire surface
+
+void copyLine(byte *pSrc, byte *pDest, uint16 leftSide, uint16 center, uint16 rightSide) {
+ // Left area
+ memcpy(pDest, pSrc, leftSide);
+ pSrc += leftSide; pDest += leftSide;
+ // Center area
+ memset(pDest, *pSrc, center);
+ ++pSrc; pDest += center;
+ // Right side
+ memcpy(pDest, pSrc, rightSide);
+ pSrc += rightSide; pDest += rightSide;
+}
+
+void Surface::createDialog(bool blackFlag) {
+ if ((_width < 20) || (_height < 20)) return;
+
+ byte *pSrc = int_dialog_frame->data();
+ byte *pDest = _data->data();
+ uint16 xCenter = _width - DIALOG_EDGE_SIZE * 2;
+ uint16 yCenter = _height - DIALOG_EDGE_SIZE * 2;
+
+ // Dialog top
+ for (int y = 0; y < 9; ++y) {
+ copyLine(pSrc, pDest, DIALOG_EDGE_SIZE - 2, xCenter + 2, DIALOG_EDGE_SIZE);
+ pSrc += (DIALOG_EDGE_SIZE - 2) + 1 + DIALOG_EDGE_SIZE;
+ pDest += _width;
+ }
+
+ // Dialog sides - note that the same source data gets used for all side lines
+ for (int y = 0; y < yCenter; ++y) {
+ copyLine(pSrc, pDest, DIALOG_EDGE_SIZE, xCenter, DIALOG_EDGE_SIZE);
+ pDest += _width;
+ }
+ pSrc += DIALOG_EDGE_SIZE * 2 + 1;
+
+ // Dialog bottom
+ for (int y = 0; y < 9; ++y) {
+ copyLine(pSrc, pDest, DIALOG_EDGE_SIZE, xCenter + 1, DIALOG_EDGE_SIZE - 1);
+ pSrc += DIALOG_EDGE_SIZE + 1 + (DIALOG_EDGE_SIZE - 1);
+ pDest += _width;
+ }
+
+ // Final processing - if black flag set, clear dialog inside area
+ if (blackFlag) {
+ Rect r = Rect(DIALOG_EDGE_SIZE, DIALOG_EDGE_SIZE,
+ _width - DIALOG_EDGE_SIZE, _height-DIALOG_EDGE_SIZE);
+ fillRect(r, 0);
+ }
+}
+
+void Surface::copyToScreen(uint16 x, uint16 y) {
+ OSystem &system = System::getReference();
+ system.copyRectToScreen(_data->data(), _width, x, y, _width, _height);
+ system.updateScreen();
+}
+
+void Surface::centerOnScreen() {
+ OSystem &system = System::getReference();
+
+ system.copyRectToScreen(_data->data(), _width,
+ (FULL_SCREEN_WIDTH - _width) / 2, (FULL_SCREEN_HEIGHT - _height) / 2,
+ _width, _height);
+ system.updateScreen();
+}
+
+uint16 Surface::textWidth(const char *s, int numChars) {
+ uint16 result = 0;
+ if (numChars == 0) numChars = strlen(s);
+
+ while (numChars-- > 0) result += fontSize[*s++ - ' '] + 2;
+ return result;
+}
+
+Surface *Surface::newDialog(uint16 width, uint8 numLines, char **lines, bool varLength, uint8 colour) {
+ Surface *s = new Surface(width, (DIALOG_EDGE_SIZE + 3) * 2 +
+ numLines * (FONT_HEIGHT - 1));
+ s->createDialog();
+
+ for (uint8 ctr = 0; ctr < numLines; ++ctr)
+ s->writeString(DIALOG_EDGE_SIZE + 3, DIALOG_EDGE_SIZE + 3 +
+ (ctr * (FONT_HEIGHT - 1)), lines[ctr], true, colour, varLength);
+ return s;
+}
+
+Surface *Surface::newDialog(uint16 width, const char *line, uint8 colour) {
+ uint8 numLines = 1;
+ uint16 lineWidth = 0;
+ char *s, *lineCopy;
+ bool newLine;
+
+ s = lineCopy = strdup(line);
+
+ // Scan through the text and insert NULLs to break the line into allowable widths
+
+ while (*s != '\0') {
+ char *wordStart = s;
+ while (*wordStart == ' ') ++wordStart;
+ char *wordEnd = strchr(wordStart, ' ');
+ char *wordEnd2 = strchr(wordStart, '\n');
+ if ((!wordEnd) || ((wordEnd2) && (wordEnd2 < wordEnd))) {
+ wordEnd = wordEnd2;
+ newLine = (wordEnd2 != NULL);
+ } else {
+ newLine = false;
+ }
+
+ if (wordEnd) --wordEnd; // move back one to end of word
+ else wordEnd = strchr(s, '\0') - 1;
+
+ uint16 wordSize = textWidth(s, (int) (wordEnd - s + 1));
+
+ if (lineWidth + wordSize > width - (DIALOG_EDGE_SIZE + 3) * 2) {
+ // Break word onto next line
+ *(wordStart - 1) = '\0';
+ ++numLines;
+ lineWidth = textWidth(wordStart, (int) (wordEnd - wordStart + 1));
+ } else if (newLine) {
+ // Break on newline
+ ++numLines;
+ ++wordEnd;
+ *wordEnd = '\0';
+ lineWidth = 0;
+ } else {
+ // Add word's length to total for line
+ lineWidth += wordSize;
+ }
+
+ s = wordEnd+1;
+ }
+
+ // Set up a list for the start of each line
+ char **lines = (char **) Memory::alloc(sizeof(char *) * numLines);
+ lines[0] = lineCopy;
+ for (int ctr = 1; ctr < numLines; ++ctr)
+ lines[ctr] = strchr(lines[ctr-1], 0) + 1;
+
+ // Create the dialog
+ Surface *result = newDialog(width, numLines, lines, true, colour);
+
+ // Deallocate used resources
+ free(lines);
+ free(lineCopy);
+
+ return result;
+}
+
+Surface *Surface::getScreen(uint16 resourceId) {
+ MemoryBlock *block = Disk::getReference().getEntry(resourceId);
+ PictureDecoder d;
+ MemoryBlock *decodedData = d.decode(block);
+ delete block;
+ return new Surface(decodedData, FULL_SCREEN_WIDTH, decodedData->size() / FULL_SCREEN_WIDTH);
+}
+
+/*--------------------------------------------------------------------------*/
+
+void Dialog::show(const char *text) {
+ Screen &screen = Screen::getReference();
+ Mouse &mouse = Mouse::getReference();
+ mouse.cursorOff();
+
+ Surface *s = Surface::newDialog(INFO_DIALOG_WIDTH, text);
+ s->copyToScreen(INFO_DIALOG_X, INFO_DIALOG_Y);
+
+ // Wait for a keypress or mouse button
+ Events::getReference().waitForPress();
+
+ screen.update();
+ mouse.cursorOn();
+}
+
+void Dialog::show(uint16 stringId) {
+ char buffer[MAX_DESC_SIZE];
+ Room &r = Room::getReference();
+ StringData &sl = StringData::getReference();
+
+ Action action = r.getCurrentAction();
+
+ const char *actionName = (action == NONE) ? NULL : actionList[action];
+ char hotspotName[MAX_HOTSPOT_NAME_SIZE];
+ if (r.hotspotId() == 0)
+ strcpy(hotspotName, "");
+ else
+ sl.getString(r.hotspot().nameId, hotspotName, NULL, NULL);
+
+ sl.getString(stringId, buffer, hotspotName, actionName);
+ show(buffer);
+}
+
+void Dialog::showMessage(uint16 messageId, uint16 characterId) {
+ MemoryBlock *data = Resources::getReference().messagesData();
+ uint16 *v = (uint16 *) data->data();
+ uint16 v2, idVal;
+ messageId &= 0x7fff;
+
+ // Skip through header to find table for given character
+ while (READ_LE_UINT16(v) != characterId) v += 2;
+
+ // Scan through secondary list
+ ++v;
+ v = (uint16 *) (data->data() + READ_LE_UINT16(v));
+ v2 = 0;
+ while ((idVal = READ_LE_UINT16(v)) != 0xffff) {
+ ++v;
+ if (READ_LE_UINT16(v) == messageId) break;
+ ++v;
+ }
+ // default response if a specific response not found
+ if (idVal == 0xffff) idVal = 0x8c4;
+
+ if (idVal == 0x76) {
+ /*
+ call sub_154 ; (64E7)
+ mov ax,word ptr ds:[5813h] ; (273F:5813=1BA3h)
+ mov [bx+ANIM_SEGMENT],ax
+ mov ax,word ptr ds:[5817h] ; (273F:5817=0ED8Eh)
+ mov [bx+ANIM_FRAME],ax
+ retn
+*/
+ } else if (idVal == 0x120) {
+ /*
+ call sub_154 ; (64E7)
+ mov ax,word ptr ds:[5813h] ; (273F:5813=1BA3h)
+ mov [bx+ANIM_SEGMENT],ax
+ mov ax,word ptr ds:[5817h] ; (273F:5817=0ED8Eh)
+ shl ax,1
+ mov [bx+ANIM_FRAME],ax
+*/
+ } else if (idVal >= 0x8000) {
+ // Handle string display
+ idVal &= 0x7fff;
+ Dialog::show(idVal);
+
+ } else if (idVal != 0) {
+ /* still to be decoded */
+
+ }
+}
+
+} // end of namespace Lure
diff --git a/lure/surface.h b/lure/surface.h
new file mode 100644
index 0000000000..9febf542f5
--- /dev/null
+++ b/lure/surface.h
@@ -0,0 +1,83 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_surface_h__
+#define __lure_surface_h__
+
+#include "common/stdafx.h"
+#include "common/str.h"
+#include "lure/disk.h"
+#include "lure/luredefs.h"
+
+using namespace Common;
+
+namespace Lure {
+
+class Surface {
+private:
+ MemoryBlock *_data;
+ uint16 _width, _height;
+public:
+ Surface(MemoryBlock *src, uint16 width, uint16 height);
+ Surface(uint16 width, uint16 height);
+ ~Surface();
+
+ static void initialise();
+ static void deinitialise();
+
+ uint16 width() { return _width; }
+ uint16 height() { return _height; }
+ MemoryBlock &data() { return *_data; }
+
+ void loadScreen(uint16 resourceId);
+ void writeChar(uint16 x, uint16 y, uint8 ascii, bool transparent, uint8 colour);
+ void writeString(uint16 x, uint16 y, Common::String line, bool transparent,
+ uint8 colour = DIALOG_TEXT_COLOUR, bool varLength = true);
+ void transparentCopyTo(Surface *dest);
+ void copyTo(Surface *dest);
+ void copyTo(Surface *dest, uint16 x, uint16 y);
+ void copyTo(Surface *dest, const Rect &srcBounds, uint16 destX, uint16 destY,
+ int transparentColour = -1);
+ void copyFrom(MemoryBlock *src) { _data->copyFrom(src); }
+ void copyFrom(MemoryBlock *src, uint32 destOffset);
+ void empty() { _data->empty(); }
+ void fillRect(const Rect &r, uint8 colour);
+ void createDialog(bool blackFlag = false);
+ void copyToScreen(uint16 x, uint16 y);
+ void centerOnScreen();
+
+ static uint16 textWidth(const char *s, int numChars = 0);
+ static Surface *newDialog(uint16 width, uint8 numLines, char **lines, bool varLength = true, uint8 colour = DIALOG_TEXT_COLOUR);
+ static Surface *newDialog(uint16 width, const char *lines, uint8 colour = DIALOG_TEXT_COLOUR);
+ static Surface *getScreen(uint16 resourceId);
+};
+
+class Dialog {
+public:
+ static void show(const char *text);
+ static void show(uint16 stringId);
+ static void showMessage(uint16 messageId, uint16 characterId);
+};
+
+} // End of namespace Lure
+
+#endif
diff --git a/lure/system.cpp b/lure/system.cpp
new file mode 100644
index 0000000000..6b305edc57
--- /dev/null
+++ b/lure/system.cpp
@@ -0,0 +1,41 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "lure/system.h"
+
+namespace Lure {
+
+OSystem *int_system = NULL;
+
+System::System(OSystem *sys) {
+ int_system = sys;
+}
+
+System::~System() {
+ int_system = NULL;
+}
+
+OSystem &System::getReference() {
+ return *int_system;
+}
+
+} // end of namespace Lure
diff --git a/lure/system.h b/lure/system.h
new file mode 100644
index 0000000000..8d7471a296
--- /dev/null
+++ b/lure/system.h
@@ -0,0 +1,40 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef __lure_system_h__
+#define __lure_system_h__
+
+#include "common/stdafx.h"
+#include "common/system.h"
+
+namespace Lure {
+
+class System {
+public:
+ System(OSystem *sys);
+ ~System();
+ static OSystem &getReference();
+};
+
+} // end of namspace Lure
+
+#endif