aboutsummaryrefslogtreecommitdiff
path: root/engines/m4/m4.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/m4/m4.cpp')
-rw-r--r--engines/m4/m4.cpp555
1 files changed, 555 insertions, 0 deletions
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