Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

preagi_winnie.cpp

/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-1-0-0rc1/engines/agi/preagi_winnie.cpp $
 * $Id: preagi_winnie.cpp 41239 2009-06-06 17:39:13Z sev $
 *
 */

#include "agi/preagi.h"
#include "agi/preagi_winnie.h"
#include "agi/graphics.h"

#include "graphics/cursorman.h"

#include "common/events.h"
#include "common/savefile.h"
#include "common/stream.h"

namespace Agi {

void Winnie::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 Winnie::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 Winnie::readRoom(int iRoom, uint8 *buffer, WTP_ROOM_HDR &roomHdr) {
      char szFile[256] = {0};

      if (_vm->getPlatform() == Common::kPlatformPC)
            sprintf(szFile, IDS_WTP_ROOM_DOS, iRoom);
      else if (_vm->getPlatform() == Common::kPlatformAmiga)
            sprintf(szFile, IDS_WTP_ROOM_AMIGA, iRoom);
      else if (_vm->getPlatform() == Common::kPlatformC64)
            sprintf(szFile, IDS_WTP_ROOM_C64, iRoom);
      else if (_vm->getPlatform() == Common::kPlatformApple2GS)
            sprintf(szFile, IDS_WTP_ROOM_APPLE, iRoom);
      Common::File file;
      if (!file.open(szFile)) {
            warning ("Could not open file \'%s\'", szFile);
            return 0;
      }
      uint32 filelen = file.size();
      if (_vm->getPlatform() == Common::kPlatformC64) { //Skip the loading address
            filelen -= 2;
            file.seek(2, SEEK_CUR);
      }
      memset(buffer, 0, sizeof(buffer));
      file.read(buffer, filelen);
      file.close();

      parseRoomHeader(&roomHdr, buffer, filelen);

      return filelen;
}

uint32 Winnie::readObj(int iObj, uint8 *buffer) {
      char szFile[256] = {0};
      if (_vm->getPlatform() == Common::kPlatformPC)
            sprintf(szFile, IDS_WTP_OBJ_DOS, iObj);
      else if (_vm->getPlatform() == Common::kPlatformAmiga)
            sprintf(szFile, IDS_WTP_OBJ_AMIGA, iObj);
      else if (_vm->getPlatform() == Common::kPlatformC64)
            sprintf(szFile, IDS_WTP_OBJ_C64, iObj);
      else if (_vm->getPlatform() == Common::kPlatformApple2GS)
            sprintf(szFile, IDS_WTP_OBJ_APPLE, iObj);
      Common::File file;
      if (!file.open(szFile)) {
            warning ("Could not open file \'%s\'", szFile);
            return 0;
      }
      uint32 filelen = file.size();
      if (_vm->getPlatform() == Common::kPlatformC64) { //Skip the loading address
            filelen -= 2;
            file.seek(2, SEEK_CUR);
      }
      memset(buffer, 0, sizeof(buffer));
      file.read(buffer, filelen);
      file.close();
      return filelen;
}

void Winnie::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 = _vm->rnd(IDI_WTP_MAX_OBJ - 1);
                  done = true;

                  for (int j = 0; j < IDI_WTP_MAX_OBJ_MISSING; j++) {
                        if (_game.iUsedObj[j] == iObj) {
                              done = false;
                              break;
                        }
                  }
            }

            _game.iUsedObj[i] = iObj;

            done = false;
            while (!done) {
                  iRoom = _vm->rnd(IDI_WTP_MAX_ROOM_NORMAL);
                  done = true;

                  for (int j = 0; j < IDI_WTP_MAX_ROOM_OBJ; j++) {
                        if (_game.iObjRoom[j] == iRoom) {
                              done = false;
                              break;
                        }
                  }
            }

            _game.iObjRoom[iObj] = iRoom;
      }
}

