scummvm/scumm/scummvm.cpp

1813 lines
47 KiB
C++
Raw Normal View History

2001-10-09 14:30:12 +00:00
/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 Ludvig Strigeus
* Copyright (C) 2001-2003 The ScummVM project
2001-10-09 14:30:12 +00:00
*
* 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$
2001-10-09 14:30:12 +00:00
*
*/
#include "stdafx.h"
#include "scumm.h"
#include "actor.h"
#include "bundle.h"
#include "charset.h"
#include "debugger.h"
#include "dialogs.h"
#include "imuse.h"
2003-03-18 16:13:52 +00:00
#include "imuse_digi.h"
#include "intern.h"
#include "object.h"
#include "resource.h"
#include "sound.h"
#include "string.h"
2002-11-30 16:23:54 +00:00
#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"
2001-10-09 14:30:12 +00:00
#ifdef _WIN32_WCE
extern void drawError(char*);
#endif
// Use g_scumm from error() ONLY
Scumm *g_scumm = 0;
ScummDebugger g_debugger;
extern NewGui *g_gui;
extern uint16 _debugLevel;
static const VersionSettings scumm_settings[] = {
/* Scumm Version 1 */
// {"maniac64", "Maniac Mansion (C64)", GID_MANIAC64, 1, 0, 0,},
// {"zak64", "Zak McKracken and the Alien Mindbenders (C64)", GID_ZAK64, 1, 0, 0,},
/* Scumm Version 2 */
2003-03-06 17:58:13 +00:00
{"maniac", "Maniac Mansion", GID_MANIAC, 2, 0, 0,
GF_AFTER_V2 | 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, 0, 0,
GF_AFTER_V2 | GF_SMALL_HEADER | GF_USE_KEY | GF_SMALL_NAMES | GF_16COLOR | GF_OLD_BUNDLE | GF_NO_SCALLING, "00.LFL"},
/* Scumm Version 3 */
2003-04-10 06:58:23 +00:00
{"indy3EGA", "Indiana Jones and the Last Crusade", GID_INDY3, 3, 0, 0,
2003-04-11 11:40:56 +00:00
GF_AFTER_V3 | 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, 0, 22,
2003-04-11 11:40:56 +00:00
GF_AFTER_V3 | 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, 0, 0,
2003-04-11 11:40:56 +00:00
GF_AFTER_V3 | GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALLING | GF_OLD256 | GF_AUDIOTRACKS, "00.LFL"},
{"loom", "Loom", GID_LOOM, 3, 5, 40,
GF_AFTER_V3 | GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALLING | GF_USE_KEY | GF_OLD_BUNDLE | GF_16COLOR | GF_ADLIB_DEFAULT, "00.LFL"},
/* Scumm Version 4 */
2003-04-10 06:58:23 +00:00
{"monkeyEGA", "Monkey Island 1 (EGA)", GID_MONKEY_EGA, 4, 0, 62,
GF_AFTER_V4 | GF_SMALL_HEADER | GF_USE_KEY | GF_16COLOR | GF_ADLIB_DEFAULT, "000.LFL"},
2003-04-11 03:57:09 +00:00
{"pass", "Passport to Adventure", GID_MONKEY_EGA, 4, 0, 62,
GF_AFTER_V4 | 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, 5, 0, 16,
GF_AFTER_V4 | GF_SMALL_HEADER | GF_USE_KEY | GF_ADLIB_DEFAULT, "000.LFL"},
{"loomcd", "Loom (256 color CD version)", GID_LOOM256, 5, 1, 42,
GF_AFTER_V4 | GF_SMALL_HEADER | GF_USE_KEY | GF_AUDIOTRACKS | GF_ADLIB_DEFAULT, "000.LFL"},
{"monkey", "Monkey Island 1", GID_MONKEY, 5, 2, 2,
GF_AFTER_V5 | GF_USE_KEY | GF_AUDIOTRACKS | GF_ADLIB_DEFAULT, 0},
{"monkey1", "Monkey Island 1 (alt)", GID_MONKEY, 5, 2, 2,
GF_AFTER_V5 | GF_USE_KEY | GF_AUDIOTRACKS | GF_ADLIB_DEFAULT, 0},
{"monkey2", "Monkey Island 2: LeChuck's revenge", GID_MONKEY2, 5, 2, 2,
GF_AFTER_V5 | GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"mi2demo", "Monkey Island 2: LeChuck's revenge (Demo)", GID_MONKEY2, 5, 2, 2,
GF_AFTER_V5 | GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"atlantis", "Indiana Jones and the Fate of Atlantis", GID_INDY4, 5, 5, 0,
GF_AFTER_V5 | GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"playfate", "Indiana Jones and the Fate of Atlantis (Demo)", GID_INDY4, 5, 5, 0,
GF_AFTER_V5 | GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"fate", "Indiana Jones and the Fate of Atlantis (Demo)", GID_INDY4, 5, 5, 0,
GF_AFTER_V5 | GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
/* Scumm Version 6 */
{"puttputt", "Putt-Putt Joins The Parade (DOS)", GID_SAMNMAX, 6, 1, 1,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"puttdemo", "Putt-Putt Joins The Parade (DOS Demo)", GID_PUTTDEMO, 6, 1, 1,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS, 0},
{"moondemo", "Putt-Putt Goes To The Moon (DOS Demo)", GID_SAMNMAX, 6, 1, 1,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"puttmoon", "Putt-Putt Goes To The Moon (DOS)", GID_SAMNMAX, 6, 1, 1,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"funpack", "Putt-Putt's Fun Pack", GID_SAMNMAX, 6, 1, 1,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"fbpack", "Fatty Bear's Fun Pack", GID_SAMNMAX, 6, 1, 1,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"fbear", "Fatty Bear's Birthday Surprise (DOS)", GID_SAMNMAX, 6, 1, 1,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"fbdemo", "Fatty Bear's Birthday Surprise (DOS Demo)", GID_SAMNMAX, 6, 1, 1,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_ADLIB_DEFAULT | GF_HUMONGOUS | GF_NEW_COSTUMES, 0},
{"tentacle", "Day Of The Tentacle", GID_TENTACLE, 6, 4, 2,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"dottdemo", "Day Of The Tentacle (Demo)", GID_TENTACLE, 6, 3, 2,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_ADLIB_DEFAULT, 0},
{"samnmax", "Sam & Max", GID_SAMNMAX, 6, 4, 2,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER, 0},
{"samdemo", "Sam & Max (Demo)", GID_SAMNMAX, 6, 3, 0,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER | GF_ADLIB_DEFAULT, 0},
{"snmdemo", "Sam & Max (Demo)", GID_SAMNMAX, 6, 5, 0,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER | GF_ADLIB_DEFAULT, 0},
{"snmidemo", "Sam & Max (Interactive WIP Demo)", GID_SAMNMAX, 6, 5, 0,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_USE_KEY | GF_DRAWOBJ_OTHER_ORDER | GF_ADLIB_DEFAULT, 0},
{"test", "Test demo game", GID_SAMNMAX, 6, 6, 6, GF_NEW_OPCODES | GF_AFTER_V6, 0},
/* Scumm Version 7 */
{"ft", "Full Throttle", GID_FT, 7, 3, 0,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_AFTER_V7 | GF_NEW_COSTUMES, 0},
{"dig", "The Dig", GID_DIG, 7, 5, 0,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_AFTER_V7 | GF_NEW_COSTUMES, 0},
/* Scumm Version 8 */
{"comi", "The Curse of Monkey Island", GID_CMI, 8, 1, 0,
GF_NEW_OPCODES | GF_AFTER_V6 | GF_AFTER_V7 | GF_AFTER_V8 | GF_NEW_COSTUMES | GF_DEFAULT_TO_1X_SCALER, 0},
{NULL, NULL, 0, 0, 0, 0, 0, NULL}
};
2003-03-06 17:58:13 +00:00
const VersionSettings *Engine_SCUMM_targetList() {
return scumm_settings;
}
2003-03-06 17:58:13 +00:00
Engine *Engine_SCUMM_create(GameDetector *detector, OSystem *syst) {
Engine *engine;
if (detector->_amiga)
detector->_features |= GF_AMIGA;
if (detector->_features & GF_AFTER_V8)
engine = new Scumm_v8(detector, syst);
else if (detector->_features & GF_AFTER_V7)
engine = new Scumm_v7(detector, syst);
else if (detector->_features & GF_AFTER_V6)
engine = new Scumm_v6(detector, syst);
else if (detector->_features & GF_AFTER_V5)
engine = new Scumm_v5(detector, syst);
else if (detector->_features & GF_AFTER_V4)
engine = new Scumm_v4(detector, syst);
else if (detector->_features & GF_AFTER_V3)
engine = new Scumm_v3(detector, syst);
else if (detector->_features & GF_AFTER_V2)
engine = new Scumm_v2(detector, syst);
else
error("Engine_SCUMM_create(): Unknown version of game engine");
return engine;
}
Scumm::Scumm (GameDetector *detector, OSystem *syst)
2003-03-06 17:58:13 +00:00
: Engine(detector, syst), _pauseDialog(0), _optionsDialog(0), _saveLoadDialog(0) {
2002-08-18 18:39:42 +00:00
OSystem::Property prop;
// Use g_scumm from error() ONLY
g_scumm = this;
_debugMode = detector->_debugMode;
_debugLevel = detector->_debugLevel;
_dumpScripts = detector->_dumpScripts;
_bootParam = detector->_bootParam;
_exe_name = (char*)detector->_gameRealName.c_str();
_game_name = (char*)detector->_gameFileName.c_str();
_gameId = detector->_gameId;
_features = detector->_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 = 0;
if (_gameId == GID_ZAK256) { // FmTowns is 320x240
_realWidth = 320;
_realHeight = 240;
2002-09-21 16:20:52 +00:00
} else if (_gameId == GID_CMI) {
_realWidth = 640;
_realHeight = 480;
} else {
_realWidth = 320;
_realHeight = 200;
}
gdi._numStrips = _realWidth / 8;
_newgui = g_gui;
_bundle = new Bundle();
2002-08-14 20:43:56 +00:00
_sound = new Sound(this);
_timer = Engine::_timer;
_sound->_sound_volume_master = detector->_master_volume;
_sound->_sound_volume_sfx = detector->_sfx_volume;
_sound->_sound_volume_music = detector->_music_volume;
2002-08-18 18:39:42 +00:00
// 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);
}
2002-08-18 18:39:42 +00:00
/* Initialize backend */
syst->init_size(_realWidth, _realHeight);
prop.cd_num = detector->_cdrom;
if (prop.cd_num >= 0 && (_features & GF_AUDIOTRACKS))
2002-09-28 15:19:21 +00:00
syst->property(OSystem::PROP_OPEN_CD, &prop);
2002-08-18 18:39:42 +00:00
// Override global fullscreen setting with any game-specific define
if (g_config->getBool("fullscreen", false)) {
2003-03-06 17:58:13 +00:00
if (!syst->property(OSystem::PROP_GET_FULLSCREEN, 0))
syst->property(OSystem::PROP_TOGGLE_FULLSCREEN, 0);
}
2002-11-30 16:03:46 +00:00
#ifndef __GP32__ //ph0x FIXME, "quick dirty hack"
2002-08-18 18:39:42 +00:00
/* Bind the mixer to the system => mixer will be invoked
* automatically when samples need to be generated */
_silentDigitalImuse = false;
2002-08-24 15:58:08 +00:00
if (!_mixer->bindToSystem(syst)) {
2002-08-18 18:39:42 +00:00
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;
2002-08-18 18:39:42 +00:00
}
2002-11-19 17:18:16 +00:00
_mixer->setVolume(kDefaultSFXVolume * kDefaultMasterVolume / 255);
2002-08-24 15:58:08 +00:00
_mixer->setMusicVolume(kDefaultMusicVolume);
2002-08-18 18:39:42 +00:00
// Init iMuse
2002-10-03 23:16:10 +00:00
if (_features & GF_AFTER_V7) {
_imuseDigital = new IMuseDigital(this);
_imuse = NULL;
2002-08-18 18:39:42 +00:00
} else {
_imuseDigital = NULL;
_imuse = IMuse::create_midi(syst, detector->createMidi());
if (_imuse) {
if (detector->_gameTempo != 0)
_imuse->property(IMuse::PROP_TEMPO_BASE, detector->_gameTempo);
_imuse->set_music_volume(_sound->_sound_volume_music);
}
2002-08-18 18:39:42 +00:00
}
2002-11-30 16:03:46 +00:00
#endif // ph0x-hack
2002-08-18 18:39:42 +00:00
// Load game from specified slot, if any
if (detector->_save_slot != -1) {
_saveLoadSlot = detector->_save_slot;
_saveLoadFlag = 2;
_saveLoadCompatible = false;
}
loadLanguageBundle();
2002-10-11 08:35:12 +00:00
_audioNames = NULL;
2002-08-14 00:01:39 +00:00
}
Scumm::~Scumm ()
{
2002-08-14 00:01:39 +00:00
delete [] _actors;
2002-12-25 21:42:22 +00:00
delete _charset;
delete _pauseDialog;
delete _optionsDialog;
delete _saveLoadDialog;
2002-08-14 00:01:39 +00:00
delete _bundle;
2002-08-14 20:43:56 +00:00
delete _sound;
2002-12-25 21:42:22 +00:00
delete _imuse;
delete _imuseDigital;
delete _languageBuffer;
delete _audioNames;
2002-08-14 00:01:39 +00:00
}
2003-03-06 17:58:13 +00:00
void Scumm::scummInit() {
2001-10-09 14:30:12 +00:00
int i;
Actor *a;
2002-08-14 20:43:56 +00:00
tempMusic = 0;
2001-10-09 14:30:12 +00:00
debug(9, "scummInit");
if (_features & GF_AFTER_V7) {
initScreens(0, 0, _realWidth, _realHeight);
} else {
initScreens(0, 16, _realWidth, 144);
}
if (_features & GF_16COLOR) {
byte pal16[16 * 4] = {
0, 0, 0, 0, 0, 0, 168, 0, 0, 168, 0, 0, 0, 168, 168, 0,
168, 0, 0, 0, 168, 0, 168, 0, 168, 84, 0, 0, 168, 168, 168, 0,
84, 84, 84, 0, 84, 84, 168, 0, 0, 252, 0, 0, 0, 252, 252, 0,
252, 84, 84, 0, 252, 0, 252, 0, 252, 252, 0, 0, 252, 252, 252, 0
};
_system->set_palette(pal16, 0, 16);
}
if (_features & GF_AFTER_V3)
2002-12-25 23:18:30 +00:00
_charset = new CharsetRendererOld256(this);
else if (_features & GF_AFTER_V8)
_charset = new CharsetRendererNut(this);
2002-12-25 23:18:30 +00:00
else
_charset = new CharsetRendererClassic(this);
memset(_charsetData, 0, sizeof(_charsetData));
2003-03-06 17:58:13 +00:00
if (!(_features & GF_SMALL_NAMES) && !(_features & GF_AFTER_V8))
loadCharset(1);
2001-10-09 14:30:12 +00:00
setShake(0);
setupCursor();
2002-12-29 19:54:11 +00:00
// Allocate and Initialize actors
_actors = new Actor[NUM_ACTORS];
for (i = 1; i < NUM_ACTORS; i++) {
a = derefActor(i);
2001-10-09 14:30:12 +00:00
a->number = i;
2002-05-15 10:25:00 +00:00
a->initActorClass(this);
a->initActor(1);
2001-10-09 14:30:12 +00:00
}
2003-03-06 17:58:13 +00:00
2001-10-16 10:01:48 +00:00
_vars[VAR_CHARINC] = 4;
2001-10-09 14:30:12 +00:00
_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++) {
2001-10-16 10:01:48 +00:00
_verbs[i].verbid = 0;
_verbs[i].right = _realWidth - 1;
2001-10-16 10:01:48 +00:00
_verbs[i].oldleft = -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;
2001-10-16 10:01:48 +00:00
_verbs[i].key = 0;
2001-10-09 14:30:12 +00:00
}
if (!(_features & GF_AFTER_V7)) {
camera._leftTrigger = 10;
camera._rightTrigger = 30;
camera._mode = 0;
}
2001-10-09 14:30:12 +00:00
camera._follows = 0;
virtscr[0].xstart = 0;
if (!(_features & GF_AFTER_V7)) {
_vars[VAR_V5_DRAWFLAGS] = 11;
_vars[VAR_59] = 3;
// Setup light
_vars[VAR_CURRENT_LIGHTS] = LIGHTMODE_actor_base | LIGHTMODE_actor_color | LIGHTMODE_screen;
_flashlightXStrips = 7;
_flashlightYStrips = 7;
_flashlight.buffer = NULL;
}
2001-10-09 14:30:12 +00:00
mouse.x = 104;
mouse.y = 56;
_ENCD_offs = 0;
_EXCD_offs = 0;
_currentScript = 0xFF;
_sentenceNum = 0;
2001-10-09 14:30:12 +00:00
_currentRoom = 0;
_numObjectsInRoom = 0;
_actorToPrintStrFor = 0;
_charsetBufPos = 0;
2001-10-09 14:30:12 +00:00
_haveMsg = 0;
_varwatch = -1;
2001-10-09 14:30:12 +00:00
_screenStartStrip = 0;
2001-10-26 17:34:50 +00:00
_vars[VAR_TALK_ACTOR] = 0;
2001-10-09 14:30:12 +00:00
_talkDelay = 0;
_keepText = false;
_currentCursor = 0;
2002-12-04 22:31:36 +00:00
_cursor.state = 0;
2001-10-09 14:30:12 +00:00
_userPut = 0;
2001-10-09 14:30:12 +00:00
_newEffect = 129;
_fullRedraw = true;
2001-10-09 14:30:12 +00:00
clearDrawObjectQueue();
for (i = 0; i < 6; i++) {
if (_features & GF_AFTER_V3) { // GF_OLD256 or GF_AFTER_V3 ?
_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 = _realWidth - 1;
_string[i].t_color = 0xF;
_string[i].t_center = 0;
_string[i].t_charset = 0;
2001-10-09 14:30:12 +00:00
}
_numInMsgStack = 0;
createResource(rtTemp, 6, 500);
2001-10-09 14:30:12 +00:00
initScummVars();
if (!(_features & GF_AFTER_V6))
2001-10-26 17:34:50 +00:00
_vars[VAR_V5_TALK_STRING_Y] = -0x50;
2001-10-09 14:30:12 +00:00
getGraphicsPerformance();
2002-08-14 20:43:56 +00:00
_sound->_current_cache = 0;
2002-12-13 03:23:17 +00:00
_lastSaveTime = _system->get_msecs();
2001-10-09 14:30:12 +00:00
}
2003-03-06 17:58:13 +00:00
void Scumm::initScummVars() {
if (!(_features & GF_AFTER_V7)) {
2002-12-04 22:31:36 +00:00
_vars[VAR_CURRENTDRIVE] = 0;
_vars[VAR_FIXEDDISK] = true;
_vars[VAR_SOUNDCARD] = 3;
_vars[VAR_VIDEOMODE] = 0x13;
_vars[VAR_HEAPSPACE] = 1400;
2002-12-04 22:31:36 +00:00
_vars[VAR_MOUSEPRESENT] = true; // FIXME - used to be 0, but that seems odd?!?
_vars[VAR_SOUNDPARAM] = 0;
_vars[VAR_SOUNDPARAM2] = 0;
_vars[VAR_SOUNDPARAM3] = 0;
if (_features & GF_AFTER_V6)
_vars[VAR_V6_EMSSPACE] = 10000;
} else {
_vars[VAR_V6_EMSSPACE] = 10000;
}
if (_features & GF_AFTER_V8) { // Fixme: How do we deal with non-cd installs?
_vars[VAR_CURRENTDISK] = 1;
_vars[VAR_LANGUAGE] = _language;
}
2003-03-06 17:58:13 +00:00
}
2001-10-09 14:30:12 +00:00
2003-03-06 17:58:13 +00:00
void Scumm::checkRange(int max, int min, int no, const char *str) {
2001-10-09 14:30:12 +00:00
if (no < min || no > max) {
char buf[1024];
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);
2001-10-09 14:30:12 +00:00
}
}
2003-03-06 17:58:13 +00:00
int Scumm::scummLoop(int delta) {
static int counter = 0;
#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);
_vars[VAR_TMR_1] += delta;
_vars[VAR_TMR_2] += delta;
_vars[VAR_TMR_3] += delta;
_vars[VAR_TMR_4] += delta;
2001-10-09 14:30:12 +00:00
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_AFTER_V7) {
_vars[VAR_CAMERA_POS_X] = camera._cur.x;
_vars[VAR_CAMERA_POS_Y] = camera._cur.y;
} else {
_vars[VAR_CAMERA_POS_X] = camera._cur.x;
}
_vars[VAR_HAVE_MSG] = (_haveMsg == 0xFE) ? 0xFF : _haveMsg;
_vars[VAR_VIRT_MOUSE_X] = _virtual_mouse_x;
_vars[VAR_VIRT_MOUSE_Y] = _virtual_mouse_y;
_vars[VAR_MOUSE_X] = mouse.x;
_vars[VAR_MOUSE_Y] = mouse.y;
_vars[VAR_DEBUGMODE] = _debugMode;
if (_gameId == GID_MONKEY_VGA) {
// FIXME: Is all this really necessary now?
if (delta == 1)
_vars[VAR_MI1_TIMER]++;
else if (++counter != 2)
_vars[VAR_MI1_TIMER] += 5;
else {
counter = 0;
_vars[VAR_MI1_TIMER] += 6;
}
} else if (_features & GF_AUDIOTRACKS) {
_vars[VAR_MI1_TIMER] = _sound->readCDTimer();
} else if ((_features & GF_OLD256) || (_features & GF_16COLOR)) {
if(tempMusic == 3) {
tempMusic = 0;
_vars[VAR_MUSIC_FLAG]++;
} else {
tempMusic++;
}
}
2002-12-13 03:23:17 +00:00
// 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;
}
_vars[VAR_GAME_LOADED] = 0;
if (_saveLoadFlag) {
load_game:
bool success;
const char *errMsg = "Succesfully saved/loaded game state in file:\n\n%s";
char filename[256];
if (_saveLoadFlag == 1) {
success = saveState(_saveLoadSlot, _saveLoadCompatible);
if (!success)
2002-12-03 23:53:42 +00:00
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
2003-03-06 17:58:13 +00:00
if (success && _saveLoadCompatible && !(_features & GF_SMALL_HEADER) && !(_features & GF_AFTER_V8))
_vars[VAR_GAME_LOADED] = 201;
} else {
success = loadState(_saveLoadSlot, _saveLoadCompatible);
if (!success)
2002-12-03 23:53:42 +00:00
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).
2003-03-06 17:58:13 +00:00
if (success && _saveLoadCompatible && !(_features & GF_SMALL_HEADER))
_vars[VAR_GAME_LOADED] = 203;
2001-10-09 14:30:12 +00:00
}
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
char buf[1024];
sprintf(buf, errMsg, filename);
Dialog *dialog = new MessageDialog(_newgui, buf, 1500, false);
runDialog(dialog);
delete dialog;
}
_saveLoadFlag = 0;
2002-12-13 03:23:17 +00:00
_lastSaveTime = _system->get_msecs();
}
if (_completeScreenRedraw) {
_completeScreenRedraw = false;
gdi.clearUpperMask();
_charset->_hasMask = false;
for (int i = 0; i < _maxVerbs; i++)
drawVerb(i, 0);
verbMouseOver(0);
_verbRedraw = false;
_fullRedraw = true;
}
2001-10-09 14:30:12 +00:00
runAllScripts();
checkExecVerbs();
2002-12-09 01:27:40 +00:00
checkAndRunSentenceScript();
2001-10-09 14:30:12 +00:00
// 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) {
gdi._cursorActive = 0;
CHARSET_1();
drawDirtyScreenParts();
2002-08-14 20:43:56 +00:00
_sound->processSoundQues();
camera._last = camera._cur;
} else {
2001-10-09 14:30:12 +00:00
walkActors();
moveCamera();
fixObjectFlags();
CHARSET_1();
2002-08-20 02:13:41 +00:00
if (camera._cur.x != camera._last.x || _BgNeedsRedraw || _fullRedraw
|| (_features & GF_AFTER_V7 && camera._cur.y != camera._last.y)) {
redrawBGAreas();
2001-10-09 14:30:12 +00:00
}
2002-08-20 02:13:41 +00:00
2001-10-09 14:30:12 +00:00
processDrawQue();
if (_verbRedraw) {
redrawVerbs();
}
setActorRedrawFlags(true, true);
2001-10-09 14:30:12 +00:00
resetActorBgs();
if (!(_vars[VAR_CURRENT_LIGHTS] & LIGHTMODE_screen) &&
_vars[VAR_CURRENT_LIGHTS] & LIGHTMODE_flashlight) {
drawFlashlight();
setActorRedrawFlags(true, false);
}
2001-10-09 14:30:12 +00:00
processActors();
_fullRedraw = false;
2001-10-09 14:30:12 +00:00
cyclePalette();
palManipulate();
2001-10-26 17:34:50 +00:00
if (_doEffect) {
_doEffect = false;
fadeIn(_newEffect);
2001-10-09 14:30:12 +00:00
clearClickedStatus();
}
if (!_verbRedraw && _cursor.state > 0) {
2001-10-09 14:30:12 +00:00
verbMouseOver(checkMouseOver(mouse.x, mouse.y));
}
_verbRedraw = false;
2001-10-09 14:30:12 +00:00
drawBlastObjects();
if (_features & GF_AFTER_V8)
processUpperActors();
drawBlastTexts();
drawDirtyScreenParts();
removeBlastObjects();
removeBlastTexts();
2001-10-09 14:30:12 +00:00
if (!(_features & GF_AFTER_V6))
playActorSounds();
2002-08-14 20:43:56 +00:00
_sound->processSoundQues();
camera._last = camera._cur;
}
if (!(++_expire_counter)) {
increaseResourceCounter();
}
animateCursor();
/* show or hide mouse */
2002-12-04 22:31:36 +00:00
_system->show_mouse(_cursor.state > 0);
_vars[VAR_TIMER] = 0;
return _vars[VAR_TIMER_NEXT];
}
2003-03-06 17:58:13 +00:00
void Scumm::startScene(int room, Actor * a, int objectNr) {
int i, where;
2001-10-09 14:30:12 +00:00
Actor *at;
2002-12-27 00:58:21 +00:00
CHECK_HEAP;
debug(1, "Loading room %d", room);
2001-10-09 14:30:12 +00:00
clearMsgQueue();
fadeOut(_switchRoomEffect2);
2001-10-09 14:30:12 +00:00
_newEffect = _switchRoomEffect;
if (_currentScript != 0xFF) {
if (vm.slot[_currentScript].where == WIO_ROOM ||
vm.slot[_currentScript].where == WIO_FLOBJECT) {
if (vm.slot[_currentScript].cutsceneOverride != 0)
error("Object %d stopped with active cutscene/override in exit",
vm.slot[_currentScript].number);
2001-10-09 14:30:12 +00:00
_currentScript = 0xFF;
} else if (vm.slot[_currentScript].where == WIO_LOCAL) {
if (vm.slot[_currentScript].cutsceneOverride != 0)
error("Script %d stopped with active cutscene/override in exit",
vm.slot[_currentScript].number);
2001-10-09 14:30:12 +00:00
_currentScript = 0xFF;
}
}
if (!(_features & GF_SMALL_HEADER)) // Disable for SH games. Overwrites
_vars[VAR_NEW_ROOM] = room; // gamevars, eg Zak cashcards
2001-10-09 14:30:12 +00:00
runExitScript();
killScriptsAndResources();
clearEnqueue();
2001-10-09 14:30:12 +00:00
stopCycle(0);
for (i = 1; i < NUM_ACTORS; i++) {
at = derefActor(i);
at->hideActor();
2001-10-09 14:30:12 +00:00
}
if (!(_features & GF_AFTER_V7)) {
for (i = 0; i < 0x100; i++)
_shadowPalette[i] = i;
}
2001-10-09 14:30:12 +00:00
clearDrawObjectQueue();
2001-10-16 10:01:48 +00:00
_vars[VAR_ROOM] = room;
_fullRedraw = true;
2001-10-09 14:30:12 +00:00
increaseResourceCounter();
2001-10-09 14:30:12 +00:00
_currentRoom = room;
2001-10-16 10:01:48 +00:00
_vars[VAR_ROOM] = room;
if (room >= 0x80 && !(_features & GF_AFTER_V7))
_roomResource = _resourceMapper[room & 0x7F];
2001-10-09 14:30:12 +00:00
else
_roomResource = room;
2001-10-16 10:01:48 +00:00
_vars[VAR_ROOM_RESOURCE] = _roomResource;
2001-10-09 14:30:12 +00:00
if (room != 0)
ensureResourceLoaded(rtRoom, room);
2001-10-09 14:30:12 +00:00
clearRoomObjects();
2001-10-09 14:30:12 +00:00
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();
2001-10-09 14:30:12 +00:00
if (!(_features & GF_AFTER_V7)) {
camera._mode = CM_NORMAL;
camera._cur.x = camera._dest.x = _realWidth / 2;
camera._cur.y = camera._dest.y = _realHeight / 2;
}
2001-10-26 17:34:50 +00:00
if (_features & GF_AFTER_V6) {
_vars[VAR_V6_SCREEN_WIDTH] = _scrWidth;
2001-10-26 17:34:50 +00:00
_vars[VAR_V6_SCREEN_HEIGHT] = _scrHeight;
}
2002-09-21 16:20:52 +00:00
_vars[VAR_CAMERA_MIN_X] = _realWidth / 2;
_vars[VAR_CAMERA_MAX_X] = _scrWidth - (_realWidth / 2);
if (_features & GF_AFTER_V7) {
_vars[VAR_CAMERA_MIN_Y] = _realHeight / 2;
_vars[VAR_CAMERA_MAX_Y] = _scrHeight - (_realHeight / 2);
setCameraAt(_realWidth / 2, _realHeight / 2);
}
2001-10-09 14:30:12 +00:00
if (_roomResource == 0)
return;
memset(gfxUsageBits, 0, sizeof(gfxUsageBits));
2001-10-09 14:30:12 +00:00
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);
2001-10-09 14:30:12 +00:00
a->moving = 0;
}
showActors();
2001-10-26 17:34:50 +00:00
_egoPositioned = false;
runEntryScript();
if (!(_features & GF_AFTER_V7)) {
if (a && !_egoPositioned) {
int x, y;
getObjectXYPos(objectNr, x, y);
a->putActor(x, y, _currentRoom);
a->moving = 0;
}
} else {
if (camera._follows) {
a = derefActorSafe(camera._follows, "startScene: follows");
setCameraAt(a->x, a->y);
}
}
2001-10-09 14:30:12 +00:00
2001-10-26 17:34:50 +00:00
_doEffect = true;
2001-10-09 14:30:12 +00:00
CHECK_HEAP;
}
2001-10-09 14:30:12 +00:00
2003-03-06 17:58:13 +00:00
void Scumm::initRoomSubBlocks() {
int i, offs;
2001-10-09 14:30:12 +00:00
byte *ptr;
byte *roomptr, *searchptr, *roomResPtr;
RoomHeader *rmhd;
2001-10-09 14:30:12 +00:00
_ENCD_offs = 0;
_EXCD_offs = 0;
2001-10-16 10:01:48 +00:00
_CLUT_offs = 0;
_PALS_offs = 0;
2001-10-09 14:30:12 +00:00
nukeResource(rtMatrix, 1);
nukeResource(rtMatrix, 2);
for (i = 1; i < _maxScaleTable; i++)
nukeResource(rtScaleTable, i);
2001-10-09 14:30:12 +00:00
memset(_localScriptList, 0, sizeof(_localScriptList));
memset(_extraBoxFlags, 0, sizeof(_extraBoxFlags));
// Determine the room and room script base address
roomResPtr = roomptr = getResourceAddress(rtRoom, _roomResource);
if (_features & GF_AFTER_V8)
roomResPtr = getResourceAddress(rtRoomScripts, _roomResource);
if (!roomptr || !roomResPtr)
error("Room %d: data not found (" __FILE__ ":%d)", _roomResource, __LINE__);
if (_features & GF_OLD_BUNDLE)
rmhd = (RoomHeader *)(roomptr + 4);
else
rmhd = (RoomHeader *)findResourceData(MKID('RMHD'), roomptr);
//
// Determine the room dimensions (width/height)
//
if (_features & GF_AFTER_V8) {
_scrWidth = READ_LE_UINT32(&(rmhd->v8.width));
_scrHeight = READ_LE_UINT32(&(rmhd->v8.height));
} else if (_features & GF_AFTER_V7) {
_scrWidth = READ_LE_UINT16(&(rmhd->v7.width));
_scrHeight = READ_LE_UINT16(&(rmhd->v7.height));
} else {
_scrWidth = READ_LE_UINT16(&(rmhd->old.width));
_scrHeight = READ_LE_UINT16(&(rmhd->old.height));
}
2001-10-09 14:30:12 +00:00
//
// Find the room image data
//
if (_features & GF_OLD_BUNDLE)
_IM00_offs = READ_LE_UINT16(roomptr + 0x0A);
else if (_features & GF_SMALL_HEADER)
_IM00_offs = findResourceData(MKID('IM00'), roomptr) - roomptr;
else if (_features & GF_AFTER_V8) {
ptr = findResource(MKID('IMAG'), roomptr);
assert(ptr);
ptr = findResource(MKID('WRAP'), ptr);
assert(ptr);
ptr = findResource(MKID('OFFS'), ptr);
assert(ptr);
// Get the address of the first SMAP (corresponds to IM00)
ptr += READ_LE_UINT32(ptr + 8);
_IM00_offs = ptr - roomptr;
} else
_IM00_offs =
2003-03-06 17:58:13 +00:00
findResource(MKID('IM00'), findResource(MKID('RMIM'), roomptr)) - roomptr;
//
// Look for an exit script
//
if (_features & GF_OLD_BUNDLE)
_EXCD_offs = READ_LE_UINT16(roomptr + 0x19);
else {
ptr = findResourceData(MKID('EXCD'), roomResPtr);
if (ptr)
_EXCD_offs = ptr - roomResPtr;
2001-10-09 14:30:12 +00:00
}
if (_dumpScripts && _EXCD_offs)
dumpResource("exit-", _roomResource, roomResPtr + _EXCD_offs - _resourceHeaderSize);
2001-10-09 14:30:12 +00:00
//
// Look for an entry script
//
if (_features & GF_OLD_BUNDLE)
_ENCD_offs = READ_LE_UINT16(roomptr + 0x1B);
else {
ptr = findResourceData(MKID('ENCD'), roomResPtr);
if (ptr)
_ENCD_offs = ptr - roomResPtr;
2001-10-09 14:30:12 +00:00
}
if (_dumpScripts && _ENCD_offs)
dumpResource("entry-", _roomResource, roomResPtr + _ENCD_offs - _resourceHeaderSize);
2001-10-09 14:30:12 +00:00
//
// Load box data
//
if (_features & GF_SMALL_HEADER) {
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 (_features & GF_AFTER_V3) // GF_OLD256 or GF_AFTER_V3 ?
size = numOfBoxes * (SIZEOF_BOX - 2) + 1;
else
size = numOfBoxes * SIZEOF_BOX + 1;
createResource(rtMatrix, 2, size);
memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
ptr += size;
if (_features & GF_OLD_BUNDLE)
// FIXME. This is an evil HACK!!!
size = (READ_LE_UINT16(roomptr + 0x15) - READ_LE_UINT16(roomptr + 0x0A)) - size;
else
size = getResourceDataSize(ptr - size - 6) - size;
if (size >= 0) { // do this :)
2002-02-22 16:59:24 +00:00
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);
}
}
2001-10-09 14:30:12 +00:00
if (_features & GF_OLD_BUNDLE)
ptr = 0; // TODO ?
else
ptr = findResourceData(MKID('SCAL'), roomptr);
2001-10-09 14:30:12 +00:00
if (ptr) {
offs = ptr - roomptr;
2003-01-06 18:46:07 +00:00
if (_features & GF_AFTER_V8) {
for (i = 1; i < _maxScaleTable; i++, offs += 16) {
2003-01-13 01:29:45 +00:00
int scale1 = READ_LE_UINT32(roomptr + offs);
int y1 = READ_LE_UINT32(roomptr + offs + 4);
int scale2 = READ_LE_UINT32(roomptr + offs + 8);
int y2 = READ_LE_UINT32(roomptr + offs + 12);
setScaleSlot(i, 0, y1, scale1, 0, y2, scale2);
}
} else {
for (i = 1; i < _maxScaleTable; i++, offs += 8) {
int a = READ_LE_UINT16(roomptr + offs);
int b = READ_LE_UINT16(roomptr + offs + 2);
int c = READ_LE_UINT16(roomptr + offs + 4);
int d = READ_LE_UINT16(roomptr + offs + 6);
if (a || b || c || d) {
setScaleItem(i, b, a, d, c);
roomptr = getResourceAddress(rtRoom, _roomResource);
}
2001-10-09 14:30:12 +00:00
}
}
}
//
// Setup local script
//
// Determine the room script base address
roomResPtr = roomptr = getResourceAddress(rtRoom, _roomResource);
if (_features & GF_AFTER_V8)
roomResPtr = getResourceAddress(rtRoomScripts, _roomResource);
searchptr = roomResPtr;
if (_features & GF_OLD_BUNDLE) {
int num_objects = *(roomResPtr + 20);
int num_sounds = *(roomResPtr + 23);
int num_scripts = *(roomResPtr + 24);
int offset = 29 + num_objects * 4 + num_sounds + num_scripts;
if ((_gameId != GID_MANIAC) && (_gameId != GID_ZAK)) {
for (;;) {
int id = *(roomResPtr + offset);
if (id == 0)
break;
_localScriptList[id - _numGlobalScripts] = offset + 1;
offset += 3;
}
}
} else if (_features & GF_SMALL_HEADER) {
while ((ptr = findResourceSmall(MKID('LSCR'), searchptr)) != 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 - 6);
}
_localScriptList[id - _numGlobalScripts] = ptr + 1 - roomptr;
searchptr = NULL;
}
} else {
while ((ptr = findResource(MKID('LSCR'), searchptr)) != NULL) {
int id = 0;
ptr += _resourceHeaderSize; /* skip tag & size */
if (_features & GF_AFTER_V8) {
id = READ_LE_UINT32(ptr);
checkRange(NUM_LOCALSCRIPT + _numGlobalScripts, _numGlobalScripts, id, "Invalid local script %d");
_localScriptList[id - _numGlobalScripts] = ptr + 4 - roomResPtr;
} else if (_features & GF_AFTER_V7) {
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 - 8);
}
searchptr = NULL;
}
}
2002-08-29 20:01:27 +00:00
// FIXME - we could simply always call findResourceData here, it will
// do the right thing even if GF_SMALL_HEADER is set. But then, we have
// to change setPaletteFromPtr() (easy). The problematic bit is save game
// compatibility - _CLUT_offs is stored in the save game after all.
// Of course we could just decide to not use _CLUT_offs anymore, and change
// setPaletteFromRes() to invoke findResourceData() each time
// (and also getPalettePtr()).
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);
2001-10-16 10:01:48 +00:00
if (ptr) {
_CLUT_offs = ptr - roomptr;
setPaletteFromRes();
}
2001-10-09 14:30:12 +00:00
if (_features & GF_AFTER_V6) {
ptr = findResource(MKID('PALS'), roomptr);
if (ptr) {
_PALS_offs = ptr - roomptr;
setPalette(0);
}
2001-10-16 10:01:48 +00:00
}
// Color cycling
if (_features & GF_OLD_BUNDLE)
ptr = 0; // TODO / FIXME ???
else
ptr = findResourceData(MKID('CYCL'), roomptr);
if (ptr)
2002-08-29 20:01:27 +00:00
initCycl(ptr);
2001-10-09 14:30:12 +00:00
// 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 (_features & GF_AFTER_V8)
gdi._transparentColor = 5; // FIXME
else
gdi._transparentColor = 255;
}
2001-10-09 14:30:12 +00:00
initBGBuffers(_scrHeight);
2001-10-09 14:30:12 +00:00
}
2003-03-06 17:58:13 +00:00
void Scumm::setScaleItem(int slot, int a, int b, int c, int d) {
2001-10-09 14:30:12 +00:00
byte *ptr;
int cur, amounttoadd, i, tmp;
2001-10-09 14:30:12 +00:00
ptr = createResource(rtScaleTable, slot, 200);
2001-10-09 14:30:12 +00:00
if (a == c)
2001-10-09 14:30:12 +00:00
return;
cur = (b - d) * a;
2001-10-09 14:30:12 +00:00
amounttoadd = d - b;
for (i = 200; i > 0; i--) {
2001-10-09 14:30:12 +00:00
tmp = cur / (c - a) + b;
if (tmp < 1)
tmp = 1;
if (tmp > 255)
tmp = 255;
2001-10-09 14:30:12 +00:00
*ptr++ = tmp;
cur += amounttoadd;
}
}
2003-03-06 17:58:13 +00:00
void Scumm::setScaleSlot(int slot, int x1, int y1, int scale1, int x2, int y2, int scale2) {
2003-01-13 13:37:43 +00:00
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;
2003-01-13 01:29:45 +00:00
}
2003-03-06 17:58:13 +00:00
void Scumm::dumpResource(char *tag, int idx, byte *ptr) {
2001-10-09 14:30:12 +00:00
char buf[256];
File out;
uint32 size;
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_UNALIGNED(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)
2001-10-09 14:30:12 +00:00
return;
out.write(ptr, size);
2001-10-09 14:30:12 +00:00
}
out.close();
2001-10-09 14:30:12 +00:00
}
2003-03-06 17:58:13 +00:00
void Scumm::clearClickedStatus() {
2001-10-09 14:30:12 +00:00
checkKeyHit();
_mouseButStat = 0;
_leftBtnPressed &= ~msClicked;
_rightBtnPressed &= ~msClicked;
2001-10-09 14:30:12 +00:00
}
2003-03-06 17:58:13 +00:00
int Scumm::checkKeyHit() {
2001-10-09 14:30:12 +00:00
int a = _keyPressed;
_keyPressed = 0;
return a;
}
2003-03-06 17:58:13 +00:00
void Scumm::pauseGame(bool user) {
pauseDialog();
2001-10-09 14:30:12 +00:00
}
2003-03-06 17:58:13 +00:00
void Scumm::setOptions() {
//_newgui->optionsDialog();
}
2003-03-06 17:58:13 +00:00
int Scumm::runDialog(Dialog *dialog) {
// Pause sound put
bool old_soundsPaused = _sound->_soundsPaused;
_sound->pauseSounds(true);
// Pause playing smush movie
_smushPlay = false;
// Open & run the dialog
int result = dialog->runModal();
// Restore old cursor
updateCursor();
// Restore playing smush movie
_smushPlay = true;
// Resume sound output
_sound->pauseSounds(old_soundsPaused);
// Return the result
return result;
}
2003-03-06 17:58:13 +00:00
void Scumm::pauseDialog() {
if (!_pauseDialog)
_pauseDialog = new PauseDialog(_newgui, this);
runDialog(_pauseDialog);
}
2003-03-06 17:58:13 +00:00
void Scumm::saveloadDialog() {
if (!_saveLoadDialog)
_saveLoadDialog = new SaveLoadDialog(_newgui, this);
runDialog(_saveLoadDialog);
}
2003-03-06 17:58:13 +00:00
void Scumm::optionsDialog() {
if (!_optionsDialog)
_optionsDialog = new OptionsDialog(_newgui, this);
runDialog(_optionsDialog);
}
2003-03-06 17:58:13 +00:00
char Scumm::displayError(bool showCancel, const char *message, ...) {
char buf[1024], result;
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;
}
2003-03-06 17:58:13 +00:00
void Scumm::shutDown(int i) {
2001-10-09 14:30:12 +00:00
/* TODO: implement this */
warning("shutDown: not implemented");
}
2003-03-06 17:58:13 +00:00
void Scumm::processKbd() {
int saveloadkey;
getKeyInput();
2001-10-09 14:30:12 +00:00
if ((_features & GF_OLD256) || (_gameId == GID_CMI)) /* FIXME: Support ingame screen */
saveloadkey = 319;
else
saveloadkey = _vars[VAR_SAVELOADDIALOG_KEY];
2001-10-09 14:30:12 +00:00
_virtual_mouse_x = mouse.x + virtscr[0].xstart;
if(_features & GF_AFTER_V7)
2002-09-01 15:01:40 +00:00
_virtual_mouse_y = mouse.y + camera._cur.y - (_realHeight / 2);
else
_virtual_mouse_y = mouse.y;
if (!(_features & GF_AFTER_V3))
_virtual_mouse_y += virtscr[0].topline;
else
_virtual_mouse_y -= 16;
2001-10-09 14:30:12 +00:00
if (_virtual_mouse_y < 0)
_virtual_mouse_y = -1;
if (_features & GF_AFTER_V3) {
if (_virtual_mouse_y >= virtscr[0].height + virtscr[0].topline)
_virtual_mouse_y = -1;
} else {
if (_virtual_mouse_y >= virtscr[0].height)
_virtual_mouse_y = -1;
}
2001-10-09 14:30:12 +00:00
if (!_lastKeyHit)
return;
if (_keyScriptNo && (_keyScriptKey == _lastKeyHit)) {
runScript(_keyScriptNo, 0, 0, 0);
return;
}
if (_lastKeyHit == KEY_SET_OPTIONS) {
setOptions();
return;
}
if (_lastKeyHit == _vars[VAR_RESTART_KEY]) {
2001-10-09 14:30:12 +00:00
warning("Restart not implemented");
2003-03-06 17:58:13 +00:00
// pauseGame(true);
2001-10-09 14:30:12 +00:00
return;
}
if (_lastKeyHit == _vars[VAR_PAUSE_KEY]) {
pauseGame(true);
2001-10-09 14:30:12 +00:00
/* pause */
return;
}
if (_lastKeyHit == _vars[VAR_CUTSCENEEXIT_KEY]) {
if (_insaneState) {
_videoFinished = true;
} else
exitCutscene();
} else if (_lastKeyHit == saveloadkey && _currentRoom != 0) {
if (_features & GF_AFTER_V7)
runScript(_vars[VAR_UNK_SCRIPT], 0, 0, 0);
saveloadDialog(); // Display NewGui
if (_features & GF_AFTER_V7)
runScript(_vars[VAR_UNK_SCRIPT_2], 0, 0, 0);
return;
} else if (_lastKeyHit == _vars[VAR_TALKSTOP_KEY]) {
2001-10-09 14:30:12 +00:00
_talkDelay = 0;
if (_sound->_sfxMode & 2)
stopTalk();
2001-10-09 14:30:12 +00:00
return;
2002-11-19 17:18:16 +00:00
} 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;
_vars[VAR_CHARINC] = _defaultTalkDelay / 20;
} else if (_lastKeyHit == '+') { // + text speed up
_defaultTalkDelay -= 5;
if (_defaultTalkDelay < 5)
_defaultTalkDelay = 5;
_vars[VAR_CHARINC] = _defaultTalkDelay / 20;
} else if (_lastKeyHit == '~' || _lastKeyHit == '#') { // Debug console
g_debugger.attach(this);
}
2001-10-09 14:30:12 +00:00
_mouseButStat = _lastKeyHit;
}
2003-03-06 17:58:13 +00:00
int Scumm::getKeyInput() {
2001-10-09 14:30:12 +00:00
_mouseButStat = 0;
_lastKeyHit = checkKeyHit();
convertKeysToClicks();
2001-10-09 14:30:12 +00:00
if (mouse.x < 0)
mouse.x = 0;
if (mouse.x > _realWidth-1)
mouse.x = _realWidth-1;
if (mouse.y < 0)
mouse.y = 0;
if (mouse.y > _realHeight-1)
mouse.y = _realHeight-1;
2001-10-09 14:30:12 +00:00
if (_leftBtnPressed & msClicked && _rightBtnPressed & msClicked) {
2001-10-09 14:30:12 +00:00
_mouseButStat = 0;
_lastKeyHit = (uint)_vars[VAR_CUTSCENEEXIT_KEY];
} else if (_leftBtnPressed & msClicked) {
_mouseButStat = MBS_LEFT_CLICK;
} else if (_rightBtnPressed & msClicked) {
_mouseButStat = MBS_RIGHT_CLICK;
2001-10-09 14:30:12 +00:00
}
if (_features & GF_AFTER_V8) {
_vars[VAR_MOUSE_BUTTONS] = 0;
_vars[VAR_MOUSE_HOLD] = 0;
_vars[VAR_RIGHTBTN_HOLD] = 0;
if (_leftBtnPressed & msClicked)
_vars[VAR_MOUSE_BUTTONS] += 1;
if (_rightBtnPressed & msClicked)
_vars[VAR_MOUSE_BUTTONS] += 2;
if (_leftBtnPressed & msDown)
_vars[VAR_MOUSE_HOLD] += 1;
if (_rightBtnPressed & msDown) {
_vars[VAR_RIGHTBTN_HOLD] = 1;
_vars[VAR_MOUSE_HOLD] += 2;
}
} else if (_features & GF_AFTER_V7) {
// _vars[VAR_LEFTBTN_DOWN] = (_leftBtnPressed&msClicked) != 0;
_vars[VAR_LEFTBTN_HOLD] = (_leftBtnPressed & msDown) != 0;
// _vars[VAR_RIGHTBTN_DOWN] = (_rightBtnPressed&msClicked) != 0;
_vars[VAR_RIGHTBTN_HOLD] = (_rightBtnPressed & msDown) != 0;
}
_leftBtnPressed &= ~msClicked;
_rightBtnPressed &= ~msClicked;
2001-10-09 14:30:12 +00:00
return _lastKeyHit;
}
2003-03-06 17:58:13 +00:00
void Scumm::convertKeysToClicks() {
2002-12-04 22:31:36 +00:00
if (_lastKeyHit && _cursor.state > 0) {
if (_lastKeyHit == 9) {
_mouseButStat = MBS_RIGHT_CLICK;
} else if (_lastKeyHit == 13) {
_mouseButStat = MBS_LEFT_CLICK;
2001-10-09 14:30:12 +00:00
} else
return;
_lastKeyHit = 0;
}
}
2003-03-06 17:58:13 +00:00
Actor *Scumm::derefActor(int id) {
return &_actors[id];
}
2003-03-06 17:58:13 +00:00
Actor *Scumm::derefActorSafe(int id, const char *errmsg) {
if (id < 1 || id >= NUM_ACTORS) {
if (_debugLevel > 1)
warning
("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;
}
2001-10-09 14:30:12 +00:00
return derefActor(id);
}
2003-03-06 17:58:13 +00:00
void Scumm::setStringVars(int slot) {
StringTab *st = &_string[slot];
2001-10-26 17:34:50 +00:00
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;
2001-10-26 17:34:50 +00:00
st->right = st->t_right;
st->color = st->t_color;
st->charset = st->t_charset;
2001-10-16 10:01:48 +00:00
}
2003-03-06 17:58:13 +00:00
void Scumm::startManiac() {
2001-10-16 10:01:48 +00:00
warning("stub startManiac()");
}
2003-03-06 17:58:13 +00:00
void Scumm::destroy() {
freeResources();
free(_objectStateTable);
free(_objectRoomTable);
free(_objectOwnerTable);
free(_inventory);
free(_arrays);
free(_verbs);
free(_objs);
free(_vars);
free(_bitVars);
free(_newNames);
free(_classData);
}
//
// Convert an old style direction to a new style one (angle),
//
2003-03-06 17:58:13 +00:00
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.
//
2003-03-06 17:58:13 +00:00
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.
//
2003-03-06 17:58:13 +00:00
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
//
2003-03-06 17:58:13 +00:00
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.
//
2003-03-06 17:58:13 +00:00
int normalizeAngle(int angle) {
int temp;
temp = (angle + 360) % 360;
return toSimpleDir(1, temp) * 45;
}
2001-10-16 10:01:48 +00:00
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);
2001-10-09 14:30:12 +00:00
} else {
strcpy(buf2, buf1);
2001-10-09 14:30:12 +00:00
}
}
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();
for (;;) {
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);
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;
#ifdef _WIN32_WCE
mouse.x = event.mouse.x;
mouse.y = event.mouse.y;
#endif
break;
case OSystem::EVENT_RBUTTONDOWN:
_rightBtnPressed |= msClicked|msDown;
#ifdef _WIN32_WCE
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;
}
}
}
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;
2002-12-13 17:12:02 +00:00
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) {
2002-12-13 17:12:02 +00:00
int r, g, b;
byte brightness;
2002-12-13 17:12:02 +00:00
r = data[0];
g = data[1];
b = data[2];
2002-12-13 17:12:02 +00:00
brightness = (byte)((0.299 * r + 0.587 * g + 0.114 * b) + 0.5);
2002-12-13 17:12:02 +00:00
*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;
}
2003-03-06 17:58:13 +00:00
void Scumm::mainRun() {
int delta = 0;
int last_time = _system->get_msecs();
int new_time;
for(;;) {
updatePalette();
_system->update_screen();
new_time = _system->get_msecs();
waitForTimer(delta * 15 + last_time - new_time);
last_time = _system->get_msecs();
2002-10-23 00:24:54 +00:00
delta = scummLoop(delta);
if (delta < 1) // Ensure we don't get into a loop
delta = 1; // by not decreasing sleepers.
}
}
2003-03-06 17:58:13 +00:00
void Scumm::launch() {
gdi._vm = this;
_maxHeapThreshold = 450000;
_minHeapThreshold = 400000;
_verbRedraw = false;
2003-03-06 17:58:13 +00:00
allocResTypeData(rtBuffer, MKID('NONE'), 10, "buffer", 0);
initVirtScreen(0, 0, 0, _realWidth, _realHeight, false, false);
setupScummVars();
2002-12-29 19:54:11 +00:00
setupOpcodes();
if (_features & GF_AFTER_V8)
NUM_ACTORS = 80;
else if ((_features & GF_AFTER_V7) || (_gameId == GID_SAMNMAX))
NUM_ACTORS = 30;
else
NUM_ACTORS = 13;
if (_features & GF_AFTER_V7)
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;
}
2002-12-29 19:54:11 +00:00
if (_features & GF_OLD_BUNDLE)
_resourceHeaderSize = 4; // FIXME - to be rechecked
2002-12-29 19:54:11 +00:00
else if (_features & GF_SMALL_HEADER)
_resourceHeaderSize = 6;
else
_resourceHeaderSize = 8;
2002-12-29 19:54:11 +00:00
readIndexFile();
scummInit();
if (!(_features & GF_AFTER_V7))
_vars[VAR_VERSION] = 21;
_vars[VAR_DEBUGMODE] = _debugMode;
if (_gameId == GID_MONKEY)
_vars[74] = 1225;
2002-08-14 20:43:56 +00:00
_sound->setupSound();
runScript(1, 0, 0, &_bootParam);
}
void Scumm::go() {
launch();
mainRun();
}