aboutsummaryrefslogtreecommitdiff
path: root/engines/lastexpress/game/logic.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/lastexpress/game/logic.cpp')
-rw-r--r--engines/lastexpress/game/logic.cpp593
1 files changed, 593 insertions, 0 deletions
diff --git a/engines/lastexpress/game/logic.cpp b/engines/lastexpress/game/logic.cpp
new file mode 100644
index 0000000000..3ef710a324
--- /dev/null
+++ b/engines/lastexpress/game/logic.cpp
@@ -0,0 +1,593 @@
+/* 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$
+ *
+ */
+
+#include "lastexpress/game/logic.h"
+
+// Data
+#include "lastexpress/data/animation.h"
+#include "lastexpress/data/cursor.h"
+#include "lastexpress/data/snd.h"
+
+// Entities
+#include "lastexpress/entities/chapters.h"
+
+// Game
+#include "lastexpress/game/action.h"
+#include "lastexpress/game/beetle.h"
+#include "lastexpress/game/entities.h"
+#include "lastexpress/game/fight.h"
+#include "lastexpress/game/inventory.h"
+#include "lastexpress/game/menu.h"
+#include "lastexpress/game/object.h"
+#include "lastexpress/game/savegame.h"
+#include "lastexpress/game/savepoint.h"
+#include "lastexpress/game/scenes.h"
+#include "lastexpress/game/sound.h"
+#include "lastexpress/game/state.h"
+
+#include "lastexpress/graphics.h"
+#include "lastexpress/helpers.h"
+#include "lastexpress/lastexpress.h"
+#include "lastexpress/resource.h"
+
+namespace LastExpress {
+
+#define EVENT_TICKS_BEETWEEN_SAVEGAMES 450
+#define GAME_TICKS_BEETWEEN_SAVEGAMES 2700
+
+Logic::Logic(LastExpressEngine *engine) : _engine(engine) {
+ _action = new Action(engine);
+ _beetle = new Beetle(engine);
+ _entities = new Entities(engine);
+ _fight = new Fight(engine);
+ _saveload = new SaveLoad(engine);
+ _state = new State(engine);
+
+ // Flags
+ _flagActionPerformed = false;
+ _ignoreFrameInterval = false;
+ _ticksSinceLastSavegame = EVENT_TICKS_BEETWEEN_SAVEGAMES;
+}
+
+Logic::~Logic() {
+ delete _action;
+ delete _beetle;
+ delete _fight;
+ delete _entities;
+ delete _saveload;
+ delete _state;
+
+ // Zero-out passed pointers
+ _engine = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Event Handling
+//////////////////////////////////////////////////////////////////////////
+#define REDRAW_CURSOR() { \
+ if (getInventory()->isMagnifierInUse()) \
+ _engine->getCursor()->setStyle(kCursorMagnifier); \
+ if (getInventory()->isFlag1() \
+ || getInventory()->isFlag2() \
+ || getInventory()->isEggHighlighted()) \
+ _engine->getCursor()->setStyle(kCursorNormal); \
+ return; \
+}
+
+void Logic::eventMouse(const Common::Event &ev) {
+ bool hotspotHandled = false;
+
+ // Reset mouse flags
+ getFlags()->mouseLeftClick = false;
+ getFlags()->mouseRightClick = false;
+
+ // Process event flags
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+
+ if (getFlags()->frameInterval)
+ _ignoreFrameInterval = false;
+
+ getFlags()->frameInterval = false;
+ }
+
+ if (getFlags()->flag_0) {
+ if (ev.type == Common::EVENT_LBUTTONUP || ev.type == Common::EVENT_RBUTTONUP) {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ getFlags()->frameInterval = true;
+ }
+ return;
+ }
+
+ if (_ignoreFrameInterval && getScenes()->checkCurrentPosition(true) && _engine->getCursor()->getStyle() == kCursorForward) {
+ getFlags()->shouldRedraw = false;
+ getFlags()->flag_0 = true;
+ return;
+ }
+
+ // Update coordinates
+ getGameState()->setCoordinates(ev.mouse);
+
+ // Handle inventory
+ getInventory()->handleMouseEvent(ev);
+
+ // Stop processing is inside the menu
+ if (getMenu()->isShown())
+ return;
+
+ // Handle whistle case
+ if (getInventory()->getSelectedItem() == kItemWhistle
+ && !getProgress().isEggOpen
+ && !getEntities()->isPlayerPosition(kCarGreenSleeping, 59)
+ && !getEntities()->isPlayerPosition(kCarGreenSleeping, 76)
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+
+ // Update cursor
+ _engine->getCursor()->setStyle(getInventory()->get(kItemWhistle)->cursor);
+
+ // Check if clicked
+ if (ev.type == Common::EVENT_LBUTTONUP && !getSound()->isBuffered("LIB045")) {
+
+ getSound()->playSoundEvent(kEntityPlayer, 45);
+
+ if (getEntities()->isPlayerPosition(kCarGreenSleeping, 26) || getEntities()->isPlayerPosition(kCarGreenSleeping, 25) || getEntities()->isPlayerPosition(kCarGreenSleeping, 23)) {
+ getSavePoints()->push(kEntityPlayer, kEntityMertens, kAction226078300);
+ } else if (getEntities()->isPlayerPosition(kCarRedSleeping, 26) || getEntities()->isPlayerPosition(kCarRedSleeping, 25) || getEntities()->isPlayerPosition(kCarRedSleeping, 23)) {
+ getSavePoints()->push(kEntityPlayer, kEntityCoudert, kAction226078300);
+ }
+
+ if (!getState()->sceneUseBackup)
+ getInventory()->unselectItem();
+ }
+
+ REDRAW_CURSOR()
+ }
+
+ // Handle match case
+ if (getInventory()->getSelectedItem() == kItemMatch
+ && (getEntities()->isPlayerInCar(kCarGreenSleeping) || getEntities()->isPlayerInCar(kCarRedSleeping))
+ && getProgress().jacket == kJacketGreen
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()
+ && (getInventory()->get(kItem2)->location == kObjectLocationNone || getEntityData(kEntityPlayer)->car != kCarRedSleeping || getEntityData(kEntityPlayer)->entityPosition != kPosition_2300)) {
+
+ // Update cursor
+ _engine->getCursor()->setStyle(getInventory()->get(kItemMatch)->cursor);
+
+ if (ev.type == Common::EVENT_LBUTTONUP) {
+
+ getAction()->playAnimation(isNight() ? kEventCathSmokeNight : kEventCathSmokeDay);
+
+ if (!getState()->sceneUseBackup)
+ getInventory()->unselectItem();
+
+ getScenes()->processScene();
+ }
+
+ REDRAW_CURSOR()
+ }
+
+ // Handle entity item case
+ EntityIndex entityIndex = getEntities()->canInteractWith(ev.mouse);
+ if (entityIndex
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+
+ InventoryItem item = getEntityData(entityIndex)->inventoryItem;
+ if (getInventory()->hasItem((InventoryItem)(item & kItemToggleHigh))) {
+ hotspotHandled = true;
+
+ _engine->getCursor()->setStyle(getInventory()->get((InventoryItem)(item & kItemToggleHigh))->cursor);
+
+ if (ev.type == Common::EVENT_LBUTTONUP)
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction1, (InventoryItem)(item & kItemToggleHigh));
+ } else if ((InventoryItem)(item & kItemInvalid)) {
+ hotspotHandled = true;
+
+ _engine->getCursor()->setStyle(kCursorTalk2);
+
+ if (ev.type == Common::EVENT_LBUTTONUP)
+ getSavePoints()->push(kEntityPlayer, entityIndex, kAction1, kCursorNormal);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Handle standard actions
+ if (hotspotHandled || getInventory()->isFlag1() || getInventory()->isFlag2() || getInventory()->isEggHighlighted())
+ return;
+
+ // Magnifier in use
+ if (getInventory()->isMagnifierInUse()) {
+ _engine->getCursor()->setStyle(kCursorMagnifier);
+
+ if (getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted())
+ _engine->getCursor()->setStyle(kCursorNormal);
+
+ return;
+ }
+
+ // Check hotspots
+ int location = 0;
+ SceneHotspot *hotspot = NULL;
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ for (Common::Array<SceneHotspot *>::iterator it = scene->getHotspots()->begin(); it != scene->getHotspots()->end(); ++it) {
+ if (!(*it)->isInside(ev.mouse))
+ continue;
+
+ if ((*it)->location < location)
+ continue;
+
+ if (!getAction()->getCursor(**it))
+ continue;
+
+ Scene *hotspotScene = getScenes()->get((*it)->scene);
+
+ if (!getEntities()->getPosition(hotspotScene->car, hotspotScene->position)
+ || (*it)->cursor == kCursorTurnRight
+ || (*it)->cursor == kCursorTurnLeft) {
+ location = (*it)->location;
+ hotspot = *it;
+ }
+ }
+
+ // No hotspot found: show the normal cursor
+ if (!hotspot) {
+ _engine->getCursor()->setStyle(kCursorNormal);
+ return;
+ }
+
+ // Found an hotspot: update the cursor and perform the action if the user clicked the mouse
+ _engine->getCursor()->setStyle(getAction()->getCursor(*hotspot));
+
+ if (ev.type != Common::EVENT_LBUTTONUP || _flagActionPerformed)
+ return;
+
+ _flagActionPerformed = true;
+
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot);
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+
+ if (testScene) {
+ getFlags()->shouldRedraw = false;
+
+ getScenes()->setScene(testScene);
+
+ if (getFlags()->shouldDrawEggOrHourGlass)
+ getInventory()->drawEgg();
+
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ // Switch to next chapter if necessary
+ if (hotspot->action == SceneHotspot::kActionSwitchChapter && hotspot->param1 == getState()->progress.chapter)
+ switchChapter();
+}
+
+void Logic::eventTick(const Common::Event &) {
+ uint ticks = 1;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Adjust ticks if an action has been performed
+ if (_flagActionPerformed)
+ ticks = 10;
+
+ _flagActionPerformed = false;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Draw the blinking egg if needed
+ if (getGlobalTimer() && !getFlags()->shouldDrawEggOrHourGlass)
+ getInventory()->drawBlinkingEgg();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Adjust time and save game if needed
+ if (getFlags()->isGameRunning) {
+ getState()->timeTicks += ticks;
+ getState()->time = (TimeValue)(getState()->time + (TimeValue)(ticks * getState()->timeDelta));
+
+ if (getState()->timeDelta) {
+
+ // Auto-save
+ if (!_ticksSinceLastSavegame) {
+ _ticksSinceLastSavegame = EVENT_TICKS_BEETWEEN_SAVEGAMES;
+ getSaveLoad()->saveGame(kSavegameTypeAuto, kEntityChapters, kEventNone);
+ }
+
+ // Save after game ticks interval
+ if ((getState()->timeTicks - getSaveLoad()->getLastSavegameTicks()) > GAME_TICKS_BEETWEEN_SAVEGAMES)
+ getSaveLoad()->saveGame(kSavegameTypeTickInterval, kEntityChapters, kEventNone);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Load scene and process hotspot
+ if (getFlags()->flag_0 && !getFlags()->mouseLeftClick && !getFlags()->mouseRightClick) {
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ if (getScenes()->checkCurrentPosition(true)
+ && !getEntities()->getPosition(scene->car, scene->position)) {
+
+ // Process hotspot
+ SceneHotspot *hotspot = scene->getHotspot();
+ SceneIndex processedScene = getAction()->processHotspot(*hotspot);
+ SceneIndex testScene = (processedScene == kSceneInvalid) ? hotspot->scene : processedScene;
+
+ if (testScene) {
+ getScenes()->setScene(testScene);
+ } else {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ if (getFlags()->isGameRunning)
+ getSavePoints()->callAndProcess();
+
+ } else {
+ getFlags()->flag_0 = false;
+ getFlags()->shouldRedraw = true;
+ updateCursor(true);
+ }
+
+ return;
+ }
+
+ // Stop processing if the game is paused
+ if (!getFlags()->isGameRunning)
+ return;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Update beetle, savepoints, entities and draw frames
+ if (_beetle->isLoaded())
+ _beetle->update();
+
+ getSavePoints()->callAndProcess();
+ getEntities()->updateCallbacks();
+ getScenes()->drawFrames(true);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Update cursor if we can interact with an entity
+ EntityIndex entity = getEntities()->canInteractWith(getCoords());
+ if (!entity) {
+ if (_engine->getCursor()->getStyle() >= kCursorTalk2)
+ updateCursor(false);
+
+ return;
+ }
+
+ // Show item cursor on entity
+ if (getInventory()->hasItem((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh)) && (int)getEntityData(entity)->inventoryItem != (int)kCursorTalk2) {
+ _engine->getCursor()->setStyle(getInventory()->get((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))->cursor);
+ return;
+ }
+
+ getLogic()->updateCursor(false);
+ _engine->getCursor()->setStyle(kCursorTalk2);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Game over, Chapters & credits
+//////////////////////////////////////////////////////////////////////////
+
+/**
+ * Resets the game state.
+ */
+void Logic::resetState() {
+ getState()->scene = kSceneDefault;
+
+ warning("Logic::resetState: not implemented! You need to restart the engine until this is implemented.");
+}
+
+/**
+ * Handle game over
+ *
+ * @param type The savegame type.
+ * @param value The value (event, time, index, ...)
+ * @param sceneIndex Index of the scene to show.
+ * @param showScene true to show a scene, false to return to menu directly
+ */
+void Logic::gameOver(SavegameType type, uint32 value, SceneIndex sceneIndex, bool showScene) const {
+
+ getSound()->processEntries();
+ getEntities()->reset();
+ getFlags()->isGameRunning = false;
+ getSavePoints()->reset();
+ getFlags()->flag_entities_0 = true;
+
+ if (showScene) {
+
+ getSound()->processEntry(SoundManager::kSoundType11);
+
+ if (sceneIndex && !getFlags()->mouseRightClick) {
+ getScenes()->loadScene(sceneIndex);
+
+ while (getSound()->isBuffered(kEntityTables4)) {
+ if (getFlags()->mouseRightClick)
+ break;
+
+ getSound()->updateQueue();
+ }
+ }
+ }
+
+ // Show Menu
+ getMenu()->show(false, type, value);
+}
+
+void Logic::switchChapter() const {
+ getSound()->clearStatus();
+
+ switch(getState()->progress.chapter) {
+ default:
+ break;
+
+ case kChapter1:
+ getInventory()->addItem(kItemParchemin);
+ getInventory()->addItem(kItemMatchBox);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter2);
+ break;
+
+ case kChapter2:
+ getInventory()->addItem(kItemScarf);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter3);
+ break;
+
+ case kChapter3:
+ getInventory()->get(kItemFirebird)->location = kObjectLocation4;
+ getInventory()->get(kItemFirebird)->isPresent = false;
+ getInventory()->get(kItem11)->location = kObjectLocation1;
+ getInventory()->addItem(kItemWhistle);
+ getInventory()->addItem(kItemKey);
+
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter4);
+ break;
+
+ case kChapter4:
+ RESET_ENTITY_STATE(kEntityChapters, Chapters, setup_chapter5);
+ break;
+
+ case kChapter5:
+ playFinalSequence();
+ break;
+ }
+}
+
+void Logic::playFinalSequence() const {
+ getSound()->processEntries();
+
+ _action->playAnimation(kEventFinalSequence);
+ showCredits();
+
+ getEntities()->reset();
+ getSavePoints()->reset();
+ getFlags()->flag_entities_0 = true;
+
+ getMenu()->show(false, kSavegameTypeIndex, 0);
+}
+
+void Logic::showCredits() const {
+ error("Logic::showCredits: not implemented!");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Misc
+//////////////////////////////////////////////////////////////////////////
+void Logic::updateCursor(bool) const { /* the cursor is always updated, even when we don't want to redraw it */
+ CursorStyle style = kCursorNormal;
+ bool interact = false;
+
+ if (getInventory()->getSelectedItem() != kItemWhistle
+ || getProgress().isEggOpen
+ || getEntities()->isPlayerPosition(kCarGreenSleeping, 59)
+ || getEntities()->isPlayerPosition(kCarGreenSleeping, 76)
+ || getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted()
+ || getInventory()->isMagnifierInUse()) {
+
+ if (getInventory()->getSelectedItem() != kItemMatch
+ || (!getEntities()->isPlayerInCar(kCarGreenSleeping) && !getEntities()->isPlayerInCar(kCarRedSleeping))
+ || getProgress().jacket != kJacketGreen
+ || getInventory()->isFlag1()
+ || getInventory()->isFlag2()
+ || getInventory()->isEggHighlighted()
+ || getInventory()->isMagnifierInUse()
+ || (getInventory()->get(kItem2)->location
+ && getEntityData(kEntityPlayer)->car == kCarRedSleeping
+ && getEntityData(kEntityPlayer)->entityPosition == kPosition_2300)) {
+
+ EntityIndex entity = getEntities()->canInteractWith(getCoords());
+ if (entity
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+ if (getInventory()->hasItem((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))) {
+ interact = true;
+ style = getInventory()->get((InventoryItem)(getEntityData(entity)->inventoryItem & kItemToggleHigh))->cursor;
+ } else if ((int)getEntityData(entity)->inventoryItem == kItemInvalid) {
+ interact = true;
+ style = kCursorTalk2;
+ }
+ }
+
+ if (!interact
+ && !getInventory()->isFlag1()
+ && !getInventory()->isFlag2()
+ && !getInventory()->isEggHighlighted()
+ && !getInventory()->isMagnifierInUse()) {
+ int location = 0;
+ SceneHotspot *hotspot = NULL;
+ Scene *scene = getScenes()->get(getState()->scene);
+
+ // Check all hotspots
+ for (Common::Array<SceneHotspot *>::iterator i = scene->getHotspots()->begin(); i != scene->getHotspots()->end(); ++i) {
+ if ((*i)->isInside(getCoords()) && (*i)->location >= location) {
+ if (getAction()->getCursor(**i)) {
+ Scene *hotspotScene = getScenes()->get((*i)->scene);
+
+ if (!getEntities()->getPosition(hotspotScene->car, hotspotScene->position)
+ || (*i)->cursor == kCursorTurnRight
+ || (*i)->cursor == kCursorTurnLeft) {
+ hotspot = *i;
+ location = (*i)->location;
+ }
+ }
+ }
+ }
+
+ style = (hotspot) ? getAction()->getCursor(*hotspot) : kCursorNormal;
+ }
+ } else {
+ style = getInventory()->get(kItemMatch)->cursor;
+ }
+
+ } else {
+ style = getInventory()->get(kItemWhistle)->cursor;
+ }
+
+ if (getInventory()->isMagnifierInUse())
+ style = kCursorMagnifier;
+
+ if (getInventory()->isFlag1() || getInventory()->isFlag2() || getInventory()->isEggHighlighted())
+ style = kCursorNormal;
+
+ _engine->getCursor()->setStyle(style);
+}
+
+} // End of namespace LastExpress