void Winnie::intro() {
      drawPic(IDS_WTP_FILE_LOGO);
      _vm->printStr(IDS_WTP_INTRO_0);
      _vm->_gfx->doUpdate();
      _vm->_system->updateScreen();
      _vm->_system->delayMillis(0x640);

      if (_vm->getPlatform() == Common::kPlatformAmiga)
            _vm->_gfx->clearScreen(0);

      drawPic(IDS_WTP_FILE_TITLE);

      _vm->printStr(IDS_WTP_INTRO_1);
      _vm->_gfx->doUpdate();
      _vm->_system->updateScreen();
      _vm->_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 Winnie::getObjInRoom(int iRoom) {
      for (int iObj = 1; iObj < IDI_WTP_MAX_ROOM_OBJ; iObj++)
            if (_game.iObjRoom[iObj] == iRoom)
                  return iObj;
      return 0;
}

#define setTakeDrop() {\
      if (getObjInRoom(_room))\
            fCanSel[IDI_WTP_SEL_TAKE] = true;\
      else\
            fCanSel[IDI_WTP_SEL_TAKE] = false;\
      if (_game.iObjHave)\
            fCanSel[IDI_WTP_SEL_DROP] = true;\
      else\
            fCanSel[IDI_WTP_SEL_DROP] = false;\
}

void Winnie::setFlag(int iFlag) {
      _game.fGame[iFlag] = 1;
}

void Winnie::clearFlag(int iFlag) {
      _game.fGame[iFlag] = 0;
}

int Winnie::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 (!_vm->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 (_game.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();

                  // 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
                  strcpy(szMenu, (char *)(buffer + pc));
                  _vm->XOR80(szMenu);
                  break;
            default:
                  // print description
                  printStrWinnie((char *)(buffer + pc));
                  if (_vm->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 (++_game.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) {
                              _vm->printStr(IDS_WTP_CANT_GO);
                              _vm->getSelection(kSelAnyKey);
                        } else {
                              _room = hdr.roomNew[iDir];
                              return IDI_WTP_PAR_GOTO;
                        }
                        break;
                  case IDI_WTP_SEL_TAKE:
                        takeObj(_room);
                        setTakeDrop();
                        break;
                  case IDI_WTP_SEL_DROP:
                        dropObj(_room);
                        setTakeDrop();
                        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);
                        _vm->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++);
                        clearFlag(opcode);
                        break;
                  case IDO_WTP_FLAG_SET:
                        opcode = *(buffer + pc++);
                        setFlag(opcode);
                        break;
                  case IDO_WTP_GAME_OVER:
                        gameOver();
                        break;
                  case IDO_WTP_WALK_MIST:
                        _mist--;
                        if (!_mist) {
                              _room = _vm->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 = _vm->rnd(IDI_WTP_MAX_ROOM_TELEPORT) + 1;
                        return IDI_WTP_PAR_GOTO;
                  default:
                        opcode = 0;
                        break;
                  }
            } while (opcode && !_vm->shouldQuit());

            if (iNewRoom) {
                  _room = iNewRoom;
                  return IDI_WTP_PAR_GOTO;
            }

            if (iBlock == 1)
                  return IDI_WTP_PAR_OK;
            _vm->_gfx->doUpdate();
            _vm->_system->updateScreen();
      }

      return IDI_WTP_PAR_OK;
}

void Winnie::keyHelp() {
      playSound(IDI_WTP_SND_KEYHELP);
      _vm->printStr(IDS_WTP_HELP_0);
      _vm->getSelection(kSelAnyKey);
      _vm->printStr(IDS_WTP_HELP_1);
      _vm->getSelection(kSelAnyKey);
}

