scummvm/scumm/scummvm.cpp
Max Horn 48b16bd819 fix warning
svn-id: r8865
2003-07-08 18:09:22 +00:00

2536 lines
67 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 Ludvig Strigeus
* Copyright (C) 2001-2003 The ScummVM project
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
#include "stdafx.h"
#include "scumm.h"
#include "actor.h"
#include "boxes.h"
#include "charset.h"
#include "debugger.h"
#include "dialogs.h"
#include "imuse.h"
#include "imuse_digi.h"
#include "intern.h"
#include "object.h"
#include "player_v2.h"
#include "resource.h"
#include "sound.h"
#include "string.h"
#include "verbs.h"
#include "common/gameDetector.h"
#include "common/config-file.h"
#include "gui/console.h"
#include "gui/newgui.h"
#include "gui/message.h"
#include "sound/mixer.h"
#include "sound/mididrv.h"
#include "akos.h"
#include "costume.h"
#ifdef MACOSX
#include <sys/types.h>
#include <sys/stat.h>
#endif
#ifdef _WIN32_WCE
extern void drawError(char*);
#endif
// Use g_scumm from error() ONLY
Scumm *g_scumm = 0;
ScummDebugger *g_debugger;
byte *Scumm::get2byteCharPtr(int idx) {
/*
switch(language)
case korean:
return ( (idx % 256) - 0xb0) * 94 + (idx / 256) - 0xa1;
case japanese:
...
case taiwan:
...
*/
idx = ( (idx % 256) - 0xb0) * 94 + (idx / 256) - 0xa1; // only for korean
return _2byteFontPtr + 2 * _2byteHeight * idx;
}
extern NewGui *g_gui;
extern uint16 _debugLevel;
static const VersionSettings scumm_settings[] = {
/* Scumm Version 1 */
/* Scumm Version 2 */
{"maniac", "Maniac Mansion", GID_MANIAC, 2, VersionSettings::ADLIB_ALWAYS,
GF_SMALL_HEADER | GF_USE_KEY | GF_SMALL_NAMES | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALLING, "00.LFL"},
{"zak", "Zak McKracken and the Alien Mindbenders", GID_ZAK, 2, VersionSettings::ADLIB_ALWAYS,
GF_SMALL_HEADER | GF_USE_KEY | GF_SMALL_NAMES | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALLING, "00.LFL"},
/* Scumm Version 3 */
{"indy3EGA", "Indiana Jones and the Last Crusade", GID_INDY3, 3, VersionSettings::ADLIB_ALWAYS,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALLING | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE, "00.LFL"},
{"indy3", "Indiana Jones and the Last Crusade (256)", GID_INDY3_256, 3, VersionSettings::ADLIB_ALWAYS,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALLING | GF_OLD256 | GF_ADLIB_DEFAULT, "00.LFL"},
{"zak256", "Zak McKracken and the Alien Mindbenders (256)", GID_ZAK256, 3, VersionSettings::ADLIB_ALWAYS,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALLING | GF_OLD256 | GF_AUDIOTRACKS, "00.LFL"},
{"loom", "Loom", GID_LOOM, 3, VersionSettings::ADLIB_ALWAYS,
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALLING | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE, "00.LFL"},
/* Scumm Version 4 */
{"monkeyEGA", "Monkey Island 1 (EGA)", GID_MONKEY_EGA, 4, VersionSettings::ADLIB_ALWAYS,
GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR | GF_ADLIB_DEFAULT, "000.LFL"},
{"pass", "Passport to Adventure", GID_MONKEY_EGA, 4, VersionSettings::ADLIB_ALWAYS,
GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR | GF_ADLIB_DEFAULT, "000.LFL"},
/* Scumm version 5 */
{"monkeyVGA", "Monkey Island 1 (256 color Floppy version)", GID_MONKEY_VGA, 4, VersionSettings::ADLIB_ALWAYS,
GF_SMALL_HEADER | GF_USE_KEY | GF_ADLIB_DEFAULT, "000.LFL"},
{"loomcd", "Loom (256 color CD version)", GID_LOOM256, 4, VersionSettings::ADLIB_ALWAYS,
GF_SMALL_HEADER | GF_USE_KEY | GF_AUDIOTRACKS | GF_ADLIB_DEFAULT, "000.LFL"},
{"monkey", "Monkey Island 1", GID_MONKEY, 5, VersionSettings::ADLIB_PREFERRED,
GF_USE_KEY | GF_AUDIOTRACKS | GF_ADLIB_DEFAULT, 0},
{"monkey1", "Monkey Island 1 (alt)", GID_MONKEY, 5, VersionSettings::ADLIB_PREFERRED,
GF_USE_KEY | GF_AUDIOTRACKS | GF_ADLIB_DEFAULT, 0},
{"game", "Monkey Island 1 (SegaCD version)", GID_MONKEY_SEGA, 5, VersionSettings::ADLIB_PREFERRED,
GF_USE_KEY | GF_AUDIOTRACKS | GF_ADLIB_DEFAULT, 0},
{"monkey2", "Monkey Island 2: LeChuck's revenge", GID_MONKEY2, 5, VersionSettings::ADLIB_PREFERRED,
GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"mi2demo", "Monkey Island 2: LeChuck's revenge (Demo)", GID_MONKEY2, 5, VersionSettings::ADLIB_PREFERRED,
GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"indydemo", "Indiana Jones and the Fate of Atlantis (FM Towns Demo)", GID_INDY4, 5, VersionSettings::ADLIB_PREFERRED,
GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"atlantis", "Indiana Jones and the Fate of Atlantis", GID_INDY4, 5, VersionSettings::ADLIB_PREFERRED,
GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"playfate", "Indiana Jones and the Fate of Atlantis (Demo)", GID_INDY4, 5, VersionSettings::ADLIB_PREFERRED,
GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"fate", "Indiana Jones and the Fate of Atlantis (Demo)", GID_INDY4, 5, VersionSettings::ADLIB_PREFERRED,
GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
/* Scumm Version 6 */
{"puttputt", "Putt-Putt Joins The Parade (DOS)", GID_PUTTPUTT, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"puttdemo", "Putt-Putt Joins The Parade (DOS Demo)", GID_PUTTDEMO, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS, 0},
{"moondemo", "Putt-Putt Goes To The Moon (DOS Demo)", GID_PUTTPUTT, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"puttmoon", "Putt-Putt Goes To The Moon (DOS)", GID_PUTTPUTT, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"funpack", "Putt-Putt's Fun Pack", GID_PUTTPUTT, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"fbpack", "Fatty Bear's Fun Pack", GID_PUTTPUTT, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"fbear", "Fatty Bear's Birthday Surprise (DOS)", GID_PUTTPUTT, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"fbdemo", "Fatty Bear's Birthday Surprise (DOS Demo)", GID_PUTTPUTT, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"tentacle", "Day Of The Tentacle", GID_TENTACLE, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"dottdemo", "Day Of The Tentacle (Demo)", GID_TENTACLE, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"samnmax", "Sam & Max", GID_SAMNMAX, 6, VersionSettings::ADLIB_DONT_CARE,
GF_NEW_OPCODES | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER, 0},
{"samdemo", "Sam & Max (Demo)", GID_SAMNMAX, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER | GF_ADLIB_DEFAULT, 0},
{"snmdemo", "Sam & Max (Demo)", GID_SAMNMAX, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER | GF_ADLIB_DEFAULT, 0},
{"snmidemo", "Sam & Max (Interactive WIP Demo)", GID_SAMNMAX, 6, VersionSettings::ADLIB_PREFERRED,
GF_NEW_OPCODES | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER | GF_ADLIB_DEFAULT, 0},
{"test", "Test demo game", GID_SAMNMAX, 6, VersionSettings::ADLIB_DONT_CARE, GF_NEW_OPCODES, 0},
/* Humongous Entertainment Scumm Version 7 */
{"farmdemo", "Let's Explore the Farm with Buzzy (Demo)", GID_SAMNMAX, 6, VersionSettings::ADLIB_DONT_CARE,
GF_NEW_OPCODES | GF_AFTER_HEV7 | GF_USE_KEY | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
/* Scumm Version 7 */
{"ft", "Full Throttle", GID_FT, 7, VersionSettings::ADLIB_DONT_CARE,
GF_NEW_OPCODES | GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE, 0},
{"dig", "The Dig", GID_DIG, 7, VersionSettings::ADLIB_DONT_CARE,
GF_NEW_OPCODES | GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE, 0},
/* Scumm Version 8 */
{"comi", "The Curse of Monkey Island", GID_CMI, 8, VersionSettings::ADLIB_DONT_CARE,
GF_NEW_OPCODES | GF_NEW_COSTUMES | GF_NEW_CAMERA | GF_DIGI_IMUSE | GF_DEFAULT_TO_1X_SCALER, 0},
{NULL, NULL, 0, 0, VersionSettings::ADLIB_DONT_CARE, 0, NULL}
};
const VersionSettings *Engine_SCUMM_targetList() {
return scumm_settings;
}
Engine *Engine_SCUMM_create(GameDetector *detector, OSystem *syst) {
Engine *engine;
if (detector->_amiga)
detector->_game.features |= GF_AMIGA;
switch (detector->_game.version) {
case 1:
case 2:
engine = new Scumm_v2(detector, syst);
break;
case 3:
engine = new Scumm_v3(detector, syst);
break;
case 4:
engine = new Scumm_v4(detector, syst);
break;
case 5:
engine = new Scumm_v5(detector, syst);
break;
case 6:
engine = new Scumm_v6(detector, syst);
break;
case 7:
engine = new Scumm_v7(detector, syst);
break;
case 8:
engine = new Scumm_v8(detector, syst);
break;
default:
error("Engine_SCUMM_create(): Unknown version of game engine");
}
return engine;
}
Scumm::Scumm (GameDetector *detector, OSystem *syst)
: Engine(detector, syst), _pauseDialog(0), _optionsDialog(0), _saveLoadDialog(0) {
OSystem::Property prop;
// Init all vars - maybe now we can get rid of our custom new/delete operators?
_imuse = NULL;
_imuseDigital = NULL;
_features = 0;
_verbs = NULL;
_objs = NULL;
_debugger = NULL;
_sound = NULL;
memset(&res, 0, sizeof(res));
memset(&vm, 0, sizeof(vm));
_smushFrameRate = 0;
_insaneState = false;
_videoFinished = false;
_smushPlay = false;
_quit = false;
_newgui = NULL;
_pauseDialog = NULL;
_optionsDialog = NULL;
_saveLoadDialog = NULL;
_debuggerDialog = NULL;
_fastMode = 0;
memset(&_rnd, 0, sizeof(RandomSource));
_gameId = 0;
memset(&gdi, 0, sizeof(Gdi));
_actors = NULL;
_inventory = NULL;
_newNames = NULL;
_scummVars = NULL;
_varwatch = 0;
_bitVars = NULL;
_numVariables = 0;
_numBitVariables = 0;
_numLocalObjects = 0;
_numGlobalObjects = 0;
_numArray = 0;
_numVerbs = 0;
_numFlObject = 0;
_numInventory = 0;
_numRooms = 0;
_numScripts = 0;
_numSounds = 0;
_numCharsets = 0;
_numNewNames = 0;
_numGlobalScripts = 0;
_numActors = 0;
_numCostumes = 0;
_audioNames = NULL;
_numAudioNames = 0;
_curActor = 0;
_curVerb = 0;
_curVerbSlot = 0;
_curPalIndex = 0;
_currentRoom = 0;
_egoPositioned = false;
_keyPressed = 0;
_lastKeyHit = 0;
_mouseButStat = 0;
_leftBtnPressed = 0;
_rightBtnPressed = 0;
_bootParam = 0;
_dumpScripts = false;
_debugMode = 0;
_language = 0;
_objectOwnerTable = NULL;
_objectRoomTable = NULL;
_objectStateTable = NULL;
memset(&_objectIDMap, 0, sizeof(ObjectIDMap));
_numObjectsInRoom = 0;
_userPut = 0;
_userState = 0;
_resourceHeaderSize = 0;
_saveLoadFlag = 0;
_saveLoadSlot = 0;
_lastSaveTime = 0;
_saveLoadCompatible = false;
memset(_saveLoadName, 0, sizeof(_saveLoadName));
_maxHeapThreshold = 0;
_minHeapThreshold = 0;
memset(_localScriptList, 0, sizeof(_localScriptList));
_scriptPointer = NULL;
_scriptOrgPointer = NULL;
_opcode = 0;
_numNestedScripts = 0;
_currentScript = 0;
_curExecScript = 0;
_lastCodePtr = NULL;
_resultVarNumber = 0;
_scummStackPos = 0;
memset(_localParamList, 0, sizeof(_localParamList));
memset(_scummStack, 0, sizeof(_scummStack));
_keyScriptKey = 0;
_keyScriptNo = 0;
_encbyte = 0;
memset(&_fileHandle, 0, sizeof(File));
_fileOffset = 0;
_exe_name = NULL;
_game_name = NULL;
_dynamicRoomOffsets = false;
memset(_resourceMapper, 0, sizeof(_resourceMapper));
_allocatedSize = 0;
_expire_counter = 0;
_lastLoadedRoom = 0;
_roomResource = 0;
OF_OWNER_ROOM = 0;
_verbMouseOver = 0;
_inventoryOffset = 0;
_classData = NULL;
_actorToPrintStrFor = 0;
_sentenceNum = 0;
memset(_sentence, 0, sizeof(_sentence));
memset(_string, 0, sizeof(_string));
_screenB = 0;
_screenH = 0;
_roomHeight = 0;
_roomWidth = 0;
_screenHeight = 0;
_screenWidth = 0;
memset(virtscr, 0, sizeof(virtscr));
memset(&camera, 0, sizeof(CameraData));
memset(_colorCycle, 0, sizeof(_colorCycle));
_ENCD_offs = 0;
_EXCD_offs = 0;
_CLUT_offs = 0;
_IM00_offs = 0;
_PALS_offs = 0;
_fullRedraw = false;
_BgNeedsRedraw = false;
_verbRedraw = false;
_screenEffectFlag = false;
_completeScreenRedraw = false;
memset(&_cursor, 0, sizeof(_cursor));
memset(_grabbedCursor, 0, sizeof(_grabbedCursor));
_currentCursor = 0;
_newEffect = 0;
_switchRoomEffect2 = 0;
_switchRoomEffect = 0;
_doEffect = false;
memset(&_flashlight, 0, sizeof(_flashlight));
_roomStrips = 0;
_bompActorPalettePtr = NULL;
_shakeEnabled= false;
_shakeFrame = 0;
_screenStartStrip = 0;
_screenEndStrip = 0;
_screenLeft = 0;
_screenTop = 0;
_blastObjectQueuePos = 0;
memset(_blastObjectQueue, 0, sizeof(_blastObjectQueue));
_blastTextQueuePos = 0;
memset(_blastTextQueue, 0, sizeof(_blastTextQueue));
_drawObjectQueNr = 0;
memset(_drawObjectQue, 0, sizeof(_drawObjectQue));
_palManipStart = 0;
_palManipEnd = 0;
_palManipCounter = 0;
_palManipPalette = NULL;
_palManipIntermediatePal = NULL;
memset(gfxUsageBits, 0, sizeof(gfxUsageBits));
_shadowPalette = NULL;
_shadowPaletteSize = 0;
memset(_currentPalette, 0, sizeof(_currentPalette));
memset(_proc_special_palette, 0, sizeof(_proc_special_palette));
_palDirtyMin = 0;
_palDirtyMax = 0;
_haveMsg = 0;
_useTalkAnims = false;
_defaultTalkDelay = 0;
_use_adlib = false;
tempMusic = 0;
_silentDigitalImuse = 0;
_noDigitalSamples = 0;
_saveSound = 1;
memset(_extraBoxFlags, 0, sizeof(_extraBoxFlags));
memset(_scaleSlots, 0, sizeof(_scaleSlots));
_charset = NULL;
_charsetColor = 0;
memset(_charsetColorMap, 0, sizeof(_charsetColorMap));
memset(_charsetData, 0, sizeof(_charsetData));
_charsetBufPos = 0;
memset(_charsetBuffer, 0, sizeof(_charsetBuffer));
_noSubtitles = false;
_numInMsgStack = 0;
_msgPtrToAdd = NULL;
_messagePtr = NULL;
_talkDelay = 0;
_keepText = false;
_existLanguageFile = false;
_languageBuffer = NULL;
_languageIndex = NULL;
memset(_transText, 0, sizeof(_transText));
_costumeRenderer = NULL;
_2byteFontPtr = 0;
//
// Init all VARS to 0xFF
//
VAR_LANGUAGE = 0xFF;
VAR_KEYPRESS = 0xFF;
VAR_EGO = 0xFF;
VAR_CAMERA_POS_X = 0xFF;
VAR_HAVE_MSG = 0xFF;
VAR_ROOM = 0xFF;
VAR_OVERRIDE = 0xFF;
VAR_MACHINE_SPEED = 0xFF;
VAR_ME = 0xFF;
VAR_NUM_ACTOR = 0xFF;
VAR_CURRENT_LIGHTS = 0xFF;
VAR_CURRENTDRIVE = 0xFF; // How about merging this with VAR_CURRENTDISK?
VAR_CURRENTDISK = 0xFF;
VAR_TMR_1 = 0xFF;
VAR_TMR_2 = 0xFF;
VAR_TMR_3 = 0xFF;
VAR_MUSIC_TIMER = 0xFF;
VAR_ACTOR_RANGE_MIN = 0xFF;
VAR_ACTOR_RANGE_MAX = 0xFF;
VAR_CAMERA_MIN_X = 0xFF;
VAR_CAMERA_MAX_X = 0xFF;
VAR_TIMER_NEXT = 0xFF;
VAR_VIRT_MOUSE_X = 0xFF;
VAR_VIRT_MOUSE_Y = 0xFF;
VAR_ROOM_RESOURCE = 0xFF;
VAR_LAST_SOUND = 0xFF;
VAR_CUTSCENEEXIT_KEY = 0xFF;
VAR_OPTIONS_KEY = 0xFF;
VAR_TALK_ACTOR = 0xFF;
VAR_CAMERA_FAST_X = 0xFF;
VAR_SCROLL_SCRIPT = 0xFF;
VAR_ENTRY_SCRIPT = 0xFF;
VAR_ENTRY_SCRIPT2 = 0xFF;
VAR_EXIT_SCRIPT = 0xFF;
VAR_EXIT_SCRIPT2 = 0xFF;
VAR_VERB_SCRIPT = 0xFF;
VAR_SENTENCE_SCRIPT = 0xFF;
VAR_INVENTORY_SCRIPT = 0xFF;
VAR_CUTSCENE_START_SCRIPT = 0xFF;
VAR_CUTSCENE_END_SCRIPT = 0xFF;
VAR_CHARINC = 0xFF;
VAR_CHARCOUNT = 0xFF;
VAR_WALKTO_OBJ = 0xFF;
VAR_DEBUGMODE = 0xFF;
VAR_HEAPSPACE = 0xFF;
VAR_RESTART_KEY = 0xFF;
VAR_PAUSE_KEY = 0xFF;
VAR_MOUSE_X = 0xFF;
VAR_MOUSE_Y = 0xFF;
VAR_TIMER = 0xFF;
VAR_TMR_4 = 0xFF;
VAR_SOUNDCARD = 0xFF;
VAR_VIDEOMODE = 0xFF;
VAR_SAVELOADDIALOG_KEY = 0xFF;
VAR_FIXEDDISK = 0xFF;
VAR_CURSORSTATE = 0xFF;
VAR_USERPUT = 0xFF;
VAR_SOUNDRESULT = 0xFF;
VAR_TALKSTOP_KEY = 0xFF;
VAR_59 = 0xFF;
VAR_NOSUBTITLES = 0xFF;
VAR_SOUNDPARAM = 0xFF;
VAR_SOUNDPARAM2 = 0xFF;
VAR_SOUNDPARAM3 = 0xFF;
VAR_MOUSEPRESENT = 0xFF;
VAR_PERFORMANCE_1 = 0xFF;
VAR_PERFORMANCE_2 = 0xFF;
VAR_ROOM_FLAG = 0xFF;
VAR_GAME_LOADED = 0xFF;
VAR_NEW_ROOM = 0xFF;
VAR_VERSION = 0xFF;
VAR_V5_TALK_STRING_Y = 0xFF;
VAR_V6_SCREEN_WIDTH = 0xFF;
VAR_V6_SCREEN_HEIGHT = 0xFF;
VAR_V6_EMSSPACE = 0xFF;
VAR_V6_RANDOM_NR = 0xFF;
VAR_STRING2DRAW = 0xFF;
VAR_CAMERA_POS_Y = 0xFF;
VAR_CAMERA_MIN_Y = 0xFF;
VAR_CAMERA_MAX_Y = 0xFF;
VAR_CAMERA_THRESHOLD_X = 0xFF;
VAR_CAMERA_THRESHOLD_Y = 0xFF;
VAR_CAMERA_SPEED_X = 0xFF;
VAR_CAMERA_SPEED_Y = 0xFF;
VAR_CAMERA_ACCEL_X = 0xFF;
VAR_CAMERA_ACCEL_Y = 0xFF;
VAR_CAMERA_DEST_X = 0xFF;
VAR_CAMERA_DEST_Y = 0xFF;
VAR_CAMERA_FOLLOWED_ACTOR = 0xFF;
VAR_LEFTBTN_DOWN = 0xFF;
VAR_RIGHTBTN_DOWN = 0xFF;
VAR_LEFTBTN_HOLD = 0xFF;
VAR_RIGHTBTN_HOLD = 0xFF;
VAR_MOUSE_BUTTONS = 0xFF;
VAR_MOUSE_HOLD = 0xFF;
VAR_SAVELOAD_SCRIPT = 0xFF;
VAR_SAVELOAD_SCRIPT2 = 0xFF;
VAR_DEFAULT_TALK_DELAY = 0xFF;
VAR_CHARSET_MASK = 0xFF;
VAR_CUSTOMSCALETABLE = 0xFF;
VAR_V6_SOUNDMODE = 0xFF;
VAR_ACTIVE_VERB = 0xFF;
VAR_ACTIVE_OBJECT1 = 0xFF;
VAR_ACTIVE_OBJECT2 = 0xFF;
VAR_VERB_ALLOWED = 0xFF;
VAR_CLICK_AREA = 0xFF;
// Use g_scumm from error() ONLY
g_scumm = this;
g_debugger = new ScummDebugger;
_debugMode = detector->_debugMode;
_debugLevel = detector->_debugLevel;
_dumpScripts = detector->_dumpScripts;
_bootParam = detector->_bootParam;
_exe_name = strdup(detector->_game.filename);
_game_name = strdup(detector->_gameFileName.c_str());
_gameId = detector->_game.id;
_version = detector->_game.version;
setFeatures(detector->_game.features);
_noSubtitles = detector->_noSubtitles;
_defaultTalkDelay = detector->_talkSpeed;
_use_adlib = detector->_use_adlib;
_language = detector->_language;
memset(&res, 0, sizeof(res));
_allocatedSize = 0;
_roomResource = 0;
_lastLoadedRoom = 0;
_expire_counter = 0;
_dynamicRoomOffsets = 0;
_shakeEnabled = false;
_hexdumpScripts = false;
_showStack = false;
if (_gameId == GID_ZAK256) { // FmTowns is 320x240
_screenWidth = 320;
_screenHeight = 240;
} else if (_gameId == GID_CMI) {
_screenWidth = 640;
_screenHeight = 480;
} else {
_screenWidth = 320;
_screenHeight = 200;
}
gdi._numStrips = _screenWidth / 8;
_newgui = g_gui;
_sound = new Sound(this);
_sound->_sound_volume_master = detector->_master_volume;
_sound->_sound_volume_sfx = detector->_sfx_volume;
_sound->_sound_volume_music = detector->_music_volume;
// Override global scaler with any game-specific define
if (g_config->get("gfx_mode")) {
prop.gfx_mode = detector->parseGraphicsMode(g_config->get("gfx_mode"));
syst->property(OSystem::PROP_SET_GFX_MODE, &prop);
}
/* Initialize backend */
syst->init_size(_screenWidth, _screenHeight);
prop.cd_num = detector->_cdrom;
if (prop.cd_num >= 0 && (_features & GF_AUDIOTRACKS))
syst->property(OSystem::PROP_OPEN_CD, &prop);
// Override global fullscreen setting with any game-specific define
if (g_config->getBool("fullscreen", false)) {
if (!syst->property(OSystem::PROP_GET_FULLSCREEN, 0))
syst->property(OSystem::PROP_TOGGLE_FULLSCREEN, 0);
}
#ifndef __GP32__ //ph0x FIXME, "quick dirty hack"
/* Bind the mixer to the system => mixer will be invoked
* automatically when samples need to be generated */
_silentDigitalImuse = false;
if (!_mixer->bindToSystem(syst)) {
warning("Sound initialization failed");
if (detector->_use_adlib) {
_use_adlib = false;
detector->_use_adlib = false;
detector->_midi_driver = MD_NULL;
warning("Adlib music was selected, switching to midi null driver");
}
_silentDigitalImuse = true;
_noDigitalSamples = true;
}
_mixer->setVolume(kDefaultSFXVolume * kDefaultMasterVolume / 255);
_mixer->setMusicVolume(kDefaultMusicVolume);
// Init iMuse
if (_features & GF_DIGI_IMUSE) {
_imuseDigital = new IMuseDigital(this);
_imuse = NULL;
_playerV2 = NULL;
} else if (_features & GF_OLD_BUNDLE) {
_playerV2 = NULL;
if (!(_features & GF_AMIGA) && _version != 1)
_playerV2 = new Player_V2(this);
_imuse = NULL;
_imuseDigital = NULL;
} else {
_playerV2 = NULL;
_imuseDigital = NULL;
_imuse = IMuse::create (syst, detector->createMidi());
if (_imuse) {
if (detector->_gameTempo != 0)
_imuse->property(IMuse::PROP_TEMPO_BASE, detector->_gameTempo);
_imuse->property (IMuse::PROP_OLD_ADLIB_INSTRUMENTS, (_features & GF_SMALL_HEADER) ? 1 : 0);
_imuse->property (IMuse::PROP_MULTI_MIDI, detector->_multi_midi);
_imuse->property (IMuse::PROP_NATIVE_MT32, detector->_native_mt32);
if (_features & GF_HUMONGOUS) {
_imuse->property (IMuse::PROP_LIMIT_PLAYERS, 1);
_imuse->property (IMuse::PROP_RECYCLE_PLAYERS, 1);
}
_imuse->set_music_volume(_sound->_sound_volume_music);
}
}
#endif // ph0x-hack
// Load game from specified slot, if any
if (detector->_save_slot != -1) {
_saveLoadSlot = detector->_save_slot;
_saveLoadFlag = 2;
_saveLoadCompatible = false;
}
loadLanguageBundle();
// Load CJK font
_CJKMode = false;
if ((_gameId == GID_DIG || _gameId == GID_CMI) && (_language == KO_KOR || _language == JA_JPN || _language == ZH_TWN)) {
File fp;
const char *fontFile = NULL;
switch(_language) {
case KO_KOR:
_CJKMode = true;
fontFile = "korean.fnt";
break;
case JA_JPN:
_CJKMode = true;
fontFile = (_gameId == GID_DIG) ? "kanji16.fnt" : "japanese.fnt";
break;
case ZH_TWN:
if (_gameId == GID_CMI) {
_CJKMode = true;
fontFile = "chinese.fnt";
}
break;
}
if (_CJKMode && fp.open(fontFile, getGameDataPath(), 1)) {
debug(2, "Loading CJK Font");
fp.seek(2,SEEK_CUR);
_2byteWidth = fp.readByte(); //FIXME: is this correct?
_2byteHeight = fp.readByte();
int numChar = 0;
switch(_language) {
case KO_KOR:
numChar = 2350;
break;
case JA_JPN:
numChar = (_gameId == GID_DIG) ? 1 : 1; //FIXME
break;
case ZH_TWN:
numChar = 1; //FIXME
break;
}
_2byteFontPtr = new byte[2 * _2byteHeight * numChar];
fp.read(_2byteFontPtr, 2 * _2byteHeight * numChar);
fp.close();
}
}
_audioNames = NULL;
}
Scumm::~Scumm () {
delete [] _actors;
delete _2byteFontPtr;
delete _charset;
delete _pauseDialog;
delete _optionsDialog;
delete _saveLoadDialog;
delete _sound;
delete _imuse;
delete _imuseDigital;
delete _playerV2;
free(_languageBuffer);
free(_audioNames);
delete _costumeRenderer;
free(_shadowPalette);
freeResources();
free(_objectStateTable);
free(_objectRoomTable);
free(_objectOwnerTable);
free(_inventory);
free(_verbs);
free(_objs);
free(_scummVars);
free(_bitVars);
free(_newNames);
free(_classData);
free(_exe_name);
free(_game_name);
free(_roomStrips);
free(_languageIndex);
delete g_debugger;
}
void Scumm::setFeatures (uint32 newFeatures) {
bool newCostumes = (_features & GF_NEW_COSTUMES) != 0;
bool newNewCostumes = (newFeatures & GF_NEW_COSTUMES) != 0;
_features = newFeatures;
if (!_costumeRenderer || newCostumes != newNewCostumes) {
delete _costumeRenderer;
if (newNewCostumes)
_costumeRenderer = new AkosRenderer(this);
else
_costumeRenderer = new CostumeRenderer(this);
}
}
void Scumm::scummInit() {
int i;
tempMusic = 0;
debug(9, "scummInit");
if (_version == 1) {
initScreens(0, 16, _screenWidth, 152);
} else if (_version >= 7) {
initScreens(0, 0, _screenWidth, _screenHeight);
} else {
initScreens(0, 16, _screenWidth, 144);
}
if (_version == 1) {
for (i = 0; i < 16; i++)
_shadowPalette[i] = i;
setupC64Palette();
} else if (_features & GF_16COLOR) {
for (i = 0; i < 16; i++)
_shadowPalette[i] = i;
setupEGAPalette();
}
if (_version <= 2) {
initV2MouseOver();
// Seems in V2 there was only a single room effect (iris),
// so we set that here.
_switchRoomEffect2 = 1;
_switchRoomEffect = 5;
}
if (_version <= 2)
_charset = new CharsetRendererV2(this);
else if (_version == 3)
_charset = new CharsetRendererV3(this);
else if (_version == 8)
_charset = new CharsetRendererNut(this);
else
_charset = new CharsetRendererClassic(this);
memset(_charsetData, 0, sizeof(_charsetData));
if (!(_features & GF_SMALL_NAMES) && _version != 8)
loadCharset(1);
if (_features & GF_OLD_BUNDLE)
loadCharset(0); // FIXME - HACK ?
setShake(0);
setupCursor();
// Allocate and Initialize actors
Actor::initActorClass(this);
_actors = new Actor[_numActors];
for (i = 1; i < _numActors; i++) {
_actors[i].number = i;
_actors[i].initActor(1);
// this is from IDB
if (_version == 1)
_actors[i].setActorCostume(i);
}
_numNestedScripts = 0;
vm.cutSceneStackPointer = 0;
memset(vm.cutScenePtr, 0, sizeof(vm.cutScenePtr));
memset(vm.cutSceneData, 0, sizeof(vm.cutSceneData));
for (i = 0; i < _maxVerbs; i++) {
_verbs[i].verbid = 0;
_verbs[i].right = _screenWidth - 1;
_verbs[i].old.left = -1;
_verbs[i].type = 0;
_verbs[i].color = 2;
_verbs[i].hicolor = 0;
_verbs[i].charset_nr = 1;
_verbs[i].curmode = 0;
_verbs[i].saveid = 0;
_verbs[i].center = 0;
_verbs[i].key = 0;
}
if (!(_features & GF_NEW_CAMERA)) {
camera._leftTrigger = 10;
camera._rightTrigger = 30;
camera._mode = 0;
}
camera._follows = 0;
virtscr[0].xstart = 0;
if (!(_features & GF_NEW_OPCODES)) {
// Setup light
_flashlight.xStrips = 7;
_flashlight.yStrips = 7;
_flashlight.buffer = NULL;
}
_mouse.x = 104;
_mouse.y = 56;
_ENCD_offs = 0;
_EXCD_offs = 0;
_currentScript = 0xFF;
_sentenceNum = 0;
_currentRoom = 0;
_numObjectsInRoom = 0;
_actorToPrintStrFor = 0;
_charsetBufPos = 0;
_haveMsg = 0;
_varwatch = -1;
_screenStartStrip = 0;
_talkDelay = 0;
_keepText = false;
_currentCursor = 0;
_cursor.state = 0;
_userPut = 0;
_newEffect = 129;
_fullRedraw = true;
clearDrawObjectQueue();
for (i = 0; i < 6; i++) {
if (_version == 3) { // FIXME - what is this?
_string[i].t_xpos = 0;
_string[i].t_ypos = 0;
} else {
_string[i].t_xpos = 2;
_string[i].t_ypos = 5;
}
_string[i].t_right = _screenWidth - 1;
_string[i].t_color = 0xF;
_string[i].t_center = 0;
_string[i].t_charset = 0;
}
_numInMsgStack = 0;
createResource(rtTemp, 6, 500);
initScummVars();
// FIXME: disabled, why we need this, it's looks completly dummy and slow down startup
// getGraphicsPerformance();
_lastSaveTime = _system->get_msecs();
}
void Scumm::initScummVars() {
// FIXME
if (_version <= 2) {
// This needs to be at least greater than 40 to get the more
// elaborate version of the EGA Zak into. I don't know where
// else it makes any difference.
VAR(VAR_MACHINE_SPEED) = 0x7FFF;
return;
}
if (_version < 6)
VAR(VAR_V5_TALK_STRING_Y) = -0x50;
if (_version == 8) { // Fixme: How do we deal with non-cd installs?
VAR(VAR_CURRENTDISK) = 1;
VAR(VAR_LANGUAGE) = _language;
} else if (_version >= 7) {
VAR(VAR_V6_EMSSPACE) = 10000;
} else {
VAR(VAR_CURRENTDRIVE) = 0;
VAR(VAR_FIXEDDISK) = true;
VAR(VAR_SOUNDCARD) = 3;
VAR(VAR_VIDEOMODE) = 0x13;
VAR(VAR_HEAPSPACE) = 1400;
VAR(VAR_MOUSEPRESENT) = true; // FIXME - used to be 0, but that seems odd?!?
if (_features & GF_HUMONGOUS)
VAR(VAR_SOUNDPARAM) = 1; // soundblaster for music
else
VAR(VAR_SOUNDPARAM) = 0;
VAR(VAR_SOUNDPARAM2) = 0;
VAR(VAR_SOUNDPARAM3) = 0;
if (_version >= 6 && VAR_V6_EMSSPACE != 0xFF)
VAR(VAR_V6_EMSSPACE) = 10000;
VAR(VAR_59) = 3;
}
if (_gameId == GID_LOOM || _gameId == GID_INDY3) {
// This is the for the Mac version of Indy3/Loom. TODO: add code to properly
// distinguish the Mac version from the PC (and other) versions.
VAR(39) = 320;
}
if (!(_features & GF_NEW_OPCODES)) {
// Setup light
VAR(VAR_CURRENT_LIGHTS) = LIGHTMODE_actor_base | LIGHTMODE_actor_color | LIGHTMODE_screen;
}
VAR(VAR_CHARINC) = 4;
VAR(VAR_TALK_ACTOR) = 0;
}
void Scumm::checkRange(int max, int min, int no, const char *str) const {
if (no < min || no > max) {
#ifdef __PALM_OS__
char buf[256]; // 1024 is too big overflow the stack
#else
char buf[1024];
#endif
sprintf(buf, str, no);
error("Value %d is out of bounds (%d,%d) in script %d (%s)", no, min,
max, vm.slot[_curExecScript].number, buf);
}
}
int Scumm::scummLoop(int delta) {
#ifndef _WIN32_WCE
if (_debugger)
_debugger->on_frame();
#endif
// Randomize the PRNG by calling it at regular intervals. This ensures
// that it will be in a different state each time you run the program.
_rnd.getRandomNumber(2);
if (_version > 2) {
VAR(VAR_TMR_1) += delta;
VAR(VAR_TMR_2) += delta;
VAR(VAR_TMR_3) += delta;
}
if (VAR_TMR_4 != 0xFF)
VAR(VAR_TMR_4) += delta;
if (delta > 15)
delta = 15;
decreaseScriptDelay(delta);
// If _talkDelay is -1, that means the text should never time out.
// This is used for drawing verb texts, e.g. the Full Throttle
// dialogue choices.
if (_talkDelay != -1) {
_talkDelay -= delta;
if (_talkDelay < 0)
_talkDelay = 0;
}
processKbd();
if (_features & GF_NEW_CAMERA) {
VAR(VAR_CAMERA_POS_X) = camera._cur.x;
VAR(VAR_CAMERA_POS_Y) = camera._cur.y;
} else if (_version <= 2) {
VAR(VAR_CAMERA_POS_X) = camera._cur.x / 8;
} else {
VAR(VAR_CAMERA_POS_X) = camera._cur.x;
}
VAR(VAR_HAVE_MSG) = (_haveMsg == 0xFE) ? 0xFF : _haveMsg;
if (_version <= 2) {
VAR(VAR_VIRT_MOUSE_X) = _virtualMouse.x / 8;
VAR(VAR_VIRT_MOUSE_Y) = _virtualMouse.y / 2;
} else {
VAR(VAR_VIRT_MOUSE_X) = _virtualMouse.x;
VAR(VAR_VIRT_MOUSE_Y) = _virtualMouse.y;
VAR(VAR_MOUSE_X) = _mouse.x;
VAR(VAR_MOUSE_Y) = _mouse.y;
if (_gameId != GID_LOOM && _gameId != GID_INDY3) {
// This is the for the Mac version of Indy3/Loom. TODO: add code to properly
// distinguish the Mac version from the PC (and other) versions.
VAR(VAR_DEBUGMODE) = _debugMode;
}
}
if (_features & GF_AUDIOTRACKS) {
// Covered automatically by the Sound class
} else if (_playerV2) {
VAR(VAR_MUSIC_TIMER) = _playerV2->getMusicTimer();
} else if (_features & GF_SMALL_HEADER) {
// TODO: The music delay (given in milliseconds) might have to be tuned a little
// to get it correct for all games. Without the ability to watch/listen to the
// original games, I can't do that myself.
const int MUSIC_DELAY = 300;
tempMusic += delta * 15; // Convert delta to milliseconds
if (tempMusic >= MUSIC_DELAY) {
tempMusic %= MUSIC_DELAY;
VAR(VAR_MUSIC_TIMER) += 1;
}
}
// Trigger autosave all 5 minutes.
if (!_saveLoadFlag && _system->get_msecs() > _lastSaveTime + 5 * 60 * 1000) {
_saveLoadSlot = 0;
sprintf(_saveLoadName, "Autosave %d", _saveLoadSlot);
_saveLoadFlag = 1;
_saveLoadCompatible = false;
}
if (VAR_GAME_LOADED != 0xFF)
VAR(VAR_GAME_LOADED) = 0;
if (_saveLoadFlag) {
load_game:
bool success;
const char *errMsg = 0;
char filename[256];
if (_saveLoadFlag == 1) {
success = saveState(_saveLoadSlot, _saveLoadCompatible);
if (!success)
errMsg = "Failed to save game state to file:\n\n%s";
// Ender: Disabled for small_header games, as can overwrite game
// variables (eg, Zak256 cashcard values). Temp disabled for V8
// because of odd timing issue with scripts and the variable reset
if (success && _saveLoadCompatible && !(_features & GF_SMALL_HEADER) && _version < 8)
VAR(VAR_GAME_LOADED) = 201;
} else {
success = loadState(_saveLoadSlot, _saveLoadCompatible);
if (!success)
errMsg = "Failed to load game state from file:\n\n%s";
// Ender: Disabled for small_header games, as can overwrite game
// variables (eg, Zak256 cashcard values).
if (success && _saveLoadCompatible && !(_features & GF_SMALL_HEADER))
VAR(VAR_GAME_LOADED) = 203;
}
makeSavegameName(filename, _saveLoadSlot, _saveLoadCompatible);
if (!success) {
displayError(false, errMsg, filename);
} else if (_saveLoadFlag == 1 && _saveLoadSlot != 0 && !_saveLoadCompatible) {
// Display "Save succesful" message, except for auto saves
#ifdef __PALM_OS__
char buf[256]; // 1024 is too big overflow the stack
#else
char buf[1024];
#endif
sprintf(buf, "Succesfully saved game state in file:\n\n%s", filename);
Dialog *dialog = new MessageDialog(_newgui, buf, 1500, false);
runDialog(dialog);
delete dialog;
}
if (success && _saveLoadFlag != 1)
clearClickedStatus();
_saveLoadFlag = 0;
_lastSaveTime = _system->get_msecs();
}
int oldEgo = 0;
if (VAR_EGO != 0xFF)
oldEgo = VAR(VAR_EGO);
if (_completeScreenRedraw) {
_completeScreenRedraw = false;
gdi.clearCharsetMask();
_charset->_hasMask = false;
for (int i = 0; i < _maxVerbs; i++)
drawVerb(i, 0);
verbMouseOver(0);
if (_version <= 2) {
redrawV2Inventory();
checkV2MouseOver(_mouse);
}
_verbRedraw = false;
_fullRedraw = true;
}
runAllScripts();
checkExecVerbs();
checkAndRunSentenceScript();
if (_quit)
return 0;
// HACK: If a load was requested, immediately perform it. This avoids
// drawing the current room right after the load is request but before
// it is performed. That was annoying esp. if you loaded while a SMUSH
// cutscene was playing.
if (_saveLoadFlag && _saveLoadFlag != 1) {
goto load_game;
}
if (_currentRoom == 0) {
CHARSET_1();
drawDirtyScreenParts();
} else {
walkActors();
moveCamera();
fixObjectFlags();
CHARSET_1();
if (camera._cur.x != camera._last.x || _BgNeedsRedraw || _fullRedraw
|| ((_features & GF_NEW_CAMERA) && camera._cur.y != camera._last.y)) {
redrawBGAreas();
}
processDrawQue();
if (_verbRedraw) {
redrawVerbs();
}
setActorRedrawFlags();
resetActorBgs();
if (!(_features & GF_NEW_OPCODES) &&
!(VAR(VAR_CURRENT_LIGHTS) & LIGHTMODE_screen) &&
VAR(VAR_CURRENT_LIGHTS) & LIGHTMODE_flashlight) {
drawFlashlight();
setActorRedrawFlags();
}
processActors();
_fullRedraw = false;
cyclePalette();
palManipulate();
if (_doEffect) {
_doEffect = false;
fadeIn(_newEffect);
clearClickedStatus();
}
if (!_verbRedraw && _cursor.state > 0) {
verbMouseOver(checkMouseOver(_mouse.x, _mouse.y));
}
_verbRedraw = false;
if (_version <= 2) {
if (oldEgo != VAR(VAR_EGO)) {
// FIXME: Reset and redraw the sentence line
oldEgo = VAR(VAR_EGO);
redrawV2Inventory();
}
checkV2MouseOver(_mouse);
}
drawBlastTexts();
drawBlastObjects();
if (_version == 8)
processUpperActors();
drawDirtyScreenParts();
removeBlastObjects();
removeBlastTexts();
if (_version <= 5)
playActorSounds();
}
_sound->processSoundQues();
camera._last = camera._cur;
if (!(++_expire_counter)) {
increaseResourceCounter();
}
animateCursor();
/* show or hide mouse */
_system->show_mouse(_cursor.state > 0);
if (VAR_TIMER != 0xFF)
VAR(VAR_TIMER) = 0;
return VAR(VAR_TIMER_NEXT);
}
void Scumm::startScene(int room, Actor * a, int objectNr) {
int i, where;
CHECK_HEAP;
debug(1, "Loading room %d", room);
clearMsgQueue();
fadeOut(_switchRoomEffect2);
_newEffect = _switchRoomEffect;
ScriptSlot *ss = &vm.slot[_currentScript];
if (_currentScript != 0xFF) {
if (ss->where == WIO_ROOM || ss->where == WIO_FLOBJECT) {
if (ss->cutsceneOverride != 0)
error("Object %d stopped with active cutscene/override in exit", ss->number);
_currentScript = 0xFF;
} else if (ss->where == WIO_LOCAL) {
// HACK to make Indy3 Demo work
if (ss->cutsceneOverride != 0 && !(_gameId == GID_INDY3 && _roomResource == 3))
error("Script %d stopped with active cutscene/override in exit", ss->number);
_currentScript = 0xFF;
}
}
if (!(_features & GF_SMALL_HEADER) && VAR_NEW_ROOM != 0xFF) // Disable for SH games. Overwrites
VAR(VAR_NEW_ROOM) = room; // gamevars, eg Zak cashcards
runExitScript();
killScriptsAndResources();
clearEnqueue();
stopCycle(0);
for (i = 1; i < _numActors; i++) {
_actors[i].hideActor();
}
if (_version < 7) {
for (i = 0; i < 256; i++)
_shadowPalette[i] = i;
if (_features & GF_SMALL_HEADER)
setDirtyColors(0, 255);
}
clearDrawObjectQueue();
VAR(VAR_ROOM) = room;
_fullRedraw = true;
increaseResourceCounter();
_currentRoom = room;
VAR(VAR_ROOM) = room;
if (room >= 0x80 && _version < 7)
_roomResource = _resourceMapper[room & 0x7F];
else
_roomResource = room;
if (VAR_ROOM_RESOURCE != 0xFF)
VAR(VAR_ROOM_RESOURCE) = _roomResource;
if (room != 0)
ensureResourceLoaded(rtRoom, room);
clearRoomObjects();
if (_currentRoom == 0) {
_ENCD_offs = _EXCD_offs = 0;
_numObjectsInRoom = 0;
return;
}
initRoomSubBlocks();
if (_features & GF_OLD_BUNDLE)
loadRoomObjectsOldBundle();
else if (_features & GF_SMALL_HEADER)
loadRoomObjectsSmall();
else
loadRoomObjects();
if (!(_features & GF_NEW_CAMERA)) {
camera._mode = CM_NORMAL;
camera._cur.x = camera._dest.x = _screenWidth / 2;
camera._cur.y = camera._dest.y = _screenHeight / 2;
}
if (VAR_V6_SCREEN_WIDTH != 0xFF && VAR_V6_SCREEN_HEIGHT != 0xFF) {
VAR(VAR_V6_SCREEN_WIDTH) = _roomWidth;
VAR(VAR_V6_SCREEN_HEIGHT) = _roomHeight;
}
VAR(VAR_CAMERA_MIN_X) = _screenWidth / 2;
VAR(VAR_CAMERA_MAX_X) = _roomWidth - (_screenWidth / 2);
if (_features & GF_NEW_CAMERA) {
VAR(VAR_CAMERA_MIN_Y) = _screenHeight / 2;
VAR(VAR_CAMERA_MAX_Y) = _roomHeight - (_screenHeight / 2);
setCameraAt(_screenWidth / 2, _screenHeight / 2);
}
if (_roomResource == 0)
return;
memset(gfxUsageBits, 0, sizeof(gfxUsageBits));
if (a) {
where = whereIsObject(objectNr);
if (where != WIO_ROOM && where != WIO_FLOBJECT)
error("startScene: Object %d is not in room %d", objectNr,
_currentRoom);
int x, y, dir;
getObjectXYPos(objectNr, x, y, dir);
a->putActor(x, y, _currentRoom);
a->setDirection(dir + 180);
a->moving = 0;
}
showActors();
_egoPositioned = false;
runEntryScript();
if (_version < 7) {
if (a && !_egoPositioned) {
int x, y;
getObjectXYPos(objectNr, x, y);
a->putActor(x, y, _currentRoom);
a->moving = 0;
}
} else {
if (a) {
// FIXME: This hack mostly is there to fix the tomb/statue room
// in The Dig. What happens there is that when you enter, you are
// placed at object 399, coords (307,141), which is in box 25.
// But then the entry script locks that and other boxes. Hence
// after the entry script runs, you basically can only do one thing
// in that room, and that is to leave it - which means the game
// is unfinishable.
// By calling adjustActorPos, we can solve the problem in this case:
// there is a very close box (box 12) which contains point (307,144).
// If we call adjustActorPos, Commander Low is moved into that box,
// and we can go on. But aqudran looked this up in his IMB DB for
// The DIG; and nothing like this is done there. Also I am pretty
// sure this used to work in 0.3.1. So apparently something broke
// down here, and I have no clue what that might be :-/
a-> adjustActorPos();
}
if (camera._follows) {
a = derefActor(camera._follows, "startScene: follows");
setCameraAt(a->x, a->y);
}
}
_doEffect = true;
CHECK_HEAP;
}
void Scumm::initRoomSubBlocks() {
int i, offs;
const byte *ptr;
byte *roomptr, *searchptr, *roomResPtr;
const RoomHeader *rmhd;
_ENCD_offs = 0;
_EXCD_offs = 0;
_CLUT_offs = 0;
_PALS_offs = 0;
nukeResource(rtMatrix, 1);
nukeResource(rtMatrix, 2);
for (i = 1; i < _maxScaleTable; i++)
nukeResource(rtScaleTable, i);
memset(_localScriptList, 0, sizeof(_localScriptList));
memset(_extraBoxFlags, 0, sizeof(_extraBoxFlags));
// Determine the room and room script base address
roomResPtr = roomptr = getResourceAddress(rtRoom, _roomResource);
if (_version == 8)
roomResPtr = getResourceAddress(rtRoomScripts, _roomResource);
if (!roomptr || !roomResPtr)
error("Room %d: data not found (" __FILE__ ":%d)", _roomResource, __LINE__);
//
// Determine the room dimensions (width/height)
//
if (_features & GF_OLD_BUNDLE)
rmhd = (const RoomHeader *)(roomptr + 4);
else
rmhd = (const RoomHeader *)findResourceData(MKID('RMHD'), roomptr);
if (_version == 1) {
_roomWidth = roomptr[4] * 8;
_roomHeight = roomptr[5] * 8;
} else if (_version == 8) {
_roomWidth = READ_LE_UINT32(&(rmhd->v8.width));
_roomHeight = READ_LE_UINT32(&(rmhd->v8.height));
} else if (_version == 7) {
_roomWidth = READ_LE_UINT16(&(rmhd->v7.width));
_roomHeight = READ_LE_UINT16(&(rmhd->v7.height));
} else {
_roomWidth = READ_LE_UINT16(&(rmhd->old.width));
_roomHeight = READ_LE_UINT16(&(rmhd->old.height));
}
//
// Find the room image data
//
if (_version == 1) {
_IM00_offs = 0;
for (i = 0; i < 4; i++){
gdi._C64Colors[i] = roomptr[6 + i];
}
gdi.decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 10), gdi._C64CharMap, 2048);
gdi.decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 12), gdi._C64PicMap, roomptr[4] * roomptr[5]);
gdi.decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 14), gdi._C64ColorMap, roomptr[4] * roomptr[5]);
gdi.decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 16), gdi._C64MaskMap, roomptr[4] * roomptr[5]);
gdi.decodeC64Gfx(roomptr + READ_LE_UINT16(roomptr + 18), gdi._C64MaskChar, READ_LE_UINT16(roomptr + READ_LE_UINT16(roomptr + 18)));
gdi._C64ObjectMode = true;
} else if (_features & GF_OLD_BUNDLE) {
_IM00_offs = READ_LE_UINT16(roomptr + 0x0A);
if (_version == 2)
_roomStrips = gdi.generateStripTable(roomptr + _IM00_offs, _roomWidth, _roomHeight, _roomStrips);
} else if (_version == 8) {
_IM00_offs = getObjectImage(roomptr, 1) - roomptr;
} else if (_features & GF_SMALL_HEADER) {
_IM00_offs = findResourceData(MKID('IM00'), roomptr) - roomptr;
} else {
_IM00_offs = findResource(MKID('IM00'), findResource(MKID('RMIM'), roomptr)) - roomptr;
}
//
// Look for an exit script
//
int EXCD_len = -1;
if (_version <= 2) {
_EXCD_offs = READ_LE_UINT16(roomptr + 0x18);
EXCD_len = READ_LE_UINT16(roomptr + 0x1A) - _EXCD_offs + _resourceHeaderSize; // HACK
} else if (_features & GF_OLD_BUNDLE) {
_EXCD_offs = READ_LE_UINT16(roomptr + 0x19);
EXCD_len = READ_LE_UINT16(roomptr + 0x1B) - _EXCD_offs + _resourceHeaderSize; // HACK
} else {
ptr = findResourceData(MKID('EXCD'), roomResPtr);
if (ptr)
_EXCD_offs = ptr - roomResPtr;
}
if (_dumpScripts && _EXCD_offs)
dumpResource("exit-", _roomResource, roomResPtr + _EXCD_offs - _resourceHeaderSize, EXCD_len);
//
// Look for an entry script
//
int ENCD_len = -1;
if (_version <= 2) {
_ENCD_offs = READ_LE_UINT16(roomptr + 0x1A);
ENCD_len = READ_LE_UINT16(roomptr) - _ENCD_offs + _resourceHeaderSize; // HACK
} else if (_features & GF_OLD_BUNDLE) {
_ENCD_offs = READ_LE_UINT16(roomptr + 0x1B);
// FIXME - the following is a hack which assumes that immediately after
// the entry script the first local script follows.
int num_objects = *(roomResPtr + 20);
int num_sounds = *(roomResPtr + 23);
int num_scripts = *(roomResPtr + 24);
ptr = roomptr + 29 + num_objects * 4 + num_sounds + num_scripts;
ENCD_len = READ_LE_UINT16(ptr + 1) - _ENCD_offs + _resourceHeaderSize; // HACK
} else {
ptr = findResourceData(MKID('ENCD'), roomResPtr);
if (ptr)
_ENCD_offs = ptr - roomResPtr;
}
if (_dumpScripts && _ENCD_offs)
dumpResource("entry-", _roomResource, roomResPtr + _ENCD_offs - _resourceHeaderSize, ENCD_len);
//
// Load box data
//
if (_features & GF_SMALL_HEADER) {
if (_version <= 2)
ptr = roomptr + *(roomptr + 0x15);
else if (_features & GF_OLD_BUNDLE)
ptr = roomptr + READ_LE_UINT16(roomptr + 0x15);
else
ptr = findResourceData(MKID('BOXD'), roomptr);
if (ptr) {
byte numOfBoxes = *ptr;
int size;
if (_version <= 2)
size = numOfBoxes * SIZEOF_BOX_V2 + 1;
else if (_version == 3)
size = numOfBoxes * SIZEOF_BOX_V3 + 1;
else
size = numOfBoxes * SIZEOF_BOX + 1;
createResource(rtMatrix, 2, size);
memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
ptr += size;
if (_version <= 2) {
size = numOfBoxes * (numOfBoxes + 1);
} else if (_features & GF_OLD_BUNDLE)
// FIXME. This is an evil HACK!!!
size = (READ_LE_UINT16(roomptr + 0x0A) - READ_LE_UINT16(roomptr + 0x15)) - size;
else
size = getResourceDataSize(ptr - size - 6) - size;
if (size >= 0) { // do this :)
createResource(rtMatrix, 1, size);
memcpy(getResourceAddress(rtMatrix, 1), ptr, size);
}
}
} else {
ptr = findResourceData(MKID('BOXD'), roomptr);
if (ptr) {
int size = getResourceDataSize(ptr);
createResource(rtMatrix, 2, size);
roomptr = getResourceAddress(rtRoom, _roomResource);
ptr = findResourceData(MKID('BOXD'), roomptr);
memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
}
ptr = findResourceData(MKID('BOXM'), roomptr);
if (ptr) {
int size = getResourceDataSize(ptr);
createResource(rtMatrix, 1, size);
roomptr = getResourceAddress(rtRoom, _roomResource);
ptr = findResourceData(MKID('BOXM'), roomptr);
memcpy(getResourceAddress(rtMatrix, 1), ptr, size);
}
}
//
// Load scale data
//
if (_features & GF_OLD_BUNDLE)
ptr = 0; // TODO ?
else
ptr = findResourceData(MKID('SCAL'), roomptr);
if (ptr) {
offs = ptr - roomptr;
int s1, s2, y1, y2;
if (_version == 8) {
for (i = 1; i < _maxScaleTable; i++, offs += 16) {
s1 = READ_LE_UINT32(roomptr + offs);
y1 = READ_LE_UINT32(roomptr + offs + 4);
s2 = READ_LE_UINT32(roomptr + offs + 8);
y2 = READ_LE_UINT32(roomptr + offs + 12);
setScaleSlot(i, 0, y1, s1, 0, y2, s2);
}
} else {
for (i = 1; i < _maxScaleTable; i++, offs += 8) {
s1 = READ_LE_UINT16(roomptr + offs);
y1 = READ_LE_UINT16(roomptr + offs + 2);
s2 = READ_LE_UINT16(roomptr + offs + 4);
y2 = READ_LE_UINT16(roomptr + offs + 6);
if (s1 || y1 || s2 || y2) {
setScaleItem(i, y1, s1, y2, s2);
roomptr = getResourceAddress(rtRoom, _roomResource);
}
}
}
}
//
// Setup local scripts
//
// Determine the room script base address
roomResPtr = roomptr = getResourceAddress(rtRoom, _roomResource);
if (_version == 8)
roomResPtr = getResourceAddress(rtRoomScripts, _roomResource);
searchptr = roomResPtr;
if (_features & GF_OLD_BUNDLE) {
int num_objects = *(roomResPtr + 20);
int num_sounds;
int num_scripts;
if (_version <= 2) {
num_sounds = *(roomResPtr + 22);
num_scripts = *(roomResPtr + 23);
ptr = roomptr + 28 + num_objects * 4;
while (num_sounds--)
loadResource(rtSound, *ptr++);
while (num_scripts--)
loadResource(rtScript, *ptr++);
} else if (_version == 3) {
num_sounds = *(roomResPtr + 23);
num_scripts = *(roomResPtr + 24);
ptr = roomptr + 29 + num_objects * 4 + num_sounds + num_scripts;
while (*ptr) {
int id = *ptr;
_localScriptList[id - _numGlobalScripts] = READ_LE_UINT16(ptr + 1);
ptr += 3;
if (_dumpScripts) {
char buf[32];
sprintf(buf, "room-%d-", _roomResource);
// HACK: to determine the sizes of the local scripts, we assume that
// a) their order in the data file is the same as in the index
// b) the last script at the same time is the last item in the room "header"
int len = - (int)_localScriptList[id - _numGlobalScripts] + _resourceHeaderSize;
if (*ptr)
len += READ_LE_UINT16(ptr + 1);
else
len += READ_LE_UINT16(roomResPtr);
dumpResource(buf, id, roomResPtr + _localScriptList[id - _numGlobalScripts] - _resourceHeaderSize, len);
}
}
}
} else if (_features & GF_SMALL_HEADER) {
ResourceIterator localScriptIterator(searchptr, true);
while ((ptr = localScriptIterator.findNext(MKID('LSCR'))) != NULL) {
int id = 0;
ptr += _resourceHeaderSize; /* skip tag & size */
id = ptr[0];
if (_dumpScripts) {
char buf[32];
sprintf(buf, "room-%d-", _roomResource);
dumpResource(buf, id, ptr - _resourceHeaderSize);
}
_localScriptList[id - _numGlobalScripts] = ptr + 1 - roomptr;
}
} else {
ResourceIterator localScriptIterator(searchptr, false);
while ((ptr = localScriptIterator.findNext(MKID('LSCR'))) != NULL) {
int id = 0;
ptr += _resourceHeaderSize; /* skip tag & size */
if (_version == 8) {
id = READ_LE_UINT32(ptr);
checkRange(NUM_LOCALSCRIPT + _numGlobalScripts, _numGlobalScripts, id, "Invalid local script %d");
_localScriptList[id - _numGlobalScripts] = ptr + 4 - roomResPtr;
} else if (_version == 7) {
id = READ_LE_UINT16(ptr);
checkRange(NUM_LOCALSCRIPT + _numGlobalScripts, _numGlobalScripts, id, "Invalid local script %d");
_localScriptList[id - _numGlobalScripts] = ptr + 2 - roomResPtr;
} else {
id = ptr[0];
_localScriptList[id - _numGlobalScripts] = ptr + 1 - roomResPtr;
}
if (_dumpScripts) {
char buf[32];
sprintf(buf, "room-%d-", _roomResource);
dumpResource(buf, id, ptr - _resourceHeaderSize);
}
}
}
if (_features & GF_OLD_BUNDLE)
ptr = 0; // TODO ? do 16 bit games use a palette?!?
else if (_features & GF_SMALL_HEADER)
ptr = findResourceSmall(MKID('CLUT'), roomptr);
else
ptr = findResourceData(MKID('CLUT'), roomptr);
if (ptr) {
_CLUT_offs = ptr - roomptr;
setPaletteFromRes();
}
if (_version >= 6) {
ptr = findResource(MKID('PALS'), roomptr);
if (ptr) {
_PALS_offs = ptr - roomptr;
setPalette(0);
}
}
// Color cycling
if (_features & GF_OLD_BUNDLE)
ptr = 0; // TODO / FIXME ???
else
ptr = findResourceData(MKID('CYCL'), roomptr);
if (ptr)
initCycl(ptr);
// Transparent color
if (_features & GF_OLD_BUNDLE)
gdi._transparentColor = 255; // TODO - FIXME
else {
ptr = findResourceData(MKID('TRNS'), roomptr);
if (ptr)
gdi._transparentColor = ptr[0];
else if (_version == 8)
gdi._transparentColor = 5; // FIXME
else
gdi._transparentColor = 255;
}
initBGBuffers(_roomHeight);
}
/*
FIXME: It seems that scale items and scale slots are the same thing after
all - they only differ in some details (scale item is used to precompute
a scale table, while for the scale slots the computations are done on the
fly; also for scale slots, the scale along the x axis can vary, too).
Now, there are various known scale glitches in FT (and apparently also
in The Dig, see FIXME comments in Actor::setupActorScale). In this context
it is very interesting that for V5, there is an opcode which invokes
setScaleItem, and for V8 one that invokes setScaleSlot. But there is no
such opcode to be found for V6/V7 games.
Hypothesis: we simple are missing this opcode, and implementing it might
fix some or all of the Dig/FT scaling issues.
*/
void Scumm::setScaleItem(int slot, int y1, int scale1, int y2, int scale2) {
byte *ptr;
int y, tmp;
if (y1 == y2)
return;
ptr = createResource(rtScaleTable, slot, 200);
for (y = 0; y < 200; y++) {
tmp = ((scale2 - scale1) * (y - y1)) / (y2 - y1) + scale1;
if (tmp < 1)
tmp = 1;
if (tmp > 255)
tmp = 255;
*ptr++ = tmp;
}
}
void Scumm::setScaleSlot(int slot, int x1, int y1, int scale1, int x2, int y2, int scale2) {
assert(1 <= slot && slot <= 20);
_scaleSlots[slot-1].x2 = x2;
_scaleSlots[slot-1].y2 = y2;
_scaleSlots[slot-1].scale2 = scale2;
_scaleSlots[slot-1].x1 = x1;
_scaleSlots[slot-1].y1 = y1;
_scaleSlots[slot-1].scale1 = scale1;
}
void Scumm::dumpResource(const char *tag, int idx, const byte *ptr, int length) {
char buf[256];
File out;
uint32 size;
if (length >= 0)
size = length;
else if (_features & GF_OLD_BUNDLE)
size = READ_LE_UINT16(ptr);
else if (_features & GF_SMALL_HEADER)
size = READ_LE_UINT32(ptr);
else
size = READ_BE_UINT32(ptr + 4);
#if defined(MACOS_CARBON)
sprintf(buf, ":dumps:%s%d.dmp", tag, idx);
#else
sprintf(buf, "dumps/%s%d.dmp", tag, idx);
#endif
out.open(buf, "", 1);
if (out.isOpen() == false) {
out.open(buf, "", 2);
if (out.isOpen() == false)
return;
out.write(ptr, size);
}
out.close();
}
void Scumm::clearClickedStatus() {
checkKeyHit();
_mouseButStat = 0;
_leftBtnPressed &= ~msClicked;
_rightBtnPressed &= ~msClicked;
}
int Scumm::checkKeyHit() {
int a = _keyPressed;
_keyPressed = 0;
if (_version <= 2 && 315 <= a && a < 315+12) {
// Convert F-Keys
a -= 314;
}
return a;
}
void Scumm::pauseGame() {
pauseDialog();
}
void Scumm::setOptions() {
//_newgui->optionsDialog();
}
int Scumm::runDialog(Dialog *dialog) {
// Pause sound put
bool old_soundsPaused = _sound->_soundsPaused;
_sound->pauseSounds(true);
// Pause playing smush movie
bool oldSmushPlay = _smushPlay;
_smushPlay = false;
// Open & run the dialog
int result = dialog->runModal();
// Restore old cursor
updateCursor();
// Resume playing smush movie, if any
_smushPlay = oldSmushPlay;
// Resume sound output
_sound->pauseSounds(old_soundsPaused);
// Return the result
return result;
}
void Scumm::pauseDialog() {
if (!_pauseDialog)
_pauseDialog = new PauseDialog(_newgui, this);
runDialog(_pauseDialog);
}
void Scumm::saveloadDialog() {
if (!_saveLoadDialog)
_saveLoadDialog = new SaveLoadDialog(_newgui, this);
runDialog(_saveLoadDialog);
}
void Scumm::optionsDialog() {
if (!_optionsDialog)
_optionsDialog = new OptionsDialog(_newgui, this);
runDialog(_optionsDialog);
}
char Scumm::displayError(bool showCancel, const char *message, ...) {
#ifdef __PALM_OS__
char buf[256], result; // 1024 is too big overflow the stack
#else
char buf[1024], result;
#endif
va_list va;
va_start(va, message);
vsprintf(buf, message, va);
va_end(va);
Dialog *dialog = new MessageDialog(_newgui, buf, 0, true, showCancel);
result = runDialog(dialog);
delete dialog;
return result;
}
void Scumm::shutDown() {
_quit = true;
}
void Scumm::restart() {
// TODO: Check this function - we should probably be reinitting a lot more stuff, and I suspect
// this leaks memory like a sieve
int i;
// Reset some stuff
_currentRoom = 0;
_currentScript = 0xFF;
killAllScriptsExceptCurrent();
setShake(0);
_sound->stopAllSounds();
// Empty variables
for (i=0;i<255;i++)
_scummVars[i] = 0;
// Empty inventory
for (i=0;i<_numGlobalObjects;i++)
clearOwnerOf(i);
// Reinit things
allocateArrays(); // Reallocate arrays
readIndexFile(); // Reread index (reset objectstate etc)
createResource(rtTemp, 6, 500); // Create temp buffer
initScummVars(); // Reinit scumm variables
_sound->setupSound(); // Reinit sound engine
// Re-run bootscript
runScript(1, 0, 0, &_bootParam);
}
void Scumm::processKbd() {
int saveloadkey;
getKeyInput();
if (_version <= 2)
saveloadkey = 5; // F5
else if ((_features & GF_OLD256) || (_gameId == GID_CMI) || (_features & GF_16COLOR)) /* FIXME: Support ingame screen ? */
saveloadkey = 319; // F5
else
saveloadkey = VAR(VAR_SAVELOADDIALOG_KEY);
_virtualMouse.x = _mouse.x + virtscr[0].xstart;
if (_features & GF_NEW_CAMERA)
_virtualMouse.y = _mouse.y + camera._cur.y - (_screenHeight / 2);
else
_virtualMouse.y = _mouse.y;
_virtualMouse.y -= virtscr[0].topline;
if (_virtualMouse.y < 0)
_virtualMouse.y = -1;
if (_virtualMouse.y >= virtscr[0].height)
_virtualMouse.y = -1;
if (!_lastKeyHit)
return;
if (_keyScriptNo && (_keyScriptKey == _lastKeyHit)) {
runScript(_keyScriptNo, 0, 0, 0);
return;
}
if (_lastKeyHit == KEY_SET_OPTIONS) {
setOptions();
return;
}
if (VAR_RESTART_KEY != 0xFF && _lastKeyHit == VAR(VAR_RESTART_KEY)) {
warning("Restart not implemented");
//restart();
return;
}
if ((VAR_PAUSE_KEY != 0xFF && _lastKeyHit == VAR(VAR_PAUSE_KEY)) ||
(VAR_PAUSE_KEY == 0xFF && _lastKeyHit == ' ')) {
pauseGame();
return;
}
if (_lastKeyHit == VAR(VAR_CUTSCENEEXIT_KEY) ||
(VAR(VAR_CUTSCENEEXIT_KEY) == 4 && _lastKeyHit == 27)) {
if (_insaneState) {
_videoFinished = true;
} else
abortCutscene();
} else if (_lastKeyHit == saveloadkey && _currentRoom != 0) {
if (VAR_SAVELOAD_SCRIPT != 0xFF)
runScript(VAR(VAR_SAVELOAD_SCRIPT), 0, 0, 0);
saveloadDialog(); // Display NewGui
if (VAR_SAVELOAD_SCRIPT2 != 0xFF)
runScript(VAR(VAR_SAVELOAD_SCRIPT2), 0, 0, 0);
return;
} else if (VAR_TALKSTOP_KEY != 0xFF && _lastKeyHit == VAR(VAR_TALKSTOP_KEY)) {
_talkDelay = 0;
if (_sound->_sfxMode & 2)
stopTalk();
return;
} else if (_lastKeyHit == '[') { // [ Music volume down
int vol = _sound->_sound_volume_music;
if (!(vol & 0xF) && vol)
vol -= 16;
vol = vol & 0xF0;
_sound->_sound_volume_music = vol;
if (_imuse)
_imuse->set_music_volume (vol);
} else if (_lastKeyHit == ']') { // ] Music volume up
int vol = _sound->_sound_volume_music;
vol = (vol + 16) & 0xFF0;
if (vol > 255) vol = 255;
_sound->_sound_volume_music = vol;
if (_imuse)
_imuse->set_music_volume (vol);
} else if (_lastKeyHit == '-') { // - text speed down
_defaultTalkDelay += 5;
if (_defaultTalkDelay > 90)
_defaultTalkDelay = 90;
VAR(VAR_CHARINC) = _defaultTalkDelay / 20;
} else if (_lastKeyHit == '+') { // + text speed up
_defaultTalkDelay -= 5;
if (_defaultTalkDelay < 5)
_defaultTalkDelay = 5;
VAR(VAR_CHARINC) = _defaultTalkDelay / 20;
} else if (_lastKeyHit == '~' || _lastKeyHit == '#') { // Debug console
g_debugger->attach(this, NULL);
} else if (_version <= 2) {
// Store the input type. So far we can't distinguish
// between 1, 3 and 5.
// 1) Verb 2) Scene 3) Inv. 4) Key
// 5) Sentence Bar
if (_lastKeyHit) { // Key Input
VAR(VAR_KEYPRESS) = _lastKeyHit;
runInputScript(4, 0, 0);
}
}
_mouseButStat = _lastKeyHit;
}
int Scumm::getKeyInput() {
_mouseButStat = 0;
_lastKeyHit = checkKeyHit();
convertKeysToClicks();
if (_mouse.x < 0)
_mouse.x = 0;
if (_mouse.x > _screenWidth-1)
_mouse.x = _screenWidth-1;
if (_mouse.y < 0)
_mouse.y = 0;
if (_mouse.y > _screenHeight-1)
_mouse.y = _screenHeight-1;
if (_leftBtnPressed & msClicked && _rightBtnPressed & msClicked) {
_mouseButStat = 0;
_lastKeyHit = (uint)VAR(VAR_CUTSCENEEXIT_KEY);
} else if (_leftBtnPressed & msClicked) {
_mouseButStat = MBS_LEFT_CLICK;
} else if (_rightBtnPressed & msClicked) {
_mouseButStat = MBS_RIGHT_CLICK;
}
if (_version == 8) {
VAR(VAR_MOUSE_BUTTONS) = 0;
VAR(VAR_MOUSE_HOLD) = 0;
VAR(VAR_RIGHTBTN_HOLD) = 0;
if (_leftBtnPressed & msClicked)
VAR(VAR_MOUSE_BUTTONS) += 1;
if (_rightBtnPressed & msClicked)
VAR(VAR_MOUSE_BUTTONS) += 2;
if (_leftBtnPressed & msDown)
VAR(VAR_MOUSE_HOLD) += 1;
if (_rightBtnPressed & msDown) {
VAR(VAR_RIGHTBTN_HOLD) = 1;
VAR(VAR_MOUSE_HOLD) += 2;
}
} else if (_version == 7) {
VAR(VAR_LEFTBTN_HOLD) = (_leftBtnPressed & msDown) != 0;
VAR(VAR_RIGHTBTN_HOLD) = (_rightBtnPressed & msDown) != 0;
}
_leftBtnPressed &= ~msClicked;
_rightBtnPressed &= ~msClicked;
return _lastKeyHit;
}
void Scumm::convertKeysToClicks() {
if (_lastKeyHit && _cursor.state > 0) {
if (_lastKeyHit == 9) {
_mouseButStat = MBS_RIGHT_CLICK;
} else if (_lastKeyHit == 13) {
_mouseButStat = MBS_LEFT_CLICK;
} else
return;
_lastKeyHit = 0;
}
}
Actor *Scumm::derefActor(int id, const char *errmsg) const {
if (id < 1 || id >= _numActors || _actors[id].number != id) {
if (errmsg)
error("Invalid actor %d in %s", id, errmsg);
else
error("Invalid actor %d", id);
}
return &_actors[id];
}
Actor *Scumm::derefActorSafe(int id, const char *errmsg) const {
if (id < 1 || id >= _numActors || _actors[id].number != id) {
debug(2, "Invalid actor %d in %s (script %d, opcode 0x%x) - This is potentially a BIG problem.",
id, errmsg, vm.slot[_curExecScript].number, _opcode);
return NULL;
}
return &_actors[id];
}
void Scumm::setStringVars(int slot) {
StringTab *st = &_string[slot];
st->xpos = st->t_xpos;
st->ypos = st->t_ypos;
st->center = st->t_center;
st->overhead = st->t_overhead;
st->no_talk_anim = st->t_no_talk_anim;
st->right = st->t_right;
st->color = st->t_color;
st->charset = st->t_charset;
}
void Scumm::startManiac() {
warning("stub startManiac()");
}
//
// Convert an old style direction to a new style one (angle),
//
int newDirToOldDir(int dir) {
if (dir >= 71 && dir <= 109)
return 1;
if (dir >= 109 && dir <= 251)
return 2;
if (dir >= 251 && dir <= 289)
return 0;
return 3;
}
//
// Convert an new style (angle) direction to an old style one.
//
int oldDirToNewDir(int dir) {
assert(0 <= dir && dir <= 3);
const int new_dir_table[4] = { 270, 90, 180, 0 };
return new_dir_table[dir];
}
//
// Convert an angle to a simple direction.
//
int toSimpleDir(int dirType, int dir) {
if (dirType) {
const int16 directions[] = { 22, 72, 107, 157, 202, 252, 287, 337 };
for (int i = 0; i < 7; i++)
if (dir >= directions[i] && dir <= directions[i+1])
return i+1;
} else {
const int16 directions[] = { 71, 109, 251, 289 };
for (int i = 0; i < 3; i++)
if (dir >= directions[i] && dir <= directions[i+1])
return i+1;
}
return 0;
}
//
// Convert a simple direction to an angle
//
int fromSimpleDir(int dirType, int dir) {
if (dirType)
return dir * 45;
else
return dir * 90;
}
//
// Normalize the given angle - that means, ensure it is positive, and
// change it to the closest multiple of 45 degree by abusing toSimpleDir.
//
int normalizeAngle(int angle) {
int temp;
temp = (angle + 360) % 360;
return toSimpleDir(1, temp) * 45;
}
const char *Scumm::getGameDataPath() const {
#ifdef MACOSX
if (_version == 8 && !memcmp(_gameDataPath, "/Volumes/MONKEY3_", 17)) {
// Special case for COMI on Mac OS X. The mount points on OS X depend
// on the volume name. Hence if playing from CD, we'd get a problem.
// So if loading of a resource file fails, we fall back to the (fixed)
// CD mount points (/Volumes/MONKEY3_1 and /Volumes/MONKEY3_2).
//
// The check for whether we play from CD or not is very hackish, though.
static char buf[256];
struct stat st;
int disk = (_scummVars && _scummVars[VAR_CURRENTDISK] == 2) ? 2 : 1;
sprintf(buf, "/Volumes/MONKEY3_%d", disk);
if (!stat(buf, &st)) {
return buf;
}
// Apparently that disk is not inserted. However since many data files
// (fonts, comi.la0) are on both disks, we also try the other CD.
disk = (disk == 1) ? 2 : 1;
sprintf(buf, "/Volumes/MONKEY3_%d", disk);
return buf;
}
#endif
return _gameDataPath;
}
void Scumm::errorString(const char *buf1, char *buf2) {
if (_currentScript != 0xFF) {
ScriptSlot *ss = &vm.slot[_currentScript];
sprintf(buf2, "(%d:%d:0x%X): %s", _roomResource,
ss->number, _scriptPointer - _scriptOrgPointer, buf1);
} else {
strcpy(buf2, buf1);
}
// Unless an error -originated- within the debugger, spawn the debugger. Otherwise
// exit out normally.
if (!_debugger) {
printf("%s", buf2); // (Print it again in-case debugger segfaults)
g_debugger->attach(this, buf2);
g_debugger->on_frame();
}
}
void Scumm::waitForTimer(int msec_delay) {
uint32 start_time;
if (_fastMode & 2)
msec_delay = 0;
else if (_fastMode & 1)
msec_delay = 10;
start_time = _system->get_msecs();
while (!_quit) {
parseEvents();
_sound->updateCD(); // Loop CD Audio if needed
if (_system->get_msecs() >= start_time + msec_delay)
break;
_system->delay_msecs(10);
}
}
void Scumm::parseEvents() {
OSystem::Event event;
while (_system->poll_event(&event)) {
switch(event.event_code) {
case OSystem::EVENT_KEYDOWN:
if (event.kbd.keycode >= '0' && event.kbd.keycode<='9'
&& (event.kbd.flags == OSystem::KBD_ALT ||
event.kbd.flags == OSystem::KBD_CTRL)) {
_saveLoadSlot = event.kbd.keycode - '0';
// don't overwrite autosave (slot 0)
if (_saveLoadSlot == 0)
_saveLoadSlot = 10;
sprintf(_saveLoadName, "Quicksave %d", _saveLoadSlot);
_saveLoadFlag = (event.kbd.flags == OSystem::KBD_ALT) ? 1 : 2;
_saveLoadCompatible = false;
} else if (event.kbd.flags==OSystem::KBD_CTRL) {
if (event.kbd.keycode == 'f')
_fastMode ^= 1;
else if (event.kbd.keycode == 'g')
_fastMode ^= 2;
else if ((event.kbd.keycode == 'd') && (!_system->property(OSystem::PROP_GET_FULLSCREEN, 0)))
g_debugger->attach(this, NULL);
else if (event.kbd.keycode == 's')
resourceStats();
else
_keyPressed = event.kbd.ascii; // Normal key press, pass on to the game.
} else if (event.kbd.flags & OSystem::KBD_ALT) {
// The result must be 273 for Alt-W
// because that's what MI2 looks for in
// its "instant win" cheat.
_keyPressed = event.kbd.keycode + 154;
} else
_keyPressed = event.kbd.ascii; // Normal key press, pass on to the game.
break;
case OSystem::EVENT_MOUSEMOVE:
_mouse.x = event.mouse.x;
_mouse.y = event.mouse.y;
_system->set_mouse_pos(event.mouse.x, event.mouse.y);
_system->update_screen();
break;
case OSystem::EVENT_LBUTTONDOWN:
_leftBtnPressed |= msClicked|msDown;
#if defined(_WIN32_WCE) || defined(__PALM_OS__)
_mouse.x = event.mouse.x;
_mouse.y = event.mouse.y;
#endif
break;
case OSystem::EVENT_RBUTTONDOWN:
_rightBtnPressed |= msClicked|msDown;
#if defined(_WIN32_WCE) || defined(__PALM_OS__)
_mouse.x = event.mouse.x;
_mouse.y = event.mouse.y;
#endif
break;
case OSystem::EVENT_LBUTTONUP:
_leftBtnPressed &= ~msDown;
break;
case OSystem::EVENT_RBUTTONUP:
_rightBtnPressed &= ~msDown;
break;
case OSystem::EVENT_QUIT:
_quit = true;
break;
default:
break;
}
}
}
void Scumm::updatePalette() {
if (_palDirtyMax == -1)
return;
bool noir_mode = (_gameId == GID_SAMNMAX && readVar(0x8000));
int first = _palDirtyMin;
int num = _palDirtyMax - first + 1;
int i;
byte palette_colors[1024];
byte *p = palette_colors;
for (i = _palDirtyMin; i <= _palDirtyMax; i++) {
byte *data;
if (_features & GF_SMALL_HEADER)
data = _currentPalette + _shadowPalette[i] * 3;
else
data = _currentPalette + i * 3;
// Sam & Max film noir mode. Convert the colours to grayscale
// before uploading them to the backend.
if (noir_mode) {
int r, g, b;
byte brightness;
r = data[0];
g = data[1];
b = data[2];
brightness = (byte)((0.299 * r + 0.587 * g + 0.114 * b) + 0.5);
*p++ = brightness;
*p++ = brightness;
*p++ = brightness;
*p++ = 0;
} else {
*p++ = data[0];
*p++ = data[1];
*p++ = data[2];
*p++ = 0;
}
}
_system->set_palette(palette_colors, first, num);
_palDirtyMax = -1;
_palDirtyMin = 256;
}
void Scumm::mainRun() {
int delta = 0;
int diff = _system->get_msecs();
while (!_quit) {
updatePalette();
_system->update_screen();
diff -= _system->get_msecs();
waitForTimer(delta * 15 + diff);
diff = _system->get_msecs();
delta = scummLoop(delta);
if (delta < 1) // Ensure we don't get into a loop
delta = 1; // by not decreasing sleepers.
if (_quit) {
// TODO: Maybe perform an autosave on exit?
// TODO: Also, we could optionally show a "Do you really want to quit?" dialog here
}
}
}
void Scumm::launch() {
gdi._vm = this;
#ifdef __PALM_OS__
// PALMOS : check if this value is correct with palm,
// old value 450000 doesn't work anymore (return _fntPtr = NULL in zak256, not tested with others)
// 2500000 is too big and make ScummVM crashes : MemMove to NULL or immediate exit if try to allocate
// memory with new operator
_maxHeapThreshold = 550000;
#else
// Since the new costumes are very big, we increase the heap limit, to avoid having
// to constantly reload stuff from the data files.
if (_features & GF_NEW_COSTUMES)
_maxHeapThreshold = 2500000;
else
_maxHeapThreshold = 550000;
#endif
_minHeapThreshold = 400000;
_verbRedraw = false;
allocResTypeData(rtBuffer, MKID('NONE'), 10, "buffer", 0);
initVirtScreen(0, 0, 0, _screenWidth, _screenHeight, false, false);
setupScummVars();
setupOpcodes();
if (_version == 8)
_numActors = 80;
else if ((_version == 7) || (_gameId == GID_SAMNMAX))
_numActors = 30;
else if (_gameId == GID_MANIAC)
_numActors = 25;
else
_numActors = 13;
if (_version >= 7)
OF_OWNER_ROOM = 0xFF;
else
OF_OWNER_ROOM = 0x0F;
// if (_gameId==GID_MONKEY2 && _bootParam == 0)
// _bootParam = 10001;
if (_gameId == GID_INDY4 && _bootParam == 0) {
_bootParam = -7873;
}
if (_features & GF_OLD_BUNDLE)
_resourceHeaderSize = 4; // FIXME - to be rechecked
else if (_features & GF_SMALL_HEADER)
_resourceHeaderSize = 6;
else
_resourceHeaderSize = 8;
readIndexFile();
scummInit();
if (_version > 2) {
if (_version < 7)
VAR(VAR_VERSION) = 21;
if (_gameId != GID_LOOM && _gameId != GID_INDY3) {
// This is the for the Mac version of Indy3/Loom. TODO: add code to properly
// distinguish the Mac version from the PC (and other) versions.
VAR(VAR_DEBUGMODE) = _debugMode;
}
}
if (_gameId == GID_MONKEY || _gameId == GID_MONKEY_SEGA)
_scummVars[74] = 1225;
_sound->setupSound();
// If requested, load a save game instead of running the boot script
if (_saveLoadFlag != 2 || !loadState(_saveLoadSlot, _saveLoadCompatible)) {
runScript(1, 0, 0, &_bootParam);
}
_saveLoadFlag = 0;
}
void Scumm::go() {
launch();
mainRun();
}