HUGO: Adding engine to the main tree

svn-id: r52137
This commit is contained in:
Eugene Sandulenko 2010-08-17 09:28:20 +00:00
parent e075f05395
commit 06960d33e1
46 changed files with 24896 additions and 1 deletions

View file

@ -233,6 +233,9 @@ DIST_FILES_ENGINEDATA=
ifdef ENABLE_DRASCULA ifdef ENABLE_DRASCULA
DIST_FILES_ENGINEDATA+=drascula.dat DIST_FILES_ENGINEDATA+=drascula.dat
endif endif
ifdef ENABLE_HUGO
DIST_FILES_ENGINEDATA+=hugo.dat
endif
ifdef ENABLE_KYRA ifdef ENABLE_KYRA
DIST_FILES_ENGINEDATA+=kyra.dat DIST_FILES_ENGINEDATA+=kyra.dat
endif endif

View file

@ -115,6 +115,9 @@ public:
#if PLUGIN_ENABLED_STATIC(GROOVIE) #if PLUGIN_ENABLED_STATIC(GROOVIE)
LINK_PLUGIN(GROOVIE) LINK_PLUGIN(GROOVIE)
#endif #endif
#if PLUGIN_ENABLED_STATIC(HUGO)
LINK_PLUGIN(HUGO)
#endif
#if PLUGIN_ENABLED_STATIC(KYRA) #if PLUGIN_ENABLED_STATIC(KYRA)
LINK_PLUGIN(KYRA) LINK_PLUGIN(KYRA)
#endif #endif

1
configure vendored
View file

@ -88,6 +88,7 @@ add_engine drascula "Drascula: The Vampire Strikes Back" yes
add_engine gob "Gobli*ns" yes add_engine gob "Gobli*ns" yes
add_engine groovie "Groovie" yes "groovie2" add_engine groovie "Groovie" yes "groovie2"
add_engine groovie2 "Groovie 2 games" no add_engine groovie2 "Groovie 2 games" no
add_engine hugo "Hugo Trilogy" no
add_engine kyra "Legend of Kyrandia" yes "lol" add_engine kyra "Legend of Kyrandia" yes "lol"
add_engine lol "Lands of Lore" no add_engine lol "Lands of Lore" no
add_engine lure "Lure of the Temptress" yes add_engine lure "Lure of the Temptress" yes

BIN
dists/engine-data/hugo.dat Executable file

Binary file not shown.

View file

@ -60,6 +60,11 @@ DEFINES += -DENABLE_GROOVIE2
endif endif
endif endif
ifdef ENABLE_HUGO
DEFINES += -DENABLE_HUGO=$(ENABLE_HUGO)
MODULES += engines/hugo
endif
ifdef ENABLE_KYRA ifdef ENABLE_KYRA
DEFINES += -DENABLE_KYRA=$(ENABLE_KYRA) DEFINES += -DENABLE_KYRA=$(ENABLE_KYRA)
MODULES += engines/kyra MODULES += engines/kyra

234
engines/hugo/detection.cpp Executable file
View file

@ -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

509
engines/hugo/display.cpp Executable file
View file

@ -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

109
engines/hugo/display.h Executable file
View file

@ -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

993
engines/hugo/engine.cpp Executable file
View file

@ -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

43
engines/hugo/engine.h Executable file
View file

@ -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

924
engines/hugo/file.cpp Executable file
View file

@ -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

86
engines/hugo/file.h Executable file
View file

@ -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

880
engines/hugo/game.h Executable file
View file

@ -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

55
engines/hugo/global.h Executable file
View file

@ -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

1446
engines/hugo/hugo.cpp Executable file

File diff suppressed because it is too large Load diff

310
engines/hugo/hugo.h Executable file
View file

@ -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

187
engines/hugo/intro.cpp Executable file
View file

@ -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

130
engines/hugo/intro.h Executable file
View file

@ -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

229
engines/hugo/inventory.cpp Executable file
View file