void Winnie::inventory() {
      char szMissing[41] = {0};

      if (_game.iObjHave)
            printObjStr(_game.iObjHave, IDI_WTP_OBJ_TAKE);
      else {
            _vm->clearTextArea();
            _vm->drawStr(IDI_WTP_ROW_MENU, IDI_WTP_COL_MENU, IDA_DEFAULT, IDS_WTP_INVENTORY_0);
      }

      sprintf(szMissing, IDS_WTP_INVENTORY_1, _game.nObjMiss);
      _vm->drawStr(IDI_WTP_ROW_OPTION_4, IDI_WTP_COL_MENU, IDA_DEFAULT, szMissing);
      _vm->_gfx->doUpdate();
      _vm->_system->updateScreen(); //TODO: Move to game's main loop
      _vm->getSelection(kSelAnyKey);
}

void Winnie::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 Winnie::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 Winnie::takeObj(int iRoom) {
      if (_game.iObjHave) {
            // player is already carrying an object, can't take
            _vm->printStr(IDS_WTP_CANT_TAKE);
            _vm->getSelection(kSelAnyKey);
      } else {
            // take object
            int iObj = getObjInRoom(iRoom);

            _game.iObjHave = iObj;
            _game.iObjRoom[iObj] = 0;

            _vm->printStr(IDS_WTP_OK);
            playSound(IDI_WTP_SND_TAKE);

            drawRoomPic();

            // print object "take" string
            printObjStr(_game.iObjHave, IDI_WTP_OBJ_TAKE);
            _vm->getSelection(kSelAnyKey);

            // HACK WARNING
            if (iObj == 18) {
                  _game.fGame[0x0d] = 1;
            }
      }
}

void Winnie::dropObj(int iRoom) {
      int iCode;

      if (getObjInRoom(iRoom)) {
            // there already is an object in the room, can't drop
            _vm->printStr(IDS_WTP_CANT_DROP);
            _vm->getSelection(kSelAnyKey);
      } else {
            // HACK WARNING
            if (_game.iObjHave == 18) {
                  _game.fGame[0x0d] = 0;
            }

            if (isRightObj(iRoom, _game.iObjHave, &iCode)) {
                  // object has been dropped in the right place
                  _vm->printStr(IDS_WTP_OK);
                  _vm->getSelection(kSelAnyKey);
                  playSound(IDI_WTP_SND_DROP_OK);
                  printObjStr(_game.iObjHave, IDI_WTP_OBJ_DROP);
                  _vm->getSelection(kSelAnyKey);

                  // increase amount of objects returned, decrease amount of objects missing
                  _game.nObjMiss--;
                  _game.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 (_game.iUsedObj[i] == _game.iObjHave) {
                              _game.iUsedObj[i] ^= 0x80;
                              break;
                        }
                  }

                  // set flag according to dropped object's id
                  _game.fGame[iCode] = 1;

                  // player is carrying nothing
                  _game.iObjHave = 0;

                  if (!_game.nObjMiss) {
                        // all objects returned, tell player to find party
                        playSound(IDI_WTP_SND_FANFARE);
                        _vm->printStr(IDS_WTP_GAME_OVER_0);
                        _vm->getSelection(kSelAnyKey);
                        _vm->printStr(IDS_WTP_GAME_OVER_1);
                        _vm->getSelection(kSelAnyKey);
                  }
            } else {
                  // drop object in the given room
                  _game.iObjRoom[_game.iObjHave] = iRoom;

                  // object has been dropped in the wrong place
                  _vm->printStr(IDS_WTP_WRONG_PLACE);
                  _vm->getSelection(kSelAnyKey);

                  playSound(IDI_WTP_SND_DROP);
                  drawRoomPic();

                  _vm->printStr(IDS_WTP_WRONG_PLACE);
                  _vm->getSelection(kSelAnyKey);

                  // print object description
                  printObjStr(_game.iObjHave, IDI_WTP_OBJ_DESC);
                  _vm->getSelection(kSelAnyKey);

                  _game.iObjHave = 0;
            }
      }
}

