From 7ca439f410ac1c46a387567b30271ae4e4a2ed30 Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Sun, 20 Apr 2008 14:47:37 +0000 Subject: Initial import of the work in progress M4 engine svn-id: r31600 --- engines/m4/m4.cpp | 555 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 555 insertions(+) create mode 100644 engines/m4/m4.cpp (limited to 'engines/m4/m4.cpp') diff --git a/engines/m4/m4.cpp b/engines/m4/m4.cpp new file mode 100644 index 0000000000..6707639d1c --- /dev/null +++ b/engines/m4/m4.cpp @@ -0,0 +1,555 @@ +/* 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/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; + + Common::File::addDefaultDirectory(_gameDataPath); + Common::File::addDefaultDirectory("goodstuf"); + Common::File::addDefaultDirectory("resource"); + + Common::addSpecialDebugLevel(kDebugScript, "script", "Script debug level"); + Common::addSpecialDebugLevel(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; +} + +int M4Engine::init() { + // Initialize backend + _system->beginGFXTransaction(); + initCommonGFX(isM4()); + if (isM4()) + _system->initSize(640, 480); + else + _system->initSize(320, 200); + _system->endGFXTransaction(); + + _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_system->getEventManager()->registerRandomSource(*_random, "m4"); + + return 0; +} + +void M4Engine::eventHandler() { + M4EventType event; + uint32 keycode = 0; + + if ((event = _events->handleEvents()) != MEVENT_NO_EVENT) { + if (_viewManager->viewCount() > 0) + _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); +} + +int M4Engine::go() { + if (isM4()) + return goM4(); + else + return goMADS(); +} + +int 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 0; +} + +int 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 0; +#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"); + + _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 0; +} + +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 -- cgit v1.2.3