diff options
Diffstat (limited to 'engines')
29 files changed, 9237 insertions, 0 deletions
| diff --git a/engines/engines.mk b/engines/engines.mk index 2c1378290c..e542ffd933 100644 --- a/engines/engines.mk +++ b/engines/engines.mk @@ -60,6 +60,11 @@ DEFINES += -DENABLE_GROOVIE2  endif  endif +ifdef ENABLE_HUGO +DEFINES += -DENABLE_HUGO=$(ENABLE_HUGO) +MODULES += engines/hugo +endif +  ifdef ENABLE_KYRA  DEFINES += -DENABLE_KYRA=$(ENABLE_KYRA)  MODULES += engines/kyra diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp new file mode 100755 index 0000000000..d780dda6d8 --- /dev/null +++ b/engines/hugo/detection.cpp @@ -0,0 +1,234 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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 "engines/advancedDetector.h" + +#include "hugo/hugo.h" +#include "hugo/intro.h" + +namespace Hugo { + +struct HugoGameDescription { +	ADGameDescription desc; +	GameType gameType; +}; + +uint32 HugoEngine::getFeatures() const { +	return _gameDescription->desc.flags; +} + +static const PlainGameDescriptor hugoGames[] = { +	// Games +	{"hugo1", "Hugo 1: Hugo's House of Horrors"}, +	{"hugo2", "Hugo 2: Hugo's Mystery Adventure"}, +	{"hugo3", "Hugo 3: Hugo's Amazon Adventure"}, + +	{0, 0} +}; + +static const HugoGameDescription gameDescriptions[] = { + +	// Hugo1 DOS +	{ +		{ +			"hugo1", 0, +			AD_ENTRY1s("house.art", "c9403b2fe539185c9fd569b6cc4ff5ca", 14811), +			Common::EN_ANY, +			Common::kPlatformPC, +			ADGF_NO_FLAGS, +			Common::GUIO_NONE +		}, +		kGameTypeHugo1 +	}, +	// Hugo1 Windows +	{ +		{ +			"hugo1", 0, +			AD_ENTRY1s("objects.dat", "3ba0f108f7690a05a34c56a02fbe644a", 126488), +			Common::EN_ANY, +			Common::kPlatformWindows, +			GF_PACKED, +			Common::GUIO_NONE +		}, +		kGameTypeHugo1 +	}, +	// Hugo2 DOS +	{ +		{ +			"hugo2", 0, +			AD_ENTRY1s("objects.dat", "88a718cc0ff2b3b25d49aaaa69d6d52c", 155240), +			Common::EN_ANY, +			Common::kPlatformPC, +			GF_PACKED, +			Common::GUIO_NONE +		}, +		kGameTypeHugo2 +	}, +	// Hugo2 Windows +	{ +		{ +			"hugo2", 0, +			AD_ENTRY1s("objects.dat", "5df4ffc851e66a544c0e95e4e084a806", 158480), +			Common::EN_ANY, +			Common::kPlatformWindows, +			GF_PACKED, +			Common::GUIO_NONE +		}, +		kGameTypeHugo2 +	}, +	// Hugo3 DOS +	{ +		{ +			"hugo3", 0, +			AD_ENTRY1s("objects.dat", "bb1b061538a445f2eb99b682c0f506cc", 136419), +			Common::EN_ANY, +			Common::kPlatformPC, +			GF_PACKED, +			Common::GUIO_NONE +		}, +		kGameTypeHugo3 +	}, +	// Hugo3 Windows +	{ +		{ +			"hugo3", 0, +			AD_ENTRY1s("objects.dat", "c9a8af7aa14cc907434eecee3ddd06d3", 136638), +			Common::EN_ANY, +			Common::kPlatformWindows, +			GF_PACKED, +			Common::GUIO_NONE +		}, +		kGameTypeHugo3 +	}, + +	{AD_TABLE_END_MARKER, kGameTypeNone} +}; + +static const ADParams detectionParams = { +	// Pointer to ADGameDescription or its superset structure +	(const byte *)gameDescriptions, +	// Size of that superset structure +	sizeof(HugoGameDescription), +	// Number of bytes to compute MD5 sum for +	5000, +	// List of all engine targets +	hugoGames, +	// Structure for autoupgrading obsolete targets +	0, +	// Name of single gameid (optional) +	0, +	// List of files for file-based fallback detection (optional) +	0, +	// Flags +	0, +	// Additional GUI options (for every game} +	Common::GUIO_NONE, +	// Maximum directory depth +	1, +	// List of directory globs +	0 +}; + +class HugoMetaEngine : public AdvancedMetaEngine { +public: +	HugoMetaEngine() : AdvancedMetaEngine(detectionParams) {} + +	const char *getName() const { +		return "Hugo Engine"; +	} + +	const char *getOriginalCopyright() const { +		return "Hugo Engine (C) 1989-1997 David P. Gray"; +	} + +	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const; + +	bool hasFeature(MetaEngineFeature f) const; +}; + +bool HugoMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const { +	if (gd) { +		*engine = new HugoEngine(syst, (const HugoGameDescription *)gd); +		((HugoEngine *)*engine)->initGame((const HugoGameDescription *)gd); +		((HugoEngine *)*engine)->initGamePart((const HugoGameDescription *)gd); +	} +	return gd != 0; +} + +bool HugoMetaEngine::hasFeature(MetaEngineFeature f) const { +	return false; +} + +} // End of namespace Hugo + +#if PLUGIN_ENABLED_DYNAMIC(HUGO) +REGISTER_PLUGIN_DYNAMIC(HUGO, PLUGIN_TYPE_ENGINE, Hugo::HugoMetaEngine); +#else +REGISTER_PLUGIN_STATIC(HUGO, PLUGIN_TYPE_ENGINE, Hugo::HugoMetaEngine); +#endif + +namespace Hugo { + +void HugoEngine::initGame(const HugoGameDescription *gd) { +	_gameType = gd->gameType; +	_platform = gd->desc.platform; +	_packedFl = (getFeatures() & GF_PACKED); +} + +void HugoEngine::initGamePart(const HugoGameDescription *gd) { +	char tmpStr[8]; +	_gameVariant = _gameType - 1 + (_platform == Common::kPlatformWindows ? 0 : 3); + +//Generate filenames +	if (gd->desc.platform == Common::kPlatformWindows) +		sprintf(tmpStr, "%s%c", gd->desc.gameid, 'w'); +	else +		sprintf(tmpStr, "%s%c", gd->desc.gameid, 'd'); + +	sprintf(_initFilename, "%s-00.SAV", tmpStr); +	sprintf(_saveFilename, "%s-%s.SAV", tmpStr, "%d"); + +	switch (_gameVariant) { +	case 0: +		_introHandler = new intro_1w(*this); +		break; +	case 1: +		_introHandler = new intro_2w(*this); +		break; +	case 2: +		_introHandler = new intro_3w(*this); +		break; +	case 3: +		_introHandler = new intro_1d(*this); +		break; +	case 4: +		_introHandler = new intro_2d(*this); +		break; +	case 5: +		_introHandler = new intro_3d(*this); +		break; +	} +} +} // End of namespace Gob diff --git a/engines/hugo/display.cpp b/engines/hugo/display.cpp new file mode 100755 index 0000000000..fde97c8f00 --- /dev/null +++ b/engines/hugo/display.cpp @@ -0,0 +1,509 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +// Display.c - DIB related code for HUGOWIN + +#include "common/system.h" + +#include "hugo/game.h" +#include "hugo/hugo.h" +#include "hugo/display.h" +#include "hugo/file.h" +#include "hugo/util.h" + +namespace Hugo { + +#define CENTER          -1                  // Used to center text in x +#define NUM_COLORS  16              // Num colors to save in palette +#define DMAX            16              // Size of add/restore rect lists +#define BMAX                (DMAX * 2)  // Size of dirty rect blit list + +#define INX(X, B) (X >= B->x && X <= B->x + B->dx) +#define INY(Y, B) (Y >= B->y && Y <= B->y + B->dy) +#define OVERLAP(A, B) ((INX(A->x, B) || INX(A->x + A->dx, B) || INX(B->x, A) || INX(B->x + B->dx, A)) && (INY(A->y, B) || INY(A->y + A->dy, B) || INY(B->y, A) || INY(B->y + B->dy, A))) + +struct rect_t {                                 // Rectangle used in Display list +	int16 x;                                        // Position in dib +	int16 y;                                        // Position in dib +	int16 dx;                                   // width +	int16 dy;                                   // height +}; + +Screen::Screen(HugoEngine &vm) : _vm(vm) { + +} + +void Screen::createPal() { +	debugC(1, kDebugDisplay, "createPal"); + +	g_system->setPalette(_vm._palette, 0, NUM_COLORS); +} + +// Translate from our 16-color palette to Windows logical palette index +uint32 Screen::GetPalIndex(byte color) { +	debugC(1, kDebugDisplay, "getPalIndex(%d)", color); + +	warning("STUB: GetPalIndex()"); +	return 0; +	//return(PALETTEINDEX(ctab[color])); +} + +// Create DIB headers and init palette +void Screen::initDisplay() { +	debugC(1, kDebugDisplay, "initDisplay"); +	// Create logical palette +	createPal(); +} + +// Move an image from source to destination +void Screen::moveImage(image_pt srcImage, uint16 x1, uint16 y1, uint16 dx, uint16 dy, uint16 width1, image_pt dstImage, uint16 x2, uint16 y2, uint16 width2) { +	int16 wrap_src = width1 - dx;                   // Wrap to next src row +	int16 wrap_dst = width2 - dx;                   // Wrap to next dst row +	int16 x; + +	debugC(3, kDebugDisplay, "moveImage(srcImage, %d, %d, %d, %d, %d, dstImage, %d, %d, %d)", x1, y1, dx, dy, width1, x2, y2, width2); + +	srcImage += y1 * width1 + x1;                   // Offset into src image +	dstImage += y2 * width2 + x2;                   // offset into dst image + +	while (dy--) {                                  // For each row +		for (x = dx; x--;)                          // For each column +			*dstImage++ = *srcImage++; +		srcImage += wrap_src;                       // Wrap to next line +		dstImage += wrap_dst; +	} +} + +void Screen::displayBackground() { +	debugC(1, kDebugDisplay, "displayBackground"); + +	g_system->copyRectToScreen(_frontBuffer, 320, 0, 0, 320, 200); +} + +// Blit the supplied rectangle from _frontBuffer to the screen +void Screen::displayRect(int16 x, int16 y, int16 dx, int16 dy) { + +	/* TODO: Suppress this commented block if it's confirmed to be useless +	    // Find destination rectangle from current scaling +	    int16 sx =  (int16)((int32)config.cx * x  / XPIX); +	    int16 sy =  (int16)((int32)config.cy * (y - DIBOFF_Y) / VIEW_DY); +	    int16 dsx = (int16)((int32)config.cx * dx / XPIX); +	    int16 dsy = (int16)((int32)config.cy * dy / VIEW_DY); +	*/ +	debugC(3, kDebugDisplay, "displayRect(%d, %d, %d, %d)", x, y, dx, dy); + +	g_system->copyRectToScreen(&_frontBuffer[x + y * 320], 320, x, y, dx, dy); +} + +void Screen::remapPal(uint16 oldIndex, uint16 newIndex) { +// Change a color by remapping supplied palette index with new index +	debugC(1, kDebugDisplay, "Remap_pal(%d, %d)", oldIndex, newIndex); + +	warning("STUB: Remap_pal()"); +	//bminfo.bmiColors[oldIndex] = ctab[newIndex]; +} + +void Screen::savePal(Common::WriteStream *f) { +	debugC(1, kDebugDisplay, "savePal"); + +	warning("STUB: savePal()"); +	//fwrite(bminfo.bmiColors, sizeof(bminfo.bmiColors), 1, f); +} + +void Screen::restorePal(Common::SeekableReadStream *f) { +	debugC(1, kDebugDisplay, "restorePal"); + +	warning("STUB: restorePal()"); +	//fread(bminfo.bmiColors, sizeof(bminfo.bmiColors), 1, f); +} + + +// Set the new background color +void Screen::setBackgroundColor(long color) { +	debugC(1, kDebugDisplay, "setBackgroundColor(%ld)", color); + +	// How???  Translate existing pixels in dib before objects rendered? +} + +// Write the supplied character in the supplied color to x,y pixel coords +void Screen::writeChar(int16 x, int16 y, char c, byte color) { +	debugC(1, kDebugDisplay, "writeChar(%d, %d, %c, %d)", x, y, c, color); + +	warning("STUB: writeChar()"); +	// x = (int16)((long) x * config.cx / XPIX); +	// y = (int16)((long) y * config.cy / YPIX); +	// SetTextColor(hDC, GetPalIndex(color)); +	// TextOut(hDC, x, y, &c, 1); +} + +// Clear prompt line for next command +void Screen::clearPromptLine() { +	debugC(1, kDebugDisplay, "clearPromptLine"); +} + + +// Return the overlay state (Foreground/Background) of the currently +// processed object by looking down the current column for an overlay +// base bit set (in which case the object is foreground). +overlayState_t Screen::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) { +	debugC(4, kDebugDisplay, "findOvl"); + +	for (; y < seq_p->lines; y++) {              // Each line in object +		image_pt ovb_p = _vm.getBaseBoundaryOverlay() + ((uint16)(dst_p - _frontBuffer) >> 3);  // Ptr into overlay bits +		if (*ovb_p & (0x80 >> ((uint16)(dst_p - _frontBuffer) & 7))) // Overlay bit is set +			return FG;                              // Found a bit - must be foreground +		dst_p += XPIX; +	} + +	return BG;                                      // No bits set, must be background +} + +// Merge an object frame into _frontBuffer at sx, sy and update rectangle list. +// If fore TRUE, force object above any overlay +void Screen::displayFrame(int sx, int sy, seq_t *seq, bool foreFl) { +	overlayState_t overlayState = UNDEF;            // Overlay state of object +	image_pt image;                                 // Ptr to object image data +	image_pt subFrontBuffer;                        // Ptr to offset in _frontBuffer +	image_pt overlay;                               // Ptr to overlay data +	int16    frontBufferwrap;                       // Wrap dst_p to next line +	int16    imageWrap;                             // Wrap src_p to next line +	uint16   x, y;                                  // Index into object data + +	debugC(3, kDebugDisplay, "displayFrame(%d, %d, seq, %d)", sx, sy, (foreFl) ? 1 : 0); + +	image = seq->imagePtr;                          // Source ptr +	subFrontBuffer = &_frontBuffer[sy * XPIX + sx]; // Destination ptr +	overlay = &_vm.getFirstOverlay()[(sy * XPIX + sx) >> 3];    // Overlay ptr +	frontBufferwrap = XPIX - seq->x2 - 1;           // Wraps dest_p after each line +	imageWrap = seq->bytesPerLine8 - seq->x2 - 1; + +	for (y = 0; y < seq->lines; y++) {              // Each line in object +		for (x = 0; x <= seq->x2; x++) { +			if (*image) {                           // Non-transparent +				overlay = _vm.getFirstOverlay() + ((uint16)(subFrontBuffer - _frontBuffer) >> 3);       // Ptr into overlay bits +				if (*overlay & (0x80 >> ((uint16)(subFrontBuffer - _frontBuffer) & 7))) {   // Overlay bit is set +					if (overlayState == UNDEF)      // Overlay defined yet? +						overlayState = findOvl(seq, subFrontBuffer, y);// No, find it. +					if (foreFl || overlayState == FG)   // Object foreground +						*subFrontBuffer = *image;   // Copy pixel +				} else                              // No overlay +					*subFrontBuffer = *image;       // Copy pixel +			} +			image++; +			subFrontBuffer++; +		} +		image += imageWrap; +		subFrontBuffer += frontBufferwrap; +	} + +	// Add this rectangle to the display list +	displayList(D_ADD, sx, sy, seq->x2 + 1, seq->lines); +} + +// Merge rectangles A,B leaving result in B +void Screen::merge(rect_t *rectA, rect_t *rectB) { +	debugC(6, kDebugDisplay, "merge"); + +	int16 xa = rectA->x + rectA->dx;                // Find x2,y2 for each rectangle +	int16 xb = rectB->x + rectB->dx; +	int16 ya = rectA->y + rectA->dy; +	int16 yb = rectB->y + rectB->dy; + +	rectB->x = MIN(rectA->x, rectB->x);             // Minimum x,y +	rectB->y = MIN(rectA->y, rectB->y); +	rectB->dx = MAX(xa, xb) - rectB->x;             // Maximum dx,dy +	rectB->dy = MAX(ya, yb) - rectB->y; +} + +// Coalesce the rectangles in the restore/add list into one unified +// blist.  len is the sizes of alist or rlist.  blen is current length +// of blist.  bmax is the max size of the blist.  Note that blist can +// have holes, in which case dx = 0.  Returns used length of blist. +int16 Screen::mergeLists(rect_t *list, rect_t *blist, int16 len, int16 blen, int16 bmax) { +	int16   coalesce[BMAX];                         // List of overlapping rects + +	debugC(4, kDebugDisplay, "mergeLists"); + +	// Process the list +	for (int16 a = 0; a < len; a++, list++) { +		// Compile list of overlapping rectangles in blit list +		int16 c = 0; +		rect_t *bp = blist; +		for (int16 b = 0; b < blen; b++, bp++) +			if (bp->dx)                             // blist entry used +				if (OVERLAP(list, bp)) +					coalesce[c++] = b; + +		// Any overlapping blit rects? +		if (c == 0)                                 // None, add a new entry +			blist[blen++] = *list; +		else {                                      // At least one overlapping +			// Merge add-list entry with first blist entry +			bp = &blist[coalesce[0]]; +			merge(list, bp); + +			// Merge any more blist entries +			while (--c) { +				rect_t *cp = &blist[coalesce[c]]; +				merge(cp, bp); +				cp->dx = 0;                         // Delete entry +			} +		} +	} +	return blen; +} + +// Process the display list +// Trailing args are int16 x,y,dx,dy for the D_ADD operation +void Screen::displayList(dupdate_t update, ...) { +	static int16  addIndex, restoreIndex;           // Index into add/restore lists +	static rect_t restoreList[DMAX];                // The restore list +	static rect_t addList[DMAX];                    // The add list +	static rect_t blistList[BMAX];                  // The blit list +	int16         blitLength = 0;                   // Length of blit list +	rect_t       *p;                                // Ptr to dlist entry +	va_list       marker;                           // Args used for D_ADD operation + +	debugC(6, kDebugDisplay, "displayList"); + +	switch (update) { +	case D_INIT:                                    // Init lists, restore whole screen +		addIndex = restoreIndex = 0; +		memcpy(_frontBuffer, _backBuffer, sizeof(_frontBuffer)); +		break; +	case D_ADD:                                     // Add a rectangle to list +		if (addIndex >= DMAX) { +			Utils::Warn(false, "Display list exceeded"); +			return; +		} +		va_start(marker, update);                   // Initialize variable arguments +		p = &addList[addIndex]; +		p->x  = va_arg(marker, int);                // x +		p->y  = va_arg(marker, int);                // y +		p->dx = va_arg(marker, int);                // dx +		p->dy = va_arg(marker, int);                // dy +		va_end(marker);                             // Reset variable arguments +		addIndex++; +		break; +	case D_DISPLAY:                                 // Display whole list +		// Don't blit if newscreen just loaded because _frontBuffer will +		// get blitted via InvalidateRect() at end of this cycle +		// and blitting here causes objects to appear too soon. +		if (_vm.getGameStatus().newScreenFl) { +			_vm.getGameStatus().newScreenFl = false; +			break; +		} + +		// Coalesce restore-list, add-list into combined blit-list +		blitLength = mergeLists(restoreList, blistList, restoreIndex, blitLength, BMAX); +		blitLength = mergeLists(addList, blistList, addIndex,  blitLength, BMAX); + +		// Blit the combined blit-list +		for (restoreIndex = 0, p = blistList; restoreIndex < blitLength; restoreIndex++, p++) +			if (p->dx)                              // Marks a used entry +				displayRect(p->x, p->y, p->dx, p->dy); +		break; +	case D_RESTORE:                                 // Restore each rectangle +		for (restoreIndex = 0, p = addList; restoreIndex < addIndex; restoreIndex++, p++) { +			// Restoring from _backBuffer to _frontBuffer +			restoreList[restoreIndex] = *p;         // Copy add-list to restore-list +			moveImage(_backBuffer, p->x, p->y, p->dx, p->dy, XPIX, _frontBuffer, p->x, p->y, XPIX); +		} +		addIndex = 0;                               // Reset add-list +		break; +	} +} + +void Screen::writeChr(int sx, int sy, byte color, char *local_fontdata) { +	/* +	    Write supplied character (font data) at sx,sy in supplied color +	    Font data as follows: + +	    *(fontdata+1) = Font Height (pixels) +	    *(fontdata+1) = Font Width (pixels) +	    *(fontdata+x) = Font Bitmap (monochrome) +	*/ + +	debugC(2, kDebugDisplay, "writeChr(%d, %d, %d, %d)", sx, sy, color, local_fontdata[0]); + +	byte height = local_fontdata[0]; +	byte width = 8; //local_fontdata[1]; + +	//warning("STUB: writechr(sx %u, sy %u, color %u, height %u, width %u)", sx, sy, color, height, width); + +	// This can probably be optimized quite a bit... +	for (int y = 0; y < height; ++y) +		for (int x = 0; x < width; ++x) { +			int pixel = y * width + x; +			int bitpos = pixel % 8; +			int offset = pixel / 8; +			byte bitTest = (1 << bitpos); +			if ((local_fontdata[2 + offset] & bitTest) == bitTest) +				_frontBuffer[(sy + y) * 320 + sx + x] = color; +			//printf("offset: %u, bitpos %u\n", offset, bitpos); +		} +} + +// Returns height of characters in current font +int16 Screen::fontHeight() { +	debugC(2, kDebugDisplay, "fontHeight"); + +	static int16 height[NUM_FONTS] = {5, 7, 8}; +	return(height[_fnt - FIRST_FONT]); +} + +/* TODO: Suppress block if it's confirmed to be useless */ +// static int16 Char_len (char c) { +// /* Returns length of single character in pixels */ +//	return (*(_font[_fnt][c] + 1) + 1); +// } + + +// Returns length of supplied string in pixels +int16 Screen::stringLength(char *s) { +	int16 sum; +	byte **fontArr = _font[_fnt]; + +	debugC(2, kDebugDisplay, "stringLength(%s)", s); + +	for (sum = 0; *s; s++) +		sum += *(fontArr[*s] + 1) + 1; + +	return(sum); +} + +// Return x which would center supplied string +int16 Screen::center(char *s) { +	debugC(1, kDebugDisplay, "center(%s)", s); + +	return ((int16)((XPIX - stringLength(s)) >> 1)); +} + +// Write string at sx,sy in supplied color in current font +// If sx == CENTER, center it +void Screen::writeStr(int16 sx, int16 sy, char *s, byte color) { +	byte **font = _font[_fnt]; + +	debugC(2, kDebugDisplay, "writeStr(%d, %d, %s, %d)", sx, sy, s, color); + +	if (sx == CENTER) +		sx = center(s); + +	for (; *s; s++) { +		writeChr(sx, sy, color, (char *)font[*s]); +		sx += *(font[*s] + 1) + 1; +	} +} + +// Shadowed version of writestr +void Screen::shadowStr(int16 sx, int16 sy, char *s, byte color) { +	debugC(1, kDebugDisplay, "shadowStr(%d, %d, %s, %d)", sx, sy, s, color); + +	if (sx == CENTER) +		sx = center(s); + +	writeStr(sx + 1, sy + 1, s, _TBLACK); +	writeStr(sx, sy, s, color); +} + +// Load font file, construct font ptrs and reverse data bytes +void Screen::loadFont(int16 fontId) { +	byte  height, width; +	static bool fontLoadedFl[NUM_FONTS] = {0, 0, 0}; + +	debugC(2, kDebugDisplay, "loadFont(%d)", fontId); + +	_fnt = fontId - FIRST_FONT;                     // Set current font number + +	if (fontLoadedFl[_fnt])                             // If already loaded, return +		return; + +	fontLoadedFl[_fnt] = true; +	_vm.file().readUIFItem(fontId, _fontdata[_fnt]); + +	// Compile font ptrs.  Note: First ptr points to height,width of font +	_font[_fnt][0] = _fontdata[_fnt];               // Store height,width of fonts + +	int16 offset = 2;                                       // Start at fontdata[2] ([0],[1] used for height,width) + +	// Setup the font array (127 characters) +	for (int i = 1; i < 128; i++) { +		_font[_fnt][i] = _fontdata[_fnt] + offset; +		height = *(_fontdata[_fnt] + offset); +		width  = *(_fontdata[_fnt] + offset + 1); + +		int16 size = height * ((width + 7) >> 3); +		for (int j = 0; j < size; j++) +			Utils::reverseByte(&_fontdata[_fnt][offset + 2 + j]); + +		offset += 2 + size; +	} + +	// for (i = 0; i < 128; ++i) { +	//      if( (char)i != 'f' && (char)i != '\\'){ +	//          continue; +	//      } +	//      int myHeight = _font[_fnt][i][0]; +	//      int myWidth = _font[_fnt][i][1]; +	//      printf("\n\nFor the letter %c, (%u, %u):\n", i, myWidth, myHeight); +	//      for (int y = 0; y < myHeight; ++y) { +	//          for (int x = 0; x < 8; ++x) { +	//              int pixel = y * (8) + x; +	//              int bitpos = pixel % 8; +	//              int offset = pixel / 8; +	//              byte bitTest = (1 << bitpos); +	//              if ((_font[_fnt][i][2 + offset] & bitTest) == bitTest) +	//                  printf("1"); +	//              else +	//                  printf("0"); +	//          } +	//          printf("\n"); +	//      } +	//  } +} + +void Screen::userHelp() { +// Introduce user to the game +// DOS versions Only +	Utils::Box(BOX_ANY , "F1  - Press F1 again\n" +	           "      for instructions\n" +	           "F2  - Sound on/off\n" +	           "F3  - Recall last line\n" +	           "F4  - Save game\n" +	           "F5  - Restore game\n" +	           "F6  - Inventory\n" +	           "F8  - Turbo button\n" +	           "F9  - Boss button\n\n" +	           "ESC - Return to game"); +} + +} // end of namespace Hugo diff --git a/engines/hugo/display.h b/engines/hugo/display.h new file mode 100755 index 0000000000..ceb87481b0 --- /dev/null +++ b/engines/hugo/display.h @@ -0,0 +1,109 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef HUGO_DISPLAY_H +#define HUGO_DISPLAY_H +namespace Hugo { + +enum overlayState_t {UNDEF, FG, BG};                        // Overlay state +struct rect_t; + +class Screen { +public: +	Screen(HugoEngine &vm); + +	int16    fontHeight(); +	int16    stringLength(char *s); + +	void     displayBackground(); +	void     displayFrame(int sx, int sy, seq_t *seq, bool foreFl); +	void     displayList(dupdate_t update, ...); +	void     displayRect(int16 x, int16 y, int16 dx, int16 dy); +	void     initDisplay(); +	void     loadFont(int16 fontId); +	void     moveImage(image_pt srcImage, uint16 x1, uint16 y1, uint16 dx, uint16 dy, uint16 width1, image_pt dstImage, uint16 x2, uint16 y2, uint16 width2); +	void     remapPal(uint16 oldIndex, uint16 newIndex); +	void     restorePal(Common::SeekableReadStream *f); +	void     savePal(Common::WriteStream *f); +	void     setBackgroundColor(long color); +	void     shadowStr(int16 sx, int16 sy, char *s, byte color); +	void     userHelp(); +	void     writeChar(int16 x, int16 y, char c, byte color); +	void     writeStr(int16 sx, int16 sy, char *s, byte color); + +	icondib_t &getIconBuffer() { +		return _iconBuffer; +	} +	viewdib_t &getBackBuffer() { +		return _backBuffer; +	} +	viewdib_t &getBackBufferBackup() { +		return _backBufferBackup; +	} +	viewdib_t &getFrontBuffer() { +		return _frontBuffer; +	} +	viewdib_t &getGUIBuffer() { +		return _GUIBuffer; +	} + +private: +	HugoEngine &_vm; + +	// Fonts used in dib (non-GDI) +	byte _fnt;                                      // Current font number +	byte _fontdata[NUM_FONTS][FONTSIZE];            // Font data +	byte *_font[NUM_FONTS][FONT_LEN];               // Ptrs to each char + +	viewdib_t _frontBuffer; +	viewdib_t _backBuffer; +	viewdib_t _GUIBuffer;                           // User interface images +	viewdib_t _backBufferBackup;                    // Backup _backBuffer during inventory +	icondib_t _iconBuffer;                          // Inventory icon DIB + +	void createPal(); +	overlayState_t findOvl(seq_t *seq_p, image_pt dst_p, uint16 y); +	void merge(rect_t *rectA, rect_t *rectB); +	int16 mergeLists(rect_t *list, rect_t *blist, int16 len, int16 blen, int16 bmax); +	void writeChr(int sx, int sy, byte color, char *local_fontdata); +	int16 center(char *s); + +// Also used in rout.cpp when DEBUG_ROUTE is defined +	unsigned int GetPalIndex(byte color); + +// Useless ? +	void     clearPromptLine(); + +}; + +} // end of namespace Hugo +#endif //HUGO_DISPLAY_H diff --git a/engines/hugo/engine.cpp b/engines/hugo/engine.cpp new file mode 100755 index 0000000000..ef93ef6279 --- /dev/null +++ b/engines/hugo/engine.cpp @@ -0,0 +1,993 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo 1-3 Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#include "common/system.h" +#include "common/random.h" +#include "common/EventRecorder.h" + +#include "hugo/game.h" +#include "hugo/hugo.h" +#include "hugo/engine.h" +#include "hugo/global.h" +#include "hugo/file.h" +#include "hugo/schedule.h" +#include "hugo/display.h" +#include "hugo/parser.h" +#include "hugo/route.h" +#include "hugo/util.h" +#include "hugo/sound.h" + +namespace Hugo { + +#define EDGE           10                       // Closest object can get to edge of screen +#define EDGE2          (EDGE * 2)           // Push object further back on edge collision +#define SHIFT          8                            // Place hero this far inside bounding box +#define MAX_OBJECTS    128              // Used in Update_images() +#define BOUND(X, Y)   ((_boundary[Y * XBYTES + X / 8] & (0x80 >> X % 8)) != 0)  // Boundary bit set + +config_t    _config;                                // User's config +maze_t      _maze = {false, 0, 0, 0, 0, 0, 0, 0, 0}; // Default to not in maze +hugo_boot_t _boot;                                  // Boot info structure file +char        _textBoxBuffer[MAX_BOX];                // Buffer for text box +command_t   _line = "";                             // Line of user text input + + +// Sets the playlist to be the default tune selection +void HugoEngine::initPlaylist(bool playlist[MAX_TUNES]) { +	debugC(1, kDebugEngine, "initPlaylist"); + +	for (int16 i = 0; i < MAX_TUNES; i++) +		playlist[i] = false; +	for (int16 i = 0; _defltTunes[i] != -1; i++) +		playlist[_defltTunes[i]] = true; +} + +// Initialize the dynamic game status +void HugoEngine::initStatus() { +	debugC(1, kDebugEngine, "initStatus"); +	_status.initSaveFl    = false;                  // Don't force initial save +	_status.storyModeFl   = false;                  // Not in story mode +	_status.gameOverFl    = false;                  // Hero not knobbled yet +	_status.recordFl      = false;                  // Not record mode +	_status.playbackFl    = false;                  // Not playback mode +	_status.demoFl        = false;                  // Not demo mode +	_status.textBoxFl     = false;                  // Not processing a text box +//	Strangerke - Not used ? +//	_status.mmtime        = false;                   // Multimedia timer support +	_status.lookFl        = false;                  // Toolbar "look" button +	_status.recallFl      = false;                  // Toolbar "recall" button +	_status.leftButtonFl  = false;                  // Left mouse button pressed +	_status.rightButtonFl = false;                  // Right mouse button pressed +	_status.newScreenFl   = false;                  // Screen not just loaded +	_status.jumpExitFl    = false;                  // Can't jump to a screen exit +	_status.godModeFl     = false;                  // No special cheats allowed +	_status.helpFl        = false;                  // Not calling WinHelp() +	_status.path[0]       = 0;                      // Path to write files +	_status.saveSlot      = 0;                      // Slot to save/restore game +	_status.screenWidth   = 0;                      // Desktop screen width + +	// Initialize every start of new game +	_status.tick            = 0;                    // Tick count +	_status.saveTick        = 0;                    // Time of last save +	_status.viewState       = V_IDLE;               // View state +	_status.inventoryState  = I_OFF;                // Inventory icon bar state +	_status.inventoryHeight = 0;                    // Inventory icon bar pos +	_status.inventoryObjId  = -1;                   // Inventory object selected (none) +	_status.routeIndex      = -1;                   // Hero not following a route +	_status.go_for          = GO_SPACE;             // Hero walking to space +	_status.go_id           = -1;                   // Hero not walking to anything +} + +// Initialize default config values.  Must be done before Initialize(). +// Reset needed to save config.cx,cy which get splatted during OnFileNew() +void HugoEngine::initConfig(inst_t action) { +	static int16 cx, cy;                    // Save window size, pos +	int16        i; + +	debugC(1, kDebugEngine, "initConfig(%d)", action); + +	switch (action) { +	case INSTALL: +		_config.musicFl = true;                     // Music state initially on +		_config.soundFl = true;                     // Sound state initially on +		_config.turboFl = false;                    // Turbo state initially off +		_config.backgroundMusicFl = false;          // No music when inactive +		_config.cx = VIEW_DX * 2;                   // Window view size +		_config.cy = VIEW_DY * 2; + +//		_config.wx = 0; +//		_config.wy = 0; + +		_config.musicVolume = 85;                   // Music volume % +		_config.soundVolume = 100;                  // Sound volume % +		initPlaylist(_config.playlist);             // Initialize default tune playlist + +		HugoEngine::get().file().readBootFile();    // Read startup structure +		HugoEngine::get().file().readConfig();      // Read user's saved config + +		cx = _config.cx;                            // Save these around OnFileNew() +		cy = _config.cy; +//		wx = _config.wx; +//		wy = _config.wy; +		break; +	case RESET: +		_config.cx = cx;                            // Restore cx, cy +		_config.cy = cy; +//		_config.wx = wx; +//		_config.wy = wy; + +		// Find first tune and play it +		for (i = 0; i < MAX_TUNES; i++) +			if (_config.playlist[i]) { +				sound().playMusic(i); +				break; +			} + +		HugoEngine::get().file().initSavedGame();   // Initialize saved game +		break; +	case RESTORE: +		warning("Unhandled action RESTORE"); +		break; +	} +} +void HugoEngine::initialize() { +	debugC(1, kDebugEngine, "initialize"); + +	sound().initSound(INSTALL); +	HugoEngine::get().scheduler().initEventQueue(); // Init scheduler stuff +	screen().initDisplay();                         // Create Dibs and palette +	HugoEngine::get().file().openDatabaseFiles();   // Open database files +	calcMaxScore();                                 // Initialise maxscore + +	_rnd = new Common::RandomSource(); +	g_eventRec.registerRandomSource(*_rnd, "hugo"); + +	_rnd->setSeed(42);                              // Kick random number generator + +	switch (getGameType()) { +	case kGameTypeHugo1: +		_episode = "\"HUGO'S HOUSE OF HORRORS\""; +		_picDir = ""; +		break; +	case kGameTypeHugo2: +		_episode = "\"Hugo's Mystery Adventure\""; +		_picDir = "hugo2/"; +		break; +	case kGameTypeHugo3: +		_episode = "\"Hugo's Amazon Adventure\""; +		_picDir = "hugo3/"; +		break; +	default: +		error("Unknown game"); +	} +} + +// Restore all resources before termination +void HugoEngine::shutdown() { +	debugC(1, kDebugEngine, "shutdown"); + +	sound().initSound(RESTORE); + +	HugoEngine::get().file().closeDatabaseFiles(); +	if (_status.recordFl || _status.playbackFl) +		HugoEngine::get().file().closePlaybackFile(); +	freeObjects(); +} + +void HugoEngine::readObjectImages() { +	debugC(1, kDebugEngine, "readObjectImages"); + +	for (int i = 0; i < _numObj; i++) +		HugoEngine::get().file().readImage(i, &_objects[i]); +} + +// Read the uif image file (inventory icons) +void HugoEngine::readUIFImages() { +	debugC(1, kDebugEngine, "readUIFImages"); + +	HugoEngine::get().file().readUIFItem(UIF_IMAGES, screen().getGUIBuffer());              // Read all uif images +} + +// Read scenery, overlay files for given screen number +void HugoEngine::readScreenFiles(int screenNum) { +	debugC(1, kDebugEngine, "readScreenFiles(%d)", screenNum); + +	HugoEngine::get().file().readBackground(screenNum);                     // Scenery file +	memcpy(screen().getBackBuffer(), screen().getFrontBuffer(), sizeof(screen().getFrontBuffer()));// Make a copy +	HugoEngine::get().file().readOverlay(screenNum, _boundary, BOUNDARY);       // Boundary file +	HugoEngine::get().file().readOverlay(screenNum, _overlay, OVERLAY);         // Overlay file +	HugoEngine::get().file().readOverlay(screenNum, _ovlBase, OVLBASE);         // Overlay base file +} + +// Update all object positions.  Process object 'local' events +// including boundary events and collisions +void HugoEngine::moveObjects() { +	object_t *obj; +	seq_t    *currImage; +	int       x1, x2, y1, y2;                       // object coordinates +	int       dx, dy;                               // Allowable motion wrt boundary +	char      radius;                               // Radius for chase (8 bit signed) + +	debugC(4, kDebugEngine, "moveObjects"); + +	// If route mode enabled, do special route processing +	if (_status.routeIndex >= 0) +		route().processRoute(); + +	// Perform any adjustments to velocity based on special path types +	// and store all (visible) object baselines into the boundary file. +	// Don't store foreground or background objects +	for (int i = 0; i < _numObj; i++) { +		obj = &_objects[i];                         // Get pointer to object +		currImage = obj->currImagePtr;              // Get ptr to current image +		if (obj->screenIndex == *_screen_p) { +			switch (obj->pathType) { +			case CHASE: +			case CHASE2: +				radius = obj->radius;               // Default to object's radius +				if (radius < 0)                     // If radius infinity, use closer value +					radius = DX; + +				dx = _hero->x + _hero->currImagePtr->x1 - obj->x - currImage->x1; +				dy = _hero->y + _hero->currImagePtr->y2 - obj->y - currImage->y2 - 1; +				if (abs(dx) <= radius) +					obj->vx = 0; +				else +					obj->vx = dx > 0 ? MIN(dx, obj->vxPath) : MAX(dx, -obj->vxPath); +				if (abs(dy) <= radius) +					obj->vy = 0; +				else +					obj->vy = dy > 0 ? MIN(dy, obj->vyPath) : MAX(dy, -obj->vyPath); + +				// Set first image in sequence (if multi-seq object) +				switch (obj->seqNumb) { +				case 4: +					if (!obj->vx) {                 // Got 4 directions +						if (obj->vx != obj->oldvx)  // vx just stopped +							if (dy >= 0) +								obj->currImagePtr = obj->seqList[DOWN].seqPtr; +							else +								obj->currImagePtr = obj->seqList[_UP].seqPtr; +					} else if (obj->vx != obj->oldvx) +						if (dx > 0) +							obj->currImagePtr = obj->seqList[RIGHT].seqPtr; +						else +							obj->currImagePtr = obj->seqList[LEFT].seqPtr; +					break; +				case 3: +				case 2: +					if (obj->vx != obj->oldvx)      // vx just stopped +						if (dx > 0)                 // Left & right only +							obj->currImagePtr = obj->seqList[RIGHT].seqPtr; +						else +							obj->currImagePtr = obj->seqList[LEFT].seqPtr; +					break; +				} + +				if (obj->vx || obj->vy) +					obj->cycling = CYCLE_FORWARD; +				else { +					obj->cycling = NOT_CYCLING; +					boundaryCollision(obj);         // Must have got hero! +				} +				obj->oldvx = obj->vx; +				obj->oldvy = obj->vy; +				currImage = obj->currImagePtr;      // Get (new) ptr to current image +				break; +			case WANDER2: +			case WANDER: +				if (!_rnd->getRandomNumber(3 * NORMAL_TPS)) {       // Kick on random interval +					obj->vx = _rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath; +					obj->vy = _rnd->getRandomNumber(obj->vyPath << 1) - obj->vyPath; + +					// Set first image in sequence (if multi-seq object) +					if (obj->seqNumb > 1) { +						if (!obj->vx && (obj->seqNumb >= 4)) { +							if (obj->vx != obj->oldvx)  // vx just stopped +								if (obj->vy > 0) +									obj->currImagePtr = obj->seqList[DOWN].seqPtr; +								else +									obj->currImagePtr = obj->seqList[_UP].seqPtr; +						} else if (obj->vx != obj->oldvx) +							if (obj->vx > 0) +								obj->currImagePtr = obj->seqList[RIGHT].seqPtr; +							else +								obj->currImagePtr = obj->seqList[LEFT].seqPtr; +					} +					obj->oldvx = obj->vx; +					obj->oldvy = obj->vy; +					currImage = obj->currImagePtr;  // Get (new) ptr to current image +				} +				if (obj->vx || obj->vy) +					obj->cycling = CYCLE_FORWARD; +				break; +			default: +				; // Really, nothing +			} +			// Store boundaries +			if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING)) +				storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2); +		} +	} + +	// Move objects, allowing for boundaries +	for (int i = 0; i < _numObj; i++) { +		obj = &_objects[i];                         // Get pointer to object +		if ((obj->screenIndex == *_screen_p) && (obj->vx || obj->vy)) { +			// Only process if it's moving + +			// Do object movement.  Delta_x,y return allowed movement in x,y +			//  to move as close to a boundary as possible without crossing it. +			currImage = obj->currImagePtr;          // Get ptr to current image +			x1 = obj->x + currImage->x1;            // Left edge of object +			x2 = obj->x + currImage->x2;            // Right edge +			y1 = obj->y + currImage->y1;            // Top edge +			y2 = obj->y + currImage->y2;            // Bottom edge + +			if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING)) +				clearBoundary(x1, x2, y2);          // Clear our own boundary +			dx = deltaX(x1, x2, obj->vx, y2); +			if (dx != obj->vx) { +				// An object boundary collision! +				boundaryCollision(obj); +				obj->vx = 0; +			} + +			dy = deltaY(x1, x2, obj->vy, y2); + +			if (dy != obj->vy) { +				// An object boundary collision! +				boundaryCollision(obj); +				obj->vy = 0; +			} + +			if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING)) +				storeBoundary(x1, x2, y2);          // Re-store our own boundary + +			obj->x += dx;                           // Update object position +			obj->y += dy; + +			// Don't let object go outside screen +			if (x1 < EDGE) +				obj->x = EDGE2; +			if (x2 > (XPIX - EDGE)) +				obj->x = XPIX - EDGE2 - (x2 - x1); +			if (y1 < EDGE) +				obj->y = EDGE2; +			if (y2 > (YPIX - EDGE)) +				obj->y = YPIX - EDGE2 - (y2 - y1); + +			if ((obj->vx == 0) && (obj->vy == 0) && (obj->pathType != WANDER2) && (obj->pathType != CHASE2)) +				obj->cycling = NOT_CYCLING; +		} +	} + +	// Clear all object baselines from the boundary file. +	for (int i = 0; i < _numObj; i++) { +		obj = &_objects[i];                         // Get pointer to object +		currImage = obj->currImagePtr;              // Get ptr to current image +		if ((obj->screenIndex == *_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING)) +			clearBoundary(obj->oldx + currImage->x1, obj->oldx + currImage->x2, obj->oldy + currImage->y2); +	} + +	// If maze mode is enabled, do special maze processing +	if (_maze.enabledFl) +		processMaze(); +} + +// Return maximum allowed movement (from zero to vx) such that object does +// not cross a boundary (either background or another object) +int HugoEngine::deltaX(int x1, int x2, int vx, int y) { +// Explanation of algorithm:  The boundaries are drawn as contiguous +// lines 1 pixel wide.  Since DX,DY are not necessarily 1, we must +// detect boundary crossing.  If vx positive, examine each pixel from +// x1 old to x2 new, else x2 old to x1 new, both at the y2 line. +// If vx zero, no need to check.  If vy non-zero then examine each +// pixel on the line segment x1 to x2 from y old to y new. +// Fix from Hugo I v1.5: +// Note the diff is munged in the return statement to cater for a special +// cases arising from differences in image widths from one sequence to +// another.  The problem occurs reversing direction at a wall where the +// new image intersects before the object can move away.  This is cured +// by comparing the intersection with half the object width pos. If the +// intersection is in the other half wrt the intended direction, use the +// desired vx, else use the computed delta.  i.e. believe the desired vx +	int b; + +	debugC(3, kDebugEngine, "deltaX(%d, %d, %d, %d)", x1, x2, vx, y); + +	if (vx == 0) +		return(0);                                  // Object stationary + +	y *= XBYTES;                                    // Offset into boundary file +	if (vx > 0) { +		// Moving to right +		for (int i = x1 >> 3; i <= (x2 + vx) >> 3; i++) // Search by byte +			if ((b = Utils::firstBit((byte)(_boundary[y + i] | _objBound[y + i]))) < 8) {   // b is index or 8 +				// Compute x of boundary and test if intersection +				b += i << 3; +				if ((b >= x1) && (b <= x2 + vx)) +					return((b < x1 + ((x2 - x1) >> 1)) ? vx : b - x2 - 1); // return dx +			} +	} else { +		// Moving to left +		for (int i = x2 >> 3; i >= (x1 + vx) >> 3; i--)// Search by byte +			if ((b = Utils::lastBit((byte)(_boundary[y + i] | _objBound[y + i]))) < 8) {    // b is index or 8 +				// Compute x of boundary and test if intersection +				b += i << 3; +				if ((b >= x1 + vx) && (b <= x2)) +					return((b > x1 + ((x2 - x1) >> 1)) ? vx : b - x1 + 1); // return dx +			} +	} +	return(vx); +} + +// Similar to Delta_x, but for movement in y direction.  Special case of +// bytes at end of line segment; must only count boundary bits falling on +// line segment. +int HugoEngine::deltaY(int x1, int x2, int vy, int y) { +	int inc, i, j, b; + +	debugC(3, kDebugEngine, "deltaY(%d, %d, %d, %d)", x1, x2, vy, y); + +	if (vy == 0) +		return(0);                                  // Object stationary + +	inc = (vy > 0 ? 1 : -1); +	for (j = y + inc; j != (y + vy + inc); j += inc) //Search by byte +		for (i = x1 >> 3; i <= x2 >> 3; i++) +			if (b = _boundary[j * XBYTES + i] | _objBound[j * XBYTES + i]) {    // Any bit set +				// Make sure boundary bits fall on line segment +				if (i == (x2 >> 3))                 // Adjust right end +					b &= 0xff << ((i << 3) + 7 - x2); +				else if (i == (x1 >> 3))            // Adjust left end +					b &= 0xff >> (x1 - (i << 3)); +				if (b) +					return(j - y - inc); +			} +	return(vy); +} + +// Store a horizontal line segment in the object boundary file +void HugoEngine::storeBoundary(int x1, int x2, int y) { +	byte *b;                                        // ptr to boundary byte + +	debugC(5, kDebugEngine, "storeBoundary(%d, %d, %d)", x1, x2, y); + +	for (int i = x1 >> 3; i <= x2 >> 3; i++) {      // For each byte in line +		b = &_objBound[y * XBYTES + i];             // get boundary byte +		if (i == x2 >> 3)                           // Adjust right end +			*b |= 0xff << ((i << 3) + 7 - x2); +		else if (i == x1 >> 3)                      // Adjust left end +			*b |= 0xff >> (x1 - (i << 3)); +		else +			*b = 0xff; +	} +} + +// Clear a horizontal line segment in the object boundary file +void HugoEngine::clearBoundary(int x1, int x2, int y) { +	int  i; +	byte *b;                                        // ptr to boundary byte + +	debugC(5, kDebugEngine, "clearBoundary(%d, %d, %d)", x1, x2, y); + +	for (i = x1 >> 3; i <= x2 >> 3; i++) {          // For each byte in line +		b = &_objBound[y * XBYTES + i];             // get boundary byte +		if (i == x2 >> 3)                           // Adjust right end +			*b &= ~(0xff << ((i << 3) + 7 - x2)); +		else if (i == x1 >> 3)                      // Adjust left end +			*b &= ~(0xff >> (x1 - (i << 3))); +		else +			*b = 0; +	} +} + +// Maze mode is enabled.  Check to see whether hero has crossed the maze +// bounding box, if so, go to the next room */ +void HugoEngine::processMaze() { +	seq_t      *currImage; +	int         x1, x2, y1, y2;                     // hero coordinates + +	debugC(1, kDebugEngine, "processMaze"); + +	//actlist     alnewscr  = {&aheroxy,&astophero,&aherostop,&anewscr,NULL}; +	//actlist_pt  alist     = &alnewscr[0]; + +	currImage = _hero->currImagePtr;                // Get ptr to current image +	x1 = _hero->x + currImage->x1;                  // Left edge of object +	x2 = _hero->x + currImage->x2;                  // Right edge +	y1 = _hero->y + currImage->y1;                  // Top edge +	y2 = _hero->y + currImage->y2;                  // Bottom edge + +	if (x1 < _maze.x1) { +		// Exit west +//		anewscr.screen = *_screen_p - 1; +		_actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p - 1; +//		aheroxy.x = _maze.x2 - SHIFT - (x2 - x1); +		_actListArr[_alNewscrIndex][0].a2.x = _maze.x2 - SHIFT - (x2 - x1); +//		aheroxy.y = _hero_p->y; +		_actListArr[_alNewscrIndex][0].a2.y = _hero->y; +		_status.routeIndex = -1; +		HugoEngine::get().scheduler().insertActionList(_alNewscrIndex); +	} else if (x2 > _maze.x2) { +		// Exit east +//			anewscr.screen = *_screen_p + 1; +		_actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p + 1; +//			aheroxy.x = _maze.x1 + SHIFT; +		_actListArr[_alNewscrIndex][0].a2.x = _maze.x1 + SHIFT; +//			aheroxy.y = _hero_p->y; +		_actListArr[_alNewscrIndex][0].a2.y = _hero->y; +		_status.routeIndex = -1; +		HugoEngine::get().scheduler().insertActionList(_alNewscrIndex); +	} else if (y1 < _maze.y1 - SHIFT) { +		// Exit north +//				anewscr.screen = *_screen_p - _maze.size; +		_actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p - _maze.size; +//				aheroxy.x = _maze.x3;             // special offset for perspective +		_actListArr[_alNewscrIndex][0].a2.x = _maze.x3; +//				aheroxy.y = _maze.y2 - SHIFT - (y2 - y1); +		_actListArr[_alNewscrIndex][0].a2.y = _maze.y2 - SHIFT - (y2 - y1); +		_status.routeIndex = -1; +		HugoEngine::get().scheduler().insertActionList(_alNewscrIndex); +	} else if (y2 > _maze.y2 - SHIFT / 2) { +		// Exit south +//					anewscr.screen = *_screen_p + _maze.size; +		_actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p + _maze.size; +//					aheroxy.x = _maze.x4;            // special offset for perspective +		_actListArr[_alNewscrIndex][0].a2.x = _maze.x4; +//					aheroxy.y = _maze.y1 + SHIFT; +		_actListArr[_alNewscrIndex][0].a2.y = _maze.y1 + SHIFT; +		_status.routeIndex = -1; +		HugoEngine::get().scheduler().insertActionList(_alNewscrIndex); +	} +} + +// Compare function for the quicksort.  The sort is to order the objects in +// increasing vertical position, using y+y2 as the baseline +// Returns -1 if ay2 < by2 else 1 if ay2 > by2 else 0 +int y2comp(const void *a, const void *b) { +	int       ay2, by2; + +	debugC(6, kDebugEngine, "y2comp"); + +	const object_t *p1 = &HugoEngine::get()._objects[*(const byte *)a]; +	const object_t *p2 = &HugoEngine::get()._objects[*(const byte *)b]; + +	if (p1 == p2) +		// Why does qsort try the same indexes? +		return (0); + +	if (p1->priority == BACKGROUND) +		return (-1); + +	if (p2->priority == BACKGROUND) +		return (1); + +	if (p1->priority == FOREGROUND) +		return (1); + +	if (p2->priority == FOREGROUND) +		return (-1); + +	ay2 = p1->y + p1->currImagePtr->y2; +	by2 = p2->y + p2->currImagePtr->y2; + +	return(ay2 - by2); +} + +// Draw all objects on screen as follows: +// 1. Sort 'FLOATING' objects in order of y2 (base of object) +// 2. Display new object frames/positions in dib +// Finally, cycle any animating objects to next frame +void HugoEngine::updateImages() { +	int       i, j, num_objs; +	object_t *obj;                                  // Pointer to object +	seq_t    *seqPtr;                               // Save curr_seq_p +	byte      objindex[MAX_OBJECTS];                // Array of indeces to objects + +	debugC(5, kDebugEngine, "updateImages"); + +	// Initialise the index array to visible objects in current screen +	for (i = 0, num_objs = 0; i < _numObj; i++) { +		obj = &_objects[i]; +		if ((obj->screenIndex == *_screen_p) && (obj->cycling >= ALMOST_INVISIBLE)) +			objindex[num_objs++] = i; +	} + +	// Sort the objects into increasing y+y2 (painter's algorithm) +	qsort(objindex, num_objs, sizeof(objindex[0]), y2comp); + +	// Add each visible object to display list +	for (i = 0; i < num_objs; i++) { +		obj = &_objects[objindex[i]]; +		// Count down inter-frame timer +		if (obj->frameTimer) +			obj->frameTimer--; + +		if (obj->cycling > ALMOST_INVISIBLE)        // Only if visible +			switch (obj->cycling) { +			case NOT_CYCLING: +				screen().displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL); +				break; +			case CYCLE_FORWARD: +				if (obj->frameTimer)                // Not time to see next frame yet +					screen().displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL); +				else +					screen().displayFrame(obj->x, obj->y, obj->currImagePtr->nextSeqPtr, obj->priority == OVEROVL); +				break; +			case CYCLE_BACKWARD: +				seqPtr = obj->currImagePtr; +				if (!obj->frameTimer)               // Show next frame +					while (seqPtr->nextSeqPtr != obj->currImagePtr) +						seqPtr = seqPtr->nextSeqPtr; +				screen().displayFrame(obj->x, obj->y, seqPtr, obj->priority == OVEROVL); +				break; +			default: +				break; +			} +	} + +	// Cycle any animating objects +	for (i = 0; i < num_objs; i++) { +		obj = &_objects[objindex[i]]; +		if (obj->cycling != INVISIBLE) { +			// Only if it's visible +			if (obj->cycling == ALMOST_INVISIBLE) +				obj->cycling = INVISIBLE; + +			// Now Rotate to next picture in sequence +			switch (obj->cycling) { +			case NOT_CYCLING: +				break; +			case CYCLE_FORWARD: +				if (!obj->frameTimer) { +					// Time to step to next frame +					obj->currImagePtr = obj->currImagePtr->nextSeqPtr; +					// Find out if this is last frame of sequence +					// If so, reset frame_timer and decrement n_cycle +					if (obj->frameInterval || obj->cycleNumb) { +						obj->frameTimer = obj->frameInterval; +						for (j = 0; j < obj->seqNumb; j++) +							if (obj->currImagePtr->nextSeqPtr == obj->seqList[j].seqPtr) +								if (obj->cycleNumb)     // Decr cycleNumb if Non-continous +									if (!--obj->cycleNumb) +										obj->cycling = NOT_CYCLING; +					} +				} +				break; +			case CYCLE_BACKWARD: +				if (!obj->frameTimer) { +					// Time to step to prev frame +					seqPtr = obj->currImagePtr; +					while (obj->currImagePtr->nextSeqPtr != seqPtr) +						obj->currImagePtr = obj->currImagePtr->nextSeqPtr; +					// Find out if this is first frame of sequence +					// If so, reset frame_timer and decrement n_cycle +					if (obj->frameInterval || obj->cycleNumb) { +						obj->frameTimer = obj->frameInterval; +						for (j = 0; j < obj->seqNumb; j++) +							if (obj->currImagePtr == obj->seqList[j].seqPtr) +								if (obj->cycleNumb)     // Decr cycleNumb if Non-continous +									if (!--obj->cycleNumb) +										obj->cycling = NOT_CYCLING; +					} +				} +				break; +			default: +				break; +			} +			obj->oldx = obj->x; +			obj->oldy = obj->y; +		} +	} +} + +// Return object index of the topmost object under the cursor, or -1 if none +// Objects are filtered if not "useful" +int16 HugoEngine::findObject(uint16 x, uint16 y) { +	object_t *obj; +	seq_t    *curImage; +	int16     objIndex = -1;                        // Index of found object +	uint16    y2Max = 0;                            // Greatest y2 +	int       i; + +	debugC(3, kDebugEngine, "findObject(%d, %d)", x, y); + +	// Check objects on screen +	for (i = 0, obj = _objects; i < _numObj; i++, obj++)    { +		// Object must be in current screen and "useful" +		if (obj->screenIndex == *_screen_p && (obj->genericCmd || obj->objValue || obj->cmdIndex)) { +			curImage = obj->currImagePtr; +			// Object must have a visible image... +			if (curImage != NULL && obj->cycling != INVISIBLE) { +				// If cursor inside object +				if (x >= (uint16)obj->x && x <= obj->x + curImage->x2 && y >= (uint16)obj->y && y <= obj->y + curImage->y2) +					// If object is closest so far +					if (obj->y + curImage->y2 > y2Max) { +						y2Max = obj->y + curImage->y2; +						objIndex = i;               // Found an object! +					} +			} else +				// ...or a dummy object that has a hotspot rectangle +				if (curImage == NULL && obj->vxPath != 0 && !obj->carriedFl) { +					// If cursor inside special rectangle +					if ((int16)x >= obj->oldx && (int16)x < obj->oldx + obj->vxPath && (int16)y >= obj->oldy && (int16)y < obj->oldy + obj->vyPath) +						// If object is closest so far +						if (obj->oldy + obj->vyPath - 1 > (int16)y2Max) { +							y2Max = obj->oldy + obj->vyPath - 1; +							objIndex = i;           // Found an object! +						} +				} +		} +	} +	return objIndex; +} + +// Find a clear space around supplied object that hero can walk to +bool HugoEngine::findObjectSpace(object_t *obj, int16 *destx, int16 *desty) { +//	bool   found = false;                            // TRUE if we found a clear space +	bool   foundFl; +	seq_t *curImage = obj->currImagePtr; +	int16  x; +	int16  y  = obj->y + curImage->y2 - 1; + +	debugC(1, kDebugEngine, "findObjectSpace(obj, %d, %d)", *destx, *desty); + +//	if (!found)                                      // Try left rear corner +	for (foundFl = true, *destx = x = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++) +		if (BOUND(x, y)) +			foundFl = false; + +	if (!foundFl)                                       // Try right rear corner +		for (foundFl = true, *destx = x = obj->x + curImage->x2 - HERO_MAX_WIDTH + 1; x <= obj->x + (int16)curImage->x2; x++) +			if (BOUND(x, y)) +				foundFl = false; + +	if (!foundFl)                                       // Try left front corner +		for (foundFl = true, y += 2, *destx = x = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++) +			if (BOUND(x, y)) +				foundFl = false; + +	if (!foundFl)                                       // Try right rear corner +		for (foundFl = true, *destx = x = obj->x + curImage->x2 - HERO_MAX_WIDTH + 1; x <= obj->x + (int16)curImage->x2; x++) +			if (BOUND(x, y)) +				foundFl = false; + +	*desty = y; +	return(foundFl); +} + +// Search background command list for this screen for supplied object. +// Return first associated verb (not "look") or NULL if none found. +char *HugoEngine::useBG(char *name) { +	int i; +	objectList_t p = _backgroundObjects[*_screen_p]; + +	debugC(1, kDebugEngine, "useBG(%s)", name); + +	for (i = 0; *_arrayVerbs[p[i].verbIndex]; i++) +		if ((name == _arrayNouns[p[i].nounIndex][0] && +		        p[i].verbIndex != _look) && +		        ((p[i].roomState == DONT_CARE) || (p[i].roomState == _screenStates[*_screen_p]))) +			return (_arrayVerbs[p[i].verbIndex][0]); + +	return (NULL); +} + +// If status.objid = -1, pick up objid, else use status.objid on objid, +// if objid can't be picked up, use it directly +void HugoEngine::useObject(int16 objId) { +	object_t *obj = &_objects[objId];               // Ptr to object +	uses_t *use;                                    // Ptr to use entry +	target_t *target;                               // Ptr to target entry +	bool foundFl;                                   // TRUE if found target entry +	char *verb;                                     // Background verb to use directly + +	debugC(1, kDebugEngine, "useObject(%d)", objId); + +	if (_status.inventoryObjId == -1) { +		// Get or use objid directly +		if ((obj->genericCmd & TAKE) || obj->objValue)  // Get collectible item +			sprintf(_line, "%s %s", _arrayVerbs[_take][0], _arrayNouns[obj->nounIndex][0]); +		else if (obj->cmdIndex != 0)            // Use non-collectible item if able +			sprintf(_line, "%s %s", _arrayVerbs[_cmdList[obj->cmdIndex][1].verbIndex][0], _arrayNouns[obj->nounIndex][0]); +		else if ((verb = useBG(_arrayNouns[obj->nounIndex][0])) != NULL) +			sprintf(_line, "%s %s", verb, _arrayNouns[obj->nounIndex][0]); +		else +			return;                         // Can't use object directly +	} else { +		// Use status.objid on objid +		// Default to first cmd verb +		sprintf(_line, "%s %s %s", _arrayVerbs[_cmdList[_objects[_status.inventoryObjId].cmdIndex][1].verbIndex][0], _arrayNouns[_objects[_status.inventoryObjId].nounIndex][0], _arrayNouns[obj->nounIndex][0]); + +		// Check valid use of objects and override verb if necessary +		for (use = _uses; use->objId != _numObj; use++) +			if (_status.inventoryObjId == use->objId) { +				// Look for secondary object, if found use matching verb +				for (foundFl = false, target = use->targets; _arrayNouns[target->nounIndex] != NULL; target++) +					if (_arrayNouns[target->nounIndex][0] == _arrayNouns[obj->nounIndex][0]) { +						foundFl = true; +						sprintf(_line, "%s %s %s", _arrayVerbs[target->verbIndex][0], _arrayNouns[_objects[_status.inventoryObjId].nounIndex][0], _arrayNouns[obj->nounIndex][0]); +					} + +				// No valid use of objects found, print failure string +				if (!foundFl) { +					// Deselect dragged icon if inventory not active +					if (_status.inventoryState != I_ACTIVE) +						_status.inventoryObjId  = -1; +					Utils::Box(BOX_ANY, HugoEngine::get()._textData[use->dataIndex]); +					return; +				} +			} +	} + +	if (_status.inventoryState == I_ACTIVE)         // If inventory active, remove it +		_status.inventoryState = I_UP; +	_status.inventoryObjId  = -1;                   // Deselect any dragged icon +	parser().lineHandler();                         // and process command +} + +// Issue "Look at <object>" command +// Note special case of swapped hero image +void HugoEngine::lookObject(object_t *obj) { +	debugC(1, kDebugEngine, "lookObject"); + +	if (obj == _hero) { +		// Hero swapped - look at other +		obj = &_objects[_heroImage]; +	} +	parser().command("%s %s", _arrayVerbs[_look][0], _arrayNouns[obj->nounIndex][0]); +} + +// Free all object images +void HugoEngine::freeObjects() { +	object_t *obj; +	seq_t *seq; + +	debugC(1, kDebugEngine, "freeObjects"); + +	// Nothing to do if not allocated yet +	if (_hero->seqList[0].seqPtr == NULL) +		return; + +	// Free all sequence lists and image data +	for (int i = 0; i < _numObj; i++) { +		obj = &_objects[i]; +		for (int j = 0; j < obj->seqNumb; j++) {        // for each sequence +			seq = obj->seqList[j].seqPtr;           // Free image +			if (seq == NULL)                        // Failure during database load +				break; +			do { +				free(seq->imagePtr); +				seq = seq->nextSeqPtr; +			} while (seq != obj->seqList[j].seqPtr); +			free(seq);                              // Free sequence record +		} +	} +} + +// Add action lists for this screen to event queue +void HugoEngine::screenActions(int screenNum) { +	uint16 *screenAct = _screenActs[screenNum]; + +	debugC(1, kDebugEngine, "screenActions(%d)", screenNum); + +	if (screenAct) { +		for (int i = 0; screenAct[i]; i++) +			HugoEngine::get().scheduler().insertActionList(screenAct[i]); +	} +} + +// Set the new screen number into the hero object and any carried objects +void HugoEngine::setNewScreen(int screenNum) { +	debugC(1, kDebugEngine, "setNewScreen(%d)", screenNum); + +	*_screen_p = screenNum;                             // HERO object +	for (int i = HERO + 1; i < _numObj; i++)            // Any others +		if (_objects[i].carriedFl)                      // being carried +			_objects[i].screenIndex = screenNum; +} + +// An object has collided with a boundary.  See if any actions are required +void HugoEngine::boundaryCollision(object_t *obj) { +	int         x, y, dx, dy; +	char        radius;                             // 8 bits signed +	hotspot_t   *hotspot; + +	debugC(1, kDebugEngine, "boundaryCollision"); + +	if (obj == _hero) { +		// Hotspots only relevant to HERO +		if (obj->vx > 0) +			x = obj->x + obj->currImagePtr->x2; +		else +			x = obj->x + obj->currImagePtr->x1; +		y = obj->y + obj->currImagePtr->y2; + +		for (int i = 0; _hotspots[i].screenIndex >= 0; i++) { +			hotspot = &_hotspots[i]; +			if (hotspot->screenIndex == obj->screenIndex) +				if ((x >= hotspot->x1) && (x <= hotspot->x2) && (y >= hotspot->y1) && (y <= hotspot->y2)) { +					HugoEngine::get().scheduler().insertActionList(hotspot->actIndex); +					break; +				} +		} +	} else { +		// Check whether an object collided with HERO +		dx = _hero->x + _hero->currImagePtr->x1 - obj->x - obj->currImagePtr->x1; +		dy = _hero->y + _hero->currImagePtr->y2 - obj->y - obj->currImagePtr->y2; +		// If object's radius is infinity, use a closer value +		radius = obj->radius; +		if (radius < 0) +			radius = DX * 2; +		if ((abs(dx) <= radius) && (abs(dy) <= radius)) +			HugoEngine::get().scheduler().insertActionList(obj->actIndex); +	} +} + +// Initialize screen components and display results +void HugoEngine::initNewScreenDisplay() { +	debugC(1, kDebugEngine, "initNewScreenDisplay"); + +	screen().displayList(D_INIT); +	screen().setBackgroundColor(_TBLACK); +	screen().displayBackground(); + +	// Stop premature object display in Display_list(D_DISPLAY) +	_status.newScreenFl = true; +} + +// Add up all the object values and all the bonus points +void HugoEngine::calcMaxScore() { +	int i; + +	debugC(1, kDebugEngine, "calcMaxScore"); + +	for (i = 0; i < _numObj; i++) +		_maxscore += _objects[i].objValue; + +	for (i = 0; i < _numBonuses; i++) +		_maxscore += _points[i].score; +} + +// Exit game, advertise trilogy, show copyright +void HugoEngine::endGame() { +	debugC(1, kDebugEngine, "endGame"); + +	if (!_boot.registered) +		Utils::Box(BOX_ANY, HugoEngine::get()._textEngine[kEsAdvertise]); +	Utils::Box(BOX_ANY, "%s\n%s", _episode, COPYRIGHT); +	_status.viewState = V_EXIT; +} + +} // end of namespace Hugo diff --git a/engines/hugo/engine.h b/engines/hugo/engine.h new file mode 100755 index 0000000000..fa4eba4fbb --- /dev/null +++ b/engines/hugo/engine.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo 1-3 Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef HUGO_ENGINE_H +#define HUGO_ENGINE_H +namespace Hugo { + +enum seqTextEngine { +	// Strings used by the engine +	kEsAdvertise = 0 +}; + +} // end of namespace Hugo +#endif // HUGO_ENGINE_H diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp new file mode 100755 index 0000000000..0a20c8a67b --- /dev/null +++ b/engines/hugo/file.cpp @@ -0,0 +1,924 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#include "common/system.h" +#include "common/file.h" +#include "common/savefile.h" + +#include "hugo/game.h" +#include "hugo/hugo.h" +#include "hugo/file.h" +#include "hugo/global.h" +#include "hugo/schedule.h" +#include "hugo/display.h" +#include "hugo/util.h" + +namespace Hugo { +FileManager::FileManager(HugoEngine &vm) : _vm(vm) { + +} + +byte *FileManager::convertPCC(byte *p, uint16 y, uint16 bpl, image_pt dataPtr) { +// Convert 4 planes (RGBI) data to 8-bit DIB format +// Return original plane data ptr +	uint16 r, g, b, i;                              // Byte index within each plane +	char   bit;                                     // Bit index within a byte + +	debugC(2, kDebugFile, "convertPCC(byte *p, %d, %d, image_pt data_p)", y, bpl); + +	dataPtr += y * bpl * 8;                         // Point to correct DIB line +	for (r = 0, g = bpl, b = g + bpl, i = b + bpl; r < bpl; r++, g++, b++, i++) // Each byte in all planes +		for (bit = 7; bit >= 0; bit--)                                          // Each bit in byte +			*dataPtr++ = (((p[r] >> bit & 1) << 0) | +			              ((p[g] >> bit & 1) << 1) | +			              ((p[b] >> bit & 1) << 2) | +			              ((p[i] >> bit & 1) << 3)); +	return p; +} + +seq_t *FileManager::readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool firstFl, const char *name) { +// Read a pcx file of length len.  Use supplied seq_p and image_p or +// allocate space if NULL.  Name used for errors.  Returns address of seq_p +// Set first TRUE to initialize b_index (i.e. not reading a sequential image in file). + +	struct {                                            // Structure of PCX file header +		byte   mfctr, vers, enc, bpx; +		uint16  x1, y1, x2, y2;                         // bounding box +		uint16  xres, yres; +		byte   palette[48];                             // EGA color palette +		byte   vmode, planes; +		uint16 bytesPerLine;                            // Bytes per line +		byte   fill2[60]; +	} PCC_header;                                       // Header of a PCC file + +	byte  c, d;                                     // code and data bytes from PCX file +	byte  pline[XPIX];                              // Hold 4 planes of data +	byte  *p = pline;                               // Ptr to above +	byte  i;                                        // PCX repeat count +	uint16 bytesPerLine4;                           // BPL in 4-bit format +	uint16 size;                                    // Size of image +	uint16 y = 0;                                   // Current line index + +	debugC(1, kDebugFile, "readPCX(..., %s)", name); + +	// Read in the PCC header and check consistency +	PCC_header.mfctr = f.readByte(); +	PCC_header.vers = f.readByte(); +	PCC_header.enc = f.readByte(); +	PCC_header.bpx = f.readByte(); +	PCC_header.x1 = f.readUint16LE(); +	PCC_header.y1 = f.readUint16LE(); +	PCC_header.x2 = f.readUint16LE(); +	PCC_header.y2 = f.readUint16LE(); +	PCC_header.xres = f.readUint16LE(); +	PCC_header.yres = f.readUint16LE(); +	f.read(PCC_header.palette, sizeof(PCC_header.palette)); +	PCC_header.vmode = f.readByte(); +	PCC_header.planes = f.readByte(); +	PCC_header.bytesPerLine = f.readUint16LE(); +	f.read(PCC_header.fill2, sizeof(PCC_header.fill2)); + +	if (PCC_header.mfctr != 10) +		Utils::Error(PCCH_ERR, name); + +	// Allocate memory for seq_t if NULL +	if (seqPtr == NULL) +		if ((seqPtr = (seq_t *)malloc(sizeof(seq_t))) == NULL) +			Utils::Error(HEAP_ERR, name); + +	// Find size of image data in 8-bit DIB format +	// Note save of x2 - marks end of valid data before garbage +	bytesPerLine4 = PCC_header.bytesPerLine * 4;    // 4-bit bpl +	seqPtr->bytesPerLine8 = bytesPerLine4 * 2;      // 8-bit bpl +	seqPtr->lines = PCC_header.y2 - PCC_header.y1 + 1; +	seqPtr->x2 = PCC_header.x2 - PCC_header.x1 + 1; +	size = seqPtr->lines * seqPtr->bytesPerLine8; + +	// Allocate memory for image data if NULL +	if (imagePtr == NULL) +		if ((imagePtr = (byte *)malloc((size_t) size)) == NULL) +			Utils::Error(HEAP_ERR, name); +	seqPtr->imagePtr = imagePtr; + +	// Process the image data, converting to 8-bit DIB format +	while (y < seqPtr->lines) { +		c = f.readByte(); +		if ((c & REP_MASK) == REP_MASK) { +			d = f.readByte();                       // Read data byte +			for (i = 0; i < (c & LEN_MASK); i++) { +				*p++ = d; +				if ((uint16)(p - pline) == bytesPerLine4) +					p = convertPCC(pline, y++, PCC_header.bytesPerLine, imagePtr); +			} +		} else { +			*p++ = c; +			if ((uint16)(p - pline) == bytesPerLine4) +				p = convertPCC(pline, y++, PCC_header.bytesPerLine, imagePtr); +		} +	} +	return seqPtr; +} + +void FileManager::readImage(int objNum, object_t *objPtr) { +// Read object file of PCC images into object supplied +	byte       x, y, j, k; +	uint16     x2;                                  // Limit on x in image data +	seq_t     *seqPtr;                              // Ptr to sequence structure +	image_pt   dibPtr;                              // Ptr to DIB data +	objBlock_t objBlock;                            // Info on file within database +	bool       firstFl = true;                      // Initializes pcx read function + +	debugC(1, kDebugFile, "readImage(%d, object_t *objPtr)", objNum); + +	if (!objPtr->seqNumb)                           // This object has no images +		return; + +	if (_vm.isPacked()) { +		_objectsArchive.seek((uint32)objNum * sizeof(objBlock_t), SEEK_SET); + +		objBlock.objOffset = _objectsArchive.readUint32LE(); +		objBlock.objLength = _objectsArchive.readUint32LE(); + +		_objectsArchive.seek(objBlock.objOffset, SEEK_SET); +	} else { +		char *buf = (char *) malloc(2048 + 1);      // Buffer for file access +		strcat(strcat(strcpy(buf, _vm._picDir), _vm._arrayNouns[objPtr->nounIndex][0]), OBJEXT); +		if (!_objectsArchive.open(buf)) { +			warning("File %s not found, trying again with %s%s", buf, _vm._arrayNouns[objPtr->nounIndex][0], OBJEXT); +			strcat(strcpy(buf, _vm._arrayNouns[objPtr->nounIndex][0]), OBJEXT); +			if (!_objectsArchive.open(buf)) +				Utils::Error(FILE_ERR, buf); +		} +	} + +	// Now read the images into an images list +	for (j = 0; j < objPtr->seqNumb; j++) {         // for each sequence +		for (k = 0; k < objPtr->seqList[j].imageNbr; k++) { // each image +			if (k == 0) {                           // First image +				// Read this image - allocate both seq and image memory +				seqPtr = readPCX(_objectsArchive, NULL, NULL, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]); +				objPtr->seqList[j].seqPtr = seqPtr; +				firstFl = false; +			} else {                                // Subsequent image +				// Read this image - allocate both seq and image memory +				seqPtr->nextSeqPtr = readPCX(_objectsArchive, NULL, NULL, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]); +				seqPtr = seqPtr->nextSeqPtr; +			} + +			// Compute the bounding box - x1, x2, y1, y2 +			// Note use of x2 - marks end of valid data in row +			x2 = seqPtr->x2; +			seqPtr->x1 = seqPtr->x2; +			seqPtr->x2 = 0; +			seqPtr->y1 = seqPtr->lines; +			seqPtr->y2 = 0; +			dibPtr = seqPtr->imagePtr; +			for (y = 0; y < seqPtr->lines; y++, dibPtr += seqPtr->bytesPerLine8 - x2) +				for (x = 0; x < x2; x++) +					if (*dibPtr++) {                    // Some data found +						if (x < seqPtr->x1) +							seqPtr->x1 = x; +						if (x > seqPtr->x2) +							seqPtr->x2 = x; +						if (y < seqPtr->y1) +							seqPtr->y1 = y; +						if (y > seqPtr->y2) +							seqPtr->y2 = y; +					} +		} +		seqPtr->nextSeqPtr = objPtr->seqList[j].seqPtr; // loop linked list to head +	} + +	// Set the current image sequence to first or last +	switch (objPtr->cycling) { +	case INVISIBLE:                                 // (May become visible later) +	case ALMOST_INVISIBLE: +	case NOT_CYCLING: +	case CYCLE_FORWARD: +		objPtr->currImagePtr = objPtr->seqList[0].seqPtr; +		break; +	case CYCLE_BACKWARD: +		objPtr->currImagePtr = seqPtr; +		break; +	} + +	if (!_vm.isPacked()) +		_objectsArchive.close(); +} + +void FileManager::readBackground(int screenIndex) { +// Read a PCX image into dib_a +	seq_t        seq;                               // Image sequence structure for Read_pcx +	sceneBlock_t sceneBlock;                        // Read a database header entry + +	debugC(1, kDebugFile, "readBackground(%d)", screenIndex); + +	if (_vm.isPacked()) { +		_sceneryArchive.seek((uint32) screenIndex * sizeof(sceneBlock_t), SEEK_SET); + +		sceneBlock.scene_off = _sceneryArchive.readUint32LE(); +		sceneBlock.scene_len = _sceneryArchive.readUint32LE(); +		sceneBlock.b_off = _sceneryArchive.readUint32LE(); +		sceneBlock.b_len = _sceneryArchive.readUint32LE(); +		sceneBlock.o_off = _sceneryArchive.readUint32LE(); +		sceneBlock.o_len = _sceneryArchive.readUint32LE(); +		sceneBlock.ob_off = _sceneryArchive.readUint32LE(); +		sceneBlock.ob_len = _sceneryArchive.readUint32LE(); + +		_sceneryArchive.seek(sceneBlock.scene_off, SEEK_SET); +	} else { +		char *buf = (char *) malloc(2048 + 1);      // Buffer for file access +		strcat(strcat(strcpy(buf, _vm._picDir), _vm._screenNames[screenIndex]), BKGEXT); +		if (!_sceneryArchive.open(buf)) { +			warning("File %s not found, trying again with %s.ART", buf, _vm._screenNames[screenIndex]); +			strcat(strcpy(buf, _vm._screenNames[screenIndex]), ".ART"); +			if (!_sceneryArchive.open(buf)) +				Utils::Error(FILE_ERR, buf); +		} +	} + +	// Read the image into dummy seq and static dib_a +	readPCX(_sceneryArchive, &seq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]); + +	if (!_vm.isPacked()) +		_sceneryArchive.close(); +} + +sound_pt FileManager::getSound(int16 sound, uint16 *size) { +// Read sound (or music) file data.  Call with SILENCE to free-up +// any allocated memory.  Also returns size of data + +	static sound_hdr_t s_hdr[MAX_SOUNDS];           // Sound lookup table +	sound_pt           soundPtr;                    // Ptr to sound data +	Common::File       fp;                          // Handle to SOUND_FILE +//	bool               music = sound < NUM_TUNES;    // TRUE if music, else sound file + +	debugC(1, kDebugFile, "getSound(%d, %d)", sound, *size); + +	// No more to do if SILENCE (called for cleanup purposes) +	if (sound == _vm._soundSilence) +		return(NULL); + +	// Open sounds file +	if (!fp.open(SOUND_FILE)) { +//		Error(FILE_ERR, SOUND_FILE); +		warning("Hugo Error: File not found %s", SOUND_FILE); +		return(NULL); +	} + +	// If this is the first call, read the lookup table +	static bool has_read_header = false; +	if (!has_read_header) { +		if (fp.read(s_hdr, sizeof(s_hdr)) != sizeof(s_hdr)) +			Utils::Error(FILE_ERR, SOUND_FILE); +		has_read_header = true; +	} + +	*size = s_hdr[sound].size; +	if (*size == 0) +		Utils::Error(SOUND_ERR, SOUND_FILE); + +	// Allocate memory for sound or music, if possible +	if ((soundPtr = (byte *)malloc(s_hdr[sound].size)) == 0) { +		Utils::Warn(false, "Low on memory"); +		return(NULL); +	} + +	// Seek to data and read it +	fp.seek(s_hdr[sound].offset, SEEK_SET); +	if (fp.read(soundPtr, s_hdr[sound].size) != s_hdr[sound].size) +		Utils::Error(FILE_ERR, SOUND_FILE); + +	fp.close(); + +	return soundPtr; +} + +bool FileManager::fileExists(char *filename) { +// Return whether file exists or not +	Common::File f; +	if (f.open(filename)) { +		f.close(); +		return true; +	} +	return false; +} + +void FileManager::readOverlay(int screenNum, image_pt image, ovl_t overlayType) { +// Open and read in an overlay file, close file +	uint32       i; +	int16        j, k; +	char         data;                              // Must be 8 bits signed +	image_pt     tmpImage = image;                  // temp ptr to overlay file +	sceneBlock_t sceneBlock;                        // Database header entry + +	debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum); + +	if (_vm.isPacked()) { +		_sceneryArchive.seek((uint32)screenNum * sizeof(sceneBlock_t), SEEK_SET); + +		sceneBlock.scene_off = _sceneryArchive.readUint32LE(); +		sceneBlock.scene_len = _sceneryArchive.readUint32LE(); +		sceneBlock.b_off = _sceneryArchive.readUint32LE(); +		sceneBlock.b_len = _sceneryArchive.readUint32LE(); +		sceneBlock.o_off = _sceneryArchive.readUint32LE(); +		sceneBlock.o_len = _sceneryArchive.readUint32LE(); +		sceneBlock.ob_off = _sceneryArchive.readUint32LE(); +		sceneBlock.ob_len = _sceneryArchive.readUint32LE(); + +		switch (overlayType) { +		case BOUNDARY: +			_sceneryArchive.seek(sceneBlock.b_off, SEEK_SET); +			i = sceneBlock.b_len; +			break; +		case OVERLAY: +			_sceneryArchive.seek(sceneBlock.o_off, SEEK_SET); +			i = sceneBlock.o_len; +			break; +		case OVLBASE: +			_sceneryArchive.seek(sceneBlock.ob_off, SEEK_SET); +			i = sceneBlock.ob_len; +			break; +		default: +			Utils::Error(FILE_ERR, "Bad ovl_type"); +			break; +		} +		if (i == 0) { +			for (i = 0; i < OVL_SIZE; i++) +				image[i] = 0; +			return; +		} +	} else { +		const char  *ovl_ext[] = {".b", ".o", ".ob"}; +		char *buf = (char *) malloc(2048 + 1);      // Buffer for file access + +		strcat(strcpy(buf, _vm._screenNames[screenNum]), ovl_ext[overlayType]); + +		if (!fileExists(buf)) { +			for (i = 0; i < OVL_SIZE; i++) +				image[i] = 0; +			return; +		} + +		if (!_sceneryArchive.open(buf)) +			Utils::Error(FILE_ERR, buf); + +//		if (eof(f_scenery)) { +//			_lclose(f_scenery); +//			return; +//		} +	} + +	switch (_vm._gameVariant) { +	case 0:                                         // Hugo 1 DOS and WIN don't pack data +	case 3: +		_sceneryArchive.read(tmpImage, OVL_SIZE); +		break; +	default: +		// Read in the overlay file using MAC Packbits.  (We're not proud!) +		k = 0;                                      // byte count +		do { +			data = _sceneryArchive.readByte();      // Read a code byte +			if ((byte)data == 0x80)             // Noop +				k = k; +			else if (data >= 0) {                   // Copy next data+1 literally +				for (i = 0; i <= (byte)data; i++, k++) +					*tmpImage++ = _sceneryArchive.readByte(); +			} else {                            // Repeat next byte -data+1 times +				j = _sceneryArchive.readByte(); + +				for (i = 0; i < (byte)(-data + 1); i++, k++) +					*tmpImage++ = j; +			} +		} while (k < OVL_SIZE); +		break; +	} + +	if (!_vm.isPacked()) +		_sceneryArchive.close(); +} + +void FileManager::saveSeq(object_t *obj) { +// Save sequence number and image number in given object +	byte   j, k; +	seq_t *q; +	bool   found; + +	debugC(1, kDebugFile, "saveSeq"); + +	for (j = 0, found = false; !found && (j < obj->seqNumb); j++) { +		q = obj->seqList[j].seqPtr; +		for (k = 0; !found && (k < obj->seqList[j].imageNbr); k++) { +			if (obj->currImagePtr == q) { +				found = true; +				obj->curSeqNum = j; +				obj->curImageNum = k; +			} else +				q = q->nextSeqPtr; +		} +	} +} + +void FileManager::restoreSeq(object_t *obj) { +// Set up cur_seq_p from stored sequence and image number in object +	int    j; +	seq_t *q; + +	debugC(1, kDebugFile, "restoreSeq"); + +	q = obj->seqList[obj->curSeqNum].seqPtr; +	for (j = 0; j < obj->curImageNum; j++) +		q = q->nextSeqPtr; +	obj->currImagePtr = q; +} + +void FileManager::saveGame(int16 slot, const char *descrip) { +// Save game to supplied slot (-1 is INITFILE) +	int     i; +	char    path[256];                                  // Full path of saved game + +	debugC(1, kDebugFile, "saveGame(%d, %s)", slot, descrip); + +	// Get full path of saved game file - note test for INITFILE +	if (slot == -1) +		sprintf(path, "%s", _vm._initFilename); +	else +		sprintf(path, _vm._saveFilename, slot); + +	Common::WriteStream *out = 0; +	if (!(out = _vm.getSaveFileManager()->openForSaving(path))) { +		warning("Can't create file '%s', game not saved", path); +		return; +	} + +	// Write version.  We can't restore from obsolete versions +	out->write(&kSavegameVersion, sizeof(kSavegameVersion)); + +	// Save description of saved game +	out->write(descrip, DESCRIPLEN); + +	// Save objects +	for (i = 0; i < _vm._numObj; i++) { +		// Save where curr_seq_p is pointing to +		saveSeq(&_vm._objects[i]); +		out->write(&_vm._objects[i], sizeof(object_t)); +	} + +	const status_t &gameStatus = _vm.getGameStatus(); + +	// Save whether hero image is swapped +	out->write(&_vm._heroImage, sizeof(_vm._heroImage)); + +	// Save score +	int score = _vm.getScore(); +	out->write(&score, sizeof(score)); + +	// Save story mode +	out->write(&gameStatus.storyModeFl, sizeof(gameStatus.storyModeFl)); + +	// Save jumpexit mode +	out->write(&gameStatus.jumpExitFl, sizeof(gameStatus.jumpExitFl)); + +	// Save gameover status +	out->write(&gameStatus.gameOverFl, sizeof(gameStatus.gameOverFl)); + +	// Save screen states +	out->write(_vm._screenStates, sizeof(*_vm._screenStates) * _vm._numScreens); + +	// Save points table +	out->write(_vm._points, sizeof(point_t) * _vm._numBonuses); + +	// Now save current time and all current events in event queue +	_vm.scheduler().saveEvents(out); + +	// Save palette table +	_vm.screen().savePal(out); + +	// Save maze status +	out->write(&_maze, sizeof(maze_t)); + +	out->finalize(); + +	delete out; +} + +void FileManager::restoreGame(int16 slot) { +// Restore game from supplied slot number (-1 is INITFILE) +	int       i; +	char      path[256];                            // Full path of saved game +	object_t *p; +	seqList_t seqList[MAX_SEQUENCES]; +//	cmdList  *cmds;                                  // Save command list pointer +	uint16    cmdIndex;                             // Save command list pointer +//	char      ver[sizeof(VER)];                      // Compare versions + +	debugC(1, kDebugFile, "restoreGame(%d)", slot); + +	// Initialize new-game status +	_vm.initStatus(); + +	// Get full path of saved game file - note test for INITFILE +	if (slot == -1) +		sprintf(path, "%s", _vm._initFilename); +	else +		sprintf(path, _vm._saveFilename, slot); + +	Common::SeekableReadStream *in = 0; +	if (!(in = _vm.getSaveFileManager()->openForLoading(path))) +		return; + +	// Check version, can't restore from different versions +	int saveVersion; +	in->read(&saveVersion, sizeof(saveVersion)); +	if (saveVersion != kSavegameVersion) { +		Utils::Error(GEN_ERR, "Savegame of incompatible version"); +		return; +	} + +	// Skip over description +	in->seek(DESCRIPLEN, SEEK_CUR); + +	// If hero image is currently swapped, swap it back before restore +	if (_vm._heroImage != HERO) +		_vm.scheduler().swapImages(HERO, _vm._heroImage); + +	// Restore objects, retain current seqList which points to dynamic mem +	// Also, retain cmnd_t pointers +	for (i = 0; i < _vm._numObj; i++) { +		p = &_vm._objects[i]; +		memcpy(seqList, p->seqList, sizeof(seqList_t)); +		cmdIndex = p->cmdIndex; +		in->read(p, sizeof(object_t)); +		p->cmdIndex = cmdIndex; +		memcpy(p->seqList, seqList, sizeof(seqList_t)); +	} + +	in->read(&_vm._heroImage, sizeof(_vm._heroImage)); + +	// If hero swapped in saved game, swap it +	if ((i = _vm._heroImage) != HERO) +		_vm.scheduler().swapImages(HERO, _vm._heroImage); +	_vm._heroImage = i; + +	status_t &gameStatus = _vm.getGameStatus(); + +	int score; +	in->read(&score, sizeof(score)); +	_vm.setScore(score); + +	in->read(&gameStatus.storyModeFl, sizeof(gameStatus.storyModeFl)); +	in->read(&gameStatus.jumpExitFl, sizeof(gameStatus.jumpExitFl)); +	in->read(&gameStatus.gameOverFl, sizeof(gameStatus.gameOverFl)); +	in->read(_vm._screenStates, sizeof(*_vm._screenStates) * _vm._numScreens); + +	// Restore points table +	in->read(_vm._points, sizeof(point_t) * _vm._numBonuses); + +	// Restore ptrs to currently loaded objects +	for (i = 0; i < _vm._numObj; i++) +		restoreSeq(&_vm._objects[i]); + +	// Now restore time of the save and the event queue +	_vm.scheduler().restoreEvents(in); + +	// Restore palette and change it if necessary +	_vm.screen().restorePal(in); + +	// Restore maze status +	in->read(&_maze, sizeof(maze_t)); + +	delete in; +} + +void FileManager::initSavedGame() { +// Initialize the size of a saved game (from the fixed initial game). +// If status.initsave is TRUE, or the initial saved game is not found, +// force a save to create one.  Normally the game will be shipped with +// the initial game file but useful to force a write during development +// when the size is changeable. +// The net result is a valid INITFILE, with status.savesize initialized. +	Common::File f;                                 // Handle of saved game file +	char path[256];                                 // Full path of INITFILE + +	debugC(1, kDebugFile, "initSavedGame"); + +	// Get full path of INITFILE +	sprintf(path, "%s", _vm._initFilename); + + +	// Force save of initial game +	if (_vm.getGameStatus().initSaveFl) +		saveGame(-1, ""); + +	// If initial game doesn't exist, create it +	Common::SeekableReadStream *in = 0; +	if (!(in = _vm.getSaveFileManager()->openForLoading(path))) { +		saveGame(-1, ""); +		if (!(in = _vm.getSaveFileManager()->openForLoading(path))) { +			Utils::Error(WRITE_ERR, path); +			return; +		} +	} + +	// Must have an open saved game now +	_vm.getGameStatus().saveSize = in->size(); +	delete in; + +	// Check sanity - maybe disk full or path set to read-only drive? +	if (_vm.getGameStatus().saveSize == -1) +		Utils::Error(WRITE_ERR, path); +} + +// Record and playback handling stuff: +typedef struct { +//	int    key;                                     // Character +	uint32 time;                                    // Time at which character was pressed +} pbdata_t; +static pbdata_t pbdata; +FILE            *fpb; + +void FileManager::openPlaybackFile(bool playbackFl, bool recordFl) { +	debugC(1, kDebugFile, "openPlaybackFile(%d, %d)", (playbackFl) ? 1 : 0, (recordFl) ? 1 : 0); + +	if (playbackFl) { +		if (!(fpb = fopen(PBFILE, "r+b"))) +			Utils::Error(FILE_ERR, PBFILE); +	} else if (recordFl) +		fpb = fopen(PBFILE, "wb"); +	pbdata.time = 0;                                // Say no key available +} + +void FileManager::closePlaybackFile() { +	fclose(fpb); +} + +void FileManager::openDatabaseFiles() { +//TODO : HUGO 1 DOS uses _stringtData instead of a strings.dat +//This should be tested adequately and should be handled by an error and not by a warning. +	debugC(1, kDebugFile, "openDatabaseFiles"); + +	if (!_stringArchive.open(STRING_FILE)) +//		Error(FILE_ERR, STRING_FILE); +		warning("Hugo Error: File not found %s", STRING_FILE); +	if (_vm.isPacked()) { +		if (!_sceneryArchive.open(SCENERY_FILE)) +			Utils::Error(FILE_ERR, SCENERY_FILE); +		if (!_objectsArchive.open(OBJECTS_FILE)) +			Utils::Error(FILE_ERR, OBJECTS_FILE); +	} +} + +void FileManager::closeDatabaseFiles() { +// TODO: stringArchive shouldn't be closed in Hugo 1 DOS +	debugC(1, kDebugFile, "closeDatabaseFiles"); + +	_stringArchive.close(); +	if (_vm.isPacked()) { +		_sceneryArchive.close(); +		_objectsArchive.close(); +	} +} + +char *FileManager::fetchString(int index) { +//TODO : HUGO 1 DOS uses _stringtData instead of a strings.dat +// Fetch string from file, decode and return ptr to string in memory +	uint32 off1, off2; + +	debugC(1, kDebugFile, "fetchString(%d)", index); + +	// Get offset to string[index] (and next for length calculation) +	_stringArchive.seek((uint32)index * sizeof(uint32), SEEK_SET); +	if (_stringArchive.read((char *)&off1, sizeof(uint32)) == 0) +		Utils::Error(FILE_ERR, "String offset"); +	if (_stringArchive.read((char *)&off2, sizeof(uint32)) == 0) +		Utils::Error(FILE_ERR, "String offset"); + +	// Check size of string +	if ((off2 - off1) >= MAX_BOX) +		Utils::Error(FILE_ERR, "Fetched string too long!"); + +	// Position to string and read it into gen purpose _textBoxBuffer +	_stringArchive.seek(off1, SEEK_SET); +	if (_stringArchive.read(_textBoxBuffer, (uint16)(off2 - off1)) == 0) +		Utils::Error(FILE_ERR, "Fetch_string"); + +	// Null terminate, decode and return it +	_textBoxBuffer[off2-off1] = '\0'; +	_vm.scheduler().decodeString(_textBoxBuffer); +	return _textBoxBuffer; +} + +void FileManager::printBootText() { +// Read the encrypted text from the boot file and print it +	Common::File ofp; +	int  i; +	char *buf; + +	debugC(1, kDebugFile, "printBootText"); + +	if (!ofp.open(BOOTFILE)) +		Utils::Error(FILE_ERR, BOOTFILE); + +	// Allocate space for the text and print it +	buf = (char *)malloc(_boot.exit_len + 1); +	if (buf) { +		// Skip over the boot structure (already read) and read exit text +		ofp.seek((long)sizeof(_boot), SEEK_SET); +		if (ofp.read(buf, _boot.exit_len) != (size_t)_boot.exit_len) +			Utils::Error(FILE_ERR, BOOTFILE); + +		// Decrypt the exit text, using CRYPT substring +		for (i = 0; i < _boot.exit_len; i++) +			buf[i] ^= CRYPT[i % strlen(CRYPT)]; + +		buf[i] = '\0'; +		//Box(BOX_OK, buf_p); +		//MessageBox(hwnd, buf_p, "License", MB_ICONINFORMATION); +		warning("printBootText(): License: %s", buf); +	} + +	free(buf); +	ofp.close(); +} + +void FileManager::readBootFile() { +// Reads boot file for program environment.  Fatal error if not there or +// file checksum is bad.  De-crypts structure while checking checksum +	byte checksum; +	byte *p; +	Common::File ofp; +	uint32 i; + +	debugC(1, kDebugFile, "readBootFile"); + +	if (!ofp.open(BOOTFILE)) +		Utils::Error(FILE_ERR, BOOTFILE); + +	if (ofp.size() < (int32)sizeof(_boot)) +		Utils::Error(FILE_ERR, BOOTFILE); + +	_boot.checksum = ofp.readByte(); +	_boot.registered = ofp.readByte(); +	ofp.read(_boot.pbswitch, sizeof(_boot.pbswitch)); +	ofp.read(_boot.distrib, sizeof(_boot.distrib)); +	_boot.exit_len = ofp.readUint16LE(); + +	p = (byte *)&_boot; +	for (i = 0, checksum = 0; i < sizeof(_boot); i++) { +		checksum ^= p[i]; +		p[i] ^= CRYPT[i % strlen(CRYPT)]; +	} +	ofp.close(); + +	if (checksum) +		Utils::Error(GEN_ERR, "Program startup file invalid"); +} + +void FileManager::readConfig() { +// Read the user's config if it exists +	Common::File f; +	fpath_t  path; +	config_t tmpConfig = _config; + +	debugC(1, kDebugFile, "readConfig"); + +	sprintf(path, "%s%s", _vm.getGameStatus().path, CONFIGFILE); +	if (f.open(path)) { +		// If config format changed, ignore it and use defaults +		if (f.read(&_config, sizeof(_config)) != sizeof(_config)) +			_config = tmpConfig; + +		f.close(); +	} +} + +void FileManager::writeConfig() { +// Write the user's config +	FILE   *f; +	fpath_t path; + +	debugC(1, kDebugFile, "writeConfig"); + +	// Write user's config +	// No error checking in case CD-ROM with no alternate path specified +	sprintf(path, "%s%s", _vm.getGameStatus().path, CONFIGFILE); +	if ((f = fopen(path, "w+")) != NULL) +		fwrite(&_config, sizeof(_config), 1, f); + +	fclose(f); +} + +uif_hdr_t *FileManager::getUIFHeader(uif_t id) { +// Returns address of uif_hdr[id], reading it in if first call +	static uif_hdr_t UIFHeader[MAX_UIFS];           // Lookup for uif fonts/images +	static bool firstFl = true; +	Common::File ip;                                // Image data file + +	debugC(1, kDebugFile, "getUIFHeader(%d)", id); + +	// Initialize offset lookup if not read yet +	if (firstFl) { +		firstFl = false; +		// Open unbuffered to do far read +		if (!ip.open(UIF_FILE)) +			Utils::Error(FILE_ERR, UIF_FILE); + +		if (ip.size() < (int32)sizeof(UIFHeader)) +			Utils::Error(FILE_ERR, UIF_FILE); + +		for (int i = 0; i < MAX_UIFS; ++i) { +			UIFHeader[i].size = ip.readUint16LE(); +			UIFHeader[i].offset = ip.readUint32LE(); +		} + +		ip.close(); +	} +	return &UIFHeader[id]; +} + +void FileManager::readUIFItem(int16 id, byte *buf) { +// Read uif item into supplied buffer. +	Common::File ip;                                // UIF_FILE handle +	uif_hdr_t *UIFHeaderPtr;                        // Lookup table of items +	seq_t seq;                                      // Dummy seq_t for image data + +	debugC(1, kDebugFile, "readUIFItem(%d, ...)", id); + +	// Open uif file to read data +	if (!ip.open(UIF_FILE)) +		Utils::Error(FILE_ERR, UIF_FILE); + +	// Seek to data +	UIFHeaderPtr = getUIFHeader((uif_t)id); +	ip.seek(UIFHeaderPtr->offset, SEEK_SET); + +	// We support pcx images and straight data +	switch (id) { +	case UIF_IMAGES:                                // Read uif images file +		readPCX(ip, &seq, buf, true, UIF_FILE); +		break; +	default:                                        // Read file data into supplied array +		if (ip.read(buf, UIFHeaderPtr->size) != UIFHeaderPtr->size) +			Utils::Error(FILE_ERR, UIF_FILE); +		break; +	} + +	ip.close(); +} + +void FileManager::instructions() { +// Simple instructions given when F1 pressed twice in a row +// Only in DOS versions +#define HELPFILE "help.dat" +#define EOP '#' /* Marks end of a page in help file */ + +	Common::File f; +	char line[1024], *wrkLine; +	char readBuf[2]; + +	wrkLine = line; +	if (!f.open(UIF_FILE)) +		Utils::Error(FILE_ERR, HELPFILE); + +	while (f.read(readBuf, 1)) { +		wrkLine[0] = readBuf[0]; +		do { +			f.read(wrkLine, 1); +		} while (*wrkLine++ != EOP); +		wrkLine[-2] = '\0';      /* Remove EOP and previous CR */ +		Utils::Box(BOX_ANY, line); +		f.read(wrkLine, 1);    /* Remove CR after EOP */ +	} +	f.close(); +} + +} // end of namespace Hugo diff --git a/engines/hugo/file.h b/engines/hugo/file.h new file mode 100755 index 0000000000..50f4b505c3 --- /dev/null +++ b/engines/hugo/file.h @@ -0,0 +1,86 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef HUGO_FILE_H +#define HUGO_FILE_H + +namespace Hugo { + +class FileManager { +public: +	FileManager(HugoEngine &vm); + + +	bool     fileExists(char *filename); + +	char    *fetchString(int index); + +	sound_pt getSound(short sound, uint16 *size); + +	void     closePlaybackFile(); +	void     closeDatabaseFiles(); +	void     initSavedGame(); +	void     instructions(); +	void     openDatabaseFiles(); +	void     readBackground(int screenIndex); +	void     readBootFile(); +	void     readConfig(); +	void     readImage(int objNum, object_t *objPtr); +	void     readOverlay(int screenNum, image_pt image, ovl_t overlayType); +	void     readUIFItem(short id, byte *buf); +	void     restoreGame(short slot); +	void     restoreSeq(object_t *obj); +	void     saveGame(short slot, const char *descrip); +	void     saveSeq(object_t *obj); +	void     writeConfig(); + +private: +	HugoEngine &_vm; + +	Common::File _stringArchive;                        /* Handle for string file */ +	Common::File _sceneryArchive;                       /* Handle for scenery file */ +	Common::File _objectsArchive;                       /* Handle for objects file */ + +	byte *convertPCC(byte *p, uint16 y, uint16 bpl, image_pt data_p); +	seq_t *readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool firstFl, const char *name); +	uif_hdr_t *getUIFHeader(uif_t id); + +//Strangerke : Not used? +	void     openPlaybackFile(bool playbackFl, bool recordFl); +	void     printBootText(); +//	bool     pkkey(); +//	char     pbget(); +}; + + +} // end of namespace Hugo +#endif //HUGO_FILE_H diff --git a/engines/hugo/game.h b/engines/hugo/game.h new file mode 100755 index 0000000000..e2e685c193 --- /dev/null +++ b/engines/hugo/game.h @@ -0,0 +1,880 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef HUGO_GAME_H +#define HUGO_GAME_H + +#include "common/keyboard.h" + +namespace Common { +class WriteStream; +class SeekableReadStream; +} + +namespace Hugo { + +// WARNING!!  Run the program at least once before release to +// generate the initial save file!  (Using the -i cmd switch) +// Set EPISODE_NUM & build.  Build pictures.mak and run "Tools/Hugo N". +// Copy helpedit\hugow_?.hlp to .\hugowin?.hlp +// Type "PPG" in the game to enter cheat mode. + +#define DEBUG       false                           // Allow me to do special things (EDIT, GOTO) +#define STORY       true                            // Skip any intro stuff if FALSE +//#define DATABASE    TRUE                          // Use database instead of individual files. Individual files are used by Hugo1 DOS! +//#define BETA        FALSE                         // Puts big msg in intro/about box +#define EPISODE_NUM         1                       // Episode we are building +#define TITLE       "Hugo for Windows" +#define COPYRIGHT   "Copyright © 1995-97, David P. Gray" +// Started code on 04/01/95 +// Don't forget to update Hugowin.rc2 with version info +//#define VER "1.0" // 10/01/95 Initial Release +//#define VER "1.1" // 10/06/95 Restore system volume levels on exit +//#define VER "v1.2"// 10/12/95 Added "background music" checkbox in volume dlg +//#define VER "v1.3"// 10/23/95 Support game 1 as shareware +//#define VER "v1.4"// 12/06/95 Faster graphics, logical palette +#define VER "v1.5"  // 10/07/97 Added order form, new web site + +// Game specific equates +#define MAX_TUNES      16                           // Max number of tunes +#define NORMAL_TPS     9                            // Number of ticks (frames) per second +#define TURBO_TPS      16                           // This many in turbo mode +#define DX             5                            // Num pixels moved in x by HERO per step +#define DY             4                            // Num pixels moved in y by HERO per step +#define XBYTES         40                           // number of bytes in a compressed line +#define XPIX           320                          // Width of pcx background file +#define YPIX           200                          // Height of pcx background file +#define VIEW_DX        XPIX                         // Width of window view +#define VIEW_DY        184                          // Height of window view +#define INV_DX         32                           // Width of an inventory icon +#define INV_DY         32                           // Height of inventory icon +#define DIBOFF_Y       8                            // Offset into dib SrcY (old status line area) +#define OVL_SIZE       (XBYTES * YPIX)              // Size of an overlay file +#define MAX_SEQUENCES  4                            // Number of sequences of images in object +#define MAX_CHARS      (XBYTES - 2)                 // Max length of user input line +#define NUM_ROWS       25                           // Number of text lines in display +#define MAX_BOX        (MAX_CHARS * NUM_ROWS)       // Max chars on screen +#define DONT_CARE      0xFF                         // Any state allowed in command verb +#define MAX_FPATH      256                          // Max length of a full path name +#define HERO_MAX_WIDTH 24                           // Maximum width of hero +#define HERO_MIN_WIDTH 16                           // Minimum width of hero +#define LOOK_NAME      1                            // Index of name used in showing takeables +#define TAKE_NAME      2                            // Index of name used in confirming take +#define TAKE_TEXT      "Picked up the %s ok." +#define REP_MASK       0xC0                         // Top 2 bits mean a repeat code +#define MAX_STRLEN     1024 +#define WARNLEN        512 +#define ERRLEN         512 +#define STEP_DY        8                            // Pixels per step movement + +// Only for non-database +#define BKGEXT         ".PCX"                       // Extension of background files +#define OBJEXT         ".PIX"                       // Extension of object picture files +#define NAME_LEN       12                           // Max length of a DOS file name + +// Definitions of 'generic' commands: Max # depends on size of gencmd in +//   the object_t record since each requires 1 bit.  Currently up to 16 +#define LOOK           1 +#define TAKE           2 +#define DROP           4 +#define LOOK_S         8                            // Description depends on state of object + +// Macros: +#define TPS           (_config.turboFl ? TURBO_TPS : NORMAL_TPS) + + +enum TEXTCOLORS { +	_TBLACK,    _TBLUE,         _TGREEN,       _TCYAN, +	_TRED,      _TMAGENTA,      _TBROWN,       _TWHITE, +	_TGRAY,     _TLIGHTBLUE,    _TLIGHTGREEN,  _TLIGHTCYAN, +	_TLIGHTRED, _TLIGHTMAGENTA, _TLIGHTYELLOW, _TBRIGHTWHITE +}; + +#define CRYPT "Copyright 1992, David P Gray, Gray Design Associates" + +struct hugo_boot_t {                                // Common HUGO boot file +	char checksum;                                  // Checksum for boot structure (not exit text) +	char registered;                                // TRUE if registered version, else FALSE +	char pbswitch[8];                               // Playback switch string +	char distrib[32];                               // Distributor branding string +	uint16 exit_len;                                // Length of exit text (next in file) +}; + +#define MAX_UIFS   32                               // Max possible uif items in hdr +#define NUM_FONTS  3                                // Number of dib fonts +#define FIRST_FONT U_FONT5 +#define FONT_LEN   128                              // Number of chars in font +#define FONTSIZE   1200                             // Max size of font data + +struct uif_hdr_t {                                  // UIF font/image look up +	uint16  size;                                   // Size of uif item +	uint32  offset;                                 // Offset of item in file +}; + +enum uif_t {U_FONT5, U_FONT6, U_FONT8, UIF_IMAGES, NUM_UIF_ITEMS}; + +// Game specific type definitions +typedef byte *image_pt;                             // ptr to an object image (sprite) +typedef byte *sound_pt;                             // ptr to sound (or music) data + +// Enumerate overlay file types +enum ovl_t {BOUNDARY, OVERLAY, OVLBASE}; + +// Enumerate error types +enum { +	GEN_ERR,   FILE_ERR, WRITE_ERR, PCCH_ERR, HEAP_ERR, EVNT_ERR,  SOUND_ERR +	//MOUSE_ERR, VID_ERR,  FONT_ERR,  ARG_ERR,  CHK_ERR,  TIMER_ERR, VBX_ERR +}; + +// Enumerate ways of cycling a sequence of frames +enum cycle_t {INVISIBLE, ALMOST_INVISIBLE, NOT_CYCLING, CYCLE_FORWARD, CYCLE_BACKWARD}; + +// Enumerate sequence index matching direction of travel +enum {RIGHT, LEFT, DOWN, _UP}; + +// Channel requirement during sound effect +enum stereo_t {BOTH_CHANNELS, RIGHT_CHANNEL, LEFT_CHANNEL}; + +// Priority for sound effect +enum priority_t {LOW_PRI, MED_PRI, HIGH_PRI}; + +// Enumerate the different path types for an object +enum path_t { +	USER,                                           // User has control of object via cursor keys +	AUTO,                                           // Computer has control, controlled by action lists +	QUIET,                                          // Computer has control and no commands allowed +	CHASE,                                          // Computer has control, object is chasing hero +	CHASE2,                                         // Same as CHASE, except keeps cycling when stationary +	WANDER,                                         // Computer has control, object is wandering randomly +	WANDER2                                         // Same as WANDER, except keeps cycling when stationary +}; + +// Enumerate whether object is foreground, background or 'floating' +// If floating, HERO can collide with it and fore/back ground is determined +// by relative y-coord of object base.  This is the general case. +// If fore or background, no collisions can take place and object is either +// behind or in front of all others, although can still be hidden by the +// the overlay plane.  OVEROVL means the object is FLOATING (to other +// objects) but is never hidden by the overlay plane +enum {FOREGROUND, BACKGROUND, FLOATING, OVEROVL}; + +// Game view state machine +enum vstate_t {V_IDLE, V_INTROINIT, V_INTRO, V_PLAY, V_INVENT, V_EXIT}; + +enum font_t {LARGE_ROMAN, MED_ROMAN, NUM_GDI_FONTS, INIT_FONTS, DEL_FONTS}; + +// Ways to dismiss a text/prompt box +enum box_t {BOX_ANY, BOX_OK, BOX_PROMPT, BOX_YESNO}; + +// Standard viewport sizes +enum wsize_t {SIZE_DEF, SIZE_1, SIZE_2, SIZE_3}; + +// Display list functions +enum dupdate_t {D_INIT, D_ADD, D_DISPLAY, D_RESTORE}; + +// General device installation commands +enum inst_t {INSTALL, RESTORE, RESET}; + +// Inventory icon bar states +enum istate_t {I_OFF, I_UP, I_DOWN, I_ACTIVE}; + +// Actions for Process_inventory() +enum invact_t {INV_INIT, INV_LEFT, INV_RIGHT, INV_GET}; + +// Purpose of an automatic route +enum go_t {GO_SPACE, GO_EXIT, GO_LOOK, GO_GET}; + +// Following are points for achieving certain actions. +struct point_t { +	byte score;                                     // The value of the point +	bool scoredFl;                                  // Whether scored yet +}; + +// Structure for initializing maze processing +struct maze_t { +	bool enabledFl;                                 // TRUE when maze processing enabled +	byte size;                                      // Size of (square) maze matrix +	int  x1, y1, x2, y2;                            // maze hotspot bounding box +	int  x3, x4;                                    // north, south x entry coordinates +	byte firstScreenIndex;                          // index of first screen in maze +}; + +// Following defines the action types and action list +enum action_t {                                     // Parameters: +	ANULL              = 0xff,                      // Special NOP used to 'delete' events in DEL_EVENTS +	ASCHEDULE          = 0,                         //  0 - Ptr to action list to be rescheduled +	START_OBJ,                                      //  1 - Object number +	INIT_OBJXY,                                     //  2 - Object number, x,y +	PROMPT,                                         //  3 - index of prompt & response string, ptrs to action +	// lists.  First if response matches, 2nd if not. +	BKGD_COLOR,                                     //  4 - new background color +	INIT_OBJVXY,                                    //  5 - Object number, vx, vy +	INIT_CARRY,                                     //  6 - Object number, carried status +	INIT_HF_COORD,                                  //  7 - Object number (gets hero's 'feet' coordinates) +	NEW_SCREEN,                                     //  8 - New screen number +	INIT_OBJSTATE,                                  //  9 - Object number, new object state +	INIT_PATH,                                      // 10 - Object number, new path type +	COND_R,                                         // 11 - Conditional on object state - req state, 2 act_lists +	TEXT,                                           // 12 - Simple text box +	SWAP_IMAGES,                                    // 13 - Swap 2 object images +	COND_SCR,                                       // 14 - Conditional on current screen +	AUTOPILOT,                                      // 15 - Set object to home in on another (stationary) object +	INIT_OBJ_SEQ,                                   // 16 - Object number, sequence index to set curr_seq_p to +	SET_STATE_BITS,                                 // 17 - Objnum, mask to OR with obj states word +	CLEAR_STATE_BITS,                               // 18 - Objnum, mask to ~AND with obj states word +	TEST_STATE_BITS,                                // 19 - Objnum, mask to test obj states word +	DEL_EVENTS,                                     // 20 - Action type to delete all occurrences of +	GAMEOVER,                                       // 21 - Disable hero & commands.  Game is over +	INIT_HH_COORD,                                  // 22 - Object number (gets hero's actual coordinates) +	EXIT,                                           // 23 - Exit game back to DOS +	BONUS,                                          // 24 - Get score bonus for an action +	COND_BOX,                                       // 25 - Conditional on object within bounding box +	SOUND,                                          // 26 - Set currently playing sound +	ADD_SCORE,                                      // 27 - Add object's value to current score +	SUB_SCORE,                                      // 28 - Subtract object's value from current score +	COND_CARRY,                                     // 29 - Conditional on carrying object +	INIT_MAZE,                                      // 30 - Start special maze hotspot processing +	EXIT_MAZE,                                      // 31 - Exit special maze processing +	INIT_PRIORITY,                                  // 32 - Initialize fbg field +	INIT_SCREEN,                                    // 33 - Initialise screen field of object +	AGSCHEDULE,                                     // 34 - Global schedule - lasts over new screen +	REMAPPAL,                                       // 35 - Remappe palette - palette index, color +	COND_NOUN,                                      // 36 - Conditional on noun appearing in line +	SCREEN_STATE,                                   // 37 - Set new screen state - used for comments +	INIT_LIPS,                                      // 38 - Position lips object for supplied object +	INIT_STORY_MODE,                                // 39 - Set story mode TRUE/FALSE (user can't type) +	WARN,                                           // 40 - Same as TEXT but can't dismiss box by typing +	COND_BONUS,                                     // 41 - Conditional on bonus having been scored +	TEXT_TAKE,                                      // 42 - Issue text box with "take" info string +	YESNO,                                          // 43 - Prompt user for Yes or No +	STOP_ROUTE,                                     // 44 - Skip any route in progress (hero still walks) +	COND_ROUTE,                                     // 45 - Conditional on route in progress +	INIT_JUMPEXIT,                                  // 46 - Initialize status.jumpexit +	INIT_VIEW,                                      // 47 - Initialize viewx, viewy, dir +	INIT_OBJ_FRAME,                                 // 48 - Object number, seq,frame to set curr_seq_p to +	OLD_SONG           = 49                         // Added by Strangerke - Set currently playing sound, old way: that is, using a string index instead of a reference in a file +}; + + +struct act0 {                                       // Type 0 - Schedule +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	uint16   actIndex;                              // Ptr to an action list +}; + +struct act1 {                                       // Type 1 - Start an object +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	int      cycleNumb;                             // Number of times to cycle +	cycle_t  cycle;                                 // Direction to start cycling +}; + +struct act2 {                                       // Type 2 - Initialise an object coords +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	int      x, y;                                  // Coordinates +}; + +struct act3 {                                       // Type 3 - Prompt user for text +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	uint16   promptIndex;                           // Index of prompt string +	int     *responsePtr;                           // Array of indexes to valid response +	// string(s) (terminate list with -1) +	uint16   actPassIndex;                          // Ptr to action list if success +	uint16   actFailIndex;                          // Ptr to action list if failure +	bool     encodedFl;                             // (HUGO 1 DOS ONLY) Whether response is encoded or not +}; + +struct act4 {                                       // Type 4 - Set new background color +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	long     newBackgroundColor;                    // New color +}; + +struct act5 {                                       // Type 5 - Initialise an object velocity +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	int      vx, vy;                                // velocity +}; + +struct act6 {                                       // Type 6 - Initialise an object carrying +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	bool     carriedFl;                             // carrying +}; + +struct act7 {                                       // Type 7 - Initialise an object to hero's coords +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +}; + +struct act8 {                                       // Type 8 - switch to new screen +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      screenIndex;                           // The new screen number +}; + +struct act9 {                                       // Type 9 - Initialise an object state +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	byte     newState;                              // New state +}; + +struct act10 {                                      // Type 10 - Initialise an object path type +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	int      newPathType;                           // New path type +	char     vxPath, vyPath;                        // Max delta velocities e.g. for CHASE +}; + +struct act11 {                                      // Type 11 - Conditional on object's state +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	byte     stateReq;                              // Required state +	uint16   actPassIndex;                          // Ptr to action list if success +	uint16   actFailIndex;                          // Ptr to action list if failure +}; + +struct act12 {                                      // Type 12 - Simple text box +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      stringIndex;                           // Index (enum) of string in strings.dat +}; + +struct act13 {                                      // Type 13 - Swap first object image with second +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      obj1;                                  // Index of first object +	int      obj2;                                  // 2nd +}; + +struct act14 {                                      // Type 14 - Conditional on current screen +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The required object +	int      screenReq;                             // The required screen number +	uint16   actPassIndex;                          // Ptr to action list if success +	uint16   actFailIndex;                          // Ptr to action list if failure +}; + +struct act15 {                                      // Type 15 - Home in on an object +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      obj1;                                  // The object number homing in +	int      obj2;                                  // The object number to home in on +	char     dx, dy;                                // Max delta velocities +}; +// Note: Don't set a sequence at time 0 of a new screen, it causes +// problems clearing the boundary bits of the object!  timer > 0 is safe +struct act16 {                                      // Type 16 - Set curr_seq_p to seq +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	int      seqIndex;                              // The index of seq array to set to +}; + +struct act17 {                                      // Type 17 - SET obj individual state bits +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	int      stateMask;                             // The mask to OR with current obj state +}; + +struct act18 {                                      // Type 18 - CLEAR obj individual state bits +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	int      stateMask;                             // The mask to ~AND with current obj state +}; + +struct act19 {                                      // Type 19 - TEST obj individual state bits +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	int      stateMask;                             // The mask to AND with current obj state +	uint16   actPassIndex;                          // Ptr to action list (all bits set) +	uint16   actFailIndex;                          // Ptr to action list (not all set) +}; + +struct act20 {                                      // Type 20 - Remove all events with this type of action +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	action_t actTypeDel;                            // The action type to remove +}; + +struct act21 {                                      // Type 21 - Gameover.  Disable hero & commands +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +}; + +struct act22 {                                      // Type 22 - Initialise an object to hero's coords +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +}; + +struct act23 {                                      // Type 23 - Exit game back to DOS +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +}; + +struct act24 {                                      // Type 24 - Get bonus score +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      pointIndex;                            // Index into points array +}; + +struct act25 {                                      // Type 25 - Conditional on bounding box +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The required object number +	int      x1, y1, x2, y2;                        // The bounding box +	uint16   actPassIndex;                          // Ptr to action list if success +	uint16   actFailIndex;                          // Ptr to action list if failure +}; + +struct act26 {                                      // Type 26 - Play a sound +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int16    soundIndex;                            // Sound index in data file +}; + +struct act27 {                                      // Type 27 - Add object's value to score +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // object number +}; + +struct act28 {                                      // Type 28 - Subtract object's value from score +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // object number +}; + +struct act29 {                                      // Type 29 - Conditional on object carried +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The required object number +	uint16   actPassIndex;                          // Ptr to action list if success +	uint16   actFailIndex;                          // Ptr to action list if failure +}; + +struct act30 {                                      // Type 30 - Start special maze processing +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	byte     mazeSize;                              // Size of (square) maze +	int      x1, y1, x2, y2;                        // Bounding box of maze +	int      x3, x4;                                // Extra x points for perspective correction +	byte     firstScreenIndex;                      // First (top left) screen of maze +}; + +struct act31 {                                      // Type 31 - Exit special maze processing +	action_t actType;                                   // The type of action +	int      timer;                                 // Time to set off the action +}; + +struct act32 {                                      // Type 32 - Init fbg field of object +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	byte     priority;                              // Value of foreground/background field +}; + +struct act33 {                                      // Type 33 - Init screen field of object +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	int      screenIndex;                           // Screen number +}; + +struct act34 {                                      // Type 34 - Global Schedule +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	uint16   actIndex;                              // Ptr to an action list +}; + +struct act35 {                                      // Type 35 - Remappe palette +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int16    oldColorIndex;                         // Old color index, 0..15 +	int16    newColorIndex;                         // New color index, 0..15 +}; + +struct act36 {                                      // Type 36 - Conditional on noun mentioned +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	uint16   nounIndex;                             // The required noun (list) +	uint16   actPassIndex;                          // Ptr to action list if success +	uint16   actFailIndex;                          // Ptr to action list if failure +}; + +struct act37 {                                      // Type 37 - Set new screen state +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      screenIndex;                           // The screen number +	byte     newState;                              // The new state +}; + +struct act38 {                                      // Type 38 - Position lips +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      lipsObjNumb;                           // The LIPS object +	int      objNumb;                               // The object to speak +	byte     dxLips;                                // Relative offset of x +	byte     dyLips;                                // Relative offset of y +}; + +struct act39 {                                      // Type 39 - Init story mode +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	bool     storyModeFl;                           // New state of story_mode flag +}; + +struct act40 {                                      // Type 40 - Unsolicited text box +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      stringIndex;                           // Index (enum) of string in strings.dat +}; + +struct act41 {                                      // Type 41 - Conditional on bonus scored +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      BonusIndex;                            // Index into bonus list +	uint16   actPassIndex;                          // Index of the action list if scored for the first time +	uint16   actFailIndex;                          // Index of the action list if already scored +}; + +struct act42 {                                      // Type 42 - Text box with "take" string +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object taken +}; + +struct act43 {                                      // Type 43 - Prompt user for Yes or No +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      promptIndex;                           // index of prompt string +	uint16   actYesIndex;                           // Ptr to action list if YES +	uint16   actNoIndex;                            // Ptr to action list if NO +}; + +struct act44 {                                      // Type 44 - Stop any route in progress +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +}; + +struct act45 {                                      // Type 45 - Conditional on route in progress +	action_t   actType;                             // The type of action +	int        timer;                               // Time to set off the action +	int        routeIndex;                          // Must be >= current status.rindex +	uint16     actPassIndex;                        // Ptr to action list if en-route +	uint16     actFailIndex;                        // Ptr to action list if not +}; + +struct act46 {                                      // Type 46 - Init status.jumpexit +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	bool     jumpExitFl;                            // New state of jumpexit flag +}; + +struct act47 {                                      // Type 47 - Init viewx,viewy,dir +	action_t actType;                               // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object +	int16    viewx;                                 // object.viewx +	int16    viewy;                                 // object.viewy +	int16    direction;                             // object.dir +}; + +struct act48 {                                      // Type 48 - Set curr_seq_p to frame n +	action_t actType;                                   // The type of action +	int      timer;                                 // Time to set off the action +	int      objNumb;                               // The object number +	int      seqIndex;                              // The index of seq array to set to +	int      frameIndex;                            // The index of frame to set to +}; + +struct act49 {                                      // Added by Strangerke - Type 79 - Play a sound (DOS way) +	action_t actType;                                   // The type of action +	int      timer;                                 // Time to set off the action +	uint16   soundIndex;                            // Sound index in string array +}; + +union act { +	act0     a0; +	act1     a1; +	act2     a2; +	act3     a3; +	act4     a4; +	act5     a5; +	act6     a6; +	act7     a7; +	act8     a8; +	act9     a9; +	act10    a10; +	act11    a11; +	act12    a12; +	act13    a13; +	act14    a14; +	act15    a15; +	act16    a16; +	act17    a17; +	act18    a18; +	act19    a19; +	act20    a20; +	act21    a21; +	act22    a22; +	act23    a23; +	act24    a24; +	act25    a25; +	act26    a26; +	act27    a27; +	act28    a28; +	act29    a29; +	act30    a30; +	act31    a31; +	act32    a32; +	act33    a33; +	act34    a34; +	act35    a35; +	act36    a36; +	act37    a37; +	act38    a38; +	act39    a39; +	act40    a40; +	act41    a41; +	act42    a42; +	act43    a43; +	act44    a44; +	act45    a45; +	act46    a46; +	act47    a47; +	act48    a48; +	act49    a49; +}; + +// The following determines how a verb is acted on, for an object +struct cmd { +	uint16 verbIndex;                               // the verb +	uint16 reqIndex;                                // ptr to list of required objects +	uint16 textDataNoCarryIndex;                    // ptr to string if any of above not carried +	byte   reqState;                                // required state for verb to be done +	byte   newState;                                // new states if verb done +	uint16 textDataWrongIndex;                      // ptr to string if wrong state +	uint16 textDataDoneIndex;                       // ptr to string if verb done +	uint16 actIndex;                                // Ptr to action list if verb done +}; + +// The following is a linked list of images in an animation sequence +// The image data is in 8-bit DIB format, i.e. 1 byte = 1 pixel +struct seq_t {                                      // Linked list of images +	byte   *imagePtr;                               // ptr to image +	uint16  bytesPerLine8;                          // bytes per line (8bits) +	uint16  lines;                                  // lines +	uint16  x1, x2, y1, y2;                         // Offsets from x,y: data bounding box +	seq_t  *nextSeqPtr;                             // ptr to next record +}; + +// The following is an array of structures of above sequences +struct seqList_t { +	uint16 imageNbr;                                // Number of images in sequence +	seq_t *seqPtr;                                  // Ptr to sequence structure +}; + +// Following is definition of object attributes +struct object_t { +	uint16     nounIndex;                           // String identifying object +	uint16     dataIndex;                           // String describing the object +	uint16     *stateDataIndex;                     // Added by Strangerke to handle the LOOK_S state-dependant descriptions +	path_t     pathType;                            // Describe path object follows +	int        vxPath, vyPath;                      // Delta velocities (e.g. for CHASE) +	uint16     actIndex;                            // Action list to do on collision with hero +	byte       seqNumb;                             // Number of sequences in list +	seq_t     *currImagePtr;                        // Sequence image currently in use +	seqList_t  seqList[MAX_SEQUENCES];              // Array of sequence structure ptrs and lengths +	cycle_t    cycling;                             // Whether cycling, forward or backward +	byte       cycleNumb;                           // No. of times to cycle +	byte       frameInterval;                       // Interval (in ticks) between frames +	byte       frameTimer;                          // Decrementing timer for above +	char       radius;                              // Defines sphere of influence by hero +	byte       screenIndex;                         // Screen in which object resides +	int        x, y;                                // Current coordinates of object +	int        oldx, oldy;                          // Previous coordinates of object +	char       vx, vy;                              // Velocity +	byte       objValue;                            // Value of object +	int        genericCmd;                          // Bit mask of 'generic' commands for object +	uint16     cmdIndex;                            // ptr to list of cmd structures for verbs +	bool       carriedFl;                           // TRUE if object being carried +	byte       state;                               // state referenced in cmd list +	bool       verbOnlyFl;                          // TRUE if verb-only cmds allowed e.g. sit,look +	byte       priority;                            // Whether object fore, background or floating +	int16      viewx, viewy;                        // Position to view object from (or 0 or -1) +	int16      direction;                           // Direction to view object from +	byte       curSeqNum;                           // Save which seq number currently in use +	byte       curImageNum;                         // Save which image of sequence currently in use +	char       oldvx;                               // Previous vx (used in wandering) +	char       oldvy;                               // Previous vy +}; + +// Following is structure of verbs and nouns for 'background' objects +//   These are objects that appear in the various screens, but nothing +//   interesting ever happens with them.  Rather than just be dumb and say +//   "don't understand" we produce an interesting msg to keep user sane. +struct background_t { +	uint16 verbIndex; +	uint16 nounIndex; +	int    commentIndex;                            // Index of comment produced on match +	bool   matchFl;                                 // TRUE if noun must match when present +	byte   roomState;                               // "State" of room. Comments might differ. +	byte   bonusIndex;                              // Index of bonus score (0 = no bonus) +}; + +typedef background_t *objectList_t; + +typedef byte overlay_t[OVL_SIZE];                   // Overlay file +typedef byte viewdib_t[(long)XPIX *YPIX];           // Viewport dib +typedef byte icondib_t[XPIX *INV_DY];               // Icon bar dib + +typedef char command_t[MAX_CHARS + 8];              // Command line (+spare for prompt,cursor) +typedef char fpath_t[MAX_FPATH];                    // File path + +// Structure to define an EXIT or other collision-activated hotspot +struct hotspot_t { +	int        screenIndex;                         // Screen in which hotspot appears +	int        x1, y1, x2, y2;                      // Bounding box of hotspot +	uint16     actIndex;                            // Actions to carry out if a 'hit' +	int16      viewx, viewy, direction;             // Used in auto-route mode +}; + +struct status_t {                                   // Game status (not saved) +	bool     initSaveFl;                            // Force save of initial game +	bool     storyModeFl;                           // Game is telling story - no commands +	bool     gameOverFl;                            // Game is over - hero knobbled +	bool     playbackFl;                            // Game is in playback mode +	bool     recordFl;                              // Game is in record mode +	bool     demoFl;                                // Game is in demo mode +	bool     debugFl;                               // Game is in debug mode +	bool     textBoxFl;                             // Game is (halted) in text box +//	Strangerke - Not used ? +//	bool     mmtimeFl;                               // Multimedia timer supported +	bool     lookFl;                                // Toolbar "look" button pressed +	bool     recallFl;                              // Toolbar "recall" button pressed +	bool     leftButtonFl;                          // Left mouse button pressed +	bool     rightButtonFl;                         // Right button pressed +	bool     newScreenFl;                           // New screen just loaded in dib_a +	bool     jumpExitFl;                            // Allowed to jump to a screen exit +	bool     godModeFl;                             // Allow DEBUG features in live version +	bool     helpFl;                                // Calling WinHelp (don't disable music) +	uint32   tick;                                  // Current time in ticks +	uint32   saveTick;                              // Time of last save in ticks +	vstate_t viewState;                             // View state machine +	istate_t inventoryState;                        // Inventory icon bar state +	int16    inventoryHeight;                       // Inventory icon bar height +	int16    inventoryObjId;                        // Inventory object selected, or -1 +	int16    routeIndex;                            // Index into route list, or -1 +	go_t     go_for;                                // Purpose of an automatic route +	int16    go_id;                                 // Index of exit of object walking to +	fpath_t  path;                                  // Alternate path for saved files +	long     saveSize;                              // Size of a saved game +	int16    saveSlot;                              // Current slot to save/restore game +	int16    screenWidth;                           // Desktop screen width +	int16    song;                                  // Current song +	int16    cx, cy;                                // Cursor position (dib coords) +}; + +struct config_t {                                   // User's config (saved) +	bool musicFl;                                   // State of Music button/menu item +	bool soundFl;                                   // State of Sound button/menu item +	bool turboFl;                                   // State of Turbo button/menu item +//	int16 wx, wy;                                    // Position of viewport +	int16 cx, cy;                                   // Size of viewport +	bool  backgroundMusicFl;                        // Continue music when task inactive +	byte  musicVolume;                              // Music volume percentage +	byte  soundVolume;                              // Sound volume percentage +	bool  playlist[MAX_TUNES];                      // Tune playlist +}; + +struct target_t {                                   // Secondary target for action +	uint16 nounIndex;                               // Secondary object +	uint16 verbIndex;                               // Action on secondary object +}; + +struct uses_t {                                     // Define uses of certain objects +	int16     objId;                                // Primary object +	uint16    dataIndex;                            // String if no secondary object matches +	target_t *targets;                              // List of secondary targets +}; + +// Global externs +extern config_t    _config;                         // User's config +extern maze_t      _maze;                           // Maze control structure +extern hugo_boot_t _boot;                           // Boot info structure +extern char        _textBoxBuffer[];                // Useful box text buffer +extern command_t   _line;                           // Line of user text input + +/* Structure of scenery file lookup entry */ +struct sceneBlock_t { +	uint32 scene_off; +	uint32 scene_len; +	uint32 b_off; +	uint32 b_len; +	uint32 o_off; +	uint32 o_len; +	uint32 ob_off; +	uint32 ob_len; +}; + +/* Structure of object file lookup entry */ +struct objBlock_t { +	uint32 objOffset; +	uint32 objLength; +}; + +#include "common/pack-start.h"						// START STRUCT PACKING +struct sound_hdr_t {								// Sound file lookup entry +	uint16 size;									// Size of sound data in bytes +	uint32 offset;									// Offset of sound data in file +} PACKED_STRUCT; +#include "common/pack-end.h"	// END STRUCT PACKING + +} + +#endif diff --git a/engines/hugo/global.h b/engines/hugo/global.h new file mode 100755 index 0000000000..fc39474ad6 --- /dev/null +++ b/engines/hugo/global.h @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +namespace Hugo { + +#define HERO 0 // In all enums, HERO is the first element + +#define DESCRIPLEN     32                           /* Length of description string */ +#define MAX_SOUNDS     64                           /* Max number of sounds */ +#define BOOTFILE       "HUGO.BSF"                   /* Name of boot structure file */ +#define CONFIGFILE     "CONFIG.DAT"                 /* Name of config file */ +#define LEN_MASK       0x3F                         /* Lower 6 bits are length */ +#define PBFILE         "playback.dat" + +/* Name scenery and objects picture databases */ +#define SCENERY_FILE   "scenery.dat" +#define OBJECTS_FILE   "objects.dat" +#define STRING_FILE    "strings.dat" +#define SOUND_FILE     "sounds.dat" + +/* User interface database (Windows Only) */ +#define UIF_FILE   "uif.dat" + +static const int kSavegameVersion = 1; +} // Namespace Hugo + diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp new file mode 100755 index 0000000000..2221ebd77b --- /dev/null +++ b/engines/hugo/hugo.cpp @@ -0,0 +1,1446 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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/system.h" +#include "common/events.h" +#include "common/debug-channels.h" + +#include "hugo/hugo.h" +#include "hugo/global.h" +#include "hugo/game.h" +#include "hugo/file.h" +#include "hugo/schedule.h" +#include "hugo/display.h" +#include "hugo/mouse.h" +#include "hugo/inventory.h" +#include "hugo/parser.h" +#include "hugo/route.h" +#include "hugo/sound.h" +#include "hugo/intro.h" + +#include "engines/util.h" + +namespace Hugo { + +HugoEngine *HugoEngine::s_Engine = NULL; + +overlay_t HugoEngine::_boundary; +overlay_t HugoEngine::_overlay; +overlay_t HugoEngine::_ovlBase; +overlay_t HugoEngine::_objBound; + +HugoEngine::HugoEngine(OSystem *syst, const HugoGameDescription *gd) : Engine(syst), _gameDescription(gd), _mouseX(0), _mouseY(0), +	_textData(0), _stringtData(0), _screenNames(0), _textEngine(0), _textIntro(0), _textMouse(0), _textParser(0), _textSchedule(0), _textUtil(0), +	_arrayNouns(0), _arrayVerbs(0), _arrayReqs(0), _hotspots(0), _invent(0), _uses(0), _catchallList(0), _backgroundObjects(0), +	_points(0), _cmdList(0), _screenActs(0), _objects(0), _actListArr(0), _heroImage(0), _defltTunes(0), _palette(0), _introX(0), +	_introY(0), _maxInvent(0), _numBonuses(0), _numScreens(0), _tunesNbr(0), _soundSilence(0), _soundTest(0), _screenStates(0), _numObj(0), +	_score(0), _maxscore(0) + +{ +	DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level"); +	DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level"); +	DebugMan.addDebugChannel(kDebugDisplay, "Display", "Display debug level"); +	DebugMan.addDebugChannel(kDebugMouse, "Mouse", "Mouse debug level"); +	DebugMan.addDebugChannel(kDebugParser, "Parser", "Parser debug level"); +	DebugMan.addDebugChannel(kDebugFile, "File", "File IO debug level"); +	DebugMan.addDebugChannel(kDebugRoute, "Route", "Route debug level"); +	DebugMan.addDebugChannel(kDebugInventory, "Inventory", "Inventory debug level"); + +	_fileManager = new FileManager(*this); +	_scheduler = new Scheduler(*this); +	_screen = new Screen(*this); +	_mouseHandler = new MouseHandler(*this); +	_inventoryHandler = new InventoryHandler(*this); +	_parser = new Parser(*this); +	_route = new Route(*this); +	_soundHandler = new SoundHandler(*this); +} + +HugoEngine::~HugoEngine() { +	delete _soundHandler; +	delete _route; +	delete _parser; +	delete _inventoryHandler; +	delete _mouseHandler; +	delete _screen; +	delete _scheduler; +	delete _fileManager; + +	free(_palette); +	free(_introX); +	free(_introY); + +#if 0 +	freeTexts(_textData); +	freeTexts(_stringtData); +	freeTexts(_textEngine); +	freeTexts(_textIntro); +	freeTexts(_textMouse); +	freeTexts(_textParser); +	freeTexts(_textSchedule); +	freeTexts(_textUtil); +#endif +	free(_textData); +	free(_stringtData); +	free(_screenNames); +	free(_textEngine); +	free(_textIntro); +	free(_textMouse); +	free(_textParser); +	free(_textSchedule); +	free(_textUtil); + +	warning("Missing: free _arrayNouns"); +	warning("Missing: free _arrayVerbs"); + +	free(_arrayReqs); +	free(_hotspots); +	free(_invent); +	free(_uses); +	free(_catchallList); + +	warning("Missing: free _background_objects"); + +	free(_points); + +	warning("Missing: free _cmdList"); +	warning("Missing: free _screenActs"); +	warning("Missing: free _objects"); + +	free(_defltTunes); +	free(_screenStates); +} + +GameType HugoEngine::getGameType() const { +	return _gameType; +} + +Common::Platform HugoEngine::getPlatform() const { +	return _platform; +} + +bool HugoEngine::isPacked() const { +	return _packedFl; +} + +Common::Error HugoEngine::run() { +	s_Engine = this; +	initGraphics(320, 200, false); + +	if (!loadHugoDat()) +		return Common::kUnknownError; + +	// Interesting situation: We have no cursor to show, since +	// the DOS version had none, and the Windows version just used +	// the windows default one. Meaning this call will just use whatever +	// was used last, i.e. the launcher GUI cursor. What to do? +	g_system->showMouse(true); + +	initStatus();                                   // Initialize game status +	initConfig(INSTALL);                            // Initialize user's config +	initialize(); +	initConfig(RESET);                              // Reset user's config + +	file().restoreGame(-1); + +	initMachine(); + +	// Start the state machine +	_status.viewState = V_INTROINIT; + +	bool doQuitFl = false; + +	while (!doQuitFl) { +		g_system->updateScreen(); +		runMachine(); +		// Handle input +		Common::Event event; +		while (_eventMan->pollEvent(event)) { +			switch (event.type) { +			case Common::EVENT_KEYDOWN: +				parser().keyHandler(event.kbd.keycode, 0); +				break; +			case Common::EVENT_MOUSEMOVE: +				_mouseX = event.mouse.x; +				_mouseY = event.mouse.y; +				break; +			case Common::EVENT_LBUTTONDOWN: +				_status.leftButtonFl = true; +				break; +			case Common::EVENT_LBUTTONUP: +				_status.leftButtonFl = false; +				break; +			case Common::EVENT_RBUTTONDOWN: +				_status.rightButtonFl = true; +				break; +			case Common::EVENT_RBUTTONUP: +				_status.rightButtonFl = false; +				break; +			case Common::EVENT_QUIT: +				doQuitFl = true; +				break; +			default: +				break; +			} +		} +	} +	return Common::kNoError; +} + +void HugoEngine::initMachine() { +	file().readBackground(_numScreens - 1);         // Splash screen +	readObjectImages();                             // Read all object images +	if (_platform == Common::kPlatformWindows) +		readUIFImages();                            // Read all uif images (only in Win versions) +} + +void HugoEngine::runMachine() { +// Hugo game state machine - called during onIdle +	static uint32 lastTime; + +	status_t &gameStatus = getGameStatus(); + +	// Don't process if we're in a textbox +	if (gameStatus.textBoxFl) +		return; + +	// Don't process if gameover +	if (gameStatus.gameOverFl) +		return; + +	// Process machine once every tick +	if (g_system->getMillis() - lastTime < (uint32)(1000 / TPS)) +		return; +	lastTime = g_system->getMillis(); + +	switch (gameStatus.viewState) { +	case V_IDLE:                                    // Not processing state machine +		intro().preNewGame();                           // Any processing before New Game selected +		break; +	case V_INTROINIT:                               // Initialization before intro begins +		intro().introInit(); +		gameStatus.viewState = V_INTRO; +		break; +	case V_INTRO:                                   // Do any game-dependant preamble +		if (intro().introPlay())    {               // Process intro screen +			scheduler().newScreen(0);               // Initialize first screen +			gameStatus.viewState = V_PLAY; +		} +		break; +	case V_PLAY:                                    // Playing game +		parser().charHandler();                     // Process user cmd input +		moveObjects();                              // Process object movement +		scheduler().runScheduler();                 // Process any actions +		screen().displayList(D_RESTORE);            // Restore previous background +		updateImages();                             // Draw into _frontBuffer, compile display list +		mouse().mouseHandler();                     // Mouse activity - adds to display list +		parser().drawStatusText(); +		screen().displayList(D_DISPLAY);            // Blit the display list to screen +		break; +	case V_INVENT:                                  // Accessing inventory +		inventory().runInventory();                 // Process Inventory state machine +		break; +	case V_EXIT:                                    // Game over or user exited +		gameStatus.viewState = V_IDLE; +		break; +	} +} + +bool HugoEngine::loadHugoDat() { +	Common::File in; +	int numElem, numSubElem, numSubAct; +	char buf[256]; +	int majVer, minVer; + +	in.open("hugo.dat"); + +	if (!in.isOpen()) { +		Common::String errorMessage = "You're missing the 'hugo.dat' file. Get it from the ScummVM website"; +		GUIErrorMessage(errorMessage); +		warning("%s", errorMessage.c_str()); +		return false; +	} + +	// Read header +	in.read(buf, 4); +	buf[4] = '\0'; + +	if (strcmp(buf, "HUGO")) { +		Common::String errorMessage = "File 'hugo.dat' is corrupt. Get it from the ScummVM website"; +		GUIErrorMessage(errorMessage); +		warning("%s", errorMessage.c_str()); +		return false; +	} + +	majVer = in.readByte(); +	minVer = in.readByte(); + +	if ((majVer != HUGO_DAT_VER_MAJ) || (minVer != HUGO_DAT_VER_MIN)) { +		snprintf(buf, 256, "File 'hugo.dat' is wrong version. Expected %d.%d but got %d.%d. Get it from the ScummVM website", HUGO_DAT_VER_MAJ, HUGO_DAT_VER_MIN, majVer, minVer); +		GUIErrorMessage(buf); +		warning("%s", buf); + +		return false; +	} + +	_numVariant = in.readUint16BE(); + +	// Read textData +	_textData = loadTextsVariante(in, 0); + +	// Read stringtData +	// Only Hugo 1 DOS should use this array +	_stringtData = loadTextsVariante(in, 0); + +	// Read arrayNouns +	_arrayNouns = loadTextsArray(in); + +	// Read arrayVerbs +	_arrayVerbs = loadTextsArray(in); + +	// Read screenNames +	_screenNames = loadTextsVariante(in, &_numScreens); + +	// Read palette +	_paletteSize = in.readUint16BE(); +	_palette = (byte *)malloc(sizeof(byte) * _paletteSize); +	for (int i = 0; i < _paletteSize; i++) { +		_palette[i] = in.readByte(); +	} + +	// Read textEngine +	_textEngine = loadTexts(in); + +	// Read textIntro +	_textIntro = loadTexts(in); + +	// Read x_intro and y_intro +	_introXSize = in.readUint16BE(); +	_introX = (byte *)malloc(sizeof(byte) * _introXSize); +	_introY = (byte *)malloc(sizeof(byte) * _introXSize); +	for (int i = 0; i < _introXSize; i++) { +		_introX[i] = in.readByte(); +		_introY[i] = in.readByte(); +	} + +	// Read textMouse +	_textMouse = loadTexts(in); + +	// Read textParser +	_textParser = loadTexts(in); + +	// Read textSchedule +	_textSchedule = loadTexts(in); + +	// Read textUtil +	_textUtil = loadTexts(in); + +	// Read _arrayReqs +	_arrayReqs = loadLongArray(in); + +	// Read _hotspots +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		int numRows = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_hotspots = (hotspot_t *)malloc(sizeof(hotspot_t) * numRows); +			for (int i = 0; i < numRows; i++) { +				_hotspots[i].screenIndex = in.readSint16BE(); +				_hotspots[i].x1 = in.readSint16BE(); +				_hotspots[i].y1 = in.readSint16BE(); +				_hotspots[i].x2 = in.readSint16BE(); +				_hotspots[i].y2 = in.readSint16BE(); +				_hotspots[i].actIndex = in.readUint16BE(); +				_hotspots[i].viewx = in.readSint16BE(); +				_hotspots[i].viewy = in.readSint16BE(); +				_hotspots[i].direction = in.readSint16BE(); +			} +		} else { +			for (int i = 0; i < numRows; i++) { +				in.readSint16BE(); +				in.readSint16BE(); +				in.readSint16BE(); +				in.readSint16BE(); +				in.readSint16BE(); +				in.readUint16BE(); +				in.readSint16BE(); +				in.readSint16BE(); +				in.readSint16BE(); +			} +		} +	} + +	//Read _invent +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_maxInvent = numElem; +			_invent = (int16 *)malloc(sizeof(int16) * numElem); +			for (int i = 0; i < numElem; i++) +				_invent[i] = in.readSint16BE(); +		} else { +			for (int i = 0; i < numElem; i++) +				in.readSint16BE(); +		} +	} + +	//Read _uses +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_uses = (uses_t *)malloc(sizeof(uses_t) * numElem); +			for (int i = 0; i < numElem; i++) { +				_uses[i].objId = in.readSint16BE(); +				_uses[i].dataIndex = in.readUint16BE(); +				numSubElem = in.readUint16BE(); +				_uses[i].targets = (target_t *)malloc(sizeof(target_t) * numSubElem); +				for (int j = 0; j < numSubElem; j++) { +					_uses[i].targets[j].nounIndex = in.readUint16BE(); +					_uses[i].targets[j].verbIndex = in.readUint16BE(); +				} +			} +		} else { +			for (int i = 0; i < numElem; i++) { +				in.readSint16BE(); +				in.readUint16BE(); +				numSubElem = in.readUint16BE(); +				for (int j = 0; j < numSubElem; j++) { +					in.readUint16BE(); +					in.readUint16BE(); +				} +			} +		} +	} + +	//Read _catchallList +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_catchallList = (background_t *)malloc(sizeof(background_t) * numElem); +			for (int i = 0; i < numElem; i++) { +				_catchallList[i].verbIndex = in.readUint16BE(); +				_catchallList[i].nounIndex = in.readUint16BE(); +				_catchallList[i].commentIndex = in.readSint16BE(); +				_catchallList[i].matchFl = (in.readByte() != 0); +				_catchallList[i].roomState = in.readByte(); +				_catchallList[i].bonusIndex = in.readByte(); +			} +		} else { +			for (int i = 0; i < numElem; i++) { +				in.readUint16BE(); +				in.readUint16BE(); +				in.readSint16BE(); +				in.readByte(); +				in.readByte(); +				in.readByte(); +			} +		} +	} + +// Read _background_objects +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_backgroundObjects = (background_t **)malloc(sizeof(background_t *) * numElem); +			for (int i = 0; i < numElem; i++) { +				numSubElem = in.readUint16BE(); +				_backgroundObjects[i] = (background_t *)malloc(sizeof(background_t) * numSubElem); +				for (int j = 0; j < numSubElem; j++) { +					_backgroundObjects[i][j].verbIndex = in.readUint16BE(); +					_backgroundObjects[i][j].nounIndex = in.readUint16BE(); +					_backgroundObjects[i][j].commentIndex = in.readSint16BE(); +					_backgroundObjects[i][j].matchFl = (in.readByte() != 0); +					_backgroundObjects[i][j].roomState = in.readByte(); +					_backgroundObjects[i][j].bonusIndex = in.readByte(); +				} +			} +		} else { +			for (int i = 0; i < numElem; i++) { +				numSubElem = in.readUint16BE(); +				for (int j = 0; j < numSubElem; j++) { +					in.readUint16BE();; +					in.readUint16BE();; +					in.readSint16BE();; +					in.readByte(); +					in.readByte(); +					in.readByte(); +				} +			} +		} +	} + +	// Read _points +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_numBonuses = numElem; +			_points = (point_t *)malloc(sizeof(point_t) * _numBonuses); +			for (int i = 0; i < _numBonuses; i++) { +				_points[i].score = in.readByte(); +				_points[i].scoredFl = false; +			} +		} else { +			for (int i = 0; i < numElem; i++) +				in.readByte(); +		} +	} + +	// Read _cmdList +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_cmdList = (cmd **)malloc(sizeof(cmd *) * numElem); +			for (int i = 0; i < numElem; i++) { +				numSubElem = in.readUint16BE(); +				_cmdList[i] = (cmd *)malloc(sizeof(cmd) * numSubElem); +				for (int j = 0; j < numSubElem; j++) { +					_cmdList[i][j].verbIndex = in.readUint16BE(); +					_cmdList[i][j].reqIndex = in.readUint16BE(); +					_cmdList[i][j].textDataNoCarryIndex = in.readUint16BE(); +					_cmdList[i][j].reqState = in.readByte(); +					_cmdList[i][j].newState = in.readByte(); +					_cmdList[i][j].textDataWrongIndex = in.readUint16BE(); +					_cmdList[i][j].textDataDoneIndex = in.readUint16BE(); +					_cmdList[i][j].actIndex = in.readUint16BE(); +				} +			} +		} else { +			for (int i = 0; i < numElem; i++) { +				numSubElem = in.readUint16BE(); +				for (int j = 0; j < numSubElem; j++) { +					in.readUint16BE(); +					in.readUint16BE(); +					in.readUint16BE(); +					in.readByte(); +					in.readByte(); +					in.readUint16BE(); +					in.readUint16BE(); +					in.readUint16BE(); +				} +			} +		} +	} + +// TODO: For Hugo2 and Hugo3, if not in story mode, increment _screenActs[0][0] (ex: kALcrashStory + 1 == kALcrashNoStory) +	// Read _screenActs +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_screenActs = (uint16 **)malloc(sizeof(uint16 *) * numElem); +			for (int i = 0; i < numElem; i++) { +				numSubElem = in.readUint16BE(); +				if (numSubElem == 0) +					_screenActs[i] = 0; +				else { +					_screenActs[i] = (uint16 *)malloc(sizeof(uint16) * numSubElem); +					for (int j = 0; j < numSubElem; j++) +						_screenActs[i][j] = in.readUint16BE(); +				} +			} +		} else { +			for (int i = 0; i < numElem; i++) { +				numSubElem = in.readUint16BE(); +				for (int j = 0; j < numSubElem; j++) +					in.readUint16BE(); +			} +		} + +	} + +// TODO: For Hugo3, if not in story mode, set _objects[2].state to 3 +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_objects = (object_t *)malloc(sizeof(object_t) * numElem); +			for (int i = 0; i < numElem; i++) { +				_objects[i].nounIndex = in.readUint16BE(); +				_objects[i].dataIndex = in.readUint16BE(); +				numSubElem = in.readUint16BE(); +				if (numSubElem == 0) +					_objects[i].stateDataIndex = 0; +				else +					_objects[i].stateDataIndex = (uint16 *)malloc(sizeof(uint16) * numSubElem); +				for (int j = 0; j < numSubElem; j++) +					_objects[i].stateDataIndex[j] = in.readUint16BE(); +				_objects[i].pathType = (path_t) in.readSint16BE(); +				_objects[i].vxPath = in.readSint16BE(); +				_objects[i].vyPath = in.readSint16BE(); +				_objects[i].actIndex = in.readUint16BE(); +				_objects[i].seqNumb = in.readByte(); +				_objects[i].currImagePtr = 0; +				if (_objects[i].seqNumb == 0) { +					_objects[i].seqList[0].imageNbr = 0; +					_objects[i].seqList[0].seqPtr = 0; +				} +				for (int j = 0; j < _objects[i].seqNumb; j++) { +					_objects[i].seqList[j].imageNbr = in.readUint16BE(); +					_objects[i].seqList[j].seqPtr = 0; +				} +				_objects[i].cycling = (cycle_t)in.readByte(); +				_objects[i].cycleNumb = in.readByte(); +				_objects[i].frameInterval = in.readByte(); +				_objects[i].frameTimer = in.readByte(); +				_objects[i].radius = in.readByte(); +				_objects[i].screenIndex = in.readByte(); +				_objects[i].x = in.readSint16BE(); +				_objects[i].y = in.readSint16BE(); +				_objects[i].oldx = in.readSint16BE(); +				_objects[i].oldy = in.readSint16BE(); +				_objects[i].vx = in.readByte(); +				_objects[i].vy = in.readByte(); +				_objects[i].objValue = in.readByte(); +				_objects[i].genericCmd = in.readSint16BE(); +				_objects[i].cmdIndex = in.readUint16BE(); +				_objects[i].carriedFl = (in.readByte() != 0); +				_objects[i].state = in.readByte(); +				_objects[i].verbOnlyFl = (in.readByte() != 0); +				_objects[i].priority = in.readByte(); +				_objects[i].viewx = in.readSint16BE(); +				_objects[i].viewy = in.readSint16BE(); +				_objects[i].direction = in.readSint16BE(); +				_objects[i].curSeqNum = in.readByte(); +				_objects[i].curImageNum = in.readByte(); +				_objects[i].oldvx = in.readByte(); +				_objects[i].oldvy = in.readByte(); +			} +		} else { +			for (int i = 0; i < numElem; i++) { +				in.readUint16BE(); +				in.readUint16BE(); +				numSubElem = in.readUint16BE(); +				for (int j = 0; j < numSubElem; j++) +					in.readUint16BE(); +				in.readSint16BE(); +				in.readSint16BE(); +				in.readSint16BE(); +				in.readUint16BE(); +				numSubElem = in.readByte(); +				for (int j = 0; j < numSubElem; j++) +					in.readUint16BE(); +				in.readByte(); +				in.readByte(); +				in.readByte(); +				in.readByte(); +				in.readByte(); +				in.readByte(); +				in.readSint16BE(); +				in.readSint16BE(); +				in.readSint16BE(); +				in.readSint16BE(); +				in.readByte(); +				in.readByte(); +				in.readByte(); +				in.readSint16BE(); +				in.readUint16BE(); +				in.readByte(); +				in.readByte(); +				in.readByte(); +				in.readByte(); +				in.readSint16BE(); +				in.readSint16BE(); +				in.readUint16BE(); +				in.readByte(); +				in.readByte(); +				in.readByte(); +				in.readByte(); +			} +		} +	} +//#define HERO 0 +	_hero = &_objects[HERO];                    // This always points to hero +	_screen_p = &(_objects[HERO].screenIndex);  // Current screen is hero's +	_heroImage = HERO;                          // Current in use hero image + +//read _actListArr +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_actListArr = (act **)malloc(sizeof(act *) * numElem); +			for (int i = 0; i < numElem; i++) { +				numSubElem = in.readUint16BE(); +				_actListArr[i] = (act *) malloc(sizeof(act) * (numSubElem + 1)); +				for (int j = 0; j < numSubElem; j++) { +					_actListArr[i][j].a0.actType = (action_t) in.readByte(); +					switch (_actListArr[i][j].a0.actType) { +					case ANULL:              // -1 +						break; +					case ASCHEDULE:          // 0 +						_actListArr[i][j].a0.timer = in.readSint16BE(); +						_actListArr[i][j].a0.actIndex = in.readUint16BE(); +						break; +					case START_OBJ:          // 1 +						_actListArr[i][j].a1.timer = in.readSint16BE(); +						_actListArr[i][j].a1.objNumb = in.readSint16BE(); +						_actListArr[i][j].a1.cycleNumb = in.readSint16BE(); +						_actListArr[i][j].a1.cycle = (cycle_t) in.readByte(); +						break; +					case INIT_OBJXY:         // 2 +						_actListArr[i][j].a2.timer = in.readSint16BE(); +						_actListArr[i][j].a2.objNumb = in.readSint16BE(); +						_actListArr[i][j].a2.x = in.readSint16BE(); +						_actListArr[i][j].a2.y = in.readSint16BE(); +						break; +					case PROMPT:             // 3 +						_actListArr[i][j].a3.timer = in.readSint16BE(); +						_actListArr[i][j].a3.promptIndex = in.readSint16BE(); +						numSubAct = in.readUint16BE(); +						_actListArr[i][j].a3.responsePtr = (int *) malloc(sizeof(int) * numSubAct); +						for (int k = 0; k < numSubAct; k++) +							_actListArr[i][j].a3.responsePtr[k] = in.readSint16BE(); +						_actListArr[i][j].a3.actPassIndex = in.readUint16BE(); +						_actListArr[i][j].a3.actFailIndex = in.readUint16BE(); +						_actListArr[i][j].a3.encodedFl = (in.readByte() == 1) ? true : false; +						break; +					case BKGD_COLOR:         // 4 +						_actListArr[i][j].a4.timer = in.readSint16BE(); +						_actListArr[i][j].a4.newBackgroundColor = in.readUint32BE(); +						break; +					case INIT_OBJVXY:        // 5 +						_actListArr[i][j].a5.timer = in.readSint16BE(); +						_actListArr[i][j].a5.objNumb = in.readSint16BE(); +						_actListArr[i][j].a5.vx = in.readSint16BE(); +						_actListArr[i][j].a5.vy = in.readSint16BE(); +						break; +					case INIT_CARRY:         // 6 +						_actListArr[i][j].a6.timer = in.readSint16BE(); +						_actListArr[i][j].a6.objNumb = in.readSint16BE(); +						_actListArr[i][j].a6.carriedFl = (in.readByte() == 1) ? true : false; +						break; +					case INIT_HF_COORD:      // 7 +						_actListArr[i][j].a7.timer = in.readSint16BE(); +						_actListArr[i][j].a7.objNumb = in.readSint16BE(); +						break; +					case NEW_SCREEN:         // 8 +						_actListArr[i][j].a8.timer = in.readSint16BE(); +						_actListArr[i][j].a8.screenIndex = in.readSint16BE(); +						break; +					case INIT_OBJSTATE:      // 9 +						_actListArr[i][j].a9.timer = in.readSint16BE(); +						_actListArr[i][j].a9.objNumb = in.readSint16BE(); +						_actListArr[i][j].a9.newState = in.readByte(); +						break; +					case INIT_PATH:          // 10 +						_actListArr[i][j].a10.timer = in.readSint16BE(); +						_actListArr[i][j].a10.objNumb = in.readSint16BE(); +						_actListArr[i][j].a10.newPathType = in.readSint16BE(); +						_actListArr[i][j].a10.vxPath = in.readByte(); +						_actListArr[i][j].a10.vyPath = in.readByte(); +						break; +					case COND_R:             // 11 +						_actListArr[i][j].a11.timer = in.readSint16BE(); +						_actListArr[i][j].a11.objNumb = in.readSint16BE(); +						_actListArr[i][j].a11.stateReq = in.readByte(); +						_actListArr[i][j].a11.actPassIndex = in.readUint16BE(); +						_actListArr[i][j].a11.actFailIndex = in.readUint16BE(); +						break; +					case TEXT:               // 12 +						_actListArr[i][j].a12.timer = in.readSint16BE(); +						_actListArr[i][j].a12.stringIndex = in.readSint16BE(); +						break; +					case SWAP_IMAGES:        // 13 +						_actListArr[i][j].a13.timer = in.readSint16BE(); +						_actListArr[i][j].a13.obj1 = in.readSint16BE(); +						_actListArr[i][j].a13.obj2 = in.readSint16BE(); +						break; +					case COND_SCR:           // 14 +						_actListArr[i][j].a14.timer = in.readSint16BE(); +						_actListArr[i][j].a14.objNumb = in.readSint16BE(); +						_actListArr[i][j].a14.screenReq = in.readSint16BE(); +						_actListArr[i][j].a14.actPassIndex = in.readUint16BE(); +						_actListArr[i][j].a14.actFailIndex = in.readUint16BE(); +						break; +					case AUTOPILOT:          // 15 +						_actListArr[i][j].a15.timer = in.readSint16BE(); +						_actListArr[i][j].a15.obj1 = in.readSint16BE(); +						_actListArr[i][j].a15.obj2 = in.readSint16BE(); +						_actListArr[i][j].a15.dx = in.readByte(); +						_actListArr[i][j].a15.dy = in.readByte(); +						break; +					case INIT_OBJ_SEQ:       // 16 +						_actListArr[i][j].a16.timer = in.readSint16BE(); +						_actListArr[i][j].a16.objNumb = in.readSint16BE(); +						_actListArr[i][j].a16.seqIndex = in.readSint16BE(); +						break; +					case SET_STATE_BITS:     // 17 +						_actListArr[i][j].a17.timer = in.readSint16BE(); +						_actListArr[i][j].a17.objNumb = in.readSint16BE(); +						_actListArr[i][j].a17.stateMask = in.readSint16BE(); +						break; +					case CLEAR_STATE_BITS:   // 18 +						_actListArr[i][j].a18.timer = in.readSint16BE(); +						_actListArr[i][j].a18.objNumb = in.readSint16BE(); +						_actListArr[i][j].a18.stateMask = in.readSint16BE(); +						break; +					case TEST_STATE_BITS:    // 19 +						_actListArr[i][j].a19.timer = in.readSint16BE(); +						_actListArr[i][j].a19.objNumb = in.readSint16BE(); +						_actListArr[i][j].a19.stateMask = in.readSint16BE(); +						_actListArr[i][j].a19.actPassIndex = in.readUint16BE(); +						_actListArr[i][j].a19.actFailIndex = in.readUint16BE(); +						break; +					case DEL_EVENTS:         // 20 +						_actListArr[i][j].a20.timer = in.readSint16BE(); +						_actListArr[i][j].a20.actTypeDel = (action_t) in.readByte(); +						break; +					case GAMEOVER:           // 21 +						_actListArr[i][j].a21.timer = in.readSint16BE(); +						break; +					case INIT_HH_COORD:      // 22 +						_actListArr[i][j].a22.timer = in.readSint16BE(); +						_actListArr[i][j].a22.objNumb = in.readSint16BE(); +						break; +					case EXIT:               // 23 +						_actListArr[i][j].a23.timer = in.readSint16BE(); +						break; +					case BONUS:              // 24 +						_actListArr[i][j].a24.timer = in.readSint16BE(); +						_actListArr[i][j].a24.pointIndex = in.readSint16BE(); +						break; +					case COND_BOX:           // 25 +						_actListArr[i][j].a25.timer = in.readSint16BE(); +						_actListArr[i][j].a25.objNumb = in.readSint16BE(); +						_actListArr[i][j].a25.x1 = in.readSint16BE(); +						_actListArr[i][j].a25.y1 = in.readSint16BE(); +						_actListArr[i][j].a25.x2 = in.readSint16BE(); +						_actListArr[i][j].a25.y2 = in.readSint16BE(); +						_actListArr[i][j].a25.actPassIndex = in.readUint16BE(); +						_actListArr[i][j].a25.actFailIndex = in.readUint16BE(); +						break; +					case SOUND:              // 26 +						_actListArr[i][j].a26.timer = in.readSint16BE(); +						_actListArr[i][j].a26.soundIndex = in.readSint16BE(); +						break; +					case ADD_SCORE:          // 27 +						_actListArr[i][j].a27.timer = in.readSint16BE(); +						_actListArr[i][j].a27.objNumb = in.readSint16BE(); +						break; +					case SUB_SCORE:          // 28 +						_actListArr[i][j].a28.timer = in.readSint16BE(); +						_actListArr[i][j].a28.objNumb = in.readSint16BE(); +						break; +					case COND_CARRY:         // 29 +						_actListArr[i][j].a29.timer = in.readSint16BE(); +						_actListArr[i][j].a29.objNumb = in.readSint16BE(); +						_actListArr[i][j].a29.actPassIndex = in.readUint16BE(); +						_actListArr[i][j].a29.actFailIndex = in.readUint16BE(); +						break; +					case INIT_MAZE:          // 30 +						_actListArr[i][j].a30.timer = in.readSint16BE(); +						_actListArr[i][j].a30.mazeSize = in.readByte(); +						_actListArr[i][j].a30.y1 = in.readSint16BE(); +						_actListArr[i][j].a30.x2 = in.readSint16BE(); +						_actListArr[i][j].a30.y2 = in.readSint16BE(); +						_actListArr[i][j].a30.x3 = in.readSint16BE(); +						_actListArr[i][j].a30.x4 = in.readSint16BE(); +						_actListArr[i][j].a30.firstScreenIndex = in.readByte(); +						break; +					case EXIT_MAZE:          // 31 +						_actListArr[i][j].a31.timer = in.readSint16BE(); +						break; +					case INIT_PRIORITY:      // 32 +						_actListArr[i][j].a32.timer = in.readSint16BE(); +						_actListArr[i][j].a32.objNumb = in.readSint16BE(); +						_actListArr[i][j].a32.priority = in.readByte(); +						break; +					case INIT_SCREEN:        // 33 +						_actListArr[i][j].a33.timer = in.readSint16BE(); +						_actListArr[i][j].a33.objNumb = in.readSint16BE(); +						_actListArr[i][j].a33.screenIndex = in.readSint16BE(); +						break; +					case AGSCHEDULE:         // 34 +						_actListArr[i][j].a34.timer = in.readSint16BE(); +						_actListArr[i][j].a34.actIndex = in.readUint16BE(); +						break; +					case REMAPPAL:           // 35 +						_actListArr[i][j].a35.timer = in.readSint16BE(); +						_actListArr[i][j].a35.oldColorIndex = in.readSint16BE(); +						_actListArr[i][j].a35.newColorIndex = in.readSint16BE(); +						break; +					case COND_NOUN:          // 36 +						_actListArr[i][j].a36.timer = in.readSint16BE(); +						_actListArr[i][j].a36.nounIndex = in.readUint16BE(); +						_actListArr[i][j].a36.actPassIndex = in.readUint16BE(); +						_actListArr[i][j].a36.actFailIndex = in.readUint16BE(); +						break; +					case SCREEN_STATE:       // 37 +						_actListArr[i][j].a37.timer = in.readSint16BE(); +						_actListArr[i][j].a37.screenIndex = in.readSint16BE(); +						_actListArr[i][j].a37.newState = in.readByte(); +						break; +					case INIT_LIPS:          // 38 +						_actListArr[i][j].a38.timer = in.readSint16BE(); +						_actListArr[i][j].a38.lipsObjNumb = in.readSint16BE(); +						_actListArr[i][j].a38.objNumb = in.readSint16BE(); +						_actListArr[i][j].a38.dxLips = in.readByte(); +						_actListArr[i][j].a38.dyLips = in.readByte(); +						break; +					case INIT_STORY_MODE:    // 39 +						_actListArr[i][j].a39.timer = in.readSint16BE(); +						_actListArr[i][j].a39.storyModeFl = (in.readByte() == 1); +						break; +					case WARN:               // 40 +						_actListArr[i][j].a40.timer = in.readSint16BE(); +						_actListArr[i][j].a40.stringIndex = in.readSint16BE(); +						break; +					case COND_BONUS:         // 41 +						_actListArr[i][j].a41.timer = in.readSint16BE(); +						_actListArr[i][j].a41.BonusIndex = in.readSint16BE(); +						_actListArr[i][j].a41.actPassIndex = in.readUint16BE(); +						_actListArr[i][j].a41.actFailIndex = in.readUint16BE(); +						break; +					case TEXT_TAKE:          // 42 +						_actListArr[i][j].a42.timer = in.readSint16BE(); +						_actListArr[i][j].a42.objNumb = in.readSint16BE(); +						break; +					case YESNO:              // 43 +						_actListArr[i][j].a43.timer = in.readSint16BE(); +						_actListArr[i][j].a43.promptIndex = in.readSint16BE(); +						_actListArr[i][j].a43.actYesIndex = in.readUint16BE(); +						_actListArr[i][j].a43.actNoIndex = in.readUint16BE(); +						break; +					case STOP_ROUTE:         // 44 +						_actListArr[i][j].a44.timer = in.readSint16BE(); +						break; +					case COND_ROUTE:         // 45 +						_actListArr[i][j].a45.timer = in.readSint16BE(); +						_actListArr[i][j].a45.routeIndex = in.readSint16BE(); +						_actListArr[i][j].a45.actPassIndex = in.readUint16BE(); +						_actListArr[i][j].a45.actFailIndex = in.readUint16BE(); +						break; +					case INIT_JUMPEXIT:      // 46 +						_actListArr[i][j].a46.timer = in.readSint16BE(); +						_actListArr[i][j].a46.jumpExitFl = (in.readByte() == 1); +						break; +					case INIT_VIEW:          // 47 +						_actListArr[i][j].a47.timer = in.readSint16BE(); +						_actListArr[i][j].a47.objNumb = in.readSint16BE(); +						_actListArr[i][j].a47.viewx = in.readSint16BE(); +						_actListArr[i][j].a47.viewy = in.readSint16BE(); +						_actListArr[i][j].a47.direction = in.readSint16BE(); +						break; +					case INIT_OBJ_FRAME:     // 48 +						_actListArr[i][j].a48.timer = in.readSint16BE(); +						_actListArr[i][j].a48.objNumb = in.readSint16BE(); +						_actListArr[i][j].a48.seqIndex = in.readSint16BE(); +						_actListArr[i][j].a48.frameIndex = in.readSint16BE(); +						break; +					case OLD_SONG:           //49 +						_actListArr[i][j].a49.timer = in.readSint16BE(); +						_actListArr[i][j].a49.soundIndex = in.readUint16BE(); +						break; +					default: +						error("Engine - Unknown action type encountered: %d", _actListArr[i][j].a0.actType); +					} +				} +				_actListArr[i][numSubElem].a0.actType = ANULL; +			} +		} else { +			for (int i = 0; i < numElem; i++) { +				numSubElem = in.readUint16BE(); +				for (int j = 0; j < numSubElem; j++) { +					numSubAct = in.readByte(); +					switch (numSubAct) { +					case ANULL:              // -1 +						break; +					case ASCHEDULE:          // 0 +						in.readSint16BE(); +						in.readUint16BE(); +						break; +					case START_OBJ:          // 1 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readByte(); +						break; +					case INIT_OBJXY:         // 2 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case PROMPT:             // 3 +						in.readSint16BE(); +						in.readSint16BE(); +						numSubAct = in.readUint16BE(); +						for (int k = 0; k < numSubAct; k++) +							in.readSint16BE(); +						in.readUint16BE(); +						in.readUint16BE(); +						in.readByte(); +						break; +					case BKGD_COLOR:         // 4 +						in.readSint16BE(); +						in.readUint32BE(); +						break; +					case INIT_OBJVXY:        // 5 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case INIT_CARRY:         // 6 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readByte(); +						break; +					case INIT_HF_COORD:      // 7 +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case NEW_SCREEN:         // 8 +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case INIT_OBJSTATE:      // 9 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readByte(); +						break; +					case INIT_PATH:          // 10 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readByte(); +						in.readByte(); +						break; +					case COND_R:             // 11 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readByte(); +						in.readUint16BE(); +						in.readUint16BE(); +						break; +					case TEXT:               // 12 +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case SWAP_IMAGES:        // 13 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case COND_SCR:           // 14 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readUint16BE(); +						in.readUint16BE(); +						break; +					case AUTOPILOT:          // 15 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readByte(); +						in.readByte(); +						break; +					case INIT_OBJ_SEQ:       // 16 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case SET_STATE_BITS:     // 17 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case CLEAR_STATE_BITS:   // 18 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case TEST_STATE_BITS:    // 19 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readUint16BE(); +						in.readUint16BE(); +						break; +					case DEL_EVENTS:         // 20 +						in.readSint16BE(); +						in.readByte(); +						break; +					case GAMEOVER:           // 21 +						in.readSint16BE(); +						break; +					case INIT_HH_COORD:      // 22 +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case EXIT:               // 23 +						in.readSint16BE(); +						break; +					case BONUS:              // 24 +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case COND_BOX:           // 25 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readUint16BE(); +						in.readUint16BE(); +						break; +					case SOUND:              // 26 +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case ADD_SCORE:          // 27 +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case SUB_SCORE:          // 28 +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case COND_CARRY:         // 29 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readUint16BE(); +						in.readUint16BE(); +						break; +					case INIT_MAZE:          // 30 +						in.readSint16BE(); +						in.readByte(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readByte(); +						break; +					case EXIT_MAZE:          // 31 +						in.readSint16BE(); +						break; +					case INIT_PRIORITY:      // 32 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readByte(); +						break; +					case INIT_SCREEN:        // 33 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case AGSCHEDULE:         // 34 +						in.readSint16BE(); +						in.readUint16BE(); +						break; +					case REMAPPAL:           // 35 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case COND_NOUN:          // 36 +						in.readSint16BE(); +						in.readUint16BE(); +						in.readUint16BE(); +						in.readUint16BE(); +						break; +					case SCREEN_STATE:       // 37 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readByte(); +						break; +					case INIT_LIPS:          // 38 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readByte(); +						in.readByte(); +						break; +					case INIT_STORY_MODE:    // 39 +						in.readSint16BE(); +						in.readByte(); +						break; +					case WARN:               // 40 +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case COND_BONUS:         // 41 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readUint16BE(); +						in.readUint16BE(); +						break; +					case TEXT_TAKE:          // 42 +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case YESNO:              // 43 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readUint16BE(); +						in.readUint16BE(); +						break; +					case STOP_ROUTE:         // 44 +						in.readSint16BE(); +						break; +					case COND_ROUTE:         // 45 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readUint16BE(); +						in.readUint16BE(); +						break; +					case INIT_JUMPEXIT:      // 46 +						in.readSint16BE(); +						in.readByte(); +						break; +					case INIT_VIEW:          // 47 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case INIT_OBJ_FRAME:     // 48 +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						in.readSint16BE(); +						break; +					case OLD_SONG:           //49 +						in.readSint16BE(); +						in.readUint16BE(); +						break; +					default: +						error("Engine - Unknown action type encountered %d - variante %d pos %d.%d", numSubAct, varnt, i, j); +					} +				} +			} +		} +	} +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		if (varnt == _gameVariant) { +			_tunesNbr     = in.readByte(); +			_soundSilence = in.readByte(); +			_soundTest    = in.readByte(); +		} else { +			in.readByte(); +			in.readByte(); +			in.readByte(); +		} +	} + +	//Read _defltTunes +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_maxInvent = numElem; +			_defltTunes = (int16 *)malloc(sizeof(int16) * numElem); +			for (int i = 0; i < numElem; i++) +				_defltTunes[i] = in.readSint16BE(); +		} else { +			for (int i = 0; i < numElem; i++) +				in.readSint16BE(); +		} +	} + +	//Read _screenStates size +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) { +			_screenStates = (byte *)malloc(sizeof(byte) * numElem); +			for (int i = 0; i < numElem; i++) +				_screenStates[i] = 0; +		} +	} + +	//Read look, take and drop special verbs indexes +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		if (varnt == _gameVariant) { +			_look = in.readUint16BE(); +			_take = in.readUint16BE(); +			_drop = in.readUint16BE(); +		} else { +			in.readUint16BE(); +			in.readUint16BE(); +			in.readUint16BE(); +		} +	} + +	//Read LASTOBJ +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) +			_numObj = numElem; +	} + +	//Read kALnewscr used by maze (Hugo 2) +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numElem = in.readUint16BE(); +		if (varnt == _gameVariant) +			_alNewscrIndex = numElem; +	} + +	return true; +} + +char **HugoEngine::loadTextsVariante(Common::File &in, uint16 *arraySize) { +	int  numTexts; +	int  entryLen; +	int  len; +	char **res = 0; +	char *pos = 0; + +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numTexts = in.readUint16BE(); +		entryLen = in.readUint16BE(); +		pos = (char *)malloc(entryLen); +		if (varnt == _gameVariant) { +			if (arraySize) +				*arraySize = numTexts; +			res = (char **)malloc(sizeof(char *) * numTexts); +			res[0] = pos; +			in.read(res[0], entryLen); +		} else { +			in.read(pos, entryLen); +		} + +		pos += DATAALIGNMENT; + +		for (int i = 1; i < numTexts; i++) { +			pos -= 2; + +			len = READ_BE_UINT16(pos); +			pos += 2 + len; + +			if (varnt == _gameVariant) +				res[i] = pos; +		} +	} + +	return res; +} + +uint16 **HugoEngine::loadLongArray(Common::File &in) { +	uint16 **resArray = 0; +	uint16 *resRow = 0; +	uint16 dummy, numRows, numElems; + +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numRows = in.readUint16BE(); +		if (varnt == _gameVariant) { +			resArray = (uint16 **)malloc(sizeof(uint16 *) * (numRows + 1)); +			resArray[numRows] = 0; +		} +		for (int i = 0; i < numRows; i++) { +			numElems = in.readUint16BE(); +			if (varnt == _gameVariant) { +				resRow = (uint16 *)malloc(sizeof(uint16) * numElems); +				for (int j = 0; j < numElems; j++) +					resRow[j] = in.readUint16BE(); +				resArray[i] = resRow; +			} else { +				for (int j = 0; j < numElems; j++) +					dummy = in.readUint16BE(); +			} +		} +	} +	return resArray; +} + +char ***HugoEngine::loadTextsArray(Common::File &in) { +	int  numNouns; +	int  numTexts; +	int  entryLen; +	int  len; +	char ***resArray = 0; +	char **res = 0; +	char *pos = 0; + +	for (int varnt = 0; varnt < _numVariant; varnt++) { +		numNouns = in.readUint16BE(); +		if (varnt == _gameVariant) { +			resArray = (char ** *)malloc(sizeof(char **) * (numNouns + 1)); +			resArray[numNouns] = 0; +		} +		for (int i = 0; i < numNouns; i++) { +			numTexts = in.readUint16BE(); +			entryLen = in.readUint16BE(); +			pos = (char *)malloc(entryLen); +			if (varnt == _gameVariant) { +				res = (char **)malloc(sizeof(char *) * numTexts); +				res[0] = pos; +				in.read(res[0], entryLen); +			} else { +				in.read(pos, entryLen); +			} + +			pos += DATAALIGNMENT; + +			for (int j = 0; j < numTexts; j++) { +				if (varnt == _gameVariant) +					res[j] = pos; + +				pos -= 2; +				len = READ_BE_UINT16(pos); +				pos += 2 + len; +			} + +			if (varnt == _gameVariant) +				resArray[i] = res; +		} +	} + +	return resArray; +} + +char **HugoEngine::loadTexts(Common::File &in) { +	int numTexts = in.readUint16BE(); +	char **res = (char **)malloc(sizeof(char *) * numTexts); +	int entryLen; +	char *pos = 0; +	int len; + +	entryLen = in.readUint16BE(); +	pos = (char *)malloc(entryLen); + +	in.read(pos, entryLen); + +	pos += DATAALIGNMENT; +	res[0] = pos; + +	for (int i = 1; i < numTexts; i++) { +		pos -= 2; +		len = READ_BE_UINT16(pos); +		pos += 2 + len; +		res[i] = pos; +	} + +	return res; +} + +void HugoEngine::freeTexts(char **ptr) { +	if (!ptr) +		return; + +	free(*ptr); +	free(ptr); +} + + +} // End of namespace Hugo diff --git a/engines/hugo/hugo.h b/engines/hugo/hugo.h new file mode 100755 index 0000000000..21705b1070 --- /dev/null +++ b/engines/hugo/hugo.h @@ -0,0 +1,310 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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 HUGO_H +#define HUGO_H + +#include "engines/engine.h" +#include "common/file.h" + +// This include is here temporarily while the engine is being refactored. +#include "hugo/game.h" + +#define HUGO_DAT_VER_MAJ 0  // 1 byte +#define HUGO_DAT_VER_MIN 16 // 1 byte +#define DATAALIGNMENT 4 + +namespace Common { +class RandomSource; +} + +namespace Hugo { +enum GameType { +	kGameTypeNone  = 0, +	kGameTypeHugo1, +	kGameTypeHugo2, +	kGameTypeHugo3 +}; + +enum HugoebugChannels { +	kDebugSchedule   = 1 <<  0, +	kDebugEngine     = 1 <<  1, +	kDebugDisplay    = 1 <<  2, +	kDebugMouse      = 1 <<  3, +	kDebugParser     = 1 <<  4, +	kDebugFile       = 1 <<  5, +	kDebugRoute      = 1 <<  6, +	kDebugInventory  = 1 <<  7 +}; + +enum HugoGameFeatures { +	GF_PACKED = (1 << 0) // Database +}; + +struct HugoGameDescription; + +class FileManager; +class Scheduler; +class Screen; +class MouseHandler; +class InventoryHandler; +class Parser; +class Route; +class SoundHandler; +class IntroHandler; + +class HugoEngine : public Engine { +public: +	HugoEngine(OSystem *syst, const HugoGameDescription *gd); +	~HugoEngine(); + +	byte   _numVariant; +	byte   _gameVariant; +	byte   _maxInvent; +	byte   _numBonuses; +	byte   _soundSilence; +	byte   _soundTest; +	byte   _tunesNbr; +	uint16 _numScreens; + +	object_t *_hero; +	byte  *_screen_p; +	byte  _heroImage; + +	byte  *_palette; +	byte  *_introX; +	byte  *_introY; +	byte  *_screenStates; +	char  **_textData; +	char  **_stringtData; +	char  **_screenNames; +	char  **_textEngine; +	char  **_textIntro; +	char  **_textMouse; +	char  **_textParser; +	char  **_textSchedule; +	char  **_textUtil; +	char  ***_arrayNouns; +	char  ***_arrayVerbs; +	uint16    **_arrayReqs; +	hotspot_t *_hotspots; +	int16     *_invent; +	uses_t    *_uses; +	background_t *_catchallList; +	background_t **_backgroundObjects; +	point_t   *_points; +	cmd       **_cmdList; +	uint16    **_screenActs; +	object_t  *_objects; +	act       **_actListArr; +	int16     *_defltTunes; +	uint16    _look; +	uint16    _take; +	uint16    _drop; +	uint16    _numObj; +	uint16    _alNewscrIndex; + +	Common::RandomSource *_rnd; + +	const char *_episode; +	const char *_picDir; + +	char      _initFilename[20]; +	char      _saveFilename[20]; + +	const HugoGameDescription *_gameDescription; +	uint32 getFeatures() const; + +	GameType getGameType() const; +	Common::Platform getPlatform() const; +	bool isPacked() const; + +	// Temporary, until the engine is fully objectified. +	static HugoEngine &get() { +		assert(s_Engine != NULL); +		return *s_Engine; +	} + +	FileManager &file() { +		return *_fileManager; +	} +	Scheduler &scheduler() { +		return *_scheduler; +	} +	Screen &screen() { +		return *_screen; +	} +	MouseHandler &mouse() { +		return *_mouseHandler; +	} +	InventoryHandler &inventory() { +		return *_inventoryHandler; +	} +	Parser &parser() { +		return *_parser; +	} +	Route &route() { +		return *_route; +	} +	SoundHandler &sound() { +		return *_soundHandler; +	} +	IntroHandler &intro() { +		return *_introHandler; +	} + +	void initGame(const HugoGameDescription *gd); +	void initGamePart(const HugoGameDescription *gd); +	bool loadHugoDat(); + +	int getMouseX() const { +		return _mouseX; +	} +	int getMouseY() const { +		return _mouseY; +	} + +	void initStatus(); +	void readObjectImages(); +	void readUIFImages(); +	void updateImages(); +	void moveObjects(); +	void useObject(int16 objId); +	bool findObjectSpace(object_t *obj, int16 *destx, int16 *desty); +	int16 findObject(uint16 x, uint16 y); +	void lookObject(object_t *obj); +	void storeBoundary(int x1, int x2, int y); +	void clearBoundary(int x1, int x2, int y); +	void endGame(); +	void readScreenFiles(int screen); +	void setNewScreen(int screen); +	void initNewScreenDisplay(); +	void screenActions(int screen); +	void shutdown(); + +	overlay_t &getBoundaryOverlay() { +		return _boundary; +	} +	overlay_t &getObjectBoundaryOverlay() { +		return _objBound; +	} +	overlay_t &getBaseBoundaryOverlay() { +		return _ovlBase; +	} +	overlay_t &getFirstOverlay() { +		return _overlay; +	} + +	status_t &getGameStatus() { +		return _status; +	} + +	int getScore() const { +		return _score; +	} +	void setScore(int newScore) { +		_score = newScore; +	} +	void adjustScore(int adjustment) { +		_score += adjustment; +	} +	int getMaxScore() const { +		return _maxscore; +	} +	void setMaxScore(int newScore) { +		_maxscore = newScore; +	} +	byte getIntroSize() { +		return _introXSize; +	} + +protected: + +	// Engine APIs +	Common::Error run(); + +private: +	int _mouseX; +	int _mouseY; +	byte _paletteSize; +	byte _introXSize; +	status_t _status;                               // Game status structure + +	static HugoEngine *s_Engine; + +// The following are bit plane display overlays which mark travel boundaries, +// foreground stationary objects and baselines for those objects (used to +// determine foreground/background wrt moving objects) + +// Vinterstum: These shouldn't be static, but we get weird pathfinding issues (and Valgrind warnings) without. +// Needs more investigation. Alignment issues? + +	static overlay_t _boundary;                             // Boundary overlay file +	static overlay_t _overlay;                              // First overlay file +	static overlay_t _ovlBase;                              // First overlay base file +	static overlay_t _objBound;                             // Boundary file marks object baselines + +	GameType _gameType; +	Common::Platform _platform; +	bool _packedFl; + +	FileManager *_fileManager; +	Scheduler *_scheduler; +	Screen *_screen; +	MouseHandler *_mouseHandler; +	InventoryHandler *_inventoryHandler; +	Parser *_parser; +	Route *_route; +	SoundHandler *_soundHandler; +	IntroHandler *_introHandler; + +	int _score;                         // Holds current score +	int _maxscore;                      // Holds maximum score + +	char **loadTextsVariante(Common::File &in, uint16 *arraySize); +	char ***loadTextsArray(Common::File &in); +	uint16 **loadLongArray(Common::File &in); +	char **loadTexts(Common::File &in); +	void freeTexts(char **ptr); + +	void initPlaylist(bool playlist[MAX_TUNES]); +	void initConfig(inst_t action); +	void initialize(); +	int deltaX(int x1, int x2, int vx, int y); +	int deltaY(int x1, int x2, int vy, int y); +	void processMaze(); +	//int y2comp (const void *a, const void *b); +	char *useBG(char *name); +	void freeObjects(); +	void boundaryCollision(object_t *obj); +	void calcMaxScore(); + +	void initMachine(); +	void runMachine(); +}; + +} // End of namespace Hugo + +#endif // Hugo_H diff --git a/engines/hugo/intro.cpp b/engines/hugo/intro.cpp new file mode 100755 index 0000000000..eac0339660 --- /dev/null +++ b/engines/hugo/intro.cpp @@ -0,0 +1,187 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#include "common/system.h" + +#include "hugo/game.h" +#include "hugo/hugo.h" +#include "hugo/intro.h" +#include "hugo/file.h" +#include "hugo/display.h" +#include "hugo/util.h" + + +namespace Hugo { + +IntroHandler::IntroHandler(HugoEngine &vm) : _vm(vm) { +} + +IntroHandler::~IntroHandler() { +} + +intro_1w::intro_1w(HugoEngine &vm) : IntroHandler(_vm) { +} + +intro_1w::~intro_1w() { +} + +void intro_1w::preNewGame() { +	// Auto-start a new game +	_vm.file().restoreGame(-1); +	_vm.getGameStatus().viewState = V_INTROINIT; +} + +void intro_1w::introInit() { +} + +bool intro_1w::introPlay() { +	return true; +} + +intro_2w::intro_2w(HugoEngine &vm) : IntroHandler(_vm) { +} + +intro_2w::~intro_2w() { +} + +void intro_2w::preNewGame() { +} + +void intro_2w::introInit() { +} + +bool intro_2w::introPlay() { +	return true; +} + +intro_3w::intro_3w(HugoEngine &vm) : IntroHandler(_vm) { +} + +intro_3w::~intro_3w() { +} + +void intro_3w::preNewGame() { +} + +void intro_3w::introInit() { +// Hugo 3 - show map and set up for introPlay() +//#if STORY +	_vm.file().readBackground(INTRO_2_FILE); +	_vm.screen().displayBackground(); +	introTicks = 0; +//#endif +} + +bool intro_3w::introPlay() { +	byte introSize = _vm.getIntroSize(); + +// Hugo 3 - Preamble screen before going into game.  Draws path of Hugo's plane. +// Called every tick.  Returns TRUE when complete +//TODO : Add proper check of story mode +//#if STORY +//	SetBkMode(TRANSPARENT); +	if (introTicks < introSize) { +		// Scale viewport x_intro,y_intro to screen (offsetting y) +		_vm.screen().writeChar(_vm._introX[introTicks], _vm._introY[introTicks] - DIBOFF_Y, 'x', _TBRIGHTWHITE); + +		// Text boxes at various times +		switch (introTicks) { +		case 4: +			Utils::Box(BOX_OK, _vm._textIntro[kIntro1]); +			break; +		case 9: +			Utils::Box(BOX_OK, _vm._textIntro[kIntro2]); +			break; +		case 35: +			Utils::Box(BOX_OK, _vm._textIntro[kIntro3]); +			break; +		} +	} + +	return (++introTicks >= introSize); +//#else //STORY +//	return true; +//#endif //STORY +} + +intro_1d::intro_1d(HugoEngine &vm) : IntroHandler(_vm) { +} + +intro_1d::~intro_1d() { +} + +void intro_1d::preNewGame() { +} + +void intro_1d::introInit() { +} + +bool intro_1d::introPlay() { +	warning("STUB: intro_1d::introPlay()"); +	return true; +} +//TODO : Add code for intro H2 DOS +intro_2d::intro_2d(HugoEngine &vm) : IntroHandler(_vm) { +} + +intro_2d::~intro_2d() { +} + +void intro_2d::preNewGame() { +} + +void intro_2d::introInit() { +} + +bool intro_2d::introPlay() { +	return true; +} + +//TODO : Add code for intro H3 DOS +intro_3d::intro_3d(HugoEngine &vm) : IntroHandler(_vm) { +} + +intro_3d::~intro_3d() { +} + +void intro_3d::preNewGame() { +} + +void intro_3d::introInit() { +} + +bool intro_3d::introPlay() { +	return true; +} + +} // end of namespace Hugo + diff --git a/engines/hugo/intro.h b/engines/hugo/intro.h new file mode 100755 index 0000000000..202507f975 --- /dev/null +++ b/engines/hugo/intro.h @@ -0,0 +1,130 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef INTRO_H +#define INTRO_H + +namespace Hugo { +// TODO : Remove this enum. Only used in Hugo 3w intro. +// Enumerate picture files.  All screens must have an entry here, in order +enum screenid_3w { +	CRASH,   WEB,       BRIDGE_3w,  BRIDGE2,   CLIFFTOP, WFALL, +	WFALL_B, WBASE,     STREAM_3w,  STREAM2,   PATH_UL,  VILLAGE, +	HUT_OUT, HUT_IN,    GARDEN_3w,  OLDMAN_3w, CLIFF,    SLOPE, +	CAMP,    SUNSET,    TURN,       PLANE,     MAP,      PATH, +	CAVE,    FINTRO,    NUM_PICS +}; +#define INTRO_2_FILE  MAP + +enum seqTextIntro { +	kIntro1 = 0, +	kIntro2 = 1, +	kIntro3 = 2 +}; + +class IntroHandler { +public: +	IntroHandler(HugoEngine &vm); +	virtual ~IntroHandler(); + +	virtual void preNewGame() = 0; +	virtual void introInit() = 0; +	virtual bool introPlay() = 0; + +protected: +	HugoEngine &_vm; +	int16 introTicks;                               // Count calls to introPlay() +}; + +class intro_1w : public IntroHandler { +public: +	intro_1w(HugoEngine &vm); +	~intro_1w(); + +	void preNewGame(); +	void introInit(); +	bool introPlay(); +}; + +class intro_1d : public IntroHandler { +public: +	intro_1d(HugoEngine &vm); +	~intro_1d(); + +	void preNewGame(); +	void introInit(); +	bool introPlay(); +}; + +class intro_2w : public IntroHandler { +public: +	intro_2w(HugoEngine &vm); +	~intro_2w(); + +	void preNewGame(); +	void introInit(); +	bool introPlay(); +}; + +class intro_2d : public IntroHandler { +public: +	intro_2d(HugoEngine &vm); +	~intro_2d(); + +	void preNewGame(); +	void introInit(); +	bool introPlay(); +}; + +class intro_3w : public IntroHandler { +public: +	intro_3w(HugoEngine &vm); +	~intro_3w(); + +	void preNewGame(); +	void introInit(); +	bool introPlay(); +}; + +class intro_3d : public IntroHandler { +public: +	intro_3d(HugoEngine &vm); +	~intro_3d(); + +	void preNewGame(); +	void introInit(); +	bool introPlay(); +}; + + +} // Namespace Hugo +#endif diff --git a/engines/hugo/inventory.cpp b/engines/hugo/inventory.cpp new file mode 100755 index 0000000000..1988e9b1cb --- /dev/null +++ b/engines/hugo/inventory.cpp @@ -0,0 +1,229 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#include "common/system.h" + +#include "hugo/hugo.h" +#include "hugo/game.h" +#include "hugo/file.h" +#include "hugo/schedule.h" +#include "hugo/display.h" +#include "hugo/mouse.h" +#include "hugo/inventory.h" +#include "hugo/parser.h" + +namespace Hugo { + +#define MAX_DISP       (XPIX / INV_DX)              // Max icons displayable + +InventoryHandler::InventoryHandler(HugoEngine &vm) : _vm(vm) { +} + +// Construct the inventory scrollbar in dib_i +// imageTotNumb is total number of inventory icons +// displayNumb is number requested for display +// scrollFl is TRUE if scroll arrows required +// firstObjId is index of first (scrolled) inventory object to display +void InventoryHandler::constructInventory(int16 imageTotNumb, int displayNumb, bool scrollFl, int16 firstObjId) { +	int16 ux, uy, ix;                           // Coordinates of icons + +	debugC(1, kDebugInventory, "constructInventory(%d, %d, %d, %d)", imageTotNumb, displayNumb, (scrollFl) ? 0 : 1, firstObjId); + +	// Clear out icon buffer +	memset(_vm.screen().getIconBuffer(), 0, sizeof(_vm.screen().getIconBuffer())); + +	// If needed, copy arrows - reduce number of icons displayable +	if (scrollFl) { // Display at first and last icon positions +		_vm.screen().moveImage(_vm.screen().getGUIBuffer(), 0, 0, INV_DX, INV_DY, XPIX, _vm.screen().getIconBuffer(), 0, 0, XPIX); +		_vm.screen().moveImage(_vm.screen().getGUIBuffer(), INV_DX, 0, INV_DX, INV_DY, XPIX, _vm.screen().getIconBuffer(), INV_DX *(MAX_DISP - 1), 0, XPIX); +		displayNumb = MIN(displayNumb, MAX_DISP - NUM_ARROWS); +	} else  // No, override first index - we can show 'em all! +		firstObjId = 0; + +	// Copy inventory icons to remaining positions +	int16 displayed = 0; +	int16 carried = 0; +	for (int16 i = 0; i < imageTotNumb; i++) { +		if (_vm._objects[_vm._invent[i]].carriedFl) { +			// Check still room to display and past first scroll index +			if (displayed < displayNumb && carried >= firstObjId) { +				// Compute source coordinates in dib_u +				ux = (i + NUM_ARROWS) * INV_DX % XPIX; +				uy = (i + NUM_ARROWS) * INV_DX / XPIX * INV_DY; + +				// Compute dest coordinates in dib_i +				ix = ((scrollFl) ? displayed + 1 : displayed) * INV_DX; +				displayed++;        // Count number displayed + +				// Copy the icon +				_vm.screen().moveImage(_vm.screen().getGUIBuffer(), ux, uy, INV_DX, INV_DY, XPIX, _vm.screen().getIconBuffer(), ix, 0, XPIX); +			} +			carried++;                              // Count number carried +		} +	} +} + +// Process required action for inventory +// Returns objId under cursor (or -1) for INV_GET +int16 InventoryHandler::processInventory(invact_t action, ...) { +	static int16 firstIconId = 0;                   // Index of first icon to display +	int16 i, j; +	int16 objId = -1;                               // Return objid under cursor +	int16 imageNumb;                                // Total number of inventory items +	int displayNumb;                                // Total number displayed/carried +	int16 cursorx, cursory;                         // Current cursor position +	bool scrollFl;                                  // TRUE if scroll arrows needed +	va_list marker;                                 // Args used for D_ADD operation + +	debugC(1, kDebugInventory, "processInventory(invact_t action, ...)"); + +	// Compute total number and number displayed, i.e. number carried +	for (imageNumb = 0, displayNumb = 0; imageNumb < _vm._maxInvent && _vm._invent[imageNumb] != -1; imageNumb++) +		if (_vm._objects[_vm._invent[imageNumb]].carriedFl) +			displayNumb++; + +	// Will we need the scroll arrows? +	scrollFl = displayNumb > MAX_DISP; + +	switch (action) { +	case INV_INIT:                                  // Initialize inventory display +		constructInventory(imageNumb, displayNumb, scrollFl, firstIconId); +		break; +	case INV_LEFT:                                  // Scroll left by one icon +		firstIconId = MAX(0, firstIconId - 1); +		constructInventory(imageNumb, displayNumb, scrollFl, firstIconId); +		break; +	case INV_RIGHT:                                 // Scroll right by one icon +		firstIconId = MIN(displayNumb, firstIconId + 1); +		constructInventory(imageNumb, displayNumb, scrollFl, firstIconId); +		break; +	case INV_GET:                                   // Return object id under cursor +		// Get cursor position from variable argument list +		va_start(marker, action);                   // Initialize variable arguments +		cursorx = va_arg(marker, int);              // Cursor x +		cursory = va_arg(marker, int);              // Cursor y +		va_end(marker);                             // Reset variable arguments + +		cursory -= DIBOFF_Y;                        // Icon bar is at true zero +		if (cursory > 0 && cursory < INV_DY) {      // Within icon bar? +			i = cursorx / INV_DX;                   // Compute icon index +			if (scrollFl)                           // Scroll buttons displayed +				if (i == 0)                         // Left scroll button +					objId = LEFT_ARROW; +				else { +					if (i == MAX_DISP - 1)          // Right scroll button +						objId = RIGHT_ARROW; +					else                            // Adjust for scroll +						i += firstIconId - 1;       // i is icon index +				} + +			// If not an arrow, find object id - limit to valid range +			if (objId == -1 && i < displayNumb) +				// Find objid by counting # carried objects == i+1 +				for (j = 0, i++; i > 0 && j < _vm._numObj; j++) +					if (_vm._objects[j].carriedFl) +						if (--i == 0) +							objId = j; +		} +		break; +	} +	return objId;               // For the INV_GET action +} + +void InventoryHandler::runInventory() { +	status_t &gameStatus = _vm.getGameStatus(); + +	debugC(1, kDebugInventory, "runInventory"); + +// Process inventory state machine +	switch (gameStatus.inventoryState) { +	case I_OFF:                                     // Icon bar off screen +		break; +	case I_UP:                                      // Icon bar moving up +		gameStatus.inventoryHeight -= STEP_DY;      // Move the icon bar up +		if (gameStatus.inventoryHeight <= 0)        // Limit travel +			gameStatus.inventoryHeight = 0; + +		// Move visible portion to _frontBuffer, restore uncovered portion, display results +		_vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, gameStatus.inventoryHeight, XPIX, _vm.screen().getFrontBuffer(), 0, DIBOFF_Y, XPIX); +		_vm.screen().moveImage(_vm.screen().getBackBufferBackup(), 0, gameStatus.inventoryHeight + DIBOFF_Y, XPIX, STEP_DY, XPIX, _vm.screen().getFrontBuffer(), 0, gameStatus.inventoryHeight + DIBOFF_Y, XPIX); +		_vm.screen().displayRect(0, DIBOFF_Y, XPIX, gameStatus.inventoryHeight + STEP_DY); + +		if (gameStatus.inventoryHeight == 0) {      // Finished moving up? +			// Yes, restore dibs and exit back to game state machine +			_vm.screen().moveImage(_vm.screen().getBackBufferBackup(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getBackBuffer(), 0, 0, XPIX); +			_vm.screen().moveImage(_vm.screen().getBackBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getFrontBuffer(), 0, 0, XPIX); +			_vm.updateImages();                     // Add objects back into display list for restore +			gameStatus.inventoryState = I_OFF; +			gameStatus.viewState = V_PLAY; +		} +		break; +	case I_DOWN:                                    // Icon bar moving down +		// If this is the first step, initialize dib_i +		// and get any icon/text out of _frontBuffer +		if (gameStatus.inventoryHeight == 0) { +			processInventory(INV_INIT);             // Initialize dib_i +			_vm.screen().displayList(D_RESTORE);    // Restore _frontBuffer +			_vm.updateImages();                     // Rebuild _frontBuffer without icons/text +			_vm.screen().displayList(D_DISPLAY);    // Blit display list to screen +		} + +		gameStatus.inventoryHeight += STEP_DY;      // Move the icon bar down +		if (gameStatus.inventoryHeight >= INV_DY)   // Limit travel +			gameStatus.inventoryHeight = INV_DY; + +		// Move visible portion to _frontBuffer, display results +		_vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, gameStatus.inventoryHeight, XPIX, _vm.screen().getFrontBuffer(), 0, DIBOFF_Y, XPIX); +		_vm.screen().displayRect(0, DIBOFF_Y, XPIX, gameStatus.inventoryHeight); + +		if (gameStatus.inventoryHeight == INV_DY) { // Finished moving down? +			// Yes, prepare view dibs for special inventory display since +			// we can't refresh objects while icon bar overlayed... +			// 1. Save backing store _backBuffer in temporary dib_c +			// 2. Make snapshot of _frontBuffer the new _backBuffer backing store +			// 3. Reset the display list +			_vm.screen().moveImage(_vm.screen().getBackBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getBackBufferBackup(), 0, 0, XPIX); +			_vm.screen().moveImage(_vm.screen().getFrontBuffer(), 0, 0, XPIX, YPIX, XPIX, _vm.screen().getBackBuffer(), 0, 0, XPIX); +			_vm.screen().displayList(D_INIT); +			gameStatus.inventoryState = I_ACTIVE; +		} +		break; +	case I_ACTIVE:                                  // Inventory active +		_vm.parser().charHandler();                 // Still allow commands +		_vm.screen().displayList(D_RESTORE);        // Restore previous background +		_vm.mouse().mouseHandler();                 // Mouse activity - adds to display list +		_vm.screen().displayList(D_DISPLAY);        // Blit the display list to screen +		break; +	} +} + +} // End of namespace Hugo diff --git a/engines/hugo/inventory.h b/engines/hugo/inventory.h new file mode 100755 index 0000000000..e04d1ba187 --- /dev/null +++ b/engines/hugo/inventory.h @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef HUGO_INVENTORY_H +#define HUGO_INVENTORY_H +namespace Hugo { + +#define NUM_ARROWS     2                            // Number of arrows (left/right) +#define LEFT_ARROW     -2                           // Cursor over Left arrow in inventory icon bar +#define RIGHT_ARROW    -3                           // Cursor over Right arrow in inventory icon bar + +class InventoryHandler { +public: +	InventoryHandler(HugoEngine &vm); + +	int16 processInventory(invact_t action, ...); +	void runInventory(); + +private: +	HugoEngine &_vm; + +	void constructInventory(int16 imageTotNumb, int displayNumb, bool scrollFl, int16 firstObjId); +}; + +} // end of namespace Hugo +#endif // HUGO_INVENTORY_H diff --git a/engines/hugo/module.mk b/engines/hugo/module.mk new file mode 100755 index 0000000000..f7aa45a2c2 --- /dev/null +++ b/engines/hugo/module.mk @@ -0,0 +1,24 @@ +MODULE := engines/hugo + +MODULE_OBJS := \ +	detection.o \ +	display.o \ +	engine.o \ +	file.o \ +	hugo.o \ +	intro.o \ +	inventory.o \ +	mouse.o \ +	parser.o \ +	route.o \ +	schedule.o \ +	sound.o \ +	util.o + +# This module can be built as a plugin +ifeq ($(ENABLE_HUGO), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/hugo/mouse.cpp b/engines/hugo/mouse.cpp new file mode 100755 index 0000000000..581a455469 --- /dev/null +++ b/engines/hugo/mouse.cpp @@ -0,0 +1,309 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +// mouse.cpp : Handle all mouse activity + +#include "common/system.h" + +#include "hugo/game.h" +#include "hugo/hugo.h" +#include "hugo/mouse.h" +#include "hugo/global.h" +#include "hugo/schedule.h" +#include "hugo/display.h" +#include "hugo/inventory.h" +#include "hugo/route.h" +#include "hugo/util.h" + +namespace Hugo { + +#define EXIT_HOTSPOT   -4                           // Cursor over Exit hotspot +#define CURSOR_NAME    2                            // Index of name used under cursor +#define CURSOR_NOCHAR  '~'                          // Don't show name of object under cursor +#define SX_OFF         10                           // Cursor offset to name string +#define SY_OFF         -2                           // Cursor offset to name string +#define IX_OFF         8                            // Cursor to icon image (dib coords) +#define IY_OFF         10                           // Cursor to icon image (dib coords) + +enum seqTextMouse { +	kMsNoWayText = 0, +	kMsExit      = 1 +}; + +MouseHandler::MouseHandler(HugoEngine &vm) : _vm(vm) { +} + +// Shadow-blit supplied string into dib_a at cx,cy and add to display list +void MouseHandler::cursorText(char *buffer, int16 cx, int16 cy, uif_t fontId, int16 color) { + +	debugC(1, kDebugMouse, "cursorText(%s, %d, %d, %d, %d)", buffer, cx, cy, fontId, color); + +	if (_vm.getPlatform() == Common::kPlatformWindows) +		_vm.screen().loadFont(fontId); + +	// Find bounding rect for string +	int16 sdx = _vm.screen().stringLength(buffer); +	int16 sdy = _vm.screen().fontHeight() + 1;                      // + 1 for shadow +	int16 sx  = (cx < XPIX / 2) ? cx + SX_OFF : cx - sdx - SX_OFF / 2; +	int16 sy  = cy + SY_OFF; + +	// Display the string and add rect to display list +	_vm.screen().shadowStr(sx, sy, buffer, _TBRIGHTWHITE); +	_vm.screen().displayList(D_ADD, sx, sy, sdx, sdy); +} + + +// Find the exit hotspot containing cx, cy. +// Return hotspot index or -1 if not found. +int16 MouseHandler::findExit(int16 cx, int16 cy) { +	int i; +	hotspot_t *hotspot; + +	debugC(2, kDebugMouse, "findExit(%d, %d)", cx, cy); + +	for (i = 0, hotspot = _vm._hotspots; hotspot->screenIndex >= 0; i++, hotspot++) +		if (hotspot->screenIndex == *_vm._screen_p) +			if (cx >= hotspot->x1 && cx <= hotspot->x2 && cy >= hotspot->y1 && cy <= hotspot->y2) +				return(i); +	return(-1); +} + +// Process a mouse right click at coord cx, cy over object objid +void MouseHandler::processRightClick(int16 objId, int16 cx, int16 cy) { +	object_t *obj; +	int16     x, y; +	bool      foundFl = false;                      // TRUE if route found to object + +	debugC(1, kDebugMouse, "Process_rclick(%d, %d, %d)", objId, cx, cy); + +	status_t &gameStatus = _vm.getGameStatus(); + +	if (gameStatus.storyModeFl || _vm._hero->pathType == QUIET)     // Make sure user has control +		return; + +	// Check if this was over iconbar +	if (gameStatus.inventoryState == I_ACTIVE && cy < INV_DY + DIBOFF_Y) { // Clicked over iconbar object +		if (gameStatus.inventoryObjId == -1) +			gameStatus.inventoryObjId = objId;      // Not using so select new object +		else if (gameStatus.inventoryObjId == objId) +			gameStatus.inventoryObjId = -1;     // Same icon - deselect it +		else +			_vm.useObject(objId);               // Use status.objid on object +	} else {                                        // Clicked over viewport object +		obj = &_vm._objects[objId]; +		switch (obj->viewx) {                       // Where to walk to +		case -1:                                    // Walk to object position +			if (_vm.findObjectSpace(obj, &x, &y)) +				foundFl = _vm.route().startRoute(GO_GET, objId, x, y); +			if (!foundFl)                               // Can't get there, try to use from here +				_vm.useObject(objId); +			break; +		case 0:                                     // Immediate use +			_vm.useObject(objId);                   // Pick up or use object +			break; +		default:                                    // Walk to view point if possible +			if (!_vm.route().startRoute(GO_GET, objId, obj->viewx, obj->viewy)) +				if (_vm._hero->cycling == INVISIBLE)    // If invisible do +					_vm.useObject(objId);           // immediate use +				else +					Utils::Box(BOX_ANY, _vm._textMouse[kMsNoWayText]);      // Can't get there +			break; +		} +	} +} + +// Process a left mouse click over: +// 1.  An icon - show description +// 2.  An object - walk to and show description +// 3.  An icon scroll arrow - scroll the iconbar +// 4.  Nothing - attempt to walk there +// 5.  Exit - walk to exit hotspot +void MouseHandler::processLeftClick(int16 objId, int16 cx, int16 cy) { +	int16 i, x, y; +	object_t *obj; +	bool foundFl = false;                               // TRUE if route found to object + +	debugC(1, kDebugMouse, "Process_lclick(%d, %d, %d)", objId, cx, cy); + +	status_t &gameStatus = _vm.getGameStatus(); + +	if (gameStatus.storyModeFl || _vm._hero->pathType == QUIET)     // Make sure user has control +		return; + +	switch (objId) { +	case -1:                                        // Empty space - attempt to walk there +		_vm.route().startRoute(GO_SPACE, 0, cx, cy); +		break; +	case LEFT_ARROW:                                // A scroll arrow - scroll the iconbar +	case RIGHT_ARROW: +		// Scroll the iconbar and display results +		_vm.inventory().processInventory(objId == LEFT_ARROW ? INV_LEFT : INV_RIGHT); +		_vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, INV_DY, XPIX, _vm.screen().getFrontBuffer(), 0, DIBOFF_Y, XPIX); +		_vm.screen().moveImage(_vm.screen().getIconBuffer(), 0, 0, XPIX, INV_DY, XPIX, _vm.screen().getBackBuffer(), 0, DIBOFF_Y, XPIX); +		_vm.screen().displayList(D_ADD, 0, DIBOFF_Y, XPIX, INV_DY); +		break; +	case EXIT_HOTSPOT:                              // Walk to exit hotspot +		i = findExit(cx, cy); +		x = _vm._hotspots[i].viewx; +		y = _vm._hotspots[i].viewy; +		if (x >= 0) {                               // Hotspot refers to an exit +			// Special case of immediate exit +			if (gameStatus.jumpExitFl) { +				// Get rid of iconbar if necessary +				if (gameStatus.inventoryState != I_OFF) +					gameStatus.inventoryState = I_UP; +				_vm.scheduler().insertActionList(_vm._hotspots[i].actIndex); +			} else {    // Set up route to exit spot +				if (_vm._hotspots[i].direction == Common::KEYCODE_RIGHT) +					x -= HERO_MAX_WIDTH; +				else if (_vm._hotspots[i].direction == Common::KEYCODE_LEFT) +					x += HERO_MAX_WIDTH; +				if (!_vm.route().startRoute(GO_EXIT, i, x, y)) +					Utils::Box(BOX_ANY, _vm._textMouse[kMsNoWayText]);      // Can't get there +			} + +			// Get rid of any attached icon +			gameStatus.inventoryObjId = -1; +		} +		break; +	default:                                        // Look at an icon or object +		obj = &_vm._objects[objId]; + +		// Over iconbar - immediate description +		if (gameStatus.inventoryState == I_ACTIVE && cy < INV_DY + DIBOFF_Y) +			_vm.lookObject(obj); +		else { +			switch (obj->viewx) {                   // Clicked over viewport object +			case -1:                                // Walk to object position +				if (_vm.findObjectSpace(obj, &x, &y)) +					foundFl = _vm.route().startRoute(GO_LOOK, objId, x, y); +				if (!foundFl)                           // Can't get there, immediate description +					_vm.lookObject(obj); +				break; +			case 0:                                 // Immediate description +				_vm.lookObject(obj); +				break; +			default:                                // Walk to view point if possible +				if (!_vm.route().startRoute(GO_LOOK, objId, obj->viewx, obj->viewy)) +					if (_vm._hero->cycling == INVISIBLE)    // If invisible do +						_vm.lookObject(obj);            // immediate decription +					else +						Utils::Box(BOX_ANY, _vm._textMouse[kMsNoWayText]);  // Can't get there +				break; +			} +		} +		break; +	} +} + +// Process mouse activity +void MouseHandler::mouseHandler() { +	int16 iconId;                                   // Find index of dragged icon +	int   iconx, icony;                             // Icon position (in dib_a) +	int16 ux, uy;                                   // Icon position (in dib_u) +	int16 objId = -1;                               // Current source object +	char *name;                                     // Name of object to display + +	debugC(2, kDebugMouse, "mouseHandler"); + +	int16 cx = _vm.getMouseX(); +	int16 cy = _vm.getMouseY(); + +	status_t &gameStatus = _vm.getGameStatus(); + +	gameStatus.cx = cx;                                 // Save cursor coords +	gameStatus.cy = cy; + +	// Don't process if outside client area +	if (cx < 0 || cx > XPIX || cy < DIBOFF_Y || cy > VIEW_DY + DIBOFF_Y) +		return; + +	// Display dragged inventory icon if one currently selected +	if (gameStatus.inventoryObjId != -1) { +		// Find index of icon +		for (iconId = 0; iconId < _vm._maxInvent; iconId++) +			if (gameStatus.inventoryObjId == _vm._invent[iconId]) +				break; + +		// Compute source coordinates in dib_u +		ux = (iconId + NUM_ARROWS) * INV_DX % XPIX; +		uy = (iconId + NUM_ARROWS) * INV_DX / XPIX * INV_DY; + +		// Compute destination coordinates in dib_a +		iconx = cx + IX_OFF; +		icony = cy + IY_OFF; +		iconx = MAX(iconx, 0);                      // Keep within dib_a bounds +		iconx = MIN(iconx, XPIX - INV_DX); +		icony = MAX(icony, 0); +		icony = MIN(icony, YPIX - INV_DY); + +		// Copy the icon and add to display list +		_vm.screen().moveImage(_vm.screen().getGUIBuffer(), ux, uy, INV_DX, INV_DY, XPIX, _vm.screen().getFrontBuffer(), iconx, icony, XPIX); +		_vm.screen().displayList(D_ADD, iconx, icony, INV_DX, INV_DY); +	} + +	// Process cursor over an object or icon +	if (gameStatus.inventoryState == I_ACTIVE)      // Check inventory icon bar first +		objId = _vm.inventory().processInventory(INV_GET, cx, cy); +	if (objId == -1)                                // No match, check rest of view +		objId = _vm.findObject(cx, cy); +	if (objId >= 0) {                               // Got a match +		// Display object name next to cursor (unless CURSOR_NOCHAR) +		// Note test for swapped hero name +		name = _vm._arrayNouns[_vm._objects[objId == HERO ? _vm._heroImage : objId].nounIndex][CURSOR_NAME]; +		if (name[0] != CURSOR_NOCHAR) +			cursorText(name, cx, cy, U_FONT8, _TBRIGHTWHITE); + +		// Process right click over object in view or iconbar +		if (gameStatus.rightButtonFl) +			processRightClick(objId, cx, cy); +	} + +	// Process cursor over an exit hotspot +	if (objId == -1) { +		int i = findExit(cx, cy); +		if (i != -1 && _vm._hotspots[i].viewx >= 0) { +			objId = EXIT_HOTSPOT; +			cursorText(_vm._textMouse[kMsExit], cx, cy, U_FONT8, _TBRIGHTWHITE); +		} +	} + +	// Left click over icon, object or to move somewhere +	if (gameStatus.leftButtonFl) +		processLeftClick(objId, cx, cy); + +	// Clear mouse click states +	gameStatus.leftButtonFl = false; +	gameStatus.rightButtonFl = false; +} + +} // end of namespace Hugo diff --git a/engines/hugo/mouse.h b/engines/hugo/mouse.h new file mode 100755 index 0000000000..79b4bc3ef8 --- /dev/null +++ b/engines/hugo/mouse.h @@ -0,0 +1,53 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef HUGO_MOUSE_H +#define HUGO_MOUSE_H +namespace Hugo { + +class MouseHandler { +public: +	MouseHandler(HugoEngine &vm); + +	void mouseHandler(); + +private: +	HugoEngine &_vm; + +	void  cursorText(char *buffer, int16 cx, int16 cy, uif_t fontId, int16 color); +	int16 findExit(int16 cx, int16 cy); +	void  processRightClick(int16 objId, int16 cx, int16 cy); +	void  processLeftClick(int16 objId, int16 cx, int16 cy); +}; + +} // end of namespace Hugo +#endif //HUGO_MOUSE_H diff --git a/engines/hugo/parser.cpp b/engines/hugo/parser.cpp new file mode 100755 index 0000000000..f9800af2cc --- /dev/null +++ b/engines/hugo/parser.cpp @@ -0,0 +1,676 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +// parser.c - handles all keyboard/command input + +#include "common/system.h" +#include "common/keyboard.h" + +#include "hugo/game.h" +#include "hugo/hugo.h" +#include "hugo/parser.h" +#include "hugo/global.h" +#include "hugo/file.h" +#include "hugo/schedule.h" +#include "hugo/display.h" +#include "hugo/route.h" +#include "hugo/util.h" +#include "hugo/sound.h" + +namespace Hugo { + +#define BLINKS  2                                   // Cursor blinks per second +#define CX(X)   LOWORD(X) +#define CY(Y)   HIWORD(Y) + +Parser::Parser(HugoEngine &vm) : +	_vm(vm), _putIndex(0), _getIndex(0) { +} + +void Parser::keyHandler(uint16 nChar, uint16 nFlags) { +	status_t &gameStatus = _vm.getGameStatus(); +	bool repeatedFl = (nFlags & 0x4000);            // TRUE if key is a repeat + +	debugC(1, kDebugParser, "keyHandler(%d, %d)", nChar, nFlags); + +// Process key down event - called from OnKeyDown() +	switch (nChar)  {                               // Set various toggle states +	case Common::KEYCODE_ESCAPE:                    // Escape key, may want to QUIT +		if (gameStatus.inventoryState == I_ACTIVE)  // Remove inventory, if displayed +			gameStatus.inventoryState = I_UP; +		gameStatus.inventoryObjId = -1;             // Deselect any dragged icon +		break; +	case Common::KEYCODE_END: +	case Common::KEYCODE_HOME: +	case Common::KEYCODE_LEFT: +	case Common::KEYCODE_RIGHT: +	case Common::KEYCODE_UP: +	case Common::KEYCODE_DOWN: +		if (!repeatedFl) { +			gameStatus.routeIndex = -1;             // Stop any automatic route +			_vm.route().setWalk(nChar);             // Direction of hero travel +		} +		break; +	case Common::KEYCODE_F1:                        // User Help (DOS) +		if (repeatedFl) { +			_vm.file().instructions(); +			nChar = '\0'; +		} else +			_vm.screen().userHelp(); +		break; +	case Common::KEYCODE_F2:                        // Toggle sound +	case Common::KEYCODE_F3:                        // Repeat last line +	case Common::KEYCODE_F4:                        // Save game +	case Common::KEYCODE_F5:                        // Restore game +	case Common::KEYCODE_F6:                        // Inventory +	case Common::KEYCODE_F8:                        // Turbo mode +	case Common::KEYCODE_F9:                        // Boss button +		warning("STUB: KeyHandler() - F2-F9 (DOS)"); +		break; +	default:                                        // Any other key +		if (!gameStatus.storyModeFl) {              // Keyboard disabled +			// Add printable keys to ring buffer + +			uint16 bnext = _putIndex + 1; +			if (bnext >= sizeof(_ringBuffer)) +				bnext = 0; +			if (bnext != _getIndex) { +				_ringBuffer[_putIndex] = nChar; +				_putIndex = bnext; +			} +		} +		break; +	} +} + +// Add any new chars to line buffer and display them. +// If CR pressed, pass line to Line_handler() +void Parser::charHandler() { +	static int16  lineIndex = 0;                    // Index into line +	static uint32 tick = 0;                         // For flashing cursor +	static char   cursor = '_'; +	char          c; +	static        command_t cmdLine;                // Build command line +	status_t     &gameStatus = _vm.getGameStatus(); +// Strangerke : Useless ? +//	bool          updateFl = (_getIndex != _putIndex);  // TRUE if any chars processed +//	command_t    status_line;                     // Includes prompt, cursor + +//Strangerke : Useless ? +//	bool          updateFl = (_getIndex != _putIndex);	// TRUE if any chars processed +	//command_t    status_line;						// Includes prompt, cursor + +	debugC(4, kDebugParser, "charHandler"); + +	// Check for one or more characters in ring buffer +	while (_getIndex != _putIndex) { +		c = _ringBuffer[_getIndex++]; +		if (_getIndex >= sizeof(_ringBuffer)) +			_getIndex = 0; + +		switch (c) { +		case Common::KEYCODE_BACKSPACE:             // Rubout key +			if (lineIndex) +				cmdLine[--lineIndex] = '\0'; +			break; +		case Common::KEYCODE_RETURN:                // EOL, pass line to line handler +			if (lineIndex && (_vm._hero->pathType != QUIET)) { +				// Remove inventory bar if active +				if (gameStatus.inventoryState == I_ACTIVE) +					gameStatus.inventoryState = I_UP; +				// Call Line handler and reset line +				command(cmdLine); +				cmdLine[lineIndex = 0] = '\0'; +			} +			break; +		default:                                    // Normal text key, add to line +			if (lineIndex >= MAX_CHARS) { +				//MessageBeep(MB_ICONASTERISK); +				warning("STUB: MessageBeep(MB_ICONASTERISK);"); +			} else if (isprint(c)) { +				cmdLine[lineIndex++] = c; +				cmdLine[lineIndex] = '\0'; +			} +			break; +		} +	} + +	// See if time to blink cursor, set cursor character +	if ((tick++ % (TPS / BLINKS)) == 0) { +// Strangerke : Useless ? +//		updateFl = true;                            // Force an update +		cursor = cursor == '_' ? ' ' : '_'; +	} + +	// See if recall button pressed +	if (gameStatus.recallFl) { +		// Copy previous line to current cmdline +		gameStatus.recallFl = false; +		strcpy(cmdLine, _line); +		lineIndex = strlen(cmdLine); +	} + +	sprintf(_statusLine, ">%s%c", cmdLine, cursor); +	sprintf(_scoreLine, "Score: %d of %d", _vm.getScore(), _vm.getMaxScore()); + +	// See if "look" button pressed +	if (gameStatus.lookFl) { +		command("look around"); +		gameStatus.lookFl = false; +	} +} + +void Parser::drawStatusText() { +	debugC(4, kDebugParser, "drawStatusText"); + +	if (_vm.getPlatform() == Common::kPlatformWindows) +		_vm.screen().loadFont(U_FONT8); +	uint16 sdx = _vm.screen().stringLength(_statusLine); +	uint16 sdy = _vm.screen().fontHeight() + 1;                 // + 1 for shadow +	uint16 posX = 0; +	uint16 posY = YPIX - sdy; +	// Display the string and add rect to display list +	_vm.screen().writeStr(posX, posY, _statusLine, _TLIGHTYELLOW); +	_vm.screen().displayList(D_ADD, posX, posY, sdx, sdy); + +	sdx = _vm.screen().stringLength(_scoreLine); +	posY = 0; +	_vm.screen().writeStr(posX, posY, _scoreLine, _TCYAN); +	_vm.screen().displayList(D_ADD, posX, posY, sdx, sdy); +} + +// Perform an immediate command.  Takes parameters a la sprintf +// Assumes final string will not overrun line[] length +void Parser::command(const char *format, ...) { +	va_list marker; + +	debugC(1, kDebugParser, "Command(%s, ...)", format); + +	va_start(marker, format); +	vsprintf(_line, format, marker); +	va_end(marker); + +	lineHandler(); +} + +char *Parser::strlwr(char *buffer) { +	char *result = buffer; + +	debugC(1, kDebugParser, "strlwr(%s)", buffer); + +	while (*buffer != '\0') { +		if (isupper(*buffer)) +			*buffer = tolower(*buffer); +		buffer++; +	} + +	return result; +} + +// Parse the user's line of text input.  Generate events as necessary +void Parser::lineHandler() { +	char     *noun, *verb;                          // ptrs to noun and verb strings +//	int       i; +	object_t *obj; +	char      farComment[XBYTES * 5] = "";          // hold 5 line comment if object not nearby +	char      contextComment[XBYTES * 5] = "";      // Unused comment for context objects +	status_t &gameStatus = _vm.getGameStatus(); + + +	debugC(1, kDebugParser, "lineHandler"); + +	// Toggle God Mode +	if (!strncmp(_line, "PPG", 3)) { +		_vm.sound().playSound(!_vm._soundTest, BOTH_CHANNELS, HIGH_PRI); +		gameStatus.godModeFl ^= 1; +		return; +	} + +	strlwr(_line);                                  // Convert to lower case + +	// God Mode cheat commands: +	// goto <screen>                                Takes hero to named screen +	// fetch <object name>                          Hero carries named object +	// fetch all                                    Hero carries all possible objects +	// find <object name>                           Takes hero to screen containing named object +	if (DEBUG || gameStatus.godModeFl) { +		// Special code to allow me to go straight to any screen +		if (strstr(_line, "goto")) +			for (int i = 0; i < _vm._numScreens; i++) +				if (!strcmp(&_line[strlen("goto") + 1], _vm._screenNames[i])) { +					_vm.scheduler().newScreen(i); +					return; +				} + +		// Special code to allow me to get objects from anywhere +		if (strstr(_line, "fetch all")) { +			for (int i = 0; i < _vm._numObj; i++) +				if (_vm._objects[i].genericCmd & TAKE) +					takeObject(&_vm._objects[i]); +			return; +		} + +		if (strstr(_line, "fetch")) { +			for (int i = 0; i < _vm._numObj; i++) +				if (!strcmp(&_line[strlen("fetch") + 1], _vm._arrayNouns[_vm._objects[i].nounIndex][0])) { +					takeObject(&_vm._objects[i]); +					return; +				} +		} + +		// Special code to allow me to goto objects +		if (strstr(_line, "find")) +			for (int i = 0; i < _vm._numObj; i++) +				if (!strcmp(&_line[strlen("find") + 1], _vm._arrayNouns[_vm._objects[i].nounIndex][0])) { +					_vm.scheduler().newScreen(_vm._objects[i].screenIndex); +					return; +				} +	} + +	// Special meta commands +	// EXIT/QUIT +	if (!strcmp("exit", _line) || strstr(_line, "quit")) { +		Utils::Box(BOX_ANY, _vm._textParser[kTBExit]); +		return; +	} + +	// SAVE/RESTORE +	if (!strcmp("save", _line) && gameStatus.viewState == V_PLAY) { +		_vm.file().saveGame(gameStatus.saveSlot, "Current game"); +		return; +	} + +	if (!strcmp("restore", _line) && gameStatus.viewState == V_PLAY || gameStatus.viewState == V_IDLE) { +		_vm.file().restoreGame(gameStatus.saveSlot); +		_vm.scheduler().restoreScreen(*_vm._screen_p); +		gameStatus.viewState = V_PLAY; +		return; +	} + +	// Empty line +	if (*_line == '\0')                             // Empty line +		return; +	if (strspn(_line, " ") == strlen(_line))        // Nothing but spaces! +		return; + +	if (gameStatus.gameOverFl) { +		// No commands allowed! +		Utils::gameOverMsg(); +		return; +	} + +	// Test for nearby objects referenced explicitly +	for (int i = 0; i < _vm._numObj; i++) { +		obj = &_vm._objects[i]; +		if (isWordPresent(_vm._arrayNouns[obj->nounIndex])) +			if (isObjectVerb(obj, _line, farComment) || isGenericVerb(obj, _line, farComment)) +				return; +	} + +	// Test for nearby objects that only require a verb +	// Note comment is unused if not near. +	for (int i = 0; i < _vm._numObj; i++) { +		obj = &_vm._objects[i]; +		if (obj->verbOnlyFl) +			if (isObjectVerb(obj, _line, contextComment) || isGenericVerb(obj, _line, contextComment)) +				return; +	} + +	// No objects match command line, try background and catchall commands +	if (isBackgroundWord(_vm._backgroundObjects[*_vm._screen_p], _line)) +		return; +	if (isCatchallVerb(_vm._backgroundObjects[*_vm._screen_p], _line)) +		return; +	if (isBackgroundWord(_vm._catchallList, _line)) +		return; +	if (isCatchallVerb(_vm._catchallList, _line)) +		return; + +	// If a not-near comment was generated, print it +	if (*farComment != '\0') { +		Utils::Box(BOX_ANY, farComment); +		return; +	} + +	// Nothing matches.  Report recognition success to user. +	verb = findVerb(_line); +	noun = findNoun(_line); +	if (verb == _vm._arrayVerbs[_vm._look][0] && _maze.enabledFl) { +		Utils::Box(BOX_ANY, _vm._textParser[kTBMaze]); +		showTakeables(); +	} else if (verb && noun)                          // A combination I didn't think of +		Utils::Box(BOX_ANY, _vm._textParser[kTBNoPoint]); +	else if (noun) +		Utils::Box(BOX_ANY, _vm._textParser[kTBNoun]); +	else if (verb) +		Utils::Box(BOX_ANY, _vm._textParser[kTBVerb]); +	else +		Utils::Box(BOX_ANY, _vm._textParser[kTBEh]); +} + +// Search for matching verb/noun pairs in background command list +// Print text for possible background object.  Return TRUE if match found +bool Parser::isBackgroundWord(objectList_t obj, char *line) { +	debugC(1, kDebugParser, "isBackgroundWord(object_list_t obj, %s)", line); + +	for (int i = 0; obj[i].verbIndex != 0; i++) +		if (isWordPresent(_vm._arrayVerbs[obj[i].verbIndex]) && +		        isWordPresent(_vm._arrayNouns[obj[i].nounIndex]) && +		        ((obj[i].roomState == DONT_CARE) || +		         (obj[i].roomState == _vm._screenStates[*_vm._screen_p]))) { +			Utils::Box(BOX_ANY, _vm.file().fetchString(obj[i].commentIndex)); +			_vm.scheduler().processBonus(obj[i].bonusIndex); +			return true; +		} +	return false; +} + +// Search for matching verbs in background command list. +// Noun is not required.  Return TRUE if match found +// Note that if the background command list has match set TRUE then do not +// print text if there are any recognizable nouns in the command line +bool Parser::isCatchallVerb(objectList_t obj, char *line) { +	debugC(1, kDebugParser, "isCatchallVerb(object_list_t obj, %s)", line); + +	for (int i = 0; obj[i].verbIndex != 0; i++) +		if (isWordPresent(_vm._arrayVerbs[obj[i].verbIndex]) && obj[i].nounIndex == 0 && +		        (!obj[i].matchFl || !findNoun(line)) && +		        ((obj[i].roomState == DONT_CARE) || +		         (obj[i].roomState == _vm._screenStates[*_vm._screen_p]))) { +			Utils::Box(BOX_ANY, _vm.file().fetchString(obj[i].commentIndex)); +			_vm.scheduler().processBonus(obj[i].bonusIndex); + +			// If this is LOOK (without a noun), show any takeable objects +			if (*(_vm._arrayVerbs[obj[i].verbIndex]) == _vm._arrayVerbs[_vm._look][0]) +				showTakeables(); + +			return(true); +		} +	return false; +} + +// Test whether hero is close to object.  Return TRUE or FALSE +// If object not near, return suitable comment; may be another object close +// If radius is -1, treat radius as infinity +// Verb is included to determine correct comment if not near +bool Parser::isNear(object_t *obj, char *verb, char *comment) { +	debugC(1, kDebugParser, "isNear(object_t *obj, %s, %s)", verb, comment); + +	if (obj->carriedFl)                             // Object is being carried +		return(true); + +	if (obj->screenIndex != *_vm._screen_p) { +		// Not in same screen +		if (obj->objValue) +			strcpy(comment, _vm._textParser[kCmtAny1]); +		else +			strcpy(comment, _vm._textParser[kCmtAny2]); +		return(false); +	} + +	if (obj->cycling == INVISIBLE) +		if (obj->seqNumb) { +			// There is an image +			strcpy(comment, _vm._textParser[kCmtAny3]); +			return(false); +		} else +			// No image, assume visible +			if ((obj->radius < 0) || +			        ((abs(obj->x - _vm._hero->x) <= obj->radius) && +			         (abs(obj->y - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) +				return(true); +			else { +				// User is not close enough +				if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0])) +					strcpy(comment, _vm._textParser[kCmtAny1]); +				else +					strcpy(comment, _vm._textParser[kCmtClose]); +				return(false); +			} + +	if ((obj->radius < 0) || +	        ((abs(obj->x - _vm._hero->x) <= obj->radius) && +	         (abs(obj->y + obj->currImagePtr->y2 - _vm._hero->y - _vm._hero->currImagePtr->y2) <= obj->radius))) +		return(true); +	else { +		// User is not close enough +		if (obj->objValue && (verb != _vm._arrayVerbs[_vm._take][0])) +			strcpy(comment, _vm._textParser[kCmtAny1]); +		else +			strcpy(comment, _vm._textParser[kCmtClose]); +		return(false); +	} +	return true; +} + +// Locate any member of object name list appearing in command line +bool Parser::isWordPresent(char **wordArr) { +	debugC(1, kDebugParser, "isWordPresent(%s)", wordArr[0]); + +	if (wordArr != NULL) { +		for (int i = 0; strlen(wordArr[i]); i++) +			if (strstr(_line, wordArr[i])) +				return(true); +	} + +	return false; +} + +// Locate word in list of nouns and return ptr to first string in noun list +char *Parser::findNoun(char *line) { +	debugC(1, kDebugParser, "findNoun(%s)", line); + +	for (int i = 0; _vm._arrayNouns[i]; i++) +		for (int j = 0; strlen(_vm._arrayNouns[i][j]); j++) +			if (strstr(line, _vm._arrayNouns[i][j])) +				return(_vm._arrayNouns[i][0]); +	return NULL; +} + +// Locate word in list of verbs and return ptr to first string in verb list +char *Parser::findVerb(char *line) { +	debugC(1, kDebugParser, "findVerb(%s)", line); + +	for (int i = 0; _vm._arrayVerbs[i]; i++) +		for (int j = 0; strlen(_vm._arrayVerbs[i][j]); j++) +			if (strstr(line, _vm._arrayVerbs[i][j])) +				return(_vm._arrayVerbs[i][0]); +	return NULL; +} + +// Describe any takeable objects visible in this screen +void Parser::showTakeables() { +	object_t *obj; + +	debugC(1, kDebugParser, "showTakeables"); + +	for (int j = 0; j < _vm._numObj; j++) { +		obj = &_vm._objects[j]; +		if ((obj->cycling != INVISIBLE) && +		        (obj->screenIndex == *_vm._screen_p) && +		        (((TAKE & obj->genericCmd) == TAKE) || obj->objValue)) { +			sprintf(_textBoxBuffer, "You can also see:\n%s.", _vm._arrayNouns[obj->nounIndex][LOOK_NAME]); +			Utils::Box(BOX_ANY, _textBoxBuffer); +		} +	} +} + +// Do all things necessary to carry an object +void Parser::takeObject(object_t *obj) { +	debugC(1, kDebugParser, "takeObject(object_t *obj)"); + +	obj->carriedFl = true; +	if (obj->seqNumb) {                             // Don't change if no image to display +		obj->cycling = INVISIBLE; +		if (_vm.getPlatform() != Common::kPlatformWindows) +			warning("takeObject : DOS version should use ALMOST_INVISIBLE"); +	} +	_vm.adjustScore(obj->objValue); + +	if (obj->seqNumb > 0)                               // If object has an image, force walk to dropped +		obj->viewx = -1;                                // (possibly moved) object next time taken! +	Utils::Box(BOX_ANY, TAKE_TEXT, _vm._arrayNouns[obj->nounIndex][TAKE_NAME]); +} + +// Do all necessary things to drop an object +void Parser::dropObject(object_t *obj) { +	debugC(1, kDebugParser, "dropObject(object_t *obj)"); + +	obj->carriedFl = false; +	obj->screenIndex = *_vm._screen_p; +	if ((obj->seqNumb > 1) || (obj->seqList[0].imageNbr > 1)) +		obj->cycling = CYCLE_FORWARD; +	else +		obj->cycling = NOT_CYCLING; +	obj->x = _vm._hero->x - 1; +	obj->y = _vm._hero->y + _vm._hero->currImagePtr->y2 - 1; +	obj->y = (obj->y + obj->currImagePtr->y2 < YPIX) ? obj->y : YPIX - obj->currImagePtr->y2 - 10; +	_vm.adjustScore(-obj->objValue); +	Utils::Box(BOX_ANY, _vm._textParser[kTBOk]); +} + +// Test whether command line contains one of the generic actions +bool Parser::isGenericVerb(object_t *obj, char *line, char *comment) { +	debugC(1, kDebugParser, "isGenericVerb(object_t *obj, %s, %s)", line, comment); + +	if (!obj->genericCmd) +		return false; + +	// Following is equivalent to switch, but couldn't do one +	if (isWordPresent(_vm._arrayVerbs[_vm._look]) && isNear(obj, _vm._arrayVerbs[_vm._look][0], comment)) { +		// Test state-dependent look before general look +		if ((obj->genericCmd & LOOK_S) == LOOK_S) { +			Utils::Box(BOX_ANY, _vm._textData[obj->stateDataIndex[obj->state]]); +			warning("isGenericVerb: use of state dependant look - To be validated"); +		} else { +			if ((LOOK & obj->genericCmd) == LOOK) +				if (_vm._textData[obj->dataIndex]) +					Utils::Box(BOX_ANY, _vm._textData[obj->dataIndex]); +				else +					return(false); +			else +				Utils::Box(BOX_ANY, _vm._textParser[kTBUnusual]); +		} +	} else if (isWordPresent(_vm._arrayVerbs[_vm._take]) && isNear(obj, _vm._arrayVerbs[_vm._take][0], comment)) { +		if (obj->carriedFl) +			Utils::Box(BOX_ANY, _vm._textParser[kTBHave]); +		else if ((TAKE & obj->genericCmd) == TAKE) +			takeObject(obj); +		else if (obj->cmdIndex != 0)                // No comment if possible commands +			return false; +		else if (!obj->verbOnlyFl && (TAKE & obj->genericCmd) == TAKE)  // Make sure not taking object in context! +			Utils::Box(BOX_ANY, _vm._textParser[kTBNoUse]); +		else +			return false; +	} else if (isWordPresent(_vm._arrayVerbs[_vm._drop])) { +		if (!obj->carriedFl && ((DROP & obj->genericCmd) == DROP)) +			Utils::Box(BOX_ANY, _vm._textParser[kTBDontHave]); +		else if (obj->carriedFl && ((DROP & obj->genericCmd) == DROP)) +			dropObject(obj); +		else if (obj->cmdIndex == 0) +			Utils::Box(BOX_ANY, _vm._textParser[kTBNeed]); +		else +			return false; +	} else                                  // It was not a generic cmd +		return false; + +	return true; +} + +// Return TRUE if object being carried by hero +bool Parser::isCarrying(uint16 wordIndex) { +	debugC(1, kDebugParser, "isCarrying(%d)", wordIndex); + +	for (int i = 0; i < _vm._numObj; i++) +		if ((wordIndex == _vm._objects[i].nounIndex) && _vm._objects[i].carriedFl) +			return true; +	return false; +} + +// Test whether command line contains a verb allowed by this object. +// If it does, and the object is near and passes the tests in the command +// list then carry out the actions in the action list and return TRUE +bool Parser::isObjectVerb(object_t *obj, char *line, char *comment) { +	int     i; +	cmd    *cmnd; +	char   *verb; +	uint16 *reqs; +	uint16  cmdIndex; + +	debugC(1, kDebugParser, "isObjectVerb(object_t *obj, %s, %s)", line, comment); + +	// First, find matching verb in cmd list +	cmdIndex = obj->cmdIndex;                       // ptr to list of commands +	if (cmdIndex == 0)                              // No commands for this obj +		return false; + +	for (i = 0; _vm._cmdList[cmdIndex][i].verbIndex != 0; i++)                  // For each cmd +		if (isWordPresent(_vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex]))        // Was this verb used? +			break; +	if (_vm._cmdList[cmdIndex][i].verbIndex == 0)   // No verbs used. +		return false; + +	// Verb match found.  Check if object is Near +	verb = *_vm._arrayVerbs[_vm._cmdList[cmdIndex][i].verbIndex]; +	if (!isNear(obj, verb, comment)) +		return(false); + +	// Check all required objects are being carried +	cmnd = &_vm._cmdList[cmdIndex][i];              // ptr to struct cmd +	if (cmnd->reqIndex) {                           // At least 1 thing in list +		reqs = _vm._arrayReqs[cmnd->reqIndex];      // ptr to list of required objects +		for (i = 0; reqs[i]; i++)                   // for each obj +			if (!isCarrying(reqs[i])) { +				Utils::Box(BOX_ANY, _vm._textData[cmnd->textDataNoCarryIndex]); +				return true; +			} +	} + +	// Required objects are present, now check state is correct +	if ((obj->state != cmnd->reqState) && (cmnd->reqState != DONT_CARE)) { +		Utils::Box(BOX_ANY, _vm._textData[cmnd->textDataWrongIndex]); +		return true; +	} + +	// Everything checked.  Change the state and carry out any actions +	if (cmnd->reqState != DONT_CARE)                // Don't change new state if required state didn't care +		obj->state = cmnd->newState; +	Utils::Box(BOX_ANY, _vm._textData[cmnd->textDataDoneIndex]); +	_vm.scheduler().insertActionList(cmnd->actIndex); + +	// See if any additional generic actions +	if ((verb == _vm._arrayVerbs[_vm._look][0]) || (verb == _vm._arrayVerbs[_vm._take][0]) || (verb == _vm._arrayVerbs[_vm._drop][0])) +		isGenericVerb(obj, line, comment); +	return true; +} + +} // end of namespace Hugo diff --git a/engines/hugo/parser.h b/engines/hugo/parser.h new file mode 100755 index 0000000000..0bfbe32715 --- /dev/null +++ b/engines/hugo/parser.h @@ -0,0 +1,95 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef HUGO_PARSER_H +#define HUGO_PARSER_H +namespace Hugo { + +enum seqTextParser { +	kTBExit     = 0, +	kTBMaze     = 1, +	kTBNoPoint  = 2, +	kTBNoun     = 3, +	kTBVerb     = 4, +	kTBEh       = 5, +	kTBUnusual  = 6, +	kTBHave     = 7, +	kTBNoUse    = 8, +	kTBDontHave = 9, +	kTBNeed     = 10, +	kTBOk       = 11, +	kCmtAny1    = 12, +	kCmtAny2    = 13, +	kCmtAny3    = 14, +	kCmtClose   = 15 +}; + +class Parser { +public: +	Parser(HugoEngine &vm); + +	bool  isWordPresent(char **wordArr); + +	void  charHandler(); +	void  command(const char *format, ...); +	void  drawStatusText(); +	void  keyHandler(uint16 nChar, uint16 nFlags); +	void  lineHandler(); + +private: +	HugoEngine &_vm; + +	char   _ringBuffer[32];                         // Ring buffer +	uint16 _putIndex; +	uint16 _getIndex;                               // Index into ring buffer + +	command_t _statusLine; +	command_t _scoreLine; + +	bool  isBackgroundWord(objectList_t obj, char *line); +	bool  isCarrying(uint16 wordIndex); +	bool  isCatchallVerb(objectList_t obj, char *line); +	bool  isGenericVerb(object_t *obj, char *line, char *comment); +	bool  isNear(object_t *obj, char *verb, char *comment); +	bool  isObjectVerb(object_t *obj, char *line, char *comment); + +	char *findNoun(char *line); +	char *findVerb(char *line); +	char *strlwr(char *buffer); + +	void  dropObject(object_t *obj); +	void  showTakeables(); +	void  takeObject(object_t *obj); +}; + +} // end of namespace Hugo +#endif //HUGO_PARSER_H diff --git a/engines/hugo/route.cpp b/engines/hugo/route.cpp new file mode 100755 index 0000000000..88208e0034 --- /dev/null +++ b/engines/hugo/route.cpp @@ -0,0 +1,509 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +// Find shortest route from hero to destination + +#include "common/system.h" + +#include "hugo/hugo.h" +#include "hugo/game.h" +#include "hugo/route.h" +#include "hugo/global.h" + +namespace Hugo { +Route::Route(HugoEngine &vm) : _vm(vm) { +} + +// Face hero in new direction, based on cursor key input by user. +void Route::setDirection(uint16 keyCode) { +	object_t *obj = _vm._hero;                      // Pointer to hero object + +	debugC(1, kDebugRoute, "setDirection(%d)", keyCode); + +	// Set first image in sequence +	switch (keyCode) { +	case Common::KEYCODE_UP: +		obj->currImagePtr = obj->seqList[_UP].seqPtr; +		break; +	case Common::KEYCODE_DOWN: +		obj->currImagePtr = obj->seqList[DOWN].seqPtr; +		break; +	case Common::KEYCODE_LEFT: +		obj->currImagePtr = obj->seqList[LEFT].seqPtr; +		break; +	case Common::KEYCODE_RIGHT: +		obj->currImagePtr = obj->seqList[RIGHT].seqPtr; +		break; +	case Common::KEYCODE_HOME: +		obj->currImagePtr = obj->seqList[LEFT].seqPtr; +		break; +	case Common::KEYCODE_END: +		obj->currImagePtr = obj->seqList[LEFT].seqPtr; +		break; +//	case Common::KEYCODE_PRIOR: +//		obj->currImagePtr = obj->seqList[RIGHT].seqPtr; +//		break; +//	case Common::KEYCODE_NEXT: +//		obj->currImagePtr = obj->seqList[RIGHT].seqPtr; +//		break; +	} +} + +// Set hero walking, based on cursor key input by user. +// Hitting same key twice will stop hero. +void Route::setWalk(uint16 direction) { +	object_t *obj = _vm._hero;                      // Pointer to hero object +	static uint16 oldDirection = 0;                 // Last direction char + +	debugC(1, kDebugRoute, "setWalk(%d)", direction); + +	if (_vm.getGameStatus().storyModeFl || obj->pathType != USER)           // Make sure user has control +		return; + +	if (!obj->vx && !obj->vy) +		oldDirection = 0;                           // Fix for consistant restarts + +	if (direction != oldDirection) { +		// Direction has changed +		setDirection(direction);                    // Face new direction +		obj->vx = obj->vy = 0; +		switch (direction) {                        // And set correct velocity +		case Common::KEYCODE_UP: +			obj->vy = -DY; +			break; +		case Common::KEYCODE_DOWN: +			obj->vy =  DY; +			break; +		case Common::KEYCODE_LEFT: +			obj->vx = -DX; +			break; +		case Common::KEYCODE_RIGHT: +			obj->vx =  DX; +			break; +		case Common::KEYCODE_HOME: +			obj->vx = -DX; +			obj->vy = -DY / 2; +			break; +		case Common::KEYCODE_END: +			obj->vx = -DX; +			obj->vy =  DY / 2; +			break; +//		case Common::KEYCODE_PRIOR: +//			obj->vx =  DX; +//			obj->vy = -DY / 2; +//			break; +//		case Common::KEYCODE_NEXT: +//			obj->vx =  DX; +//			obj->vy =  DY / 2; +//			break; +		} +		oldDirection = direction; +		obj->cycling = CYCLE_FORWARD; +	} else { +		// Same key twice - halt hero +		obj->vy = 0; +		obj->vx = 0; +		oldDirection = 0; +		obj->cycling = NOT_CYCLING; +	} +} + +// Recursive algorithm!  Searches from hero to dest_x, dest_y +// Find horizontal line segment about supplied point and recursively +// find line segments for each point above and below that segment. +// When destination point found in segment, start surfacing and leave +// a trail in segment[] from destination back to hero. +// +// Note:  there is a bug which allows a route through a 1-pixel high +// narrow gap if between 2 segments wide enough for hero.  To work +// around this, make sure any narrow gaps are 2 or more pixels high. +// An example of this was the blocking guard in Hugo1/Dead-End. +void Route::segment(int16 x, int16 y) { +	int16 x1, x2;                                   // Range of segment +// Note use of static - can't waste stack +	static image_pt   p;                            // Ptr to _boundaryMap[y] +	static segment_t *seg_p;                        // Ptr to segment + +	debugC(1, kDebugRoute, "segment(%d, %d)", x, y); + +	// Bomb out if stack exhausted +	// Vinterstum: Is this just a safeguard, or actually used? +	//_fullStackFl = _stackavail () < 256; +	_fullStackFl = false; + +	// Find and fill on either side of point +	p = _boundaryMap[y]; +	for (x1 = x; x1 > 0; x1--) +		if (p[x1] == 0) { +#if DEBUG_ROUTE +			SetPixel(hDC, (int16)((long)config.cx * x1  / XPIX), (int16)((long)config.cy *(y - DIBOFF_Y) / VIEW_DY), GetPalIndex(_TLIGHTMAGENTA)); +#endif +			p[x1] = kMapFill; +		} else +			break; +	for (x2 = x + 1; x2 < XPIX; x2++) +		if (p[x2] == 0) { +#if DEBUG_ROUTE +			SetPixel(hDC, (int16)((long)config.cx * x2  / XPIX), (int16)((long)config.cy *(y - DIBOFF_Y) / VIEW_DY), GetPalIndex(_TLIGHTGREEN)); +#endif +			p[x2] = kMapFill; +		} else +			break; +	x1++; +	x2--; + +	// Discard path if not wide enough for hero - dead end +	if (_heroWidth > x2 - x1 + 1) +		return; + +	// Have we found the destination yet? +	if (y == _destY && x1 <= _destX && x2 >= _destX) +		_routeFoundFl = true; + +	// Bounds check y in case no boundary around screen +	if (y <= 0 || y >= YPIX - 1) +		return; +#if FALSE +	// Find all segments above and below current +	if (hero_p->x < x1 || hero_p->x + HERO_MAX_WIDTH > x2) { +		// Hero x not in segment, search x1..x2 +		// Find all segments above current +		for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) +			if (_boundaryMap[y - 1][x] == 0) +				segment(x, y - 1); + +		// Find all segments below current +		for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) +			if (_boundaryMap[y + 1][x] == 0) +				segment(x, y + 1); +	} +#endif +	if (_vm._hero->x < x1) { +		// Hero x not in segment, search x1..x2 +		// Find all segments above current +		for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) +			if (_boundaryMap[y - 1][x] == 0) +				segment(x, y - 1); + +		// Find all segments below current +		for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) +			if (_boundaryMap[y + 1][x] == 0) +				segment(x, y + 1); +	} else if (_vm._hero->x + HERO_MAX_WIDTH > x2) { +		// Hero x not in segment, search x1..x2 +		// Find all segments above current +		for (x = x2; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x >= x1; x--) +			if (_boundaryMap[y - 1][x] == 0) +				segment(x, y - 1); + +		// Find all segments below current +		for (x = x2; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x >= x1; x--) +			if (_boundaryMap[y + 1][x] == 0) +				segment(x, y + 1); +	} else { +		// Organize search around hero x position - this gives +		// better chance for more direct route. +		for (x = _vm._hero->x; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) +			if (_boundaryMap[y - 1][x] == 0) +				segment(x, y - 1); +		for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x < _vm._hero->x; x++) +			if (_boundaryMap[y - 1][x] == 0) +				segment(x, y - 1); +		for (x = _vm._hero->x; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x <= x2; x++) +			if (_boundaryMap[y + 1][x] == 0) +				segment(x, y + 1); +		for (x = x1; !(_routeFoundFl | _fullStackFl | _fullSegmentFl) && x < _vm._hero->x; x++) +			if (_boundaryMap[y + 1][x] == 0) +				segment(x, y + 1); +	} + +	// If found, surface, leaving trail back to hero +	if (_routeFoundFl) { +		// Bomb out if too many segments (leave one spare) +		if (_segmentNumb >= kMaxSeg - 1) +			_fullSegmentFl = true; +		else { +			// Create segment +			seg_p = &_segment[_segmentNumb]; +			seg_p->y  = y; +			seg_p->x1 = x1; +			seg_p->x2 = x2; +			_segmentNumb++; +		} +	} +} + +// Create and return ptr to new node.  Initialize with previous node. +// Returns NULL if MAX_NODES exceeded +Point *Route::newNode() { +	debugC(1, kDebugRoute, "newNode"); + +	if (_routeListIndex >= kMaxNodes)               // Too many nodes +		return(NULL);                               // Incomplete route - failure +	_routeListIndex++; +	_route[_routeListIndex] = _route[_routeListIndex - 1];  // Initialize with previous node +	return(&_route[_routeListIndex]); +} + +// Construct route to cx, cy.  Return TRUE if successful. +// 1.  Copy boundary bitmap to local byte map (include object bases) +// 2.  Construct list of segments segment[] from hero to destination +// 3.  Compress to shortest route in route[] +bool Route::findRoute(int16 cx, int16 cy) { +	int16      i,   j,   x, y;                      // Loop on coordinates +	int16      x1,  x2,  dx;                        // Overlap between segments +	int16      herox1, herox2, heroy;               // Current hero baseline +	object_t  *obj;                                 // Ptr to object +	segment_t *seg_p;                               // Ptr to segment +	Point     *routeNode;                           // Ptr to route node + +	debugC(1, kDebugRoute, "findRoute(%d, %d)", cx, cy); + +	// Initialize for search +	_routeFoundFl  = false;                         // Path not found yet +	_fullStackFl = false;                           // Stack not exhausted +	_fullSegmentFl  = false;                        // Segments not exhausted +	_segmentNumb = 0;                               // Segment index +	_heroWidth = HERO_MIN_WIDTH;                    // Minimum width of hero +	_destY = cy;                                    // Destination coords +	_destX = cx;                                    // Destination coords +	herox1 = _vm._hero->x + _vm._hero->currImagePtr->x1;        // Hero baseline +	herox2 = _vm._hero->x + _vm._hero->currImagePtr->x2;        // Hero baseline +	heroy  = _vm._hero->y + _vm._hero->currImagePtr->y2;        // Hero baseline + +	// Store all object baselines into objbound (except hero's = [0]) +	for (i = 1, obj = &_vm._objects[i]; i < _vm._numObj; i++, obj++) +		if ((obj->screenIndex == *_vm._screen_p) && (obj->cycling != INVISIBLE) && (obj->priority == FLOATING)) +			_vm.storeBoundary(obj->oldx + obj->currImagePtr->x1, obj->oldx + obj->currImagePtr->x2, obj->oldy + obj->currImagePtr->y2); + +	// Combine objbound and boundary bitmaps to local byte map +	for (y = 0; y < YPIX; y++) +		for (x = 0; x < XBYTES; x++) +			for (i = 0; i < 8; i++) +				_boundaryMap[y][x * 8 + i] = ((_vm.getObjectBoundaryOverlay()[y * XBYTES + x] | _vm.getBoundaryOverlay()[y * XBYTES + x]) & (0x80 >> i)) ? kMapBound : 0; + +	// Clear all object baselines from objbound +	for (i = 0, obj = _vm._objects; i < _vm._numObj; i++, obj++) +		if ((obj->screenIndex == *_vm._screen_p) && (obj->cycling != INVISIBLE) && (obj->priority == FLOATING)) +			_vm.clearBoundary(obj->oldx + obj->currImagePtr->x1, obj->oldx + obj->currImagePtr->x2, obj->oldy + obj->currImagePtr->y2); + +#if DEBUG_ROUTE +	{ +//	hDC = GetDC(hview); +		for (y = 0; y < YPIX; y++) +			for (x = 0; x < XPIX; x++) +				if (_boundaryMap[y][x]) +					SetPixel(hDC, (int16)((long)config.cx * x  / XPIX), (int16)((long)config.cy *(y - DIBOFF_Y) / VIEW_DY), GetPalIndex(_TBRIGHTWHITE)); +	} +#endif + +	// Search from hero to destination +	segment(herox1, heroy); + +//#if DEBUG_ROUTE +//	ReleaseDC(hview, hDC); +//#endif + +	// Not found or not enough stack or MAX_SEG exceeded +	if (!_routeFoundFl || _fullStackFl || _fullSegmentFl) { +#if DEBUG_ROUTE +		Box(BOX_ANY, "%s", (_fullStackFl) ? "Stack blown!" : (_fullSegmentFl) ? "Ran out of segments!" : "No Route!"); +#endif +		return(false); +	} + +	// Now find the route of nodes from destination back to hero +	// Assign first node as destination +	_route[0].x = _destX; +	_route[0].y = _destY; + +	// Make a final segment for hero's base (we left a spare) +	_segment[_segmentNumb].y  = heroy; +	_segment[_segmentNumb].x1 = herox1; +	_segment[_segmentNumb].x2 = herox2; +	_segmentNumb++; + +	// Look in segments[] for straight lines from destination to hero +	for (i = 0, _routeListIndex = 0; i < _segmentNumb - 1; i++) { +		if ((routeNode = newNode()) == NULL)        // New node for new segment +			return(false);                          // Too many nodes +		routeNode->y = _segment[i].y; + +		// Look ahead for furthest straight line +		for (j = i + 1; j < _segmentNumb; j++) { +			seg_p = &_segment[j]; +			// Can we get to this segment from previous node? +			if (seg_p->x1 <= routeNode->x && seg_p->x2 >= routeNode->x + _heroWidth - 1) +				routeNode->y = seg_p->y;            // Yes, keep updating node +			else { +				// No, create another node on previous segment to reach it +				if ((routeNode = newNode()) == NULL)    // Add new route node +					return (false);                 // Too many nodes + +				// Find overlap between old and new segments +				x1 = MAX(_segment[j - 1].x1, seg_p->x1); +				x2 = MIN(_segment[j - 1].x2, seg_p->x2); + +				// If room, add a little offset to reduce staircase effect +				dx = HERO_MAX_WIDTH >> 1; +				if (x2 - x1 < _heroWidth + dx) +					dx = 0; + +				// Bear toward final hero position +				if (j == _segmentNumb - 1) +					routeNode->x = herox1; +				else if (herox1 < x1) +					routeNode->x = x1 + dx; +				else if (herox1 > x2 - _heroWidth + 1) +					routeNode->x = x2 - _heroWidth - dx; +				else +					routeNode->x = herox1; +				i = j - 2;                          // Restart segment (-1 to offset auto increment) +				break; +			} +		} + +		// Terminate loop if we've reached hero +		if (routeNode->x == herox1 && routeNode->y == heroy) +			break; +	} +	return true; +} + +// Process hero in route mode - called from Move_objects() +void Route::processRoute() { +	int16 herox, heroy;                             // Hero position +	Point *routeNode;                               // Ptr to current route node +	static bool turnedFl = false;                   // Used to get extra cylce for turning + +	status_t &gameStatus = _vm.getGameStatus(); + +	debugC(1, kDebugRoute, "processRoute"); + +	// Current hero position +	herox = _vm._hero->x + _vm._hero->currImagePtr->x1; +	heroy = _vm._hero->y + _vm._hero->currImagePtr->y2; +	routeNode = &_route[gameStatus.routeIndex]; + +	// Arrived at node? +	if (abs(herox - routeNode->x) < DX + 1 && abs(heroy - routeNode->y) < DY) { +		// DX too low +		// Close enough - position hero exactly +		_vm._hero->x = _vm._hero->oldx = routeNode->x - _vm._hero->currImagePtr->x1; +		_vm._hero->y = _vm._hero->oldy = routeNode->y - _vm._hero->currImagePtr->y2; +		_vm._hero->vx = _vm._hero->vy = 0; +		_vm._hero->cycling = NOT_CYCLING; + +		// Arrived at final node? +		if (--gameStatus.routeIndex < 0) { +			// See why we walked here +			switch (gameStatus.go_for) { +			case GO_EXIT:                           // Walked to an exit, proceed into it +				setWalk(_vm._hotspots[gameStatus.go_id].direction); +				break; +			case GO_LOOK:                           // Look at an object +				if (turnedFl) { +					_vm.lookObject(&_vm._objects[gameStatus.go_id]); +					turnedFl = false; +				} else { +					setDirection(_vm._objects[gameStatus.go_id].direction); +					gameStatus.routeIndex++;        // Come round again +					turnedFl = true; +				} +				break; +			case GO_GET:                            // Get (or use) an object +				if (turnedFl) { +					_vm.useObject(gameStatus.go_id); +					turnedFl = false; +				} else { +					setDirection(_vm._objects[gameStatus.go_id].direction); +					gameStatus.routeIndex++;        // Come round again +					turnedFl = true; +				} +				break; +			case GO_SPACE: +				warning("Unhandled gameStatus.go_for GO_STATUS"); +				break; +			} +		} +	} else if (_vm._hero->vx == 0 && _vm._hero->vy == 0) { +		// Set direction of travel if at a node +		// Note realignment when changing to (thinner) up/down sprite, +		// otherwise hero could bump into boundaries along route. +		if (herox < routeNode->x) +			setWalk(Common::KEYCODE_RIGHT); +		else if (herox > routeNode->x) +			setWalk(Common::KEYCODE_LEFT); +		else if (heroy < routeNode->y) { +			setWalk(Common::KEYCODE_DOWN); +			_vm._hero->x = _vm._hero->oldx = routeNode->x - _vm._hero->currImagePtr->x1; +		} else if (heroy > routeNode->y) { +			setWalk(Common::KEYCODE_UP); +			_vm._hero->x = _vm._hero->oldx = routeNode->x - _vm._hero->currImagePtr->x1; +		} +	} +} + +// Start a new route from hero to cx, cy +// go_for is the purpose, id indexes the exit or object to walk to +// Returns FALSE if route not found +bool Route::startRoute(go_t go_for, int16 id, int16 cx, int16 cy) { +	bool foundFl = false;                           // TRUE if route found ok + +	status_t &gameStatus = _vm.getGameStatus(); + +	debugC(1, kDebugRoute, "startRoute(%d, %d, %d, %d)", go_for, id, cx, cy); + +	// Don't attempt to walk if user does not have control +	if (_vm._hero->pathType != USER) +		return false; + +	// if inventory showing, make it go away +	if (gameStatus.inventoryState != I_OFF) +		gameStatus.inventoryState = I_UP; + +	gameStatus.go_for = go_for;                     // Purpose of trip +	gameStatus.go_id  = id;                         // Index of exit/object + +	// Adjust destination to center hero if walking to cursor +	if (gameStatus.go_for == GO_SPACE) +		cx -= HERO_MIN_WIDTH / 2; + +	if ((foundFl = findRoute(cx, cy))) {            // Found a route? +		gameStatus.routeIndex = _routeListIndex;    // Node index +		_vm._hero->vx = _vm._hero->vy = 0;      // Stop manual motion +	} + +	return foundFl; +} + +} // end of namespace Hugo diff --git a/engines/hugo/route.h b/engines/hugo/route.h new file mode 100755 index 0000000000..b21aadbbbd --- /dev/null +++ b/engines/hugo/route.h @@ -0,0 +1,84 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef HUGO_ROUTE_H +#define HUGO_ROUTE_H + +namespace Hugo { + +#define kMapBound 1                                 // Mark a boundary outline +#define kMapFill  2                                 // Mark a boundary filled +#define kMaxSeg   256                               // Maximum number of segments +#define kMaxNodes 256                               // Maximum nodes in route +#define DEBUG_ROUTE FALSE + +struct Point { +	int x; +	int y; +}; + +struct segment_t {                                  // Search segment +	int16 y;                                        // y position +	int16 x1, x2;                                   // Range of segment +}; + +class Route { +public: +	Route(HugoEngine &vm); + +	void processRoute(); +	bool startRoute(go_t go_for, short id, short cx, short cy); +	void setDirection(uint16 keyCode); +	void setWalk(uint16 direction); + +private: +	HugoEngine &_vm; + +	byte _boundaryMap[YPIX][XPIX];                  // Boundary byte map +	segment_t _segment[kMaxSeg];                    // List of points in fill-path +	Point _route[kMaxNodes];                        // List of nodes in route (global) +	int16 _segmentNumb;                             // Count number of segments +	int16 _routeListIndex;                          // Index into route list +	int16 _destX; +	int16 _destY; +	int16 _heroWidth;                               // Hero width +	bool  _routeFoundFl;                            // TRUE when path found +	bool  _fullStackFl;                             // TRUE if stack exhausted +	bool  _fullSegmentFl;                           // Segments exhausted + +	void segment(int16 x, int16 y); +	bool findRoute(int16 cx, int16 cy); +	Point *newNode(); +}; + +} // end of namespace Hugo +#endif //HUGO_ROUTE_H diff --git a/engines/hugo/schedule.cpp b/engines/hugo/schedule.cpp new file mode 100755 index 0000000000..b14fef10f3 --- /dev/null +++ b/engines/hugo/schedule.cpp @@ -0,0 +1,677 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +// This module contains all the scheduling and timing stuff + +#include "common/system.h" +#include "common/stream.h" + +#include "hugo/game.h" +#include "hugo/hugo.h" +#include "hugo/schedule.h" +#include "hugo/global.h" +#include "hugo/file.h" +#include "hugo/display.h" +#include "hugo/parser.h" +#include "hugo/util.h" +#include "hugo/sound.h" + +namespace Hugo { + +#define SIGN(X)       (X < 0 ? -1 : 1) + +Scheduler::Scheduler(HugoEngine &vm) : _vm(vm) { +} + +// Initialise the timer event queue +void Scheduler::initEventQueue() { +	debugC(1, kDebugSchedule, "initEventQueue"); + +	// Chain next_p from first to last +	for (int i = kMaxEvents; --i;) +		_events[i - 1].nextEvent = &_events[i]; +	_events[kMaxEvents - 1].nextEvent = 0; + +	// Chain prev_p from last to first +	for (int i = 1; i < kMaxEvents; i++) +		_events[i].prevEvent = &_events[i - 1]; +	_events[0].prevEvent = 0; + +	_headEvent = _tailEvent = 0;                    // Event list is empty +	_freeEvent = _events;                           // Free list is full +} + +// Return a ptr to an event structure from the free list +event_t *Scheduler::getQueue() { +	debugC(4, kDebugSchedule, "getQueue"); +	event_t *resEvent; + +	if (!_freeEvent)                                // Error: no more events available +		Utils::Error(EVNT_ERR, "getQueue"); +	resEvent = _freeEvent; +	_freeEvent = _freeEvent->nextEvent; +	resEvent->nextEvent = 0; +	return resEvent; +} + +// Delete an event structure (i.e. return it to the free list) +// Historical note:  Originally event p was assumed to be at head of queue +// (i.e. earliest) since all events were deleted in order when proceeding to +// a new screen.  To delete an event from the middle of the queue, the action +// was overwritten to be ANULL.  With the advent of GLOBAL events, Del_queue +// was modified to allow deletes anywhere in the list, and the DEL_EVENT +// action was modified to perform the actual delete. +void Scheduler::delQueue(event_t *curEvent) { +	debugC(4, kDebugSchedule, "delQueue"); +	if (curEvent == _headEvent)                     // If p was the head ptr +		_headEvent = curEvent->nextEvent;           // then make new head_p +	else {                                          // Unlink p +		curEvent->prevEvent->nextEvent = curEvent->nextEvent; +		if (curEvent->nextEvent) +			curEvent->nextEvent->prevEvent = curEvent->prevEvent; +		else +			_tailEvent = curEvent->prevEvent; +	} + +	if (_headEvent) +		_headEvent->prevEvent = 0;                  // Mark end of list +	else +		_tailEvent = 0;                             // Empty queue + +	curEvent->nextEvent = _freeEvent;               // Return p to free list +	if (_freeEvent)                                 // Special case, if free list was empty +		_freeEvent->prevEvent = curEvent; +	_freeEvent = curEvent; +} + +// Insert the action pointed to by p into the timer event queue +// The queue goes from head (earliest) to tail (latest) timewise +void Scheduler::insertAction(act *action) { +	debugC(1, kDebugSchedule, "insertAction - Action type A%d", action->a0.actType); + +	// First, get and initialise the event structure +	event_t *curEvent = getQueue(); +	curEvent->action = action; +	switch (action->a0.actType) {                   // Assign whether local or global +	case AGSCHEDULE: +		curEvent->localActionFl = false;            // Lasts over a new screen +		break; +	default: +		curEvent->localActionFl = true;             // Rest are for current screen only +		break; +	} + +	curEvent->time = action->a0.timer + getTicks(); // Convert rel to abs time + +	// Now find the place to insert the event +	if (!_tailEvent) {                              // Empty queue +		_tailEvent = _headEvent = curEvent; +		curEvent->nextEvent = curEvent->prevEvent = NULL; +	} else { +		event_t *wrkEvent = _tailEvent;             // Search from latest time back +		bool found = false; + +		while (wrkEvent && !found) { +			if (wrkEvent->time <= curEvent->time) { // Found if new event later +				found = true; +				if (wrkEvent == _tailEvent)         // New latest in list +					_tailEvent = curEvent; +				else +					wrkEvent->nextEvent->prevEvent = curEvent; +				curEvent->nextEvent = wrkEvent->nextEvent; +				wrkEvent->nextEvent = curEvent; +				curEvent->prevEvent = wrkEvent; +			} +			wrkEvent = wrkEvent->prevEvent; +		} + +		if (!found) {                               // Must be earliest in list +			_headEvent->prevEvent = curEvent;       // So insert as new head +			curEvent->nextEvent = _headEvent; +			curEvent->prevEvent = NULL; +			_headEvent = curEvent; +		} +	} +} + +void Scheduler::insertActionList(uint16 actIndex) { +// Call Insert_action for each action in the list supplied +	debugC(1, kDebugSchedule, "insertActionList(%d)", actIndex); + +	if (_vm._actListArr[actIndex]) +		for (int i = 0; _vm._actListArr[actIndex][i].a0.actType != ANULL; i++) +			insertAction(&_vm._actListArr[actIndex][i]); +} + +void Scheduler::decodeString(char *line) { +// Decode a string +	debugC(1, kDebugSchedule, "decodeString(%s)", line); + +	static char cypher[] = "Copyright 1992, Gray Design Associates"; + +	for (uint16 i = 0; i < strlen(line); i++) +		line[i] -= cypher[i % strlen(cypher)]; +	debugC(1, kDebugSchedule, "result : %s", line); +} + +event_t *Scheduler::doAction(event_t *curEvent) { +// This function performs the action in the event structure pointed to by p +// It dequeues the event and returns it to the free list.  It returns a ptr +// to the next action in the list, except special case of NEW_SCREEN +	event_t  *wrkEvent;                             // Save ev_p->next_p for return +	event_t  *saveEvent;                            // Used in DEL_EVENTS +	char     *response;                             // User's response string +	object_t *obj1; +	object_t *obj2; +	int       dx, dy; +	act      *action;                               // Ptr to action structure + +	status_t &gameStatus = _vm.getGameStatus(); + +	action = curEvent->action; +	debugC(1, kDebugSchedule, "doAction - Event action type : %d", action->a0.actType); + +	switch (action->a0.actType) { +	case ANULL:                                     // Big NOP from DEL_EVENTS +		break; +	case ASCHEDULE:                                 // act0: Schedule an action list +		insertActionList(action->a0.actIndex); +		break; +	case START_OBJ:                                 // act1: Start an object cycling +		_vm._objects[action->a1.objNumb].cycleNumb = action->a1.cycleNumb; +		_vm._objects[action->a1.objNumb].cycling = action->a1.cycle; +		break; +	case INIT_OBJXY:                                // act2: Initialise an object +		_vm._objects[action->a2.objNumb].x = action->a2.x;          // Coordinates +		_vm._objects[action->a2.objNumb].y = action->a2.y; +		break; +	case PROMPT:                                    // act3: Prompt user for key phrase +// TODO : Add specific code for Hugo 1 DOS, which is handled differently, +		response = Utils::Box(BOX_PROMPT, _vm.file().fetchString(action->a3.promptIndex)); + +		warning("STUB: doAction(act3), expecting answer %s", response); + +// TODO : The answer of the player is not handled currently! Once it'll be read in the messageBox, uncomment this block +#if 0 +		bool      found; +		char     *tmpStr;                           // General purpose string ptr + +		for (found = false, dx = 0; !found && (action->a3.responsePtr[dx] != -1); dx++) { +			tmpStr = _vm.file().Fetch_string(action->a3.responsePtr[dx]); +			if (strstr(_vm.parser().strlwr(response) , tmpStr)) +				found = true; +		} + +		if (found) +			insertActionList(action->a3.actPassIndex); +		else +			insertActionList(action->a3.actFailIndex); +#endif + +//HACK: As the answer is not read, currently it's always considered correct +		insertActionList(action->a3.actPassIndex); +		break; +	case BKGD_COLOR:                                // act4: Set new background color +		HugoEngine::get().screen().setBackgroundColor(action->a4.newBackgroundColor); +		break; +	case INIT_OBJVXY:                               // act5: Initialise an object +		_vm._objects[action->a5.objNumb].vx = action->a5.vx;        // velocities +		_vm._objects[action->a5.objNumb].vy = action->a5.vy; +		break; +	case INIT_CARRY:                                // act6: Initialise an object +		_vm._objects[action->a6.objNumb].carriedFl = action->a6.carriedFl;  // carried status +		break; +	case INIT_HF_COORD:                             // act7: Initialise an object to hero's "feet" coords +		_vm._objects[action->a7.objNumb].x = _vm._hero->x - 1; +		_vm._objects[action->a7.objNumb].y = _vm._hero->y + _vm._hero->currImagePtr->y2 - 1; +		_vm._objects[action->a7.objNumb].screenIndex = *_vm._screen_p;  // Don't forget screen! +		break; +	case NEW_SCREEN:                                // act8: Start new screen +		newScreen(action->a8.screenIndex); +		break; +	case INIT_OBJSTATE:                             // act9: Initialise an object state +		_vm._objects[action->a9.objNumb].state = action->a9.newState; +		break; +	case INIT_PATH:                                 // act10: Initialise an object path and velocity +		_vm._objects[action->a10.objNumb].pathType = (path_t) action->a10.newPathType; +		_vm._objects[action->a10.objNumb].vxPath = action->a10.vxPath; +		_vm._objects[action->a10.objNumb].vyPath = action->a10.vyPath; +		break; +	case COND_R:                                    // act11: action lists conditional on object state +		if (_vm._objects[action->a11.objNumb].state == action->a11.stateReq) +			insertActionList(action->a11.actPassIndex); +		else +			insertActionList(action->a11.actFailIndex); +		break; +	case TEXT:                                      // act12: Text box (CF WARN) +		Utils::Box(BOX_ANY, _vm.file().fetchString(action->a12.stringIndex));   // Fetch string from file +		break; +	case SWAP_IMAGES:                               // act13: Swap 2 object images +		swapImages(action->a13.obj1, action->a13.obj2); +		break; +	case COND_SCR:                                  // act14: Conditional on current screen +		if (_vm._objects[action->a14.objNumb].screenIndex == action->a14.screenReq) +			insertActionList(action->a14.actPassIndex); +		else +			insertActionList(action->a14.actFailIndex); +		break; +	case AUTOPILOT:                                 // act15: Home in on a (stationary) object +		// object p1 will home in on object p2 +		obj1 = &_vm._objects[action->a15.obj1]; +		obj2 = &_vm._objects[action->a15.obj2]; +		obj1->pathType = AUTO; +		dx = obj1->x + obj1->currImagePtr->x1 - obj2->x - obj2->currImagePtr->x1; +		dy = obj1->y + obj1->currImagePtr->y1 - obj2->y - obj2->currImagePtr->y1; + +		if (dx == 0)                                // Don't EVER divide by zero! +			dx = 1; +		if (dy == 0) +			dy = 1; + +		if (abs(dx) > abs(dy)) { +			obj1->vx = action->a15.dx * -SIGN(dx); +			obj1->vy = abs((action->a15.dy * dy) / dx) * -SIGN(dy); +		} else { +			obj1->vy = action->a15.dy * -SIGN(dy); +			obj1->vx = abs((action->a15.dx * dx) / dy) * -SIGN(dx); +		} +		break; +	case INIT_OBJ_SEQ:                              // act16: Set sequence number to use +		// Note: Don't set a sequence at time 0 of a new screen, it causes +		// problems clearing the boundary bits of the object!  t>0 is safe +		_vm._objects[action->a16.objNumb].currImagePtr = _vm._objects[action->a16.objNumb].seqList[action->a16.seqIndex].seqPtr; +		break; +	case SET_STATE_BITS:                            // act17: OR mask with curr obj state +		_vm._objects[action->a17.objNumb].state |= action->a17.stateMask; +		break; +	case CLEAR_STATE_BITS:                          // act18: AND ~mask with curr obj state +		_vm._objects[action->a18.objNumb].state &= ~action->a18.stateMask; +		break; +	case TEST_STATE_BITS:                           // act19: If all bits set, do apass else afail +		if ((_vm._objects[action->a19.objNumb].state & action->a19.stateMask) == action->a19.stateMask) +			insertActionList(action->a19.actPassIndex); +		else +			insertActionList(action->a19.actFailIndex); +		break; +	case DEL_EVENTS:                                // act20: Remove all events of this action type +		// Note: actions are not deleted here, simply turned into NOPs! +		wrkEvent = _headEvent;                      // The earliest event +		while (wrkEvent) {                          // While events found in list +			saveEvent = wrkEvent->nextEvent; +			if (wrkEvent->action->a20.actType == action->a20.actTypeDel) +				delQueue(wrkEvent); +			wrkEvent = saveEvent; +		} +		break; +	case GAMEOVER:                                  // act21: Game over! +		// NOTE: Must wait at least 1 tick before issuing this action if +		// any objects are to be made invisible! +		gameStatus.gameOverFl = true; +		break; +	case INIT_HH_COORD:                             // act22: Initialise an object to hero's actual coords +		_vm._objects[action->a22.objNumb].x = _vm._hero->x; +		_vm._objects[action->a22.objNumb].y = _vm._hero->y; +		_vm._objects[action->a22.objNumb].screenIndex = *_vm._screen_p;// Don't forget screen! +		break; +	case EXIT:                                      // act23: Exit game back to DOS +		_vm.endGame(); +		break; +	case BONUS:                                     // act24: Get bonus score for action +		processBonus(action->a24.pointIndex); +		break; +	case COND_BOX:                                  // act25: Conditional on bounding box +		obj1 = &_vm._objects[action->a25.objNumb]; +		dx = obj1->x + obj1->currImagePtr->x1; +		dy = obj1->y + obj1->currImagePtr->y2; +		if ((dx >= action->a25.x1) && (dx <= action->a25.x2) && +		        (dy >= action->a25.y1) && (dy <= action->a25.y2)) +			insertActionList(action->a25.actPassIndex); +		else +			insertActionList(action->a25.actFailIndex); +		break; +	case SOUND:                                     // act26: Play a sound (or tune) +		if (action->a26.soundIndex < _vm._tunesNbr) +			_vm.sound().playMusic(action->a26.soundIndex); +		else +			_vm.sound().playSound(action->a26.soundIndex, BOTH_CHANNELS, MED_PRI); +		break; +	case ADD_SCORE:                                 // act27: Add object's value to score +		_vm.adjustScore(_vm._objects[action->a27.objNumb].objValue); +		break; +	case SUB_SCORE:                                 // act28: Subtract object's value from score +		_vm.adjustScore(-_vm._objects[action->a28.objNumb].objValue); +		break; +	case COND_CARRY:                                // act29: Conditional on object being carried +		if (_vm._objects[action->a29.objNumb].carriedFl) +			insertActionList(action->a29.actPassIndex); +		else +			insertActionList(action->a29.actFailIndex); +		break; +	case INIT_MAZE:                                 // act30: Enable and init maze structure +		_maze.enabledFl = true; +		_maze.size = action->a30.mazeSize; +		_maze.x1 = action->a30.x1; +		_maze.y1 = action->a30.y1; +		_maze.x2 = action->a30.x2; +		_maze.y2 = action->a30.y2; +		_maze.x3 = action->a30.x3; +		_maze.x4 = action->a30.x4; +		_maze.firstScreenIndex = action->a30.firstScreenIndex; +		break; +	case EXIT_MAZE:                                 // act31: Disable maze mode +		_maze.enabledFl = false; +		break; +	case INIT_PRIORITY: +		_vm._objects[action->a32.objNumb].priority = action->a32.priority; +		break; +	case INIT_SCREEN: +		_vm._objects[action->a33.objNumb].screenIndex = action->a33.screenIndex; +		break; +	case AGSCHEDULE:                                // act34: Schedule a (global) action list +		insertActionList(action->a34.actIndex); +		break; +	case REMAPPAL:                                  // act35: Remap a palette color +		HugoEngine::get().screen().remapPal(action->a35.oldColorIndex, action->a35.newColorIndex); +		break; +	case COND_NOUN:                                 // act36: Conditional on noun mentioned +		if (_vm.parser().isWordPresent(_vm._arrayNouns[action->a36.nounIndex])) +			insertActionList(action->a36.actPassIndex); +		else +			insertActionList(action->a36.actFailIndex); +		break; +	case SCREEN_STATE:                              // act37: Set new screen state +		_vm._screenStates[action->a37.screenIndex] = action->a37.newState; +		break; +	case INIT_LIPS:                                 // act38: Position lips on object +		_vm._objects[action->a38.lipsObjNumb].x = _vm._objects[action->a38.objNumb].x + action->a38.dxLips; +		_vm._objects[action->a38.lipsObjNumb].y = _vm._objects[action->a38.objNumb].y + action->a38.dyLips; +		_vm._objects[action->a38.lipsObjNumb].screenIndex = *_vm._screen_p; // Don't forget screen! +		_vm._objects[action->a38.lipsObjNumb].cycling = CYCLE_FORWARD; +		break; +	case INIT_STORY_MODE:                           // act39: Init story_mode flag +		// This is similar to the QUIET path mode, except that it is +		// independant of it and it additionally disables the ">" prompt +		gameStatus.storyModeFl = action->a39.storyModeFl; + +		// End the game after story if this is special vendor demo mode +		if (gameStatus.demoFl && action->a39.storyModeFl == false) +			_vm.endGame(); +		break; +	case WARN:                                      // act40: Text box (CF TEXT) +		Utils::Box(BOX_OK, _vm.file().fetchString(action->a40.stringIndex)); +		break; +	case COND_BONUS:                                // act41: Perform action if got bonus +		if (_vm._points[action->a41.BonusIndex].scoredFl) +			insertActionList(action->a41.actPassIndex); +		else +			insertActionList(action->a41.actFailIndex); +		break; +	case TEXT_TAKE:                                 // act42: Text box with "take" message +		Utils::Box(BOX_ANY, TAKE_TEXT, _vm._arrayNouns[_vm._objects[action->a42.objNumb].nounIndex][TAKE_NAME]); +		break; +	case YESNO:                                     // act43: Prompt user for Yes or No +		warning("doAction(act43) - Yes/No Box"); +		if (Utils::Box(BOX_YESNO, _vm.file().fetchString(action->a43.promptIndex)) != NULL) +			insertActionList(action->a43.actYesIndex); +		else +			insertActionList(action->a43.actNoIndex); +		break; +	case STOP_ROUTE:                                // act44: Stop any route in progress +		gameStatus.routeIndex = -1; +		break; +	case COND_ROUTE:                                // act45: Conditional on route in progress +		if (gameStatus.routeIndex >= action->a45.routeIndex) +			insertActionList(action->a45.actPassIndex); +		else +			insertActionList(action->a45.actFailIndex); +		break; +	case INIT_JUMPEXIT:                             // act46: Init status.jumpexit flag +		// This is to allow left click on exit to get there immediately +		// For example the plane crash in Hugo2 where hero is invisible +		// Couldn't use INVISIBLE flag since conflicts with boat in Hugo1 +		gameStatus.jumpExitFl = action->a46.jumpExitFl; +		break; +	case INIT_VIEW:                                 // act47: Init object.viewx, viewy, dir +		_vm._objects[action->a47.objNumb].viewx = action->a47.viewx; +		_vm._objects[action->a47.objNumb].viewy = action->a47.viewy; +		_vm._objects[action->a47.objNumb].direction = action->a47.direction; +		break; +	case INIT_OBJ_FRAME:                            // act48: Set seq,frame number to use +		// Note: Don't set a sequence at time 0 of a new screen, it causes +		// problems clearing the boundary bits of the object!  t>0 is safe +		_vm._objects[action->a48.objNumb].currImagePtr = _vm._objects[action->a48.objNumb].seqList[action->a48.seqIndex].seqPtr; +		for (dx = 0; dx < action->a48.frameIndex; dx++) +			_vm._objects[action->a48.objNumb].currImagePtr = _vm._objects[action->a48.objNumb].currImagePtr->nextSeqPtr; +		break; +	case OLD_SONG: +		//TODO For Hugo 1 and Hugo2 DOS: The songs were not stored in a DAT file, but directly as +		//strings. the current play_music should be modified to use a strings instead of reading +		//the file, in those cases. This replaces, for those DOS versions, act26. +		warning("STUB: doAction(act49)"); +		break; +	default: +		Utils::Error(EVNT_ERR, "doAction"); +		break; +	} + +	if (action->a0.actType == NEW_SCREEN)           // New_screen() deletes entire list +		return (NULL);                              // next_p = NULL since list now empty +	else { +		wrkEvent = curEvent->nextEvent; +		delQueue(curEvent);                         // Return event to free list +		return(wrkEvent);                           // Return next event ptr +	} +} + +// This is the scheduler which runs every tick.  It examines the event queue +// for any events whose time has come.  It dequeues these events and performs +// the action associated with the event, returning it to the free queue +void Scheduler::runScheduler() { +	debugC(6, kDebugSchedule, "runScheduler"); + +	status_t &gameStatus = _vm.getGameStatus(); + +	event_t *curEvent = _headEvent;                 // The earliest event +	while (curEvent && curEvent->time <= gameStatus.tick) // While mature events found +		curEvent = doAction(curEvent);              // Perform the action (returns next_p) +	gameStatus.tick++;                              // Accessed elsewhere via getTicks() +} + +uint32 Scheduler::getTicks() { +// Return system time in ticks.  A tick is 1/TICKS_PER_SEC mS +	debugC(3, kDebugSchedule, "getTicks"); + +	return _vm.getGameStatus().tick; +} + +void Scheduler::processBonus(int bonusIndex) { +// Add indecated bonus to score if not added already +	debugC(1, kDebugSchedule, "processBonus(%d)", bonusIndex); + +	if (!_vm._points[bonusIndex].scoredFl) { +		_vm.adjustScore(_vm._points[bonusIndex].score); +		_vm._points[bonusIndex].scoredFl = true; +	} +} + +// Transition to a new screen as follows: +//	1. Clear out all non-global events from event list. +//	2. Set the new screen (in the hero object and any carried objects) +//	3. Read in the screen files for the new screen +//	4. Schedule action list for new screen +//	5. Initialise prompt line and status line +void Scheduler::newScreen(int screenIndex) { +	debugC(1, kDebugSchedule, "newScreen(%d)", screenIndex); + +	// Make sure the background file exists! +	if (!_vm.isPacked()) { +		char line[32]; +		if (!_vm.file().fileExists(strcat(strncat(strcpy(line, _vm._picDir), _vm._screenNames[screenIndex], NAME_LEN), BKGEXT)) && +		        !_vm.file().fileExists(strcat(strcpy(line, _vm._screenNames[screenIndex]), ".ART"))) { +			Utils::Box(BOX_ANY, _vm._textSchedule[kSsNoBackground]); +			return; +		} +	} + +	// 1. Clear out all local events +	event_t *curEvent = _headEvent;                 // The earliest event +	event_t *wrkEvent;                              // Event ptr +	while (curEvent) {                              // While mature events found +		wrkEvent = curEvent->nextEvent;             // Save p (becomes undefined after Del) +		if (curEvent->localActionFl) +			delQueue(curEvent);                     // Return event to free list +		curEvent = wrkEvent; +	} + +	// 2. Set the new screen in the hero object and any being carried +	_vm.setNewScreen(screenIndex); + +	// 3. Read in new screen files +	_vm.readScreenFiles(screenIndex); + +	// 4. Schedule action list for this screen +	_vm.screenActions(screenIndex); + +	// 5. Initialise prompt line and status line +	_vm.initNewScreenDisplay(); +} + +// Write the event queue to the file with handle f +// Note that we convert all the event structure ptrs to indexes +// using -1 for NULL.  We can't convert the action ptrs to indexes +// so we save address of first dummy action ptr to compare on restore. +void Scheduler::saveEvents(Common::WriteStream *f) { +	uint32   curTime; +	event_t  saveEvents_[kMaxEvents];                // Convert event ptrs to indexes +	event_t *wrkEvent;                              // Event ptr +	int16    freeIndex;                             // Free list index +	int16    headIndex;                             // Head of list index +	int16    tailIndex;                             // Tail of list index + +	debugC(1, kDebugSchedule, "saveEvents"); + +	curTime = getTicks(); + +	// Convert event ptrs to indexes +	for (int16 i = 0; i < kMaxEvents; i++) { +		wrkEvent = &_events[i]; +		saveEvents_[i] = *wrkEvent; +		saveEvents_[i].prevEvent = (wrkEvent->prevEvent == NULL) ? (event_t *) - 1 : (event_t *)(wrkEvent->prevEvent - _events); +		saveEvents_[i].nextEvent = (wrkEvent->nextEvent == NULL) ? (event_t *) - 1 : (event_t *)(wrkEvent->nextEvent - _events); +	} +	freeIndex = (_freeEvent == 0) ? -1 : _freeEvent - _events; +	headIndex = (_headEvent == 0) ? -1 : _headEvent - _events; +	tailIndex = (_tailEvent == 0) ? -1 : _tailEvent - _events; + +	f->write(&curTime,   sizeof(curTime)); +	f->write(&freeIndex, sizeof(freeIndex)); +	f->write(&headIndex, sizeof(headIndex)); +	f->write(&tailIndex, sizeof(tailIndex)); +	f->write(saveEvents_, sizeof(saveEvents_)); +} + +// Restore the event list from file with handle f +void Scheduler::restoreEvents(Common::SeekableReadStream *f) { +	uint32   curTime, saveTime; +	event_t *wrkEvent;                              // Event ptr +	event_t  savedEvents[kMaxEvents];               // Convert event ptrs to indexes +	int16    freeIndex;                             // Free list index +	int16    headIndex;                             // Head of list index +	int16    tailIndex;                             // Tail of list index + +	debugC(1, kDebugSchedule, "restoreEvents"); + +	f->read(&saveTime,  sizeof(saveTime));          // time of save +	f->read(&freeIndex, sizeof(freeIndex)); +	f->read(&headIndex, sizeof(headIndex)); +	f->read(&tailIndex, sizeof(tailIndex)); +	f->read(savedEvents, sizeof(savedEvents)); + +	// Restore events indexes to pointers +	for (int i = 0; i < kMaxEvents; i++) { +		wrkEvent = &savedEvents[i]; +		_events[i] = *wrkEvent; +		_events[i].prevEvent = (wrkEvent->prevEvent == (event_t *) - 1) ? (event_t *)0 : &_events[(size_t)wrkEvent->prevEvent ]; +		_events[i].nextEvent = (wrkEvent->nextEvent == (event_t *) - 1) ? (event_t *)0 : &_events[(size_t)wrkEvent->nextEvent ]; +	} +	_freeEvent = (freeIndex == -1) ? NULL : &_events[freeIndex]; +	_headEvent = (headIndex == -1) ? NULL : &_events[headIndex]; +	_tailEvent = (tailIndex == -1) ? NULL : &_events[tailIndex]; + +	// Adjust times to fit our time +	curTime = getTicks(); +	wrkEvent = _headEvent;                              // The earliest event +	while (wrkEvent) {                              // While mature events found +		wrkEvent->time = wrkEvent->time - saveTime + curTime; +		wrkEvent = wrkEvent->nextEvent; +	} +} + +void Scheduler::restoreScreen(int screenIndex) { +// Transition to a new screen as follows: +//	1. Set the new screen (in the hero object and any carried objects) +//	2. Read in the screen files for the new screen +//	3. Initialise prompt line and status line + +	debugC(1, kDebugSchedule, "restoreScreen(%d)", screenIndex); + +	// 1. Set the new screen in the hero object and any being carried +	_vm.setNewScreen(screenIndex); + +	// 2. Read in new screen files +	_vm.readScreenFiles(screenIndex); + +	// 3. Initialise prompt line and status line +	_vm.initNewScreenDisplay(); +} + +void Scheduler::swapImages(int objNumb1, int objNumb2) { +// Swap all the images of one object with another.  Set hero_image (we make +// the assumption for now that the first obj is always the HERO) to the object +// number of the swapped image +	seqList_t tmpSeqList[MAX_SEQUENCES]; + +	debugC(1, kDebugSchedule, "swapImages(%d, %d)", objNumb1, objNumb2); + +	_vm.file().saveSeq(&_vm._objects[objNumb1]); +	memcpy(tmpSeqList, _vm._objects[objNumb1].seqList, sizeof(seqList_t)); +	memcpy(_vm._objects[objNumb1].seqList, _vm._objects[objNumb2].seqList, sizeof(seqList_t)); +	memcpy(_vm._objects[objNumb2].seqList, tmpSeqList, sizeof(seqList_t)); +	_vm.file().restoreSeq(&_vm._objects[objNumb1]); +	_vm._objects[objNumb2].currImagePtr = _vm._objects[objNumb2].seqList[0].seqPtr; +	_vm._heroImage = (_vm._heroImage == HERO) ? objNumb2 : HERO; + +	// Make sure baseline stays constant +	_vm._objects[objNumb1].y += _vm._objects[objNumb2].currImagePtr->y2 - _vm._objects[objNumb1].currImagePtr->y2; +} + +} // end of namespace Hugo diff --git a/engines/hugo/schedule.h b/engines/hugo/schedule.h new file mode 100755 index 0000000000..e24bcc0c64 --- /dev/null +++ b/engines/hugo/schedule.h @@ -0,0 +1,85 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef HUGO_SCHEDULE_H +#define HUGO_SCHEDULE_H + +namespace Hugo { + +#define kMaxEvents     50                           /* Max events in event queue */ + +struct event_t { +	act            *action;                         /* Ptr to action to perform */ +	bool            localActionFl;                  /* TRUE if action is only for this screen */ +	uint32          time;                           /* (absolute) time to perform action */ +	struct event_t *prevEvent;                      /* Chain to previous event */ +	struct event_t *nextEvent;                      /* Chain to next event */ +}; + +class Scheduler { +public: +	Scheduler(HugoEngine &vm); + +	void   initEventQueue(); +	void   insertAction(act *action); +	void   insertActionList(uint16 actIndex); +	void   decodeString(char *line); +	void   runScheduler(); +	uint32 getTicks(); +	void   processBonus(int bonusIndex); +	void   newScreen(int screenIndex); +	void   restoreEvents(Common::SeekableReadStream *f); +	void   saveEvents(Common::WriteStream *f); +	void   restoreScreen(int screenIndex); +	void   swapImages(int objNumb1, int objNumb2); + +private: +	enum seqTextSchedule { +		kSsNoBackground = 0, +		kSsBadSaveGame  = 1 +	}; + +	HugoEngine &_vm; + +	event_t _events[kMaxEvents];                        /* Statically declare event structures */ + +	event_t *_freeEvent;                                /* Free list of event structures */ +	event_t *_headEvent;                                /* Head of list (earliest time) */ +	event_t *_tailEvent;                                /* Tail of list (latest time) */ + +	event_t *getQueue(); +	void     delQueue(event_t *curEvent); +	event_t *doAction(event_t *curEvent); +}; + +} // end of namespace Hugo +#endif //HUGO_SCHEDULE_H diff --git a/engines/hugo/sound.cpp b/engines/hugo/sound.cpp new file mode 100755 index 0000000000..3673bda7a6 --- /dev/null +++ b/engines/hugo/sound.cpp @@ -0,0 +1,189 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +/* sound.c - sound effects and music support */ + +#include "common/system.h" + +#include "sound/decoders/raw.h" +#include "sound/audiostream.h" + +#include "hugo/hugo.h" +#include "hugo/game.h" +#include "hugo/file.h" +#include "hugo/sound.h" + +namespace Hugo { + +uint16 SeqID;                                       // Device id of (MIDI) sequencer +uint16 SeqVolID;                                    // Low level id to set midi volume +uint16 WavID = 0;                                   // Device id of waveaudio + +//HWAVEOUT hwav;                                    // Handle of waveaudio +//LPWAVEHDR lphdr;                                  // WaveOut structure ptr + +SoundHandler::SoundHandler(HugoEngine &vm) : _vm(vm) { +} + +void SoundHandler::setMusicVolume() { +	/* Set the FM music volume from config.mvolume (0..100%) */ +	warning("STUB: setMusicVolume()"); + +	// uint32 dwVolume; +	// +	// if (config.music) { +	//  dwVolume = config.mvolume * 0xffffL / 100;  // Convert % to 0..0xffff +	//  dwVolume |= dwVolume << 16;                 // Set volume in both stereo words +	//  midiOutSetVolume(SeqVolID, dwVolume); +	// } +} + +void SoundHandler::stopSound() { +	/* Stop any sound that might be playing */ +	warning("STUB: stopSound()"); + +	// waveOutReset(hwav); +	// waveOutUnprepareHeader(hwav, lphdr, sizeof(WAVEHDR)); +} + +void SoundHandler::stopMusic() { +	/* Stop any tune that might be playing */ +	warning("STUB: stopMusic()"); +	//mciSendCommand(SeqID, MCI_CLOSE, MCI_WAIT, 0); +} + +void SoundHandler::toggleMusic() { +// Turn music on and off +	if (_config.musicFl) +		stopMusic(); +	_config.musicFl = !_config.musicFl; +	initSound(RESET); +} + +void SoundHandler::toggleSound() { +// Turn digitized sound on and off +	_config.soundFl = !_config.soundFl; +	initSound(RESET); +} + +void SoundHandler::playMIDI(sound_pt seq_p, uint16 size) { +// Write supplied midi data to a temp file for MCI interface +// If seq_p is NULL, delete temp file + +	warning("STUB: playMIDI()"); +} + + +void SoundHandler::playMusic(int16 tune) { +	/* Read a tune sequence from the sound database and start playing it */ +	sound_pt seqPtr;                                // Sequence data from file +	uint16 size;                                    // Size of sequence data + +	if (_config.musicFl) { +		_vm.getGameStatus().song = tune; +		seqPtr = _vm.file().getSound(tune, &size); +		playMIDI(seqPtr, size); +	} +} + + +void SoundHandler::playSound(int16 sound, stereo_t channel, byte priority) { +	/* Produce various sound effects on supplied stereo channel(s) */ +	/* Override currently playing sound only if lower or same priority */ + +	// uint32 dwVolume;                             // Left, right volume of sound +	sound_pt sound_p;                               // Sound data +	uint16 size;                                    // Size of data +	static byte curPriority = 0;                    // Priority of currently playing sound +	// +	/* Sound disabled */ +	if (!_config.soundFl || !_vm._mixer->isReady()) +		return; +	// +	// // See if last wave still playing - if so, check priority +	// if (waveOutUnprepareHeader(hwav, lphdr, sizeof(WAVEHDR)) == WAVERR_STILLPLAYING) +	//  if (priority < curPriority)                 // Don't override unless priority >= current +	//      return; +	//  else +	//      Stop_sound(); +	curPriority = priority; +	// +	/* Get sound data */ +	if ((sound_p = _vm.file().getSound(sound, &size)) == NULL) +		return; + +	Audio::AudioStream *stream = Audio::makeRawStream(sound_p, size, 11025, Audio::FLAG_UNSIGNED); +	_vm._mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, stream); + +} + +void SoundHandler::initSound(inst_t action) { +	/* Initialize for MCI sound and midi */ + +	warning("STUB: initSound()"); +} + +void SoundHandler::pauseSound(bool activeFl, int hTask) { +// Pause and restore music, sound on losing activity to hTask +// Don't stop music if we are parent of new task, i.e. WinHelp() +// or config.music_bkg is TRUE. + +//TODO: Is 'hTask' still useful ? + +	static bool firstFl = true; +	static bool musicFl, soundFl; + +	if (firstFl) { +		firstFl = false; +		musicFl = _config.musicFl; +		soundFl = _config.soundFl; +	} + +	// Kill or restore music, sound +	if (activeFl) { // Remember states, reset WinHelp flag +		_config.musicFl = musicFl; +		_config.soundFl = soundFl; +		_vm.getGameStatus().helpFl = false; +	} else {    // Store states and disable +		musicFl = _config.musicFl; +		soundFl = _config.soundFl; + +		// Don't disable music during WinHelp() or config.music_bkg +		if (!_vm.getGameStatus().helpFl && !_config.backgroundMusicFl) { +			_config.musicFl = false; +			_config.soundFl = false; +		} +	} +	initSound(RESET); +} + +} // end of namespace Hugo diff --git a/engines/hugo/sound.h b/engines/hugo/sound.h new file mode 100755 index 0000000000..a9136b99e1 --- /dev/null +++ b/engines/hugo/sound.h @@ -0,0 +1,63 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef HUGO_SOUND_H +#define HUGO_SOUND_H + +#include "sound/mixer.h" + +namespace Hugo { + +class SoundHandler { +public: +	SoundHandler(HugoEngine &vm); + +	void toggleMusic(); +	void toggleSound(); +	void setMusicVolume(); +	void playMusic(short tune); +	void playSound(short sound, stereo_t channel, byte priority); +	void initSound(inst_t action); + +private: +	HugoEngine &_vm; +	Audio::SoundHandle _soundHandle; + +	void stopSound(); +	void stopMusic(); +	void playMIDI(sound_pt seq_p, uint16 size); +	void pauseSound(bool activeFl, int hTask); + +}; + +} // end of namespace Hugo +#endif //HUGO_SOUND_H diff --git a/engines/hugo/util.cpp b/engines/hugo/util.cpp new file mode 100755 index 0000000000..f623583fd1 --- /dev/null +++ b/engines/hugo/util.cpp @@ -0,0 +1,215 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#include "common/system.h" +#include "gui/message.h" + +#include "hugo/game.h" +#include "hugo/hugo.h" +#include "hugo/util.h" +#include "hugo/sound.h" + +namespace Hugo { + +int Utils::firstBit(byte data) { +	/* Returns index (0 to 7) of first 1 in supplied byte, or 8 if not found */ +	int i; + +	if (!data) +		return(8); + +	for (i = 0; i < 8; i++) { +		if ((data << i) & 0x80) +			break; +	} + +	return(i); +} + +int Utils::lastBit(byte data) { +	/* Returns index (0 to 7) of last 1 in supplied byte, or 8 if not found */ +	int i; +	if (!data) +		return(8); + +	for (i = 7; i >= 0; i--) { +		if ((data << i) & 0x80) +			break; +	} + +	return(i); +} + +void Utils::reverseByte(byte *data) { +	/* Reverse the bit order in supplied byte */ +	byte maskIn = 0x80; +	byte maskOut = 0x01; +	byte result = 0; + +	for (byte i = 0; i < 8; i++, maskIn >>= 1, maskOut <<= 1) +		if (*data & maskIn) +			result |= maskOut; + +	*data = result; +} + +char *Utils::Box(box_t dismiss, const char *s, ...) { +	static char buffer[MAX_STRLEN + 1];             // Format text into this +	va_list marker; + +	if (!s) return(NULL);                           // NULL strings catered for + +	if (s[0] == '\0') +		return(NULL); + +	if (strlen(s) > MAX_STRLEN - 100) {             // Test length +		Warn(false, "String too big:\n%s", s); +		return(NULL); +	} + +	va_start(marker, s); +	vsprintf(buffer, s, marker);                    // Format string into buffer +	va_end(marker); + +	//Warn(false, "BOX: %s", buffer); +	int boxTime = strlen(buffer) * 30; +	GUI::TimedMessageDialog dialog(buffer, MAX(1500, boxTime)); +	dialog.runModal(); + +	// TODO: Some boxes (i.e. the combination code for the shed), needs to return an input. +	return buffer; +} + +void Utils::Warn(bool technote, const char *format, ...) { +	/* Warning handler.  Print supplied message and continue */ +	/* Arguments are same as printf */ +	/* technote TRUE if we are to refer user to technote file */ +	char buffer[WARNLEN]; +	bool soundFl = _config.soundFl; +	va_list marker; + +	_config.soundFl = false;                            // Kill sound to allow beep sound +	HugoEngine::get().sound().initSound(RESET); + +	va_start(marker, format); +	vsnprintf(buffer, WARNLEN, format, marker); +////    if (technote) +////        strcat      (buffer, sTech); +	//MessageBeep(MB_ICONEXCLAMATION); +	//MessageBox(hwnd, buffer, "HugoWin Warning", MB_OK | MB_ICONEXCLAMATION); +	warning("Hugo warning: %s", buffer); +	va_end(marker); + +	//sndPlaySound(NULL, 0);                        // Stop beep and restore sound + +	_config.soundFl = soundFl; +	HugoEngine::get().sound().initSound(RESET); +} + +void Utils::Error(int error_type, const char *format, ...) { +	/* Fatal error handler.  Reset environment, print error and exit */ +	/* Arguments are same as printf */ +	va_list marker; +	char buffer[ERRLEN + 1]; +	bool fatal = true;                              // Fatal error, else continue + +	switch (error_type) { +	case FILE_ERR: +//	case FONT_ERR: +		strcpy(buffer, HugoEngine::get()._textUtil[kErr1]); +		break; +	case WRITE_ERR: +		strcpy(buffer, HugoEngine::get()._textUtil[kErr2]); +		fatal = false;                              // Allow continuation +		break; +	case PCCH_ERR: +		strcpy(buffer, HugoEngine::get()._textUtil[kErr3]); +		break; +	case HEAP_ERR: +		strcpy(buffer, HugoEngine::get()._textUtil[kErr4]); +		break; +	case SOUND_ERR: +		strcpy(buffer, HugoEngine::get()._textUtil[kErr5]); +		break; +//	case TIMER_ERR: +//		strcpy(buffer, HugoEngine::get()._textUtil[kObsoleteErr1]); +//		break; +//	case VBX_ERR: +//		strcpy(buffer, HugoEngine::get()._textUtil[kObsoleteErr2]); +//		break; +	default: +		strcpy(buffer, HugoEngine::get()._textUtil[kErr6]); +		break; +	} + +	if (fatal) +		HugoEngine::get().shutdown();                                   // Restore any devices before exit + +	va_start(marker, format); +	snprintf(&buffer[strlen(buffer)], ERRLEN - strlen(buffer), format, marker); +	//MessageBeep(MB_ICONEXCLAMATION); +	//MessageBox(hwnd, buffer, "HugoWin Error", MB_OK | MB_ICONEXCLAMATION); +	warning("Hugo Error: %s", buffer); +	va_end(marker); + +	if (fatal) +		exit(1); +} + +void Utils::gameOverMsg(void) { +	// Print options for user when dead +	//MessageBox(hwnd, gameoverstring, "Be more careful next time!", MB_OK | MB_ICONINFORMATION); +	warning("STUB: Gameover_msg(): %s", HugoEngine::get()._textUtil[kGameOver]); +} + +#if 0 +// Strangerke: Useless? +void Utils::Debug_out(char *format, ...) { +	/* Write debug info to file */ +	static FILE *fp = NULL; +	va_list marker; + +	if (HugoEngine::get().getGameStatus().debugFl) { +		/* Create/truncate if first call, else append */ +		if ((fp = fopen("debug.txt", fp == NULL ? "w" : "a")) == NULL) { +			Error(WRITE_ERR, "debug.txt"); +			return; +		} + +		va_start(marker, format); +		vfprintf(fp, format, marker); +		va_end(marker); +		fclose(fp); +	} +} +#endif +} // end of namespace Hugo diff --git a/engines/hugo/util.h b/engines/hugo/util.h new file mode 100755 index 0000000000..aa58c5e2f1 --- /dev/null +++ b/engines/hugo/util.h @@ -0,0 +1,63 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +/* + * This code is based on original Hugo Trilogy source code + * + * Copyright (c) 1989-1995 David P. Gray + * + */ + +#ifndef HUGO_UTIL_H +#define HUGO_UTIL_H + +namespace Hugo { + +enum seqTextUtil { +	kTech     = 0, +	kErr1     = 1, +	kErr2     = 2, +	kErr3     = 3, +	kErr4     = 4, +	kErr5     = 5, +	kErr6     = 6, +	kGameOver = 7 +//	kObsoleteErr1 = 8, +//	kObsoleteErr2 = 9 +}; + +namespace Utils { +int      firstBit(byte data); +int      lastBit(byte data); +void     reverseByte(byte *data); +void     Warn(bool technote, const char *format, ...); +void     Error(int code, const char *format, ...); +void     gameOverMsg(); +//	void     Debug_out(char *format, ...); +char    *Box(box_t, const char *, ...); +} + +} // Namespace Hugo +#endif | 