void Winnie::dropObjRnd() {
      if (!_game.iObjHave)
            return;

      int iRoom = 0;
      bool done = false;

      while (!done) {
            iRoom = _vm->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 (_game.iObjRoom[j] == iRoom) {
                        done = false;
                  }
            }
      }

      _game.iObjRoom[_game.iObjHave] = iRoom;
      _game.iObjHave = 0;
}

void Winnie::wind() {
      int iRoom = 0;
      bool done;

      _doWind = 0;
      _game.nMoves = 0;
      if (!_game.nObjMiss)
            return;

      _vm->printStr(IDS_WTP_WIND_0);
      playSound(IDI_WTP_SND_WIND_0);
      _vm->getSelection(kSelAnyKey);

      _vm->printStr(IDS_WTP_WIND_1);
      playSound(IDI_WTP_SND_WIND_0);
      _vm->getSelection(kSelAnyKey);

      dropObjRnd();

      // randomize positions of objects at large
      for (int i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++) {
            if (!(_game.iUsedObj[i] & IDI_XOR_KEY)) {
                  done = false;
                  while (!done) {
                        iRoom = _vm->rnd(IDI_WTP_MAX_ROOM_NORMAL);
                        done = true;

                        for (int j = 0; j < IDI_WTP_MAX_ROOM_OBJ; j++) {
                              if (_game.iObjRoom[j] == iRoom) {
                                    done = false;
                              }
                        }
                  }
                  _game.iObjRoom[_game.iUsedObj[i]] = iRoom;
            }
      }
}

void Winnie::mist() {
      // mist length in turns is (2-5)
      _mist = _vm->rnd(4) + 2;

      _room = IDI_WTP_ROOM_MIST;
      drawRoomPic();

      _vm->printStr(IDS_WTP_MIST);
}

void Winnie::tigger() {
      _room = IDI_WTP_ROOM_TIGGER;

      drawRoomPic();
      _vm->printStr(IDS_WTP_TIGGER);

      dropObjRnd();
}

void Winnie::showOwlHelp() {
      if (_game.iObjHave) {
            _vm->printStr(IDS_WTP_OWL_0);
            _vm->getSelection(kSelAnyKey);
            printObjStr(_game.iObjHave, IDI_WTP_OBJ_HELP);
            _vm->getSelection(kSelAnyKey);
      }
      if (getObjInRoom(_room)) {
            _vm->printStr(IDS_WTP_OWL_0);
            _vm->getSelection(kSelAnyKey);
            printObjStr(getObjInRoom(_room), IDI_WTP_OBJ_HELP);
            _vm->getSelection(kSelAnyKey);
      }
}


void Winnie::drawMenu(char *szMenu, int iSel, int fCanSel[]) {
      int iRow = 0, iCol = 0;

      _vm->clearTextArea();
      _vm->drawStr(IDI_WTP_ROW_MENU, IDI_WTP_COL_MENU, IDA_DEFAULT, szMenu);

      if (fCanSel[IDI_WTP_SEL_NORTH])
            _vm->drawStr(IDI_WTP_ROW_OPTION_4, IDI_WTP_COL_NSEW, IDA_DEFAULT, IDS_WTP_NSEW);
      if (fCanSel[IDI_WTP_SEL_TAKE])
            _vm->drawStr(IDI_WTP_ROW_OPTION_4, IDI_WTP_COL_TAKE, IDA_DEFAULT, IDS_WTP_TAKE);
      if (fCanSel[IDI_WTP_SEL_DROP])
            _vm->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;
      }
      _vm->drawStr(iRow, iCol - 1, IDA_DEFAULT, ">");
      _vm->_gfx->doUpdate();
      _vm->_system->updateScreen(); //TODO: Move to game's main loop
}

void Winnie::incMenuSel(int *iSel, int fCanSel[]) {
      do {
            *iSel += 1;
            if (*iSel > IDI_WTP_SEL_DROP) *iSel = IDI_WTP_SEL_OPT_1;
      } while (!fCanSel[*iSel]);
}

