[Scummvm-git-logs] scummvm master -> 9dd5469873b21ed4dcd931c011d59bd66af5f85b
bluegr
noreply at scummvm.org
Sat May 28 14:34:59 UTC 2022
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
9dd5469873 AGI: Add files missing from commit 3be24c759b504ad026073c0106e9b337ebad821f
Commit: 9dd5469873b21ed4dcd931c011d59bd66af5f85b
https://github.com/scummvm/scummvm/commit/9dd5469873b21ed4dcd931c011d59bd66af5f85b
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2022-05-28T17:34:40+03:00
Commit Message:
AGI: Add files missing from commit 3be24c759b504ad026073c0106e9b337ebad821f
Changed paths:
A engines/agi/preagi/mickey.cpp
A engines/agi/preagi/mickey.h
A engines/agi/preagi/preagi.cpp
A engines/agi/preagi/preagi.h
A engines/agi/preagi/troll.cpp
A engines/agi/preagi/troll.h
A engines/agi/preagi/winnie.cpp
A engines/agi/preagi/winnie.h
diff --git a/engines/agi/preagi/mickey.cpp b/engines/agi/preagi/mickey.cpp
new file mode 100644
index 00000000000..3126be1ab5b
--- /dev/null
+++ b/engines/agi/preagi/mickey.cpp
@@ -0,0 +1,2358 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/events.h"
+#include "common/savefile.h"
+#include "common/textconsole.h"
+
+#include "graphics/cursorman.h"
+
+#include "agi/preagi/preagi.h"
+#include "agi/preagi/mickey.h"
+#include "agi/graphics.h"
+
+namespace Agi {
+
+int MickeyEngine::getDat(int iRoom) {
+ if (((iRoom > 0) && (iRoom < 24)) || iRoom == 154 || iRoom == 155) return IDI_MSA_PLANET_EARTH;
+ if ((iRoom >= 30) && (iRoom <= 39)) return IDI_MSA_PLANET_VENUS;
+ if ((iRoom >= 40) && (iRoom <= 69)) return IDI_MSA_PLANET_NEPTUNE;
+ if ((iRoom >= 70) && (iRoom <= 82)) return IDI_MSA_PLANET_MERCURY;
+ if ((iRoom >= 83) && (iRoom <= 92)) return IDI_MSA_PLANET_SATURN;
+ if ((iRoom >= 93) && (iRoom <= 103)) return IDI_MSA_PLANET_PLUTO;
+ if ((iRoom >= 106) && (iRoom <= 120)) return IDI_MSA_PLANET_JUPITER;
+ if ((iRoom >= 121) && (iRoom <= 132)) return IDI_MSA_PLANET_MARS;
+ if ((iRoom >= 133) && (iRoom <= 145)) return IDI_MSA_PLANET_URANUS;
+ return IDI_MSA_PLANET_SPACESHIP;
+}
+
+void MickeyEngine::readExe(int ofs, uint8 *buffer, long buflen) {
+ Common::File infile;
+ if (!infile.open("mickey.exe"))
+ return;
+ infile.seek(ofs, SEEK_SET);
+ infile.read(buffer, buflen);
+ infile.close();
+}
+
+void MickeyEngine::getDatFileName(int iRoom, char *szFile) {
+ sprintf(szFile, IDS_MSA_PATH_DAT, IDS_MSA_NAME_DAT[getDat(iRoom)]);
+}
+
+void MickeyEngine::readDatHdr(char *szFile, MSA_DAT_HEADER *hdr) {
+ Common::File infile;
+
+ if (!infile.open(szFile))
+ return;
+
+ hdr->filelen = infile.readByte();
+ hdr->filelen += infile.readByte() * 0x100;
+
+ for (int i = 0; i < IDI_MSA_MAX_ROOM; i++) {
+ hdr->ofsRoom[i] = infile.readByte();
+ hdr->ofsRoom[i] += infile.readByte() * 0x100;
+ }
+ for (int i = 0; i < IDI_MSA_MAX_ROOM; i++) {
+ hdr->ofsDesc[i] = infile.readByte();
+ hdr->ofsDesc[i] += infile.readByte() * 0x100;
+ }
+ for (int i = 0; i < IDI_MSA_MAX_ROOM; i++) {
+ hdr->ofsStr[i] = infile.readByte();
+ hdr->ofsStr[i] += infile.readByte() * 0x100;
+ }
+
+ infile.close();
+}
+
+void MickeyEngine::readOfsData(int offset, int iItem, uint8 *buffer, long buflen) {
+ uint16 ofs[256];
+
+ readExe(offset, buffer, buflen);
+ memcpy(ofs, buffer, sizeof(ofs));
+
+ for (int i = 0; i < 256; i++)
+ ofs[i] = buffer[i * 2] + 256 * buffer[i * 2 + 1];
+
+ readExe(ofs[iItem] + IDI_MSA_OFS_EXE, buffer, buflen);
+}
+
+// User Interface
+
+bool MickeyEngine::chooseY_N(int ofsPrompt, bool fErrorMsg) {
+ printExeStr(ofsPrompt);
+
+ while (!shouldQuit()) {
+ switch (getSelection(kSelYesNo)) {
+ case 0:
+ return false;
+ case 1:
+ return true;
+ default:
+ if (fErrorMsg) {
+ printExeStr(IDO_MSA_PRESS_YES_OR_NO);
+ waitAnyKey();
+ printExeStr(ofsPrompt);
+ }
+ break;
+ }
+ }
+
+ return false;
+}
+
+int MickeyEngine::choose1to9(int ofsPrompt) {
+ int answer = 0;
+ printExeStr(ofsPrompt);
+
+ while (!shouldQuit()) {
+ answer = getSelection(kSelNumber);
+ if (answer == 10) {
+ printExeStr(IDO_MSA_PRESS_1_TO_9);
+ if (getSelection(kSelAnyKey) == 0)
+ return 0;
+ printExeStr(ofsPrompt);
+ } else return answer;
+ }
+
+ return 0;
+}
+
+void MickeyEngine::printStr(char *buffer) {
+ int pc = 1;
+ int nRows, iCol, iRow;
+
+ nRows = *buffer + IDI_MSA_ROW_MENU_0;
+
+ clearTextArea();
+
+ for (iRow = IDI_MSA_ROW_MENU_0; iRow < nRows; iRow++) {
+ iCol = *(buffer + pc++);
+ drawStr(iRow, iCol, IDA_DEFAULT, buffer + pc);
+ pc += strlen(buffer + pc) + 1;
+ }
+
+ // Show the string on screen
+ _gfx->updateScreen();
+}
+
+void MickeyEngine::printLine(const char *buffer) {
+ clearTextArea();
+
+ drawStr(22, 18 - strlen(buffer) / 2, IDA_DEFAULT, buffer);
+
+ // Show the string on screen
+ _gfx->updateScreen();
+
+ waitAnyKey(true);
+}
+
+void MickeyEngine::printExeStr(int ofs) {
+ uint8 buffer[256] = {0};
+
+ if (!ofs)
+ return;
+
+ readExe(ofs, buffer, sizeof(buffer));
+ printStr((char *)buffer);
+}
+
+void MickeyEngine::printExeMsg(int ofs) {
+ if (!ofs)
+ return;
+
+ printExeStr(ofs);
+ waitAnyKey(true);
+}
+
+void MickeyEngine::printDatString(int iStr) {
+ char buffer[256];
+ int iDat = getDat(_gameStateMickey.iRoom);
+
+ MSA_DAT_HEADER hdr;
+ char szFile[256] = {0};
+
+ sprintf(szFile, IDS_MSA_PATH_DAT, IDS_MSA_NAME_DAT[iDat]);
+ readDatHdr(szFile, &hdr);
+
+ Common::File infile;
+
+ if (!infile.open(szFile))
+ return;
+
+ infile.seek(hdr.ofsStr[iStr] + IDI_MSA_OFS_DAT, SEEK_SET);
+ infile.read((uint8 *)buffer, 256);
+ infile.close();
+
+ printStr(buffer);
+}
+
+void MickeyEngine::printDesc(int iRoom) {
+ MSA_DAT_HEADER hdr;
+ char szFile[256] = {0};
+
+ getDatFileName(iRoom, szFile);
+ readDatHdr(szFile, &hdr);
+
+ Common::File infile;
+
+ if (!infile.open(szFile))
+ return;
+
+ char *buffer = (char *)malloc(256);
+ memset(buffer, 0, 256);
+
+ infile.seek(hdr.ofsDesc[iRoom - 1] + IDI_MSA_OFS_DAT, SEEK_SET);
+ infile.read(buffer, 256);
+ infile.close();
+
+ printStr(buffer);
+ free(buffer);
+}
+
+bool MickeyEngine::checkMenu() {
+ MSA_MENU menu;
+ int iSel0, iSel1;
+ MSA_DAT_HEADER hdr;
+ char szFile[256] = {0};
+ Common::File infile;
+
+ getDatFileName(_gameStateMickey.iRoom, szFile);
+ readDatHdr(szFile, &hdr);
+ if (!infile.open(szFile))
+ return false;
+
+ char *buffer = new char[sizeof(MSA_MENU)];
+ infile.seek(hdr.ofsRoom[_gameStateMickey.iRoom - 1] + IDI_MSA_OFS_DAT, SEEK_SET);
+ infile.read((uint8 *)buffer, sizeof(MSA_MENU));
+ infile.close();
+
+ memcpy(&menu, buffer, sizeof(MSA_MENU));
+ patchMenu(&menu);
+ memcpy(buffer, &menu, sizeof(MSA_MENU));
+
+ getMenuSel(buffer, &iSel0, &iSel1);
+ delete[] buffer;
+
+ return parse(menu.cmd[iSel0].data[iSel1], menu.arg[iSel0].data[iSel1]);
+}
+
+void MickeyEngine::drawMenu(MSA_MENU &menu, int sel0, int sel1) {
+ int iWord;
+ int iRow;
+ int sel;
+ uint8 attr;
+
+ // draw menu
+
+ clearTextArea();
+
+ for (iRow = 0; iRow < 2; iRow++) {
+ for (iWord = 0; iWord < menu.row[iRow].count; iWord++) {
+ if (iRow)
+ sel = sel1;
+ else
+ sel = sel0;
+
+ if (iWord == sel)
+ attr = IDA_DEFAULT_REV;
+ else
+ attr = IDA_DEFAULT;
+
+ drawStr(IDI_MSA_ROW_MENU_0 + iRow, menu.row[iRow].entry[iWord].x0,
+ attr, (char *)menu.row[iRow].entry[iWord].szText);
+ }
+ }
+
+ // Menu created, show it on screen
+ _gfx->updateScreen();
+}
+
+void MickeyEngine::getMouseMenuSelRow(MSA_MENU &menu, int *sel0, int *sel1, int iRow, int x, int y) {
+ int iWord;
+ int *sel = nullptr;
+
+ switch (iRow) {
+ case 0:
+ if (y != IDI_MSA_ROW_MENU_0) return;
+ sel = sel0;
+ break;
+ case 1:
+ if (y != IDI_MSA_ROW_MENU_1) return;
+ sel = sel1;
+ break;
+ default:
+ return;
+ }
+
+ for (iWord = 0; iWord < menu.row[iRow].count; iWord++) {
+ if ((x >= menu.row[iRow].entry[iWord].x0) &&
+ (x < (int)(menu.row[iRow].entry[iWord].x0 +
+ strlen((char *)menu.row[iRow].entry[iWord].szText)))) {
+ *sel = iWord;
+ break;
+ }
+ }
+}
+
+bool MickeyEngine::getMenuSelRow(MSA_MENU &menu, int *sel0, int *sel1, int iRow) {
+ Common::Event event;
+ int *sel = nullptr;
+ int nWords;
+ int x, y;
+ int goIndex = -1, northIndex = -1, southIndex = -1, eastIndex = -1, westIndex = -1;
+
+ switch (iRow) {
+ case 0:
+ sel = sel0;
+ break;
+ case 1:
+ sel = sel1;
+ break;
+ default:
+ break;
+ }
+ nWords = menu.row[iRow].count;
+ _clickToMove = false;
+
+ for (int i = 0; i <= menu.row[0].count; i++)
+ if (menu.row[0].entry[i].szText[0] == 71 && menu.row[0].entry[i].szText[1] == 79) // GO
+ goIndex = i;
+
+ if (goIndex >= 0) {
+ for (int j = 0; j <= menu.row[1].count; j++) {
+ if (menu.row[1].entry[j].szText[0] == 78 && menu.row[1].entry[j].szText[1] == 79 &&
+ menu.row[1].entry[j].szText[2] == 82 && menu.row[1].entry[j].szText[3] == 84 &&
+ menu.row[1].entry[j].szText[4] == 72)
+ northIndex = j;
+ if (menu.row[1].entry[j].szText[0] == 83 && menu.row[1].entry[j].szText[1] == 79 &&
+ menu.row[1].entry[j].szText[2] == 85 && menu.row[1].entry[j].szText[3] == 84 &&
+ menu.row[1].entry[j].szText[4] == 72)
+ southIndex = j;
+ if (menu.row[1].entry[j].szText[0] == 69 && menu.row[1].entry[j].szText[1] == 65 &&
+ menu.row[1].entry[j].szText[2] == 83 && menu.row[1].entry[j].szText[3] == 84)
+ eastIndex = j;
+ if (menu.row[1].entry[j].szText[0] == 87 && menu.row[1].entry[j].szText[1] == 69 &&
+ menu.row[1].entry[j].szText[2] == 83 && menu.row[1].entry[j].szText[3] == 84)
+ westIndex = j;
+ }
+ }
+
+ drawMenu(menu, *sel0, *sel1);
+
+ while (!shouldQuit()) {
+ while (_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_RETURN_TO_LAUNCHER:
+ case Common::EVENT_QUIT:
+ return 0;
+ case Common::EVENT_MOUSEMOVE:
+ if (iRow < 2) {
+ x = event.mouse.x / 8;
+ y = event.mouse.y / 8;
+ // If the mouse hovers over the menu, refresh the menu
+ if ((iRow == 0 && y == IDI_MSA_ROW_MENU_0) || (iRow == 1 && y == IDI_MSA_ROW_MENU_1)) {
+ getMouseMenuSelRow(menu, sel0, sel1, iRow, x, y);
+ drawMenu(menu, *sel0, *sel1);
+ }
+
+ // Change cursor
+ if (northIndex >= 0 && (event.mouse.x >= 20 && event.mouse.x <= (IDI_MSA_PIC_WIDTH + 10) * 2) &&
+ (event.mouse.y >= 0 && event.mouse.y <= 10)) {
+ //_gfx->setCursorPalette(true);
+ // TODO:?????
+ } else if (southIndex >= 0 && (event.mouse.x >= 20 && event.mouse.x <= (IDI_MSA_PIC_WIDTH + 10) * 2) &&
+ (event.mouse.y >= IDI_MSA_PIC_HEIGHT - 10 && event.mouse.y <= IDI_MSA_PIC_HEIGHT)) {
+ //_gfx->setCursorPalette(true);
+ } else if (westIndex >= 0 && (event.mouse.y >= 0 && event.mouse.y <= IDI_MSA_PIC_HEIGHT) &&
+ (event.mouse.x >= 20 && event.mouse.x <= 30)) {
+ //_gfx->setCursorPalette(true);
+ } else if (eastIndex >= 0 && (event.mouse.y >= 0 && event.mouse.y <= IDI_MSA_PIC_HEIGHT) &&
+ (event.mouse.x >= IDI_MSA_PIC_WIDTH * 2 && event.mouse.x <= (IDI_MSA_PIC_WIDTH + 10) * 2)) {
+ //_gfx->setCursorPalette(true);
+ } else {
+ //_gfx->setCursorPalette(false);
+ }
+ }
+ break;
+ case Common::EVENT_LBUTTONUP:
+ // Click to move
+ if (northIndex >= 0 && (event.mouse.x >= 20 && event.mouse.x <= (IDI_MSA_PIC_WIDTH + 10) * 2) &&
+ (event.mouse.y >= 0 && event.mouse.y <= 10)) {
+ *sel0 = goIndex;
+ *sel1 = northIndex;
+
+ drawMenu(menu, *sel0, *sel1);
+
+ //_gfx->setCursorPalette(false);
+ // TODO???
+ _clickToMove = true;
+ } else if (southIndex >= 0 && (event.mouse.x >= 20 && event.mouse.x <= (IDI_MSA_PIC_WIDTH + 10) * 2) &&
+ (event.mouse.y >= IDI_MSA_PIC_HEIGHT - 10 && event.mouse.y <= IDI_MSA_PIC_HEIGHT)) {
+ *sel0 = goIndex;
+ *sel1 = southIndex;
+
+ drawMenu(menu, *sel0, *sel1);
+
+ //_gfx->setCursorPalette(false);
+ // TODO???
+ _clickToMove = true;
+ } else if (westIndex >= 0 && (event.mouse.y >= 0 && event.mouse.y <= IDI_MSA_PIC_HEIGHT) &&
+ (event.mouse.x >= 20 && event.mouse.x <= 30)) {
+ *sel0 = goIndex;
+ *sel1 = westIndex;
+
+ drawMenu(menu, *sel0, *sel1);
+
+ //_gfx->setCursorPalette(false);
+ // TODO???
+ _clickToMove = true;
+ } else if (eastIndex >= 0 && (event.mouse.y >= 0 && event.mouse.y <= IDI_MSA_PIC_HEIGHT) &&
+ (event.mouse.x >= IDI_MSA_PIC_WIDTH * 2 && event.mouse.x <= (IDI_MSA_PIC_WIDTH + 10) * 2)) {
+ *sel0 = goIndex;
+ *sel1 = eastIndex;
+
+ drawMenu(menu, *sel0, *sel1);
+
+ //_gfx->setCursorPalette(false);
+ // TODO???
+ _clickToMove = true;
+ } else {
+ //_gfx->setCursorPalette(false);
+ // TODO???
+ }
+ return true;
+ case Common::EVENT_RBUTTONUP:
+ *sel0 = 0;
+ *sel1 = -1;
+ return false;
+ case Common::EVENT_WHEELUP:
+ if (iRow < 2) {
+ *sel -= 1;
+
+ if (*sel < 0)
+ *sel = nWords - 1;
+
+ drawMenu(menu, *sel0, *sel1);
+ }
+ break;
+ case Common::EVENT_WHEELDOWN:
+ if (iRow < 2) {
+ *sel += 1;
+
+ if (*sel == nWords)
+ *sel = 0;
+
+ drawMenu(menu, *sel0, *sel1);
+ }
+ break;
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_2:
+ // Hidden message
+ if (_gameStateMickey.iRoom == IDI_MSA_PIC_MERCURY_CAVE_0) {
+ for (int i = 0; i < 5; i++) {
+ printExeMsg(IDO_MSA_HIDDEN_MSG[i]);
+ }
+ clearTextArea();
+ waitAnyKey();
+ }
+ break;
+ case Common::KEYCODE_8:
+ if (event.kbd.flags & Common::KBD_CTRL) {
+ *sel0 = 0;
+ *sel1 = -1;
+
+ return false;
+ }
+ break;
+ case Common::KEYCODE_ESCAPE:
+ *sel0 = 0;
+ *sel1 = -1;
+
+ return false;
+ case Common::KEYCODE_s:
+ flipFlag(VM_FLAG_SOUND_ON);
+ break;
+ case Common::KEYCODE_c:
+ inventory();
+ drawRoom();
+
+ *sel0 = 0;
+ *sel1 = -1;
+
+ return false;
+ case Common::KEYCODE_b:
+ printRoomDesc();
+ drawMenu(menu, *sel0, *sel1);
+
+ *sel0 = 0;
+ *sel1 = -1;
+
+ return false;
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_KP4:
+ case Common::KEYCODE_4:
+ if (iRow < 2) {
+ *sel -= 1;
+
+ if (*sel < 0)
+ *sel = nWords - 1;
+
+ drawMenu(menu, *sel0, *sel1);
+ }
+ break;
+ case Common::KEYCODE_RIGHT:
+ case Common::KEYCODE_SPACE:
+ case Common::KEYCODE_KP6:
+ case Common::KEYCODE_6:
+ if (iRow < 2) {
+ *sel += 1;
+
+ if (*sel == nWords)
+ *sel = 0;
+
+ drawMenu(menu, *sel0, *sel1);
+ }
+ break;
+ case Common::KEYCODE_RETURN:
+ case Common::KEYCODE_KP_ENTER:
+ return true;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ animate();
+ drawMenu(menu, *sel0, *sel1);
+ }
+ animate();
+ drawMenu(menu, *sel0, *sel1);
+ }
+
+ return false;
+}
+
+void MickeyEngine::getMenuSel(char *buffer, int *sel0, int *sel1) {
+ MSA_MENU menu;
+
+ memcpy(&menu, buffer, sizeof(MSA_MENU));
+
+ *sel0 = 0;
+ *sel1 = -1;
+
+ // Show the mouse cursor for the menu
+ CursorMan.showMouse(true);
+
+ while (!shouldQuit()) {
+ while (!shouldQuit()) {
+ if (getMenuSelRow(menu, sel0, sel1, 0)) {
+ if (_clickToMove)
+ break;
+
+ *sel1 = 0;
+
+ if (getMenuSelRow(menu, sel0, sel1, 1)) {
+ break;
+ }
+ }
+ }
+
+ if (_clickToMove || getMenuSelRow(menu, sel0, sel1, 2)) {
+ break;
+ }
+ }
+
+ // Menu selection made, hide the mouse cursor
+ CursorMan.showMouse(false);
+}
+
+void MickeyEngine::centerMenu(MSA_MENU *menu) {
+ int iWord;
+ int iRow;
+ int w, x;
+
+ for (iRow = 0; iRow < 2; iRow++) {
+ w = 0;
+ for (iWord = 0; iWord < menu->row[iRow].count; iWord++) {
+ w += strlen((char *)menu->row[iRow].entry[iWord].szText);
+ }
+ w += menu->row[iRow].count - 1;
+ x = (40 - w) / 2; // FIX
+
+ for (iWord = 0; iWord < menu->row[iRow].count; iWord++) {
+ menu->row[iRow].entry[iWord].x0 = x;
+ x += strlen((char *)menu->row[iRow].entry[iWord].szText) + 1;
+ }
+ }
+}
+
+void MickeyEngine::patchMenu(MSA_MENU *menu) {
+ uint8 buffer[512];
+ uint8 menubuf[sizeof(MSA_MENU)];
+ int nPatches;
+ int pBuf = 0;
+
+ // change planet name in ship airlock menu
+ if (_gameStateMickey.iRoom == IDI_MSA_PIC_SHIP_AIRLOCK) {
+ strcpy((char *)menu->row[1].entry[2].szText, IDS_MSA_NAME_PLANET[_gameStateMickey.iPlanet]);
+ }
+
+ // exit if fix unnecessary
+ if (!_gameStateMickey.iRmMenu[_gameStateMickey.iRoom]) {
+ centerMenu(menu);
+ return;
+ }
+
+ // copy menu to menubuf
+ memcpy(menubuf, menu, sizeof(menubuf));
+
+ // read patches
+ readOfsData(
+ IDOFS_MSA_MENU_PATCHES,
+ _gameStateMickey.nRmMenu[_gameStateMickey.iRoom] + _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] - 1,
+ buffer, sizeof(buffer)
+ );
+
+ // get number of patches
+ nPatches = buffer[pBuf++];
+
+ // patch menubuf
+ for (int iPatch = 0; iPatch < nPatches; iPatch++) {
+ if (buffer[pBuf] > sizeof(menubuf)) {
+ // patch address out of bounds
+ }
+ menubuf[buffer[pBuf]] = buffer[pBuf + 1];
+ pBuf += 2;
+ }
+
+ // copy menubuf back to menu
+ memcpy(menu, menubuf, sizeof(MSA_MENU));
+
+ // center menu
+ centerMenu(menu);
+}
+
+void MickeyEngine::printDatMessage(int iStr) {
+ printDatString(iStr);
+ waitAnyKey(true);
+}
+
+// Sound
+
+void MickeyEngine::playNote(MSA_SND_NOTE note) {
+ if (!note.counter) {
+ // Pause
+ _system->delayMillis((uint)(note.length / IDI_SND_TIMER_RESOLUTION));
+ } else {
+ PreAgiEngine::playNote(IDI_SND_OSCILLATOR_FREQUENCY / note.counter, (int32)(note.length / IDI_SND_TIMER_RESOLUTION));
+ }
+}
+
+void MickeyEngine::playSound(ENUM_MSA_SOUND iSound) {
+ if (!getFlag(VM_FLAG_SOUND_ON))
+ return;
+
+ Common::Event event;
+ MSA_SND_NOTE note;
+ uint8 *buffer = new uint8[1024];
+ int pBuf = 1;
+
+ switch (iSound) {
+ case IDI_MSA_SND_XL30:
+ for (int iNote = 0; iNote < 6; iNote++) {
+ note.counter = rnd(59600) + 59;
+ note.length = 4;
+ playNote(note);
+ }
+ break;
+ default:
+ readOfsData(IDOFS_MSA_SOUND_DATA, iSound, buffer, 1024);
+
+ for (;;) {
+ memcpy(¬e, buffer + pBuf, sizeof(note));
+ if (!note.counter && !note.length)
+ break;
+
+ playNote(note);
+
+ pBuf += 3;
+
+ if (iSound == IDI_MSA_SND_THEME) {
+ while (_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_RETURN_TO_LAUNCHER:
+ case Common::EVENT_QUIT:
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONUP:
+ case Common::EVENT_KEYDOWN:
+ delete[] buffer;
+ return;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ break;
+ }
+
+ delete[] buffer;
+}
+
+// Graphics
+
+void MickeyEngine::drawObj(ENUM_MSA_OBJECT iObj, int x0, int y0) {
+ char szFile[255] = {0};
+ sprintf(szFile, IDS_MSA_PATH_OBJ, IDS_MSA_NAME_OBJ[iObj]);
+
+ Common::File file;
+ if (!file.open(szFile))
+ return;
+
+ uint8 *buffer = new uint8[4096];
+ uint32 size = file.size();
+ file.read(buffer, size);
+ file.close();
+
+ if (iObj == IDI_MSA_OBJECT_CRYSTAL)
+ _picture->setPictureFlags(kPicFStep);
+
+ _picture->setOffset(x0, y0);
+ _picture->decodePicture(buffer, size, false, IDI_MSA_PIC_WIDTH, IDI_MSA_PIC_HEIGHT);
+ _picture->setOffset(0, 0);
+ _picture->showPic(10, 0, IDI_MSA_PIC_WIDTH, IDI_MSA_PIC_HEIGHT);
+}
+
+void MickeyEngine::drawPic(int iPic) {
+ char szFile[255] = {0};
+ sprintf(szFile, IDS_MSA_PATH_PIC, iPic);
+
+ Common::File file;
+ if (!file.open(szFile))
+ return;
+
+ uint8 *buffer = new uint8[4096];
+ uint32 size = file.size();
+ file.read(buffer, size);
+ file.close();
+
+ // Note that decodePicture clears the screen
+ _picture->setOffset(10, 0);
+ _picture->decodePicture(buffer, size, true, IDI_MSA_PIC_WIDTH, IDI_MSA_PIC_HEIGHT);
+ _picture->setOffset(0, 0);
+ _picture->showPic(10, 0, IDI_MSA_PIC_WIDTH, IDI_MSA_PIC_HEIGHT);
+}
+
+void MickeyEngine::drawRoomAnimation() {
+ uint8 objLight[] = {
+ 0xF0, 1, 0xF9, 2, 43, 45, 0xFF
+ };
+
+ switch (_gameStateMickey.iRoom) {
+ case IDI_MSA_PIC_EARTH_SHIP:
+ case IDI_MSA_PIC_VENUS_SHIP:
+ case IDI_MSA_PIC_NEPTUNE_SHIP:
+ case IDI_MSA_PIC_MERCURY_SHIP:
+ case IDI_MSA_PIC_SATURN_SHIP:
+ case IDI_MSA_PIC_PLUTO_SHIP:
+ case IDI_MSA_PIC_JUPITER_SHIP:
+ case IDI_MSA_PIC_MARS_SHIP:
+ case IDI_MSA_PIC_URANUS_SHIP:
+ case IDI_MSA_PIC_SHIP_VENUS:
+ case IDI_MSA_PIC_SHIP_NEPTUNE:
+ case IDI_MSA_PIC_SHIP_MERCURY:
+ case IDI_MSA_PIC_SHIP_SATURN:
+ case IDI_MSA_PIC_SHIP_PLUTO:
+ case IDI_MSA_PIC_SHIP_JUPITER:
+ case IDI_MSA_PIC_SHIP_MARS:
+ case IDI_MSA_PIC_SHIP_URANUS: {
+ // draw blinking ship lights
+
+ uint8 iColor = 0;
+
+ _picture->setPattern(2, 0);
+
+ for (int i = 0; i < 12; i++) {
+ iColor = _gameStateMickey.nFrame + i;
+ if (iColor > 15)
+ iColor -= 15;
+
+ objLight[1] = iColor;
+ objLight[4] += 7;
+
+ _picture->setPictureData(objLight);
+ _picture->setPictureFlags(kPicFCircle);
+ _picture->drawPicture();
+ }
+ _picture->showPic(10, 0, IDI_MSA_PIC_WIDTH, IDI_MSA_PIC_HEIGHT);
+
+
+ _gameStateMickey.nFrame--;
+ if (_gameStateMickey.nFrame < 0)
+ _gameStateMickey.nFrame = 15;
+
+ playSound(IDI_MSA_SND_PRESS_BLUE);
+ }
+ break;
+
+ case IDI_MSA_PIC_SHIP_CONTROLS:
+
+ // draw XL30 screen
+ if (_gameStateMickey.fAnimXL30) {
+ if (_gameStateMickey.nFrame > 5)
+ _gameStateMickey.nFrame = 0;
+
+ drawObj((ENUM_MSA_OBJECT)(IDI_MSA_OBJECT_XL31 + _gameStateMickey.nFrame), 0, 4);
+ _gameStateMickey.nFrame++;
+ };
+
+ break;
+
+ default:
+
+ // draw crystal
+ if (_gameStateMickey.iRoom == IDI_MSA_XTAL_ROOM_XY[_gameStateMickey.iPlanet][0]) {
+ if (!_gameStateMickey.fHasXtal) {
+ switch (_gameStateMickey.iPlanet) {
+ case IDI_MSA_PLANET_VENUS:
+ if (_gameStateMickey.iRmMenu[_gameStateMickey.iRoom] != 2)
+ break;
+ // fall through
+ default:
+ drawObj(
+ IDI_MSA_OBJECT_CRYSTAL,
+ IDI_MSA_XTAL_ROOM_XY[_gameStateMickey.iPlanet][1],
+ IDI_MSA_XTAL_ROOM_XY[_gameStateMickey.iPlanet][2]
+ );
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+}
+
+void MickeyEngine::drawRoom() {
+ uint8 buffer[512];
+ int pBuf = 0;
+ int nObjs;
+
+ // Draw room picture
+ if (_gameStateMickey.iRoom == IDI_MSA_PIC_TITLE) {
+ drawPic(IDI_MSA_PIC_TITLE);
+ } else {
+ drawPic(_gameStateMickey.iRmPic[_gameStateMickey.iRoom]);
+
+ if (_gameStateMickey.iRoom == IDI_MSA_PIC_SHIP_CONTROLS) {
+ // Draw ship control room window
+ if (_gameStateMickey.fFlying) {
+ drawObj(IDI_MSA_OBJECT_W_SPACE, 0, 0);
+ } else {
+ drawObj((ENUM_MSA_OBJECT)(IDI_MSA_OBJECT_W_EARTH + _gameStateMickey.iPlanet), 0, 1);
+ }
+ }
+ }
+
+ // Draw room objects
+ if (_gameStateMickey.iRoom >= IDI_MSA_MAX_PIC_ROOM) {
+ drawRoomAnimation();
+ return;
+ }
+
+ if (_gameStateMickey.iRmObj[_gameStateMickey.iRoom] != IDI_MSA_OBJECT_NONE) {
+ readOfsData(IDO_MSA_ROOM_OBJECT_XY_OFFSETS,
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom], buffer, sizeof(buffer));
+
+ nObjs = buffer[pBuf++];
+
+ for (int iObj = 0; iObj < nObjs; iObj++) {
+ drawObj((ENUM_MSA_OBJECT)buffer[pBuf], buffer[pBuf + 1], buffer[pBuf + 2]);
+ pBuf += 3;
+ }
+ }
+
+ // Draw room animation
+ drawRoomAnimation();
+}
+
+// Straight mapping, CGA colors to CGA
+const byte BCGColorMappingCGAToCGA[4] = {
+ 0, 1, 2, 3
+};
+
+// Mapping table to map CGA colors to EGA
+const byte BCGColorMappingCGAToEGA[4] = {
+ 0, 11, 13, 15
+};
+
+void MickeyEngine::drawLogo() {
+ const int width = 80;
+ const int height = 85 * 2;
+ byte color1, color2, color3, color4;
+ byte *fileBuffer = nullptr;
+ uint32 fileBufferSize = 0;
+ byte *dataBuffer;
+ byte curByte;
+ const byte *BCGColorMapping = BCGColorMappingCGAToEGA;
+
+ // disable color mapping in case we are in CGA mode
+ if (_renderMode == Common::kRenderCGA)
+ BCGColorMapping = BCGColorMappingCGAToCGA;
+
+ // read logos.bcg
+ Common::File infile;
+ if (!infile.open(IDS_MSA_PATH_LOGO))
+ return;
+
+ fileBufferSize = infile.size();
+ fileBuffer = new byte[fileBufferSize];
+ infile.read(fileBuffer, fileBufferSize);
+ infile.close();
+
+ if (fileBufferSize < (width * height / 4))
+ error("logos.bcg: unexpected end of file");
+
+ // Show BCG picture
+ // It's basically uncompressed CGA 4-color data (4 pixels per byte)
+ dataBuffer = fileBuffer;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ curByte = *dataBuffer++;
+
+ color1 = BCGColorMapping[(curByte >> 6) & 0x03];
+ color2 = BCGColorMapping[(curByte >> 4) & 0x03];
+ color3 = BCGColorMapping[(curByte >> 2) & 0x03];
+ color4 = BCGColorMapping[(curByte >> 0) & 0x03];
+
+ _gfx->putPixelOnDisplay(x * 4 + 0, y, color1);
+ _gfx->putPixelOnDisplay(x * 4 + 1, y, color2);
+ _gfx->putPixelOnDisplay(x * 4 + 2, y, color3);
+ _gfx->putPixelOnDisplay(x * 4 + 3, y, color4);
+ }
+ }
+
+ _gfx->copyDisplayToScreen();
+
+ delete[] fileBuffer;
+}
+
+void MickeyEngine::animate() {
+ _system->delayMillis(IDI_MSA_ANIM_DELAY);
+ drawRoomAnimation();
+}
+
+void MickeyEngine::printRoomDesc() {
+ // print room description
+ printDesc(_gameStateMickey.iRoom);
+ waitAnyKey(true);
+
+ // print extended room description
+ if (_gameStateMickey.oRmTxt[_gameStateMickey.iRoom]) {
+ printExeMsg(_gameStateMickey.oRmTxt[_gameStateMickey.iRoom] + IDI_MSA_OFS_EXE);
+ }
+}
+
+bool MickeyEngine::loadGame() {
+ Common::InSaveFile *infile;
+ char szFile[256] = {0};
+ bool diskerror = true;
+ int sel;
+ int saveVersion = 0;
+ int i = 0;
+
+ while (diskerror) {
+ sel = choose1to9(IDO_MSA_LOAD_GAME[1]);
+ if (!sel)
+ return false;
+
+ // load game
+ sprintf(szFile, "%s.s%02d", getTargetName().c_str(), sel);
+ if (!(infile = getSaveFileMan()->openForLoading(szFile))) {
+ printLine("PLEASE CHECK THE DISK DRIVE");
+
+ if (getSelection(kSelAnyKey) == 0)
+ return false;
+ } else {
+ if (infile->readUint32BE() != MKTAG('M', 'I', 'C', 'K')) {
+ warning("MickeyEngine::loadGame wrong save game format");
+ return false;
+ }
+
+ saveVersion = infile->readByte();
+ if (saveVersion < 2) {
+ warning("The planet data in this save game is corrupted. Load aborted");
+ return false;
+ }
+
+ if (saveVersion != MSA_SAVEGAME_VERSION)
+ warning("Old save game version (%d, current version is %d). Will try and read anyway, but don't be surprised if bad things happen", saveVersion, MSA_SAVEGAME_VERSION);
+
+ _gameStateMickey.iRoom = infile->readByte();
+ _gameStateMickey.iPlanet = infile->readByte();
+ _gameStateMickey.iDisk = infile->readByte();
+
+ _gameStateMickey.nAir = infile->readByte();
+ _gameStateMickey.nButtons = infile->readByte();
+ _gameStateMickey.nRocks = infile->readByte();
+
+ _gameStateMickey.nXtals = infile->readByte();
+
+ for (i = 0; i < IDI_MSA_MAX_DAT; i++)
+ _gameStateMickey.iPlanetXtal[i] = infile->readByte();
+
+ for (i = 0; i < IDI_MSA_MAX_PLANET; i++)
+ _gameStateMickey.iClue[i] = infile->readUint16LE();
+
+ infile->read(_gameStateMickey.szAddr, IDI_MSA_MAX_BUTTON + 1);
+
+ _gameStateMickey.fHasXtal = infile->readByte() == 1;
+ _gameStateMickey.fIntro = infile->readByte() == 1;
+ _gameStateMickey.fSuit = infile->readByte() == 1;
+ _gameStateMickey.fShipDoorOpen = infile->readByte() == 1;
+ _gameStateMickey.fFlying = infile->readByte() == 1;
+ _gameStateMickey.fStoryShown = infile->readByte() == 1;
+ _gameStateMickey.fPlanetsInitialized = infile->readByte() == 1;
+ _gameStateMickey.fTempleDoorOpen = infile->readByte() == 1;
+ _gameStateMickey.fAnimXL30 = infile->readByte() == 1;
+
+ for (i = 0; i < IDI_MSA_MAX_ITEM; i++)
+ _gameStateMickey.fItem[i] = infile->readByte() == 1;
+
+ for (i = 0; i < IDI_MSA_MAX_ITEM; i++)
+ _gameStateMickey.fItemUsed[i] = infile->readByte() == 1;
+
+ for (i = 0; i < IDI_MSA_MAX_ITEM; i++)
+ _gameStateMickey.iItem[i] = infile->readSByte();
+
+ _gameStateMickey.nItems = infile->readByte();
+
+ for (i = 0; i < IDI_MSA_MAX_ROOM; i++)
+ _gameStateMickey.iRmObj[i] = infile->readSByte();
+
+ for (i = 0; i < IDI_MSA_MAX_ROOM; i++)
+ _gameStateMickey.iRmPic[i] = infile->readByte();
+
+ for (i = 0; i < IDI_MSA_MAX_ROOM; i++)
+ _gameStateMickey.oRmTxt[i] = infile->readUint16LE();
+
+ for (i = 0; i < IDI_MSA_MAX_ROOM; i++)
+ _gameStateMickey.iRmMenu[i] = infile->readByte();
+
+ for (i = 0; i < IDI_MSA_MAX_ROOM; i++)
+ _gameStateMickey.nRmMenu[i] = infile->readByte();
+
+ _gameStateMickey.nFrame = infile->readSByte();
+
+ diskerror = false;
+ delete infile;
+ }
+ }
+
+ printExeMsg(IDO_MSA_LOAD_GAME[2]);
+ return true;
+}
+
+void MickeyEngine::saveGame() {
+ Common::OutSaveFile *outfile;
+ char szFile[256] = {0};
+ bool diskerror = true;
+ int sel;
+ int i = 0;
+
+ bool fOldDisk = chooseY_N(IDO_MSA_SAVE_GAME[0], false);
+
+ if (fOldDisk)
+ printExeStr(IDO_MSA_SAVE_GAME[1]);
+ else
+ printExeStr(IDO_MSA_SAVE_GAME[2]);
+
+ if (getSelection(kSelAnyKey) == 0)
+ return;
+
+ while (diskerror) {
+ sel = choose1to9(IDO_MSA_SAVE_GAME[3]);
+ if (!sel)
+ return;
+
+ if (fOldDisk)
+ printExeStr(IDO_MSA_SAVE_GAME[5]);
+ else
+ printExeStr(IDO_MSA_SAVE_GAME[4]);
+
+ if (getSelection(kSelAnyKey) == 0)
+ return;
+
+ // save game
+ sprintf(szFile, "%s.s%02d", getTargetName().c_str(), sel);
+ if (!(outfile = getSaveFileMan()->openForSaving(szFile))) {
+ printLine("PLEASE CHECK THE DISK DRIVE");
+
+ if (getSelection(kSelAnyKey) == 0)
+ return;
+ } else {
+ outfile->writeUint32BE(MKTAG('M', 'I', 'C', 'K')); // header
+ outfile->writeByte(MSA_SAVEGAME_VERSION);
+
+ outfile->writeByte(_gameStateMickey.iRoom);
+ outfile->writeByte(_gameStateMickey.iPlanet);
+ outfile->writeByte(_gameStateMickey.iDisk);
+
+ outfile->writeByte(_gameStateMickey.nAir);
+ outfile->writeByte(_gameStateMickey.nButtons);
+ outfile->writeByte(_gameStateMickey.nRocks);
+
+ outfile->writeByte(_gameStateMickey.nXtals);
+
+ for (i = 0; i < IDI_MSA_MAX_DAT; i++)
+ outfile->writeByte(_gameStateMickey.iPlanetXtal[i]);
+
+ for (i = 0; i < IDI_MSA_MAX_PLANET; i++)
+ outfile->writeUint16LE(_gameStateMickey.iClue[i]);
+
+ outfile->write(_gameStateMickey.szAddr, IDI_MSA_MAX_BUTTON + 1);
+
+ outfile->writeByte(_gameStateMickey.fHasXtal ? 1 : 0);
+ outfile->writeByte(_gameStateMickey.fIntro ? 1 : 0);
+ outfile->writeByte(_gameStateMickey.fSuit ? 1 : 0);
+ outfile->writeByte(_gameStateMickey.fShipDoorOpen ? 1 : 0);
+ outfile->writeByte(_gameStateMickey.fFlying ? 1 : 0);
+ outfile->writeByte(_gameStateMickey.fStoryShown ? 1 : 0);
+ outfile->writeByte(_gameStateMickey.fPlanetsInitialized ? 1 : 0);
+ outfile->writeByte(_gameStateMickey.fTempleDoorOpen ? 1 : 0);
+ outfile->writeByte(_gameStateMickey.fAnimXL30 ? 1 : 0);
+
+ for (i = 0; i < IDI_MSA_MAX_ITEM; i++)
+ outfile->writeByte(_gameStateMickey.fItem[i] ? 1 : 0);
+
+ for (i = 0; i < IDI_MSA_MAX_ITEM; i++)
+ outfile->writeByte(_gameStateMickey.fItemUsed[i] ? 1 : 0);
+
+ for (i = 0; i < IDI_MSA_MAX_ITEM; i++)
+ outfile->writeSByte(_gameStateMickey.iItem[i]);
+
+ outfile->writeByte(_gameStateMickey.nItems);
+
+ for (i = 0; i < IDI_MSA_MAX_ROOM; i++)
+ outfile->writeSByte(_gameStateMickey.iRmObj[i]);
+
+ for (i = 0; i < IDI_MSA_MAX_ROOM; i++)
+ outfile->writeByte(_gameStateMickey.iRmPic[i]);
+
+ for (i = 0; i < IDI_MSA_MAX_ROOM; i++)
+ outfile->writeUint16LE(_gameStateMickey.oRmTxt[i]);
+
+ for (i = 0; i < IDI_MSA_MAX_ROOM; i++)
+ outfile->writeByte(_gameStateMickey.iRmMenu[i]);
+
+ for (i = 0; i < IDI_MSA_MAX_ROOM; i++)
+ outfile->writeByte(_gameStateMickey.nRmMenu[i]);
+
+ outfile->writeSByte(_gameStateMickey.nFrame);
+
+ outfile->finalize();
+
+ if (outfile->err())
+ warning("Can't write file '%s'. (Disk full?)", szFile);
+
+ diskerror = false;
+ delete outfile;
+ }
+ }
+
+ printExeMsg(IDO_MSA_SAVE_GAME[6]);
+}
+
+void MickeyEngine::showPlanetInfo() {
+ for (int i = 0; i < 4; i++) {
+ printExeStr(IDO_MSA_PLANET_INFO[_gameStateMickey.iPlanet][i]);
+ waitAnyKey();
+ }
+}
+
+void MickeyEngine::printStory() {
+ char buffer[IDI_MSA_LEN_STORY] = {0};
+ char szLine[41] = {0};
+ int iRow;
+ int pBuf = 0;
+
+ readExe(IDO_MSA_GAME_STORY, (uint8 *)buffer, sizeof(buffer));
+
+ clearScreen(IDA_DEFAULT);
+ for (iRow = 0; iRow < 25; iRow++) {
+ Common::strlcpy(szLine, buffer + pBuf, 41);
+ drawStr(iRow, 0, IDA_DEFAULT, szLine);
+ pBuf += strlen(szLine) + 1;
+ }
+ waitAnyKey();
+
+ clearScreen(IDA_DEFAULT);
+ for (iRow = 0; iRow < 21; iRow++) {
+ Common::strlcpy(szLine, buffer + pBuf, 41);
+ drawStr(iRow, 0, IDA_DEFAULT, szLine);
+ pBuf += strlen(szLine) + 1;
+ }
+ waitAnyKey();
+
+ //Set back to black
+ _gfx->clearDisplay(0);
+ _gfx->updateScreen();
+
+ drawRoom();
+
+ _gameStateMickey.fStoryShown = true;
+}
+
+int MickeyEngine::getPlanet() {
+ if (!_gameStateMickey.nButtons)
+ return -1;
+
+ for (int iPlanet = 0; iPlanet < IDI_MSA_MAX_DAT - 1; iPlanet++) {
+ if (!strcmp(IDS_MSA_ADDR_PLANET[iPlanet], _gameStateMickey.szAddr)) {
+ return iPlanet;
+ }
+ }
+
+ return -1;
+}
+
+void MickeyEngine::pressOB(int iButton) {
+ char szButtons[12] = {0};
+
+ // check if too many buttons pressed
+ if (_gameStateMickey.nButtons == IDI_MSA_MAX_BUTTON) {
+ _gameStateMickey.nButtons = 0;
+ memset(_gameStateMickey.szAddr, 0, sizeof(_gameStateMickey.szAddr));
+ printExeMsg(IDO_MSA_TOO_MANY_BUTTONS_PRESSED);
+ return;
+ }
+
+ // add button press to address
+ _gameStateMickey.nButtons++;
+ _gameStateMickey.szAddr[_gameStateMickey.nButtons - 1] = (char)iButton;
+
+ // format buttons string
+ for (int i = 0; i < IDI_MSA_MAX_BUTTON; i++) {
+ szButtons[i * 2] = _gameStateMickey.szAddr[i];
+ if (_gameStateMickey.szAddr[i + 1]) szButtons[(i * 2) + 1] = ',';
+ }
+
+ // print pressed buttons
+ printLine("MICKEY HAS PRESSED: ");
+ drawStr(20, 22, IDA_DEFAULT, szButtons);
+ waitAnyKey();
+}
+
+void MickeyEngine::insertDisk(int iDisk) {
+ clearTextArea();
+ drawStr(IDI_MSA_ROW_INSERT_DISK, IDI_MSA_COL_INSERT_DISK, IDA_DEFAULT, (const char *)IDS_MSA_INSERT_DISK[iDisk]);
+ waitAnyKey();
+}
+
+void MickeyEngine::gameOver() {
+ // We shouldn't run the game over segment if we're quitting.
+ if (shouldQuit())
+ return;
+
+ drawPic(IDI_MSA_PIC_EARTH_SHIP_LEAVING);
+ printExeMsg(IDO_MSA_GAME_OVER[3]);
+ playSound(IDI_MSA_SND_GAME_OVER);
+
+ if (_gameStateMickey.fItemUsed[IDI_MSA_ITEM_LETTER]) {
+ drawPic(IDI_MSA_PIC_EARTH_MINNIE);
+ printExeMsg(IDO_MSA_GAME_OVER[4]);
+ printExeMsg(IDO_MSA_GAME_OVER[5]);
+ } else {
+ printExeMsg(IDO_MSA_GAME_OVER[6]);
+ printExeMsg(IDO_MSA_GAME_OVER[7]);
+ }
+
+ waitAnyKey();
+}
+
+void MickeyEngine::flipSwitch() {
+ if (_gameStateMickey.fHasXtal || _gameStateMickey.nXtals) {
+ if (!_gameStateMickey.fStoryShown)
+ printStory();
+
+ // Initialize planet data
+ if (!_gameStateMickey.fPlanetsInitialized) {
+ int iHint = 0;
+ int iPlanet = 0;
+
+ memset(_gameStateMickey.iPlanetXtal, 0, sizeof(_gameStateMickey.iPlanetXtal));
+ memset(_gameStateMickey.iClue, 0, sizeof(_gameStateMickey.iClue));
+
+ _gameStateMickey.iPlanetXtal[0] = IDI_MSA_PLANET_EARTH;
+ _gameStateMickey.iPlanetXtal[8] = IDI_MSA_PLANET_URANUS;
+
+ for (int i = 1; i < IDI_MSA_MAX_PLANET; i++) {
+ if (i < 8) {
+ do {
+ // Earth (planet 0) and Uranus (planet 8) are excluded
+ iPlanet = rnd(IDI_MSA_MAX_PLANET - 2);
+ } while (planetIsAlreadyAssigned(iPlanet));
+ } else {
+ iPlanet = IDI_MSA_PLANET_URANUS; // Uranus is always last
+ }
+
+ _gameStateMickey.iPlanetXtal[i] = iPlanet;
+ iHint = rnd(5) - 1; // clues are 0-4
+ _gameStateMickey.iClue[i] = IDO_MSA_NEXT_PIECE[iPlanet][iHint];
+ }
+
+ _gameStateMickey.fPlanetsInitialized = true;
+ }
+
+ // activate screen animation
+ _gameStateMickey.fAnimXL30 = true;
+
+ clearTextArea();
+ playSound(IDI_MSA_SND_XL30);
+ printExeMsg(IDO_MSA_XL30_SPEAKING);
+
+ if (_gameStateMickey.fHasXtal) {
+ _gameStateMickey.fHasXtal = false;
+ printExeMsg(IDO_MSA_CRYSTAL_PIECE_FOUND);
+ }
+
+ if (_gameStateMickey.nXtals == IDI_MSA_MAX_PLANET) {
+ printExeMsg(IDO_MSA_GAME_OVER[0]);
+ printExeMsg(IDO_MSA_GAME_OVER[1]);
+ printExeMsg(IDO_MSA_GAME_OVER[2]);
+
+#if 0
+ // DEBUG
+ strcpy(_gameStateMickey.szAddr, (char *)IDS_MSA_ADDR_PLANET[IDI_MSA_PLANET_EARTH]);
+ _gameStateMickey.nButtons = strlen(_gameStateMickey.szAddr);
+#endif
+
+ } else {
+ printExeStr(_gameStateMickey.iClue[_gameStateMickey.nXtals]);
+
+#if 0
+ // DEBUG
+ drawStr(24, 12, IDA_DEFAULT, (char *)IDS_MSA_NAME_PLANET_2[_gameStateMickey.iPlanetXtal[_gameStateMickey.nXtals]]);
+ drawStr(24, 22, IDA_DEFAULT, (char *)IDS_MSA_ADDR_PLANET[_gameStateMickey.iPlanetXtal[_gameStateMickey.nXtals]]);
+ strcpy(_gameStateMickey.szAddr, (char *)IDS_MSA_ADDR_PLANET[_gameStateMickey.iPlanetXtal[_gameStateMickey.nXtals]]);
+ _gameStateMickey.nButtons = strlen(_gameStateMickey.szAddr);
+ _gfx->doUpdate();
+#endif
+
+ waitAnyKey(true);
+ }
+ } else {
+ printStory();
+ }
+}
+
+void MickeyEngine::inventory() {
+ int iRow = IDI_MSA_ROW_INV_ITEMS;
+ char szCrystals[12] = {0};
+
+ sprintf(szCrystals, IDS_MSA_CRYSTALS, IDS_MSA_CRYSTAL_NO[_gameStateMickey.nXtals]);
+
+ CursorMan.showMouse(false);
+
+ clearScreen(IDA_DEFAULT);
+ drawStr(IDI_MSA_ROW_INV_TITLE, IDI_MSA_COL_INV_TITLE, IDA_DEFAULT, IDS_MSA_INVENTORY);
+ drawStr(IDI_MSA_ROW_INV_CRYSTALS, IDI_MSA_COL_INV_ITEMS, IDA_DEFAULT, szCrystals);
+
+ for (int iItem = 0; iItem < IDI_MSA_MAX_ITEM; iItem++) {
+ if (_gameStateMickey.fItem[_gameStateMickey.iItem[iItem]] && (_gameStateMickey.iItem[iItem] != IDI_MSA_OBJECT_NONE)) {
+ drawStr(iRow++, IDI_MSA_COL_INV_ITEMS, IDA_DEFAULT, (const char *)IDS_MSA_NAME_ITEM[_gameStateMickey.iItem[iItem]]);
+ }
+ }
+
+ waitAnyKey();
+
+ clearScreen(IDA_DEFAULT);
+
+ CursorMan.showMouse(true);
+}
+
+void MickeyEngine::intro() {
+ // Draw Sierra logo
+ drawLogo(); // Original does not even show this, so we skip it too
+ waitAnyKey(); // Not in the original, but needed so that the logo is visible
+
+ // draw title picture
+ _gameStateMickey.iRoom = IDI_MSA_PIC_TITLE;
+ drawRoom();
+
+ // show copyright and play theme
+ printExeMsg(IDO_MSA_COPYRIGHT);
+
+ // Quit if necessary
+ if (shouldQuit())
+ return;
+
+ playSound(IDI_MSA_SND_THEME);
+
+ // load game
+ _gameStateMickey.fIntro = true;
+ if (chooseY_N(IDO_MSA_LOAD_GAME[0], true)) {
+ if (loadGame()) {
+ _gameStateMickey.iPlanet = IDI_MSA_PLANET_EARTH;
+ _gameStateMickey.fIntro = false;
+ _gameStateMickey.iRoom = IDI_MSA_PIC_SHIP_CORRIDOR;
+ return;
+ }
+ }
+
+ // Quit if necessary
+ if (shouldQuit())
+ return;
+
+ // play spaceship landing scene
+ _gameStateMickey.iPlanet = IDI_MSA_PLANET_EARTH;
+ _gameStateMickey.iRoom = IDI_MSA_PIC_EARTH_ROAD_4;
+
+ drawRoom();
+ printRoomDesc();
+
+ // Quit if necessary
+ if (shouldQuit())
+ return;
+
+ playSound(IDI_MSA_SND_SHIP_LAND);
+
+ // Flash screen 3 times
+ for (byte i = 0; i < 3; i++) {
+ playSound(IDI_MSA_SND_PRESS_BLUE);
+
+ //Set screen to white
+ _gfx->clearDisplay(15);
+ _gfx->updateScreen();
+
+ _system->delayMillis(IDI_MSA_ANIM_DELAY);
+
+ //Set back to black
+ _gfx->clearDisplay(0);
+ _gfx->updateScreen();
+
+ drawRoom();
+ printDesc(_gameStateMickey.iRoom);
+ }
+
+ printExeMsg(IDO_MSA_INTRO);
+}
+
+void MickeyEngine::getItem(ENUM_MSA_ITEM iItem) {
+ _gameStateMickey.fItem[iItem] = true;
+ _gameStateMickey.iItem[_gameStateMickey.nItems++] = iItem;
+ _gameStateMickey.oRmTxt[_gameStateMickey.iRoom] = 0;
+ playSound(IDI_MSA_SND_TAKE);
+ drawRoom();
+}
+
+void MickeyEngine::getXtal(int iStr) {
+ _gameStateMickey.oRmTxt[_gameStateMickey.iRoom] = 0;
+ _gameStateMickey.fHasXtal = true;
+ _gameStateMickey.nXtals++;
+ playSound(IDI_MSA_SND_CRYSTAL);
+ drawRoom();
+ printDatMessage(iStr);
+}
+
+bool MickeyEngine::parse(int cmd, int arg) {
+ switch (cmd) {
+
+ // BASIC
+
+ case IDI_MSA_ACTION_GOTO_ROOM:
+ _gameStateMickey.iRoom = arg;
+ return true;
+ case IDI_MSA_ACTION_SHOW_INT_STR:
+ printLine(IDS_MSA_ERRORS[arg]);
+ break;
+ case IDI_MSA_ACTION_SHOW_DAT_STR:
+ printDatMessage(arg);
+ break;
+
+ // GENERAL
+
+ case IDI_MSA_ACTION_PLANET_INFO:
+ showPlanetInfo();
+ break;
+ case IDI_MSA_ACTION_SAVE_GAME:
+ saveGame();
+ break;
+ case IDI_MSA_ACTION_LOOK_MICKEY:
+ printLine("YOU CAN SEE MICKEY ALREADY");
+ break;
+
+ // EARTH
+
+ case IDI_MSA_ACTION_GET_ROPE:
+ if (_gameStateMickey.iRmMenu[_gameStateMickey.iRoom] == 2) {
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = IDI_MSA_OBJECT_NONE;
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 3;
+ getItem(IDI_MSA_ITEM_ROPE);
+ printLine("MICKEY TAKES THE ROPE");
+ } else {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ printDatMessage(11);
+ }
+ break;
+ case IDI_MSA_ACTION_UNTIE_ROPE:
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_EARTH_TIRE_SWING_1;
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = 0;
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 2;
+ drawRoom();
+ printDatMessage(12);
+ break;
+ case IDI_MSA_ACTION_GET_BONE:
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = IDI_MSA_OBJECT_NONE;
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ getItem(IDI_MSA_ITEM_BONE);
+ printDatMessage(arg);
+ break;
+ case IDI_MSA_ACTION_GET_XTAL_EARTH:
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ getXtal(arg);
+ break;
+ case IDI_MSA_ACTION_LOOK_DESK:
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = 2;
+ drawRoom();
+ printDatMessage(arg);
+ break;
+ case IDI_MSA_ACTION_WRITE_LETTER:
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 3;
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_EARTH_MAILBOX] = 1;
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = IDI_MSA_OBJECT_NONE;
+ getItem(IDI_MSA_ITEM_LETTER);
+ printDatMessage(arg);
+ break;
+ case IDI_MSA_ACTION_MAIL_LETTER:
+ _gameStateMickey.fItemUsed[IDI_MSA_ITEM_LETTER] = true;
+ _gameStateMickey.fItem[IDI_MSA_ITEM_LETTER] = false;
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 0;
+ printDatMessage(arg);
+ break;
+ case IDI_MSA_ACTION_OPEN_MAILBOX:
+ if (_gameStateMickey.fItemUsed[IDI_MSA_ITEM_LETTER]) {
+ printDatMessage(110);
+ } else {
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_OPEN_CUPBOARD:
+ if (_gameStateMickey.iRmMenu[_gameStateMickey.iRoom]) {
+ if (_gameStateMickey.iRmObj[_gameStateMickey.iRoom] == IDI_MSA_OBJECT_NONE) {
+ printDatMessage(78);
+ } else {
+ printDatMessage(arg);
+ }
+ } else {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_EARTH_KITCHEN_1;
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = 3;
+ drawRoom();
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GET_FLASHLIGHT:
+ if (!mickeyHasItem(IDI_MSA_ITEM_FLASHLIGHT)) {
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = IDI_MSA_OBJECT_NONE;
+ getItem(IDI_MSA_ITEM_FLASHLIGHT);
+ drawRoom();
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_OPEN_CABINET:
+ if (_gameStateMickey.iRmMenu[_gameStateMickey.iRoom]) {
+ printDatMessage(109);
+ } else {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_EARTH_GARAGE_1;
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = 15;
+ drawRoom();
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GET_CROWBAR:
+ if (!mickeyHasItem(IDI_MSA_ITEM_CROWBAR)) {
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom]--;
+ getItem(IDI_MSA_ITEM_CROWBAR);
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GET_WRENCH:
+ if (!mickeyHasItem(IDI_MSA_ITEM_WRENCH)) {
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] -= 2;
+ getItem(IDI_MSA_ITEM_WRENCH);
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_OPEN_CLOSET:
+ if (_gameStateMickey.iRmMenu[_gameStateMickey.iRoom]) {
+ printDatMessage(99);
+ } else {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_EARTH_BEDROOM_1;
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = 7;
+ drawRoom();
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GET_MATTRESS:
+ if (!mickeyHasItem(IDI_MSA_ITEM_MATTRESS)) {
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom]--;
+ getItem(IDI_MSA_ITEM_MATTRESS);
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GET_SCARF:
+ if (!mickeyHasItem(IDI_MSA_ITEM_SCARF)) {
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] -= 2;
+ getItem(IDI_MSA_ITEM_SCARF);
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GET_SUNGLASSES:
+ if (!mickeyHasItem(IDI_MSA_ITEM_SUNGLASSES)) {
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom]--;
+ getItem(IDI_MSA_ITEM_SUNGLASSES);
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GET_SCALE:
+ if (!mickeyHasItem(IDI_MSA_ITEM_SCALE)) {
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_VENUS_WEIGH] = 1;
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_NEPTUNE_WEIGH] = 1;
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_MERCURY_WEIGH] = 1;
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_SATURN_WEIGH] = 1;
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_PLUTO_WEIGH] = 1;
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_JUPITER_WEIGH] = 1;
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_MARS_WEIGH] = 1;
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_URANUS_WEIGH] = 1;
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] -= 2;
+ getItem(IDI_MSA_ITEM_SCALE);
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GOTO_SPACESHIP:
+ _gameStateMickey.iRoom = IDI_MSA_PIC_SHIP_AIRLOCK;
+ if (_gameStateMickey.iPlanet != IDI_MSA_PLANET_EARTH)
+ insertDisk(0);
+ return true;
+
+ // VENUS
+
+ case IDI_MSA_ACTION_DOWN_CHASM:
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_ROPE]) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ }
+ printDatMessage(arg);
+ break;
+ case IDI_MSA_ACTION_DOWN_ROPE:
+ if (_gameStateMickey.fItemUsed[IDI_MSA_ITEM_ROPE]) {
+ _gameStateMickey.iRoom = IDI_MSA_PIC_VENUS_PROBE;
+ return true;
+ } else {
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_USE_ROPE:
+ if (_gameStateMickey.fItemUsed[IDI_MSA_ITEM_ROPE]) {
+ printDatMessage(22);
+ } else {
+ _gameStateMickey.fItemUsed[IDI_MSA_ITEM_ROPE] = true;
+ _gameStateMickey.fItem[IDI_MSA_ITEM_ROPE] = false;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_VENUS_CHASM_1;
+ drawRoom();
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_OPEN_HATCH:
+ if (_gameStateMickey.fItemUsed[IDI_MSA_ITEM_WRENCH]) {
+ if ((_gameStateMickey.iRmMenu[_gameStateMickey.iRoom] == 3) || (_gameStateMickey.iRmPic[_gameStateMickey.iRoom] == IDI_MSA_PIC_VENUS_PROBE_1))
+ printDatMessage(39);
+ else {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 2;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_VENUS_PROBE_1;
+ drawRoom();
+ printDatMessage(24);
+ }
+ } else {
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_WRENCH]) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ }
+
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_USE_WRENCH:
+ _gameStateMickey.fItemUsed[IDI_MSA_ITEM_WRENCH] = true;
+ printDatString(arg);
+
+ if (_gameStateMickey.iRmPic[_gameStateMickey.iRoom] == IDI_MSA_PIC_VENUS_PROBE_1) {
+ clearRow(22);
+ }
+
+ waitAnyKey();
+ break;
+ case IDI_MSA_ACTION_GET_XTAL_VENUS:
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 3;
+ getXtal(arg);
+ break;
+
+ // TRITON (NEPTUNE)
+
+ case IDI_MSA_ACTION_LOOK_CASTLE:
+ if (!_gameStateMickey.iRmMenu[_gameStateMickey.iRoom]) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ }
+ printDatMessage(arg);
+ break;
+ case IDI_MSA_ACTION_ENTER_OPENING:
+ if (_gameStateMickey.fItemUsed[IDI_MSA_ITEM_CROWBAR]) {
+ _gameStateMickey.iRoom = IDI_MSA_PIC_NEPTUNE_CASTLE_4;
+
+ return true;
+ } else {
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_CROWBAR]) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 2;
+ }
+
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_USE_CROWBAR:
+ _gameStateMickey.fItemUsed[IDI_MSA_ITEM_CROWBAR] = true;
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_NEPTUNE_ENTRANCE_1;
+ drawRoom();
+ printDatMessage(arg);
+ break;
+ case IDI_MSA_ACTION_GET_XTAL_NEPTUNE:
+ if (_gameStateMickey.fHasXtal) {
+ printDatMessage(71);
+ } else {
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_SCARF]) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ }
+
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_TALK_LEADER:
+ _gameStateMickey.iRoom = IDI_MSA_PIC_NEPTUNE_ENTRYWAY;
+
+ printDatMessage(arg);
+ return true;
+ case IDI_MSA_ACTION_GIVE_SCARF:
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = 18;
+ getXtal(arg);
+ _gameStateMickey.fItem[IDI_MSA_ITEM_SCARF] = false;
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 0;
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_EARTH_BEDROOM] = 2;
+ _gameStateMickey.iRoom = IDI_MSA_PIC_NEPTUNE_ENTRYWAY;
+
+ return true;
+
+ // MERCURY
+
+ case IDI_MSA_ACTION_GET_XTAL_MERCURY:
+ if (_gameStateMickey.fHasXtal) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 2;
+ printDatMessage(32);
+ } else {
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_SUNGLASSES]) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ }
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GIVE_SUNGLASSES:
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = 17;
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 2;
+ _gameStateMickey.fItem[IDI_MSA_ITEM_SUNGLASSES] = false;
+
+ getXtal(arg);
+
+ break;
+
+ // TITAN (SATURN)
+
+ case IDI_MSA_ACTION_CROSS_LAKE:
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_MATTRESS]) {
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_SATURN_LAKE_0] = 1;
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_SATURN_LAKE_1] = 1;
+ _gameStateMickey.iRmMenu[IDI_MSA_PIC_SATURN_LAKE_2] = 1;
+ }
+
+ printDatMessage(arg);
+
+ break;
+ case IDI_MSA_ACTION_USE_MATTRESS:
+ _gameStateMickey.iRoom = IDI_MSA_PIC_SATURN_ISLAND;
+
+ printDatMessage(arg);
+
+ return true;
+ case IDI_MSA_ACTION_GET_XTAL_SATURN:
+ if (_gameStateMickey.fHasXtal) {
+ printDatMessage(29);
+ } else {
+ getXtal(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_LEAVE_ISLAND:
+ _gameStateMickey.iRoom = IDI_MSA_PIC_SATURN_LAKE_1;
+
+ printDatMessage(arg);
+
+ return true;
+
+ // PLUTO
+
+ case IDI_MSA_ACTION_GET_XTAL_PLUTO:
+ if (_gameStateMickey.fHasXtal) {
+ printDatMessage(19);
+ } else {
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_BONE]) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ }
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GIVE_BONE:
+ _gameStateMickey.fItem[IDI_MSA_ITEM_BONE] = false;
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 0;
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = 16;
+
+ getXtal(arg);
+
+ break;
+
+ // IO (JUPITER)
+
+ case IDI_MSA_ACTION_GET_ROCK_0:
+ case IDI_MSA_ACTION_GET_ROCK_1:
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_ROCK]) {
+ printDatMessage(38);
+ } else {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom] = IDI_MSA_OBJECT_NONE;
+ getItem(IDI_MSA_ITEM_ROCK);
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GET_XTAL_JUPITER:
+ if (_gameStateMickey.fHasXtal) {
+ printDatMessage(15);
+ } else {
+ switch (_gameStateMickey.nRocks) {
+ case 0:
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_ROCK]) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ }
+ printDatMessage(arg);
+ break;
+ case 1:
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_ROCK]) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ }
+ printDatMessage(34);
+ break;
+ case 2:
+ getXtal(35);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case IDI_MSA_ACTION_THROW_ROCK:
+ _gameStateMickey.fItem[IDI_MSA_ITEM_ROCK] = false;
+ _gameStateMickey.nItems--;
+ _gameStateMickey.iRmObj[_gameStateMickey.iRoom]++;
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 0;
+
+ drawRoom();
+
+ if (_gameStateMickey.nRocks) {
+ printDatMessage(37);
+ } else {
+ printDatMessage(arg);
+ }
+
+ _gameStateMickey.nRocks++;
+ break;
+
+ // MARS
+
+ case IDI_MSA_ACTION_GO_TUBE:
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_FLASHLIGHT]) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ }
+
+ printDatMessage(arg);
+
+ break;
+ case IDI_MSA_ACTION_USE_FLASHLIGHT:
+ _gameStateMickey.iRoom = IDI_MSA_PIC_MARS_TUBE_1;
+
+ printDatMessage(15);
+
+ return true;
+ case IDI_MSA_ACTION_PLUTO_DIG:
+ if (_gameStateMickey.fHasXtal) {
+ printDatMessage(21);
+ } else {
+ getXtal(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GET_XTAL_MARS:
+ if (_gameStateMickey.fHasXtal) {
+ printDatMessage(23);
+ } else {
+ printDatMessage(arg);
+ }
+ break;
+
+ // OBERON (URANUS)
+
+ case IDI_MSA_ACTION_ENTER_TEMPLE:
+ _gameStateMickey.iRoom = IDI_MSA_PIC_URANUS_TEMPLE;
+
+ return true;
+ case IDI_MSA_ACTION_USE_CRYSTAL:
+ if (_gameStateMickey.iRmMenu[_gameStateMickey.iRoom]) {
+ printDatMessage(25);
+ } else {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_URANUS_TEMPLE_1;
+
+ drawRoom();
+
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_URANUS_TEMPLE;
+
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_OPEN_DOOR:
+ if (_gameStateMickey.fTempleDoorOpen) {
+ printDatMessage(36);
+ } else {
+ _gameStateMickey.fTempleDoorOpen = 1;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_URANUS_TEMPLE_2;
+
+ drawRoom();
+
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_ENTER_DOOR:
+ if (_gameStateMickey.fTempleDoorOpen) {
+ _gameStateMickey.iRoom = IDI_MSA_PIC_URANUS_STEPS;
+
+ return true;
+ } else {
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GET_XTAL_URANUS:
+ if (_gameStateMickey.fHasXtal) {
+ printDatMessage(34);
+ } else {
+ if (_gameStateMickey.fItem[IDI_MSA_ITEM_CROWBAR]) {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ }
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_USE_CROWBAR_1:
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 0;
+
+ getXtal(arg);
+
+ break;
+
+ // SPACESHIP
+
+ case IDI_MSA_ACTION_GO_NORTH:
+ if (_gameStateMickey.fShipDoorOpen) {
+ if (_gameStateMickey.fSuit) {
+ printDatMessage(45);
+ } else {
+ _gameStateMickey.iRoom = IDI_MSA_PIC_SHIP_CORRIDOR;
+ return true;
+ }
+ } else {
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_GO_PLANET:
+ if (!_gameStateMickey.fShipDoorOpen) {
+ if ((_gameStateMickey.nXtals == IDI_MSA_MAX_PLANET) && (_gameStateMickey.iPlanet == IDI_MSA_PLANET_EARTH))
+ gameOver();
+ if ((_gameStateMickey.iPlanet == _gameStateMickey.iPlanetXtal[_gameStateMickey.nXtals]) || (_gameStateMickey.iPlanet == IDI_MSA_PLANET_EARTH)) {
+ _gameStateMickey.fHasXtal = false;
+ _gameStateMickey.iRoom = IDI_MSA_HOME_PLANET[_gameStateMickey.iPlanet];
+
+ if (_gameStateMickey.iPlanet != IDI_MSA_PLANET_EARTH)
+ insertDisk(1);
+
+ return true;
+ } else {
+ _gameStateMickey.iRoom = IDI_MSA_SHIP_PLANET[_gameStateMickey.iPlanet];
+
+ return true;
+ }
+ } else {
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_PRESS_BUTTON:
+ if (_gameStateMickey.fShipDoorOpen) { // inner door open
+ if (_gameStateMickey.iPlanet && !_gameStateMickey.fSuit) {
+ printDatMessage(arg);
+ } else {
+ _gameStateMickey.fShipDoorOpen = false;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom]--;
+
+ drawRoom();
+
+ printDatMessage(2);
+ }
+ } else {
+ _gameStateMickey.fShipDoorOpen = true;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom]++;
+
+ drawRoom();
+
+ printDatMessage(14);
+ }
+ break;
+ case IDI_MSA_ACTION_WEAR_SPACESUIT:
+ if (_gameStateMickey.fSuit) {
+ if (_gameStateMickey.fShipDoorOpen) {
+ _gameStateMickey.fSuit = false;
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 0;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] -= 2;
+
+ drawRoom();
+
+ printDatMessage(13);
+ } else {
+ printDatMessage(3);
+ }
+ } else {
+ if (_gameStateMickey.iPlanet) {
+ _gameStateMickey.fSuit = true;
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] += 2;
+
+ drawRoom();
+
+ printDatMessage(arg);
+ } else {
+ printDatMessage(12);
+ }
+ }
+ break;
+ case IDI_MSA_ACTION_READ_GAUGE:
+ printDatString(arg);
+ drawStr(21, 15, IDA_DEFAULT, (const char *)IDS_MSA_TEMP_C[_gameStateMickey.iPlanet]);
+ drawStr(21, 23, IDA_DEFAULT, (const char *)IDS_MSA_TEMP_F[_gameStateMickey.iPlanet]);
+
+ waitAnyKey();
+
+ break;
+ case IDI_MSA_ACTION_PRESS_ORANGE:
+ if (_gameStateMickey.fFlying) {
+ printDatMessage(4);
+ } else {
+ playSound(IDI_MSA_SND_PRESS_ORANGE);
+ printDatMessage(arg);
+ pressOB(IDI_MSA_BUTTON_ORANGE);
+ }
+ break;
+ case IDI_MSA_ACTION_PRESS_BLUE:
+ if (_gameStateMickey.fFlying) {
+ printDatMessage(4);
+ } else {
+ playSound(IDI_MSA_SND_PRESS_BLUE);
+ printDatMessage(arg);
+ pressOB(IDI_MSA_BUTTON_BLUE);
+ }
+ break;
+ case IDI_MSA_ACTION_FLIP_SWITCH:
+ flipSwitch();
+ break;
+ case IDI_MSA_ACTION_PUSH_THROTTLE:
+ if (_gameStateMickey.fFlying) {
+ _gameStateMickey.fFlying = false;
+ _gameStateMickey.nButtons = 0;
+
+ memset(_gameStateMickey.szAddr, 0, sizeof(_gameStateMickey.szAddr));
+
+ drawRoom();
+
+ printDatString(22);
+
+ drawStr(IDI_MSA_ROW_PLANET, IDI_MSA_COL_PLANET, IDA_DEFAULT,
+ (const char *)IDS_MSA_PLANETS[_gameStateMickey.iPlanet]);
+
+ waitAnyKey(true);
+
+ showPlanetInfo();
+ } else {
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_PULL_THROTTLE:
+ if (_gameStateMickey.fFlying) {
+ printDatMessage(18);
+ } else {
+ if (getPlanet() != -1) {
+ _gameStateMickey.fFlying = true;
+ _gameStateMickey.iPlanet = getPlanet();
+
+ drawRoom();
+
+ printDatMessage(16);
+ } else {
+ _gameStateMickey.nButtons = 0;
+
+ memset(_gameStateMickey.szAddr, 0, sizeof(_gameStateMickey.szAddr));
+
+ printDatMessage(17);
+ }
+ }
+ break;
+ case IDI_MSA_ACTION_LEAVE_ROOM:
+ if (_gameStateMickey.fFlying) {
+ printDatMessage(24);
+ } else {
+ _gameStateMickey.iRoom = arg;
+ return true;
+ }
+ break;
+ case IDI_MSA_ACTION_OPEN_CABINET_1:
+ if (_gameStateMickey.iRmMenu[_gameStateMickey.iRoom]) {
+ printLine("THE CABINET IS ALREADY OPEN");
+ } else {
+ _gameStateMickey.iRmMenu[_gameStateMickey.iRoom] = 1;
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_SHIP_KITCHEN_1;
+
+ drawRoom();
+
+ printDatMessage(arg);
+ }
+ break;
+ case IDI_MSA_ACTION_READ_MAP:
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_STAR_MAP;
+
+ drawRoom();
+
+ printDatMessage(46);
+ printDatMessage(47);
+ printDatMessage(48);
+
+ _gameStateMickey.iRmPic[_gameStateMickey.iRoom] = IDI_MSA_PIC_SHIP_BEDROOM;
+
+ drawRoom();
+ break;
+ case IDI_MSA_ACTION_GO_WEST:
+ _gameStateMickey.nButtons = 0;
+
+ memset(_gameStateMickey.szAddr, 0, sizeof(_gameStateMickey.szAddr));
+
+ _gameStateMickey.iRoom = arg;
+
+ return true;
+ break;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+// Keyboard
+
+void MickeyEngine::waitAnyKey(bool anim) {
+ Common::Event event;
+
+ if (!anim)
+ _gfx->updateScreen();
+
+ while (!shouldQuit()) {
+ while (_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_RETURN_TO_LAUNCHER:
+ case Common::EVENT_QUIT:
+ case Common::EVENT_KEYDOWN:
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_RBUTTONUP:
+ return;
+ default:
+ break;
+ }
+ }
+
+ if (anim) {
+ animate();
+ }
+
+ _gfx->updateScreen();
+ _system->delayMillis(10);
+ }
+}
+
+// Console-related functions
+
+void MickeyEngine::debugCurRoom() {
+ getDebugger()->debugPrintf("Current Room = %d\n", _gameStateMickey.iRoom);
+
+ if (_gameStateMickey.iRmObj[_gameStateMickey.iRoom] != IDI_MSA_OBJECT_NONE) {
+ getDebugger()->debugPrintf("Object %d is in the room\n", _gameStateMickey.iRmObj[_gameStateMickey.iRoom]);
+ }
+}
+
+void MickeyEngine::debugGotoRoom(int room) {
+ _gameStateMickey.iRoom = room;
+ drawRoom();
+}
+
+MickeyEngine::MickeyEngine(OSystem *syst, const AGIGameDescription *gameDesc) : PreAgiEngine(syst, gameDesc) {
+ setDebugger(new MickeyConsole(this));
+}
+
+MickeyEngine::~MickeyEngine() {
+ //_console deleted by Engine
+}
+
+void MickeyEngine::init() {
+ uint8 buffer[512];
+
+ // clear game struct
+ memset(&_gameStateMickey, 0, sizeof(_gameStateMickey));
+ memset(&_gameStateMickey.iItem, IDI_MSA_OBJECT_NONE, sizeof(_gameStateMickey.iItem));
+ // read room extended desc flags
+ //readExe(IDO_MSA_ROOM_TEXT, buffer, sizeof(buffer));
+ //memcpy(_gameStateMickey.fRmTxt, buffer, sizeof(_gameStateMickey.fRmTxt));
+
+ // read room extended desc offsets
+ readExe(IDO_MSA_ROOM_TEXT_OFFSETS, buffer, sizeof(buffer));
+ memcpy(_gameStateMickey.oRmTxt, buffer, sizeof(_gameStateMickey.oRmTxt));
+ for (int i = 0; i < IDI_MSA_MAX_ROOM; i++)
+ _gameStateMickey.oRmTxt[i] = buffer[i * 2] + 256 * buffer[i * 2 + 1];
+
+ // read room object indices
+ //readExe(IDO_MSA_ROOM_OBJECT, buffer, sizeof(buffer));
+ //memcpy(_gameStateMickey.iRmObj, buffer, sizeof(_gameStateMickey.iRmObj));
+
+ // read room picture indices
+ //readExe(IDO_MSA_ROOM_PICTURE, buffer, sizeof(buffer));
+ //memcpy(_gameStateMickey.iRmPic, buffer, sizeof(_gameStateMickey.iRmPic));
+
+ // read room menu patch indices
+ readExe(IDO_MSA_ROOM_MENU_FIX, buffer, sizeof(buffer));
+ memcpy(_gameStateMickey.nRmMenu, buffer, sizeof(_gameStateMickey.nRmMenu));
+
+ // set room picture and room object indices
+ for (int i = 0; i < IDI_MSA_MAX_ROOM; i++) {
+ _gameStateMickey.iRmPic[i] = i;
+ _gameStateMickey.iRmObj[i] = -1;
+ }
+ _gameStateMickey.iRmPic[IDI_MSA_PIC_SHIP_AIRLOCK] = IDI_MSA_PIC_SHIP_AIRLOCK_0;
+ _gameStateMickey.iRmObj[IDI_MSA_PIC_EARTH_BATHROOM] = 11;
+ _gameStateMickey.iRmObj[IDI_MSA_PIC_JUPITER_LAVA] = 21;
+ _gameStateMickey.iRmObj[IDI_MSA_PIC_JUPITER_ROCK_0] = 20;
+ _gameStateMickey.iRmObj[IDI_MSA_PIC_JUPITER_ROCK_1] = 19;
+ _gameStateMickey.iRmObj[IDI_MSA_PIC_EARTH_IN_DOGHOUSE] = 1;
+
+#if 0
+ // DEBUG
+ _gameStateMickey.iPlanet = IDI_MSA_PLANET_EARTH;
+ _gameStateMickey.iRoom = IDI_MSA_PIC_SHIP_CONTROLS;
+ _gameStateMickey.fHasXtal = true;
+ _gameStateMickey.nXtals = 9;
+ _gameStateMickey.fItemUsed[IDI_MSA_ITEM_LETTER] = true;
+
+#endif
+
+ setFlag(VM_FLAG_SOUND_ON, true); // enable sound
+}
+
+Common::Error MickeyEngine::go() {
+ init();
+
+ // Game intro
+ intro();
+
+ // Game loop
+ while (!shouldQuit()) {
+ drawRoom();
+
+ if (_gameStateMickey.fIntro) {
+ _gameStateMickey.fIntro = false;
+ } else {
+ printRoomDesc();
+ }
+
+ bool done;
+ if (_gameStateMickey.iRoom == IDI_MSA_PIC_NEPTUNE_GUARD) {
+ _gameStateMickey.iRoom = IDI_MSA_PIC_NEPTUNE_LEADER;
+ done = true;
+ } else {
+ done = false;
+ }
+
+ while (!done && !shouldQuit()) {
+ // Check air supply
+ if (_gameStateMickey.fSuit) {
+ _gameStateMickey.nAir -= 1;
+ for (int i = 0; i < 4; i++) {
+ if (_gameStateMickey.nAir == IDI_MSA_AIR_SUPPLY[i]) {
+ playSound(IDI_MSA_SND_XL30);
+ printExeMsg(IDO_MSA_XL30_SPEAKING);
+ printExeMsg(IDO_MSA_AIR_SUPPLY[i]);
+ if (i == 3)
+ return Common::kNoError;
+ }
+ }
+ } else {
+ _gameStateMickey.nAir = 50; // max air supply
+ }
+
+ done = checkMenu();
+ }
+
+ _gameStateMickey.nFrame = 0;
+ }
+
+ gameOver();
+
+ return Common::kNoError;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/preagi/mickey.h b/engines/agi/preagi/mickey.h
new file mode 100644
index 00000000000..a92fe82e36f
--- /dev/null
+++ b/engines/agi/preagi/mickey.h
@@ -0,0 +1,759 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef AGI_PREAGI_MICKEY_H
+#define AGI_PREAGI_MICKEY_H
+
+namespace Agi {
+
+#define MSA_SAVEGAME_VERSION 2
+
+// strings
+#define IDS_MSA_PATH_DAT "dat/%s"
+#define IDS_MSA_PATH_OBJ "obj/%s.ooo"
+#define IDS_MSA_PATH_PIC "%d.pic"
+#define IDS_MSA_PATH_LOGO "logos.bcg"
+
+#define IDS_MSA_INVENTORY "MICKEY IS CARRYING THE FOLLOWING:"
+#define IDS_MSA_CRYSTALS "%s CRYSTALS"
+
+const char IDS_MSA_CRYSTAL_NO[][3] = {
+ "NO", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9"
+};
+const char IDS_MSA_TEMP_C[][5] = {
+ " 20 ", " 480", "-200", " 430", "-185", "-230", "-130", "-150", "-215"
+};
+const char IDS_MSA_TEMP_F[][5] = {
+ " 68 ", " 897", "-328", " 807", "-301", "-382", "-202", "-238", "-355"
+};
+const char IDS_MSA_PLANETS[][10] = {
+ "EARTH. ", "VENUS. ", "TRITON. ", "MERCURY.", "TITAN. ",
+ "PLUTO. ", "IO. ", "MARS. ", "OBERON. "
+};
+
+const char IDS_MSA_ERRORS[][40] = {
+ "THAT CANNOT BE UNDERSTOOD",
+ "TRY GOING THERE INSTEAD",
+ "THAT CAN'T BE DONE",
+ "MICKEY WOULDN'T WANT TO DO THAT!",
+ "WHICH DIRECTION?",
+ "THAT DOESN'T MAKE SENSE!",
+ "MICKEY WOULDN'T WANT TO DO THAT!"
+};
+
+// patch Mickey.exe offset 0x21E to value 0x01 to enable debug mode
+
+const char IDS_MSA_INSERT_DISK[][40] = {
+ "Please insert disk 1 and press any key", "Please insert disk 2 and press any key"
+};
+
+// max values
+
+#define IDI_MSA_MAX_PLANET 9
+#define IDI_MSA_MAX_DAT 10
+#define IDI_MSA_MAX_PIC_ROOM 224
+#define IDI_MSA_MAX_ROOM 160
+
+#define IDI_MSA_MAX_BUTTON 6
+#define IDI_MSA_MAX_ITEM 11
+
+#define IDI_MSA_ANIM_DELAY 25
+
+#define IDI_MSA_LEN_STORY 1372
+
+// rows
+
+#define IDI_MSA_ROW_MENU_0 20
+#define IDI_MSA_ROW_MENU_1 21
+#define IDI_MSA_ROW_INV_TITLE 2
+#define IDI_MSA_ROW_INV_CRYSTALS 4
+#define IDI_MSA_ROW_INV_ITEMS 5
+#define IDI_MSA_ROW_TEMPERATURE 21
+#define IDI_MSA_ROW_PLANET 22
+#define IDI_MSA_ROW_INSERT_DISK 23
+
+#define IDI_MSA_COL_INV_TITLE 4
+#define IDI_MSA_COL_INV_ITEMS 15
+#define IDI_MSA_COL_PLANET 28
+#define IDI_MSA_COL_INSERT_DISK 1
+
+// screen
+
+#define IDI_MSA_PIC_WIDTH 140
+#define IDI_MSA_PIC_HEIGHT 159
+
+// pictures
+
+#define IDI_MSA_PIC_EARTH_TIRE_SWING 1
+#define IDI_MSA_PIC_EARTH_TIRE_SWING_1 200 // rope taken, swing on ground
+#define IDI_MSA_PIC_EARTH_DOGHOUSE 2
+#define IDI_MSA_PIC_EARTH_IN_DOGHOUSE 154
+#define IDI_MSA_PIC_EARTH_TREE 3
+#define IDI_MSA_PIC_EARTH_GARDEN 4
+#define IDI_MSA_PIC_EARTH_FRONT_HOUSE 5
+#define IDI_MSA_PIC_EARTH_HAMMOCK 6
+#define IDI_MSA_PIC_EARTH_BUTTERFLY 7
+#define IDI_MSA_PIC_EARTH_MAILBOX 8
+#define IDI_MSA_PIC_EARTH_ROAD_0 9
+#define IDI_MSA_PIC_EARTH_ROAD_1 10
+#define IDI_MSA_PIC_EARTH_ROAD_2 11
+#define IDI_MSA_PIC_EARTH_ROAD_3 12
+#define IDI_MSA_PIC_EARTH_ROAD_4 13 // starting room
+#define IDI_MSA_PIC_EARTH_ROAD_5 14
+#define IDI_MSA_PIC_EARTH_ROAD_6 15
+#define IDI_MSA_PIC_EARTH_ROAD_7 18
+#define IDI_MSA_PIC_EARTH_UNDER_TREE 16
+#define IDI_MSA_PIC_EARTH_UP_IN_TREE 155 // CRYSTAL
+#define IDI_MSA_PIC_EARTH_SHIP 17
+#define IDI_MSA_PIC_EARTH_LIVING_ROOM 19
+#define IDI_MSA_PIC_EARTH_KITCHEN 20
+#define IDI_MSA_PIC_EARTH_KITCHEN_1 159 // cupboard open
+#define IDI_MSA_PIC_EARTH_GARAGE 21
+#define IDI_MSA_PIC_EARTH_GARAGE_1 160 // cabinet open
+#define IDI_MSA_PIC_EARTH_BEDROOM 22
+#define IDI_MSA_PIC_EARTH_BEDROOM_1 161 // closet open
+#define IDI_MSA_PIC_EARTH_BATHROOM 23 // WEIGH MICKEY
+#define IDI_MSA_PIC_EARTH_SHIP_LEAVING 24
+#define IDI_MSA_PIC_EARTH_MINNIE 25
+
+#define IDI_MSA_PIC_SHIP_AIRLOCK 25
+#define IDI_MSA_PIC_SHIP_AIRLOCK_0 201 // door closed
+#define IDI_MSA_PIC_SHIP_AIRLOCK_1 202 // door open
+#define IDI_MSA_PIC_SHIP_AIRLOCK_2 203 // door closed, spacesuits on
+#define IDI_MSA_PIC_SHIP_AIRLOCK_3 204 // door open, spacesuits on
+#define IDI_MSA_PIC_SHIP_BEDROOM 29
+#define IDI_MSA_PIC_SHIP_CONTROLS 26
+#define IDI_MSA_PIC_SHIP_CORRIDOR 27
+#define IDI_MSA_PIC_SHIP_KITCHEN 28
+#define IDI_MSA_PIC_SHIP_KITCHEN_1 172 // cabinet open
+
+#define IDI_MSA_PIC_SHIP_VENUS 146
+#define IDI_MSA_PIC_SHIP_NEPTUNE 147
+#define IDI_MSA_PIC_SHIP_MERCURY 148
+#define IDI_MSA_PIC_SHIP_SATURN 149
+#define IDI_MSA_PIC_SHIP_PLUTO 150
+#define IDI_MSA_PIC_SHIP_JUPITER 151
+#define IDI_MSA_PIC_SHIP_MARS 152
+#define IDI_MSA_PIC_SHIP_URANUS 153
+
+#define IDI_MSA_PIC_VENUS_0 30
+#define IDI_MSA_PIC_VENUS_1 31
+#define IDI_MSA_PIC_VENUS_2 32
+#define IDI_MSA_PIC_VENUS_3 34
+#define IDI_MSA_PIC_VENUS_4 36
+#define IDI_MSA_PIC_VENUS_5 38
+#define IDI_MSA_PIC_VENUS_CHASM 35
+#define IDI_MSA_PIC_VENUS_CHASM_1 183 // rope lowered
+#define IDI_MSA_PIC_VENUS_PROBE 39 // CRYSTAL, USE WRENCH
+#define IDI_MSA_PIC_VENUS_PROBE_1 184 // hatch open
+#define IDI_MSA_PIC_VENUS_SHIP 33
+#define IDI_MSA_PIC_VENUS_WEIGH 37 // WEIGH MICKEY
+
+#define IDI_MSA_PIC_NEPTUNE_0 40
+#define IDI_MSA_PIC_NEPTUNE_1 42
+#define IDI_MSA_PIC_NEPTUNE_2 43
+#define IDI_MSA_PIC_NEPTUNE_3 44
+#define IDI_MSA_PIC_NEPTUNE_4 45
+#define IDI_MSA_PIC_NEPTUNE_5 48
+#define IDI_MSA_PIC_NEPTUNE_6 50
+#define IDI_MSA_PIC_NEPTUNE_7 52
+#define IDI_MSA_PIC_NEPTUNE_8 53
+#define IDI_MSA_PIC_NEPTUNE_9 54
+#define IDI_MSA_PIC_NEPTUNE_10 55
+#define IDI_MSA_PIC_NEPTUNE_11 56
+#define IDI_MSA_PIC_NEPTUNE_BABIES 61
+#define IDI_MSA_PIC_NEPTUNE_CASTLE_0 46
+#define IDI_MSA_PIC_NEPTUNE_CASTLE_1 51
+#define IDI_MSA_PIC_NEPTUNE_CASTLE_2 57
+#define IDI_MSA_PIC_NEPTUNE_CASTLE_3 58
+#define IDI_MSA_PIC_NEPTUNE_CASTLE_4 59
+#define IDI_MSA_PIC_NEPTUNE_CASTLE_5 60
+#define IDI_MSA_PIC_NEPTUNE_CASTLE_6 66
+#define IDI_MSA_PIC_NEPTUNE_CASTLE_7 67
+#define IDI_MSA_PIC_NEPTUNE_CASTLE_8 68
+#define IDI_MSA_PIC_NEPTUNE_EATING_AREA 62
+#define IDI_MSA_PIC_NEPTUNE_ENTRANCE 47
+#define IDI_MSA_PIC_NEPTUNE_ENTRANCE_1 185 // entrance open
+#define IDI_MSA_PIC_NEPTUNE_ENTRYWAY 63
+#define IDI_MSA_PIC_NEPTUNE_GUARD 69
+#define IDI_MSA_PIC_NEPTUNE_LEADER 64 // CRYSTAL, GIVE SCARF
+#define IDI_MSA_PIC_NEPTUNE_SHIP 49
+#define IDI_MSA_PIC_NEPTUNE_SLEEP_AREA 65
+#define IDI_MSA_PIC_NEPTUNE_WEIGH 41
+
+#define IDI_MSA_PIC_MERCURY_0 71
+#define IDI_MSA_PIC_MERCURY_1 73
+#define IDI_MSA_PIC_MERCURY_2 75
+#define IDI_MSA_PIC_MERCURY_3 77
+#define IDI_MSA_PIC_MERCURY_4 80
+#define IDI_MSA_PIC_MERCURY_ALIEN_0 72 // CRYSTAL, GIVE SUNGLASSES
+#define IDI_MSA_PIC_MERCURY_ALIEN_1 74
+#define IDI_MSA_PIC_MERCURY_ALIEN_2 81
+#define IDI_MSA_PIC_MERCURY_CAVE_0 70 // hidden feature, press '2' here
+#define IDI_MSA_PIC_MERCURY_CAVE_1 78
+#define IDI_MSA_PIC_MERCURY_CAVE_2 79
+#define IDI_MSA_PIC_MERCURY_SHIP 76
+#define IDI_MSA_PIC_MERCURY_WEIGH 82
+
+#define IDI_MSA_PIC_SATURN_0 84
+#define IDI_MSA_PIC_SATURN_1 86
+#define IDI_MSA_PIC_SATURN_2 90
+#define IDI_MSA_PIC_SATURN_3 91
+#define IDI_MSA_PIC_SATURN_ISLAND 89 // CRYSTAL
+#define IDI_MSA_PIC_SATURN_LAKE_0 85 // USE MATTRESS
+#define IDI_MSA_PIC_SATURN_LAKE_1 88 // USE MATTRESS
+#define IDI_MSA_PIC_SATURN_LAKE_2 92 // USE MATTRESS
+#define IDI_MSA_PIC_SATURN_SHIP 87
+#define IDI_MSA_PIC_SATURN_WEIGH 83 // WEIGH MICKEY
+
+#define IDI_MSA_PIC_PLUTO_0 93
+#define IDI_MSA_PIC_PLUTO_1 96
+#define IDI_MSA_PIC_PLUTO_2 97
+#define IDI_MSA_PIC_PLUTO_3 98
+#define IDI_MSA_PIC_PLUTO_4 101
+#define IDI_MSA_PIC_PLUTO_ALIENS 100 // CRYSTAL, GIVE BONE
+#define IDI_MSA_PIC_PLUTO_CAVE_0 99
+#define IDI_MSA_PIC_PLUTO_CAVE_1 103
+#define IDI_MSA_PIC_PLUTO_CRATER 102
+#define IDI_MSA_PIC_PLUTO_SHIP 95
+#define IDI_MSA_PIC_PLUTO_WEIGH 94 // WEIGH MICKEY
+
+#define IDI_MSA_PIC_JUPITER_0 106
+#define IDI_MSA_PIC_JUPITER_1 107
+#define IDI_MSA_PIC_JUPITER_2 108
+#define IDI_MSA_PIC_JUPITER_3 109
+#define IDI_MSA_PIC_JUPITER_4 113
+#define IDI_MSA_PIC_JUPITER_5 116
+#define IDI_MSA_PIC_JUPITER_6 117
+#define IDI_MSA_PIC_JUPITER_7 120
+#define IDI_MSA_PIC_JUPITER_CRACK 114
+#define IDI_MSA_PIC_JUPITER_LAVA 110 // CRYSTAL, THROW ROCK
+#define IDI_MSA_PIC_JUPITER_ROCK_0 112 // GET ROCK
+#define IDI_MSA_PIC_JUPITER_ROCK_1 119 // GET ROCK
+#define IDI_MSA_PIC_JUPITER_SHIP 115
+#define IDI_MSA_PIC_JUPITER_WEIGH 118 // WEIGH MICKEY
+
+#define IDI_MSA_PIC_MARS_0 121
+#define IDI_MSA_PIC_MARS_1 124
+#define IDI_MSA_PIC_MARS_2 125
+#define IDI_MSA_PIC_MARS_3 126
+#define IDI_MSA_PIC_MARS_4 127
+#define IDI_MSA_PIC_MARS_5 128
+#define IDI_MSA_PIC_MARS_6 130
+#define IDI_MSA_PIC_MARS_SHIP 123
+#define IDI_MSA_PIC_MARS_TUBE_0 129
+#define IDI_MSA_PIC_MARS_TUBE_1 131
+#define IDI_MSA_PIC_MARS_VOLCANO 132 // CRYSTAL, DIG PLUTO
+#define IDI_MSA_PIC_MARS_WEIGH 122 // WEIGH MICKEY
+
+#define IDI_MSA_PIC_URANUS_0 133
+#define IDI_MSA_PIC_URANUS_1 134
+#define IDI_MSA_PIC_URANUS_2 135
+#define IDI_MSA_PIC_URANUS_3 138
+#define IDI_MSA_PIC_URANUS_4 139
+#define IDI_MSA_PIC_URANUS_5 140
+#define IDI_MSA_PIC_URANUS_6 142
+#define IDI_MSA_PIC_URANUS_CHAMBER 145 // CRYSTAL, USE CROWBAR
+#define IDI_MSA_PIC_URANUS_SHIP 137
+#define IDI_MSA_PIC_URANUS_STEPS 144
+#define IDI_MSA_PIC_URANUS_ENTRANCE 141 // ENTER TEMPLE
+#define IDI_MSA_PIC_URANUS_TEMPLE 143 // USE CRYSTAL, ENTER DOOR
+#define IDI_MSA_PIC_URANUS_TEMPLE_1 206 // crystal used
+#define IDI_MSA_PIC_URANUS_TEMPLE_2 207 // door open
+#define IDI_MSA_PIC_URANUS_WEIGH 136 // WEIGH MICKEY
+
+#define IDI_MSA_PIC_STAR_MAP 165
+#define IDI_MSA_PIC_TITLE 240
+
+// objects
+
+enum ENUM_MSA_OBJECT {
+ IDI_MSA_OBJECT_NONE = -1,
+ IDI_MSA_OBJECT_ROCK_0,
+ IDI_MSA_OBJECT_WRENCH,
+ IDI_MSA_OBJECT_SCALE,
+ IDI_MSA_OBJECT_CROWBAR,
+ IDI_MSA_OBJECT_BONE,
+ IDI_MSA_OBJECT_SUNGLASSES,
+ IDI_MSA_OBJECT_DESK_STUFF,
+ IDI_MSA_OBJECT_MATTRESS,
+ IDI_MSA_OBJECT_SCARF,
+ IDI_MSA_OBJECT_FLASHLIGHT,
+ IDI_MSA_OBJECT_ROPE,
+ IDI_MSA_OBJECT_ROCK_1,
+ IDI_MSA_OBJECT_SCARF_C64,
+ IDI_MSA_OBJECT_ROCK_2,
+ IDI_MSA_OBJECT_ROCK_3,
+ IDI_MSA_OBJECT_W_EARTH,
+ IDI_MSA_OBJECT_W_VENUS,
+ IDI_MSA_OBJECT_W_TRITON,
+ IDI_MSA_OBJECT_W_MERCURY,
+ IDI_MSA_OBJECT_W_TITAN,
+ IDI_MSA_OBJECT_W_PLUTO,
+ IDI_MSA_OBJECT_W_IO,
+ IDI_MSA_OBJECT_W_MARS,
+ IDI_MSA_OBJECT_W_OBERON,
+ IDI_MSA_OBJECT_W_SPACE,
+ IDI_MSA_OBJECT_XL31,
+ IDI_MSA_OBJECT_XL31E,
+ IDI_MSA_OBJECT_XL32,
+ IDI_MSA_OBJECT_XL32E,
+ IDI_MSA_OBJECT_XL33,
+ IDI_MSA_OBJECT_XL33E,
+ IDI_MSA_OBJECT_CRYSTAL
+};
+
+const char IDS_MSA_NAME_OBJ[][9] = {
+ "rok1", "wrench", "scale", "cbar", "bone", "glasses", "deskstuf", "raft",
+ "scarf", "flashlit", "rope", "rok1", "scarfc64", "rok2", "rock35", "earthw",
+ "venw", "trw", "merw", "titw", "plw", "iow", "mrw", "obw", "spw", "xl31",
+ "xl31e", "xl32", "xl32e", "xl33", "xl33e", "crys1"
+};
+
+const int IDI_MSA_XTAL_ROOM_XY[IDI_MSA_MAX_PLANET][3] = {
+ // room x y
+ {IDI_MSA_PIC_EARTH_UP_IN_TREE, 14, 76},
+ {IDI_MSA_PIC_VENUS_PROBE, 74, 80},
+ {IDI_MSA_PIC_NEPTUNE_LEADER, 70, 27},
+ {IDI_MSA_PIC_MERCURY_ALIEN_0, 123, 64},
+ {IDI_MSA_PIC_SATURN_ISLAND, 110, 115},
+ {IDI_MSA_PIC_PLUTO_ALIENS, 60, 104},
+ {IDI_MSA_PIC_JUPITER_LAVA, 56, 54},
+ {IDI_MSA_PIC_MARS_VOLCANO, 107, 100},
+ {IDI_MSA_PIC_URANUS_CHAMBER, 90, 4}
+};
+
+// planets
+
+enum ENUM_MSA_PLANET {
+ IDI_MSA_PLANET_EARTH = 0,
+ IDI_MSA_PLANET_VENUS,
+ IDI_MSA_PLANET_NEPTUNE,
+ IDI_MSA_PLANET_MERCURY,
+ IDI_MSA_PLANET_SATURN,
+ IDI_MSA_PLANET_PLUTO,
+ IDI_MSA_PLANET_JUPITER,
+ IDI_MSA_PLANET_MARS,
+ IDI_MSA_PLANET_URANUS,
+ IDI_MSA_PLANET_SPACESHIP
+};
+
+const char IDS_MSA_NAME_DAT[][13] = {
+ "earth.dat", "venus.dat", "neptune.dat", "mercury.dat", "saturn.dat",
+ "pluto.dat", "jupiter.dat", "mars.dat", "uranus.dat", "spacship.dat"
+};
+
+const char IDS_MSA_NAME_PLANET[][10] = {
+ "EARTH", "VENUS", "TRITON", "MERCURY", "TITAN",
+ "PLUTO", "IO", "MARS", "OBERON"
+};
+
+const char IDS_MSA_NAME_PLANET_2[][10] = {
+ "EARTH", "VENUS", "NEPTUNE", "MERCURY", "SATURN",
+ "PLUTO", "JUPITER", "MARS", "URANUS"
+};
+
+const char IDS_MSA_ADDR_PLANET[][7] = {
+ "OB", "B", "OOBBB", "O", "OOBB",
+ "OOOBBB", "OBB", "OOB", "OOOBB"
+};
+
+const int IDI_MSA_HOME_PLANET[] = {
+ IDI_MSA_PIC_EARTH_SHIP, IDI_MSA_PIC_VENUS_SHIP, IDI_MSA_PIC_NEPTUNE_SHIP, IDI_MSA_PIC_MERCURY_SHIP,
+ IDI_MSA_PIC_SATURN_SHIP, IDI_MSA_PIC_PLUTO_SHIP, IDI_MSA_PIC_JUPITER_SHIP, IDI_MSA_PIC_MARS_SHIP,
+ IDI_MSA_PIC_URANUS_SHIP
+};
+
+const int IDI_MSA_SHIP_PLANET[] = {
+ 0, IDI_MSA_PIC_SHIP_VENUS, IDI_MSA_PIC_SHIP_NEPTUNE, IDI_MSA_PIC_SHIP_MERCURY, IDI_MSA_PIC_SHIP_SATURN,
+ IDI_MSA_PIC_SHIP_PLUTO, IDI_MSA_PIC_SHIP_JUPITER, IDI_MSA_PIC_SHIP_MARS, IDI_MSA_PIC_SHIP_URANUS
+};
+
+// items
+
+enum ENUM_MSA_ITEM {
+ IDI_MSA_ITEM_FLASHLIGHT = 0,
+ IDI_MSA_ITEM_ROPE,
+ IDI_MSA_ITEM_BONE,
+ IDI_MSA_ITEM_LETTER,
+ IDI_MSA_ITEM_CROWBAR,
+ IDI_MSA_ITEM_WRENCH,
+ IDI_MSA_ITEM_MATTRESS,
+ IDI_MSA_ITEM_SCARF,
+ IDI_MSA_ITEM_SUNGLASSES,
+ IDI_MSA_ITEM_SCALE,
+ IDI_MSA_ITEM_ROCK
+};
+
+const char IDS_MSA_NAME_ITEM[][15] = {
+ "A FLASHLIGHT", "A ROPE ", "A BONE ", "A LETTER", "A CROWBAR", "A WRENCH",
+ "A MATTRESS", "A SCARF", "SUNGLASSES", "A SCALE ", "A ROCK "
+};
+
+// buttons
+
+#define IDI_MSA_BUTTON_ORANGE 0x4F // 'O'
+#define IDI_MSA_BUTTON_BLUE 0x42 // 'B'
+
+// file structures
+
+struct MSA_TEXT_ENTRY {
+ uint8 x0;
+ uint8 szText[11];
+};
+
+struct MSA_TEXT_BLOCK {
+ uint8 count;
+ MSA_TEXT_ENTRY entry[5];
+};
+
+struct MSA_MSG_BLOCK {
+ uint8 data[5];
+};
+
+struct MSA_MENU {
+ MSA_TEXT_BLOCK row[2];
+ MSA_MSG_BLOCK cmd[5];
+ MSA_MSG_BLOCK arg[5];
+};
+
+struct MSA_DAT_HEADER {
+ uint16 filelen;
+ uint16 ofsRoom[IDI_MSA_MAX_ROOM];
+ uint16 ofsDesc[IDI_MSA_MAX_ROOM];
+ uint16 ofsStr[IDI_MSA_MAX_ROOM];
+};
+
+struct MSA_SND_NOTE {
+ uint16 counter; // freq = 1193180 / counter
+ uint8 length; // msec = length / 0.0182
+};
+
+// file offset modifiers
+
+#define IDI_MSA_OFS_DAT 0x0002
+#define IDI_MSA_OFS_EXE 0x35C0
+
+// actions
+
+#define IDI_MSA_ACTION_GOTO_ROOM 0x00
+#define IDI_MSA_ACTION_SHOW_INT_STR 0x01
+#define IDI_MSA_ACTION_UNUSED 0x02
+#define IDI_MSA_ACTION_SHOW_DAT_STR 0x03
+
+#define IDI_MSA_ACTION_GET_ROPE 0x7F
+#define IDI_MSA_ACTION_UNTIE_ROPE 0x80
+#define IDI_MSA_ACTION_GET_BONE 0x81
+#define IDI_MSA_ACTION_GET_XTAL_EARTH 0x82
+#define IDI_MSA_ACTION_LOOK_DESK 0x83
+#define IDI_MSA_ACTION_WRITE_LETTER 0x84
+#define IDI_MSA_ACTION_MAIL_LETTER 0x85
+#define IDI_MSA_ACTION_OPEN_CUPBOARD 0x86
+#define IDI_MSA_ACTION_GET_FLASHLIGHT 0x87
+#define IDI_MSA_ACTION_OPEN_CABINET 0x88
+#define IDI_MSA_ACTION_GET_CROWBAR 0x89
+#define IDI_MSA_ACTION_GET_WRENCH 0x8A
+#define IDI_MSA_ACTION_OPEN_CLOSET 0x8B
+#define IDI_MSA_ACTION_GET_MATTRESS 0x8C
+#define IDI_MSA_ACTION_GET_SCARF 0x8D
+#define IDI_MSA_ACTION_GET_SUNGLASSES 0x8E
+#define IDI_MSA_ACTION_GET_SCALE 0x8F
+#define IDI_MSA_ACTION_GOTO_SPACESHIP 0x90
+
+#define IDI_MSA_ACTION_DOWN_CHASM 0x91
+#define IDI_MSA_ACTION_DOWN_ROPE 0x92
+#define IDI_MSA_ACTION_USE_ROPE 0x93
+#define IDI_MSA_ACTION_OPEN_HATCH 0x94
+#define IDI_MSA_ACTION_USE_WRENCH 0x95
+#define IDI_MSA_ACTION_GET_XTAL_VENUS 0x96
+
+#define IDI_MSA_ACTION_LOOK_CASTLE 0x97
+#define IDI_MSA_ACTION_ENTER_OPENING 0x98
+#define IDI_MSA_ACTION_USE_CROWBAR 0x99
+#define IDI_MSA_ACTION_GET_XTAL_NEPTUNE 0x9A
+#define IDI_MSA_ACTION_TALK_LEADER 0x9B
+#define IDI_MSA_ACTION_GIVE_SCARF 0x9C
+
+#define IDI_MSA_ACTION_GET_XTAL_MERCURY 0x9D
+#define IDI_MSA_ACTION_GIVE_SUNGLASSES 0x9E
+#define IDI_MSA_ACTION_CROSS_LAKE 0x9F
+#define IDI_MSA_ACTION_USE_MATTRESS 0xA0
+#define IDI_MSA_ACTION_GET_XTAL_SATURN 0xA1
+#define IDI_MSA_ACTION_LEAVE_ISLAND 0xA2
+
+#define IDI_MSA_ACTION_GET_XTAL_PLUTO 0xA3
+#define IDI_MSA_ACTION_GIVE_BONE 0xA4
+
+#define IDI_MSA_ACTION_GET_ROCK_0 0xA5
+#define IDI_MSA_ACTION_GET_ROCK_1 0xA6
+#define IDI_MSA_ACTION_GET_XTAL_JUPITER 0xA7
+#define IDI_MSA_ACTION_THROW_ROCK 0xA8
+
+#define IDI_MSA_ACTION_GO_TUBE 0xA9
+#define IDI_MSA_ACTION_USE_FLASHLIGHT 0xAA
+#define IDI_MSA_ACTION_PLUTO_DIG 0xAB
+#define IDI_MSA_ACTION_GET_XTAL_MARS 0xAC
+
+#define IDI_MSA_ACTION_USE_CRYSTAL 0xAD
+#define IDI_MSA_ACTION_OPEN_DOOR 0xAE
+#define IDI_MSA_ACTION_ENTER_DOOR 0xAF
+#define IDI_MSA_ACTION_GET_XTAL_URANUS 0xB0
+#define IDI_MSA_ACTION_USE_CROWBAR_1 0xB1
+
+#define IDI_MSA_ACTION_GO_NORTH 0xB2
+#define IDI_MSA_ACTION_GO_PLANET 0xB3
+#define IDI_MSA_ACTION_PRESS_BUTTON 0xB4
+#define IDI_MSA_ACTION_WEAR_SPACESUIT 0xB5
+#define IDI_MSA_ACTION_READ_GAUGE 0xB6
+#define IDI_MSA_ACTION_PRESS_ORANGE 0xB7
+#define IDI_MSA_ACTION_PRESS_BLUE 0xB8
+#define IDI_MSA_ACTION_FLIP_SWITCH 0xB9
+#define IDI_MSA_ACTION_PUSH_THROTTLE 0xBA
+#define IDI_MSA_ACTION_PULL_THROTTLE 0xBB
+#define IDI_MSA_ACTION_LEAVE_ROOM 0xBC
+#define IDI_MSA_ACTION_OPEN_CABINET_1 0xBD
+#define IDI_MSA_ACTION_READ_MAP 0xBE
+#define IDI_MSA_ACTION_GO_WEST 0xBF
+
+#define IDI_MSA_ACTION_PLANET_INFO 0xC0
+#define IDI_MSA_ACTION_ENTER_TEMPLE 0xC1
+#define IDI_MSA_ACTION_OPEN_MAILBOX 0xC2
+#define IDI_MSA_ACTION_SAVE_GAME 0xC3
+#define IDI_MSA_ACTION_LOOK_MICKEY 0xC4
+
+// sounds
+
+enum ENUM_MSA_SOUND {
+ IDI_MSA_SND_THEME,
+ IDI_MSA_SND_CRYSTAL,
+ IDI_MSA_SND_TAKE,
+ IDI_MSA_SND_GAME_OVER,
+ IDI_MSA_SND_PRESS_BLUE,
+ IDI_MSA_SND_PRESS_ORANGE,
+ IDI_MSA_SND_SHIP_LAND,
+ IDI_MSA_SND_XL30
+};
+
+// message offsets within mickey.exe
+
+const int IDO_MSA_HIDDEN_MSG[] = {
+ 0x8C44, 0x8C83, 0x8D23, 0x8D97, 0x8E2A
+};
+
+const int IDO_MSA_GAME_OVER[] = {
+ 0x7914, 0x7978, 0x7A17, 0x7A94, 0x7B04, 0x7B8F, 0x7BEB, 0x7C79
+};
+
+const int IDO_MSA_SAVE_GAME[] = {
+ 0x73FA, 0x7436, 0x746C, 0x74E9, 0x75F6, 0x766A, 0x758B
+ // do you have a formatted disk, insert disk, insert disk 2, save by number
+ // everything will be lost, previous game will be lost, game saved
+};
+
+const int IDO_MSA_LOAD_GAME[] = {
+ 0x76CE, 0x770B, 0x7777
+ // do you want to load game, insert game save disk, game restored
+};
+
+const int IDO_MSA_AIR_SUPPLY[] = {
+ 0x7D10, 0x7D31, 0x7D51, 0x7D9B
+ // be aware, low, dangerously low, out of air
+};
+
+const int IDI_MSA_AIR_SUPPLY[] = { 30, 20, 10, 0 };
+
+// planet information
+
+const int IDO_MSA_PLANET_INFO[IDI_MSA_MAX_PLANET][4] = {
+ {0x6313, 0x63B2, 0x6449, 0}, // EARTH
+ {0x61EB, 0x6288, 0, 0}, // VENUS
+ {0x6B64, 0x6C06, 0x6CA3, 0}, // NEPTUNE
+ {0x609B, 0x612C, 0x61CA, 0}, // MERCURY
+ {0x6879, 0x6916, 0x6984, 0}, // SATURN
+ {0x6CCF, 0x6D72, 0x6E10, 0}, // PLUTO
+ {0x667C, 0x6714, 0x67B1, 0x684E}, // JUPITER
+ {0x6471, 0x650F, 0x65AD, 0x6651}, // MARS
+ {0x69C3, 0x6A62, 0x6B00, 0} // URANUS
+};
+
+// next crystal piece hints
+
+const int IDO_MSA_NEXT_PIECE[IDI_MSA_MAX_PLANET][5] = {
+ {0, 0, 0, 0, 0}, // earth
+ {0x4DCC, 0x4E20, 0x4E64, 0x4E9E, 0x4F0B}, // venus
+ {0x5900, 0x599B, 0x5A07, 0x5A8E, 0x5B07}, // neptune
+ {0x4F57, 0x4FA3, 0x4FF1, 0x5056, 0x50BD}, // mercury
+ {0x5471, 0x54DF, 0x5548, 0x55C2, 0x562A}, // saturn
+ {0x5B78, 0x5BB6, 0x5C29, 0x5C76, 0x5CE1}, // pluto
+ {0x526B, 0x52DA, 0x5340, 0x53A1, 0x540C}, // jupiter
+ {0x50F6, 0x512C, 0x5170, 0x51D5, 0x5228}, // mars
+ {0x56AA, 0x571C, 0x579E, 0x5807, 0x5875} // uranus
+};
+
+// message offsets
+
+#define IDO_MSA_COPYRIGHT 0x7801
+#define IDO_MSA_INTRO 0x4679
+#define IDO_MSA_GAME_STORY 0x6E9C
+
+#define IDO_MSA_PRESS_1_TO_9 0x7530
+#define IDO_MSA_PRESS_YES_OR_NO 0x480D
+#define IDO_MSA_TOO_MANY_BUTTONS_PRESSED 0x5DF7
+
+#define IDO_MSA_XL30_SPEAKING 0x4725
+#define IDO_MSA_CRYSTAL_PIECE_FOUND 0x600C
+
+#define IDO_MSA_ROOM_TEXT_OFFSETS 0x8B01
+#define IDO_MSA_ROOM_OBJECT_XY_OFFSETS 0x8EA8
+#define IDO_MSA_ROOM_MENU_FIX 0x4a27
+
+// offsets to offset arrays
+
+#define IDOFS_MSA_MENU_PATCHES 0x5e7a
+#define IDOFS_MSA_SOUND_DATA 0x9deb
+
+// game structure
+
+struct MSA_GAME {
+ uint8 iRoom;
+ uint8 iPlanet;
+ uint8 iDisk;
+
+ uint8 nAir;
+ uint8 nButtons;
+ uint8 nRocks;
+
+ uint8 nXtals;
+ uint8 iPlanetXtal[IDI_MSA_MAX_DAT];
+ uint16 iClue[IDI_MSA_MAX_PLANET];
+ char szAddr[IDI_MSA_MAX_BUTTON + 1];
+
+ // Flags
+ bool fHasXtal;
+ bool fIntro;
+ bool fSuit;
+ bool fShipDoorOpen;
+ bool fFlying;
+ bool fStoryShown;
+ bool fPlanetsInitialized;
+ bool fTempleDoorOpen;
+ bool fAnimXL30;
+ bool fItem[IDI_MSA_MAX_ITEM];
+ bool fItemUsed[IDI_MSA_MAX_ITEM];
+
+ int8 iItem[IDI_MSA_MAX_ITEM];
+ uint8 nItems;
+
+ //uint8 fRmTxt[IDI_MSA_MAX_ROOM];
+ int8 iRmObj[IDI_MSA_MAX_ROOM];
+ uint8 iRmPic[IDI_MSA_MAX_ROOM];
+ uint16 oRmTxt[IDI_MSA_MAX_ROOM];
+
+ uint8 iRmMenu[IDI_MSA_MAX_ROOM];
+ uint8 nRmMenu[IDI_MSA_MAX_ROOM];
+
+ int8 nFrame;
+};
+
+class PreAgiEngine;
+
+class MickeyEngine : public PreAgiEngine {
+public:
+ MickeyEngine(OSystem *syst, const AGIGameDescription *gameDesc);
+ ~MickeyEngine() override;
+
+ void init();
+ Common::Error go() override;
+
+ void debugCurRoom();
+ void debugGotoRoom(int);
+ void drawPic(int);
+ void drawObj(ENUM_MSA_OBJECT, int, int);
+
+protected:
+ MSA_GAME _gameStateMickey;
+ bool _clickToMove;
+
+ int getDat(int);
+ void readExe(int, uint8 *, long);
+ void getDatFileName(int, char *);
+ void readDatHdr(char *, MSA_DAT_HEADER *);
+ void readOfsData(int, int, uint8 *, long);
+ bool chooseY_N(int, bool);
+ int choose1to9(int);
+ void printStr(char *);
+ void printLine(const char *);
+ void printExeStr(int);
+ void printExeMsg(int);
+ void printDesc(int);
+ bool checkMenu();
+ void drawMenu(MSA_MENU &, int, int);
+ void getMouseMenuSelRow(MSA_MENU &, int *, int *, int, int, int);
+ bool getMenuSelRow(MSA_MENU &, int *, int *, int);
+ void getMenuSel(char *, int *, int *);
+ void centerMenu(MSA_MENU *);
+ void patchMenu(MSA_MENU *);
+ void printDatString(int);
+ void printDatMessage(int);
+ void playNote(MSA_SND_NOTE);
+ void playSound(ENUM_MSA_SOUND);
+ void drawRoomAnimation();
+ void drawRoom();
+ void drawLogo();
+ void animate();
+ void printRoomDesc();
+ bool loadGame();
+ void saveGame();
+ void showPlanetInfo();
+ void printStory();
+ int getPlanet();
+ void pressOB(int);
+ void insertDisk(int);
+ void gameOver();
+ void inventory();
+ void intro();
+ void getItem(ENUM_MSA_ITEM);
+ void getXtal(int);
+ bool parse(int, int);
+ void flipSwitch();
+ void waitAnyKey(bool anim = false);
+
+ bool planetIsAlreadyAssigned(int planet) {
+ for (int j = 0; j < IDI_MSA_MAX_PLANET; j++) {
+ if (_gameStateMickey.iPlanetXtal[j] == planet)
+ return true;
+ }
+ return false;
+ }
+
+ bool mickeyHasItem(int item) {
+ if (_gameStateMickey.fItem[item]) {
+ printDatMessage(90); // Mickey already has item
+ return true;
+ } else {
+ return false;
+ }
+ }
+};
+
+} // End of namespace Agi
+
+#endif
diff --git a/engines/agi/preagi/preagi.cpp b/engines/agi/preagi/preagi.cpp
new file mode 100644
index 00000000000..134435a9afa
--- /dev/null
+++ b/engines/agi/preagi/preagi.cpp
@@ -0,0 +1,263 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "audio/mixer.h"
+#include "audio/softsynth/pcspk.h"
+
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/random.h"
+
+#include "agi/preagi/preagi.h"
+#include "agi/graphics.h"
+
+namespace Agi {
+
+PreAgiEngine::PreAgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBase(syst, gameDesc) {
+
+ // Setup mixer
+ syncSoundSettings();
+
+ memset(&_debug, 0, sizeof(struct AgiDebug));
+
+ _speakerHandle = new Audio::SoundHandle();
+}
+
+void PreAgiEngine::initialize() {
+ initRenderMode();
+
+ _font = new GfxFont(this);
+ _gfx = new GfxMgr(this, _font);
+ _picture = new PictureMgr(this, _gfx);
+
+ _font->init();
+
+ _game.gameFlags = 0;
+
+ //_game._vm->_text->charAttrib_Set(15, 0);
+
+ _defaultColor = 0xF;
+
+ _game.name[0] = '\0';
+
+ //_game._vm->_text->configureScreen(0); // hardcoded
+
+ _gfx->initVideo();
+
+ _speakerStream = new Audio::PCSpeaker(_mixer->getOutputRate());
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, _speakerHandle,
+ _speakerStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+
+ debugC(2, kDebugLevelMain, "Detect game");
+
+ // clear all resources and events
+ for (int i = 0; i < MAX_DIRECTORY_ENTRIES; i++) {
+ _game.pictures[i].reset();
+ _game.sounds[i] = nullptr; // _game.sounds contains pointers now
+ _game.dirPic[i].reset();
+ _game.dirSound[i].reset();
+ }
+}
+
+PreAgiEngine::~PreAgiEngine() {
+ _mixer->stopHandle(*_speakerHandle);
+ delete _speakerStream;
+ delete _speakerHandle;
+
+ delete _picture;
+ delete _gfx;
+ delete _font;
+}
+
+int PreAgiEngine::rnd(int hi) {
+ return (_rnd->getRandomNumber(hi - 1) + 1);
+}
+
+// Screen functions
+void PreAgiEngine::clearScreen(int attr, bool overrideDefault) {
+ if (overrideDefault)
+ _defaultColor = attr;
+
+ _gfx->clearDisplay((attr & 0xF0) / 0x10);
+}
+
+void PreAgiEngine::clearGfxScreen(int attr) {
+ _gfx->drawDisplayRect(0, 0, DISPLAY_DEFAULT_WIDTH - 1, IDI_MAX_ROW_PIC * 8 - 1, (attr & 0xF0) / 0x10);
+}
+
+// String functions
+
+void PreAgiEngine::drawStr(int row, int col, int attr, const char *buffer) {
+ int code;
+
+ if (attr == kColorDefault)
+ attr = _defaultColor;
+
+ for (int iChar = 0; iChar < (int)strlen(buffer); iChar++) {
+ code = buffer[iChar];
+
+ switch (code) {
+ case '\n':
+ case 0x8D:
+ if (++row == 200 / 8) return;
+ col = 0;
+ break;
+
+ case '|':
+ // swap attribute nibbles
+ break;
+
+ default:
+ _gfx->drawCharacter(row, col, code, attr & 0x0f, attr >> 4, false);
+
+ if (++col == 320 / 8) {
+ col = 0;
+ if (++row == 200 / 8) return;
+ }
+ }
+ }
+}
+
+void PreAgiEngine::drawStrMiddle(int row, int attr, const char *buffer) {
+ int col = (25 / 2) - (strlen(buffer) / 2); // 25 = 320 / 8 (maximum column)
+ drawStr(row, col, attr, buffer);
+}
+
+void PreAgiEngine::clearTextArea() {
+ int start = IDI_MAX_ROW_PIC;
+
+ if (getGameID() == GID_TROLL)
+ start = 21;
+
+ for (int row = start; row < 200 / 8; row++) {
+ clearRow(row);
+ }
+}
+
+void PreAgiEngine::clearRow(int row) {
+ drawStr(row, 0, IDA_DEFAULT, " "); // 40 spaces
+}
+
+void PreAgiEngine::printStr(const char *szMsg) {
+ clearTextArea();
+ drawStr(21, 0, IDA_DEFAULT, szMsg);
+ g_system->updateScreen();
+}
+
+void PreAgiEngine::XOR80(char *buffer) {
+ for (size_t i = 0; i < strlen(buffer); i++)
+ if (buffer[i] & 0x80)
+ buffer[i] ^= 0x80;
+}
+
+void PreAgiEngine::printStrXOR(char *szMsg) {
+ XOR80(szMsg);
+ printStr(szMsg);
+}
+
+// Input functions
+
+int PreAgiEngine::getSelection(SelectionTypes type) {
+ Common::Event event;
+
+ while (!shouldQuit()) {
+ while (_eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_RETURN_TO_LAUNCHER:
+ case Common::EVENT_QUIT:
+ return 0;
+ case Common::EVENT_RBUTTONUP:
+ return 0;
+ case Common::EVENT_LBUTTONUP:
+ if (type == kSelYesNo || type == kSelAnyKey)
+ return 1;
+ break;
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_y:
+ if (type == kSelYesNo)
+ return 1;
+ break;
+ case Common::KEYCODE_n:
+ if (type == kSelYesNo)
+ return 0;
+ break;
+ case Common::KEYCODE_ESCAPE:
+ if (type == kSelNumber || type == kSelAnyKey)
+ return 0;
+ break;
+ case Common::KEYCODE_1:
+ case Common::KEYCODE_2:
+ case Common::KEYCODE_3:
+ case Common::KEYCODE_4:
+ case Common::KEYCODE_5:
+ case Common::KEYCODE_6:
+ case Common::KEYCODE_7:
+ case Common::KEYCODE_8:
+ case Common::KEYCODE_9:
+ if (type == kSelNumber)
+ return event.kbd.keycode - Common::KEYCODE_1 + 1;
+ break;
+ case Common::KEYCODE_SPACE:
+ if (type == kSelSpace)
+ return 1;
+ break;
+ case Common::KEYCODE_BACKSPACE:
+ if (type == kSelBackspace)
+ return 0;
+ break;
+ default:
+ if (event.kbd.flags & Common::KBD_CTRL)
+ break;
+ if (type == kSelYesNo) {
+ return 2;
+ } else if (type == kSelNumber) {
+ return 10;
+ } else if (type == kSelAnyKey || type == kSelBackspace) {
+ return 1;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ _system->updateScreen();
+ _system->delayMillis(10);
+ }
+ return 0;
+}
+
+void PreAgiEngine::playNote(int16 frequency, int32 length) {
+ _speakerStream->play(Audio::PCSpeaker::kWaveFormSquare, frequency, length);
+ waitForTimer(length);
+}
+
+void PreAgiEngine::waitForTimer(int msec_delay) {
+ uint32 start_time = _system->getMillis();
+
+ while (_system->getMillis() < start_time + msec_delay) {
+ g_system->updateScreen();
+ _system->delayMillis(10);
+ }
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/preagi/preagi.h b/engines/agi/preagi/preagi.h
new file mode 100644
index 00000000000..2175e4b7af1
--- /dev/null
+++ b/engines/agi/preagi/preagi.h
@@ -0,0 +1,119 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef AGI_PREAGI_PREAGI_H
+#define AGI_PREAGI_PREAGI_H
+
+#include "agi/agi.h"
+
+namespace Audio {
+class SoundHandle;
+class PCSpeaker;
+}
+
+namespace Agi {
+
+// default attributes
+#define IDA_DEFAULT 0x0F
+#define IDA_DEFAULT_REV 0xF0
+
+#define IDI_SND_OSCILLATOR_FREQUENCY 1193180
+#define IDI_SND_TIMER_RESOLUTION 0.0182
+
+#define kColorDefault 0x1337
+
+#define IDI_MAX_ROW_PIC 20
+
+enum SelectionTypes {
+ kSelYesNo,
+ kSelNumber,
+ kSelSpace,
+ kSelAnyKey,
+ kSelBackspace
+};
+
+class PreAgiEngine : public AgiBase {
+ int _gameId;
+
+protected:
+ void initialize() override;
+
+ void pollTimer() {}
+ int getKeypress() override { return 0; }
+ bool isKeypress() override { return false; }
+ void clearKeyQueue() override {}
+
+ PreAgiEngine(OSystem *syst, const AGIGameDescription *gameDesc);
+ ~PreAgiEngine() override;
+ int getGameId() {
+ return _gameId;
+ }
+
+ PictureMgr *_picture;
+
+ void clearImageStack() override {}
+ void recordImageStackCall(uint8 type, int16 p1, int16 p2, int16 p3,
+ int16 p4, int16 p5, int16 p6, int16 p7) override {}
+ void replayImageStackCall(uint8 type, int16 p1, int16 p2, int16 p3,
+ int16 p4, int16 p5, int16 p6, int16 p7) override {}
+ void releaseImageStack() override {}
+ int saveGame(const Common::String &fileName, const Common::String &saveName) { return -1; }
+ int loadGame(const Common::String &fileName, bool checkId = true) { return -1; }
+
+ // Game
+ Common::String getTargetName() { return _targetName; }
+
+ // Screen
+ void clearScreen(int attr, bool overrideDefault = true);
+ void clearGfxScreen(int attr);
+ void setDefaultTextColor(int attr) { _defaultColor = attr; }
+
+ // Keyboard
+ int getSelection(SelectionTypes type);
+
+ int rnd(int hi);
+
+ // Text
+ void drawStr(int row, int col, int attr, const char *buffer);
+ void drawStrMiddle(int row, int attr, const char *buffer);
+ void clearTextArea();
+ void clearRow(int row);
+ void XOR80(char *buffer);
+ void printStr(const char *szMsg);
+ void printStrXOR(char *szMsg);
+
+ // Saved Games
+ Common::SaveFileManager *getSaveFileMan() { return _saveFileMan; }
+
+ void playNote(int16 frequency, int32 length);
+ void waitForTimer(int msec_delay);
+
+private:
+ int _defaultColor;
+
+ Audio::PCSpeaker *_speakerStream;
+ Audio::SoundHandle *_speakerHandle;
+};
+
+} // End of namespace Agi
+
+
+#endif
diff --git a/engines/agi/preagi/troll.cpp b/engines/agi/preagi/troll.cpp
new file mode 100644
index 00000000000..6611f5f5f25
--- /dev/null
+++ b/engines/agi/preagi/troll.cpp
@@ -0,0 +1,770 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "agi/preagi/preagi.h"
+#include "agi/preagi/troll.h"
+#include "agi/graphics.h"
+
+#include "common/events.h"
+#include "common/textconsole.h"
+
+#include "graphics/cursorman.h"
+
+namespace Agi {
+
+TrollEngine::TrollEngine(OSystem *syst, const AGIGameDescription *gameDesc) : PreAgiEngine(syst, gameDesc) {
+}
+
+TrollEngine::~TrollEngine() {
+}
+
+// User Interface
+
+void TrollEngine::pressAnyKey(int col) {
+ drawStr(24, col, kColorDefault, IDS_TRO_PRESSANYKEY);
+ g_system->updateScreen();
+ getSelection(kSelAnyKey);
+}
+
+void TrollEngine::drawMenu(const char *szMenu, int iSel) {
+ clearTextArea();
+ drawStr(21, 0, kColorDefault, szMenu);
+ drawStr(22 + iSel, 0, kColorDefault, " *");
+ g_system->updateScreen();
+}
+
+bool TrollEngine::getMenuSel(const char *szMenu, int *iSel, int nSel) {
+ Common::Event event;
+ int y;
+
+ drawMenu(szMenu, *iSel);
+
+ while (!shouldQuit()) {
+ while (_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_RETURN_TO_LAUNCHER:
+ case Common::EVENT_QUIT:
+ return 0;
+ case Common::EVENT_MOUSEMOVE:
+ y = event.mouse.y / 8;
+
+ if (y >= 22)
+ if (nSel > y - 22)
+ *iSel = y - 22;
+
+ drawMenu(szMenu, *iSel);
+ break;
+ case Common::EVENT_LBUTTONUP:
+ return true;
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_t:
+ case Common::KEYCODE_f:
+ inventory();
+
+ return false;
+ case Common::KEYCODE_DOWN:
+ case Common::KEYCODE_SPACE:
+ *iSel += 1;
+
+ if (*iSel == nSel)
+ *iSel = IDI_TRO_SEL_OPTION_1;
+
+ drawMenu(szMenu, *iSel);
+ break;
+ case Common::KEYCODE_UP:
+ *iSel -= 1;
+
+ if (*iSel == IDI_TRO_SEL_OPTION_1 - 1)
+ *iSel = nSel - 1;
+
+ drawMenu(szMenu, *iSel);
+ break;
+ case Common::KEYCODE_RETURN:
+ case Common::KEYCODE_KP_ENTER:
+ return true;
+ case Common::KEYCODE_s:
+ if (event.kbd.hasFlags(Common::KBD_CTRL)) {
+ if (_soundOn) {
+ playTune(2, 1);
+ _soundOn = !_soundOn;
+ } else {
+ _soundOn = !_soundOn;
+ playTune(3, 1);
+ }
+ }
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ _system->updateScreen();
+ _system->delayMillis(10);
+ }
+
+ return true;
+}
+
+// Graphics
+
+void TrollEngine::drawPic(int iPic, bool f3IsCont, bool clr, bool troll) {
+ _picture->setDimensions(IDI_TRO_PIC_WIDTH, IDI_TRO_PIC_HEIGHT);
+
+ if (clr) {
+ clearScreen(0x0f, false);
+ _picture->clear();
+ }
+
+ _picture->setPictureData(_gameData + IDO_TRO_FRAMEPIC);
+ _picture->drawPicture();
+
+ _picture->setPictureData(_gameData + _pictureOffsets[iPic]);
+
+ int addFlag = 0;
+
+ if (troll)
+ addFlag = kPicFTrollMode;
+
+ if (f3IsCont) {
+ _picture->setPictureFlags(kPicFf3Cont | addFlag);
+ } else {
+ _picture->setPictureFlags(kPicFf3Stop | addFlag);
+ }
+
+ _picture->drawPicture();
+
+ _picture->showPic(); // TODO: *HAVE* to add coordinates + height/width!!
+ g_system->updateScreen();
+}
+
+// Game Logic
+
+void TrollEngine::inventory() {
+ clearScreen(0x07);
+ drawStr(1, 12, kColorDefault, IDS_TRO_TREASURE_0);
+ drawStr(2, 12, kColorDefault, IDS_TRO_TREASURE_1);
+
+ for (int i = 0; i < IDI_TRO_MAX_TREASURE - _treasuresLeft; i++) {
+ int n = _inventory[i] - 1;
+ drawStr(2 + i, 10, _items[n].bg << 4 | 0x0f, Common::String::format(" %2d ", i + 1).c_str());
+ drawStr(2 + i, 14, _items[n].bg << 4 | _items[n].fg, _items[n].name);
+ }
+
+ switch (_treasuresLeft) {
+ case 1:
+ drawStr(20, 10, kColorDefault, Common::String::format(IDS_TRO_TREASURE_5, _treasuresLeft).c_str());
+ break;
+ case 0:
+ drawStr(20, 1, kColorDefault, IDS_TRO_TREASURE_6);
+ break;
+ case IDI_TRO_MAX_TREASURE:
+ drawStr(3, 17, kColorDefault, IDS_TRO_TREASURE_2);
+ break;
+ default:
+ drawStr(20, 10, kColorDefault, Common::String::format(IDS_TRO_TREASURE_4, _treasuresLeft).c_str());
+ break;
+ }
+
+ pressAnyKey(6);
+}
+
+void TrollEngine::waitAnyKeyIntro() {
+ Common::Event event;
+ int iMsg = 0;
+
+ while (!shouldQuit()) {
+ while (_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_RETURN_TO_LAUNCHER:
+ case Common::EVENT_QUIT:
+ case Common::EVENT_LBUTTONUP:
+ case Common::EVENT_KEYDOWN:
+ return;
+ default:
+ break;
+ }
+ }
+
+ switch (iMsg) {
+ case 200:
+ iMsg = 0;
+ // fall through
+ case 0:
+ drawStr(22, 3, kColorDefault, IDS_TRO_INTRO_2);
+ g_system->updateScreen();
+ break;
+ case 100:
+ drawStr(22, 3, kColorDefault, IDS_TRO_INTRO_3);
+ g_system->updateScreen();
+ break;
+ default:
+ break;
+ }
+
+ iMsg++;
+
+ _system->updateScreen();
+ _system->delayMillis(10);
+ }
+}
+
+void TrollEngine::credits() {
+ clearScreen(0x07);
+
+ drawStr(1, 2, kColorDefault, IDS_TRO_CREDITS_0);
+
+ int color = 10;
+ char str[2];
+
+ str[1] = 0;
+
+ for (uint i = 0; i < strlen(IDS_TRO_CREDITS_1); i++) {
+ str[0] = IDS_TRO_CREDITS_1[i];
+ drawStr(7, 19 + i, color++, str);
+ if (color > 15)
+ color = 9;
+ }
+
+ drawStr(8, 19, kColorDefault, IDS_TRO_CREDITS_2);
+
+ drawStr(13, 11, 9, IDS_TRO_CREDITS_3);
+ drawStr(15, 8, 10, IDS_TRO_CREDITS_4);
+ drawStr(17, 7, 12, IDS_TRO_CREDITS_5);
+ drawStr(19, 2, 14, IDS_TRO_CREDITS_6);
+
+ g_system->updateScreen();
+
+ pressAnyKey();
+}
+
+void TrollEngine::tutorial() {
+ bool done = false;
+ int iSel = 0;
+ //char szTreasure[16] = {0};
+
+ while (!shouldQuit()) {
+ clearScreen(0xFF);
+
+ printStr(IDS_TRO_TUTORIAL_0);
+ getSelection(kSelSpace);
+
+ clearScreen(0x55);
+ setDefaultTextColor(0x0F);
+
+ done = false;
+ while (!done && !shouldQuit()) {
+ getMenuSel(IDS_TRO_TUTORIAL_1, &iSel, IDI_TRO_MAX_OPTION);
+
+ switch (iSel) {
+ case IDI_TRO_SEL_OPTION_1:
+ clearScreen(0x22, false);
+ g_system->updateScreen();
+ break;
+ case IDI_TRO_SEL_OPTION_2:
+ clearScreen(0x00, false);
+ g_system->updateScreen();
+ break;
+ case IDI_TRO_SEL_OPTION_3:
+ done = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // do you need more practice ?
+ clearScreen(0x4F);
+ drawStr(7, 4, kColorDefault, IDS_TRO_TUTORIAL_5);
+ drawStr(9, 4, kColorDefault, IDS_TRO_TUTORIAL_6);
+ g_system->updateScreen();
+
+ if (!getSelection(kSelYesNo))
+ break;
+ }
+
+ // show info texts
+ clearScreen(0x5F);
+ drawStr(4, 1, kColorDefault, IDS_TRO_TUTORIAL_7);
+ drawStr(5, 1, kColorDefault, IDS_TRO_TUTORIAL_8);
+ g_system->updateScreen();
+ pressAnyKey();
+
+ clearScreen(0x2F);
+ drawStr(6, 1, kColorDefault, IDS_TRO_TUTORIAL_9);
+ g_system->updateScreen();
+ pressAnyKey();
+
+ clearScreen(0x19);
+ drawStr(7, 1, kColorDefault, IDS_TRO_TUTORIAL_10);
+ drawStr(8, 1, kColorDefault, IDS_TRO_TUTORIAL_11);
+ g_system->updateScreen();
+ pressAnyKey();
+
+ clearScreen(0x6E);
+ drawStr(9, 1, kColorDefault, IDS_TRO_TUTORIAL_12);
+ drawStr(10, 1, kColorDefault, IDS_TRO_TUTORIAL_13);
+ g_system->updateScreen();
+ pressAnyKey();
+
+ clearScreen(0x4C);
+ drawStr(11, 1, kColorDefault, IDS_TRO_TUTORIAL_14);
+ drawStr(12, 1, kColorDefault, IDS_TRO_TUTORIAL_15);
+ g_system->updateScreen();
+ pressAnyKey();
+
+ clearScreen(0x5D);
+ drawStr(13, 1, kColorDefault, IDS_TRO_TUTORIAL_16);
+ drawStr(14, 1, kColorDefault, IDS_TRO_TUTORIAL_17);
+ drawStr(15, 1, kColorDefault, IDS_TRO_TUTORIAL_18);
+ g_system->updateScreen();
+ pressAnyKey();
+
+ // show treasures
+ clearScreen(0x2A);
+ drawStr(2, 1, kColorDefault, IDS_TRO_TUTORIAL_19);
+ for (int i = 0; i < IDI_TRO_MAX_TREASURE; i++)
+ drawStr(19 - i, 11, kColorDefault, _items[i].name);
+
+ g_system->updateScreen();
+
+ pressAnyKey();
+}
+
+void TrollEngine::intro() {
+ // sierra on-line presents
+ clearScreen(0x2F);
+ drawStr(9, 10, kColorDefault, IDS_TRO_INTRO_0);
+ drawStr(14, 15, kColorDefault, IDS_TRO_INTRO_1);
+ g_system->updateScreen();
+ _system->delayMillis(3200);
+
+ CursorMan.showMouse(true);
+
+ // Draw logo
+ setDefaultTextColor(0x0f);
+ drawPic(45, false, true);
+ g_system->updateScreen();
+
+ // wait for keypress and alternate message
+ waitAnyKeyIntro();
+
+ // have you played this game before?
+ drawStr(22, 3, kColorDefault, IDS_TRO_INTRO_4);
+ drawStr(23, 6, kColorDefault, IDS_TRO_INTRO_5);
+ g_system->updateScreen();
+
+ if (!getSelection(kSelYesNo))
+ tutorial();
+
+ credits();
+}
+
+void TrollEngine::gameOver() {
+ // We do a check to see if the game should quit. Without this, the game show the picture, plays the
+ // music, and then quits. So if the game is quitting, we shouldn't run the "game over" part.
+ if (shouldQuit())
+ return;
+
+ char szMoves[40];
+
+ clearTextArea();
+ drawPic(42, true, true);
+
+ playTune(4, 25);
+
+ printUserMessage(16);
+
+ printUserMessage(33);
+
+ clearTextArea();
+
+ drawPic(46, true, true);
+
+ sprintf(szMoves, IDS_TRO_GAMEOVER_0, _moves);
+ drawStr(21, 1, kColorDefault, szMoves);
+ drawStr(22, 1, kColorDefault, IDS_TRO_GAMEOVER_1);
+ g_system->updateScreen();
+
+ pressAnyKey();
+}
+
+void TrollEngine::drawTroll() {
+ for (int i = 0; i < IDI_TRO_NUM_NONTROLL; i++)
+ if (_currentRoom == _nonTrollRooms[i]) {
+ _isTrollAway = true;
+ return;
+ }
+
+ drawPic(43, false, false, true);
+}
+
+int TrollEngine::drawRoom(char *menu) {
+ bool contFlag = false;
+
+ if (_currentRoom == 1) {
+ _picture->setDimensions(IDI_TRO_PIC_WIDTH, IDI_TRO_PIC_HEIGHT);
+ clearScreen(0x00, false);
+ _picture->clear();
+ } else {
+
+ if (_currentRoom != 42) {
+ if (_roomPicDeltas[_currentRoom]) {
+ contFlag = true;
+ }
+ }
+
+ drawPic(_currentRoom, contFlag, true);
+ g_system->updateScreen();
+
+ if (_currentRoom == 42) {
+ drawPic(44, false, false); // don't clear
+ } else {
+ if (!_isTrollAway) {
+ drawTroll();
+ }
+ }
+ }
+
+ g_system->updateScreen();
+
+ int n = 0;
+ strncat(menu, (char *)_gameData + _locMessagesIdx[_currentRoom], 39);
+
+ for (int i = 0; i < 3; i++) {
+ if (_roomDescs[_roomPicture - 1].options[i]) {
+ strncat(menu, Common::String::format("\n %d.", i).c_str(), 5);
+
+ strncat(menu, (char *)_gameData + _options[_roomDescs[_roomPicture - 1].options[i] - 1], 35);
+
+ n = i + 1;
+ }
+ }
+
+ return n;
+}
+
+void TrollEngine::playTune(int tune, int len) {
+ if (!_soundOn)
+ return;
+
+ int freq, duration;
+ int ptr = _tunes[tune - 1];
+
+ for (int i = 0; i < len; i++) {
+ freq = READ_LE_UINT16(_gameData + ptr);
+ ptr += 2;
+ duration = READ_LE_UINT16(_gameData + ptr);
+ ptr += 2;
+
+ playNote(freq, duration);
+ }
+}
+
+void TrollEngine::pickupTreasure(int treasureId) {
+ _inventory[IDI_TRO_MAX_TREASURE - _treasuresLeft] = treasureId;
+
+ if (_currentRoom != 24) {
+ clearTextArea();
+ drawPic(_currentRoom, false, true);
+ g_system->updateScreen();
+ }
+
+ printUserMessage(treasureId + 16);
+
+ clearTextArea();
+
+ _treasuresLeft--;
+
+ switch (_treasuresLeft) {
+ case 1:
+ drawStr(22, 1, kColorDefault, IDS_TRO_TREASURE_7);
+ break;
+ case 0:
+ drawStr(22, 1, kColorDefault, IDS_TRO_TREASURE_8);
+ drawStr(23, 4, kColorDefault, IDS_TRO_TREASURE_9);
+
+ _roomStates[6] = 1;
+
+ _locMessagesIdx[6] = IDO_TRO_ALLTREASURES;
+ break;
+ default:
+ drawStr(22, 1, kColorDefault, Common::String::format(IDS_TRO_TREASURE_3, _treasuresLeft).c_str());
+ break;
+ }
+
+ pressAnyKey();
+}
+
+void TrollEngine::printUserMessage(int msgId) {
+ int i;
+
+ clearTextArea();
+
+ for (i = 0; i < _userMessages[msgId - 1].num; i++) {
+ drawStr(21 + i, 1, kColorDefault, _userMessages[msgId - 1].msg[i]);
+ }
+
+ if (msgId == 34) {
+ for (i = 0; i < 2; i++)
+ playTune(5, 11);
+ }
+ pressAnyKey();
+}
+
+void TrollEngine::gameLoop() {
+ bool done = false;
+ char menu[160 + 5];
+ int currentOption, numberOfOptions;
+ int roomParam;
+ int haveFlashlight;
+
+ _moves = 0;
+ _roomPicture = 1;
+ _treasuresLeft = IDI_TRO_MAX_TREASURE;
+ haveFlashlight = false;
+ _currentRoom = 0;
+ _isTrollAway = true;
+ _soundOn = true;
+
+ memset(_roomStates, 0, sizeof(_roomStates));
+
+ memset(_inventory, 0, sizeof(_inventory));
+
+ while (!done && !shouldQuit()) {
+ *menu = 0;
+
+ currentOption = 0;
+
+ numberOfOptions = drawRoom(menu);
+
+ if (getMenuSel(menu, ¤tOption, numberOfOptions)) {
+ _moves++;
+ } else {
+ continue;
+ }
+
+ roomParam = _roomDescs[_roomPicture - 1].roomDescIndex[currentOption];
+
+ switch (_roomDescs[_roomPicture - 1].optionTypes[currentOption]) {
+ case OT_FLASHLIGHT:
+ if (!haveFlashlight) {
+ printUserMessage(13);
+ break;
+ }
+ // fall through
+ case OT_GO:
+ _currentRoom = roomParam;
+ _roomPicture = _roomPicStartIdx[_currentRoom];
+ _roomPicture += _roomStates[_currentRoom];
+
+ if (_currentRoom < 6 || _treasuresLeft == 0) {
+ _isTrollAway = true;
+ } else { // make odd 1:3
+ _isTrollAway = (rnd(3) != 2);
+ }
+ break;
+ case OT_GET:
+ if (!_isTrollAway) {
+ printUserMessage(34);
+ } else {
+ for (int i = 0; i < 4; i++) {
+ playTune(1, 3);
+ // delayMillis()
+ }
+
+ _roomStates[_currentRoom] = 1;
+ _roomPicDeltas[_currentRoom] = 0;
+
+ _roomPicture++;
+
+ if (_roomConnects[roomParam - 1] != 0xff) {
+ _roomStates[_roomConnects[roomParam - 1]] = 1;
+ }
+
+ if (roomParam == 1)
+ haveFlashlight = true;
+
+ _locMessagesIdx[_currentRoom] = IDO_TRO_LOCMESSAGES +
+ (roomParam + 42) * 39;
+
+ pickupTreasure(roomParam);
+ }
+ break;
+ case OT_DO:
+ if (roomParam != 16) {
+ printUserMessage(roomParam);
+ break;
+ }
+
+ done = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+}
+
+void TrollEngine::fillOffsets() {
+ int i;
+
+ for (i = 0; i < IDI_TRO_PICNUM; i++)
+ _pictureOffsets[i] = READ_LE_UINT16(_gameData + IDO_TRO_PIC_START + i * 2);
+
+ for (i = 0; i < IDI_TRO_NUM_OPTIONS; i++)
+ _options[i] = READ_LE_UINT16(_gameData + IDO_TRO_OPTIONS + i * 2);
+
+ for (i = 0; i < IDI_TRO_NUM_NUMROOMS; i++) {
+ _roomPicStartIdx[i] = _gameData[IDO_TRO_PICSTARTIDX + i];
+ _roomPicDeltas[i] = _gameData[IDO_TRO_ROOMPICDELTAS + i];
+ _roomConnects[i] = _gameData[IDO_TRO_ROOMCONNECTS + i];
+ }
+
+ for (i = 0; i < IDI_TRO_NUM_LOCDESCS; i++)
+ _locMessagesIdx[i] = IDO_TRO_LOCMESSAGES + i * 39;
+
+ int start = READ_LE_UINT16(_gameData + IDO_TRO_ROOMDESCS);
+ int ptr;
+ int j;
+
+ for (i = 0; i < IDI_TRO_NUM_ROOMDESCS; i++, start += 2) {
+ ptr = READ_LE_UINT16(_gameData + start);
+
+ for (j = 0; j < 3; j++)
+ _roomDescs[i].options[j] = _gameData[ptr++];
+
+ for (j = 0; j < 3; j++) {
+ switch (_gameData[ptr++]) {
+ case 0:
+ _roomDescs[i].optionTypes[j] = OT_GO;
+ break;
+ case 1:
+ _roomDescs[i].optionTypes[j] = OT_GET;
+ break;
+ case 2:
+ _roomDescs[i].optionTypes[j] = OT_DO;
+ break;
+ case 3:
+ _roomDescs[i].optionTypes[j] = OT_FLASHLIGHT;
+ break;
+ default:
+ error("Bad data @ (%x) %d", ptr - 1, i);
+ }
+ }
+
+ for (j = 0; j < 3; j++)
+ _roomDescs[i].roomDescIndex[j] = _gameData[ptr++];
+ }
+
+ start = IDO_TRO_USERMESSAGES;
+
+ for (i = 0; i < IDI_TRO_NUM_USERMSGS; i++, start += 2) {
+ ptr = READ_LE_UINT16(_gameData + start);
+
+ _userMessages[i].num = _gameData[ptr++];
+
+ for (j = 0; j < _userMessages[i].num; j++, ptr += 39) {
+ memcpy(_userMessages[i].msg[j], _gameData + ptr, 39);
+ _userMessages[i].msg[j][39] = 0;
+ }
+ }
+
+ start = IDO_TRO_ITEMS;
+
+ for (i = 0; i < IDI_TRO_MAX_TREASURE; i++, start += 2) {
+ ptr = READ_LE_UINT16(_gameData + start);
+ _items[i].bg = _gameData[ptr++];
+ _items[i].fg = _gameData[ptr++];
+ memcpy(_items[i].name, _gameData + ptr, 15);
+ _items[i].name[15] = 0;
+ }
+
+ for (i = 0; i < IDO_TRO_NONTROLLROOMS; i++)
+ _nonTrollRooms[i] = _gameData[IDO_TRO_NONTROLLROOMS + i];
+
+ _tunes[0] = 0x3BFD;
+ _tunes[1] = 0x3C09;
+ _tunes[2] = 0x3C0D;
+ _tunes[3] = 0x3C11;
+ _tunes[4] = 0x3C79;
+ _tunes[5] = 0x3CA5;
+}
+
+// Init
+
+void TrollEngine::init() {
+ _picture->setPictureVersion(AGIPIC_V15);
+ //SetScreenPar(320, 200, (char *)ibm_fontdata);
+
+ const int gaps[] = { 0x3A40, 0x4600, 0x4800, 0x5800, 0x5a00, 0x6a00,
+ 0x6c00, 0x7400, 0x7600, 0x7c00, 0x7e00, 0x8e00,
+ 0x9000, 0xa000, 0xa200, 0xb200, 0xb400, 0xc400,
+ 0xc600, 0xd600, 0xd800, 0xe800, 0xea00, 0xfa00,
+ 0xfc00, 0x10c00, 0x10e00, 0x11e00, 0x12000, 0x13000
+ };
+
+ Common::File infile;
+ if (!infile.open(IDA_TRO_BINNAME))
+ return;
+
+ _gameData = (byte *)malloc(0xD9C0);
+
+ bool flip = true;
+ byte *ptr = _gameData;
+ int diff;
+
+ for (int i = 0; i < ARRAYSIZE(gaps) - 1; i++) {
+ diff = gaps[i + 1] - gaps[i];
+
+ if (flip) {
+ infile.seek(gaps[i]);
+ infile.read(ptr, diff);
+ ptr += diff;
+ } else {
+ }
+ flip = !flip;
+ }
+
+ // One sector is off
+ infile.seek(0x18470);
+ infile.read(_gameData + 15632, 592);
+
+ infile.close();
+
+ fillOffsets();
+}
+
+Common::Error TrollEngine::go() {
+ init();
+
+ while (!shouldQuit()) {
+ intro();
+ gameLoop();
+ gameOver();
+ }
+
+ return Common::kNoError;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/preagi/troll.h b/engines/agi/preagi/troll.h
new file mode 100644
index 00000000000..7c4c765c4c8
--- /dev/null
+++ b/engines/agi/preagi/troll.h
@@ -0,0 +1,224 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef AGI_PREAGI_TROLL_H
+#define AGI_PREAGI_TROLL_H
+
+namespace Agi {
+
+// strings
+
+#define IDS_TRO_DISK "ERROR ERROR !"
+#define IDS_TRO_PATH_PIC "%s"
+
+#define IDS_TRO_PRESSANYKEY "PRESS ANY KEY TO CONTINUE:"
+
+#define IDS_TRO_INTRO_0 "SIERRA ON-LINE INC."
+#define IDS_TRO_INTRO_1 "Presents :"
+#define IDS_TRO_INTRO_2 "Copyright 1984 Sierra On-Line Inc."
+#define IDS_TRO_INTRO_3 " Press any key to continue. "
+#define IDS_TRO_INTRO_4 "HAVE YOU PLAYED THIS GAME BEFORE ?"
+#define IDS_TRO_INTRO_5 "PRESS <Y> OR <N>"
+
+#define IDS_TRO_TUTORIAL_0 " First press the <space bar>.\n 1. Turn the screen GREEN.\n 2. Turn the screen BLACK.\n *3. SEE a SURPRISE, and then more."
+#define IDS_TRO_TUTORIAL_1 " Press <return> to make your choice.\n 1. Turn the screen GREEN.\n 2. Turn the screen BLACK.\n 3. SEE a SURPRISE, and then more."
+//#define IDS_TRO_TUTORIAL_0 "First press the <space bar>."
+//#define IDS_TRO_TUTORIAL_1 "1. Turn the screen GREEN."
+//#define IDS_TRO_TUTORIAL_2 "2. Turn the screen BLACK."
+//#define IDS_TRO_TUTORIAL_3 "3. SEE a SURPRISE, and then more."
+//#define IDS_TRO_TUTORIAL_4 "Press <return> to make your choice."
+#define IDS_TRO_TUTORIAL_5 "Would you like more practice ?"
+#define IDS_TRO_TUTORIAL_6 "Press <Y> for yes, <N> for no."
+#define IDS_TRO_TUTORIAL_7 "The evil TROLL has hidden all the"
+#define IDS_TRO_TUTORIAL_8 "Treasures of MARK, the Dwarf King."
+#define IDS_TRO_TUTORIAL_9 "Help KING MARK find his Treasures."
+#define IDS_TRO_TUTORIAL_10 "You can't take a Treasure if the TROLL"
+#define IDS_TRO_TUTORIAL_11 "is in the same picture as the Treasure."
+#define IDS_TRO_TUTORIAL_12 "To make the TROLL go away you have to"
+#define IDS_TRO_TUTORIAL_13 "make the picture change."
+#define IDS_TRO_TUTORIAL_14 "During the game see the Treasures you"
+#define IDS_TRO_TUTORIAL_15 "have already found by pressing <F>."
+#define IDS_TRO_TUTORIAL_16 "During the game you can turn the sound"
+#define IDS_TRO_TUTORIAL_17 "on or off by pressing the <S> key "
+#define IDS_TRO_TUTORIAL_18 "while holding down the <Ctrl> key."
+#define IDS_TRO_TUTORIAL_19 "The TROLL has hidden these Treasures:"
+
+#define IDS_TRO_CREDITS_0 "Prepare to enter the world of . . ."
+#define IDS_TRO_CREDITS_1 "TROLL'S TALE (tm)"
+#define IDS_TRO_CREDITS_2 "------------"
+#define IDS_TRO_CREDITS_3 "Written by MIKE MACCHESNEY"
+#define IDS_TRO_CREDITS_4 "Conversion by PETER OLIPHANT"
+#define IDS_TRO_CREDITS_5 "Graphic Art by DOUG MACNEILL"
+#define IDS_TRO_CREDITS_6 "Original Version by AL LOWE"
+
+#define IDS_TRO_TREASURE_0 "TREASURES FOUND"
+#define IDS_TRO_TREASURE_1 "---------------"
+#define IDS_TRO_TREASURE_2 "NONE"
+#define IDS_TRO_TREASURE_3 "THERE ARE STILL %d TREASURES TO FIND"
+#define IDS_TRO_TREASURE_4 "%d TREASURES TO FIND"
+#define IDS_TRO_TREASURE_5 "%d TREASURE TO FIND"
+#define IDS_TRO_TREASURE_6 "YOU HAVE FOUND ALL OF THE TREASURES!!"
+#define IDS_TRO_TREASURE_7 "THERE'S ONLY ONE MORE TREASURE TO FIND."
+#define IDS_TRO_TREASURE_8 "GREAT!! YOU HAVE FOUND EVERY TREASURE."
+#define IDS_TRO_TREASURE_9 "TAKE THE TREASURES TO THE GUARD."
+
+#define IDS_TRO_GAMEOVER_0 "You took %d moves to complete TROLL'S"
+#define IDS_TRO_GAMEOVER_1 "TALE. Do you think you can do better?"
+
+// picture
+
+#define IDI_TRO_PICNUM 47
+
+#define IDI_TRO_PIC_WIDTH 160
+#define IDI_TRO_PIC_HEIGHT 168
+#define IDI_TRO_PIC_X0 0
+#define IDI_TRO_PIC_Y0 0
+#define IDI_TRO_PIC_FLAGS IDF_AGI_PIC_V15
+
+// max values
+
+#define IDI_TRO_MAX_TREASURE 16
+#define IDI_TRO_MAX_OPTION 3
+
+#define IDI_TRO_SEL_OPTION_1 0
+#define IDI_TRO_SEL_OPTION_2 1
+#define IDI_TRO_SEL_OPTION_3 2
+
+#define IDI_TRO_MAX_ROW_PIC 21
+
+#define IDI_TRO_NUM_ROOMDESCS 65
+#define IDI_TRO_NUM_OPTIONS 129
+#define IDI_TRO_NUM_NUMROOMS 43
+
+#define IDI_TRO_NUM_USERMSGS 34
+
+#define IDI_TRO_NUM_LOCDESCS 59
+
+#define IDI_TRO_NUM_NONTROLL 9
+
+// offsets
+
+#define IDA_TRO_BINNAME "troll.img"
+
+#define IDO_TRO_DATA_START 0x3A40
+#define IDO_TRO_PIC_START 0x3EF5
+#define IDO_TRO_LOCMESSAGES 0x1F7C
+#define IDO_TRO_USERMESSAGES 0x34A4
+#define IDO_TRO_ROOMDESCS 0x0082
+#define IDO_TRO_OPTIONS 0x0364
+#define IDO_TRO_PICSTARTIDX 0x02CD
+#define IDO_TRO_ROOMPICDELTAS 0x030C
+#define IDO_TRO_ALLTREASURES 0x3B24
+#define IDO_TRO_ITEMS 0x34E8
+#define IDO_TRO_FRAMEPIC 0x3EC2
+#define IDO_TRO_ROOMCONNECTS 0x02FA
+#define IDO_TRO_NONTROLLROOMS 0x3CF9
+
+enum OptionType {
+ OT_GO,
+ OT_GET,
+ OT_DO,
+ OT_FLASHLIGHT
+};
+
+struct RoomDesc {
+ int options[3];
+ OptionType optionTypes[3];
+ int roomDescIndex[3];
+};
+
+struct UserMsg {
+ int num;
+ char msg[3][40];
+};
+
+struct Item {
+ byte bg;
+ byte fg;
+ char name[16];
+};
+
+class TrollEngine : public PreAgiEngine {
+public:
+ TrollEngine(OSystem *syst, const AGIGameDescription *gameDesc);
+ ~TrollEngine() override;
+
+ Common::Error go() override;
+
+private:
+ int _roomPicture;
+ int _treasuresLeft;
+ int _currentRoom;
+ int _moves;
+
+ bool _isTrollAway;
+
+ int _inventory[IDI_TRO_MAX_TREASURE];
+
+ bool _soundOn;
+
+ byte *_gameData;
+
+ void init();
+ void intro();
+ void drawPic(int iPic, bool f3IsCont, bool clear, bool troll = false);
+ void drawTroll();
+ void gameLoop();
+ void gameOver();
+ void tutorial();
+ void credits();
+
+ void inventory();
+ void pickupTreasure(int treasureId);
+
+ int drawRoom(char *menu);
+ void printUserMessage(int msgId);
+
+ void pressAnyKey(int col = 4);
+ void waitAnyKeyIntro();
+
+ void playTune(int tune, int len);
+
+ bool getMenuSel(const char *, int *, int);
+
+ void drawMenu(const char *szMenu, int iSel);
+
+ void fillOffsets();
+
+ // These are come from game data
+ int _pictureOffsets[IDI_TRO_PICNUM];
+ int _roomPicStartIdx[IDI_TRO_NUM_NUMROOMS];
+ int _roomPicDeltas[IDI_TRO_NUM_NUMROOMS];
+ int _roomStates[IDI_TRO_NUM_NUMROOMS];
+ UserMsg _userMessages[IDI_TRO_NUM_USERMSGS];
+ int _locMessagesIdx[IDI_TRO_NUM_LOCDESCS];
+ RoomDesc _roomDescs[IDI_TRO_NUM_ROOMDESCS];
+ int _options[IDI_TRO_NUM_OPTIONS];
+ Item _items[IDI_TRO_MAX_TREASURE];
+ int _roomConnects[IDI_TRO_NUM_OPTIONS];
+ int _nonTrollRooms[IDO_TRO_NONTROLLROOMS];
+
+ int _tunes[6];
+};
+
+} // End of namespace Agi
+
+#endif
diff --git a/engines/agi/preagi/winnie.cpp b/engines/agi/preagi/winnie.cpp
new file mode 100644
index 00000000000..9f993df8d1c
--- /dev/null
+++ b/engines/agi/preagi/winnie.cpp
@@ -0,0 +1,1392 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "agi/preagi/preagi.h"
+#include "agi/preagi/winnie.h"
+#include "agi/graphics.h"
+
+#include "graphics/cursorman.h"
+
+#include "common/events.h"
+#include "common/memstream.h"
+#include "common/savefile.h"
+#include "common/textconsole.h"
+
+#include "audio/mididrv.h"
+
+namespace Agi {
+
+void WinnieEngine::parseRoomHeader(WTP_ROOM_HDR *roomHdr, byte *buffer, int len) {
+ int i;
+
+ Common::MemoryReadStreamEndian readS(buffer, len, _isBigEndian);
+
+ roomHdr->roomNumber = readS.readByte();
+ roomHdr->objId = readS.readByte();
+ roomHdr->ofsPic = readS.readUint16();
+ roomHdr->fileLen = readS.readUint16();
+ roomHdr->reserved0 = readS.readUint16();
+
+ for (i = 0; i < IDI_WTP_MAX_DIR; i++)
+ roomHdr->roomNew[i] = readS.readByte();
+
+ roomHdr->objX = readS.readByte();
+ roomHdr->objY = readS.readByte();
+
+ roomHdr->reserved1 = readS.readUint16();
+
+ for (i = 0; i < IDI_WTP_MAX_BLOCK; i++)
+ roomHdr->ofsDesc[i] = readS.readUint16();
+
+ for (i = 0; i < IDI_WTP_MAX_BLOCK; i++)
+ roomHdr->ofsBlock[i] = readS.readUint16();
+
+ for (i = 0; i < IDI_WTP_MAX_STR; i++)
+ roomHdr->ofsStr[i] = readS.readUint16();
+
+ roomHdr->reserved2 = readS.readUint32();
+
+ for (i = 0; i < IDI_WTP_MAX_BLOCK; i++)
+ for (byte j = 0; j < IDI_WTP_MAX_BLOCK; j++)
+ roomHdr->opt[i].ofsOpt[j] = readS.readUint16();
+}
+
+void WinnieEngine::parseObjHeader(WTP_OBJ_HDR *objHdr, byte *buffer, int len) {
+ int i;
+
+ Common::MemoryReadStreamEndian readS(buffer, len, _isBigEndian);
+
+ objHdr->fileLen = readS.readUint16();
+ objHdr->objId = readS.readUint16();
+
+ for (i = 0; i < IDI_WTP_MAX_OBJ_STR_END; i++)
+ objHdr->ofsEndStr[i] = readS.readUint16();
+
+ for (i = 0; i < IDI_WTP_MAX_OBJ_STR; i++)
+ objHdr->ofsStr[i] = readS.readUint16();
+
+ objHdr->ofsPic = readS.readUint16();
+}
+
+uint32 WinnieEngine::readRoom(int iRoom, uint8 *buffer, WTP_ROOM_HDR &roomHdr) {
+ Common::String fileName;
+
+ if (getPlatform() == Common::kPlatformDOS)
+ fileName = Common::String::format(IDS_WTP_ROOM_DOS, iRoom);
+ else if (getPlatform() == Common::kPlatformAmiga)
+ fileName = Common::String::format(IDS_WTP_ROOM_AMIGA, iRoom);
+ else if (getPlatform() == Common::kPlatformC64)
+ fileName = Common::String::format(IDS_WTP_ROOM_C64, iRoom);
+ else if (getPlatform() == Common::kPlatformApple2)
+ fileName = Common::String::format(IDS_WTP_ROOM_APPLE, iRoom);
+
+ Common::File file;
+ if (!file.open(fileName)) {
+ warning("Could not open file \'%s\'", fileName.c_str());
+ return 0;
+ }
+
+ uint32 filelen = file.size();
+ if (getPlatform() == Common::kPlatformC64) { // Skip the loading address
+ filelen -= 2;
+ file.seek(2, SEEK_CUR);
+ }
+
+ memset(buffer, 0, 4096);
+ file.read(buffer, filelen);
+ file.close();
+
+ parseRoomHeader(&roomHdr, buffer, filelen);
+
+ return filelen;
+}
+
+uint32 WinnieEngine::readObj(int iObj, uint8 *buffer) {
+ Common::String fileName;
+
+ if (getPlatform() == Common::kPlatformDOS)
+ fileName = Common::String::format(IDS_WTP_OBJ_DOS, iObj);
+ else if (getPlatform() == Common::kPlatformAmiga)
+ fileName = Common::String::format(IDS_WTP_OBJ_AMIGA, iObj);
+ else if (getPlatform() == Common::kPlatformC64)
+ fileName = Common::String::format(IDS_WTP_OBJ_C64, iObj);
+ else if (getPlatform() == Common::kPlatformApple2)
+ fileName = Common::String::format(IDS_WTP_OBJ_APPLE, iObj);
+
+ Common::File file;
+ if (!file.open(fileName)) {
+ warning("Could not open file \'%s\'", fileName.c_str());
+ return 0;
+ }
+
+ uint32 filelen = file.size();
+ if (getPlatform() == Common::kPlatformC64) { // Skip the loading address
+ filelen -= 2;
+ file.seek(2, SEEK_CUR);
+ }
+
+ memset(buffer, 0, 2048);
+ file.read(buffer, filelen);
+ file.close();
+ return filelen;
+}
+
+void WinnieEngine::randomize() {
+ int iObj = 0;
+ int iRoom = 0;
+ bool done;
+
+ for (int i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++) {
+ done = false;
+
+ while (!done) {
+ iObj = rnd(IDI_WTP_MAX_OBJ - 1);
+ done = true;
+
+ for (int j = 0; j < IDI_WTP_MAX_OBJ_MISSING; j++) {
+ if (_gameStateWinnie.iUsedObj[j] == iObj) {
+ done = false;
+ break;
+ }
+ }
+ }
+
+ _gameStateWinnie.iUsedObj[i] = iObj;
+
+ done = false;
+ while (!done) {
+ iRoom = rnd(IDI_WTP_MAX_ROOM_NORMAL);
+ done = true;
+
+ for (int j = 0; j < IDI_WTP_MAX_ROOM_OBJ; j++) {
+ if (_gameStateWinnie.iObjRoom[j] == iRoom) {
+ done = false;
+ break;
+ }
+ }
+ }
+
+ _gameStateWinnie.iObjRoom[iObj] = iRoom;
+ }
+}
+
+void WinnieEngine::intro() {
+ drawPic(IDS_WTP_FILE_LOGO);
+ printStr(IDS_WTP_INTRO_0);
+ g_system->updateScreen();
+ _system->delayMillis(0x640);
+
+ if (getPlatform() == Common::kPlatformAmiga)
+ _gfx->clearDisplay(0);
+
+ drawPic(IDS_WTP_FILE_TITLE);
+
+ printStr(IDS_WTP_INTRO_1);
+ g_system->updateScreen();
+ _system->delayMillis(0x640);
+
+ if (!playSound(IDI_WTP_SND_POOH_0))
+ return;
+
+ if (!playSound(IDI_WTP_SND_POOH_1))
+ return;
+
+ if (!playSound(IDI_WTP_SND_POOH_2))
+ return;
+}
+
+int WinnieEngine::getObjInRoom(int iRoom) {
+ for (int iObj = 1; iObj < IDI_WTP_MAX_ROOM_OBJ; iObj++)
+ if (_gameStateWinnie.iObjRoom[iObj] == iRoom)
+ return iObj;
+ return 0;
+}
+
+void WinnieEngine::setTakeDrop(int fCanSel[]) {
+ fCanSel[IDI_WTP_SEL_TAKE] = getObjInRoom(_room);
+ fCanSel[IDI_WTP_SEL_DROP] = _gameStateWinnie.iObjHave;
+}
+
+void WinnieEngine::setWinnieFlag(int iFlag) {
+ _gameStateWinnie.fGame[iFlag] = 1;
+}
+
+void WinnieEngine::clearWinnieFlag(int iFlag) {
+ _gameStateWinnie.fGame[iFlag] = 0;
+}
+
+int WinnieEngine::parser(int pc, int index, uint8 *buffer) {
+ WTP_ROOM_HDR hdr;
+ int startpc = pc;
+ int8 opcode;
+ int iNewRoom = 0;
+
+ int iSel, iDir, iBlock;
+ int fCanSel[IDI_WTP_SEL_LAST + 1];
+ char szMenu[121] = {0};
+ bool done;
+ int fBlock;
+
+ // extract header from buffer
+ parseRoomHeader(&hdr, buffer, sizeof(WTP_ROOM_HDR));
+
+ while (!shouldQuit()) {
+ pc = startpc;
+
+ // check if block is to be run
+
+ iBlock = *(buffer + pc++);
+ if (iBlock == 0)
+ return IDI_WTP_PAR_OK;
+
+ fBlock = *(buffer + pc++);
+ if (_gameStateWinnie.fGame[iBlock] != fBlock)
+ return IDI_WTP_PAR_OK;
+
+ // extract text from block
+
+ opcode = *(buffer + pc);
+ switch (opcode) {
+ case 0:
+ case IDO_WTP_OPTION_0:
+ case IDO_WTP_OPTION_1:
+ case IDO_WTP_OPTION_2:
+ // clear fCanSel block
+ memset(fCanSel, 0, sizeof(fCanSel));
+
+ // check if NSEW directions should be displayed
+ if (hdr.roomNew[0]) {
+ fCanSel[IDI_WTP_SEL_NORTH] = fCanSel[IDI_WTP_SEL_SOUTH] =
+ fCanSel[IDI_WTP_SEL_EAST] = fCanSel[IDI_WTP_SEL_WEST] = true;
+ }
+
+ // check if object in room or player carrying one
+ setTakeDrop(fCanSel);
+
+ // check which rows have a menu option
+ for (iSel = 0; iSel < IDI_WTP_MAX_OPTION; iSel++) {
+ opcode = *(buffer + pc++);
+ if (opcode) {
+ fCanSel[opcode - IDO_WTP_OPTION_0] = true;
+ fCanSel[iSel + IDI_WTP_SEL_REAL_OPT_1] = opcode - 0x14;
+ }
+ }
+
+ // extract menu string
+ Common::strlcpy(szMenu, (char *)(buffer + pc), 121);
+ XOR80(szMenu);
+ break;
+ default:
+ // print description
+ printStrWinnie((char *)(buffer + pc));
+ if (getSelection(kSelBackspace) == 1)
+ return IDI_WTP_PAR_OK;
+ else
+ return IDI_WTP_PAR_BACK;
+ }
+
+ // input handler
+
+ done = false;
+ while (!done) {
+ // run wind if it's time
+ if (_doWind)
+ wind();
+
+ // get menu selection
+ getMenuSel(szMenu, &iSel, fCanSel);
+
+ if (++_gameStateWinnie.nMoves == IDI_WTP_MAX_MOVES_UNTIL_WIND)
+ _doWind = true;
+
+ if (_winnieEvent && (_room <= IDI_WTP_MAX_ROOM_TELEPORT)) {
+ if (!_tiggerMist) {
+ _tiggerMist = 1;
+ tigger();
+ } else {
+ _tiggerMist = 0;
+ mist();
+ }
+ _winnieEvent = false;
+ return IDI_WTP_PAR_GOTO;
+ }
+
+ // process selection
+ switch (iSel) {
+ case IDI_WTP_SEL_HOME:
+ switch (_room) {
+ case IDI_WTP_ROOM_HOME:
+ case IDI_WTP_ROOM_MIST:
+ case IDI_WTP_ROOM_TIGGER:
+ break;
+ default:
+ _room = IDI_WTP_ROOM_HOME;
+ return IDI_WTP_PAR_GOTO;
+ }
+ break;
+ case IDI_WTP_SEL_BACK:
+ return IDI_WTP_PAR_BACK;
+ case IDI_WTP_SEL_OPT_1:
+ case IDI_WTP_SEL_OPT_2:
+ case IDI_WTP_SEL_OPT_3:
+ done = true;
+ break;
+ case IDI_WTP_SEL_NORTH:
+ case IDI_WTP_SEL_SOUTH:
+ case IDI_WTP_SEL_EAST:
+ case IDI_WTP_SEL_WEST:
+ iDir = iSel - IDI_WTP_SEL_NORTH;
+
+ if (hdr.roomNew[iDir] == IDI_WTP_ROOM_NONE) {
+ printStr(IDS_WTP_CANT_GO);
+ getSelection(kSelAnyKey);
+ } else {
+ _room = hdr.roomNew[iDir];
+ return IDI_WTP_PAR_GOTO;
+ }
+ break;
+ case IDI_WTP_SEL_TAKE:
+ takeObj(_room);
+ setTakeDrop(fCanSel);
+ break;
+ case IDI_WTP_SEL_DROP:
+ dropObj(_room);
+ setTakeDrop(fCanSel);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // jump to the script block of the selected option
+ pc = hdr.opt[index].ofsOpt[iSel] - _roomOffset;
+
+ opcode = *(buffer + pc);
+ if (!opcode) pc++;
+
+ // process script
+ do {
+ opcode = *(buffer + pc++);
+ switch (opcode) {
+ case IDO_WTP_GOTO_ROOM:
+ opcode = *(buffer + pc++);
+ iNewRoom = opcode;
+ break;
+ case IDO_WTP_PRINT_MSG:
+ opcode = *(buffer + pc++);
+ printRoomStr(_room, opcode);
+ getSelection(kSelAnyKey);
+ break;
+ case IDO_WTP_PRINT_STR:
+ opcode = *(buffer + pc++);
+ printRoomStr(_room, opcode);
+ break;
+ case IDO_WTP_DROP_OBJ:
+ opcode = *(buffer + pc++);
+ opcode = -1;
+ dropObjRnd();
+ break;
+ case IDO_WTP_FLAG_CLEAR:
+ opcode = *(buffer + pc++);
+ clearWinnieFlag(opcode);
+ break;
+ case IDO_WTP_FLAG_SET:
+ opcode = *(buffer + pc++);
+ setWinnieFlag(opcode);
+ break;
+ case IDO_WTP_GAME_OVER:
+ gameOver();
+ break;
+ case IDO_WTP_WALK_MIST:
+ _mist--;
+ if (!_mist) {
+ _room = rnd(IDI_WTP_MAX_ROOM_TELEPORT) + 1;
+ return IDI_WTP_PAR_GOTO;
+ }
+ break;
+ case IDO_WTP_PLAY_SOUND:
+ opcode = *(buffer + pc++);
+ playSound((ENUM_WTP_SOUND)opcode);
+ break;
+ case IDO_WTP_SAVE_GAME:
+ saveGame();
+ _room = IDI_WTP_ROOM_HOME;
+ return IDI_WTP_PAR_GOTO;
+ case IDO_WTP_LOAD_GAME:
+ loadGame();
+ _room = IDI_WTP_ROOM_HOME;
+ return IDI_WTP_PAR_GOTO;
+ case IDO_WTP_OWL_HELP:
+ opcode = *(buffer + pc++);
+ showOwlHelp();
+ break;
+ case IDO_WTP_GOTO_RND:
+ _room = rnd(IDI_WTP_MAX_ROOM_TELEPORT) + 1;
+ return IDI_WTP_PAR_GOTO;
+ default:
+ opcode = 0;
+ break;
+ }
+ } while (opcode && !shouldQuit());
+
+ if (iNewRoom) {
+ _room = iNewRoom;
+ return IDI_WTP_PAR_GOTO;
+ }
+
+ if (iBlock == 1)
+ return IDI_WTP_PAR_OK;
+ g_system->updateScreen();
+ }
+
+ return IDI_WTP_PAR_OK;
+}
+
+void WinnieEngine::keyHelp() {
+ playSound(IDI_WTP_SND_KEYHELP);
+ printStr(IDS_WTP_HELP_0);
+ getSelection(kSelAnyKey);
+ printStr(IDS_WTP_HELP_1);
+ getSelection(kSelAnyKey);
+}
+
+void WinnieEngine::inventory() {
+ if (_gameStateWinnie.iObjHave)
+ printObjStr(_gameStateWinnie.iObjHave, IDI_WTP_OBJ_TAKE);
+ else {
+ clearTextArea();
+ drawStr(IDI_WTP_ROW_MENU, IDI_WTP_COL_MENU, IDA_DEFAULT, IDS_WTP_INVENTORY_0);
+ }
+
+ Common::String missing = Common::String::format(IDS_WTP_INVENTORY_1, _gameStateWinnie.nObjMiss);
+
+ drawStr(IDI_WTP_ROW_OPTION_4, IDI_WTP_COL_MENU, IDA_DEFAULT, missing.c_str());
+ g_system->updateScreen();
+ getSelection(kSelAnyKey);
+}
+
+void WinnieEngine::printObjStr(int iObj, int iStr) {
+ WTP_OBJ_HDR hdr;
+ uint8 *buffer = (uint8 *)malloc(2048);
+
+ readObj(iObj, buffer);
+ parseObjHeader(&hdr, buffer, sizeof(hdr));
+
+ printStrWinnie((char *)(buffer + hdr.ofsStr[iStr] - _objOffset));
+ free(buffer);
+}
+
+bool WinnieEngine::isRightObj(int iRoom, int iObj, int *iCode) {
+ WTP_ROOM_HDR roomhdr;
+ WTP_OBJ_HDR objhdr;
+ uint8 *roomdata = (uint8 *)malloc(4096);
+ uint8 *objdata = (uint8 *)malloc(2048);
+
+ readRoom(iRoom, roomdata, roomhdr);
+ readObj(iObj, objdata);
+ parseObjHeader(&objhdr, objdata, sizeof(WTP_OBJ_HDR));
+
+ free(roomdata);
+ free(objdata);
+
+ *iCode = objhdr.objId;
+
+ if (objhdr.objId == 11) objhdr.objId = 34;
+
+ if (roomhdr.objId == objhdr.objId)
+ return true;
+ else
+ return false;
+}
+
+void WinnieEngine::takeObj(int iRoom) {
+ if (_gameStateWinnie.iObjHave) {
+ // player is already carrying an object, can't take
+ printStr(IDS_WTP_CANT_TAKE);
+ getSelection(kSelAnyKey);
+ } else {
+ // take object
+ int iObj = getObjInRoom(iRoom);
+
+ _gameStateWinnie.iObjHave = iObj;
+ _gameStateWinnie.iObjRoom[iObj] = 0;
+
+ printStr(IDS_WTP_OK);
+ playSound(IDI_WTP_SND_TAKE);
+
+ drawRoomPic();
+
+ // print object "take" string
+ printObjStr(_gameStateWinnie.iObjHave, IDI_WTP_OBJ_TAKE);
+ getSelection(kSelAnyKey);
+
+ // HACK WARNING
+ if (iObj == 18) {
+ _gameStateWinnie.fGame[0x0d] = 1;
+ }
+ }
+}
+
+void WinnieEngine::dropObj(int iRoom) {
+ int iCode;
+
+ if (getObjInRoom(iRoom)) {
+ // there already is an object in the room, can't drop
+ printStr(IDS_WTP_CANT_DROP);
+ getSelection(kSelAnyKey);
+ } else {
+ // HACK WARNING
+ if (_gameStateWinnie.iObjHave == 18) {
+ _gameStateWinnie.fGame[0x0d] = 0;
+ }
+
+ if (isRightObj(iRoom, _gameStateWinnie.iObjHave, &iCode)) {
+ // object has been dropped in the right place
+ printStr(IDS_WTP_OK);
+ getSelection(kSelAnyKey);
+ playSound(IDI_WTP_SND_DROP_OK);
+ printObjStr(_gameStateWinnie.iObjHave, IDI_WTP_OBJ_DROP);
+ getSelection(kSelAnyKey);
+
+ // increase amount of objects returned, decrease amount of objects missing
+ _gameStateWinnie.nObjMiss--;
+ _gameStateWinnie.nObjRet++;
+
+ // xor the dropped object with 0x80 to signify it has been dropped in the right place
+ for (int i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++) {
+ if (_gameStateWinnie.iUsedObj[i] == _gameStateWinnie.iObjHave) {
+ _gameStateWinnie.iUsedObj[i] ^= 0x80;
+ break;
+ }
+ }
+
+ // set flag according to dropped object's id
+ _gameStateWinnie.fGame[iCode] = 1;
+
+ // player is carrying nothing
+ _gameStateWinnie.iObjHave = 0;
+
+ if (!_gameStateWinnie.nObjMiss) {
+ // all objects returned, tell player to find party
+ playSound(IDI_WTP_SND_FANFARE);
+ printStr(IDS_WTP_GAME_OVER_0);
+ getSelection(kSelAnyKey);
+ printStr(IDS_WTP_GAME_OVER_1);
+ getSelection(kSelAnyKey);
+ }
+ } else {
+ // drop object in the given room
+ _gameStateWinnie.iObjRoom[_gameStateWinnie.iObjHave] = iRoom;
+
+ // object has been dropped in the wrong place
+ printStr(IDS_WTP_WRONG_PLACE);
+ getSelection(kSelAnyKey);
+
+ playSound(IDI_WTP_SND_DROP);
+ drawRoomPic();
+
+ printStr(IDS_WTP_WRONG_PLACE);
+ getSelection(kSelAnyKey);
+
+ // print object description
+ printObjStr(_gameStateWinnie.iObjHave, IDI_WTP_OBJ_DESC);
+ getSelection(kSelAnyKey);
+
+ _gameStateWinnie.iObjHave = 0;
+ }
+ }
+}
+
+void WinnieEngine::dropObjRnd() {
+ if (!_gameStateWinnie.iObjHave)
+ return;
+
+ int iRoom = 0;
+ bool done = false;
+
+ while (!done) {
+ iRoom = rnd(IDI_WTP_MAX_ROOM_NORMAL);
+ done = true;
+ if (iRoom == _room)
+ done = false;
+ for (int j = 0; j < IDI_WTP_MAX_ROOM_OBJ; j++) {
+ if (_gameStateWinnie.iObjRoom[j] == iRoom) {
+ done = false;
+ }
+ }
+ }
+
+ _gameStateWinnie.iObjRoom[_gameStateWinnie.iObjHave] = iRoom;
+ _gameStateWinnie.iObjHave = 0;
+}
+
+void WinnieEngine::wind() {
+ int iRoom = 0;
+ bool done;
+
+ _doWind = 0;
+ _gameStateWinnie.nMoves = 0;
+ if (!_gameStateWinnie.nObjMiss)
+ return;
+
+ printStr(IDS_WTP_WIND_0);
+ playSound(IDI_WTP_SND_WIND_0);
+ getSelection(kSelAnyKey);
+
+ printStr(IDS_WTP_WIND_1);
+ playSound(IDI_WTP_SND_WIND_0);
+ getSelection(kSelAnyKey);
+
+ dropObjRnd();
+
+ // randomize positions of objects at large
+ for (int i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++) {
+ if (!(_gameStateWinnie.iUsedObj[i] & IDI_XOR_KEY)) {
+ done = false;
+ while (!done) {
+ iRoom = rnd(IDI_WTP_MAX_ROOM_NORMAL);
+ done = true;
+
+ for (int j = 0; j < IDI_WTP_MAX_ROOM_OBJ; j++) {
+ if (_gameStateWinnie.iObjRoom[j] == iRoom) {
+ done = false;
+ }
+ }
+ }
+ _gameStateWinnie.iObjRoom[_gameStateWinnie.iUsedObj[i]] = iRoom;
+ }
+ }
+}
+
+void WinnieEngine::mist() {
+ // mist length in turns is (2-5)
+ _mist = rnd(4) + 2;
+
+ _room = IDI_WTP_ROOM_MIST;
+ drawRoomPic();
+
+ printStr(IDS_WTP_MIST);
+}
+
+void WinnieEngine::tigger() {
+ _room = IDI_WTP_ROOM_TIGGER;
+
+ drawRoomPic();
+ printStr(IDS_WTP_TIGGER);
+
+ dropObjRnd();
+}
+
+void WinnieEngine::showOwlHelp() {
+ if (_gameStateWinnie.iObjHave) {
+ printStr(IDS_WTP_OWL_0);
+ getSelection(kSelAnyKey);
+ printObjStr(_gameStateWinnie.iObjHave, IDI_WTP_OBJ_HELP);
+ getSelection(kSelAnyKey);
+ }
+ if (getObjInRoom(_room)) {
+ printStr(IDS_WTP_OWL_0);
+ getSelection(kSelAnyKey);
+ printObjStr(getObjInRoom(_room), IDI_WTP_OBJ_HELP);
+ getSelection(kSelAnyKey);
+ }
+}
+
+
+void WinnieEngine::drawMenu(char *szMenu, int iSel, int fCanSel[]) {
+ int iRow = 0, iCol = 0;
+
+ clearTextArea();
+ drawStr(IDI_WTP_ROW_MENU, IDI_WTP_COL_MENU, IDA_DEFAULT, szMenu);
+
+ if (fCanSel[IDI_WTP_SEL_NORTH])
+ drawStr(IDI_WTP_ROW_OPTION_4, IDI_WTP_COL_NSEW, IDA_DEFAULT, IDS_WTP_NSEW);
+ if (fCanSel[IDI_WTP_SEL_TAKE])
+ drawStr(IDI_WTP_ROW_OPTION_4, IDI_WTP_COL_TAKE, IDA_DEFAULT, IDS_WTP_TAKE);
+ if (fCanSel[IDI_WTP_SEL_DROP])
+ drawStr(IDI_WTP_ROW_OPTION_4, IDI_WTP_COL_DROP, IDA_DEFAULT, IDS_WTP_DROP);
+
+ switch (iSel) {
+ case IDI_WTP_SEL_OPT_1:
+ case IDI_WTP_SEL_OPT_2:
+ case IDI_WTP_SEL_OPT_3:
+ iRow = IDI_WTP_ROW_OPTION_1 + iSel;
+ iCol = IDI_WTP_COL_OPTION;
+ break;
+ case IDI_WTP_SEL_NORTH:
+ iRow = IDI_WTP_ROW_OPTION_4;
+ iCol = IDI_WTP_COL_NORTH;
+ break;
+ case IDI_WTP_SEL_SOUTH:
+ iRow = IDI_WTP_ROW_OPTION_4;
+ iCol = IDI_WTP_COL_SOUTH;
+ break;
+ case IDI_WTP_SEL_EAST:
+ iRow = IDI_WTP_ROW_OPTION_4;
+ iCol = IDI_WTP_COL_EAST;
+ break;
+ case IDI_WTP_SEL_WEST:
+ iRow = IDI_WTP_ROW_OPTION_4;
+ iCol = IDI_WTP_COL_WEST;
+ break;
+ case IDI_WTP_SEL_TAKE:
+ iRow = IDI_WTP_ROW_OPTION_4;
+ iCol = IDI_WTP_COL_TAKE;
+ break;
+ case IDI_WTP_SEL_DROP:
+ iRow = IDI_WTP_ROW_OPTION_4;
+ iCol = IDI_WTP_COL_DROP;
+ break;
+ default:
+ break;
+ }
+ drawStr(iRow, iCol - 1, IDA_DEFAULT, ">");
+ g_system->updateScreen();
+}
+
+void WinnieEngine::incMenuSel(int *iSel, int fCanSel[]) {
+ do {
+ *iSel += 1;
+ if (*iSel > IDI_WTP_SEL_DROP) *iSel = IDI_WTP_SEL_OPT_1;
+ } while (!fCanSel[*iSel]);
+}
+
+void WinnieEngine::decMenuSel(int *iSel, int fCanSel[]) {
+ do {
+ *iSel -= 1;
+ if (*iSel < IDI_WTP_SEL_OPT_1) *iSel = IDI_WTP_SEL_DROP;
+ } while (!fCanSel[*iSel]);
+}
+
+void WinnieEngine::getMenuMouseSel(int *iSel, int fCanSel[], int x, int y) {
+ switch (y) {
+ case IDI_WTP_ROW_OPTION_1:
+ case IDI_WTP_ROW_OPTION_2:
+ case IDI_WTP_ROW_OPTION_3:
+ if (fCanSel[y - IDI_WTP_ROW_OPTION_1]) *iSel = y - IDI_WTP_ROW_OPTION_1;
+ break;
+ case IDI_WTP_ROW_OPTION_4:
+ if (fCanSel[IDI_WTP_SEL_NORTH] && (x > IDI_WTP_COL_NORTH - 1) && (x < 6)) *iSel = IDI_WTP_SEL_NORTH;
+ if (fCanSel[IDI_WTP_SEL_SOUTH] && (x > IDI_WTP_COL_SOUTH - 1) && (x < 13)) *iSel = IDI_WTP_SEL_SOUTH;
+ if (fCanSel[IDI_WTP_SEL_EAST] && (x > IDI_WTP_COL_EAST - 1) && (x < 19)) *iSel = IDI_WTP_SEL_EAST;
+ if (fCanSel[IDI_WTP_SEL_WEST] && (x > IDI_WTP_COL_WEST - 1) && (x < 25)) *iSel = IDI_WTP_SEL_WEST;
+ if (fCanSel[IDI_WTP_SEL_TAKE] && (x > IDI_WTP_COL_TAKE - 1) && (x < 33)) *iSel = IDI_WTP_SEL_TAKE;
+ if (fCanSel[IDI_WTP_SEL_DROP] && (x > IDI_WTP_COL_DROP - 1) && (x < 39)) *iSel = IDI_WTP_SEL_DROP;
+ break;
+ default:
+ break;
+ }
+}
+
+void WinnieEngine::makeSel(int *iSel, int fCanSel[]) {
+ if (fCanSel[*iSel])
+ return;
+
+ keyHelp();
+ clrMenuSel(iSel, fCanSel);
+}
+
+void WinnieEngine::getMenuSel(char *szMenu, int *iSel, int fCanSel[]) {
+ Common::Event event;
+ int x, y;
+
+ clrMenuSel(iSel, fCanSel);
+ drawMenu(szMenu, *iSel, fCanSel);
+
+ // Show the mouse cursor for the menu
+ CursorMan.showMouse(true);
+
+ while (!shouldQuit()) {
+ while (_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_RETURN_TO_LAUNCHER:
+ case Common::EVENT_QUIT:
+ return;
+ case Common::EVENT_MOUSEMOVE:
+ x = event.mouse.x / 8;
+ y = event.mouse.y / 8;
+ getMenuMouseSel(iSel, fCanSel, x, y);
+
+ // Change cursor
+ if (fCanSel[IDI_WTP_SEL_NORTH] && hotspotNorth.contains(event.mouse.x, event.mouse.y)) {
+ //_gfx->setCursorPalette(true);
+ // ????
+ } else if (fCanSel[IDI_WTP_SEL_SOUTH] && hotspotSouth.contains(event.mouse.x, event.mouse.y)) {
+ //_gfx->setCursorPalette(true);
+ } else if (fCanSel[IDI_WTP_SEL_WEST] && hotspotWest.contains(event.mouse.x, event.mouse.y)) {
+ //_gfx->setCursorPalette(true);
+ } else if (fCanSel[IDI_WTP_SEL_EAST] && hotspotEast.contains(event.mouse.x, event.mouse.y)) {
+ //_gfx->setCursorPalette(true);
+ } else {
+ //_gfx->setCursorPalette(false);
+ }
+
+ break;
+ case Common::EVENT_LBUTTONUP:
+ // Click to move
+ if (fCanSel[IDI_WTP_SEL_NORTH] && hotspotNorth.contains(event.mouse.x, event.mouse.y)) {
+ *iSel = IDI_WTP_SEL_NORTH;
+ makeSel(iSel, fCanSel);
+ //_gfx->setCursorPalette(false);
+ // TODO???
+ return;
+ } else if (fCanSel[IDI_WTP_SEL_SOUTH] && hotspotSouth.contains(event.mouse.x, event.mouse.y)) {
+ *iSel = IDI_WTP_SEL_SOUTH;
+ makeSel(iSel, fCanSel);
+ //_gfx->setCursorPalette(false);
+ // TODO???
+ return;
+ } else if (fCanSel[IDI_WTP_SEL_WEST] && hotspotWest.contains(event.mouse.x, event.mouse.y)) {
+ *iSel = IDI_WTP_SEL_WEST;
+ makeSel(iSel, fCanSel);
+ //_gfx->setCursorPalette(false);
+ // TODO???
+ return;
+ } else if (fCanSel[IDI_WTP_SEL_EAST] && hotspotEast.contains(event.mouse.x, event.mouse.y)) {
+ *iSel = IDI_WTP_SEL_EAST;
+ makeSel(iSel, fCanSel);
+ //_gfx->setCursorPalette(false);
+ // TODO???
+ return;
+ } else {
+ //_gfx->setCursorPalette(false);
+ // TODO???
+ }
+
+ switch (*iSel) {
+ case IDI_WTP_SEL_OPT_1:
+ case IDI_WTP_SEL_OPT_2:
+ case IDI_WTP_SEL_OPT_3:
+ for (int iSel2 = 0; iSel2 < IDI_WTP_MAX_OPTION; iSel2++) {
+ if (*iSel == (fCanSel[iSel2 + IDI_WTP_SEL_REAL_OPT_1] - 1)) {
+ *iSel = iSel2;
+ // Menu selection made, hide the mouse cursor
+ CursorMan.showMouse(false);
+ return;
+ }
+ }
+ break;
+ default:
+ if (fCanSel[*iSel]) {
+ // Menu selection made, hide the mouse cursor
+ CursorMan.showMouse(false);
+ return;
+ }
+ break;
+ }
+ break;
+ case Common::EVENT_RBUTTONUP:
+ *iSel = IDI_WTP_SEL_BACK;
+ // Menu selection made, hide the mouse cursor
+ CursorMan.showMouse(false);
+ return;
+ case Common::EVENT_WHEELUP:
+ decMenuSel(iSel, fCanSel);
+ break;
+ case Common::EVENT_WHEELDOWN:
+ incMenuSel(iSel, fCanSel);
+ break;
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_ESCAPE:
+ *iSel = IDI_WTP_SEL_HOME;
+ // Menu selection made, hide the mouse cursor
+ CursorMan.showMouse(false);
+ return;
+ case Common::KEYCODE_BACKSPACE:
+ *iSel = IDI_WTP_SEL_BACK;
+ // Menu selection made, hide the mouse cursor
+ CursorMan.showMouse(false);
+ return;
+ case Common::KEYCODE_c:
+ inventory();
+ break;
+ case Common::KEYCODE_SPACE:
+ case Common::KEYCODE_RIGHT:
+ case Common::KEYCODE_DOWN:
+ incMenuSel(iSel, fCanSel);
+ break;
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_UP:
+ decMenuSel(iSel, fCanSel);
+ break;
+ case Common::KEYCODE_1:
+ case Common::KEYCODE_2:
+ case Common::KEYCODE_3:
+ *iSel = event.kbd.keycode - Common::KEYCODE_1;
+ if (fCanSel[*iSel + IDI_WTP_SEL_REAL_OPT_1]) {
+ // Menu selection made, hide the mouse cursor
+ CursorMan.showMouse(false);
+ return;
+ } else {
+ keyHelp();
+ clrMenuSel(iSel, fCanSel);
+ }
+ break;
+ case Common::KEYCODE_n:
+ *iSel = IDI_WTP_SEL_NORTH;
+ makeSel(iSel, fCanSel);
+ break;
+ case Common::KEYCODE_s:
+ if (event.kbd.flags & Common::KBD_CTRL) {
+ flipFlag(VM_FLAG_SOUND_ON);
+ } else {
+ *iSel = IDI_WTP_SEL_SOUTH;
+ makeSel(iSel, fCanSel);
+ }
+ break;
+ case Common::KEYCODE_e:
+ *iSel = IDI_WTP_SEL_EAST;
+ makeSel(iSel, fCanSel);
+ break;
+ case Common::KEYCODE_w:
+ *iSel = IDI_WTP_SEL_WEST;
+ makeSel(iSel, fCanSel);
+ break;
+ case Common::KEYCODE_t:
+ *iSel = IDI_WTP_SEL_TAKE;
+ makeSel(iSel, fCanSel);
+ break;
+ case Common::KEYCODE_d:
+ *iSel = IDI_WTP_SEL_DROP;
+ makeSel(iSel, fCanSel);
+ break;
+ case Common::KEYCODE_RETURN:
+ switch (*iSel) {
+ case IDI_WTP_SEL_OPT_1:
+ case IDI_WTP_SEL_OPT_2:
+ case IDI_WTP_SEL_OPT_3:
+ for (int iSel2 = 0; iSel2 < IDI_WTP_MAX_OPTION; iSel2++) {
+ if (*iSel == (fCanSel[iSel2 + IDI_WTP_SEL_REAL_OPT_1] - 1)) {
+ *iSel = iSel2;
+ // Menu selection made, hide the mouse cursor
+ CursorMan.showMouse(false);
+ return;
+ }
+ }
+ break;
+ default:
+ if (fCanSel[*iSel]) {
+ // Menu selection made, hide the mouse cursor
+ CursorMan.showMouse(false);
+ return;
+ }
+ break;
+ }
+ break;
+ default:
+ if (!event.kbd.flags) { // if the control/alt/shift keys are not pressed
+ keyHelp();
+ clrMenuSel(iSel, fCanSel);
+ }
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ drawMenu(szMenu, *iSel, fCanSel);
+ }
+ }
+}
+
+void WinnieEngine::gameLoop() {
+ WTP_ROOM_HDR hdr;
+ uint8 *roomdata = (uint8 *)malloc(4096);
+ int iBlock;
+ uint8 decodePhase = 0;
+
+ while (!shouldQuit()) {
+ if (decodePhase == 0) {
+ if (!_gameStateWinnie.nObjMiss && (_room == IDI_WTP_ROOM_PICNIC))
+ _room = IDI_WTP_ROOM_PARTY;
+
+ readRoom(_room, roomdata, hdr);
+ drawRoomPic();
+ g_system->updateScreen();
+ decodePhase = 1;
+ }
+
+ if (decodePhase == 1) {
+ if (getObjInRoom(_room)) {
+ printObjStr(getObjInRoom(_room), IDI_WTP_OBJ_DESC);
+ getSelection(kSelAnyKey);
+ }
+ decodePhase = 2;
+ }
+
+ if (decodePhase == 2) {
+ for (iBlock = 0; iBlock < IDI_WTP_MAX_BLOCK; iBlock++) {
+ if (parser(hdr.ofsDesc[iBlock] - _roomOffset, iBlock, roomdata) == IDI_WTP_PAR_BACK) {
+ decodePhase = 1;
+ break;
+ }
+ }
+ if (decodePhase == 2)
+ decodePhase = 3;
+ }
+
+ if (decodePhase == 3) {
+ for (iBlock = 0; iBlock < IDI_WTP_MAX_BLOCK; iBlock++) {
+ if (parser(hdr.ofsBlock[iBlock] - _roomOffset, iBlock, roomdata) == IDI_WTP_PAR_GOTO) {
+ decodePhase = 0;
+ break;
+ } else if (parser(hdr.ofsBlock[iBlock] - _roomOffset, iBlock, roomdata) == IDI_WTP_PAR_BACK) {
+ decodePhase = 2;
+ break;
+ }
+ }
+ }
+ }
+
+ free(roomdata);
+}
+
+void WinnieEngine::drawPic(const char *szName) {
+ Common::String fileName = szName;
+
+ if (getPlatform() != Common::kPlatformAmiga)
+ fileName += ".pic";
+ else
+ fileName = "misc/" + fileName;
+
+ Common::File file;
+
+ if (!file.open(fileName)) {
+ warning("Could not open file \'%s\'", fileName.c_str());
+ return;
+ }
+
+ uint8 *buffer = (uint8 *)malloc(4096);
+ uint32 size = file.size();
+ file.read(buffer, size);
+ file.close();
+
+ _picture->decodePicture(buffer, size, 1, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
+ _picture->showPic(IDI_WTP_PIC_X0, IDI_WTP_PIC_Y0, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
+
+ free(buffer);
+}
+
+void WinnieEngine::drawObjPic(int iObj, int x0, int y0) {
+ if (!iObj)
+ return;
+
+ WTP_OBJ_HDR objhdr;
+ uint8 *buffer = (uint8 *)malloc(2048);
+ uint32 objSize = readObj(iObj, buffer);
+ parseObjHeader(&objhdr, buffer, sizeof(WTP_OBJ_HDR));
+
+ _picture->setOffset(x0, y0);
+ _picture->decodePicture(buffer + objhdr.ofsPic - _objOffset, objSize, 0, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
+ _picture->setOffset(0, 0);
+ _picture->showPic(10, 0, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
+
+ free(buffer);
+}
+
+void WinnieEngine::drawRoomPic() {
+ WTP_ROOM_HDR roomhdr;
+ uint8 *buffer = (uint8 *)malloc(4096);
+ int iObj = getObjInRoom(_room);
+
+ // clear gfx screen
+ _gfx->clearDisplay(0);
+
+ // read room picture
+ readRoom(_room, buffer, roomhdr);
+
+ // draw room picture
+ _picture->decodePicture(buffer + roomhdr.ofsPic - _roomOffset, 4096, 1, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
+ _picture->showPic(IDI_WTP_PIC_X0, IDI_WTP_PIC_Y0, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
+
+ // draw object picture
+ drawObjPic(iObj, IDI_WTP_PIC_X0 + roomhdr.objX, IDI_WTP_PIC_Y0 + roomhdr.objY);
+
+ free(buffer);
+}
+
+bool WinnieEngine::playSound(ENUM_WTP_SOUND iSound) {
+ // TODO: Only DOS sound is supported, currently
+ if (getPlatform() != Common::kPlatformDOS) {
+ warning("STUB: playSound(%d)", iSound);
+ return false;
+ }
+
+ Common::String fileName = Common::String::format(IDS_WTP_SND_DOS, iSound);
+
+ Common::File file;
+ if (!file.open(fileName))
+ return false;
+
+ uint32 size = file.size();
+ byte *data = new byte[size];
+ file.read(data, size);
+ file.close();
+
+ _game.sounds[0] = AgiSound::createFromRawResource(data, size, 0, _soundemu);
+ _sound->startSound(0, 0);
+
+ bool cursorShowing = CursorMan.showMouse(false);
+ _system->updateScreen();
+
+ // Loop until the sound is done
+ bool skippedSound = false;
+ while (!shouldQuit() && _game.sounds[0]->isPlaying()) {
+ Common::Event event;
+ while (_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ _sound->stopSound();
+ skippedSound = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ _system->delayMillis(10);
+ }
+
+ if (cursorShowing) {
+ CursorMan.showMouse(true);
+ _system->updateScreen();
+ }
+
+ delete _game.sounds[0];
+ _game.sounds[0] = nullptr;
+
+ return !shouldQuit() && !skippedSound;
+}
+
+void WinnieEngine::clrMenuSel(int *iSel, int fCanSel[]) {
+ *iSel = IDI_WTP_SEL_OPT_1;
+ while (!fCanSel[*iSel]) {
+ *iSel += 1;
+ }
+ //_gfx->setCursorPalette(false);
+ // TODO???
+}
+
+void WinnieEngine::printRoomStr(int iRoom, int iStr) {
+ WTP_ROOM_HDR hdr;
+ uint8 *buffer = (uint8 *)malloc(4096);
+
+ readRoom(iRoom, buffer, hdr);
+ printStrWinnie((char *)(buffer + hdr.ofsStr[iStr - 1] - _roomOffset));
+
+ free(buffer);
+}
+
+void WinnieEngine::gameOver() {
+ // sing the Pooh song forever
+ while (!shouldQuit()) {
+ printStr(IDS_WTP_SONG_0);
+ playSound(IDI_WTP_SND_POOH_0);
+ printStr(IDS_WTP_SONG_1);
+ playSound(IDI_WTP_SND_POOH_1);
+ printStr(IDS_WTP_SONG_2);
+ playSound(IDI_WTP_SND_POOH_2);
+ getSelection(kSelAnyKey);
+ }
+}
+
+void WinnieEngine::saveGame() {
+ int i = 0;
+
+ Common::OutSaveFile *outfile = getSaveFileMan()->openForSaving(IDS_WTP_FILE_SAVEGAME);
+
+ if (!outfile)
+ return;
+
+ outfile->writeUint32BE(MKTAG('W', 'I', 'N', 'N')); // header
+ outfile->writeByte(WTP_SAVEGAME_VERSION);
+
+ outfile->writeByte(_gameStateWinnie.fSound);
+ outfile->writeByte(_gameStateWinnie.nMoves);
+ outfile->writeByte(_gameStateWinnie.nObjMiss);
+ outfile->writeByte(_gameStateWinnie.nObjRet);
+ outfile->writeByte(_gameStateWinnie.iObjHave);
+
+ for (i = 0; i < IDI_WTP_MAX_FLAG; i++)
+ outfile->writeByte(_gameStateWinnie.fGame[i]);
+
+ for (i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++)
+ outfile->writeByte(_gameStateWinnie.iUsedObj[i]);
+
+ for (i = 0; i < IDI_WTP_MAX_ROOM_OBJ; i++)
+ outfile->writeByte(_gameStateWinnie.iObjRoom[i]);
+
+ outfile->finalize();
+
+ if (outfile->err())
+ warning("Can't write file '%s'. (Disk full?)", IDS_WTP_FILE_SAVEGAME);
+
+ delete outfile;
+}
+
+void WinnieEngine::loadGame() {
+ int saveVersion = 0;
+ int i = 0;
+
+ Common::InSaveFile *infile = getSaveFileMan()->openForLoading(IDS_WTP_FILE_SAVEGAME);
+
+ if (!infile)
+ return;
+
+ if (infile->readUint32BE() == MKTAG('W', 'I', 'N', 'N')) {
+ saveVersion = infile->readByte();
+ if (saveVersion != WTP_SAVEGAME_VERSION)
+ warning("Old save game version (%d, current version is %d). Will try and read anyway, but don't be surprised if bad things happen", saveVersion, WTP_SAVEGAME_VERSION);
+
+ _gameStateWinnie.fSound = infile->readByte();
+ _gameStateWinnie.nMoves = infile->readByte();
+ _gameStateWinnie.nObjMiss = infile->readByte();
+ _gameStateWinnie.nObjRet = infile->readByte();
+ _gameStateWinnie.iObjHave = infile->readByte();
+ } else {
+ // This is probably a save from the original interpreter, throw a warning and attempt
+ // to read it as LE
+ warning("No header found in save game, assuming it came from the original interpreter");
+ // Note that the original saves variables as 16-bit integers, but only 8 bits are used.
+ // Since we read the save file data as little-endian, we skip the first byte of each
+ // variable
+
+ infile->seek(0); // Jump back to the beginning of the file
+
+ infile->readUint16LE(); // skip unused field
+ infile->readByte(); // first 8 bits of fSound
+ _gameStateWinnie.fSound = infile->readByte();
+ infile->readByte(); // first 8 bits of nMoves
+ _gameStateWinnie.nMoves = infile->readByte();
+ infile->readByte(); // first 8 bits of nObjMiss
+ _gameStateWinnie.nObjMiss = infile->readByte();
+ infile->readByte(); // first 8 bits of nObjRet
+ _gameStateWinnie.nObjRet = infile->readByte();
+ infile->readUint16LE(); // skip unused field
+ infile->readUint16LE(); // skip unused field
+ infile->readUint16LE(); // skip unused field
+ infile->readByte(); // first 8 bits of iObjHave
+ _gameStateWinnie.iObjHave = infile->readByte();
+ infile->readUint16LE(); // skip unused field
+ infile->readUint16LE(); // skip unused field
+ infile->readUint16LE(); // skip unused field
+ }
+
+ for (i = 0; i < IDI_WTP_MAX_FLAG; i++)
+ _gameStateWinnie.fGame[i] = infile->readByte();
+
+ for (i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++)
+ _gameStateWinnie.iUsedObj[i] = infile->readByte();
+
+ for (i = 0; i < IDI_WTP_MAX_ROOM_OBJ; i++)
+ _gameStateWinnie.iObjRoom[i] = infile->readByte();
+
+ // Note that saved games from the original interpreter have 2 more 16-bit fields here
+ // which are ignored
+
+ delete infile;
+}
+
+void WinnieEngine::printStrWinnie(char *szMsg) {
+ if (getPlatform() != Common::kPlatformAmiga)
+ printStrXOR(szMsg);
+ else
+ printStr(szMsg);
+}
+
+// Console-related functions
+
+void WinnieEngine::debugCurRoom() {
+ getDebugger()->debugPrintf("Current Room = %d\n", _room);
+}
+
+WinnieEngine::WinnieEngine(OSystem *syst, const AGIGameDescription *gameDesc) : PreAgiEngine(syst, gameDesc) {
+ setDebugger(new WinnieConsole(this));
+}
+
+WinnieEngine::~WinnieEngine() {
+}
+
+void WinnieEngine::init() {
+ // Initialize sound
+
+ switch (MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK | MDT_PCJR))) {
+ case MT_PCSPK:
+ _soundemu = SOUND_EMU_PC;
+ break;
+ case MT_PCJR:
+ _soundemu = SOUND_EMU_PCJR;
+ break;
+ default:
+ _soundemu = SOUND_EMU_NONE;
+ break;
+ }
+
+ _sound = new SoundMgr(this, _mixer);
+ setFlag(VM_FLAG_SOUND_ON, true); // enable sound
+
+ memset(&_gameStateWinnie, 0, sizeof(_gameStateWinnie));
+ _gameStateWinnie.fSound = 1;
+ _gameStateWinnie.nObjMiss = IDI_WTP_MAX_OBJ_MISSING;
+ _gameStateWinnie.nObjRet = 0;
+ _gameStateWinnie.fGame[0] = 1;
+ _gameStateWinnie.fGame[1] = 1;
+ _room = IDI_WTP_ROOM_HOME;
+
+ _mist = -1;
+ _doWind = false;
+ _winnieEvent = false;
+
+ if (getPlatform() != Common::kPlatformAmiga) {
+ _isBigEndian = false;
+ _roomOffset = IDI_WTP_OFS_ROOM;
+ _objOffset = IDI_WTP_OFS_OBJ;
+ } else {
+ _isBigEndian = true;
+ _roomOffset = 0;
+ _objOffset = 0;
+ }
+
+ if (getPlatform() == Common::kPlatformC64 || getPlatform() == Common::kPlatformApple2)
+ _picture->setPictureVersion(AGIPIC_C64);
+
+ hotspotNorth = Common::Rect(20, 0, (IDI_WTP_PIC_WIDTH + 10) * 2, 10);
+ hotspotSouth = Common::Rect(20, IDI_WTP_PIC_HEIGHT - 10, (IDI_WTP_PIC_WIDTH + 10) * 2, IDI_WTP_PIC_HEIGHT);
+ hotspotEast = Common::Rect(IDI_WTP_PIC_WIDTH * 2, 0, (IDI_WTP_PIC_WIDTH + 10) * 2, IDI_WTP_PIC_HEIGHT);
+ hotspotWest = Common::Rect(20, 0, 30, IDI_WTP_PIC_HEIGHT);
+}
+
+Common::Error WinnieEngine::go() {
+ init();
+ randomize();
+
+ // The intro is not supported on these platforms yet
+ if (getPlatform() != Common::kPlatformC64 && getPlatform() != Common::kPlatformApple2)
+ intro();
+
+ gameLoop();
+
+ return Common::kNoError;
+}
+
+} // End of namespace AGI
diff --git a/engines/agi/preagi/winnie.h b/engines/agi/preagi/winnie.h
new file mode 100644
index 00000000000..399f640b82b
--- /dev/null
+++ b/engines/agi/preagi/winnie.h
@@ -0,0 +1,355 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef AGI_PREAGI_WINNIE_H
+#define AGI_PREAGI_WINNIE_H
+
+namespace Agi {
+
+#define WTP_SAVEGAME_VERSION 1
+#define IDI_XOR_KEY 0x80
+
+// strings
+#define IDS_WTP_ROOM_DOS "rooms/rm.%02d"
+#define IDS_WTP_ROOM_AMIGA "rooms/room.%d"
+#define IDS_WTP_ROOM_C64 "room%02d"
+#define IDS_WTP_ROOM_APPLE "room%d.obj"
+#define IDS_WTP_OBJ_DOS "obj.%02d"
+#define IDS_WTP_OBJ_AMIGA "objects/object.%d"
+#define IDS_WTP_OBJ_C64 "object%02d"
+#define IDS_WTP_OBJ_APPLE "object%d.obj"
+#define IDS_WTP_SND_DOS "snd.%02d"
+#define IDS_WTP_SND_AMIGA "Sounds"
+#define IDS_WTP_SND_C64 "sound.obj"
+#define IDS_WTP_SND_APPLE "sound.obj"
+
+#define IDS_WTP_FILE_LOGO "logo"
+#define IDS_WTP_FILE_TITLE "title"
+#define IDS_WTP_FILE_SAVEGAME "savegame"
+#define IDS_WTP_FILE_RND "rnd"
+
+#define IDS_WTP_DISK_ERROR "There is a problem with your disk drive.Please make sure your Winnie-the-Pooh disk is in the drive correctly."
+
+#define IDS_WTP_INTRO_0 " PRESENT"
+#define IDS_WTP_INTRO_1 " TM designates trademark of\n Sierra On-Line, Inc.\n (c) 1985 Walt Disney Productions"
+
+#define IDS_WTP_HELP_0 "The <SPACE BAR> moves the pointer. Press <RETURN> when it is by the choice you want. Press the <Backspace> key to see what you just finished reading."
+#define IDS_WTP_HELP_1 "Press <C> to see what you are carrying. <Ctrl-S> turns the sound off and on. <ESC> takes you to the playroom (in caseyou get lost or want to save the game)."
+
+#define IDS_WTP_GAME_OVER_0 "Congratulations!! You did it! You returned everything that was lost. Now,Christopher Robin invites you to a Hero party."
+#define IDS_WTP_GAME_OVER_1 "The good news is: YOU are the Hero!! The bad news is: you have to find the party by yourself. Good luck!"
+
+#define IDS_WTP_OWL_0 "\"For example, that object you are carrying now is interesting. I know I've seen it before. Hmmm. Let me think about this . . .\""
+#define IDS_WTP_OWL_1 "\"You know, this object here beside me isfamiliar. I'm sure I could give you some sort of clue about it. Let me see. . .\""
+
+#define IDS_WTP_WIND_0 "Oh, no! The Blustery Wind begins to howl. It has returned, and mixed up all the objects in the Wood."
+#define IDS_WTP_WIND_1 "But don't worry. Everyone still has theobjects you returned to them.\n\n (Today must be Winds-day!)"
+#define IDS_WTP_TIGGER "\"Hallooooo, there!!!! It's ME, Tigger! Let's BOUNCE!\""
+#define IDS_WTP_MIST "Oh, look out! The mysterious mist is coming in. It gets so thick that you can't see through it. Just keep walkingand it will soon clear up."
+
+#define IDS_WTP_SONG_0 "Winnie-the-Pooh, Winnie-the-Pooh, Tubby little cubby all stuffed with fluff, He's Winnie-the-Pooh, Winnie-the-Pooh, Willy, nilly, silly, old bear."
+#define IDS_WTP_SONG_1 "Deep in the Hundred Acre Wood, Where Christopher Robin plays, You will find the enchanted neighborhoodof Christopher's childhood days."
+#define IDS_WTP_SONG_2 "A donkey named Eeyore is his friend, and Kanga and little Roo. There's Rabbit and Piglet and there's Owl But most of all Winnie-the-Pooh!"
+
+#define IDS_WTP_NSEW "North South East West"
+#define IDS_WTP_TAKE "Take"
+#define IDS_WTP_DROP "Drop"
+#define IDS_WTP_CANT_GO "\nSorry, but you can't go that way."
+#define IDS_WTP_CANT_TAKE "You can't take it. You can only carry one object at a time."
+#define IDS_WTP_CANT_DROP "You can't drop it. Another object is already here."
+#define IDS_WTP_WRONG_PLACE "\nOk, but it doesn't belong here."
+#define IDS_WTP_OK "\nOk."
+
+#define IDS_WTP_INVENTORY_0 "You are carrying nothing."
+#define IDS_WTP_INVENTORY_1 "Number of objects still missing: %d"
+
+// COMMODORE 64 version strings
+
+#define IDS_WTP_FILE_SAVEGAME_C64 "saved game"
+#define IDS_WTP_DISK_ERROR_C64 "There is a problem with your disk drive.Please make sure your disk is in the drive correctly."
+#define IDS_WTP_HELP_0_C64 "The <SPACE BAR> moves the pointer. Press <RETURN> when it is by the choice you want. <F1> brings back what you have already read."
+#define IDS_WTP_HELP_1_C64 "<F3> takes you back to the playroom (if you get lost, or want to save the game).<F5> turns the sound off and on. <F7> shows what you're carrying."
+#define IDS_WTP_WRONG_PLACE_C64 "\nOk, but this is not the right place."
+
+// maximum values
+
+#define IDI_WTP_MAX_OBJ_MISSING 10
+
+#define IDI_WTP_MAX_ROOM 62
+#define IDI_WTP_MAX_OBJ 40
+#define IDI_WTP_MAX_SND 14
+#define IDI_WTP_MAX_PIC 2
+
+#define IDI_WTP_MAX_ROOM_NORMAL 57
+#define IDI_WTP_MAX_ROOM_TELEPORT 30
+#define IDI_WTP_MAX_ROOM_OBJ 42
+#define IDI_WTP_MAX_BLOCK 4
+#define IDI_WTP_MAX_STR 6
+#define IDI_WTP_MAX_OBJ_STR 4
+#define IDI_WTP_MAX_OBJ_STR_END 2
+#define IDI_WTP_MAX_FLAG 40
+#define IDI_WTP_MAX_OPTION 3
+#define IDI_WTP_MAX_DIR 4
+#define IDI_WTP_MAX_MOVES_UNTIL_WIND 150
+
+// positions
+
+#define IDI_WTP_ROW_MENU 21
+#define IDI_WTP_ROW_OPTION_1 21
+#define IDI_WTP_ROW_OPTION_2 22
+#define IDI_WTP_ROW_OPTION_3 23
+#define IDI_WTP_ROW_OPTION_4 24
+
+#define IDI_WTP_COL_MENU 0
+#define IDI_WTP_COL_OPTION 1
+#define IDI_WTP_COL_NSEW 1
+#define IDI_WTP_COL_NORTH 1
+#define IDI_WTP_COL_SOUTH 8
+#define IDI_WTP_COL_EAST 15
+#define IDI_WTP_COL_WEST 21
+#define IDI_WTP_COL_TAKE 29
+#define IDI_WTP_COL_DROP 35
+#define IDI_WTP_COL_PRESENT 17
+
+// data file offset modifiers
+
+#define IDI_WTP_OFS_ROOM 0x5400
+#define IDI_WTP_OFS_OBJ 0x0800
+
+// picture
+
+#define IDI_WTP_PIC_WIDTH 140
+#define IDI_WTP_PIC_HEIGHT 159
+#define IDI_WTP_PIC_X0 10
+#define IDI_WTP_PIC_Y0 0
+#define IDI_WTP_PIC_FLAGS IDF_AGI_PIC_V2
+
+// selections
+
+enum {
+ IDI_WTP_SEL_HOME = -2,
+ IDI_WTP_SEL_BACK,
+ IDI_WTP_SEL_OPT_1,
+ IDI_WTP_SEL_OPT_2,
+ IDI_WTP_SEL_OPT_3,
+ IDI_WTP_SEL_NORTH,
+ IDI_WTP_SEL_SOUTH,
+ IDI_WTP_SEL_EAST,
+ IDI_WTP_SEL_WEST,
+ IDI_WTP_SEL_TAKE,
+ IDI_WTP_SEL_DROP,
+ IDI_WTP_SEL_REAL_OPT_1,
+ IDI_WTP_SEL_REAL_OPT_2,
+ IDI_WTP_SEL_REAL_OPT_3
+};
+
+#define IDI_WTP_SEL_LAST IDI_WTP_SEL_REAL_OPT_3
+
+// rooms
+
+enum {
+ IDI_WTP_ROOM_NONE = -1,
+ IDI_WTP_ROOM_NORTH,
+ IDI_WTP_ROOM_SOUTH,
+ IDI_WTP_ROOM_EAST,
+ IDI_WTP_ROOM_WEST
+};
+
+#define IDI_WTP_ROOM_HIDE 0
+
+#define IDI_WTP_ROOM_PICNIC 2
+#define IDI_WTP_ROOM_HOME 28
+#define IDI_WTP_ROOM_PARTY 58
+#define IDI_WTP_ROOM_MIST 59
+#define IDI_WTP_ROOM_TIGGER 61
+
+// sound
+
+enum ENUM_WTP_SOUND {
+ IDI_WTP_SND_POOH_0 = 1,
+ IDI_WTP_SND_TIGGER,
+ IDI_WTP_SND_TAKE,
+ IDI_WTP_SND_DROP,
+ IDI_WTP_SND_DROP_OK,
+ IDI_WTP_SND_FANFARE,
+ IDI_WTP_SND_POOH_1,
+ IDI_WTP_SND_KEYHELP,
+ IDI_WTP_SND_POOH_2,
+ IDI_WTP_SND_WIND_0,
+ IDI_WTP_SND_WIND_1
+};
+
+// script opcodes
+
+#define IDO_WTP_GOTO_ROOM 0x06
+#define IDO_WTP_PRINT_MSG 0x08
+#define IDO_WTP_PRINT_STR 0x0A
+#define IDO_WTP_DROP_OBJ 0x0C
+#define IDO_WTP_FLAG_CLEAR 0x0E
+#define IDO_WTP_FLAG_SET 0x10
+#define IDO_WTP_GAME_OVER 0x12
+#define IDO_WTP_WALK_MIST 0x14
+#define IDO_WTP_PLAY_SOUND 0x16
+#define IDO_WTP_SAVE_GAME 0x18
+#define IDO_WTP_LOAD_GAME 0x1A
+#define IDO_WTP_OWL_HELP 0x1C
+#define IDO_WTP_GOTO_RND 0x1E
+
+#define IDO_WTP_OPTION_0 0x15
+#define IDO_WTP_OPTION_1 0x16
+#define IDO_WTP_OPTION_2 0x17
+
+enum {
+ IDI_WTP_OBJ_DESC = 0,
+ IDI_WTP_OBJ_TAKE,
+ IDI_WTP_OBJ_DROP,
+ IDI_WTP_OBJ_HELP
+};
+
+enum {
+ IDI_WTP_PAR_OK = 0,
+ IDI_WTP_PAR_GOTO,
+ IDI_WTP_PAR_BACK
+};
+
+// room file option block
+
+struct WTP_ROOM_BLOCK {
+ uint16 ofsOpt[IDI_WTP_MAX_BLOCK];
+};
+
+// room file header
+
+struct WTP_ROOM_HDR {
+ uint8 roomNumber;
+ uint8 objId;
+ uint16 ofsPic;
+ uint16 fileLen;
+ uint16 reserved0;
+ int8 roomNew[IDI_WTP_MAX_DIR];
+ uint8 objX;
+ uint8 objY;
+ uint16 reserved1;
+ uint16 ofsDesc[IDI_WTP_MAX_BLOCK];
+ uint16 ofsBlock[IDI_WTP_MAX_BLOCK];
+ uint16 ofsStr[IDI_WTP_MAX_STR];
+ uint32 reserved2;
+ WTP_ROOM_BLOCK opt[IDI_WTP_MAX_BLOCK];
+};
+
+// object file header
+
+struct WTP_OBJ_HDR {
+ uint16 fileLen;
+ uint16 objId;
+ uint16 ofsEndStr[IDI_WTP_MAX_OBJ_STR_END];
+ uint16 ofsStr[IDI_WTP_MAX_OBJ_STR];
+ uint16 ofsPic;
+};
+
+// savegame
+
+struct WTP_SAVE_GAME {
+ uint8 fSound;
+ uint8 nMoves;
+ uint8 nObjMiss;
+ uint8 nObjRet;
+ uint8 iObjHave;
+ uint8 fGame[IDI_WTP_MAX_FLAG];
+ uint8 iUsedObj[IDI_WTP_MAX_OBJ_MISSING];
+ uint8 iObjRoom[IDI_WTP_MAX_ROOM_OBJ];
+};
+
+class PreAgiEngine;
+
+class WinnieEngine : public PreAgiEngine {
+public:
+ WinnieEngine(OSystem *syst, const AGIGameDescription *gameDesc);
+ ~WinnieEngine() override;
+
+ void init();
+ Common::Error go() override;
+
+ void debugCurRoom();
+
+private:
+ WTP_SAVE_GAME _gameStateWinnie;
+ int _room;
+ int _mist;
+ bool _doWind;
+ bool _winnieEvent;
+ int _tiggerMist;
+
+ int _roomOffset;
+ int _objOffset;
+ bool _isBigEndian;
+ Common::Rect hotspotNorth, hotspotSouth, hotspotEast, hotspotWest;
+
+ void randomize();
+ void intro();
+ void drawPic(const char *);
+ void gameLoop();
+
+ void parseRoomHeader(WTP_ROOM_HDR *roomHdr, byte *buffer, int len);
+ void parseObjHeader(WTP_OBJ_HDR *objHdr, byte *buffer, int len);
+ uint32 readRoom(int, uint8 *, WTP_ROOM_HDR &);
+
+ void drawRoomPic();
+ int parser(int, int, uint8 *);
+ int getObjInRoom(int);
+ bool getSelOkBack();
+ void getMenuSel(char *, int *, int[]);
+ void keyHelp();
+ void clrMenuSel(int *, int[]);
+ void incMenuSel(int *, int[]);
+ void decMenuSel(int *, int[]);
+ void drawMenu(char *, int, int[]);
+ void printRoomStr(int, int);
+ void inventory();
+ void printObjStr(int, int);
+ uint32 readObj(int, uint8 *);
+ void takeObj(int);
+ void dropObj(int);
+ bool isRightObj(int, int, int *);
+ void drawObjPic(int, int, int);
+ void getMenuMouseSel(int *, int[], int, int);
+ void setWinnieFlag(int);
+ void clearWinnieFlag(int);
+ void gameOver();
+ void saveGame();
+ void loadGame();
+ void dropObjRnd();
+ void setTakeDrop(int[]);
+ void makeSel(int *, int[]);
+
+ void wind();
+ void mist();
+ void tigger();
+
+ void showOwlHelp();
+ bool playSound(ENUM_WTP_SOUND);
+
+ void printStrWinnie(char *szMsg);
+};
+
+} // End of namespace Agi
+
+#endif
More information about the Scummvm-git-logs
mailing list