diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp index 488e9866e1c..35c8054b020 100644 --- a/engines/agos/agos.cpp +++ b/engines/agos/agos.cpp @@ -182,6 +182,7 @@ AGOSEngine::AGOSEngine(OSystem *syst) _lastVgaTick = 0; _marks = 0; + _scanFlag = false; _scriptVar2 = 0; _runScriptReturn1 = 0; @@ -288,11 +289,14 @@ AGOSEngine::AGOSEngine(OSystem *syst) _firstTimeStruct = 0; _pendingDeleteTimeEvent = 0; + _initMouse = 0; _leftButtonDown = 0; + _mouseDown = 0; _rightButtonDown = 0; _clickOnly = 0; - _leftClick = 0; _oneClick = 0; + _leftClick = 0; + _rightClick = 0; _noRightClick = false; _leftButton = 0; @@ -321,11 +325,14 @@ AGOSEngine::AGOSEngine(OSystem *syst) _soundFileId = 0; _lastMusicPlayed = 0; _nextMusicToPlay = 0; + _sampleEnd = 0; + _sampleWait = 0; _showPreposition = 0; _showMessageFlag = 0; _newDirtyClip = false; + _wiped = false; _copyScnFlag = 0; _vgaSpriteChanged = 0; @@ -341,6 +348,7 @@ AGOSEngine::AGOSEngine(OSystem *syst) _curVgaFile1 = 0; _curVgaFile2 = 0; _curSfxFile = 0; + _curSfxFileSize = 0; _syncCount = 0; @@ -683,6 +691,14 @@ static const uint16 initialVideoWindows_Common[20] = { 3, 3, 14, 127, }; +static const uint16 initialVideoWindows_PN[20] = { + 3, 0, 14, 136, + 0, 0, 3, 136, + 17, 0, 3, 136, + 0, 0, 20, 200, + 3, 2, 14, 129, +}; + void AGOSEngine_PuzzlePack::setupGame() { gss = &puzzlepack_settings; _numVideoOpcodes = 85; @@ -836,6 +852,18 @@ void AGOSEngine_Elvira1::setupGame() { AGOSEngine::setupGame(); } +void AGOSEngine_PN::setupGame() { + gss = &simon1_settings; + _numVideoOpcodes = 57; + _vgaMemSize = 1000000; + _frameCount = 4; + _vgaBaseDelay = 1; + _vgaPeriod = 50; + _numVars = 256; + + AGOSEngine::setupGame(); +} + void AGOSEngine::setupGame() { allocItemHeap(); allocTablesHeap(); @@ -870,6 +898,8 @@ void AGOSEngine::setupGame() { for (int i = 0; i < 20; i++) { if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { _videoWindows[i] = initialVideoWindows_Simon[i]; + } else if (getGameType() == GType_PN) { + _videoWindows[i] = initialVideoWindows_PN[i]; } else { _videoWindows[i] = initialVideoWindows_Common[i]; } @@ -965,7 +995,7 @@ void AGOSEngine::pause() { while (_pause && !shouldQuit()) { delay(1); - if (_keyPressed.keycode == Common::KEYCODE_p) + if (_keyPressed.keycode == Common::KEYCODE_PAUSE) pauseEngine(false); } } @@ -1037,7 +1067,6 @@ uint32 AGOSEngine::getTime() const { return _system->getMillis() / 1000; } - void AGOSEngine::syncSoundSettings() { _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); diff --git a/engines/agos/agos.h b/engines/agos/agos.h index ebb4a66ae65..7ac65236712 100644 --- a/engines/agos/agos.h +++ b/engines/agos/agos.h @@ -31,6 +31,7 @@ #include "common/array.h" #include "common/keyboard.h" #include "common/rect.h" +#include "common/stack.h" #include "common/util.h" #include "agos/animation.h" @@ -38,6 +39,9 @@ #include "agos/sound.h" #include "agos/vga.h" +// TODO: Replace with more portable code +#include + namespace AGOS { /* Enable and set to zone number number to dump */ @@ -72,6 +76,9 @@ struct HitArea { Item *itemPtr; uint16 verb; uint16 priority; + + // Personal Nightmare specific + uint16 msg1, msg2; HitArea() { memset(this, 0, sizeof(*this)); } }; @@ -127,6 +134,7 @@ struct AnimTable { }; enum SIMONGameType { + GType_PN = 0, GType_ELVIRA1 = 1, GType_ELVIRA2 = 2, GType_WW = 3, @@ -167,7 +175,7 @@ class AGOSEngine : public Engine { // Engine APIs Common::Error init(); - Common::Error go(); + virtual Common::Error go(); virtual Common::Error run() { Common::Error err; err = init(); @@ -282,6 +290,7 @@ protected: uint32 _lastVgaTick; uint16 _marks; + bool _scanFlag; bool _scriptVar2; bool _runScriptReturn1; @@ -346,7 +355,7 @@ protected: int16 _scriptAdj1, _scriptAdj2; uint16 _curWindow; - WindowBlock *_textWindow; + WindowBlock *_inputWindow, *_textWindow; Item *_subjectItem, *_objectItem; Item *_currentPlayer; @@ -387,6 +396,7 @@ protected: TimeEvent *_firstTimeStruct, *_pendingDeleteTimeEvent; + bool _initMouse; Common::Point _mouse; Common::Point _mouseOld; @@ -401,8 +411,10 @@ protected: byte _leftButtonDown; byte _leftButton, _leftButtonCount, _leftButtonOld; + byte _mouseDown; byte _rightButtonDown; - bool _clickOnly, _leftClick, _oneClick; + bool _clickOnly, _oneClick; + bool _leftClick, _rightClick; bool _noRightClick; Item *_dummyItem1; @@ -429,11 +441,11 @@ protected: uint16 _soundFileId; int16 _lastMusicPlayed; int16 _nextMusicToPlay; - bool _showPreposition; bool _showMessageFlag; bool _newDirtyClip; + bool _wiped; uint16 _copyScnFlag, _vgaSpriteChanged; byte *_block, *_blockEnd; @@ -443,7 +455,6 @@ protected: byte *_curVgaFile1; byte *_curVgaFile2; - byte *_curSfxFile; uint16 _syncCount; @@ -503,10 +514,12 @@ protected: byte _stringReturnBuffer[2][180]; HitArea _hitAreas[250]; + HitArea *_hitAreaList; AnimTable _screenAnim1[90]; VgaPointersEntry _vgaBufferPointers[450]; VgaSprite _vgaSprites[200]; + VgaSleepStruct _onStopTable[60]; VgaSleepStruct _waitEndTable[60]; VgaSleepStruct _waitSyncTable[60]; @@ -585,6 +598,10 @@ public: AGOSEngine(OSystem *syst); virtual ~AGOSEngine(); + byte *_curSfxFile; + uint32 _curSfxFileSize; + uint16 _sampleEnd, _sampleWait; + protected: virtual uint16 to16Wrapper(uint value); virtual uint16 readUint16Wrapper(const void *src); @@ -598,17 +615,17 @@ protected: void readGamePcText(Common::SeekableReadStream *in); virtual void readItemChildren(Common::SeekableReadStream *in, Item *item, uint tmp); void readItemFromGamePc(Common::SeekableReadStream *in, Item *item); - void loadGamePcFile(); + virtual void loadGamePcFile(); void readGamePcFile(Common::SeekableReadStream *in); void decompressData(const char *srcName, byte *dst, uint32 offset, uint32 srcSize, uint32 dstSize); + void decompressPN(Common::Stack &dataList, uint8 *&dataOut, int &dataOutSize); void loadOffsets(const char *filename, int number, uint32 &file, uint32 &offset, uint32 &compressedSize, uint32 &size); - void loadSound(uint sound); - void loadSound(uint sound, int pan, int vol, uint type); + void loadSound(uint16 sound, int16 pan, int16 vol, uint16 type); + void loadSound(uint16 sound, uint16 freq, uint16 flags); void loadVoice(uint speechId); void loadSoundFile(const char *filename); - int getUserFlag(Item *item, int a); int getUserFlag1(Item *item, int a); int getUserItem(Item *item, int n); @@ -638,6 +655,7 @@ protected: /* used in debugger */ void dumpAllSubroutines(); + void dumpAllVgaFiles(); void dumpSubroutines(); void dumpSubroutine(Subroutine *sub); void dumpSubroutineLine(SubroutineLine *sl, Subroutine *sub); @@ -707,6 +725,8 @@ protected: bool isBoxDead(uint hitarea); void undefineBox(uint hitarea); void defineBox(int id, int x, int y, int width, int height, int flags, int verb, Item *itemPtr); + void defineBox(uint16 id, uint16 x, uint16 y, uint16 width, uint16 height, uint16 msg1, uint16 msg2, uint16 flags); + HitArea *findEmptyHitArea(); virtual void resetVerbs(); @@ -749,6 +769,7 @@ protected: virtual int weightOf(Item *x); void xPlace(Item *x, Item *y); + void restoreMenu(); void drawMenuStrip(uint windowNum, uint menuNum); void lightMenuStrip(int a); void unlightMenuStrip(); @@ -826,7 +847,7 @@ protected: void loadIconFile(); void loadMenuFile(); - bool processSpecialKeys(); + virtual bool processSpecialKeys(); void hitarea_stuff_helper(); void permitInput(); @@ -835,12 +856,13 @@ protected: void justifyStart(); void justifyOutPut(byte chr); - void loadZone(uint16 zoneNum); + void loadZone(uint16 zoneNum, bool useError = true); void animate(uint16 windowNum, uint16 zoneNum, uint16 vgaSpriteId, int16 x, int16 y, uint16 palette, bool vgaScript = false); void setImage(uint16 vgaSpriteId, bool vgaScript = false); - void setWindowImage(uint16 mode, uint16 vgaSpriteId); - void setWindowImageEx(uint16 mode, uint16 vgaSpriteId); + void setWindowImage(uint16 mode, uint16 vgaSpriteId, bool specialCase = false); + virtual void setWindowImageEx(uint16 mode, uint16 vgaSpriteId); + void drawEdging(); void skipSpeech(); @@ -908,6 +930,17 @@ public: void vc41_scrollLeft(); void vc42_delayIfNotEQ(); + // Video Script Opcodes, Personal Nightmare + void vc11_onStop(); + void vc36_pause(); + void vc39_volume(); + void vc44_enableBox(); + void vc45_disableBox(); + void vc46_maxBox(); + void vc48_specialEffect(); + void vc50_setBox(); + void vc55_scanFlag(); + // Video Script Opcodes, Elvira 1 void vc17_waitEnd(); void vc22_setPaletteOld(); @@ -1121,11 +1154,12 @@ protected: void clearVideoBackGround(uint16 windowNum, uint16 color); void setPaletteSlot(uint16 srcOffs, uint8 dstOffs); + void checkOnStopTable(); void checkWaitEndTable(); - bool ifObjectHere(uint16 val); - bool ifObjectAt(uint16 a, uint16 b); - bool ifObjectState(uint16 a, int16 b); + virtual bool ifObjectHere(uint16 val); + virtual bool ifObjectAt(uint16 a, uint16 b); + virtual bool ifObjectState(uint16 a, int16 b); bool isVgaQueueEmpty(); void haltAnimation(); @@ -1152,7 +1186,7 @@ protected: void colorBlock(WindowBlock *window, uint16 x, uint16 y, uint16 w, uint16 h); void restoreWindow(WindowBlock *window); - void restoreBlock(uint16 h, uint16 w, uint16 y, uint16 x); + void restoreBlock(uint16 x, uint16 y, uint16 w, uint16 h); byte *getBackBuf(); byte *getBackGround(); @@ -1162,7 +1196,7 @@ protected: bool decrunchFile(byte *src, byte *dst, uint32 size); void loadVGABeardFile(uint16 id); - void loadVGAVideoFile(uint16 id, uint8 type); + void loadVGAVideoFile(uint16 id, uint8 type, bool useError = true); bool loadVGASoundFile(uint16 id, uint8 type); void openGameFile(); @@ -1247,6 +1281,253 @@ protected: virtual char *genSaveName(int slot); }; +class AGOSEngine_PN : public AGOSEngine { + struct stackframe { + struct stackframe *nextframe; + int16 flag[6]; + int16 param[8]; + int16 classnum; + uint8 *linpos; + uint8 *lbase; + int16 ll; + int16 linenum; + int16 process; + jmp_buf *savearea; + stackframe() { memset(this, 0, sizeof(*this)); } + }; + + + virtual Common::Error go(); + void demoSeq(); + void introSeq(); + void setupBoxes(); +public: + AGOSEngine_PN(OSystem *system); + ~AGOSEngine_PN(); + + virtual void setupGame(); + virtual void setupOpcodes(); + virtual void setupVideoOpcodes(VgaOpcodeProc *op); + + virtual void executeOpcode(int opcode); + + int actCallD(int n); + + void opn_opcode00(); + void opn_opcode01(); + void opn_opcode02(); + void opn_opcode03(); + void opn_opcode04(); + void opn_opcode05(); + void opn_opcode06(); + void opn_opcode07(); + void opn_opcode08(); + void opn_opcode09(); + void opn_opcode10(); + void opn_opcode11(); + void opn_opcode12(); + void opn_opcode13(); + void opn_opcode14(); + void opn_opcode15(); + void opn_opcode16(); + void opn_opcode17(); + void opn_opcode18(); + void opn_opcode19(); + void opn_opcode20(); + void opn_opcode21(); + void opn_opcode22(); + void opn_opcode23(); + void opn_opcode24(); + void opn_opcode25(); + void opn_opcode26(); + void opn_opcode27(); + void opn_opcode28(); + void opn_opcode29(); + void opn_opcode30(); + void opn_opcode31(); + void opn_opcode32(); + void opn_opcode33(); + void opn_opcode34(); + void opn_opcode35(); + void opn_opcode36(); + void opn_opcode37(); + void opn_opcode38(); + void opn_opcode39(); + void opn_opcode40(); + void opn_opcode41(); + void opn_opcode42(); + void opn_opcode43(); + void opn_opcode44(); + void opn_opcode45(); + void opn_opcode46(); + void opn_opcode47(); + void opn_opcode48(); + void opn_opcode49(); + void opn_opcode50(); + void opn_opcode51(); + void opn_opcode52(); + void opn_opcode53(); + void opn_opcode54(); + void opn_opcode55(); + void opn_opcode56(); + void opn_opcode57(); + void opn_opcode62(); + void opn_opcode63(); + + // Video Script Opcodes, Personal Nightmare + void vc36_pause(); + + stackframe *_stackbase; + + byte *_dataBase, *_textBase; + uint32 _dataBaseSize, _textBaseSize; + + HitArea _invHitAreas[45]; + + char _buffer[80]; + char _inputline[61]; + char _saveFile[20]; + char _sb[80]; + uint8 _wordcp[7]; + + const char *_mouseString, *_mouseString1; + char _objectName1[15], _objectName2[15]; + char _inMessage[20]; + char _placeMessage[15]; + uint8 _inputReady; + uint8 _inputting; + uint16 _intputCounter, _inputMax; + uint16 _mousePrintFG; + HitArea *_dragStore; + uint8 _hitCalled; + + uint32 _quickptr[16]; + uint16 _quickshort[12]; + + bool _noScanFlag; + char _keyboardBuffer[61]; + + uint16 _objects; + int16 _objectCountS; + + int16 _bp; + int _xofs; + int16 _havinit; + uint16 _seed; + + char *_curwrdptr; + char *_inpp; + int _fnst; + int _procnum; + int _linct; + int _linembr; + uint8 *_linebase; + uint8 *_workptr; + jmp_buf *_cjmpbuff; + jmp_buf _loadfail; + + uint16 getptr(uint32 pos); + uint32 getlong(uint32 pos); + + virtual void loadGamePcFile(); + + int bitextract(uint32 ptr, int offs); + int doaction(); + int doline(int needsave); + int setposition(int process, int line); + int varval(); + + char *getMessage(char *msg, uint16 num); + void getResponse(uint16 charNum, uint16 objNum, uint16 &msgNum1, uint16 &msgNum2); + void getObjectName(char *v, uint16 x); + + void processor(); + void setbitf(uint32 ptr, int offs, int val); + void setqptrs(); + void writeval(uint8 *ptr, int val); + + void addstack(int type); + void dumpstack(); + void junkstack(); + void popstack(int type); + void funccpy(int *store); + void funcentry(int *storestore, int procn); + + int findentry(); + int findset(); + int gvwrd(uint8 *wptr, int mask); + int samewrd(uint8 *w1, uint8 *w2, int ln); + int wrdmatch(uint8 *word1, int mask1, uint8 *word2, int mask2); + + bool testContainer(uint16 a); + bool testObvious(uint16 a); + bool testSeen(uint16 a); + + bool ifObjectInInv(uint16 a); + int inventoryOn(int val); + int inventoryOff(); + void mouseHit(); + void execMouseHit(HitArea *ha); + void hitBox1(HitArea *ha); + void hitBox2(HitArea *ha); + void hitBox3(HitArea *ha); + void hitBox4(HitArea *ha); + void hitBox5(HitArea *ha); + void hitBox6(HitArea *ha); + void hitBox7(HitArea *ha); + void hitBox8(HitArea *ha); + void hitBox9(HitArea *ha); + void hitBox11(HitArea *ha); + + void drawIconHitBar(); + void iconPage(); + void printIcon(HitArea *ha, uint8 i, uint8 r); + + bool badload(int8 errorNum); + int loadfl(char *name); + int savfl(char *name); + void getFilename(); + void sysftodb(); + void dbtosysf(); + + uint32 ftext(uint32 base, int n); + char *unctok(char *c, int n); + void uncomstr(char *c, uint32 x); + void patok(int n); + void pcf(uint8 ch); + void pcl(const char *s); + void pmesd(int n); + void plocd(int n, int m); + void pobjd(int n, int m); + void ptext(uint32 tptr); + + virtual void clearVideoWindow(uint16 windowNum, uint16 color); + virtual void setWindowImageEx(uint16 mode, uint16 vga_res); + + virtual bool ifObjectHere(uint16 val); + virtual bool ifObjectAt(uint16 a, uint16 b); + virtual bool ifObjectState(uint16 a, int16 b); + + virtual void boxController(uint x, uint y, uint mode); + virtual void timerProc(); + + void addChar(uint8 chr); + void clearInputLine(); + void handleKeyboard(); + virtual void handleMouseMoved(); + void interact(char *buffer, uint8 size); + + virtual bool processSpecialKeys(); +protected: + typedef void (AGOSEngine_PN::*OpcodeProcPN) (); + struct OpcodeEntryPN { + OpcodeProcPN proc; + const char *desc; + }; + + const OpcodeEntryPN *_opcodesPN; +}; + class AGOSEngine_Elvira1 : public AGOSEngine { public: AGOSEngine_Elvira1(OSystem *system); @@ -1322,6 +1603,10 @@ protected: }; const OpcodeEntryElvira1 *_opcodesElvira1; + + virtual void drawIcon(WindowBlock *window, uint icon, uint x, uint y); + + virtual char *genSaveName(int slot); }; class AGOSEngine_Elvira2 : public AGOSEngine_Elvira1 { diff --git a/engines/agos/charset-fontdata.cpp b/engines/agos/charset-fontdata.cpp index 8d058c3bfce..6f26c566a41 100644 --- a/engines/agos/charset-fontdata.cpp +++ b/engines/agos/charset-fontdata.cpp @@ -1767,6 +1767,107 @@ static const byte english_elvira1Font[] = { 0x00, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, }; +static const byte english_pnFont[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, + 0x00, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x24, 0x7E, 0x24, 0x24, 0x7E, 0x24, 0x00, + 0x00, 0x08, 0x3E, 0x28, 0x3E, 0x0A, 0x3E, 0x08, + 0x00, 0x62, 0x64, 0x08, 0x10, 0x26, 0x46, 0x00, + 0x00, 0x10, 0x28, 0x10, 0x2A, 0x44, 0x3A, 0x00, + 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x00, + 0x00, 0x20, 0x10, 0x10, 0x10, 0x10, 0x20, 0x00, + 0x00, 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, + 0x00, 0x3C, 0x46, 0x4A, 0x52, 0x62, 0x3C, 0x00, + 0x00, 0x18, 0x28, 0x08, 0x08, 0x08, 0x3E, 0x00, + 0x00, 0x3C, 0x42, 0x02, 0x3C, 0x40, 0x7E, 0x00, + 0x00, 0x3C, 0x42, 0x0C, 0x02, 0x42, 0x3C, 0x00, + 0x00, 0x08, 0x18, 0x28, 0x48, 0x7E, 0x08, 0x00, + 0x00, 0x7E, 0x40, 0x7C, 0x02, 0x42, 0x3C, 0x00, + 0x00, 0x3C, 0x40, 0x7C, 0x42, 0x42, 0x3C, 0x00, + 0x00, 0x7E, 0x02, 0x04, 0x08, 0x10, 0x10, 0x00, + 0x00, 0x3C, 0x42, 0x3C, 0x42, 0x42, 0x3C, 0x00, + 0x00, 0x3C, 0x42, 0x42, 0x3E, 0x02, 0x3C, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x10, 0x20, + 0x00, 0x00, 0x04, 0x08, 0x10, 0x08, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x08, 0x04, 0x08, 0x10, 0x00, + 0x00, 0x3C, 0x42, 0x04, 0x08, 0x00, 0x08, 0x00, + 0x00, 0x3C, 0x4A, 0x56, 0x5E, 0x40, 0x3C, 0x00, + 0x00, 0x3C, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x00, + 0x00, 0x7C, 0x42, 0x7C, 0x42, 0x42, 0x7C, 0x00, + 0x00, 0x3C, 0x42, 0x40, 0x40, 0x42, 0x3C, 0x00, + 0x00, 0x78, 0x44, 0x42, 0x42, 0x44, 0x78, 0x00, + 0x00, 0x7E, 0x40, 0x7C, 0x40, 0x40, 0x7E, 0x00, + 0x00, 0x7E, 0x40, 0x7C, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x3C, 0x42, 0x40, 0x4E, 0x42, 0x3C, 0x00, + 0x00, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x00, + 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, + 0x00, 0x02, 0x02, 0x02, 0x42, 0x42, 0x3C, 0x00, + 0x00, 0x44, 0x48, 0x70, 0x48, 0x44, 0x42, 0x00, + 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x00, + 0x00, 0x42, 0x66, 0x5A, 0x42, 0x42, 0x42, 0x00, + 0x00, 0x42, 0x62, 0x52, 0x4A, 0x46, 0x42, 0x00, + 0x00, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, + 0x00, 0x7C, 0x42, 0x42, 0x7C, 0x40, 0x40, 0x00, + 0x00, 0x3C, 0x42, 0x42, 0x52, 0x4A, 0x3C, 0x00, + 0x00, 0x7C, 0x42, 0x42, 0x7C, 0x44, 0x42, 0x00, + 0x00, 0x3C, 0x40, 0x3C, 0x02, 0x42, 0x3C, 0x00, + 0x00, 0xFE, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, + 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, + 0x00, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, + 0x00, 0x42, 0x42, 0x42, 0x42, 0x5A, 0x24, 0x00, + 0x00, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x00, + 0x00, 0x82, 0x44, 0x28, 0x10, 0x10, 0x10, 0x00, + 0x00, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x00, + 0x00, 0x0E, 0x08, 0x08, 0x08, 0x08, 0x0E, 0x00, + 0x00, 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, + 0x00, 0x70, 0x10, 0x10, 0x10, 0x10, 0x70, 0x00, + 0x00, 0x10, 0x38, 0x54, 0x10, 0x10, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x1C, 0x22, 0x78, 0x20, 0x20, 0x7E, 0x00, + 0x00, 0x00, 0x38, 0x04, 0x3C, 0x44, 0x3C, 0x00, + 0x00, 0x20, 0x20, 0x3C, 0x22, 0x22, 0x3C, 0x00, + 0x00, 0x00, 0x1C, 0x20, 0x20, 0x20, 0x1C, 0x00, + 0x00, 0x04, 0x04, 0x3C, 0x44, 0x44, 0x3C, 0x00, + 0x00, 0x00, 0x38, 0x44, 0x78, 0x40, 0x3C, 0x00, + 0x00, 0x0C, 0x10, 0x18, 0x10, 0x10, 0x10, 0x00, + 0x00, 0x00, 0x3C, 0x44, 0x44, 0x3C, 0x04, 0x38, + 0x00, 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x00, + 0x00, 0x10, 0x00, 0x30, 0x10, 0x10, 0x38, 0x00, + 0x00, 0x04, 0x00, 0x04, 0x04, 0x04, 0x24, 0x18, + 0x00, 0x20, 0x28, 0x30, 0x30, 0x28, 0x24, 0x00, + 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x00, + 0x00, 0x00, 0x68, 0x54, 0x54, 0x54, 0x54, 0x00, + 0x00, 0x00, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00, + 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, + 0x00, 0x00, 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, + 0x00, 0x00, 0x3C, 0x44, 0x44, 0x3C, 0x04, 0x06, + 0x00, 0x00, 0x1C, 0x20, 0x20, 0x20, 0x20, 0x00, + 0x00, 0x00, 0x38, 0x40, 0x38, 0x04, 0x78, 0x00, + 0x00, 0x10, 0x38, 0x10, 0x10, 0x10, 0x0C, 0x00, + 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, + 0x00, 0x00, 0x44, 0x44, 0x28, 0x28, 0x10, 0x00, + 0x00, 0x00, 0x44, 0x54, 0x54, 0x54, 0x28, 0x00, + 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, + 0x00, 0x00, 0x44, 0x44, 0x44, 0x3C, 0x04, 0x38, + 0x00, 0x00, 0x7C, 0x08, 0x10, 0x20, 0x7C, 0x00, + 0x00, 0x0E, 0x08, 0x30, 0x08, 0x08, 0x0E, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, + 0x00, 0x70, 0x10, 0x0C, 0x10, 0x10, 0x70, 0x00, + 0x00, 0x14, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3C, 0x42, 0x99, 0xA1, 0xA1, 0x99, 0x42, 0x3C, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + void AGOSEngine::windowDrawChar(WindowBlock *window, uint x, uint y, byte chr) { const byte *src; byte color, *dst; @@ -1851,6 +1952,12 @@ void AGOSEngine::windowDrawChar(WindowBlock *window, uint x, uint y, byte chr) { w = 6; src = english_elvira1Font + (chr - 32) * 8; + } else { + dst = (byte *)screen->pixels + y * _dxSurfacePitch + x + window->textColumnOffset; + h = 8; + w = 8; + + src = english_pnFont + (chr - 32) * 8; } color = window->textColor; diff --git a/engines/agos/charset.cpp b/engines/agos/charset.cpp index 8acfecb1ce1..3f38e17ea19 100644 --- a/engines/agos/charset.cpp +++ b/engines/agos/charset.cpp @@ -610,13 +610,21 @@ void AGOSEngine::windowNewLine(WindowBlock *window) { window->textColumnOffset = (getGameType() == GType_ELVIRA2) ? 4 : 0; window->textLength = 0; - if (window->textRow == window->height) { - if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || - getGameType() == GType_WW) { + if (getGameType() == GType_PN) { + window->textRow++; + if (window->textRow == window->height) { windowScroll(window); + window->textRow--; } } else { - window->textRow++; + if (window->textRow == window->height) { + if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || + getGameType() == GType_WW) { + windowScroll(window); + } + } else { + window->textRow++; + } } } diff --git a/engines/agos/cursor.cpp b/engines/agos/cursor.cpp index d8c5793ba21..d0481e8f5a1 100644 --- a/engines/agos/cursor.cpp +++ b/engines/agos/cursor.cpp @@ -474,6 +474,71 @@ get_out: _litBoxFlag = 0; } +void AGOSEngine_PN::handleMouseMoved() { + if (_mouseHideCount) { + CursorMan.showMouse(false); + return; + } + + CursorMan.showMouse(true); + _mouse = _eventMan->getMousePos(); + + if (_leftClick == true) { + _leftClick = false; + if (_dragFlag != 0) { + _hitCalled = 4; + } else if (_lockWord & 0x10) { + if (_oneClick != 0) { + _hitCalled = 2; + _oneClick = 0; + } else { + _oneClick++; + } + } else { + _hitCalled = 1; + } + _mouseDown = 0; + } + + if (_rightClick == true) { + _rightClick = false; + if (_hitCalled == 0) + _hitCalled = 5; + } + + if (_mouse != _mouseOld) + _needHitAreaRecalc++; + + if (_leftButton != 0) { + if (_mouseDown <= 20) { + _mouseDown++; + if (_mouseDown > 20) { + if (_lockWord & 0x10) { + if (_oneClick == 0) + _hitCalled = 3; + } else { + _hitCalled = 3; + } + } + } + } else if ((_lockWord & 0x10) && _oneClick != 0) { + _oneClick++; + if (_oneClick < 10) { + _hitCalled = 1; + _oneClick = 0; + } + } + + if (!_wiped) + boxController(_mouse.x, _mouse.y, 0); + + _mouseOld = _mouse; + drawMousePointer(); + + _needHitAreaRecalc = 0; + _litBoxFlag = 0; +} + void AGOSEngine::handleMouseMoved() { uint x; @@ -721,8 +786,15 @@ void AGOSEngine::drawMousePointer() { } else { const uint16 *src; int i, j; + uint8 color; - const uint8 color = (getGameType() == GType_ELVIRA1) ? 15: 65; + if (getGameType() == GType_PN) { + color = (getPlatform() == Common::kPlatformPC) ? 15 : 14; + } else if (getGameType() == GType_ELVIRA1) { + color = 15; + } else { + color = 65; + } memset(_mouseData, 0xFF, _maxCursorWidth * _maxCursorHeight); if (getGameType() == GType_WW) { diff --git a/engines/agos/debug.cpp b/engines/agos/debug.cpp index 069af828c3a..0959d48e071 100644 --- a/engines/agos/debug.cpp +++ b/engines/agos/debug.cpp @@ -231,8 +231,10 @@ void AGOSEngine::dumpVideoScript(const byte *src, bool singeOpcode) { strn = str = simon1_videoOpcodeNameTable[opcode]; } else if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) { strn = str = ww_videoOpcodeNameTable[opcode]; - } else { + } else if (getGameType() == GType_ELVIRA1) { strn = str = elvira1_videoOpcodeNameTable[opcode]; + } else { + strn = str = pn_videoOpcodeNameTable[opcode]; } if (strn == NULL) { @@ -295,6 +297,24 @@ void AGOSEngine::dumpVgaScriptAlways(const byte *ptr, uint16 res, uint16 id) { printf("; end\n"); } +void AGOSEngine::dumpAllVgaFiles() { + uint8 start = (getGameType() == GType_PN) ? 0 : 2; + uint8 end = (getGameType() == GType_PN) ? 26 : 450; + + for (int f = start; f < end; f++) { + uint16 zoneNum = (getGameType() == GType_PN) ? 0 : f; + loadZone(f, false); + + VgaPointersEntry *vpe = &_vgaBufferPointers[zoneNum]; + if (vpe->vgaFile1 != NULL) { + _curVgaFile1 = vpe->vgaFile1; + dumpVgaFile(_curVgaFile1); + } + } + + error("Complete"); +} + void AGOSEngine_Feeble::dumpVgaFile(const byte *vga) { const byte *pp; const byte *p; diff --git a/engines/agos/debug.h b/engines/agos/debug.h index e2f05590578..375878a4bcd 100644 --- a/engines/agos/debug.h +++ b/engines/agos/debug.h @@ -2324,6 +2324,79 @@ static const char *const puzzlepack_opcodeNameTable[256] = { "BBBB|SET_COLOR", }; +const char *const pn_videoOpcodeNameTable[] = { + /* 0 */ + "x|RET", + "ddd|FADEOUT", + "d|CALL", + "ddddd|NEW_SPRITE", + /* 4 */ + "ddd|FADEIN", + "vdj|IF_EQUAL", + "dj|IF_OBJECT_HERE", + "dj|IF_OBJECT_NOT_HERE", + /* 8 */ + "ddj|IF_OBJECT_IS_AT", + "ddj|IF_OBJECT_STATE_IS", + "dddd|DRAW", + "d|ON_STOP", + /* 12 */ + "|TEST_STOP", + "d|DELAY", + "d|SET_SPRITE_OFFSET_X", + "d|SET_SPRITE_OFFSET_Y", + /* 16 */ + "|SYNC", + "d|WAIT_SYNC", + "d|WAIT_END", + "i|JUMP_REL", + /* 20 */ + "|CHAIN_TO", + "dd|SET_REPEAT", + "i|END_REPEAT", + "d|SET_PALETTE", + /* 24 */ + "d|SET_PRIORITY", + "diid|SET_SPRITE_XY", + "x|HALT_SPRITE", + "ddddd|SET_WINDOW", + /* 28 */ + "|RESET", + "dddd|PLAY_SOUND", + "|STOP_ALL_SOUNDS", + "d|SET_FRAME_RATE", + /* 32 */ + "d|SET_WINDOW", + "|SAVE_SCREEN", + "|MOUSE_ON", + "|MOUSE_OFF", + /* 36 */ + "|PAUSE", + "d|VC_37", + "dd|CLEAR_WINDOW", + "d|SET_VOLUME", + /* 40 */ + "dd|SET_WINDOW_IMAGE", + "dd|POKE_PALETTE", + "|VC_42", + "|VC_43", + /* 44 */ + "d|ENABLE_BOX", + "d|DISABLE_BOX", + "d|MAX_BOX", + "dd|VC_47", + /* 48 */ + "dd|SPEC_EFFECT", + "|VC_49", + "ddddddddd|SET_BOX", + "v|IF_VAR_NOT_ZERO", + /* 52 */ + "vd|SET_VAR", + "vd|ADD_VAR", + "vd|SUB_VAR", + "|SCAN_FLAGS", +}; + const char *const elvira1_videoOpcodeNameTable[] = { /* 0 */ "x|RET", diff --git a/engines/agos/detection.cpp b/engines/agos/detection.cpp index 07365c1b032..59f603a9057 100644 --- a/engines/agos/detection.cpp +++ b/engines/agos/detection.cpp @@ -65,6 +65,7 @@ static const ADObsoleteGameID obsoleteGameIDsTable[] = { }; static const PlainGameDescriptor simonGames[] = { + {"pn", "Personal Nightmare"}, {"elvira1", "Elvira - Mistress of the Dark"}, {"elvira2", "Elvira II - The Jaws of Cerberus"}, {"waxworks", "Waxworks"}, @@ -132,6 +133,9 @@ bool AgosMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGame bool res = true; switch (gd->gameType) { + case AGOS::GType_PN: + *engine = new AGOS::AGOSEngine_PN(syst); + break; case AGOS::GType_ELVIRA1: *engine = new AGOS::AGOSEngine_Elvira1(syst); break; diff --git a/engines/agos/detection_tables.h b/engines/agos/detection_tables.h index df8175ac8b7..43c225697d5 100644 --- a/engines/agos/detection_tables.h +++ b/engines/agos/detection_tables.h @@ -26,6 +26,93 @@ namespace AGOS { static const AGOSGameDescription gameDescriptions[] = { + // Personal Nightmare 1.1 - English Amiga + { + { + "pn", + "Floppy", + + { + { "icon.tmp", GAME_ICONFILE, "cd94091218ac2c46918fd3c0cbd81d5e", -1}, + { "night.dbm", GAME_BASEFILE, "712c445d8e938956403a759978eab01b", -1}, + { "night.txt", GAME_TEXTFILE, "52630ad100f473a2cdc7c699536d6730", -1}, + { NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformAmiga, + ADGF_NO_FLAGS + }, + + GType_PN, + GID_PN, + GF_OLD_BUNDLE | GF_CRUNCHED | GF_PLANAR + }, + + // Personal Nightmare - English Atari ST Floppy Demo + { + { + "pn", + "Demo", + + { + { "01.IN", 0, "23a4c8c4c9ac460fee7281080b5274e3", 756}, + { "02.IN", 0, "31be87808826538f0c0caebd5fedd48f", 73100}, + { "03.IN", 0, "0e125f3df4e4b800936ebdcc8dc96060", 101664}, + { NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformAtariST, + ADGF_DEMO + }, + + GType_PN, + GID_PN, + GF_OLD_BUNDLE | GF_CRUNCHED | GF_PLANAR | GF_DEMO + }, + + // Personal Nightmare 1.1 - English AtariST Floppy + { + { + "pn", + "Floppy", + + { + { "night.dbm", GAME_BASEFILE, "712c445d8e938956403a759978eab01b", -1}, + { "night.txt", GAME_TEXTFILE, "52630ad100f473a2cdc7c699536d6730", -1}, + { NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformAtariST, + ADGF_NO_FLAGS + }, + + GType_PN, + GID_PN, + GF_OLD_BUNDLE | GF_CRUNCHED | GF_PLANAR + }, + + // Personal Nightmare 1.1c - English DOS Floppy + { + { + "pn", + "Floppy", + + { + { "icon.out", GAME_ICONFILE, "40d8347c3154bfa8b642d6860a4b9481", -1}, + { "night.dbm", GAME_BASEFILE, "177311ae059243f6a2740e950585d786", -1}, + { "night.txt", GAME_TEXTFILE, "861fc1fa0864eef585f5865dee52e325", -1}, + { NULL, 0, NULL, 0} + }, + Common::EN_ANY, + Common::kPlatformPC, + ADGF_NO_FLAGS + }, + + GType_PN, + GID_PN, + GF_OLD_BUNDLE | GF_CRUNCHED | GF_PLANAR + }, + // Elvira 1 - English Amiga Floppy Demo { { diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp index 1ff2f9245cd..248e9ee6c79 100644 --- a/engines/agos/event.cpp +++ b/engines/agos/event.cpp @@ -185,14 +185,13 @@ void AGOSEngine::restartAnimation() { if (!(_lockWord & 0x10)) return; - _window4Flag = 2; - - setMoveRect(0, 0, 224, 127); - displayScreen(); + if (getGameType() != GType_PN) { + _window4Flag = 2; + setMoveRect(0, 0, 224, 127); + displayScreen(); + } _lockWord &= ~0x10; - - // Check picture queue } void AGOSEngine::addVgaEvent(uint16 num, uint8 type, const byte *codePtr, uint16 curSprite, uint16 curZoneNum) { @@ -520,6 +519,9 @@ void AGOSEngine::delay(uint amount) { setBitFlag(92, false); _rightButtonDown++; break; + case Common::EVENT_RBUTTONUP: + _rightClick = true; + break; case Common::EVENT_RTL: case Common::EVENT_QUIT: return; @@ -611,6 +613,45 @@ void AGOSEngine_Feeble::timerProc() { _lockWord &= ~2; } +void AGOSEngine_PN::timerProc() { + if (_lockWord & 0x80E9 || _lockWord & 2) + return; + + _syncCount++; + + _lockWord |= 2; + + _sound->handleSound(); + handleMouseMoved(); + handleKeyboard(); + + if (!(_lockWord & 0x10)) { + if (_sampleWait) { + _vgaCurSpriteId = 0xFFFF; + vc15_sync(); + _sampleWait = false; + } + if (_sampleEnd) { + _vgaCurSpriteId = 0xFFFE; + vc15_sync(); + _sampleEnd = false; + } + + processVgaEvents(); + processVgaEvents(); + _cepeFlag ^= 1; + if (!_cepeFlag) + processVgaEvents(); + } + + if (_displayScreen) { + displayScreen(); + _displayScreen = false; + } + + _lockWord &= ~2; +} + void AGOSEngine::timerProc() { if (_lockWord & 0x80E9 || _lockWord & 2) return; diff --git a/engines/agos/gfx.cpp b/engines/agos/gfx.cpp index 95abbc0425f..98df66b021e 100644 --- a/engines/agos/gfx.cpp +++ b/engines/agos/gfx.cpp @@ -928,6 +928,12 @@ void AGOSEngine::drawImage(VC10_state *state) { _window4Flag = 1; } + } else { + state->surf_addr = (byte *)screen->pixels; + state->surf_pitch = _screenWidth; + + xoffs = (vlut[0] * 2 + state->x) * 8; + yoffs = vlut[1] + state->y; } state->surf_addr += xoffs + yoffs * state->surf_pitch; @@ -1057,7 +1063,7 @@ void AGOSEngine::animate(uint16 windowNum, uint16 zoneNum, uint16 vgaSpriteId, i vsp->y = y; vsp->x = x; vsp->image = 0; - if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) + if (getGameType() == GType_PN || getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) vsp->palette = 0; else vsp->palette = palette; @@ -1168,7 +1174,7 @@ void AGOSEngine::setImage(uint16 vgaSpriteId, bool vgaScript) { uint16 count; const byte *vc_ptr_org; - zoneNum = vgaSpriteId / 100; + zoneNum = (getGameType() == GType_PN) ? 0 : vgaSpriteId / 100; for (;;) { vpe = &_vgaBufferPointers[zoneNum]; @@ -1185,6 +1191,7 @@ void AGOSEngine::setImage(uint16 vgaSpriteId, bool vgaScript) { _noOverWrite = 0xFFFF; } else { _curSfxFile = vpe->sfxFile; + _curSfxFileSize = vpe->sfxFileEnd - vpe->sfxFile; _zoneNumber = zoneNum; if (vpe->vgaFile1 != NULL) @@ -1234,8 +1241,17 @@ void AGOSEngine::setImage(uint16 vgaSpriteId, bool vgaScript) { } assert(READ_BE_UINT16(&((ImageHeader_WW *) b)->id) == vgaSpriteId); - if (!vgaScript) - clearVideoWindow(_windowNum, READ_BE_UINT16(&((ImageHeader_WW *) b)->color)); + if (!vgaScript) { + uint16 color = READ_BE_UINT16(&((ImageHeader_WW *) b)->color); + if (getGameType() == GType_PN) { + if (color & 0x80) + _wiped = true; + else if (_wiped == true) + restoreMenu(); + color &= 0xFF7F; + } + clearVideoWindow(_windowNum, color); + } } if (_dumpVgaScripts) { @@ -1262,6 +1278,14 @@ void AGOSEngine::setImage(uint16 vgaSpriteId, bool vgaScript) { _vcPtr = vc_ptr_org; } +void AGOSEngine_PN::setWindowImageEx(uint16 mode, uint16 vga_res) { + if (!_initMouse) { + _initMouse = 1; + vc33_setMouseOn(); + } + setWindowImage(mode, vga_res); +} + void AGOSEngine::setWindowImageEx(uint16 mode, uint16 vgaSpriteId) { _window3Flag = 0; @@ -1299,7 +1323,7 @@ void AGOSEngine::setWindowImageEx(uint16 mode, uint16 vgaSpriteId) { } } -void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId) { +void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId, bool specialCase) { uint16 updateWindow; _windowNum = updateWindow = mode; @@ -1307,7 +1331,7 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId) { if (getGameType() == GType_FF || getGameType() == GType_PP) { vc27_resetSprite(); - } else { + } else if (!specialCase) { VgaTimerEntry *vte = _vgaTimerList; while (vte->type != ANIMATE_INT) vte++; @@ -1331,7 +1355,7 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId) { } } - setImage(vgaSpriteId); + setImage(vgaSpriteId, specialCase); if (getGameType() == GType_FF || getGameType() == GType_PP) { fillBackGroundFromBack(); @@ -1423,6 +1447,9 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId) { src = _window4BackScn; srcWidth = _videoWindows[18] * 16; } + } else { + src = (byte *)screen->pixels + xoffs + yoffs * _screenWidth; + srcWidth = _screenWidth; } _boxStarHeight = height; @@ -1433,7 +1460,14 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId) { src += srcWidth; } - if (getGameType() == GType_ELVIRA1 && updateWindow == 3 && _bottomPalette) { + if (getGameType() == GType_PN && !_wiped && !specialCase) { + uint8 color = (getPlatform() == Common::kPlatformPC) ? 7 : 15; + dst = (byte *)screen->pixels + 48; + memset(dst, color, 224); + + dst = (byte *)screen->pixels + 132 * _screenWidth + 48; + memset(dst, color, 224); + } else if (getGameType() == GType_ELVIRA1 && updateWindow == 3 && _bottomPalette) { dst = (byte *)screen->pixels + 133 * _screenWidth; int size = 67 * _screenWidth; @@ -1449,4 +1483,26 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId) { _lockWord &= ~0x20; } +// Personal Nightmare specific +void AGOSEngine::drawEdging() { + byte *dst; + uint8 color = (getPlatform() == Common::kPlatformPC) ? 7 : 15; + + Graphics::Surface *screen = _system->lockScreen(); + + dst = (byte *)screen->pixels + 136 * _screenWidth; + uint8 len = 52; + + while (len--) { + dst[0] = color; + dst[319] = color; + dst += _screenWidth; + } + + dst = (byte *)screen->pixels + 187 * _screenWidth; + memset(dst, color, _screenWidth); + + _system->unlockScreen(); +} + } // End of namespace AGOS diff --git a/engines/agos/icons.cpp b/engines/agos/icons.cpp index f668b6e0a5e..908b712ade3 100644 --- a/engines/agos/icons.cpp +++ b/engines/agos/icons.cpp @@ -302,7 +302,7 @@ void AGOSEngine_Elvira2::drawIcon(WindowBlock *window, uint icon, uint x, uint y _lockWord &= ~0x8000; } -void AGOSEngine::drawIcon(WindowBlock *window, uint icon, uint x, uint y) { +void AGOSEngine_Elvira1::drawIcon(WindowBlock *window, uint icon, uint x, uint y) { byte *dst; byte *src; @@ -329,6 +329,43 @@ void AGOSEngine::drawIcon(WindowBlock *window, uint icon, uint x, uint y) { _lockWord &= ~0x8000; } +void AGOSEngine::drawIcon(WindowBlock *window, uint icon, uint x, uint y) { + byte *dst; + byte *src; + + _lockWord |= 0x8000; + + Graphics::Surface *screen = _system->lockScreen(); + dst = (byte *)screen->pixels + y * _dxSurfacePitch + x * 8; + src = _iconFilePtr + icon * 146; + + if (icon == 0xFF) { + // Draw Blank Icon + for (int yp = 0; yp < 24; yp++) { + memset(dst, 0, 24); + dst += _dxSurfacePitch; + } + } else { + uint8 palette[4]; + palette[0] = *src >> 4; + palette[1] = *src++ & 0xf; + palette[2] = *src >> 4; + palette[3] = *src++ & 0xf; + for (int yp = 0; yp < 24; ++yp, src += 6) { + // Get bit-set representing the 24 pixels for the line + uint32 v1 = (READ_BE_UINT16(src) << 8) | *(src + 4); + uint32 v2 = (READ_BE_UINT16(src + 2) << 8) | *(src + 5); + for (int xp = 0; xp < 24; ++xp, v1 >>= 1, v2 >>= 1) { + dst[yp * _screenWidth + (23 - xp)] = palette[((v1 & 1) << 1) | (v2 & 1)]; + } + } + } + + _system->unlockScreen(); + + _lockWord &= ~0x8000; +} + void AGOSEngine_Feeble::drawIconArray(uint num, Item *itemRef, int line, int classMask) { Item *item_ptr_org = itemRef; WindowBlock *window; @@ -923,7 +960,7 @@ void AGOSEngine::drawArrow(uint16 x, uint16 y, int8 dir) { void AGOSEngine_Simon1::removeArrows(WindowBlock *window, uint num) { if (getGameType() == GType_SIMON1) { - restoreBlock(200, 320, 146, 304); + restoreBlock(304, 146, 320, 200); } } @@ -941,7 +978,7 @@ void AGOSEngine::removeArrows(WindowBlock *window, uint num) { if (num != 2) { uint y = window->height * 4 + window->y - 19; uint x = window->width + window->x; - restoreBlock(y + 38, x + 16, y, x); + restoreBlock(x, y, x + 16, y + 38); } else { colorBlock(window, 240, 151, 16, 38); } @@ -984,4 +1021,94 @@ void AGOSEngine::removeIconArray(uint num) { _fcsData2[num] = 0; } +static const byte hitBarData[12 * 7] = { + 0x3C, 0x00, 0x80, 0x00, 0x88, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0xD8, 0x00, 0x00, 0x04, 0x48, 0x00, 0x00, 0x00, + 0x20, 0x89, 0x8E, 0x00, 0xA8, 0x86, 0x10, 0x04, 0x08, 0x21, 0x88, 0x00, + 0x38, 0x50, 0x84, 0x00, 0x89, 0x49, 0x28, 0x04, 0x08, 0x52, 0x14, 0x00, + 0x20, 0x20, 0x84, 0x00, 0x89, 0x48, 0x38, 0x04, 0x08, 0x53, 0x9C, 0x00, + 0x20, 0x50, 0x84, 0x00, 0x89, 0x48, 0x20, 0x04, 0x48, 0x50, 0x90, 0x00, + 0x3C, 0x89, 0xC3, 0x00, 0x88, 0x88, 0x18, 0x03, 0x86, 0x23, 0x0C, 0x00 +}; + +// Personal Nightmare specific +void AGOSEngine_PN::drawIconHitBar() { + Graphics::Surface *screen = _system->lockScreen(); + byte *dst = (byte *)screen->pixels + 3 * _dxSurfacePitch + 6 * 8; + const byte *src = hitBarData; + uint8 color = (getPlatform() == Common::kPlatformPC) ? 7 : 15; + + for (int h = 0; h < 7; h++) { + for (int w = 0; w < 12; w++) { + int8 b = *src++; + for (int i = 0; i < 8; i++) { + if (b < 0) { + dst[w * 8 + i] = color; + } + + b <<= 1; + } + } + dst += _dxSurfacePitch; + } + + _system->unlockScreen(); +} + +void AGOSEngine_PN::iconPage() { + _objectCountS = -1; + + mouseOff(); + + uint8 objRoom = getptr(_quickptr[12] + _variableArray[210] * _quickshort[5] + 20); + uint8 iconNum = getptr(_quickptr[0] + objRoom * _quickshort[0] + 4); + + drawIcon(NULL, iconNum, 6, 12); + + HitArea *ha = _invHitAreas + 5; + for (uint8 r = 0; r < 5; r++) { + for (uint8 i = 0; i < 7; i++) { + printIcon(ha, i, r); + ha++; + } + } + + mouseOn(); +} + +bool AGOSEngine_PN::ifObjectInInv(uint16 a) { + return _variableArray[210] == getptr(_quickptr[11] + a * _quickshort[4] + 2); +} + +bool AGOSEngine_PN::testContainer(uint16 a) { + return bitextract(_quickptr[1] + a * _quickshort[1], 0) != 0; +} + +bool AGOSEngine_PN::testObvious(uint16 a) { + return bitextract(_quickptr[1] + a * _quickshort[1], 4) != 0; +} + +bool AGOSEngine_PN::testSeen(uint16 a) { + return bitextract(_quickptr[1] + a * _quickshort[1], 3) != 0; +} + +void AGOSEngine_PN::printIcon(HitArea *ha, uint8 i, uint8 r) { + if (_objects == _objectCountS) { + ha->flags |= kOBFBoxDisabled; + drawIcon(NULL, 0xFF, 12 + i * 3, 12 + 24 * r); + } else { + _objectCountS++; + if (!ifObjectInInv(_objectCountS) || !testObvious(_objectCountS)) { + printIcon(ha, i, r); + } else { + + uint8 iconNum = getptr(_quickptr[0] + _objectCountS * _quickshort[0] + 4); + drawIcon(NULL, iconNum, 12 + i * 3, 12 + 24 * r); + + ha->msg1 = _objectCountS | 0x8000; + ha->flags &= ~kOBFBoxDisabled; + } + } +} + } // End of namespace AGOS diff --git a/engines/agos/input.cpp b/engines/agos/input.cpp index e7e4e61c3c7..991d355979c 100644 --- a/engines/agos/input.cpp +++ b/engines/agos/input.cpp @@ -606,6 +606,129 @@ bool AGOSEngine::processSpecialKeys() { return verbCode; } +// Personal Nightmare specific +void AGOSEngine_PN::clearInputLine() { + _inputting = 0; + _inputReady = 0; + clearWindow(_windowArray[2]); +} + +void AGOSEngine_PN::handleKeyboard() { + if (!_inputReady) + return; + + if (_hitCalled != 0) { + mouseHit(); + } + + int16 chr = -1; + if (_mouseString) { + const char *strPtr = _mouseString; + while (*strPtr != 0 && *strPtr != 13) + addChar(*strPtr++); + _mouseString = 0; + + chr = *strPtr; + if (chr == 13) { + addChar(13); + } + } + if (_mouseString1 && chr != 13) { + const char *strPtr = _mouseString1; + while (*strPtr != 13) + addChar(*strPtr++); + _mouseString1 = 0; + + chr = *strPtr; + if (chr == 13) { + addChar(13); + } + } + if (chr == -1) { + chr = _keyPressed.ascii; + if (chr == 8 || chr == 13) { + addChar(chr); + } else if (!(_lockWord & 0x10)) { + if (chr >= 32) + addChar(chr); + } + } + + if (chr == 13) { + _mouseString = 0; + _mouseString1 = 0; + _mousePrintFG = 0; + _inputReady = 0; + } + + _keyPressed.reset(); +} + +void AGOSEngine_PN::interact(char *buffer, uint8 size) { + if (!_inputting) { + memset(_keyboardBuffer, 0, sizeof(_keyboardBuffer)); + _intputCounter = 0; + _inputMax = size; + _inputWindow = _windowArray[_curWindow]; + windowPutChar(_inputWindow, 128); + _inputting = 1; + _inputReady = 1; + } + + while (!shouldQuit() && _inputReady) { + if (!_noScanFlag && _scanFlag) { + buffer[0] = 1; + buffer[1] = 0; + _scanFlag = 0; + break; + } + delay(1); + } + + if (!_inputReady) { + memcpy(buffer, _keyboardBuffer, size); + _inputting = 0; + } +} + +void AGOSEngine_PN::addChar(uint8 chr) { + if (chr == 13) { + _keyboardBuffer[_intputCounter++] = chr; + userGameBackSpace(_inputWindow, 8); + windowPutChar(_inputWindow, 13); + } else if (chr == 8 && _intputCounter) { + userGameBackSpace(_inputWindow, 8); + userGameBackSpace(_inputWindow, 8); + windowPutChar(_inputWindow, 128); + + _keyboardBuffer[--_intputCounter] = 0; + } else if (chr >= 32 && _intputCounter < _inputMax) { + _keyboardBuffer[_intputCounter++] = chr; + + userGameBackSpace(_inputWindow, 8); + windowPutChar(_inputWindow, chr); + windowPutChar(_inputWindow, 128); + } +} + +bool AGOSEngine_PN::processSpecialKeys() { + if (shouldQuit()) + _exitCutscene = true; + + switch (_keyPressed.keycode) { + case Common::KEYCODE_ESCAPE: + _exitCutscene = true; + break; + case Common::KEYCODE_PAUSE: + pause(); + break; + default: + break; + } + + _keyPressed.reset(); + return false; +} + + } // End of namespace AGOS - - diff --git a/engines/agos/intern.h b/engines/agos/intern.h index 823a9ed3451..8d336344930 100644 --- a/engines/agos/intern.h +++ b/engines/agos/intern.h @@ -214,6 +214,22 @@ enum BoxFlags { kBFBoxItem = 0x80 }; +enum OldBoxFlags_PN { + kOBFObject = 0x1, + kOBFExit = 0x2, + kOBFDraggable = 0x4, + kOBFUseEmptyLine = 0x8, + kOBFBoxDisabled = 0x10, + kOBFInventoryBox = 0x20, + kOBFRoomBox = 0x40, + kOBFMoreBox = 0x80, + kOBFNoShowName = 0x100, + kOBFUseMessageList = 0x400, + // ScummVM specific + kOBFBoxSelected = 0x800, + kOBFInvertTouch = 0x1000 +}; + enum SubObjectFlags { kOFText = 0x1, kOFSize = 0x2, @@ -251,11 +267,13 @@ enum GameFileTypes { GAME_TBLFILE = 1 << 7, GAME_XTBLFILE = 1 << 8, GAME_RESTFILE = 1 << 9, + GAME_TEXTFILE = 1 << 10, - GAME_GFXIDXFILE = 1 << 10 + GAME_GFXIDXFILE = 1 << 11 }; enum GameIds { + GID_PN, GID_ELVIRA1, GID_ELVIRA2, GID_WAXWORKS, diff --git a/engines/agos/menus.cpp b/engines/agos/menus.cpp index e6d926b06db..7f61925528d 100644 --- a/engines/agos/menus.cpp +++ b/engines/agos/menus.cpp @@ -53,6 +53,27 @@ void AGOSEngine::loadMenuFile() { in.close(); } +// Personal Nightmare specific +void AGOSEngine::restoreMenu() { + _wiped = 0; + + _lockWord |= 0x80; + + clearVideoWindow(3, 0); + + uint16 oldWindowNum = _windowNum; + + setWindowImage(1, 1); + setWindowImage(2, 2); + + drawEdging(); + + _windowNum = oldWindowNum; + + _lockWord |= 0x20; + _lockWord &= ~0x80; +} + // Elvira 1 specific void AGOSEngine::drawMenuStrip(uint windowNum, uint menuNum) { WindowBlock *window = _windowArray[windowNum % 8]; diff --git a/engines/agos/module.mk b/engines/agos/module.mk index fe2dcba2f11..0da19a4f883 100644 --- a/engines/agos/module.mk +++ b/engines/agos/module.mk @@ -20,6 +20,7 @@ MODULE_OBJS := \ midi.o \ midiparser_s1d.o \ oracle.o \ + pn.o \ res.o \ res_ami.o \ res_snd.o \ @@ -28,6 +29,7 @@ MODULE_OBJS := \ script.o \ script_e1.o \ script_e2.o \ + script_pn.o \ script_ww.o \ script_s1.o \ script_s2.o \ @@ -39,6 +41,7 @@ MODULE_OBJS := \ verb.o \ vga.o \ vga_e2.o \ + vga_pn.o \ vga_ww.o \ vga_s1.o \ vga_s2.o \ diff --git a/engines/agos/pn.cpp b/engines/agos/pn.cpp new file mode 100644 index 00000000000..66ff6c9c182 --- /dev/null +++ b/engines/agos/pn.cpp @@ -0,0 +1,293 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/config-manager.h" + +#include "agos/intern.h" +#include "agos/agos.h" + +namespace AGOS { + +AGOSEngine_PN::AGOSEngine_PN(OSystem *system) + : AGOSEngine(system) { + + _dataBase = 0; + _dataBaseSize = 0; + _textBase = 0; + _textBaseSize = 0; + + memset(_buffer, 0, sizeof(_buffer)); + memset(_inputline, 0, sizeof(_inputline)); + memset(_saveFile, 0, sizeof(_saveFile)); + memset(_sb, 0, sizeof(_sb)); + memset(_wordcp, 0, sizeof(_wordcp)); + + memset(_objectName1, 0, sizeof(_objectName1)); + memset(_objectName2, 0, sizeof(_objectName2)); + + _dragStore = 0; + _hitCalled = 0; + _inputReady = 0; + _inputting = 0; + _intputCounter = 0; + _inputMax = 0; + _mousePrintFG = 0; + _mouseString = 0; + _mouseString1 = 0; + memset(_inMessage, 0, sizeof(_inMessage)); + memset(_placeMessage, 0, sizeof(_placeMessage)); + + memset(_quickptr, 0, sizeof(_quickptr)); + memset(_quickshort, 0, sizeof(_quickshort)); + + _noScanFlag = false; + memset(_keyboardBuffer, 0, sizeof(_keyboardBuffer)); + + _objects = 0; + _objectCountS = 0; + + _bp = 0; + _xofs = 0; + _havinit = 0; + _seed = 0; + + _curwrdptr = 0; + _inpp = 0; + _fnst = 0; + _linembr = 0; + _linct = 0; + _procnum = 0; + + _linebase = 0; + _workptr = 0; + + _cjmpbuff = NULL; +} + +AGOSEngine_PN::~AGOSEngine_PN() { + free(_dataBase); + free(_textBase); + + free(_cjmpbuff); + free(_stackbase); +} + +const byte egaPalette[48] = { + 0, 0, 0, + 0, 0, 170, + 0, 170, 0, + 0, 170, 170, + 170, 0, 0, + 170, 0, 170, + 170, 85, 0, + 170, 170, 170, + 85, 85, 85, + 85, 85, 255, + 85, 255, 85, + 85, 255, 255, + 255, 85, 85, + 255, 85, 255, + 255, 255, 85, + 255, 255, 255 +}; + +Common::Error AGOSEngine_PN::go() { + loadGamePcFile(); + + if (getFileName(GAME_ICONFILE) != NULL) { + loadIconFile(); + } + + setupBoxes(); + + vc34_setMouseOff(); + + addVgaEvent(_frameCount, ANIMATE_INT, NULL, 0, 0); + + if (getPlatform() == Common::kPlatformPC) { + // Set EGA Palette + for (int i = 0; i < 16; i++) { + _displayPalette[i * 4 + 0] = egaPalette[i * 3 + 0]; + _displayPalette[i * 4 + 1] = egaPalette[i * 3 + 1]; + _displayPalette[i * 4 + 2] = egaPalette[i * 3 + 2]; + _displayPalette[i * 4 + 3] = 0; + } + _paletteFlag = 1; + } + + _inputWindow = _windowArray[2] = openWindow(0, 192, 40, 1, 1, 0, 15); + _textWindow = _windowArray[0] = openWindow(1, 136, 38, 6, 1, 0, 15); + + if (getFeatures() & GF_DEMO) { + demoSeq(); + } else { + introSeq(); + processor(); + } + + return Common::kNoError; +} + +void AGOSEngine_PN::demoSeq() { + while (!shouldQuit()) { + loadZone(0); + setWindowImage(3, 0); + while (!shouldQuit() && _variableArray[228] != 1) + delay(1); + + loadZone(1); + setWindowImage(0, 0); + while (!shouldQuit() && _variableArray[228] != 2) + delay(1); + + loadZone(2); + setWindowImage(0, 0); + while (!shouldQuit() && _variableArray[228] != 3) + delay(1); + } +} + +void AGOSEngine_PN::introSeq() { + loadZone(25); // Zone 'I' + setWindowImage(3, 0); + + _exitCutscene = false; + while (!shouldQuit() && !_exitCutscene && _variableArray[228] != 1) { + processSpecialKeys(); + delay(1); + } + + setWindowImage(3, 3); + delay(100); + + loadZone(27); // Zone 'K' + setWindowImage(3, 0); + + _exitCutscene = false; + while (!shouldQuit() && !_exitCutscene && _variableArray[228] != 2) { + processSpecialKeys(); + delay(1); + } +} + +void AGOSEngine_PN::setupBoxes() { + _hitAreaList = _invHitAreas; + // Inventory box + defineBox( 0, 11, 68, 16, 26, 25, 0, kOBFDraggable | kOBFUseEmptyLine | kOBFInventoryBox | kOBFNoShowName); + // Room Box + defineBox( 1, 11, 103, 16, 26, 26, 0, kOBFDraggable | kOBFUseEmptyLine | kOBFRoomBox | kOBFNoShowName); + // Exit box + defineBox( 2, 48, 2, 8, 28, 27, 0, kOBFUseEmptyLine | kOBFNoShowName); + // More box + defineBox( 3, 80, 2, 8, 26, 28, 0, kOBFUseEmptyLine | kOBFMoreBox | kOBFNoShowName); + // Close box + defineBox( 4, 110, 2, 8, 28, 29, 0, kOBFUseEmptyLine | kOBFNoShowName); + + // Icon boxes + uint8 num = 5; + for (uint8 r = 0; r < 5; r++) { + for (uint8 i = 0; i < 7; i++) { + defineBox(num, 96 + i * 24, 12 + r * 24, 24, 24, 0, 3, kOBFObject | kOBFDraggable); + num++; + } + } + + // Mark the end of inventory boxes + HitArea *ha = _hitAreaList + num; + ha->id = 0xFFFF; + + _hitAreaList = _hitAreas; + defineBox( 0, 0, 0, 200, 320, 0, 0, kOBFBoxDisabled | kOBFNoShowName); + defineBox( 1, 273, 4, 5, 45, 1, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox( 2, 273, 12, 5, 45, 2, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox( 3, 273, 20, 5, 45, 3, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox( 4, 273, 28, 5, 45, 4, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox( 5, 273, 36, 5, 45, 5, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox( 6, 273, 44, 5, 45, 6, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox( 7, 273, 52, 5, 45, 7, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox( 8, 273, 60, 5, 45, 8, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox( 9, 273, 68, 5, 45, 9, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox(10, 273, 76, 5, 45, 10, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox(11, 273, 84, 5, 45, 11, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox(12, 273, 92, 5, 45, 12, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox(13, 273, 100, 5, 45, 13, 0, kOBFUseEmptyLine | kOBFBoxDisabled | kOBFNoShowName); + defineBox(14, 273, 107, 5, 45, 14, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox(15, 273, 115, 5, 45, 15, 0, kOBFUseEmptyLine | kOBFNoShowName | kOBFInvertTouch); + defineBox(16, 273, 123, 5, 45, 16, 0, kOBFUseEmptyLine | kOBFBoxDisabled | kOBFNoShowName); + defineBox(17, 20, 5, 7, 7, 17, 0, kOBFUseEmptyLine | kOBFNoShowName); + defineBox(18, 28, 11, 7, 13, 18, 0, kOBFUseEmptyLine | kOBFNoShowName); + defineBox(19, 36, 21, 7, 7, 19, 0, kOBFUseEmptyLine | kOBFNoShowName); + defineBox(20, 27, 31, 7, 13, 20, 0, kOBFUseEmptyLine | kOBFNoShowName); + defineBox(21, 20, 37, 7, 7, 21, 0, kOBFUseEmptyLine | kOBFNoShowName); + defineBox(22, 5, 31, 7, 13, 22, 0, kOBFUseEmptyLine | kOBFNoShowName); + defineBox(23, 4, 21, 7, 7, 23, 0, kOBFUseEmptyLine | kOBFNoShowName); + defineBox(24, 5, 11, 7, 13, 24, 0, kOBFUseEmptyLine | kOBFNoShowName); + defineBox(25, 11, 68, 16, 26, 25, 0, kOBFDraggable | kOBFUseEmptyLine | kOBFInventoryBox | kOBFNoShowName); + defineBox(26, 11, 103, 16, 26, 26, 0, kOBFDraggable | kOBFUseEmptyLine | kOBFRoomBox | kOBFNoShowName); +} + +void AGOSEngine_PN::processor() { + int q; + + setqptrs(); + q = setjmp(_loadfail); + + _variableArray[6] = 0; + + if (getPlatform() == Common::kPlatformAtariST) { + _variableArray[21] = 2; + } else if (getPlatform() == Common::kPlatformAmiga) { + _variableArray[21] = 0; + } else { + _variableArray[21] = 1; + } + + _variableArray[16] = _quickshort[6]; + _variableArray[17] = _quickshort[7]; + _variableArray[19] = getptr(55L); + setposition(q, 0); + doline(0); +} + +void AGOSEngine_PN::setqptrs() { + int a = 0; + + while (a < 11) { + _quickptr[a] = getlong(3L * a); + a++; + } + _quickptr[11] = getlong(58L); + _quickptr[12] = getlong(61L); + _quickshort[0] = getptr(35L); + _quickshort[1] = getptr(37L); + _quickshort[2] = getptr(39L); + _quickshort[3] = getptr(41L); + _quickshort[4] = getptr(43L); + _quickshort[5] = getptr(45L); + _quickshort[6] = getptr(51L); + _quickshort[7] = getptr(53L); +} + +} // End of namespace AGOS diff --git a/engines/agos/res.cpp b/engines/agos/res.cpp index a33a16bbc94..1540e5a0181 100644 --- a/engines/agos/res.cpp +++ b/engines/agos/res.cpp @@ -27,6 +27,7 @@ #include "common/file.h" +#include "common/util.h" #include "agos/agos.h" #include "agos/intern.h" @@ -149,6 +150,46 @@ int AGOSEngine::allocGamePcVars(Common::SeekableReadStream *in) { return itemArrayInited; } +void AGOSEngine_PN::loadGamePcFile() { + Common::File in; + + if (getFileName(GAME_BASEFILE) != NULL) { + // Read dataBase + in.open(getFileName(GAME_BASEFILE)); + if (in.isOpen() == false) { + error("loadGamePcFile: Can't load database file '%s'", getFileName(GAME_BASEFILE)); + } + + _dataBaseSize = in.size(); + _dataBase = (byte *)malloc(_dataBaseSize); + if (_dataBase == NULL) + error("loadGamePcFile: Out of memory for dataBase"); + in.read(_dataBase, _dataBaseSize); + in.close(); + + if (_dataBase[31] != 0) + error("Later version of system requested"); + } + + if (getFileName(GAME_TEXTFILE) != NULL) { + // Read textBase + in.open(getFileName(GAME_TEXTFILE)); + if (in.isOpen() == false) { + error("loadGamePcFile: Can't load textbase file '%s'", getFileName(GAME_TEXTFILE)); + } + + _textBaseSize = in.size(); + _textBase = (byte *)malloc(_textBaseSize); + if (_textBase == NULL) + error("loadGamePcFile: Out of memory for textBase"); + in.read(_textBase, _textBaseSize); + in.close(); + + if (_textBase[getlong(30L)] != 128) + error("Unknown compression format"); + } +} + void AGOSEngine::loadGamePcFile() { Common::File in; int fileSize; @@ -646,6 +687,96 @@ bool AGOSEngine::decrunchFile(byte *src, byte *dst, uint32 size) { #undef SD_TYPE_LITERAL #undef SD_TYPE_MATCH +static bool getBit(Common::Stack &dataList, uint32 &srcVal) { + bool result = srcVal & 1; + srcVal >>= 1; + if (srcVal == 0) { + srcVal = dataList.pop(); + + result = srcVal & 1; + srcVal = (srcVal >> 1) | 0x80000000L; + } + + return result; +} + +static uint32 copyBits(Common::Stack &dataList, uint32 &srcVal, int numBits) { + uint32 destVal = 0; + + for (int i = 0; i < numBits; ++i) { + bool f = getBit(dataList, srcVal); + destVal = (destVal << 1) | (f ? 1 : 0); + } + + return destVal; +} + +static void transferLoop(uint8 *dataOut, int &outIndex, uint32 destVal, int max) { + assert(outIndex > max - 1); + byte *pDest = dataOut + outIndex; + + for (int i = 0; (i <= max) && (outIndex > 0) ; ++i) { + pDest = dataOut + --outIndex; + *pDest = pDest[destVal]; + } +} + +void AGOSEngine::decompressPN(Common::Stack &dataList, uint8 *&dataOut, int &dataOutSize) { + // Set up the output data area + dataOutSize = dataList.pop(); + dataOut = new uint8[dataOutSize]; + int outIndex = dataOutSize; + + // Decompression routine + uint32 srcVal = dataList.pop(); + uint32 destVal; + + while (outIndex > 0) { + uint32 numBits = 0; + int count = 0; + + if (getBit(dataList, srcVal)) { + destVal = copyBits(dataList, srcVal, 2); + + if (destVal < 2) { + count = destVal + 2; + destVal = copyBits(dataList, srcVal, destVal + 9); + transferLoop(dataOut, outIndex, destVal, count); + continue; + } else if (destVal != 3) { + count = copyBits(dataList, srcVal, 8); + destVal = copyBits(dataList, srcVal, 8); + transferLoop(dataOut, outIndex, destVal, count); + continue; + } else { + numBits = 8; + count = 8; + } + } else if (getBit(dataList, srcVal)) { + destVal = copyBits(dataList, srcVal, 8); + transferLoop(dataOut, outIndex, destVal, 1); + continue; + } else { + numBits = 3; + count = 0; + } + + destVal = copyBits(dataList, srcVal, numBits); + count += destVal; + + // Loop through extracting specified number of bytes + for (int i = 0; i <= count; ++i) { + // Shift 8 bits from the source to the destination + for (int bitCtr = 0; bitCtr < 8; ++bitCtr) { + bool flag = getBit(dataList, srcVal); + destVal = (destVal << 1) | (flag ? 1 : 0); + } + + dataOut[--outIndex] = destVal & 0xff; + } + } +} + void AGOSEngine::loadVGABeardFile(uint16 id) { uint32 offs, size; @@ -690,7 +821,7 @@ void AGOSEngine::loadVGABeardFile(uint16 id) { } } -void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type) { +void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type, bool useError) { File in; char filename[15]; byte *dst; @@ -729,12 +860,16 @@ void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type) { sprintf(filename, "%c%d.out", 48 + id, type); } else if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2) { sprintf(filename, "%.2d%d.pkd", id, type); + } else if (getGameType() == GType_PN) { + sprintf(filename, "%c%d.in", id + 48, type); } else { sprintf(filename, "%.3d%d.pkd", id, type); } } else { if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) { sprintf(filename, "%.2d%d.VGA", id, type); + } else if (getGameType() == GType_PN) { + sprintf(filename, "%c%d.out", id + 48, type); } else { sprintf(filename, "%.3d%d.VGA", id, type); } @@ -742,19 +877,42 @@ void AGOSEngine::loadVGAVideoFile(uint16 id, uint8 type) { in.open(filename); if (in.isOpen() == false) { - error("loadVGAVideoFile: Can't load %s", filename); + if (useError) + error("loadVGAVideoFile: Can't load %s", filename); + + _block = _blockEnd = NULL; + return; } dstSize = srcSize = in.size(); - if (getFeatures() & GF_CRUNCHED) { - byte *srcBuffer = (byte *)malloc(srcSize); - if (in.read(srcBuffer, srcSize) != srcSize) + if (getGameType() == GType_PN && getPlatform() == Common::kPlatformPC && id == 17 && type == 2) { + // The A2.out file isn't compressed in PC version of Personal Nightmare + dst = allocBlock(dstSize + extraBuffer); + if (in.read(dst, dstSize) != dstSize) error("loadVGAVideoFile: Read failed"); + } else if (getFeatures() & GF_CRUNCHED) { + if (getGameType() == GType_PN) { + Common::Stack data; + byte *dataOut = 0; + int dataOutSize = 0; - dstSize = READ_BE_UINT32(srcBuffer + srcSize - 4); - dst = allocBlock (dstSize + extraBuffer); - decrunchFile(srcBuffer, dst, srcSize); - free(srcBuffer); + for (uint i = 0; i < srcSize / 4; ++i) + data.push(in.readUint32BE()); + + decompressPN(data, dataOut, dataOutSize); + dst = allocBlock (dataOutSize + extraBuffer); + memcpy(dst, dataOut, dataOutSize); + delete[] dataOut; + } else { + byte *srcBuffer = (byte *)malloc(srcSize); + if (in.read(srcBuffer, srcSize) != srcSize) + error("loadVGAVideoFile: Read failed"); + + dstSize = READ_BE_UINT32(srcBuffer + srcSize - 4); + dst = allocBlock (dstSize + extraBuffer); + decrunchFile(srcBuffer, dst, srcSize); + free(srcBuffer); + } } else { dst = allocBlock(dstSize + extraBuffer); if (in.read(dst, dstSize) != dstSize) diff --git a/engines/agos/res_ami.cpp b/engines/agos/res_ami.cpp index 3bd951365fb..f86d0344f54 100644 --- a/engines/agos/res_ami.cpp +++ b/engines/agos/res_ami.cpp @@ -92,7 +92,7 @@ static void bitplaneToChunkyText(uint16 *w, uint8 colorDepth, uint8 *&dst) { } } -static void convertCompressedImage(const byte *src, byte *dst, uint8 colorDepth, int height, int width) { +static void convertCompressedImage(const byte *src, byte *dst, uint8 colorDepth, int height, int width, bool horizontal = true) { const byte *plane[kMaxColorDepth]; byte *uncptr[kMaxColorDepth]; int length, i, j; @@ -119,10 +119,19 @@ static void convertCompressedImage(const byte *src, byte *dst, uint8 colorDepth, uncbfroutptr = uncbfrout; const int chunkSize = colorDepth > 4 ? 16 : 8; - for (i = 0; i < width / 16; ++i) { + if (horizontal) { for (j = 0; j < height; ++j) { - memcpy(dst + width * chunkSize / 16 * j + chunkSize * i, uncbfroutptr, chunkSize); - uncbfroutptr += chunkSize; + for (i = 0; i < width / 16; ++i) { + memcpy(dst + width * chunkSize / 16 * j + chunkSize * i, uncbfroutptr, chunkSize); + uncbfroutptr += chunkSize; + } + } + } else { + for (i = 0; i < width / 16; ++i) { + for (j = 0; j < height; ++j) { + memcpy(dst + width * chunkSize / 16 * j + chunkSize * i, uncbfroutptr, chunkSize); + uncbfroutptr += chunkSize; + } } } @@ -152,7 +161,7 @@ byte *AGOSEngine::convertImage(VC10_state *state, bool compressed) { byte *dst = _planarBuf; if (compressed) { - convertCompressedImage(src, dst, colorDepth, height, width); + convertCompressedImage(src, dst, colorDepth, height, width, (getGameType() == GType_PN)); } else { length = (width + 15) / 16 * height; for (i = 0; i < length; i++) { diff --git a/engines/agos/res_snd.cpp b/engines/agos/res_snd.cpp index 5ac2f06a284..891e183d4f5 100644 --- a/engines/agos/res_snd.cpp +++ b/engines/agos/res_snd.cpp @@ -364,6 +364,8 @@ bool AGOSEngine::loadVGASoundFile(uint16 id, uint8 type) { sprintf(filename, "%c%d.out", 48 + id, type); } else if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2) { sprintf(filename, "%.2d%d.out", id, type); + } else if (getGameType() == GType_PN) { + sprintf(filename, "%c%d.in", id + 48, type); } else { sprintf(filename, "%.3d%d.out", id, type); } @@ -375,6 +377,8 @@ bool AGOSEngine::loadVGASoundFile(uint16 id, uint8 type) { sprintf(filename, "%.2d.SND", elvira1_soundTable[id]); } else if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) { sprintf(filename, "%.2d%d.VGA", id, type); + } else if (getGameType() == GType_PN) { + sprintf(filename, "%c%d.out", id + 48, type); } else { sprintf(filename, "%.3d%d.VGA", id, type); } @@ -386,7 +390,19 @@ bool AGOSEngine::loadVGASoundFile(uint16 id, uint8 type) { } dstSize = srcSize = in.size(); - if (getGameType() == GType_ELVIRA1 && getFeatures() & GF_DEMO) { + if (getGameType() == GType_PN) { + Common::Stack data; + byte *dataOut = 0; + int dataOutSize = 0; + + for (uint i = 0; i < srcSize / 4; ++i) + data.push(in.readUint32BE()); + + decompressPN(data, dataOut, dataOutSize); + dst = allocBlock (dataOutSize); + memcpy(dst, dataOut, dataOutSize); + delete[] dataOut; + } else if (getGameType() == GType_ELVIRA1 && getFeatures() & GF_DEMO) { byte *srcBuffer = (byte *)malloc(srcSize); if (in.read(srcBuffer, srcSize) != srcSize) error("loadVGASoundFile: Read failed"); @@ -457,39 +473,7 @@ void AGOSEngine::loadSoundFile(const char* filename) { _sound->playSfxData(dst, 0, 0, 0); } -void AGOSEngine::loadSound(uint sound) { - byte *dst; - uint32 offs, size; - - if (_curSfxFile == NULL) - return; - - dst = _curSfxFile; - if (getGameType() == GType_WW) { - uint tmp = sound; - while (tmp--) - dst += READ_LE_UINT16(dst) + 4; - - size = READ_LE_UINT16(dst); - offs = 4; - } else if (getGameType() == GType_ELVIRA2) { - while (READ_BE_UINT32(dst + 4) != sound) - dst += 12; - - size = READ_BE_UINT32(dst); - offs = READ_BE_UINT32(dst + 8); - } else { - while (READ_BE_UINT16(dst + 6) != sound) - dst += 12; - - size = READ_BE_UINT16(dst + 2); - offs = READ_BE_UINT32(dst + 8); - } - - _sound->playRawData(dst + offs, sound, size); -} - -void AGOSEngine::loadSound(uint sound, int pan, int vol, uint type) { +void AGOSEngine::loadSound(uint16 sound, int16 pan, int16 vol, uint16 type) { byte *dst; if (getGameId() == GID_DIMP) { @@ -532,12 +516,67 @@ void AGOSEngine::loadSound(uint sound, int pan, int vol, uint type) { dst = _curSfxFile + READ_LE_UINT32(_curSfxFile + sound * 4); } - if (type == 3) - _sound->playSfx5Data(dst, sound, pan, vol); - else if (type == 2) + if (type == Sound::TYPE_AMBIENT) _sound->playAmbientData(dst, sound, pan, vol); - else + else if (type == Sound::TYPE_SFX) _sound->playSfxData(dst, sound, pan, vol); + else if (type == Sound::TYPE_SFX5) + _sound->playSfx5Data(dst, sound, pan, vol); +} + +void AGOSEngine::loadSound(uint16 sound, uint16 freq, uint16 flags) { + byte *dst; + uint32 offs, size = 0; + + if (_curSfxFile == NULL) + return; + + dst = _curSfxFile; + if (getGameType() == GType_WW) { + uint tmp = sound; + while (tmp--) { + dst += READ_LE_UINT16(dst) + 4; + size += READ_LE_UINT16(dst) + 4; + + if (size > _curSfxFileSize) + error("loadSound: Reading beyond EOF"); + } + + size = READ_LE_UINT16(dst); + offs = 4; + } else if (getGameType() == GType_ELVIRA2) { + while (READ_BE_UINT32(dst + 4) != sound) { + dst += 12; + size += 12; + + if (size > _curSfxFileSize) + error("loadSound: Reading beyond EOF"); + } + + size = READ_BE_UINT32(dst); + offs = READ_BE_UINT32(dst + 8); + } else { + while (READ_BE_UINT16(dst + 6) != sound) { + dst += 12; + size += 12; + + if (size > _curSfxFileSize) + error("loadSound: Reading beyond EOF"); + + } + + size = READ_BE_UINT16(dst + 2); + offs = READ_BE_UINT32(dst + 8); + } + + // TODO: Handle other sound flags and frequency + if (flags == 2 && _sound->isSfxActive()) { + _sound->queueSound(dst + offs, sound, size, 8000); + } else { + if (flags == 0) + _sound->stopSfx(); + _sound->playRawData(dst + offs, sound, size, 8000); + } } void AGOSEngine::loadVoice(uint speechId) { diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp index 83165312df4..700d451b834 100644 --- a/engines/agos/saveload.cpp +++ b/engines/agos/saveload.cpp @@ -125,12 +125,21 @@ char *AGOSEngine_Elvira2::genSaveName(int slot) { return buf; } -char *AGOSEngine::genSaveName(int slot) { +char *AGOSEngine_Elvira1::genSaveName(int slot) { static char buf[20]; sprintf(buf, "elvira1.%.3d", slot); return buf; } +char *AGOSEngine::genSaveName(int slot) { + static char buf[20]; + if (getPlatform() == Common::kPlatformPC) + sprintf(buf, "pn-pc.%.3d", slot); + else + sprintf(buf, "pn.%.3d", slot); + return buf; +} + void AGOSEngine::quickLoadOrSave() { // Quick load & save is only supported complete version of Simon the Sorcerer 1/2 if (getGameType() == GType_PP || getGameType() == GType_FF || @@ -1544,4 +1553,125 @@ bool AGOSEngine_Elvira2::saveGame(uint slot, const char *caption) { return result; } +// Personal Nightmare specific +bool AGOSEngine_PN::badload(int8 errorNum) { + if (errorNum == -2) + return 0; + /* Load error recovery routine */ + while (_stackbase != NULL) { + /* Clear any stack */ + dumpstack(); + } + /* Restart from process 1 */ + longjmp(_loadfail, 1); + return 1; +} + +void AGOSEngine_PN::getFilename() { + _noScanFlag = 1; + clearInputLine(); + + memset(_saveFile, 0, sizeof(_saveFile)); + while (!shouldQuit() && !strlen(_saveFile)) { + const char *msg = "File name : "; + pcf((unsigned char)'\n'); + while (*msg) + pcf((unsigned char)*msg++); + + interact(_saveFile, 8); + pcf((unsigned char)'\n'); + _noScanFlag = 0; + } +} + +int AGOSEngine_PN::loadfl(char *name) { + Common::InSaveFile *f; + haltAnimation(); + + f = _saveFileMan->openForLoading(name); + if (f == NULL) { + restartAnimation(); + return -2; + } + f->read(_saveFile, 8); + + if (f->readByte() != 41) { + restartAnimation(); + delete f; + return -2; + } + if (f->readByte() != 33) { + restartAnimation(); + delete f; + return -2; + } + // TODO: Make endian safe + if (!f->read(_dataBase + _quickptr[2], (int)(_quickptr[6] - _quickptr[2]))) { + restartAnimation(); + delete f; + return -1; + } + delete f; + restartAnimation(); + dbtosysf(); + return 0; +} + +int AGOSEngine_PN::savfl(char *name) { + Common::OutSaveFile *f; + sysftodb(); + haltAnimation(); + + f = _saveFileMan->openForSaving(name); + if (f == NULL) { + restartAnimation(); + + const char *msg = "Couldn't save. "; + pcf((unsigned char)'\n'); + while (*msg) + pcf((unsigned char)*msg++); + + return 0; + } + f->write(_saveFile, 8); + + f->writeByte(41); + f->writeByte(33); + // TODO: Make endian safe + if (!f->write(_dataBase + _quickptr[2], (int)(_quickptr[6] - _quickptr[2]))) { + delete f; + restartAnimation(); + error("Couldn't save "); + return 0; + } + f->finalize(); + delete f; + + restartAnimation(); + return 1; +} + +void AGOSEngine_PN::sysftodb() { + uint32 pos = _quickptr[2]; + int ct = 0; + + while (ct < (getptr(49L) / 2)) { + _dataBase[pos] = (uint8)(_variableArray[ct] % 256); + _dataBase[pos + 1] = (uint8)(_variableArray[ct] / 256); + pos+=2; + ct++; + } +} + +void AGOSEngine_PN::dbtosysf() { + uint32 pos = _quickptr[2]; + int ct = 0; + + while (ct < (getptr(49L) / 2)) { + _variableArray[ct] = _dataBase[pos] + 256 * _dataBase[pos + 1]; + pos += 2; + ct++; + } +} + } // End of namespace AGOS diff --git a/engines/agos/script_e2.cpp b/engines/agos/script_e2.cpp index 655fc16ff1b..8802ed921df 100644 --- a/engines/agos/script_e2.cpp +++ b/engines/agos/script_e2.cpp @@ -574,9 +574,7 @@ void AGOSEngine_Elvira2::oe2_ifExitLocked() { void AGOSEngine_Elvira2::oe2_playEffect() { // 174: play sound uint soundId = getVarOrWord(); - loadSound(soundId); - - debug(0, "oe2_playEffect: stub (%d)", soundId); + loadSound(soundId, 0, 0); } void AGOSEngine_Elvira2::oe2_getDollar2() { @@ -636,7 +634,7 @@ void AGOSEngine_Elvira2::oe2_printMonsterDamage() { void AGOSEngine_Elvira2::oe2_isAdjNoun() { // 179: item unk1 unk2 is Item *item = getNextItemPtr(); - int16 a = getNextWord(), b = getNextWord(); + int16 a = getNextWord(), n = getNextWord(); if (getGameType() == GType_ELVIRA2) { // WORKAROUND bug #1745996: A NULL item can occur when @@ -647,7 +645,7 @@ void AGOSEngine_Elvira2::oe2_isAdjNoun() { } } - setScriptCondition(item->adjective == a && item->noun == b); + setScriptCondition(item->adjective == a && item->noun == n); } void AGOSEngine_Elvira2::oe2_b2Set() { diff --git a/engines/agos/script_pn.cpp b/engines/agos/script_pn.cpp new file mode 100644 index 00000000000..f46f6db7dff --- /dev/null +++ b/engines/agos/script_pn.cpp @@ -0,0 +1,1109 @@ +/* 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 "agos/agos.h" +#include "agos/vga.h" + +namespace AGOS { + +#define OPCODE(x) _OPCODE(AGOSEngine_PN, x) + +void AGOSEngine_PN::setupOpcodes() { + static const OpcodeEntryPN opcodes[] = { + /* 00 */ + OPCODE(opn_opcode00), + OPCODE(opn_opcode01), + OPCODE(opn_opcode02), + OPCODE(opn_opcode03), + /* 04 */ + OPCODE(opn_opcode04), + OPCODE(opn_opcode05), + OPCODE(opn_opcode06), + OPCODE(opn_opcode07), + /* 08 */ + OPCODE(opn_opcode08), + OPCODE(opn_opcode09), + OPCODE(opn_opcode10), + OPCODE(opn_opcode11), + /* 12 */ + OPCODE(opn_opcode12), + OPCODE(opn_opcode13), + OPCODE(opn_opcode14), + OPCODE(opn_opcode15), + /* 16 */ + OPCODE(opn_opcode16), + OPCODE(opn_opcode17), + OPCODE(opn_opcode18), + OPCODE(opn_opcode19), + /* 20 */ + OPCODE(opn_opcode20), + OPCODE(opn_opcode21), + OPCODE(opn_opcode22), + OPCODE(opn_opcode23), + /* 24 */ + OPCODE(opn_opcode24), + OPCODE(opn_opcode25), + OPCODE(opn_opcode26), + OPCODE(opn_opcode27), + /* 28 */ + OPCODE(opn_opcode28), + OPCODE(opn_opcode29), + OPCODE(opn_opcode30), + OPCODE(opn_opcode31), + /* 32 */ + OPCODE(opn_opcode32), + OPCODE(opn_opcode33), + OPCODE(opn_opcode34), + OPCODE(opn_opcode35), + /* 36 */ + OPCODE(opn_opcode36), + OPCODE(opn_opcode37), + OPCODE(opn_opcode38), + OPCODE(opn_opcode39), + /* 40 */ + OPCODE(opn_opcode40), + OPCODE(opn_opcode41), + OPCODE(opn_opcode42), + OPCODE(opn_opcode43), + /* 44 */ + OPCODE(opn_opcode44), + OPCODE(opn_opcode45), + OPCODE(opn_opcode46), + OPCODE(opn_opcode47), + /* 48 */ + OPCODE(opn_opcode48), + OPCODE(opn_opcode49), + OPCODE(opn_opcode50), + OPCODE(opn_opcode51), + /* 52 */ + OPCODE(opn_opcode52), + OPCODE(opn_opcode53), + OPCODE(opn_opcode54), + OPCODE(opn_opcode55), + /* 56 */ + OPCODE(opn_opcode56), + OPCODE(opn_opcode57), + OPCODE(o_invalid), + OPCODE(o_invalid), + /* 60 */ + OPCODE(o_invalid), + OPCODE(o_invalid), + OPCODE(opn_opcode62), + OPCODE(opn_opcode63), + }; + + _opcodesPN = opcodes; + _numOpcodes = 64; +} + +void AGOSEngine_PN::executeOpcode(int opcode) { + OpcodeProcPN op = _opcodesPN[opcode].proc; + (this->*op) (); +} + +#define readfromline() (_linct-- ? (int)*_workptr++ : readoverr()) + +int readoverr() { + error("Internal Error - Line Over-run"); +} + +// ----------------------------------------------------------------------- +// Personal Nightmare Opcodes +// ----------------------------------------------------------------------- + +void AGOSEngine_PN::opn_opcode00() { + uint8 *str = _workptr; + varval(); + writeval(str, varval()); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode01() { + uint8 *str = _workptr; + int32 sp = varval() + varval(); + _variableArray[12] = sp % 65536; + _variableArray[13] = sp / 65536; + if (sp > 65535) + sp=65535; + writeval(str, (int)sp); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode02() { + uint8 *str = _workptr; + int32 sp = varval(); + sp -= varval(); + _variableArray[12] = sp % 65536; + _variableArray[13] = sp / 65536; + if(sp < 0) + sp = 0; + writeval(str, (int)sp); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode03() { + uint8 *str = _workptr; + int32 sp = varval() * varval(); + _variableArray[12] = sp % 65536; + _variableArray[13] = sp / 65536; + if (sp > 65535) + sp = 65535; + writeval(str, (int)sp); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode04() { + uint8 *str = _workptr; + int32 sp = varval(); + int32 sp2 = varval(); + if (sp2 == 0) + error("Division by 0"); + sp = sp / sp2; + _variableArray[12] = sp % 65536; + _variableArray[13] = sp / 65536; + writeval(str, (int)sp); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode05() { + pcf((uint8)'\n'); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode06() { + pmesd(varval()); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode07() { + int32 sp = varval(); + plocd((int)sp, varval()); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode08() { + int32 sp = varval(); + pobjd((int)sp, varval()); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode09() { + pmesd(varval()); + pcf((uint8)'\n'); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode10() { + int32 sp = varval(); + plocd((int)sp, varval()); + pcf((uint8)'\n'); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode11() { + int32 sp = varval(); + pobjd((int)sp, varval()); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode12() { + char bf[8]; + int a = 0; + sprintf(bf,"%d", varval()); + while(bf[a]) + pcf(bf[a++]); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode13() { + char bf[8]; + int a = 0; + sprintf(bf,"%d", varval()); + while(bf[a]) + pcf(bf[a++]); + pcf((uint8)'\n'); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode14() { + clearWindow(_windowArray[_curWindow]); + pcf((uint8)255); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode15() { + int32 x = varval();; + if ((x < 0) || (x > 4)) + x = 0; + + pcf((unsigned char)254); + _curWindow = x; + _xofs = (8 * _windowArray[_curWindow]->textLength) / 6 + 1; + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode16() { + int32 sp = varval(); + setScriptReturn((sp >= 0 && sp <= 4)); +} + +void AGOSEngine_PN::opn_opcode17() { + int16 v1 = varval(); + int16 v2 = varval(); + setScriptReturn(v1 < v2); +} + +void AGOSEngine_PN::opn_opcode18() { + int16 v1 = varval(); + int16 v2 = varval(); + setScriptReturn(v1 > v2); +} + +void AGOSEngine_PN::opn_opcode19() { + int16 v1 = varval(); + int16 v2 = varval(); + setScriptReturn(v1 == v2); +} + +void AGOSEngine_PN::opn_opcode20() { + int16 v1 = varval(); + int16 v2 = varval(); + setScriptReturn(v1 != v2); +} + +void AGOSEngine_PN::opn_opcode21() { + setposition(_procnum, varval()); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode22() { + int pf[8]; + int a; + a = varval(); + funcentry(pf, a); + funccpy(pf); + setposition(a, 0); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode23() { + setScriptReturn(actCallD(varval())); +} + +void AGOSEngine_PN::opn_opcode24() { + popstack(-1); + longjmp(*(_stackbase->savearea), 2); + setScriptReturn(false); +} + +void AGOSEngine_PN::opn_opcode25() { + popstack(-1); + longjmp(*(_stackbase->savearea), 1); + setScriptReturn(false); +} + +void AGOSEngine_PN::opn_opcode26() { + while ((_stackbase->classnum != -1) && (_stackbase != NULL)) + junkstack(); + dumpstack(); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode27() { + quitGame(); + // Make sure the quit event is processed immediately. + delay(0); +} + +void AGOSEngine_PN::opn_opcode28() { + addstack(varval()); + _stackbase->savearea = _cjmpbuff; + setScriptReturn(false); +} + +void AGOSEngine_PN::opn_opcode29() { + popstack(varval()); + longjmp(*(_stackbase->savearea), -1); + setScriptReturn(false); +} + +void AGOSEngine_PN::opn_opcode30() { + _variableArray[1] = varval(); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode31() { + int a, slot = 0; + char bf[60]; + + if ((a = varval()) > 2) { + setScriptReturn(false); + return; + } + + switch (a) { + case 0: + getFilename(); + slot = matchSaveGame(_saveFile, countSaveGames()); + strcpy(bf, genSaveName(slot)); + break; + case 1: + strcpy(bf, "test.sav"); + break; + case 2: + // NOTE: Is this case ever used? + error("opn_opcode31: case 2"); + break; + } + + a = loadfl(bf); + if (a) + setScriptReturn(badload(a)); + else + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode32() { + char bf[60]; + int a; + + if ((a = varval()) > 2) { + setScriptReturn(true); + return; + } + + switch (a) { + case 0: + getFilename(); + strcpy(bf, genSaveName(countSaveGames())); + break; + case 1: + strcpy(bf, "test.sav"); + break; + case 2: + // NOTE: Is this case ever used? + error("opn_opcode32: case 2"); + break; + } + + a = savfl(bf); + setScriptReturn(a); +} + +void AGOSEngine_PN::opn_opcode33() { + setScriptReturn((varval() < 3) ? 1 : 0); +} + +void AGOSEngine_PN::opn_opcode34() { + uint16 msgNum1, msgNum2; + varval(); + getResponse((int)_variableArray[166], (int)_variableArray[167], msgNum1, msgNum2); + _variableArray[168]= msgNum1; + _variableArray[169]= msgNum2; + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode35() { + int a; + uint8 *sav = _workptr; + varval(); + a = varval(); + if ((a = gvwrd((uint8 *)_wordcp, a)) == -1) { + setScriptReturn(false); + return; + } + + writeval(sav, a); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode36() { + int ct = 0; + while (ct < _dataBase[57] + 1) + _wordcp[ct++] = 0; + ct = 1; + if (isspace(*_inpp)) + while ((*_inpp) && (isspace(*_inpp))) + _inpp++; + if (*_inpp == 0) { + setScriptReturn(false); + return; + } + _curwrdptr = _inpp; + _wordcp[0] = *_inpp++; + if ((_wordcp[0] == '.') || (_wordcp[0] == ',') || (_wordcp[0] == '"')) { + setScriptReturn(true); + return; + } + while ((*_inpp != '.') && (*_inpp != ',') && (!isspace(*_inpp)) && (*_inpp != '\0') && + (*_inpp!='"')) { + if (ct < _dataBase[57]) + _wordcp[ct++] = *_inpp; + _inpp++; + } + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode37() { + _curwrdptr = NULL; + + _inputReady = 1; + interact(_inputline, 49); + + if ((_inpp = strchr(_inputline,'\n')) != NULL) + *_inpp = '\0'; + _inpp = _inputline; + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode38() { + _noScanFlag = 1; + clearInputLine(); + writeval(_workptr, _keyPressed.ascii); + _keyPressed.reset(); + _noScanFlag = 0; + varval(); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode39() { + pcf((uint8)varval()); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode40() { + setScriptReturn(doaction() | doaction()); +} + +void AGOSEngine_PN::opn_opcode41() { + setScriptReturn(doaction() & doaction()); +} + +void AGOSEngine_PN::opn_opcode42() { + setScriptReturn(doaction() ^ doaction()); +} + +void AGOSEngine_PN::opn_opcode43() { + setScriptReturn(!(doaction())); +} + +void AGOSEngine_PN::opn_opcode44() { + pcf((uint8)254); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode45() { + uint8 *myptr; + int x; + + if (_havinit == 0) { + _seed = (int16)getTime(); + _havinit = 1; + } + _seed = 1 + (75 * (_seed + 1) - 1) % 65537; + myptr = _workptr; + varval(); + x = varval(); + if (x == 0) + error("Illegal range specified for RANDOM"); + writeval(myptr, (_seed % x)); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode46() { + char *x = _curwrdptr; + if (x == NULL) { + setScriptReturn(true); + return; + } + pcf(*x); + if ((*x == '.') || (*x == '"') || (*x == ',')) { + setScriptReturn(true); + return; + } + x++; + while ((*x != '.') && (*x != ',') && (*x != '"') && (!isspace(*x)) && (*x != '\0')) + pcf(*x++); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode47() { + pmesd(varval() * 256 + varval()); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode48() { + pmesd(varval() * 256 + varval()); + pcf((uint8)'\n'); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode49() { + setScriptReturn(findentry()); +} + +void AGOSEngine_PN::opn_opcode50() { + _fnst = 0; + setScriptReturn(findset()); +} + +void AGOSEngine_PN::opn_opcode51() { + _fnst = varval(); + setScriptReturn(findset()); +} + +void AGOSEngine_PN::opn_opcode52() { + int32 mode = varval(); + if (mode == 1) { + setWindowImage(mode, varval(), true); + } else { + setWindowImageEx(mode, varval()); + } + + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode53() { + vc27_resetSprite(); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode54() { + stopAnimate(varval()); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode55() { + varval(); + varval(); + varval(); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode56() { + varval(); + varval(); + varval(); + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode57() { + uint16 windowNum = varval(); + uint16 vgaSpriteId = varval(); + int16 x = varval(); + int16 y = varval(); + uint16 palette = varval(); + + _lockWord |= 0x40; + animate(windowNum, 0, vgaSpriteId, x, y, palette); + _lockWord &= ~0x40; + + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode62() { + int32 zoneNum = varval(); + + _lockWord |= 0x80; + + vc29_stopAllSounds(); + + _hitCalled = 0; + _oneClick = 0; + + loadZone(zoneNum); + + setWindowImage(2, 2); + + _copyScnFlag = 0; + _vgaSpriteChanged = 0; + + _lockWord &= ~0x80; + + setScriptReturn(true); +} + +void AGOSEngine_PN::opn_opcode63() { + int a = readfromline(); + switch (a) { + case 65: + setScriptReturn(inventoryOn(varval())); + break; + case 64: + setScriptReturn((_lockWord & 0x10) != 0); + break; + case 63: + setScriptReturn(inventoryOff()); + break; + default: + error("opn_opcode63: unknown code %d", a); + } +} + +int AGOSEngine_PN::inventoryOn(int val) { + writeVariable(210, val); + if (_lockWord & 0x10) { + iconPage(); + } else { + _lockWord |= 0x10; + _hitAreaList = _invHitAreas; + + _windowArray[2]->textColor = 0; + windowPutChar(_windowArray[2], 13); + + clearVideoWindow(4, 0); + drawIconHitBar(); + + _objects = _variableArray[211]; + _objectCountS = -1; + iconPage(); + } + return 1; +} + +int AGOSEngine_PN::inventoryOff() { + if (_lockWord & 0x10) { + _windowArray[2]->textColor = 15; + + restoreBlock(48, 2, 272, 130); + + _hitAreaList = _hitAreas; + _lockWord &= ~0x10; + _vgaSpriteChanged++; + } + return 1; +} + +// ----------------------------------------------------------------------- +// Personal Nightmare Script Code +// ----------------------------------------------------------------------- + + +static int bitvalue[8] = {128, 64, 32, 16, 8, 4, 2, 1}; + +int AGOSEngine_PN::bitextract(uint32 ptr, int offs) { + return ((bitvalue[offs % 8] & _dataBase[ptr + offs / 8]) != 0); +} + +uint16 AGOSEngine_PN::getptr(uint32 pos) { + if (pos > _dataBaseSize) + error("getptr: Read beyond EOF (%d)", pos); + return (int)READ_LE_UINT16(_dataBase + pos); +} + +uint32 AGOSEngine_PN::getlong(uint32 pos) { + if (pos > _dataBaseSize) + error("getlong: Read beyond EOF (%d)", pos); + return (uint32)READ_LE_UINT24(_dataBase + pos); +} + +int AGOSEngine_PN::varval() { + int a; + int b; + + if ((a = readfromline()) < 247) { + return a; + } + + switch (a) { + case 249: + b = readfromline(); + return((int)(b + 256 * readfromline())); + break; + case 250: + return(readfromline()); + case 251: + return((int)_variableArray[varval()]); + case 252: + b = varval(); + return((int)_dataBase[_quickptr[0] + b * _quickshort[0] + varval()]); + case 254: + b = varval(); + return((int)_dataBase[_quickptr[3] + b * _quickshort[2] + varval()]); + case 247: + b = varval(); + return((int)getptr(_quickptr[11] + (b * _quickshort[4]) + (2 * varval()))); + case 248: + b = varval(); + return((int)getptr(_quickptr[12] + (b * _quickshort[5]) + (2 * varval()))); + case 253: + b = varval(); + return(bitextract((int32)_quickptr[1] + b * _quickshort[1], varval())); + case 255: + b = varval(); + return(bitextract((int32)_quickptr[4] + b * _quickshort[3], varval())); + default: + error("VARVAL : Illegal code encountered"); + } +} + +void AGOSEngine_PN::writeval(uint8 *ptr, int val) { + uint8 *savpt = _workptr; + int lsav = _linct, a, b, x; + _workptr = ptr; + _linct = 255; + + if ((a = readfromline()) < 247) + error("Write to constant"); + + switch (a) { + case 249: + error("Write to constant"); + break; + case 250: + error("Write to constant"); + break; + case 251: + _variableArray[varval()] = val; + break; + case 252: + b = varval(); + _dataBase[_quickptr[0] + b * _quickshort[0] + varval()] = val; + break; + case 254: + b = varval(); + _dataBase[_quickptr[3] + b * _quickshort[2] + varval()] = val; + break; + case 247: + b = varval(); + x = _quickptr[11] + b * _quickshort[4] + varval() * 2; + WRITE_LE_UINT16(_dataBase + x, val); + break; + case 248: + b = varval(); + x = _quickptr[12] + b * _quickshort[5] + varval() * 2; + WRITE_LE_UINT16(_dataBase + x, val); + break; + case 253: + b = varval(); + setbitf((uint32)_quickptr[1] + b * _quickshort[1], varval(), val); + break; + case 255: + b = varval(); + setbitf((uint32)_quickptr[4] + b * _quickshort[3], varval(), val); + break; + default: + error("WRITEVAL : undefined evaluation"); + } + _linct = lsav; + _workptr = savpt; +} + +void AGOSEngine_PN::setbitf(uint32 ptr, int offs, int val) { + ptr += offs / 8; + _dataBase[ptr] &= (255 - bitvalue[offs % 8]); + if (val != 0) + _dataBase[ptr] |= bitvalue[offs % 8]; +} + +int AGOSEngine_PN::actCallD(int n) { + int pf[8]; + funcentry(pf, n); + addstack(-1); + funccpy(pf); + setposition(n, 0); + return(doline(1)); +} + +int AGOSEngine_PN::doaction() { + if (_linct == 0) + return 0; + + _opcode = readfromline(); + + if (_opcode > 63) { + return (actCallD(_opcode - 64)); + } + + setScriptReturn(0); + executeOpcode(_opcode); + delay(0); + + return getScriptReturn(); +} + +int AGOSEngine_PN::doline(int needsave) { + int x; + jmp_buf *ljmpbuff = NULL; + jmp_buf *mybuf; + + mybuf = (jmp_buf *)malloc(sizeof(jmp_buf)); + if (mybuf == NULL) + error("Out of memory - stack overflow"); + + if ((x = setjmp(*mybuf)) > 0) { + dumpstack(); + _cjmpbuff = ljmpbuff; + free((char *)mybuf); + return (x - 1); + } + + if (x == -1) { + _cjmpbuff = mybuf; + goto carryon; + } + ljmpbuff = _cjmpbuff; + _cjmpbuff = mybuf; + if (needsave) + _stackbase->savearea = mybuf; + +nln: _linct = ((*_linebase) & 127) - 1; + _workptr = _linebase + 1; + if (*_linebase > 127) { + x = varval(); + if (x != (int)_variableArray[1]) + goto skipln; + } + +carryon: + while((x = doaction()) && !shouldQuit()); + +skipln: _linebase += 127 & *_linebase; + _linembr++; + + if (!shouldQuit()) + goto nln; + + return 0; +} + +int AGOSEngine_PN::findentry() { + int stepmt; + int curObj = 0; + uint32 ofs = _quickptr[11]; + int c1, c2; + + c1 = varval(); + c2 = varval(); + stepmt = _quickshort[4]; + + while (curObj < _quickshort[6]) { + if (((c1 == 255) || (c1 == getptr(ofs))) && + (c2 == getptr(ofs + 2))) { + _variableArray[23] = curObj; + return 1; + } + curObj++; + ofs += stepmt; + } + return 0; +} + +int AGOSEngine_PN::findset() { + int curObj = _fnst; + int c1, c2, c3, c4; + int stepmt = _quickshort[4]; + uint32 ofs = _quickptr[11] + stepmt * curObj; + c1 = varval(); + c2 = varval(); + c3 = varval(); + c4 = varval(); + while (curObj < _quickshort[6]) { + if (((c1 ==255) || (c1 == getptr(ofs))) && + ((c2 == 255) || (c2 == getptr(ofs + 2))) && + ((c3 == 255) || (c3 == getptr(ofs + 4))) && + ((c4 == 255) || (c4 == getptr(ofs + 6)))) { + _variableArray[23] = curObj; + _fnst = curObj + 1; + return 1; + } + curObj++; + ofs += stepmt; + } + return 0; +} + +void AGOSEngine_PN::funccpy(int *store) { + int a = 0; + int b = 24; + + while (a < 8) { + _variableArray[b++] = *store++; + a++; + } +} + +void AGOSEngine_PN::funcentry(int *store, int procn) { + int ct = 0; + int nprm; + + nprm = _dataBase[getlong(_quickptr[6] + 3L * procn)]; + while (ct < nprm) { + *store++ = varval(); + ct++; + } +} + +int AGOSEngine_PN::gvwrd(uint8 *wptr, int mask) { + int val, code, q = _dataBase[57]; + uint8 *vocbase = _dataBase + getlong(15); + while (*vocbase != 255) { + if (*vocbase < 128) { + val = vocbase[q] + 256 * vocbase[q + 1]; + code = vocbase[q + 2]; + } + if (wrdmatch(vocbase, mask, wptr, code)) + return val; + vocbase += (*vocbase > 127) ? q : q + 3; + } + return -1; +} + +int AGOSEngine_PN::samewrd(uint8 *w1, uint8 *w2, int ln) { + int ct = 0; + + while (ct < ln) { + if (toupper(*w1) > toupper(*w2)) + return 1; + if (toupper(*w1) < toupper(*w2)) + return -1; + ct++; + w1++; + w2++; + } + return 0; +} + +int AGOSEngine_PN::setposition(int process, int line) { + uint8 *ourptr; + int np; + int ct = 0; + ourptr = _dataBase + getlong(_quickptr[6] + 3L * process); + np = *ourptr++; + while (ct < line) { + ourptr += (127 & *ourptr); + ct++; + } +x1: _linebase = ourptr; + _linct = (127 & (*ourptr)) - 1; + if (*ourptr++ > 127) { + ct = varval(); + if (ct != (int)_variableArray[1]) { + ourptr += _linct - 1; + line++; + goto x1; + } + } + _linembr = line; + _procnum = process; + _variableArray[0] = process; + _workptr = ourptr; + return np; +} + +int AGOSEngine_PN::wrdmatch(uint8 *word1, int mask1, uint8 *word2, int mask2) { + uint8 sv; + + if ((mask1 & mask2) == 0) + return 0; + + sv = *word1; + *word1 &= 127; + if (samewrd(word1, word2, _dataBase[57])) { + *word1 = sv; + return 0; + } + *word1 = sv; + return 1; +} + +// ----------------------------------------------------------------------- +// Personal Nightmare Stack Code +// ----------------------------------------------------------------------- + +void AGOSEngine_PN::addstack(int type) { + struct stackframe *a; + int pt, ct = 0; + + a = (struct stackframe *)malloc(sizeof(struct stackframe)); + if (a == NULL) + error("Out of memory - stack overflow"); + + a->nextframe = _stackbase; + _stackbase = a; + pt = 0; + while (ct < 6) + a->flag[ct++] = _variableArray[pt++]; + ct = 0; + pt = 24; + while (ct < 8) + a->param[ct++] = _variableArray[pt++]; + a->classnum = type; + a->ll = _linct; + a->linenum = _linembr; + a->linpos = _workptr; + a->lbase = _linebase; + a->process = _procnum; +} + +void AGOSEngine_PN::dumpstack() { + struct stackframe *a; + + if (_stackbase == NULL) + error("Stack underflow or unknown longjmp"); + + a = _stackbase->nextframe; + free((char *)_stackbase); + _stackbase = a; +} + +void AGOSEngine_PN::junkstack() { + struct stackframe *a; + + if (_stackbase == NULL) + error("Stack underflow or unknown longjmp"); + + a = _stackbase->nextframe; + if (_stackbase->classnum == -1) + free((char *)_stackbase->savearea); + free((char *)_stackbase); + _stackbase = a; +} + +void AGOSEngine_PN::popstack(int type) { + int a = 0, b; + + while ((_stackbase != NULL) && (_stackbase->classnum != type)) + junkstack(); + + if (_stackbase == NULL) + error("Stack underflow or unknown longjmp"); + + _linct = _stackbase->ll; + _linebase = _stackbase->lbase; + _workptr = _stackbase->linpos; + _procnum = _stackbase->process; + _linembr = _stackbase->linenum; + b = 0; + while (a < 6) + _variableArray[b++] = _stackbase->flag[a++]; + b = 24; + a = 0; + while (a < 8) + _variableArray[b++] = _stackbase->param[a++]; +} + +} // End of namespace AGOS diff --git a/engines/agos/sound.cpp b/engines/agos/sound.cpp index 4f56daf1cf7..77324a072d9 100644 --- a/engines/agos/sound.cpp +++ b/engines/agos/sound.cpp @@ -389,6 +389,11 @@ Sound::Sound(AGOSEngine *vm, const GameSpecificSettings *gss, Audio::Mixer *mixe _ambientPlaying = 0; + _soundQueuePtr = 0; + _soundQueueNum = 0; + _soundQueueSize = 0; + _soundQueueFreq = 0; + if (_vm->getFeatures() & GF_TALKIE) { loadVoiceFile(gss); @@ -649,6 +654,10 @@ bool Sound::hasVoice() const { return _hasVoiceFile; } +bool Sound::isSfxActive() const { + return _mixer->isSoundHandleActive(_effectsHandle); +} + bool Sound::isVoiceActive() const { return _mixer->isSoundHandleActive(_voiceHandle); } @@ -660,6 +669,10 @@ void Sound::stopAllSfx() { _ambientPlaying = 0; } +void Sound::stopSfx() { + _mixer->stopHandle(_effectsHandle); +} + void Sound::stopVoice() { _mixer->stopHandle(_voiceHandle); } @@ -686,8 +699,33 @@ void Sound::ambientPause(bool b) { } } +// Personal Nightmare specific +void Sound::handleSound() { + if (_soundQueuePtr && !isSfxActive()) { + playRawData(_soundQueuePtr, _soundQueueNum, _soundQueueSize, _soundQueueFreq); + + _vm->_sampleWait = 1; + _vm->_sampleEnd = 1; + _soundQueuePtr = 0; + _soundQueueNum = 0; + _soundQueueSize = 0; + _soundQueueFreq = 0; + } +} + +void Sound::queueSound(byte *ptr, uint16 sound, uint32 size, uint16 freq) { + if (_effectsPaused) + return; + + // Only a single sound can be queued + _soundQueuePtr = ptr; + _soundQueueNum = sound; + _soundQueueSize = size; + _soundQueueFreq = freq; +} + // Elvira 1/2 and Waxworks specific -void Sound::playRawData(byte *soundData, uint sound, uint size) { +void Sound::playRawData(byte *soundData, uint sound, uint size, uint freq) { if (_effectsPaused) return; @@ -695,9 +733,9 @@ void Sound::playRawData(byte *soundData, uint sound, uint size) { memcpy(buffer, soundData, size); if (_vm->getPlatform() == Common::kPlatformPC) - _mixer->playRaw(Audio::Mixer::kSFXSoundType, &_effectsHandle, buffer, size, 8000, Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE); + _mixer->playRaw(Audio::Mixer::kSFXSoundType, &_effectsHandle, buffer, size, freq, Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE); else - _mixer->playRaw(Audio::Mixer::kSFXSoundType, &_effectsHandle, buffer, size, 8000, Audio::Mixer::FLAG_AUTOFREE); + _mixer->playRaw(Audio::Mixer::kSFXSoundType, &_effectsHandle, buffer, size, freq, Audio::Mixer::FLAG_AUTOFREE); } // Feeble Files specific diff --git a/engines/agos/sound.h b/engines/agos/sound.h index 1e5a8a47d21..be7aa6c0767 100644 --- a/engines/agos/sound.h +++ b/engines/agos/sound.h @@ -26,6 +26,7 @@ #ifndef AGOS_SOUND_H #define AGOS_SOUND_H +#include "sound/audiostream.h" #include "sound/mixer.h" #include "agos/intern.h" #include "common/str.h" @@ -60,12 +61,23 @@ private: bool _hasEffectsFile; bool _hasVoiceFile; - uint _ambientPlaying; + uint16 _ambientPlaying; + // Personal Nightmare specfic + byte *_soundQueuePtr; + uint16 _soundQueueNum; + uint32 _soundQueueSize; + uint16 _soundQueueFreq; public: Sound(AGOSEngine *vm, const GameSpecificSettings *gss, Audio::Mixer *mixer); ~Sound(); + enum TypeFlags { + TYPE_AMBIENT = 1 << 0, + TYPE_SFX = 1 << 1, + TYPE_SFX5 = 1 << 2 + }; + void loadVoiceFile(const GameSpecificSettings *gss); void loadSfxFile(const GameSpecificSettings *gss); @@ -77,8 +89,12 @@ public: void playEffects(uint sound); void playAmbient(uint sound); + // Personal Nightmare specfic + void handleSound(); + void queueSound(byte *ptr, uint16 sound, uint32 size, uint16 freq); + // Elvira 1/2 and Waxworks specific - void playRawData(byte *soundData, uint sound, uint size); + void playRawData(byte *soundData, uint sound, uint size, uint freq); // Feeble Files specific void playAmbientData(byte *soundData, uint sound, uint pan, uint vol); @@ -89,8 +105,10 @@ public: void switchVoiceFile(const GameSpecificSettings *gss, uint disc); bool hasVoice() const; + bool isSfxActive() const; bool isVoiceActive() const; void stopAllSfx(); + void stopSfx(); void stopSfx5(); void stopVoice(); void stopAll(); diff --git a/engines/agos/string.cpp b/engines/agos/string.cpp index 3c651cce0c3..8bfaa1e4eb0 100644 --- a/engines/agos/string.cpp +++ b/engines/agos/string.cpp @@ -910,4 +910,649 @@ void AGOSEngine_Waxworks::printBox() { changeWindow(0); } +// Personal Nightmare specific + +uint32 AGOSEngine_PN::ftext(uint32 base, int n) { + uint32 b = base; + int ct = n; + while (ct) { + while(_textBase[b++]); + ct--; + } + return b; +} + +char *AGOSEngine_PN::unctok(char *c, int n) { + int x; + uint8 *tokbase; + tokbase = _textBase + getlong(30); + x = n; + while (x-=(*tokbase++ > 127)); + while (*tokbase < 128) + *c++=*tokbase++; + *c++ = *tokbase & 127; + *c = 0; + return c; +} + +void AGOSEngine_PN::uncomstr(char *c, uint32 x) { + if (x > _textBaseSize) + error("UNCOMSTR: TBASE over-run\n"); + while (_textBase[x]) { + if (_textBase[x] < 244) { + c = unctok(c, _textBase[x]); + } else { + c = unctok(c, (_textBase[x] - 244) * 254 + _textBase[x + 1] - 1); + x++; + } + x++; + } + *c++ = 13; + *c = 0; +} + +static const char *objectNames[30] = { + "\0", + "Take \0", + "Inventory\r", + "Open \0", + "Close \0", + "Lock \0", + "Unlock \0", + "Examine \0", + "Look in \0", + "Exits \r", + "Look\r", + "Wait\r", + "Pause\r", + "\0", + "Save\r", + "Restore\r", + "\0", + "N\r", + "NE\r", + "E\r", + "SE\r", + "S\r", + "SW\r", + "W\r", + "NW\r", + "INVENTORY\r", + "ROOM DESCRIPTION\r", + "x\r", + "MORE\r", + "CLOSE\r" +}; + +void AGOSEngine_PN::getObjectName(char *v, uint16 x) { + if (x & 0x8000) { + x &= ~0x8000; + if (x > getptr(51)) + error("getObjectName: Object %d out of range", x); + uncomstr(v, ftext(getlong(27), x * _dataBase[47])); + } else { + assert(x < 30); + strcpy(v, objectNames[x]); + } +} + +void AGOSEngine_PN::pcl(const char *s) { + strcat(_sb, s); + if (strchr(s, '\n') == 0) { + for (char *str = _sb; *str; str++) + windowPutChar(_windowArray[_curWindow], *str); + strcpy(_sb, ""); + } +} + +void AGOSEngine_PN::pcf(uint8 ch) { + int ct = 0; + if (ch == '[') + ch = '\n'; + if (ch == 0) + return; /* Trap any C EOS chrs */ + if (ch == 255) { + _bp = 0; + _xofs = 0; + return; /* pcf(255) initialises the routine */ + } /* pcf(254) flushes its working _buffer */ + if (ch != 254) { + if ((ch != 32) || (_bp + _xofs != 50)) + _buffer[_bp++] = ch; + } + if ((ch != 254) && (!isspace(ch)) && (_bp < 60)) + return; + /* We know have a case of needing to print the text */ + if (_bp + _xofs > 50) { + pcl("\n"); + if (_buffer[0] == ' ') + ct = 1; /* Skip initial space */ + /* Note ' ' will give a single start of line space */ + _xofs = 0; + } + _buffer[_bp] = 0; + pcl(_buffer + ct); + _xofs += _bp; + _bp = 0; + if (ch == '\n') + _xofs = 0; /* At Newline! */ +} + +void AGOSEngine_PN::patok(int n) { + int x; + uint8 *tokbase; + tokbase = _textBase + getlong(30); + x = n; + while (x -= (*tokbase++ > 127)); + while (*tokbase < 128) + pcf(*tokbase++); + pcf((uint8)(*tokbase & 127)); +} + +void AGOSEngine_PN::pmesd(int n) { + ptext(ftext(getlong(24), n)); +} + +void AGOSEngine_PN::plocd(int n, int m) { + if (n > getptr(53)) + error("Location out of range"); + ptext(ftext(getlong(21), n * _dataBase[48] + m)); +} + +void AGOSEngine_PN::pobjd(int n, int m) { + if (n > getptr(51)) + error("Object out of range"); + ptext(ftext(getlong(27), n * _dataBase[47] + m)); +} + +void AGOSEngine_PN::ptext(uint32 tptr) { + if (tptr > _textBaseSize) + error("ptext: attempt to print beyond end of TBASE\n"); + + while (_textBase[tptr]) { + if (_textBase[tptr] < 244) { + patok(_textBase[tptr++]); + } else { + patok((_textBase[tptr] - 244) * 254 + _textBase[tptr + 1] - 1); + tptr += 2; + } + } +} + +const uint8 characters[11][80] = { +// PETERMASON + { + 118, 225, + 91, 118, + 94, 124, + 236, 161, + 241, 166, + 168, 4, + 138, 46, + 139, 46, + 249, 50, + 38, 56, + 80, 59, + 149, 69, + 37, 77, + 93, 93, + 86, 95, + 0, + 0, + 58, 130, + 62, 178, + 83, 95, + 0, + 121, 58, + 122, 59, + 126, 60, + 124, 61, + 240, 62, + 123, 63, + 0 + }, +// JBLANDFORD + { + 0, + 0, + 0, + 0 + }, +// SBLANDFORD + { + 120, 223, + 94, 126, + 112, 134, + 45, 152, + 241, 166, + 168, 3, + 150, 26, + 220, 29, + 138, 42, + 139, 47, + 249, 50, + 38, 56, + 230, 64, + 37, 77, + 93, 94, + 86, 96, + 0, + 0, + 58, 129, + 59, 112, + 83, 96, + 81, 106, + 62, 169, + 0, + 121, 54, + 122, 55, + 119, 56, + 118, 57, + 0 + }, +// MRJONES + { + 121, 218, + 91, 118, + 253, 121, + 154, 138, + 235, 173, + 236, 161, + 241, 165, + 168, 0, + 150, 21, + 36, 33, + 138, 42, + 249, 50, + 80, 60, + 4, 60, + 37, 78, + 68, 33, + 93, 92, + 101, 109, + 0, + 36, 35, + 68, 90, + 0, + 58, 128, + 59, 111, + 62, 182, + 0, + 122, 13, + 126, 14, + 124, 15, + 240, 16, + 120, 17, + 119, 18, + 118, 19, + 52, 20, + 125, 21, + 127, 22, + 123, 23, + 117, 24, + 0 + }, +// MRSJONES + { + 122, 219, + 91, 119, + 253, 123, + 112, 136, + 154, 137, + 95, 142, + 45, 152, + 109, 155, + 235, 160, + 168, 1, + 151, 13, + 145, 15, + 150, 22, + 220, 28, + 36, 33, + 138, 43, + 13, 51, + 80, 59, + 230, 64, + 149, 69, + 86, 100, + 0, + 36, 36, + 0, + 58, 127, + 62, 179, + 83, 100, + 81, 102, + 0, + 121, 25, + 126, 26, + 124, 27, + 120, 28, + 119, 29, + 118, 30, + 52, 31, + 125, 32, + 127, 33, + 123, 34, + 117, 35, + 0 + }, +// MRROBERTS + { + 123, 229, + 91, 117, + 253, 120, + 94, 125, + 112, 134, + 109, 156, + 235, 172, + 236, 162, + 241, 165, + 168, 3, + 36, 33, + 249, 50, + 38, 56, + 80, 58, + 37, 75, + 34, 81, + 68, 33, + 101, 109, + 0, + 36, 40, + 68, 88, + 0, + 59, 111, + 62, 181, + 0, + 0 + }, +// POSTMISTRESS + { + 124, 221, + 91, 119, + 253, 122, + 112, 136, + 95, 142, + 130, 149, + 109, 155, + 235, 176, + 220, 29, + 36, 33, + 138, 43, + 13, 51, + 80, 57, + 149, 68, + 37, 73, + 34, 33, + 68, 33, + 86, 100, + 0, + 36, 39, + 34, 80, + 68, 86, + 0, + 58, 130, + 62, 181, + 83, 100, + 81, 103, + 0, + 121, 41, + 122, 42, + 126, 43, + 240, 44, + 120, 45, + 119, 46, + 118, 47, + 52, 48, + 123, 49, + 83, 50, + 117, 51, + 0 + }, +// MWILLIAMS + { + 125, 227, + 94, 124, + 95, 141, + 241, 166, + 168, 4, + 150, 26, + 38, 54, + 4, 60, + 230, 65, + 149, 68, + 37, 76, + 101, 109, + 0, + 230, 63, + 0, + 59, 112, + 62, 183, + 0, + 240, 71, + 120, 72, + 118, 73, + 52, 74, + 117, 75, + 0 + }, +// TONY + { + 126, 220, + 95, 143, + 130, 149, + 45, 153, + 109, 154, + 235, 158, + 241, 166, + 168, 2, + 145, 15, + 150, 24, + 220, 20, + 36, 20, + 4, 60, + 37, 79, + 86, 97, + 0, + 150, 23, + 220, 27, + 36, 34, + 0, + 83, 97, + 0, + 121, 36, + 122, 37, + 124, 38, + 240, 39, + 52, 40, + 0 + }, +// PIG + { + 127, 228, + 112, 133, + 45, 153, + 235, 157, + 236, 163, + 241, 165, + 36, 33, + 80, 58, + 34, 81, + 68, 33, + 86, 98, + 0, + 36, 37, + 68, 90, + 0, + 62, 184, + 83, 98, + 0, + 121, 76, + 122, 77, + 126, 78, + 124, 79, + 240, 80, + 120, 81, + 118, 82, + 52, 83, + 125, 84, + 123, 85, + 83, 86, + 117, 87, + 0 + }, +// JUDY + { + 0, + 0, + 0, + 240, 52, + 117, 53, + 0 + } +}; + +void AGOSEngine_PN::getResponse(uint16 charNum, uint16 objNum, uint16 &msgNum1, uint16 &msgNum2) { + const uint8 *ptr; + uint16 num; + + msgNum1 = 0; + msgNum2 = 0; + + if (charNum == 83) + charNum += 45; + + if (charNum < 118 || charNum > 128) { + return; + } + + ptr = characters[charNum - 118]; + + while ((num = *ptr++) != 0) { + if (num == objNum) { + msgNum1 = *ptr++; + msgNum1 += 400; + + while ((num = *ptr++) != 0) + ptr++; + break; + } + ptr++; + } + + while ((num = *ptr++) != 0) { + if (num == objNum) { + msgNum2 = *ptr++; + msgNum2 += 400; + + if (msgNum1 == 569) + msgNum1 += 400; + if (msgNum2 == 0) + msgNum2 = msgNum1; + return; + } + ptr++; + } + + if (objNum >= 200) + msgNum1 = 0; + + objNum -= 200; + while ((num = *ptr++) != 0) { + if (num == objNum) { + msgNum1 = *ptr++; + msgNum1 += 400; + + if (msgNum1 == 569) + msgNum1 += 400; + if (msgNum2 == 0) + msgNum2 = msgNum1; + return; + } + ptr++; + } + + objNum += 200; + while ((num = *ptr++) != 0) { + if (num == objNum) { + msgNum1 = *ptr++; + msgNum1 += 200; + + if (msgNum1 == 569) + msgNum1 += 400; + if (msgNum2 == 0) + msgNum2 = msgNum1; + return; + } + ptr++; + } + + if (msgNum1 == 569) + msgNum1 += 400; + if (msgNum2 == 0) + msgNum2 = msgNum1; +} + +char *AGOSEngine_PN::getMessage(char *msg, uint16 num) { + char *origPtr, *strPtr2, *strPtr1 = msg; + uint8 count; + + getObjectName(strPtr1, num); + if (!(num & 0x8000)) { + return msg; + } + + if (strPtr1[0] == 0x41 || strPtr1[0] == 0x61) { + if (strPtr1[1] != 0x20) + strPtr1 += 2; + } else if (strPtr1[0] == 0x54 || strPtr1[0] == 0x74) { + if (strPtr1[1] == 0x68 && + strPtr1[2] == 0x65 && + strPtr1[3] == 0x20) + strPtr1 += 4; + } + + origPtr = strPtr1; + while (strPtr1[0] != 13) + strPtr1++; + + strPtr1[0] = 32; + strPtr1[1] = 13; + strPtr1[2] = 0; + + if (_lockWord & 0x10) { + strPtr1 = origPtr; + count = 6; + while (strPtr1[0] != 0) { + if (strPtr1[0] == 32) { + count = 6; + } else { + count--; + if (count == 0) { + char *tmpPtr = strPtr1; + strPtr2 = strPtr1; + + while (strPtr2[0] != 0 && strPtr2[0] != 32) + strPtr2++; + + while (strPtr2[0] != 0) { + strPtr1[0] = strPtr2[0]; + strPtr2++; + strPtr1++; + } + + strPtr1[0] = strPtr2[0]; + strPtr2++; + strPtr1++; + + strPtr1 = tmpPtr; + count = 6; + } + } + strPtr1++; + } + } + + return origPtr; +} + } // End of namespace AGOS diff --git a/engines/agos/verb.cpp b/engines/agos/verb.cpp index 3c71e539ef3..6f91562a872 100644 --- a/engines/agos/verb.cpp +++ b/engines/agos/verb.cpp @@ -496,6 +496,18 @@ bool AGOSEngine::isBoxDead(uint hitarea) { return (ha->flags & kBFBoxDead) == 0; } +void AGOSEngine::defineBox(uint16 id, uint16 x, uint16 y, uint16 height, uint16 width, uint16 msg1, uint16 msg2, uint16 flags) { + HitArea *ha = _hitAreaList + id; + ha->x = x; + ha->y = y; + ha->width = width; + ha->height = height; + ha->msg1 = msg1; + ha->msg2 = msg2; + ha->flags = flags; + ha->id = ha->priority = id; +} + void AGOSEngine::defineBox(int id, int x, int y, int width, int height, int flags, int verb, Item *itemPtr) { HitArea *ha; undefineBox(id); @@ -985,6 +997,18 @@ void AGOSEngine::invertBox(HitArea *ha, byte a, byte b, byte c, byte d) { color ^= 2; src[i] = color; } + } else if (getGameType() == GType_PN) { + if (getPlatform() == Common::kPlatformPC) { + if (color != 15) { + color ^= 7; + src[i] = color; + } + } else { + if (color != 14) { + color ^= 15; + src[i] = color; + } + } } else { if (a >= color && b < color) { if (c >= color) @@ -1003,4 +1027,263 @@ void AGOSEngine::invertBox(HitArea *ha, byte a, byte b, byte c, byte d) { _lockWord &= ~0x8000; } +// Personal Nightmare specific +void AGOSEngine_PN::boxController(uint x, uint y, uint mode) { + HitArea *best_ha; + HitArea *ha = _hitAreaList; + + best_ha = NULL; + + do { + if (!(ha->flags & kOBFBoxDisabled)) { + if (x >= ha->x && y >= ha->y && x - ha->x < ha->width && y - ha->y < ha->height && + best_ha == NULL) { + best_ha = ha; + } else { + if (ha->flags & kOBFBoxSelected) { + hitarea_leave(ha , true); + ha->flags &= ~kOBFBoxSelected; + } + } + } else { + ha->flags &= ~kOBFBoxSelected; + } + } while (ha++, ha->id != 0xFFFF); + + if (mode != 0) { + _lastHitArea = best_ha; + } + + if (best_ha == NULL) { + return; + } + + if (best_ha->flags & kOBFInvertTouch && !(best_ha->flags & kOBFBoxSelected)) { + hitarea_leave(best_ha, false); + best_ha->flags |= kOBFBoxSelected; + } +} + +void AGOSEngine_PN::mouseHit() { + if (_hitCalled == 5) { + execMouseHit(NULL); + } else { + boxController(_mouse.x, _mouse.y, 1); + if (_hitCalled == 4 || _lastHitArea != 0) { + execMouseHit(_lastHitArea); + } + } + _hitCalled = 0; + _oneClick = 0; +} + +void AGOSEngine_PN::execMouseHit(HitArea *ha) { + if (_hitCalled == 1) { + if (ha->flags & kOBFUseMessageList) + hitBox11(ha); + else if (ha->flags & kOBFMoreBox) + hitBox9(ha); + else if (ha->flags & kOBFExit) + hitBox7(ha); + else if (ha->flags & kOBFUseEmptyLine) + hitBox2(ha); + else + hitBox1(ha); + } else if (_hitCalled == 2) { + if (ha->flags & (kOBFObject | kOBFInventoryBox | kOBFRoomBox)) + hitBox3(ha); + else if (ha->flags & kOBFUseMessageList) + hitBox11(ha); + else if (ha->flags & kOBFMoreBox) + hitBox9(ha); + else if (ha->flags & kOBFExit) + hitBox7(ha); + else if (ha->flags & kOBFUseEmptyLine) + hitBox2(ha); + else + hitBox1(ha); + } else if (_hitCalled == 3) { + if ((ha->flags & kOBFDraggable) && _dragFlag == 0) { + _dragFlag = 1; + _dragStore = ha; + _needHitAreaRecalc++; + } + } else if (_hitCalled == 4) { + _dragFlag = 0; + _hitCalled = 0; + _oneClick = 0; + _mouseDown = 0; + _needHitAreaRecalc++; + if (ha != 0) { + if (ha->flags & kOBFInventoryBox) + hitBox5(ha); + else if (ha->flags & kOBFRoomBox) + hitBox6(ha); + } else if (_lockWord & 10) { + hitBox8(ha); + } + } else { + _hitCalled = 0; + if (_mouseString == 0) { + _mouseString = (const char *)"\r"; + } + } +} + +void AGOSEngine_PN::hitBox1(HitArea *ha) { + if (_mouseString) + return; + + _mouseString = getMessage(_objectName1, ha->msg1); + if (_intputCounter) { + char *msgPtr = getMessage(_objectName1, ha->msg1); + while (*msgPtr != 13) + msgPtr++; + *msgPtr = 0; + } else if (!(ha->flags & kOBFNoShowName)) { + _mousePrintFG++; + _mouseString1 = _mouseString; + _mouseString = (const char*)"showname \0"; + + } +} + +void AGOSEngine_PN::hitBox2(HitArea *ha) { + if (!_intputCounter) + hitBox1(ha); +} + +void AGOSEngine_PN::hitBox3(HitArea *ha) { + if (!_intputCounter) + hitBox4(ha); +} + +void AGOSEngine_PN::hitBox4(HitArea *ha) { + if (_mouseString) + return; + + uint16 num = ha->msg1 & ~0x8000; + if ((_lockWord & 0x10) && !(ha->flags & (kOBFInventoryBox | kOBFRoomBox)) && + !testContainer(num)) { + return; + } + + _mouseString = getMessage(_objectName2, ha->msg2); + _mouseString1 = getMessage(_objectName1, ha->msg1); + _mousePrintFG++; +} + +void AGOSEngine_PN::hitBox5(HitArea *ha) { + if (_intputCounter || _mouseString) + return; + + if (_dragStore && (_dragStore->flags & kOBFInventoryBox)) + return; + + _mousePrintFG++; + _mouseString = (const char *)"take \0"; + _mouseString1 = getMessage(_objectName1, _dragStore->msg1); + + if (_dragStore->flags & kOBFRoomBox) + _mouseString1 = (const char *)"all\r"; +} + +void AGOSEngine_PN::hitBox6(HitArea *ha) { + if (_intputCounter || _mouseString) + return; + + if (_dragStore->flags & kOBFRoomBox) + return; + + _mousePrintFG++; + _mouseString = (const char *)"drop \0"; + _mouseString1 = getMessage(_objectName1, _dragStore->msg1); + + if (_dragStore->flags & kOBFInventoryBox) + _mouseString1 = (const char *)"all\r"; +} + +void AGOSEngine_PN::hitBox7(HitArea *ha) { + if (_intputCounter) { + if (!(ha->flags & kOBFUseEmptyLine)) { + hitBox1(ha); + } + return; + } + + if (_mouseString) + return; + + _mousePrintFG++; + _mouseString1 = getMessage(_objectName1, ha->msg1); + + uint16 num = ha->msg1 & ~0x8000; + uint16 state = getptr(_quickptr[0] + num * _quickshort[0] + 2); + if (state == 3) { + _mouseString = (const char *)"unlock \0"; + } else if (state == 2) { + _mouseString = (const char *)"open \0"; + } else { + _mouseString = (const char *)"go through \0"; + } +} + +void AGOSEngine_PN::hitBox8(HitArea *ha) { + char *msgPtr; + + if (_intputCounter || _mouseString) + return; + + if (ha == 0 || _dragStore == ha) + return; + + uint16 num = ha->msg1 & ~0x8000; + if (!testSeen(num)) + return; + + msgPtr = getMessage(_objectName1, ha->msg1); + sprintf(_inMessage, " in %s", msgPtr); + _mouseString1 = _inMessage; + + msgPtr = getMessage(_objectName1, _dragStore->msg1); + + char *tmpPtr = _placeMessage; + while (*msgPtr != 0) { + if (*msgPtr != 13) + *tmpPtr++ = *msgPtr; + msgPtr++; + } + *tmpPtr = 0; + + sprintf(_placeMessage, " in %s", _placeMessage); + _mouseString = _placeMessage; +} + +void AGOSEngine_PN::hitBox9(HitArea *ha) { + if (_objectCountS == _objects) { + _objectCountS = -1; + } + iconPage(); +} + +static const char *messageList[9] = { + "North\r", + "East\r", + "South\r", + "West\r", + "Up\r", + "Down\r", + "Push grey button\r", + "Push red button\r", + "Go under car\r" +}; + +void AGOSEngine_PN::hitBox11(HitArea *ha) { + if (_intputCounter || _mouseString) + return; + + _mouseString = messageList[ha->msg1]; + _mousePrintFG++; +} + } // End of namespace AGOS diff --git a/engines/agos/vga.cpp b/engines/agos/vga.cpp index 2ad09fb4305..323e8de96c0 100644 --- a/engines/agos/vga.cpp +++ b/engines/agos/vga.cpp @@ -133,6 +133,7 @@ void AGOSEngine::setupVgaOpcodes() { memset(_vga_opcode_table, 0, sizeof(_vga_opcode_table)); switch (getGameType()) { + case GType_PN: case GType_ELVIRA1: case GType_ELVIRA2: case GType_WW: @@ -176,6 +177,27 @@ void AGOSEngine::runVgaScript() { } } +bool AGOSEngine_PN::ifObjectHere(uint16 a) { + if (getFeatures() & GF_DEMO) + return 0; + else + return _variableArray[39] == getptr(_quickptr[11] + a * _quickshort[4] + 2); +} + +bool AGOSEngine_PN::ifObjectAt(uint16 a, uint16 b) { + if (getFeatures() & GF_DEMO) + return 0; + else + return b == getptr(_quickptr[11] + a * _quickshort[4] + 2); +} + +bool AGOSEngine_PN::ifObjectState(uint16 a, int16 b) { + if (getFeatures() & GF_DEMO) + return 0; + else + return b == getptr(_quickptr[0] + a * _quickshort[0] + 2); +} + bool AGOSEngine::ifObjectHere(uint16 a) { Item *item; @@ -256,7 +278,7 @@ void AGOSEngine::setBitFlag(uint bit, bool value) { } int AGOSEngine::vcReadVarOrWord() { - if (getGameType() == GType_ELVIRA1) { + if (getGameType() == GType_PN || getGameType() == GType_ELVIRA1) { return vcReadNextWord(); } else { int16 var = vcReadNextWord(); @@ -289,6 +311,17 @@ void AGOSEngine::vcWriteVar(uint var, int16 value) { void AGOSEngine::vcSkipNextInstruction() { + static const byte opcodeParamLenPN[] = { + 0, 6, 2, 10, 6, 4, 2, 2, + 4, 4, 8, 2, 0, 2, 2, 2, + 0, 2, 2, 2, 0, 4, 2, 2, + 2, 8, 0, 10, 0, 8, 0, 2, + 2, 0, 0, 0, 0, 2, 4, 2, + 4, 4, 0, 0, 2, 2, 2, 4, + 4, 0, 18, 2, 4, 4, 4, 0, + 4 + }; + static const byte opcodeParamLenElvira1[] = { 0, 6, 2, 10, 6, 4, 2, 2, 4, 4, 8, 2, 0, 2, 2, 2, @@ -362,9 +395,12 @@ void AGOSEngine::vcSkipNextInstruction() { } else if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) { opcode = vcReadNextWord(); _vcPtr += opcodeParamLenWW[opcode]; - } else { + } else if (getGameType() == GType_ELVIRA1) { opcode = vcReadNextWord(); _vcPtr += opcodeParamLenElvira1[opcode]; + } else { + opcode = vcReadNextWord(); + _vcPtr += opcodeParamLenPN[opcode]; } if (_dumpVgaOpcodes) @@ -411,7 +447,7 @@ void AGOSEngine::vc3_loadSprite() { vgaSpriteId = vcReadNextWord(); } else { vgaSpriteId = vcReadNextWord(); - zoneNum = vgaSpriteId / 100; + zoneNum = (getGameType() == GType_PN) ? 0 : vgaSpriteId / 100; } x = vcReadNextWord(); @@ -444,8 +480,9 @@ void AGOSEngine::vc5_ifEqual() { } void AGOSEngine::vc6_ifObjectHere() { - if (!ifObjectHere(vcReadNextWord())) + if (!ifObjectHere(vcReadNextWord())) { vcSkipNextInstruction(); + } } void AGOSEngine::vc7_ifObjectNotHere() { @@ -610,7 +647,7 @@ void AGOSEngine::drawImage_init(int16 image, uint16 palette, int16 x, int16 y, u if (state.image < 0) state.image = vcReadVar(-state.image); - state.palette = palette * 16; + state.palette = (getGameType() == GType_PN) ? 0 : palette * 16; state.paletteMod = 0; state.x = x - _scrollX; @@ -645,7 +682,11 @@ void AGOSEngine::drawImage_init(int16 image, uint16 palette, int16 x, int16 y, u state.y_skip = 0; /* rows to skip = bl */ if (getFeatures() & GF_PLANAR) { - state.srcPtr = convertImage(&state, ((flags & 0x80) != 0)); + if (getGameType() == GType_PN) { + state.srcPtr = convertImage(&state, ((state.flags & (kDFCompressed | kDFCompressedFlip)) != 0)); + } + else + state.srcPtr = convertImage(&state, ((flags & 0x80) != 0)); // converted planar clip is already uncompressed if (state.flags & kDFCompressedFlip) { @@ -691,6 +732,36 @@ void AGOSEngine::drawImage_init(int16 image, uint16 palette, int16 x, int16 y, u drawImage(&state); } +void AGOSEngine::checkOnStopTable() { + VgaSleepStruct *vfs = _onStopTable, *vfs_tmp; + while (vfs->ident != 0) { + if (vfs->ident == _vgaCurSpriteId) { + VgaSprite *vsp = findCurSprite(); + animate(vsp->windowNum, vsp->zoneNum, vfs->id, vsp->x, vsp->y, vsp->palette, true); + vfs_tmp = vfs; + do { + memcpy(vfs_tmp, vfs_tmp + 1, sizeof(VgaSleepStruct)); + vfs_tmp++; + } while (vfs_tmp->ident != 0); + } else { + vfs++; + } + } +} + +void AGOSEngine::vc11_onStop() { + uint16 id = vcReadNextWord(); + + VgaSleepStruct *vfs = _onStopTable; + while (vfs->ident) + vfs++; + + vfs->ident = _vgaCurSpriteId; + vfs->codePtr = _vcPtr; + vfs->id = id; + vfs->zoneNum = _vgaCurZoneNum; +} + void AGOSEngine::vc12_delay() { uint16 num; @@ -728,7 +799,13 @@ void AGOSEngine::vc14_addToSpriteY() { void AGOSEngine::vc15_sync() { VgaSleepStruct *vfs = _waitSyncTable, *vfs_tmp; - uint16 id = vcReadNextWord(); + uint16 id; + + if (getGameType() == GType_PN) + id = _vgaCurSpriteId; + else + id = vcReadNextWord(); + while (vfs->ident != 0) { if (vfs->ident == id) { addVgaEvent(_vgaBaseDelay, ANIMATE_EVENT, vfs->codePtr, vfs->id, vfs->zoneNum); @@ -779,12 +856,13 @@ void AGOSEngine::checkWaitEndTable() { void AGOSEngine::vc17_waitEnd() { uint16 id = vcReadNextWord(); + uint16 zoneNum = (getGameType() == GType_PN) ? 0 : id / 100; VgaSleepStruct *vfs = _waitEndTable; while (vfs->ident) vfs++; - if (isSpriteLoaded(id, id / 100)) { + if (isSpriteLoaded(id, zoneNum)) { vfs->ident = id; vfs->codePtr = _vcPtr; vfs->id = _vgaCurSpriteId; @@ -867,12 +945,21 @@ void AGOSEngine::vc22_setPaletteOld() { b = vcReadNextWord(); + // PC version of Personal Nightmare uses standard EGA palette + if (getGameType() == GType_PN && getPlatform() == Common::kPlatformPC) + return; + num = 16; palptr = _displayPalette; _bottomPalette = 1; - if (getGameType() == GType_ELVIRA1) { + if (getGameType() == GType_PN) { + if (b > 128) { + b-= 128; + palptr = _displayPalette + 64; + } + } else if (getGameType() == GType_ELVIRA1) { if (b >= 1000) { b -= 1000; _bottomPalette = 0; @@ -990,6 +1077,7 @@ void AGOSEngine::vc24_setSpriteXY() { void AGOSEngine::vc25_halt_sprite() { checkWaitEndTable(); + checkOnStopTable(); VgaSprite *vsp = findCurSprite(); while (vsp->id != 0) { @@ -1046,6 +1134,12 @@ void AGOSEngine::vc27_resetSprite() { vfs++; } + vfs = _onStopTable; + while (vfs->ident) { + vfs->ident = 0; + vfs++; + } + vte = _vgaTimerList; while (vte->delay) { // Skip the animateSprites event in earlier games @@ -1082,13 +1176,12 @@ void AGOSEngine::vc27_resetSprite() { void AGOSEngine::vc28_playSFX() { uint16 sound = vcReadNextWord(); - uint16 channels = vcReadNextWord(); - uint16 frequency = vcReadNextWord(); + uint16 chans = vcReadNextWord(); + uint16 freq = vcReadNextWord(); uint16 flags = vcReadNextWord(); + debug(0, "vc28_playSFX: (sound %d, channels %d, frequency %d, flags %d)", sound, chans, freq, flags); - loadSound(sound); - - debug(0, "vc28_playSFX: (%d, %d, %d, %d)", sound, channels, frequency, flags); + loadSound(sound, freq, flags); } void AGOSEngine::vc29_stopAllSounds() { @@ -1154,8 +1247,23 @@ void AGOSEngine::clearVideoBackGround(uint16 num, uint16 color) { } } +void AGOSEngine_PN::clearVideoWindow(uint16 num, uint16 color) { + const uint16 *vlut = &_videoWindows[num * 4]; + uint16 xoffs = vlut[0] * 16; + uint16 yoffs = vlut[1]; + + Graphics::Surface *screen = _system->lockScreen(); + byte *dst = (byte *)screen->pixels + xoffs + yoffs * screen->pitch; + for (uint h = 0; h < vlut[3]; h++) { + memset(dst, color, vlut[2] * 16); + dst += screen->pitch; + } + _system->unlockScreen(); +} + void AGOSEngine_Simon2::clearVideoWindow(uint16 num, uint16 color) { const uint16 *vlut = &_videoWindows[num * 4]; + uint16 xoffs = vlut[0] * 16; uint16 yoffs = vlut[1]; uint16 dstWidth = _videoWindows[18] * 16; @@ -1243,6 +1351,10 @@ void AGOSEngine::vc37_pokePalette() { uint16 offs = vcReadNextWord(); uint16 color = vcReadNextWord(); + // PC version of Personal Nightmare uses standard EGA palette + if (getGameType() == GType_PN && getPlatform() == Common::kPlatformPC) + return; + byte *palptr = _displayPalette + offs * 4; palptr[0] = ((color & 0xf00) >> 8) * 32; palptr[1] = ((color & 0x0f0) >> 4) * 32; diff --git a/engines/agos/vga_e2.cpp b/engines/agos/vga_e2.cpp index 07c6799d0ec..50f9ad7deec 100644 --- a/engines/agos/vga_e2.cpp +++ b/engines/agos/vga_e2.cpp @@ -183,9 +183,9 @@ void AGOSEngine::vc52_playSound() { int16 vol = vcReadNextWord(); if (ambient) - loadSound(sound, pan, vol, 2); + loadSound(sound, pan, vol, Sound::TYPE_AMBIENT); else - loadSound(sound, pan, vol, 1); + loadSound(sound, pan, vol, Sound::TYPE_SFX); } else if (getGameType() == GType_SIMON2) { if (ambient) _sound->playAmbient(sound); @@ -196,7 +196,7 @@ void AGOSEngine::vc52_playSound() { } else if (getGameId() == GID_SIMON1DOS) { playSting(sound); } else { - loadSound(sound); + loadSound(sound, 0, 0); } } diff --git a/engines/agos/vga_ff.cpp b/engines/agos/vga_ff.cpp index 9a65c340d91..58eb2f4cc01 100644 --- a/engines/agos/vga_ff.cpp +++ b/engines/agos/vga_ff.cpp @@ -205,7 +205,7 @@ void AGOSEngine::vc83_playSoundLoop() { int16 vol = vcReadNextWord(); int16 pan = vcReadNextWord(); - loadSound(sound, pan, vol, 3); + loadSound(sound, pan, vol, Sound::TYPE_SFX5); } void AGOSEngine::vc84_stopSoundLoop() { diff --git a/engines/agos/vga_pn.cpp b/engines/agos/vga_pn.cpp new file mode 100644 index 00000000000..5c6eaf67787 --- /dev/null +++ b/engines/agos/vga_pn.cpp @@ -0,0 +1,188 @@ +/* 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$ + * + */ + +// Video script opcodes for Simon1/Simon2 + + +#include "agos/agos.h" +#include "agos/intern.h" +#include "agos/vga.h" + +#include "common/system.h" + +#include "graphics/surface.h" + +namespace AGOS { + +void AGOSEngine_PN::setupVideoOpcodes(VgaOpcodeProc *op) { + op[1] = &AGOSEngine::vc1_fadeOut; + op[2] = &AGOSEngine::vc2_call; + op[3] = &AGOSEngine::vc3_loadSprite; + op[4] = &AGOSEngine::vc4_fadeIn; + op[5] = &AGOSEngine::vc5_ifEqual; + op[6] = &AGOSEngine::vc6_ifObjectHere; + op[7] = &AGOSEngine::vc7_ifObjectNotHere; + op[8] = &AGOSEngine::vc8_ifObjectIsAt; + op[9] = &AGOSEngine::vc9_ifObjectStateIs; + op[10] = &AGOSEngine::vc10_draw; + op[11] = &AGOSEngine::vc11_onStop; + op[13] = &AGOSEngine::vc12_delay; + op[14] = &AGOSEngine::vc13_addToSpriteX; + op[15] = &AGOSEngine::vc14_addToSpriteY; + op[16] = &AGOSEngine::vc15_sync; + op[17] = &AGOSEngine::vc16_waitSync; + op[18] = &AGOSEngine::vc17_waitEnd; + op[19] = &AGOSEngine::vc18_jump; + op[20] = &AGOSEngine::vc19_loop; + op[21] = &AGOSEngine::vc20_setRepeat; + op[22] = &AGOSEngine::vc21_endRepeat; + op[23] = &AGOSEngine::vc22_setPaletteOld; + op[24] = &AGOSEngine::vc23_setPriority; + op[25] = &AGOSEngine::vc24_setSpriteXY; + op[26] = &AGOSEngine::vc25_halt_sprite; + op[27] = &AGOSEngine::vc26_setSubWindow; + op[28] = &AGOSEngine::vc27_resetSprite; + op[29] = &AGOSEngine::vc28_playSFX; + op[30] = &AGOSEngine::vc29_stopAllSounds; + op[31] = &AGOSEngine::vc30_setFrameRate; + op[32] = &AGOSEngine::vc31_setWindow; + op[33] = &AGOSEngine::vc32_saveScreen; + op[34] = &AGOSEngine::vc33_setMouseOn; + op[35] = &AGOSEngine::vc34_setMouseOff; + op[36] = &AGOSEngine::vc36_pause; + op[38] = &AGOSEngine::vc35_clearWindow; + op[39] = &AGOSEngine::vc39_volume; + op[40] = &AGOSEngine::vc36_setWindowImage; + op[41] = &AGOSEngine::vc37_pokePalette; + op[44] = &AGOSEngine::vc44_enableBox; + op[45] = &AGOSEngine::vc45_disableBox; + op[46] = &AGOSEngine::vc46_maxBox; + op[48] = &AGOSEngine::vc48_specialEffect; + op[50] = &AGOSEngine::vc50_setBox; + op[51] = &AGOSEngine::vc38_ifVarNotZero; + op[52] = &AGOSEngine::vc39_setVar; + op[53] = &AGOSEngine::vc40_scrollRight; + op[54] = &AGOSEngine::vc41_scrollLeft; + op[55] = &AGOSEngine::vc55_scanFlag; +} + +void AGOSEngine::vc36_pause() { + const char *message1 = "Press any key to continue"; + bool oldWiped = _wiped; + _wiped = 0; + + _lockWord |= 8; + + windowPutChar(_windowArray[2], 13); + + for (; *message1; message1++) + windowPutChar(_windowArray[2], *message1); + + while (!shouldQuit()) { + if (_keyPressed.ascii != 0) + break; + delay(1); + } + + _keyPressed.reset(); + + windowPutChar(_windowArray[2], 13); + windowPutChar(_windowArray[2], 128); + _wiped = oldWiped; + + _lockWord &= ~8; +} + +void AGOSEngine::vc39_volume() { + _vcPtr += 2; +} + +void AGOSEngine::vc44_enableBox() { + HitArea *ha = _hitAreas + vcReadNextWord(); + ha->flags &= ~kOBFBoxDisabled; +} + +void AGOSEngine::vc45_disableBox() { + HitArea *ha = _hitAreas + vcReadNextWord(); + ha->flags |= kOBFBoxDisabled; +} + +void AGOSEngine::vc46_maxBox() { + HitArea *ha = _hitAreas + vcReadNextWord(); + ha->id = 0xFFFF; +} + +void AGOSEngine::vc48_specialEffect() { + uint16 num = vcReadNextWord(); + vcReadNextWord(); + + if (getPlatform() == Common::kPlatformPC) { + if (num == 1) { + Graphics::Surface *screen = _system->lockScreen(); + byte *dst = (byte *)screen->pixels; + + for (uint h = 0; h < _screenHeight; h++) { + for (uint w = 0; w < _screenWidth; w++) { + if (dst[w] == 15) + dst[w] = 4; + } + dst += _screenWidth; + } + _system->unlockScreen(); + } else if (num == 2) { + const char *str = "There are gurgling noises from the sink."; + for (; *str; str++) + windowPutChar(_textWindow, *str); + } + } +} + +void AGOSEngine::vc50_setBox() { + uint16 id, x, y, w, h, msg1, msg2, flags; + const uint16 *vlut; + + id = vcReadNextWord(); + vlut = &_videoWindows[vcReadNextWord() * 4]; + x = vlut[0] * 16 + vcReadNextWord(); + y = vlut[1] + vcReadNextWord(); + h = vcReadNextWord(); + w = vcReadNextWord(); + msg1 = vcReadNextWord(); + msg2 = vcReadNextWord(); + flags = vcReadNextWord(); + + // Compressed string + if (!(flags & kOBFUseMessageList)) { + msg1 += 0x8000; + } + + defineBox(id, x, y, h, w, msg1, msg2, flags); +} + +void AGOSEngine::vc55_scanFlag() { + _scanFlag = 1; +} + +} // End of namespace AGOS diff --git a/engines/agos/window.cpp b/engines/agos/window.cpp index 9f1fc33bf07..31215f1f7c6 100644 --- a/engines/agos/window.cpp +++ b/engines/agos/window.cpp @@ -71,10 +71,12 @@ WindowBlock *AGOSEngine::openWindow(uint x, uint y, uint w, uint h, uint flags, // Characters are 6 pixels if (getGameType() == GType_ELVIRA2) window->textMaxLength = (window->width * 8 - 4) / 6; + else if (getGameType() == GType_PN) + window->textMaxLength = window->width * 8 / 6 + 1; else window->textMaxLength = window->width * 8 / 6; - if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) + if (getGameType() == GType_PN || getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) clearWindow(window); if (getGameType() == GType_SIMON1 && getPlatform() == Common::kPlatformAmiga && window->fillColor == 225) @@ -202,9 +204,9 @@ void AGOSEngine::restoreWindow(WindowBlock *window) { _restoreWindow6 = 0; } - restoreBlock(window->y + window->height * 8, (window->x + window->width) * 8, window->y, window->x * 8); + restoreBlock(window->x * 8, window->y, (window->x + window->width) * 8, window->y + window->height * 8); } else if (getGameType() == GType_SIMON1) { - restoreBlock(window->y + window->height * 8 + ((window == _windowArray[2]) ? 1 : 0), (window->x + window->width) * 8, window->y, window->x * 8); + restoreBlock(window->x * 8, window->y, (window->x + window->width) * 8, window->y + window->height * 8 + ((window == _windowArray[2]) ? 1 : 0)); } else { uint16 x = window->x; uint16 w = window->width; @@ -220,13 +222,13 @@ void AGOSEngine::restoreWindow(WindowBlock *window) { } } - restoreBlock(window->y + window->height * 8, (x + w) * 8, window->y, x * 8); + restoreBlock(x * 8, window->y, (x + w) * 8, window->y + window->height * 8); } _lockWord &= ~0x8000; } -void AGOSEngine::restoreBlock(uint16 h, uint16 w, uint16 y, uint16 x) { +void AGOSEngine::restoreBlock(uint16 x, uint16 y, uint16 w, uint16 h) { byte *dst, *src; uint i; @@ -266,7 +268,7 @@ void AGOSEngine::setTextColor(uint color) { } void AGOSEngine::sendWindow(uint a) { - if (_textWindow != _windowArray[0]) { + if (getGameType() == GType_PN || _textWindow != _windowArray[0]) { if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) { if (!(_textWindow->flags & 1)) { haltAnimation(); diff --git a/engines/agos/zones.cpp b/engines/agos/zones.cpp index 79199c90f16..4e3d5b00074 100644 --- a/engines/agos/zones.cpp +++ b/engines/agos/zones.cpp @@ -63,29 +63,34 @@ static const uint8 zoneTable[160] = { 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, }; -void AGOSEngine::loadZone(uint16 zoneNum) { +void AGOSEngine::loadZone(uint16 zoneNum, bool useError) { VgaPointersEntry *vpe; CHECK_BOUNDS(zoneNum, _vgaBufferPointers); - vpe = _vgaBufferPointers + zoneNum; - if (vpe->vgaFile1 != NULL) - return; + if (getGameType() == GType_PN) { + // Only a single zone is used in Personal Nightmare + vpe = _vgaBufferPointers; + vc27_resetSprite(); + } else { + vpe = _vgaBufferPointers + zoneNum; + if (vpe->vgaFile1 != NULL) + return; + } - // Loading order is important - // due to resource managment + // Loading order is important due to resource managment if (getPlatform() == Common::kPlatformAmiga && getGameType() == GType_WW && zoneTable[zoneNum] == 3) { uint8 num = (zoneNum >= 85) ? 94 : 18; - loadVGAVideoFile(num, 2); + loadVGAVideoFile(num, 2, useError); } else { - loadVGAVideoFile(zoneNum, 2); + loadVGAVideoFile(zoneNum, 2, useError); } vpe->vgaFile2 = _block; vpe->vgaFile2End = _blockEnd; - loadVGAVideoFile(zoneNum, 1); + loadVGAVideoFile(zoneNum, 1, useError); vpe->vgaFile1 = _block; vpe->vgaFile1End = _blockEnd; diff --git a/tools/credits.pl b/tools/credits.pl index d3c5a526e0e..abde7c474bd 100755 --- a/tools/credits.pl +++ b/tools/credits.pl @@ -498,6 +498,7 @@ begin_credits("Credits"); begin_section("AGOS"); add_person("Torbjörn Andersson", "eriktorbjorn", ""); + add_person("Paul Gilbert", "dreammaster", ""); add_person("Travis Howell", "Kirben", ""); end_section();