void Winnie::decMenuSel(int *iSel, int fCanSel[]) {
      do {
            *iSel -= 1;
            if (*iSel < IDI_WTP_SEL_OPT_1) *iSel = IDI_WTP_SEL_DROP;
      } while (!fCanSel[*iSel]);
}

void Winnie::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;
      }
}

#define makeSel() {\
      if (fCanSel[*iSel]) {\
            return;\
      } else {\
            keyHelp();\
            clrMenuSel(iSel, fCanSel);\
      }\
}

void Winnie::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 (!_vm->shouldQuit()) {
            while (_vm->_system->getEventManager()->pollEvent(event)) {
                  switch(event.type) {
                  case Common::EVENT_RTL:
                  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)) {
                              _vm->_gfx->setCursorPalette(true);
                        } else if (fCanSel[IDI_WTP_SEL_SOUTH] && hotspotSouth.contains(event.mouse.x, event.mouse.y)) {
                              _vm->_gfx->setCursorPalette(true);
                        } else if (fCanSel[IDI_WTP_SEL_WEST] && hotspotWest.contains(event.mouse.x, event.mouse.y)) {
                              _vm->_gfx->setCursorPalette(true);
                        } else if (fCanSel[IDI_WTP_SEL_EAST] && hotspotEast.contains(event.mouse.x, event.mouse.y)) {
                              _vm->_gfx->setCursorPalette(true);
                        } else {
                              _vm->_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();
                              _vm->_gfx->setCursorPalette(false);
                              return;
                        } else if (fCanSel[IDI_WTP_SEL_SOUTH] && hotspotSouth.contains(event.mouse.x, event.mouse.y)) {
                              *iSel = IDI_WTP_SEL_SOUTH;
                              makeSel();
                              _vm->_gfx->setCursorPalette(false);
                              return;
                        } else if (fCanSel[IDI_WTP_SEL_WEST] && hotspotWest.contains(event.mouse.x, event.mouse.y)) {
                              *iSel = IDI_WTP_SEL_WEST;
                              makeSel();
                              _vm->_gfx->setCursorPalette(false);
                              return;
                        } else if (fCanSel[IDI_WTP_SEL_EAST] && hotspotEast.contains(event.mouse.x, event.mouse.y)) {
                              *iSel = IDI_WTP_SEL_EAST;
                              makeSel();
                              _vm->_gfx->setCursorPalette(false);
                              return;
                        } else {
                              _vm->_gfx->setCursorPalette(false);
                        }

                        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:
                        if (event.kbd.keycode == Common::KEYCODE_d && (event.kbd.flags & Common::KBD_CTRL) && _vm->_console) {
                              _vm->_console->attach();
                              _vm->_console->onFrame();
                              continue;
                        }

                        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();
                              break;
                        case Common::KEYCODE_s:
                              if (event.kbd.flags & Common::KBD_CTRL) {
                                    _vm->flipflag(fSoundOn);
                              } else {
                                    *iSel = IDI_WTP_SEL_SOUTH;
                                    makeSel();
                              }
                              break;
                        case Common::KEYCODE_e:
                              *iSel = IDI_WTP_SEL_EAST;
                              makeSel();
                              break;
                        case Common::KEYCODE_w:
                              *iSel = IDI_WTP_SEL_WEST;
                              makeSel();
                              break;
                        case Common::KEYCODE_t:
                              *iSel = IDI_WTP_SEL_TAKE;
                              makeSel();
                              break;
                        case Common::KEYCODE_d:
                              *iSel = IDI_WTP_SEL_DROP;
                              makeSel();
                              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;
                              }
                        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 Winnie::gameLoop() {
      WTP_ROOM_HDR hdr;
      uint8 *roomdata = (uint8 *)malloc(4096);
      int iBlock;

phase0:
      if (!_game.nObjMiss && (_room == IDI_WTP_ROOM_PICNIC))
            _room = IDI_WTP_ROOM_PARTY;

      readRoom(_room, roomdata, hdr);
      drawRoomPic();
      _vm->_gfx->doUpdate();
      _vm->_system->updateScreen();

phase1:
      if (getObjInRoom(_room)) {
            printObjStr(getObjInRoom(_room), IDI_WTP_OBJ_DESC);
            _vm->getSelection(kSelAnyKey);
      }

phase2:
      for (iBlock = 0; iBlock < IDI_WTP_MAX_BLOCK; iBlock++) {
            if (parser(hdr.ofsDesc[iBlock] - _roomOffset, iBlock, roomdata) == IDI_WTP_PAR_BACK)
                  goto phase1;
      }

      while (!_vm->shouldQuit()) {
            for (iBlock = 0; iBlock < IDI_WTP_MAX_BLOCK; iBlock++) {
                  switch(parser(hdr.ofsBlock[iBlock] - _roomOffset, iBlock, roomdata)) {
                  case IDI_WTP_PAR_GOTO:
                        goto phase0;
                        break;
                  case IDI_WTP_PAR_BACK:
                        goto phase2;
                        break;
                  }
            }
      }

      free(roomdata);
}

