Add initial support for Personal Nightmare.

Thanks to dreammaster for file decompression and icon decoding code.

NOTE: setjmp/longjmp code will require conversion for portability.

svn-id: r39216
This commit is contained in:
Travis Howell 2009-03-08 08:45:21 +00:00
parent 7bfab75a08
commit 2620d6836c
34 changed files with 4245 additions and 143 deletions

View File

@ -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"));

View File

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

View File

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

View File

@ -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++;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -606,6 +606,129 @@ bool AGOSEngine::processSpecialKeys() {
return verbCode;
}
// Personal Nightmare specific
void AGOSEngine_PN::clearInputLine() {
_inputting = 0;
_inputReady = 0;
clearWindow(_windowArray[2]);
}
void AGOSEngine_PN::handleKeyboard() {
if (!_inputReady)
return;
if (_hitCalled != 0) {
mouseHit();
}
int16 chr = -1;
if (_mouseString) {
const char *strPtr = _mouseString;
while (*strPtr != 0 && *strPtr != 13)
addChar(*strPtr++);
_mouseString = 0;
chr = *strPtr;
if (chr == 13) {
addChar(13);
}
}
if (_mouseString1 && chr != 13) {
const char *strPtr = _mouseString1;
while (*strPtr != 13)
addChar(*strPtr++);
_mouseString1 = 0;
chr = *strPtr;
if (chr == 13) {
addChar(13);
}
}
if (chr == -1) {
chr = _keyPressed.ascii;
if (chr == 8 || chr == 13) {
addChar(chr);
} else if (!(_lockWord & 0x10)) {
if (chr >= 32)
addChar(chr);
}
}
if (chr == 13) {
_mouseString = 0;
_mouseString1 = 0;
_mousePrintFG = 0;
_inputReady = 0;
}
_keyPressed.reset();
}
void AGOSEngine_PN::interact(char *buffer, uint8 size) {
if (!_inputting) {
memset(_keyboardBuffer, 0, sizeof(_keyboardBuffer));
_intputCounter = 0;
_inputMax = size;
_inputWindow = _windowArray[_curWindow];
windowPutChar(_inputWindow, 128);
_inputting = 1;
_inputReady = 1;
}
while (!shouldQuit() && _inputReady) {
if (!_noScanFlag && _scanFlag) {
buffer[0] = 1;
buffer[1] = 0;
_scanFlag = 0;
break;
}
delay(1);
}
if (!_inputReady) {
memcpy(buffer, _keyboardBuffer, size);
_inputting = 0;
}
}
void AGOSEngine_PN::addChar(uint8 chr) {
if (chr == 13) {
_keyboardBuffer[_intputCounter++] = chr;
userGameBackSpace(_inputWindow, 8);
windowPutChar(_inputWindow, 13);
} else if (chr == 8 && _intputCounter) {
userGameBackSpace(_inputWindow, 8);
userGameBackSpace(_inputWindow, 8);
windowPutChar(_inputWindow, 128);
_keyboardBuffer[--_intputCounter] = 0;
} else if (chr >= 32 && _intputCounter < _inputMax) {
_keyboardBuffer[_intputCounter++] = chr;
userGameBackSpace(_inputWindow, 8);
windowPutChar(_inputWindow, chr);
windowPutChar(_inputWindow, 128);
}
}
bool AGOSEngine_PN::processSpecialKeys() {
if (shouldQuit())
_exitCutscene = true;
switch (_keyPressed.keycode) {
case Common::KEYCODE_ESCAPE:
_exitCutscene = true;
break;
case Common::KEYCODE_PAUSE:
pause();
break;
default:
break;
}
_keyPressed.reset();
return false;
}
} // End of namespace AGOS

View File

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

View File

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

View File

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

293
engines/agos/pn.cpp Normal file
View File

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

View File

@ -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");
} else if (getFeatures() & GF_CRUNCHED) {
if (getGameType() == GType_PN) {
Common::Stack<uint32> data;
byte *dataOut = 0;
int dataOutSize = 0;
dstSize = READ_BE_UINT32(srcBuffer + srcSize - 4);
dst = allocBlock (dstSize + extraBuffer);
decrunchFile(srcBuffer, dst, srcSize);
free(srcBuffer);
for (uint i = 0; i < srcSize / 4; ++i)
data.push(in.readUint32BE());
decompressPN(data, dataOut, dataOutSize);
dst = allocBlock (dataOutSize + extraBuffer);
memcpy(dst, dataOut, dataOutSize);
delete[] dataOut;
} else {
byte *srcBuffer = (byte *)malloc(srcSize);
if (in.read(srcBuffer, srcSize) != srcSize)
error("loadVGAVideoFile: Read failed");
dstSize = READ_BE_UINT32(srcBuffer + srcSize - 4);
dst = allocBlock (dstSize + extraBuffer);
decrunchFile(srcBuffer, dst, srcSize);
free(srcBuffer);
}
} else {
dst = allocBlock(dstSize + extraBuffer);
if (in.read(dst, dstSize) != dstSize)

View File

@ -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++) {

View File

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

View File

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

View File

@ -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() {

1109
engines/agos/script_pn.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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();

View File

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

View File

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

View File

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

View File

@ -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);
}
}

View File

@ -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() {

188
engines/agos/vga_pn.cpp Normal file
View File

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

View File

@ -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();

View File

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

View File

@ -498,6 +498,7 @@ begin_credits("Credits");
begin_section("AGOS");
add_person("Torbj&ouml;rn Andersson", "eriktorbjorn", "");
add_person("Paul Gilbert", "dreammaster", "");
add_person("Travis Howell", "Kirben", "");
end_section();