aboutsummaryrefslogtreecommitdiff
path: root/engines/agos
diff options
context:
space:
mode:
Diffstat (limited to 'engines/agos')
-rw-r--r--engines/agos/agos.cpp35
-rw-r--r--engines/agos/agos.h321
-rw-r--r--engines/agos/charset-fontdata.cpp107
-rw-r--r--engines/agos/charset.cpp16
-rw-r--r--engines/agos/cursor.cpp74
-rw-r--r--engines/agos/debug.cpp22
-rw-r--r--engines/agos/debug.h73
-rw-r--r--engines/agos/detection.cpp4
-rw-r--r--engines/agos/detection_tables.h87
-rw-r--r--engines/agos/event.cpp53
-rw-r--r--engines/agos/gfx.cpp72
-rw-r--r--engines/agos/icons.cpp133
-rw-r--r--engines/agos/input.cpp125
-rw-r--r--engines/agos/intern.h20
-rw-r--r--engines/agos/menus.cpp21
-rw-r--r--engines/agos/module.mk3
-rw-r--r--engines/agos/pn.cpp293
-rw-r--r--engines/agos/res.cpp178
-rw-r--r--engines/agos/res_ami.cpp19
-rw-r--r--engines/agos/res_snd.cpp115
-rw-r--r--engines/agos/saveload.cpp132
-rw-r--r--engines/agos/script_e2.cpp8
-rw-r--r--engines/agos/script_pn.cpp1109
-rw-r--r--engines/agos/sound.cpp44
-rw-r--r--engines/agos/sound.h22
-rw-r--r--engines/agos/string.cpp645
-rw-r--r--engines/agos/verb.cpp283
-rw-r--r--engines/agos/vga.cpp140
-rw-r--r--engines/agos/vga_e2.cpp6
-rw-r--r--engines/agos/vga_ff.cpp2
-rw-r--r--engines/agos/vga_pn.cpp188
-rw-r--r--engines/agos/window.cpp14
-rw-r--r--engines/agos/zones.cpp23
33 files changed, 4244 insertions, 143 deletions
diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp
index 488e9866e1..35c8054b02 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 ebb4a66ae6..7ac6523671 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 <setjmp.h>
+
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<uint32> &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 8d058c3bfc..6f26c566a4 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 8acfecb1ce..3f38e17ea1 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 d8c5793ba2..d0481e8f5a 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 069af828c3..0959d48e07 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 e2f0559057..375878a4bc 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 07365c1b03..59f603a905 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 df8175ac8b..43c225697d 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 1ff2f9245c..248e9ee6c7 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 95abbc0425..98df66b021 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 f668b6e0a5..908b712ade 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 e7e4e61c3c..991d355979 100644
--- a/engines/agos/input.cpp
+++ b/engines/agos/input.cpp
@@ -606,6 +606,129 @@ bool AGOSEngine::processSpecialKeys() {
return verbCode;
}
-} // End of namespace AGOS
+// 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 823a9ed345..8d33634493 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 e6d926b06d..7f61925528 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 fe2dcba2f1..0da19a4f88 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 0000000000..66ff6c9c18
--- /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 a33a16bbc9..1540e5a018 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<uint32> &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<uint32> &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<uint32> &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");
-
- dstSize = READ_BE_UINT32(srcBuffer + srcSize - 4);
- dst = allocBlock (dstSize + extraBuffer);
- decrunchFile(srcBuffer, dst, srcSize);
- free(srcBuffer);
+ } else if (getFeatures() & GF_CRUNCHED) {
+ if (getGameType() == GType_PN) {
+ Common::Stack<uint32> 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 + 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 3bd951365f..f86d0344f5 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 5ac2f06a28..891e183d4f 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<uint32> 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 83165312df..700d451b83 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 655fc16ff1..8802ed921d 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 0000000000..f46f6db7df
--- /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 4f56daf1cf..77324a072d 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 1e5a8a47d2..be7aa6c076 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 3c651cce0c..8bfaa1e4eb 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 3c71e539ef..6f91562a87 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 2ad09fb430..323e8de96c 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 07c6799d0e..50f9ad7dee 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 9a65c340d9..58eb2f4cc0 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 0000000000..5c6eaf6778
--- /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 9f1fc33bf0..31215f1f7c 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 79199c90f1..4e3d5b0007 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;