void Winnie::drawPic(const char *szName) {
      char szFile[256] = {0};
      Common::File file;
      uint8 *buffer = (uint8 *)malloc(4096);

      // construct filename
      if (_vm->getPlatform() != Common::kPlatformAmiga)
            sprintf(szFile, "%s.pic", szName);
      else
            strcpy(szFile, szName);
      if (!file.open(szFile)) {
            warning ("Could not open file \'%s\'", szFile);
            return;
      }

      uint32 size = file.size();
      file.read(buffer, size);
      file.close();

      _vm->_picture->decodePicture(buffer, size, 1, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
      _vm->_picture->showPic(IDI_WTP_PIC_X0, IDI_WTP_PIC_Y0, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);

      free(buffer);
}

void Winnie::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));

      _vm->_picture->setOffset(x0, y0);
      _vm->_picture->decodePicture(buffer + objhdr.ofsPic - _objOffset, objSize, 0, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
      _vm->_picture->setOffset(0, 0);
      _vm->_picture->showPic(10, 0, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);

      free(buffer);
}

void Winnie::drawRoomPic() {
      WTP_ROOM_HDR roomhdr;
      uint8 *buffer = (uint8 *)malloc(4096);
      int iObj = getObjInRoom(_room);

      // clear gfx screen
      _vm->_gfx->clearScreen(0);

      // read room picture
      readRoom(_room, buffer, roomhdr);

      // draw room picture
      _vm->_picture->decodePicture(buffer + roomhdr.ofsPic - _roomOffset, 4096, 1, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
      _vm->_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 Winnie::playSound(ENUM_WTP_SOUND iSound) {
      //TODO
      warning ("STUB: playSound(%d)", iSound);
      return 1;
}

void Winnie::clrMenuSel(int *iSel, int fCanSel[]) {
      *iSel = IDI_WTP_SEL_OPT_1;
      while (!fCanSel[*iSel]) {
            *iSel += 1;
      }
      _vm->_gfx->setCursorPalette(false);
}

void Winnie::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 Winnie::gameOver() {
      // sing the Pooh song forever
      while (!_vm->shouldQuit()) {
            _vm->printStr(IDS_WTP_SONG_0);
            playSound(IDI_WTP_SND_POOH_0);
            _vm->printStr(IDS_WTP_SONG_1);
            playSound(IDI_WTP_SND_POOH_1);
            _vm->printStr(IDS_WTP_SONG_2);
            playSound(IDI_WTP_SND_POOH_2);
            _vm->getSelection(kSelAnyKey);
      }
}

void Winnie::saveGame() {
      Common::OutSaveFile* outfile;
      char szFile[256] = {0};
      int i = 0;

      sprintf(szFile, IDS_WTP_FILE_SAVEGAME);
      if (!(outfile = _vm->getSaveFileMan()->openForSaving(szFile)))
            return;

      outfile->writeUint32BE(MKID_BE('WINN'));  // header
      outfile->writeByte(WTP_SAVEGAME_VERSION);

      outfile->writeByte(_game.fSound);
      outfile->writeByte(_game.nMoves);
      outfile->writeByte(_game.nObjMiss);
      outfile->writeByte(_game.nObjRet);
      outfile->writeByte(_game.iObjHave);

      for (i = 0; i < IDI_WTP_MAX_FLAG; i++)
            outfile->writeByte(_game.fGame[i]);

      for (i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++)
            outfile->writeByte(_game.iUsedObj[i]);

      for (i = 0; i < IDI_WTP_MAX_ROOM_OBJ; i++)
            outfile->writeByte(_game.iObjRoom[i]);

      outfile->finalize();

      if (outfile->err())
            warning("Can't write file '%s'. (Disk full?)", szFile);

      delete outfile;
}

void Winnie::loadGame() {
      Common::InSaveFile* infile;
      char szFile[256] = {0};
      int saveVersion = 0;
      int i = 0;

      sprintf(szFile, IDS_WTP_FILE_SAVEGAME);
      if (!(infile = _vm->getSaveFileMan()->openForLoading(szFile)))
            return;

      if (infile->readUint32BE() == MKID_BE('WINN')) {
            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);

            _game.fSound = infile->readByte();
            _game.nMoves = infile->readByte();
            _game.nObjMiss = infile->readByte();
            _game.nObjRet = infile->readByte();
            _game.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
            _game.fSound = infile->readByte();
            infile->readByte();                             // first 8 bits of nMoves
            _game.nMoves = infile->readByte();
            infile->readByte();                             // first 8 bits of nObjMiss
            _game.nObjMiss = infile->readByte();
            infile->readByte();                             // first 8 bits of nObjRet
            _game.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
            _game.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++)
            _game.fGame[i] = infile->readByte();

      for (i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++)
            _game.iUsedObj[i] = infile->readByte();

      for (i = 0; i < IDI_WTP_MAX_ROOM_OBJ; i++)
            _game.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 Winnie::printStrWinnie(char *szMsg) {
      if (_vm->getPlatform() != Common::kPlatformAmiga)
            _vm->printStrXOR(szMsg);
      else
            _vm->printStr(szMsg);
}

// Console-related functions

void Winnie::debugCurRoom() {
      _vm->_console->DebugPrintf("Current Room = %d\n", _room);
}

Winnie::Winnie(PreAgiEngine* vm) : _vm(vm) {
      _vm->_console = new Winnie_Console(_vm, this);
}

void Winnie::init() {
      memset(&_game, 0, sizeof(_game));
      _game.fSound = 1;
      _game.nObjMiss = IDI_WTP_MAX_OBJ_MISSING;
      _game.nObjRet = 0;
      _game.fGame[0] = 1;
      _game.fGame[1] = 1;
      _room = IDI_WTP_ROOM_HOME;

      _mist = -1;
      _doWind = false;
      _winnieEvent = false;

      if (_vm->getPlatform() != Common::kPlatformAmiga) {
            _isBigEndian = false;
            _roomOffset = IDI_WTP_OFS_ROOM;
            _objOffset = IDI_WTP_OFS_OBJ;
      } else {
            _isBigEndian = true;
            _roomOffset = 0;
            _objOffset = 0;
      }

      if (_vm->getPlatform() == Common::kPlatformC64 || _vm->getPlatform() == Common::kPlatformApple2GS)
            _vm->_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);
}

void Winnie::run() {
      randomize();
      if (_vm->getPlatform() != Common::kPlatformC64 && _vm->getPlatform() != Common::kPlatformApple2GS)
            intro();
      gameLoop();
}

}

Generated by  Doxygen 1.6.0   Back to index