diff options
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 } @@ -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 |