@ -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

55
engines/hugo/inventory.h Executable file
View file

@ -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

24
engines/hugo/module.mk Executable file
View file

@ -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

309
engines/hugo/mouse.cpp Executable file
View file

@ -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

53
engines/hugo/mouse.h Executable file
View file

@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 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

676
engines/hugo/parser.cpp Executable file
View file

@ -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

95
engines/hugo/parser.h Executable file
View file

@ -0,0 +1,95 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 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

509
engines/hugo/route.cpp Executable file
View file

@ -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

84
engines/hugo/route.h Executable file
View file

@ -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

677
engines/hugo/schedule.cpp Executable file
View file

@ -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

85
engines/hugo/schedule.h Executable file
View file

@ -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

189
engines/hugo/sound.cpp Executable file
View file

@ -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

63
engines/hugo/sound.h Executable file
View file

@ -0,0 +1,63 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 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

215
engines/hugo/util.cpp Executable file
View file

@ -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

63
engines/hugo/util.h Executable file
View file

@ -0,0 +1,63 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 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

View file

@ -30,7 +30,7 @@ convbdf
where SIZE is replaced by the desired font height. where SIZE is replaced by the desired font height.
create_drascula create_drascula (sev)
--------------- ---------------
Stores a lot of hardcoded data of Drascula in a data file, based on Stores a lot of hardcoded data of Drascula in a data file, based on
the game's original source code. This includes the game's character the game's original source code. This includes the game's character
@ -39,6 +39,10 @@ create_drascula
(mostly the dialog subtitles) in English, Spanish, German, French (mostly the dialog subtitles) in English, Spanish, German, French
and Italian. This tool is used to create the drascula.dat file. and Italian. This tool is used to create the drascula.dat file.
create_hugo (Strangerke)
-----------
Creates hugo.dat file which contains all kinds of static data contained
in original game executable.
create_kyradat (LordHoto, athrxx) create_kyradat (LordHoto, athrxx)
-------------- --------------

1269
tools/create_hugo/create_hugo.cpp Executable file

File diff suppressed because it is too large Load diff

555
tools/create_hugo/create_hugo.h Executable file
View file

