mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-15 06:08:35 +00:00
c50940bbf4
svn-id: r44266
551 lines
15 KiB
C++
551 lines
15 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
//#define SCRIPT_TEST
|
|
//#define INTRO_TEST
|
|
|
|
#include "m4/globals.h"
|
|
#include "m4/burger_data.h"
|
|
#include "m4/m4.h"
|
|
#include "m4/resource.h"
|
|
#include "m4/sprite.h"
|
|
#include "m4/hotspot.h"
|
|
#include "m4/font.h"
|
|
#include "m4/rails.h"
|
|
#include "m4/midi.h"
|
|
#include "m4/events.h"
|
|
#include "m4/graphics.h"
|
|
#include "m4/viewmgr.h"
|
|
#include "m4/gui.h"
|
|
#include "m4/woodscript.h"
|
|
#include "m4/actor.h"
|
|
#include "m4/sound.h"
|
|
#include "m4/rails.h"
|
|
#include "m4/script.h"
|
|
#include "m4/compression.h"
|
|
#include "m4/animation.h"
|
|
#include "m4/m4_menus.h"
|
|
#include "m4/m4_views.h"
|
|
#include "m4/mads_anim.h"
|
|
#include "m4/mads_menus.h"
|
|
|
|
#include "common/file.h"
|
|
#include "common/events.h"
|
|
#include "common/EventRecorder.h"
|
|
#include "common/endian.h"
|
|
#include "common/system.h"
|
|
#include "common/config-manager.h"
|
|
#include "engines/engine.h"
|
|
#include "graphics/surface.h"
|
|
#include "sound/mididrv.h"
|
|
|
|
namespace M4 {
|
|
|
|
// FIXME: remove global
|
|
M4Engine *_vm;
|
|
|
|
void escapeHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
|
|
// For now, simply exit the game
|
|
vm->_events->quitFlag = true;
|
|
}
|
|
|
|
// Temporary hotkey handler for use in testing the TextviewView class
|
|
|
|
void textviewHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
|
|
// Deactivate the scene if it's currently active
|
|
View *sceneView = vm->_viewManager->getView(VIEWID_SCENE);
|
|
if (sceneView != NULL)
|
|
vm->_viewManager->deleteView(sceneView);
|
|
|
|
// Activate the textview view
|
|
vm->_font->setFont(FONT_CONVERSATION_MADS);
|
|
TextviewView *textView = new TextviewView(vm);
|
|
vm->_viewManager->addView(textView);
|
|
textView->setScript("quotes", NULL);
|
|
}
|
|
|
|
void saveGameHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
|
|
// TODO: See CreateF2SaveMenu - save menu should only be activated when
|
|
// certain conditions are met, such as player_commands_allowed, and isInterfaceVisible
|
|
vm->loadMenu(SAVE_MENU, true);
|
|
}
|
|
|
|
void loadGameHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
|
|
// TODO: See CreateF3LoadMenu - save menu should only be activated when
|
|
// certain conditions are met, such as player_commands_allowed, and isInterfaceVisible
|
|
vm->loadMenu(LOAD_MENU, true);
|
|
}
|
|
|
|
void gameMenuHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
|
|
vm->loadMenu(GAME_MENU);
|
|
}
|
|
|
|
M4Engine::M4Engine(OSystem *syst, const M4GameDescription *gameDesc) :
|
|
Engine(syst), _gameDescription(gameDesc) {
|
|
|
|
// FIXME
|
|
_vm = this;
|
|
|
|
SearchMan.addSubDirectoryMatching(_gameDataDir, "goodstuf");
|
|
SearchMan.addSubDirectoryMatching(_gameDataDir, "resource");
|
|
|
|
Common::addDebugChannel(kDebugScript, "script", "Script debug level");
|
|
Common::addDebugChannel(kDebugConversations, "conversations", "Conversations debugging");
|
|
}
|
|
|
|
|
|
M4Engine::~M4Engine() {
|
|
delete _globals;
|
|
delete _midi;
|
|
delete _saveLoad;
|
|
delete _kernel;
|
|
delete _player;
|
|
delete _mouse;
|
|
delete _events;
|
|
delete _font;
|
|
delete _actor;
|
|
// delete _scene; // deleted by the viewmanager
|
|
delete _dialogs;
|
|
delete _screen;
|
|
delete _inventory;
|
|
delete _viewManager;
|
|
delete _rails;
|
|
delete _converse;
|
|
delete _script;
|
|
delete _ws;
|
|
delete _random;
|
|
delete _animation;
|
|
delete _palette;
|
|
delete _resourceManager;
|
|
}
|
|
|
|
Common::Error M4Engine::run() {
|
|
// Initialize backend
|
|
if (isM4())
|
|
initGraphics(640, 480, true);
|
|
else
|
|
initGraphics(320, 200, false);
|
|
|
|
_screen = new M4Surface(true); // Special form for creating screen reference
|
|
|
|
int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
|
|
bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
|
|
|
|
MidiDriver *driver = MidiDriver::createMidi(midiDriver);
|
|
if (native_mt32)
|
|
driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
|
|
|
|
_midi = new MidiPlayer(this, driver);
|
|
_midi->setGM(true);
|
|
_midi->setNativeMT32(native_mt32);
|
|
|
|
_globals = new Globals(this);
|
|
if (isM4())
|
|
_resourceManager = new M4ResourceManager(this);
|
|
else
|
|
_resourceManager = new MADSResourceManager(this);
|
|
_saveLoad = new SaveLoad(this);
|
|
_palette = new Palette(this);
|
|
_mouse = new Mouse(this);
|
|
_events = new Events(this);
|
|
_kernel = new Kernel(this);
|
|
_player = new Player(this);
|
|
_font = new Font(this);
|
|
if (getGameType() == GType_Burger) {
|
|
_actor = new Actor(this);
|
|
_interfaceView = new GameInterfaceView(this);
|
|
_conversationView = new ConversationView(this);
|
|
} else {
|
|
_actor = NULL;
|
|
}
|
|
_rails = new Rails(); // needs to be initialized before _scene
|
|
_scene = new Scene(this);
|
|
_dialogs = new Dialogs();
|
|
_viewManager = new ViewManager(this);
|
|
_inventory = new Inventory(this);
|
|
_sound = new Sound(this, _mixer, 255);
|
|
_converse = new Converse(this);
|
|
_script = new ScriptInterpreter(this);
|
|
_ws = new WoodScript(this);
|
|
_animation = new Animation(this);
|
|
//_callbacks = new Callbacks(this);
|
|
_random = new Common::RandomSource();
|
|
g_eventRec.registerRandomSource(*_random, "m4");
|
|
|
|
if (isM4())
|
|
return goM4();
|
|
else
|
|
return goMADS();
|
|
}
|
|
|
|
void M4Engine::eventHandler() {
|
|
M4EventType event;
|
|
uint32 keycode = 0;
|
|
|
|
if ((event = _events->handleEvents()) != MEVENT_NO_EVENT) {
|
|
if (_viewManager->containsViews())
|
|
_viewManager->handleMouseEvents(event);
|
|
}
|
|
|
|
if (_events->kbdCheck(keycode))
|
|
_viewManager->handleKeyboardEvents(keycode);
|
|
}
|
|
|
|
bool M4Engine::delay(int duration, bool keyAborts, bool clickAborts) {
|
|
uint32 endTime = g_system->getMillis() + duration;
|
|
M4EventType event;
|
|
uint32 keycode = 0;
|
|
|
|
while (!_events->quitFlag && (g_system->getMillis() < endTime)) {
|
|
event = _events->handleEvents();
|
|
if (clickAborts && ((event == MEVENT_LEFT_RELEASE) || (event == MEVENT_RIGHT_RELEASE)))
|
|
return true;
|
|
|
|
if (_events->kbdCheck(keycode)) {
|
|
if (keyAborts)
|
|
return true;
|
|
}
|
|
|
|
g_system->delayMillis(10);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void M4Engine::loadMenu(MenuType menuType, bool loadSaveFromHotkey, bool calledFromMainMenu) {
|
|
if (isM4() && (menuType != MAIN_MENU)) {
|
|
bool menuActive = _viewManager->getView(VIEWID_MENU) != NULL;
|
|
|
|
if (!menuActive)
|
|
_palette->fadeToGreen(M4_DIALOG_FADE_STEPS, M4_DIALOG_FADE_DELAY);
|
|
}
|
|
|
|
View *view;
|
|
|
|
switch (menuType) {
|
|
case MAIN_MENU:
|
|
if (getGameType() == GType_RexNebular)
|
|
view = new RexMainMenuView(this);
|
|
else if (getGameType() == GType_DragonSphere)
|
|
view = new DragonMainMenuView(this);
|
|
else
|
|
view = new MadsMainMenuView(this);
|
|
break;
|
|
case GAME_MENU:
|
|
view = new OrionMenuView(this, 200, 100, GAME_MENU, calledFromMainMenu, loadSaveFromHotkey);
|
|
break;
|
|
case OPTIONS_MENU:
|
|
view = new OrionMenuView(this, 172, 160, OPTIONS_MENU, calledFromMainMenu, loadSaveFromHotkey);
|
|
break;
|
|
case LOAD_MENU:
|
|
case SAVE_MENU:
|
|
view = new OrionMenuView(this, 145, 10, menuType, calledFromMainMenu, loadSaveFromHotkey);
|
|
break;
|
|
default:
|
|
error("Unknown menu type");
|
|
break;
|
|
}
|
|
|
|
_viewManager->addView(view);
|
|
_viewManager->moveToFront(view);
|
|
}
|
|
|
|
Common::Error M4Engine::goMADS() {
|
|
_palette->setMadsSystemPalette();
|
|
|
|
_mouse->init("cursor.ss", NULL);
|
|
_mouse->setCursorNum(0);
|
|
|
|
// Load MADS data files
|
|
_globals->loadMadsVocab(); // vocab.dat
|
|
_globals->loadMadsQuotes(); // quotes.dat
|
|
_globals->loadMadsMessagesInfo(); // messages.dat
|
|
// TODO: objects.dat
|
|
// TODO: hoganus.dat (what is it used for?)
|
|
|
|
// Test code to dump all messages to the console
|
|
//for (int i = 0; i < _globals->getMessagesSize(); i++)
|
|
//printf("%s\n----------\n", _globals->loadMessage(i));
|
|
|
|
if ((getGameType() == GType_RexNebular) || (getGameType() == GType_DragonSphere)) {
|
|
loadMenu(MAIN_MENU);
|
|
|
|
} else {
|
|
if (getGameType() == GType_DragonSphere) {
|
|
_scene->loadScene(FIRST_SCENE);
|
|
} else if (getGameType() == GType_Phantom) {
|
|
//_scene->loadScene(FIRST_SCENE);
|
|
_scene->loadScene(106); // a more interesting scene
|
|
}
|
|
|
|
_viewManager->addView(_scene);
|
|
|
|
_font->setFont(FONT_MAIN_MADS);
|
|
_font->setColors(2, 1, 3);
|
|
_font->writeString(_scene->getBackgroundSurface(), "Testing the M4/MADS ScummVM engine", 5, 160, 310, 2);
|
|
_font->writeString(_scene->getBackgroundSurface(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 5, 180, 310, 2);
|
|
|
|
if (getGameType() == GType_DragonSphere) {
|
|
//_scene->showMADSV2TextBox("Test", 10, 10, NULL);
|
|
}
|
|
|
|
_mouse->cursorOn();
|
|
}
|
|
|
|
_viewManager->systemHotkeys().add(Common::KEYCODE_ESCAPE, &escapeHotkeyHandler);
|
|
_viewManager->systemHotkeys().add(Common::KEYCODE_KP_MULTIPLY, &textviewHotkeyHandler);
|
|
|
|
// Load the general game SFX/voices
|
|
if (getGameType() == GType_RexNebular) {
|
|
_sound->loadDSRFile("rex009.dsr");
|
|
} else if (getGameType() == GType_Phantom) {
|
|
_sound->loadDSRFile("phan009.dsr");
|
|
} else if (getGameType() == GType_DragonSphere) {
|
|
_sound->loadDSRFile("drag009.dsr");
|
|
}
|
|
|
|
uint32 nextFrame = g_system->getMillis();
|
|
while (!_events->quitFlag) {
|
|
eventHandler();
|
|
|
|
_animation->updateAnim();
|
|
|
|
// Call the updateState method of all views
|
|
_viewManager->updateState();
|
|
|
|
if (g_system->getMillis() >= nextFrame) {
|
|
|
|
_viewManager->refreshAll();
|
|
nextFrame = g_system->getMillis();// + GAME_FRAME_DELAY;
|
|
}
|
|
|
|
g_system->delayMillis(10);
|
|
}
|
|
|
|
return Common::kNoError;
|
|
}
|
|
|
|
Common::Error M4Engine::goM4() {
|
|
|
|
_script->open("m4.dat");
|
|
|
|
#ifdef SCRIPT_TEST
|
|
|
|
#if 0
|
|
ScriptFunction *func = _script->loadFunction("room_parser_142");
|
|
_script->runFunction(func);
|
|
#endif
|
|
|
|
#if 1
|
|
ScriptFunction *func = _script->loadFunction("room_daemon_951");
|
|
for (int i = 1; i < 58; i++) {
|
|
_vm->_kernel->trigger = i;
|
|
_script->runFunction(func);
|
|
printf("=================================\n");
|
|
fflush(stdout);
|
|
}
|
|
#endif
|
|
|
|
return Common::kNoError;
|
|
#endif
|
|
|
|
// Set up the inventory
|
|
|
|
// Set up the game interface view
|
|
//_interfaceView->inventoryAdd("Money", "", 55); // Sample item
|
|
|
|
if (getGameType() == GType_Burger) {
|
|
for (int i = 0; i < ARRAYSIZE(burger_inventory); i++) {
|
|
char* itemName = strdup(burger_inventory[i].name);
|
|
_inventory->registerObject(itemName, burger_inventory[i].scene,
|
|
burger_inventory[i].icon);
|
|
_inventory->addToBackpack(i); // debug: this adds ALL objects to the player's backpack
|
|
}
|
|
|
|
_viewManager->addView(_interfaceView);
|
|
}
|
|
|
|
// Show intro
|
|
|
|
if (getGameType() == GType_Burger) {
|
|
_kernel->newRoom = TITLE_SCENE_BURGER;
|
|
} else {
|
|
_scene->getBackgroundSurface()->loadBackgroundRiddle("main menu");
|
|
_ws->setBackgroundSurface(_scene->getBackgroundSurface());
|
|
}
|
|
|
|
_viewManager->addView(_scene);
|
|
|
|
// Setup game wide hotkeys. Note that Orion Burger used F2/F3 for Save/Restore,
|
|
// but for standardisation with most other games, F5/F7 are also mapped
|
|
|
|
_viewManager->systemHotkeys().add(Common::KEYCODE_ESCAPE, &escapeHotkeyHandler);
|
|
_viewManager->systemHotkeys().add(Common::KEYCODE_F2, &saveGameHotkeyHandler);
|
|
_viewManager->systemHotkeys().add(Common::KEYCODE_F3, &loadGameHotkeyHandler);
|
|
_viewManager->systemHotkeys().add(Common::KEYCODE_F5, &saveGameHotkeyHandler);
|
|
_viewManager->systemHotkeys().add(Common::KEYCODE_F7, &loadGameHotkeyHandler);
|
|
_viewManager->systemHotkeys().add(Common::KEYCODE_F9, &gameMenuHotkeyHandler);
|
|
|
|
// Start playing Orion Burger intro music
|
|
//_midi->playMusic("999intro", 255, false, -1, -1);
|
|
|
|
// TODO: start playing intro animations
|
|
|
|
// TODO: Master Lu
|
|
|
|
// Test for mouse
|
|
_mouse->init("cursor", NULL);
|
|
_mouse->setCursorNum(0);
|
|
_mouse->cursorOn();
|
|
|
|
_ws->assets()->loadAsset("SHOW SCRIPT", NULL);
|
|
_ws->assets()->loadAsset("STREAM SCRIPT", NULL);
|
|
|
|
#ifdef INTRO_TEST
|
|
int curPart = 0;
|
|
Machine *mach = NULL;
|
|
#endif
|
|
|
|
_ws->setSurfaceView(_scene);
|
|
|
|
uint32 nextFrame = g_system->getMillis();
|
|
while (!_events->quitFlag) {
|
|
|
|
// This should probably be moved to either Scene or Kernel
|
|
if (_kernel->currentRoom != _kernel->newRoom) {
|
|
|
|
_ws->clear();
|
|
|
|
_kernel->currentSection = _kernel->newRoom / 100;
|
|
_kernel->currentRoom = _kernel->newRoom;
|
|
|
|
_scene->loadScene(_kernel->currentRoom);
|
|
|
|
_ws->setBackgroundSurface(_scene->getBackgroundSurface());
|
|
_ws->setInverseColorTable(_scene->getInverseColorTable());
|
|
|
|
_kernel->loadSectionScriptFunctions();
|
|
_kernel->loadRoomScriptFunctions();
|
|
|
|
_kernel->roomInit();
|
|
|
|
#ifdef INTRO_TEST
|
|
if (_kernel->currentRoom == 951) {
|
|
curPart = 0;
|
|
mach = _ws->streamSeries("PLANET X HILLTOP A", 1, 0x1000, 0);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
eventHandler();
|
|
|
|
// Call the updateState method of all views
|
|
_viewManager->updateState();
|
|
|
|
// Handle frame updates
|
|
if (g_system->getMillis() >= nextFrame) {
|
|
#ifdef INTRO_TEST
|
|
// Orion Burger intro test (scene 951)
|
|
// This is ugly and bad, machine is not deleted so there's a huge memory
|
|
// leak too. But hey, we can see some of the intro!
|
|
if (mach && mach->getState() == -1) {
|
|
if (curPart == 0)
|
|
mach = _ws->streamSeries("Planet X Low Ground Shot", 1, 0x1000, 0);
|
|
else if (curPart == 1)
|
|
mach = _ws->streamSeries("Planet X Hilltop B", 1, 0x1000, 0);
|
|
else if (curPart == 2)
|
|
mach = _ws->streamSeries("Space Station Panorama A", 1, 0x1000, 0);
|
|
else if (curPart == 3)
|
|
mach = _ws->streamSeries("Cargo Transfer Area A", 1, 0x1000, 0);
|
|
else if (curPart == 4)
|
|
mach = _ws->streamSeries("VP's Office A", 1, 0x1000, 0);
|
|
else if (curPart == 5)
|
|
mach = _ws->streamSeries("Hologram", 1, 0x1000, 0);
|
|
else if (curPart == 6)
|
|
mach = _ws->streamSeries("VP's Office B", 1, 0x1000, 0);
|
|
else if (curPart == 7)
|
|
mach = _ws->streamSeries("Cargo Transfer Area B", 1, 0x1000, 0);
|
|
else if (curPart == 8)
|
|
mach = _ws->streamSeries("Cargo Transfer Controls", 1, 0x1000, 0);
|
|
else if (curPart == 9)
|
|
mach = _ws->streamSeries("Space Station Panorama B", 1, 0x1000, 0);
|
|
// This last scene is from the rolling demo
|
|
//else if (curPart == 10)
|
|
// mach = _ws->streamSeries("Call To Action", 1, 0x1000, 0);
|
|
curPart++;
|
|
}
|
|
#endif
|
|
_ws->update();
|
|
_viewManager->refreshAll();
|
|
nextFrame = g_system->getMillis();// + GAME_FRAME_DELAY;
|
|
|
|
// TEST STUFF ONLY
|
|
if (_player->commandReady) {
|
|
_kernel->roomParser();
|
|
_player->commandReady = false;
|
|
}
|
|
|
|
}
|
|
|
|
g_system->delayMillis(10);
|
|
}
|
|
|
|
return Common::kNoError;
|
|
}
|
|
|
|
void M4Engine::dumpFile(const char* filename, bool uncompress) {
|
|
Common::SeekableReadStream *fileS = res()->get(filename);
|
|
byte buffer[256];
|
|
FILE *destFile = fopen(filename, "wb");
|
|
int bytesRead = 0;
|
|
printf("Dumping %s, size: %i\n", filename, fileS->size());
|
|
|
|
if (!uncompress) {
|
|
while (!fileS->eos()) {
|
|
bytesRead = fileS->read(buffer, 256);
|
|
fwrite(buffer, bytesRead, 1, destFile);
|
|
}
|
|
} else {
|
|
MadsPack packData(fileS);
|
|
Common::MemoryReadStream *sourceUnc;
|
|
for (int i = 0; i < packData.getCount(); i++) {
|
|
sourceUnc = packData.getItemStream(i);
|
|
printf("Dumping compressed chunk %i of %i, size is %i\n", i + 1, packData.getCount(), sourceUnc->size());
|
|
while (!sourceUnc->eos()) {
|
|
bytesRead = sourceUnc->read(buffer, 256);
|
|
fwrite(buffer, bytesRead, 1, destFile);
|
|
}
|
|
delete sourceUnc;
|
|
}
|
|
}
|
|
|
|
fclose(destFile);
|
|
res()->toss(filename);
|
|
res()->purge();
|
|
}
|
|
|
|
} // End of namespace M4
|