@ -0,0 +1,555 @@
/* 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 CREATE_HUGO_H
#define CREATE_HUGO_H
#define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(x[0])))
#define DATAALIGNMENT 4
#define HUGO_DAT_VER_MAJ 0 // 1 byte
#define HUGO_DAT_VER_MIN 16 // 1 byte
typedef unsigned char uint8;
typedef unsigned char byte;
typedef unsigned short uint16;
typedef signed short int16;
// 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; // Index of the action list to carry out if a 'hit'
int16 viewx, viewy, direction; // Used in auto-route mode
};
struct target_t { // Secondary target for action
uint16 nounIndex; // Index of the noun
uint16 verbIndex; // Index of the verb
};
#define MAX_TARGET 12 // Max # secondary "MakeUseOf" targets
struct uses_t { // Define uses of certain objects
int16 objid; // Primary object
uint16 dataIndex; // Index of the string if no secondary object matches
target_t targets[MAX_TARGET]; // List of secondary targets
};
// 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; // Index of the verb
uint16 nounIndex; // Index of the noun
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;
struct cmd {
uint16 verbIndex; // Index of the verb
uint16 reqIndex; // Index of the list of required objects
uint16 textDataNoCarryIndex; // Index of the 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; // Index of the string if wrong state
uint16 textDataDoneIndex; // Index of the string if verb done
uint16 actIndex; // Index of the action list if verb done
};
struct seq_t { // Linked list of images
byte *imagePtr; // ptr to image
uint16 bytesPerLine8; // bytes per line (8 bits)
uint16 lines; // lines
uint16 x1, x2, y1, y2; // Offsets from x,y: data bounding box
seq_t *nextSeqPtr; // ptr to next record
};
struct seqList_t {
uint16 imageNbr; // Number of images in sequence
seq_t *seqPtr; // Ptr to sequence structure
};
#define MAX_SEQUENCES 4 // Number of sequences of images in object
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; // Velocity (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 curSeqNumb; // Save which seq number currently in use
byte curImageNumb; // Save which image of sequence currently in use
char oldvx; // Previous vx (used in wandering)
char oldvy; // Previous vy
};
struct act0 { // Type 0 - Schedule
byte actType; // The type of action
int timer; // Time to set off the action
uint16 actIndex; // Index of an action list
};
struct act1 { // Type 1 - Start an object
byte 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
byte 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
byte 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; // Index of the action list if success
uint16 actFailIndex; // Index of the action list if failure
bool encoded; // (HUGO 1 DOS ONLY) Whether response is encoded or not
};
struct act4 { // Type 4 - Set new background color
byte actType; // The type of action
int timer; // Time to set off the action
long newBkgColor; // New color
};
struct act5 { // Type 5 - Initialise an object velocity
byte 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
byte 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
byte 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
byte 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
byte 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
byte 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
byte actType; // The type of action
int timer; // Time to set off the action
int objNumb; // The object number
byte stateReq; // Required state
uint16 actPassIndex; // Index of the action list if success
uint16 actFailIndex; // Index of the action list if failure
};
struct act12 { // Type 12 - Simple text box
byte 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
byte 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
byte atype; // 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; // Index of the action list if success
uint16 actFailIndex; // Index of the action list if failure
};
struct act15 { // Type 15 - Home in on an object
byte 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 vx, vy; // 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! t>0 is safe
struct act16 { // Type 16 - Set curr_seq_p to seq
byte 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
byte 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
byte 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
byte 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; // Index of the action list (all bits set)
uint16 actFailIndex; // Index of the action list (not all set)
};
struct act20 { // Type 20 - Remove all events with this type of action
byte actType; // The type of action
int timer; // Time to set off the action
byte actTypeDel; // The action type to remove
};
struct act21 { // Type 21 - Gameover. Disable hero & commands
byte actType; // The type of action
int timer; // Time to set off the action
};
struct act22 { // Type 22 - Initialise an object to hero's coords
byte 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
byte actType; // The type of action
int timer; // Time to set off the action
};
struct act24 { // Type 24 - Get bonus score
byte 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
byte 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; // Index of the action list if success
uint16 actFailIndex; // Index of the action list if failure
};
struct act26 { // Type 26 - Play a sound
byte 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
byte 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
byte 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
byte actType; // The type of action
int timer; // Time to set off the action
int objNumb; // The required object number
uint16 actPassIndex; // Index of the action list if success
uint16 actFailIndex; // Index of the action list if failure
};
struct act30 { // Type 30 - Start special maze processing
byte 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
byte actType; // The type of action
int timer; // Time to set off the action
};
struct act32 { // Type 32 - Init fbg field of object
byte 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
byte 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
byte actType; // The type of action
int timer; // Time to set off the action
uint16 actIndex; // Index of an action list
};
struct act35 { // Type 35 - Remappe palette
byte 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
byte actType; // The type of action
int timer; // Time to set off the action
uint16 nounIndex; // The required noun (list)
uint16 actPassIndex; // Index of the action list if success
uint16 actFailIndex; // Index of the action list if failure
};
struct act37 { // Type 37 - Set new screen state
byte 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
byte 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
byte 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
byte 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
byte 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
byte 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
byte actType; // The type of action
int timer; // Time to set off the action
int prompt; // Index of prompt string
uint16 actYesIndex; // Index of the action list if YES
uint16 actNoIndex; // Index of the action list if NO
};
struct act44 { // Type 44 - Stop any route in progress
byte actType; // The type of action
int timer; // Time to set off the action
};
struct act45 { // Type 45 - Conditional on route in progress
byte actType; // The type of action
int timer; // Time to set off the action
int routeIndex; // Must be >= current status.rindex
uint16 actPassIndex; // Index of the action list if en-route
uint16 actFailIndex; // Index of the action list if not
};
struct act46 { // Type 46 - Init status.jumpexit
byte 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
byte 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
byte 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)
byte actType; // The type of action
int timer; // Time to set off the action
uint16 songIndex; // Song 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;
};
typedef void *actListPtr; // Ptr to a list of actions
typedef actListPtr *actList; // A list of actions
void writeTextArray(FILE *outFile, char *textData[], int nbrText);
void writeUint16Array(FILE *outFile, uint16 *uint16Array[], int nbrElem);
void writeHotspot(FILE *outFile, hotspot_t hotspots[], int nbrElem);
void writeUseArray(FILE *outFile, uses_t uses[], int nbrElem);
void writeBackgroundArray(FILE *outFile, background_t background[], int nbrElem);
void writeCmdArray(FILE *outFile, cmd *cmdList[], int nbrElem);
void writeScreenActs(FILE *outFile, uint16 *screenActs[], int nbrElem);
void writeObjectArray(FILE *outFile, object_t objects[], int nbrElem);
void writeActListArray(FILE *outFile, actList actListArr[], int nbrElem);
#endif // CREATE_HUGO_H

1565
tools/create_hugo/enums.h Executable file

File diff suppressed because it is too large Load diff

10
tools/create_hugo/module.mk Executable file
View file

@ -0,0 +1,10 @@
MODULE := tools/create_hugo
MODULE_OBJS := \
create_hugo.o
# Set the name of the executable
TOOL_EXECUTABLE := create_hugo
# Include common rules
include $(srcdir)/rules.mk

11879
tools/create_hugo/staticdata.h Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
/* 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 STATICDISPLAY_H
#define STATICDISPLAY_H
#define SIZE_PAL_ARRAY 64
// Color table of standard 16 VGA colors
// Values from "Programmers guide to EGA/VGA cards" Ferraro, p303
#define V1 168 // Low intensity value
#define V2 255 // High intensity value
#define V3 87 // Special for Brown/Gray
#define V4 32 // De-saturate hi intensity
byte _palette[SIZE_PAL_ARRAY] = {
0, 0, 0, 0, // BLACK
0, 0, V1, 0, // BLUE
0, V1, 0, 0, // GREEN
0, V1, V1, 0, // CYAN
V1, 0, 0, 0, // RED
V1, 0, V1, 0, // MAGENTA
V1, V3, 0, 0, // BROWN
V1, V1, V1, 0, // WHITE (LIGHT GRAY)
V3, V3, V3, 0, // GRAY (DARK GRAY)
V4, V4, V2, 0, // LIGHTBLUE
V4, V2, V4, 0, // LIGHTGREEN
V4, V2, V2, 0, // LIGHTCYAN
V2, V4, V4, 0, // LIGHTRED
V2, V4, V2, 0, // LIGHTMAGENTA
V2, V2, V4, 0, // YELLOW
V2, V2, V2, 0 // BRIGHTWHITE
};
#endif

View file

@ -0,0 +1,52 @@
/* 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 STATICENGINE_H
#define STATICENGINE_H
#define NUM_ENGINE_TEXT 1
const char *textEngine[NUM_ENGINE_TEXT] = {
"Hugo and Penelope say:\n\n"
"We hope you liked our adventure and\n"
"hope to see you very soon in Hugo's\n"
"Mystery Adventure and Hugo's Amazon\n"
"Adventure. They are just like this\n"
"game but bigger and better!\n\n"
"Call 1-800-2424-PsL now to order the\n"
"Hugo Trilogy for Windows for only $36!\n"
"(Note: This number is for ORDERS only).\n\n"
"It includes all 3 games plus the 30-page\n"
"answer book. See Ordering information\n"
"for more details and some screenshots."
};
#endif

59
tools/create_hugo/staticintro.h Executable file
View file

@ -0,0 +1,59 @@
/* 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 STATICINTRO_H
#define STATICINTRO_H
#define NUM_INTRO_TEXT 3
#define NUM_INTRO_TICK 36
// We use intro_tick as an index into the following coordinate list for the plane path.
const byte x_intro[NUM_INTRO_TICK] = {
210, 204, 198, 192, 186, 180, 174, 168, 162, 156,
152, 149, 152, 158, 165, 171, 170, 165, 161, 157,
150, 144, 138, 134, 133, 134, 138, 144, 146, 142,
137, 132, 128, 124, 120, 115
};
const byte y_intro[NUM_INTRO_TICK] = {
61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
63, 66, 71, 74, 72, 75, 80, 82, 83, 84,
84, 84, 85, 89, 94, 99, 103, 104, 100, 98,
100, 103, 106, 109, 111, 112
};
const char *textIntro[NUM_INTRO_TEXT] = {
"Hugo and Penelope are returning\nhome from their vacation at the\ncottage of Great Uncle Horace.",
"Suddenly, a freak magnetic storm\ncauses the compass in their light\naircraft to spin wildly! Unable\nto navigate, Hugo loses all sense\nof direction...",
"Finally, hopelessly lost over a\nSouth American Jungle, the plane\nabout to run out of gas, Hugo\nspots a clearing just big enough\nto land it.\n\nWith fingers clenching the controls\nhe shouts: Hold on Penelope, we're\ngoing down...!"
};
#endif

43
tools/create_hugo/staticmouse.h Executable file
View file

@ -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 Trilogy source code
*
* Copyright (c) 1989-1995 David P. Gray
*
*/
#ifndef STATICMOUSE_H
#define STATICMOUSE_H
#define NUM_MOUSE_TEXT 2
const char *textMouse[NUM_MOUSE_TEXT] = {
"I don't know how to get there!",
"Exit"
};
#endif

View file

@ -0,0 +1,56 @@
/* 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 STATICPARSER_H
#define STATICPARSER_H
#define NUM_PARSER_TEXT 16
const char *textParser[NUM_PARSER_TEXT] = {
"You should press ALT+F4 or click on Game/Exit.",
"You are in a maze of\ntwisty little paths,\nwhich are all alike!",
"There's no point!",
"I don't fully understand.",
"I don't quite understand.",
"Eh?",
"You see nothing\nunusual about it.",
"You already have it.",
"It is of no use to you.",
"You don't have it.",
"No! You'll be needing it.",
"Ok.",
"You don't have any!",
"There aren't any!",
"I don't see any here!",
"You're not close enough!"
};
#endif //STATICPARSER_H

View file

@ -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 Trilogy source code
*
* Copyright (c) 1989-1995 David P. Gray
*
*/
#ifndef STATICSCHEDULE_H
#define STATICSCHEDULE_H
#define NUM_SCHEDULE_TEXT 2
const char *textSchedule[NUM_SCHEDULE_TEXT] = {
"Can't find background file!",
"Obsolete saved game format will be converted!"
};
#endif

51
tools/create_hugo/staticutil.h Executable file
View file

@ -0,0 +1,51 @@
/* 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 STATICUTIL_H
#define STATICUTIL_H
#define NUM_UTIL_TEXT 8
const char *textUtil[NUM_UTIL_TEXT] = {
"\n\nPlease read the supplied 'technote' file which may contain information on this problem.",
"File not found: ",
"Unable to write file.\nDisk full or perhaps read-only?\n",
"Bad data file format:\n",
"Insufficient memory to run game.\n",
"Sound missing from sound file:\n",
"An error has occurred.\n",
"I'm afraid all you can do at this point is:\n\n- Load a saved game (Ctrl+L)\n- Start a new game (Ctrl+N)\n- Quit! (Alt+F4)"
// "No timers available, try again later.\n",
// "Unable to find or load VBX file:\n"
};
#endif //STATICENGINE_H