diff options
Diffstat (limited to 'engines/lastexpress/game')
-rw-r--r-- | engines/lastexpress/game/action.cpp | 35 | ||||
-rw-r--r-- | engines/lastexpress/game/beetle.cpp | 38 | ||||
-rw-r--r-- | engines/lastexpress/game/beetle.h | 1 | ||||
-rw-r--r-- | engines/lastexpress/game/entities.cpp | 79 | ||||
-rw-r--r-- | engines/lastexpress/game/entities.h | 1 | ||||
-rw-r--r-- | engines/lastexpress/game/inventory.cpp | 92 | ||||
-rw-r--r-- | engines/lastexpress/game/inventory.h | 24 | ||||
-rw-r--r-- | engines/lastexpress/game/logic.cpp | 40 | ||||
-rw-r--r-- | engines/lastexpress/game/logic.h | 3 | ||||
-rw-r--r-- | engines/lastexpress/game/object.cpp | 2 | ||||
-rw-r--r-- | engines/lastexpress/game/savegame.cpp | 454 | ||||
-rw-r--r-- | engines/lastexpress/game/savegame.h | 96 | ||||
-rw-r--r-- | engines/lastexpress/game/savepoint.cpp | 27 | ||||
-rw-r--r-- | engines/lastexpress/game/savepoint.h | 10 | ||||
-rw-r--r-- | engines/lastexpress/game/scenes.cpp | 37 | ||||
-rw-r--r-- | engines/lastexpress/game/scenes.h | 1 | ||||
-rw-r--r-- | engines/lastexpress/game/state.cpp | 12 | ||||
-rw-r--r-- | engines/lastexpress/game/state.h | 2 |
18 files changed, 688 insertions, 266 deletions
diff --git a/engines/lastexpress/game/action.cpp b/engines/lastexpress/game/action.cpp index 98d74dd1a7..796abf2ce7 100644 --- a/engines/lastexpress/game/action.cpp +++ b/engines/lastexpress/game/action.cpp @@ -29,7 +29,6 @@ #include "lastexpress/entities/abbot.h" #include "lastexpress/entities/anna.h" -#include "lastexpress/entities/entity.h" #include "lastexpress/game/beetle.h" #include "lastexpress/game/entities.h" @@ -42,9 +41,7 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -332,6 +329,22 @@ static const struct { {"8042A", 600} }; +template<class Arg, class Res, class T> +class Functor1MemConst : public Common::Functor1<Arg, Res> { +public: + typedef Res (T::*FuncType)(Arg) const; + + Functor1MemConst(T *t, const FuncType &func) : _t(t), _func(func) {} + + bool isValid() const { return _func != 0 && _t != 0; } + Res operator()(Arg v1) const { + return (_t->*_func)(v1); + } +private: + mutable T *_t; + const FuncType _func; +}; + Action::Action(LastExpressEngine *engine) : _engine(engine) { ADD_ACTION(dummy); ADD_ACTION(inventory); @@ -381,7 +394,7 @@ Action::Action(LastExpressEngine *engine) : _engine(engine) { } Action::~Action() { - for (int i = 0; i < (int)_actions.size(); i++) + for (uint i = 0; i < _actions.size(); i++) SAFE_DELETE(_actions[i]); _actions.clear(); @@ -407,9 +420,7 @@ SceneIndex Action::processHotspot(const SceneHotspot &hotspot) { ////////////////////////////////////////////////////////////////////////// // Action 0 IMPLEMENT_ACTION(dummy) - warning("[Action::action_dummy] Dummy action function called (hotspot action: %d)", hotspot.action); - - return kSceneInvalid; + error("[Action::action_dummy] Dummy action function called (hotspot action: %d)", hotspot.action); } ////////////////////////////////////////////////////////////////////////// @@ -1742,14 +1753,14 @@ CursorStyle Action::getCursor(const SceneHotspot &hotspot) const { return kCursorBackward; case SceneHotspot::kActionKnockOnDoor: - warning("================================= DOOR %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= DOOR %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; else return (CursorStyle)getObjects()->get(object).cursor; case SceneHotspot::kAction12: - warning("================================= OBJECT %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= OBJECT %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; @@ -1759,7 +1770,7 @@ CursorStyle Action::getCursor(const SceneHotspot &hotspot) const { return kCursorNormal; case SceneHotspot::kActionPickItem: - warning("================================= ITEM %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= ITEM %03d =================================", object); if (object >= kObjectCompartmentA) return kCursorNormal; @@ -1770,7 +1781,7 @@ CursorStyle Action::getCursor(const SceneHotspot &hotspot) const { return kCursorNormal; case SceneHotspot::kActionDropItem: - warning("================================= ITEM %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= ITEM %03d =================================", object); if (object >= kObjectCompartmentA) return kCursorNormal; @@ -1887,7 +1898,7 @@ LABEL_KEY: // Handle compartment action case SceneHotspot::kActionCompartment: case SceneHotspot::kActionExitCompartment: - warning("================================= DOOR %03d =================================", object); + debugC(2, kLastExpressDebugScenes, "================================= DOOR %03d =================================", object); if (object >= kObjectMax) return kCursorNormal; diff --git a/engines/lastexpress/game/beetle.cpp b/engines/lastexpress/game/beetle.cpp index ab707ddae9..d7a369ba40 100644 --- a/engines/lastexpress/game/beetle.cpp +++ b/engines/lastexpress/game/beetle.cpp @@ -27,7 +27,6 @@ #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -95,7 +94,7 @@ void Beetle::load() { // Check that all sequences are loaded properly _data->isLoaded = true; - for (int i = 0; i < (int)_data->sequences.size(); i++) { + for (uint i = 0; i < _data->sequences.size(); i++) { if (!_data->sequences[i]->isLoaded()) { _data->isLoaded = false; break; @@ -337,26 +336,13 @@ void Beetle::drawUpdate() { } } -#define INVERT_Y() \ - switch (_data->indexes[_data->offset]) { \ - default: \ - break; \ - case 24: \ - case 25: \ - case 26: \ - case 27: \ - case 28: \ - _data->coordY = -_data->coordY; \ - break; \ - } - // Invert direction - INVERT_Y(); + invertDirection(); SequenceFrame *frame = new SequenceFrame(_data->currentSequence, (uint16)_data->currentFrame); updateFrame(frame); - INVERT_Y(); + invertDirection(); getScenes()->addToQueue(frame); @@ -364,6 +350,24 @@ void Beetle::drawUpdate() { _data->frame = frame; } +void Beetle::invertDirection() { + if (!_data) + error("[Beetle::invertDirection] Sequences have not been loaded"); + + switch (_data->indexes[_data->offset]) { + default: + break; + + case 24: + case 25: + case 26: + case 27: + case 28: + _data->coordY = -_data->coordY; + break; + } +} + void Beetle::move() { if (!_data) error("[Beetle::move] Sequences have not been loaded"); diff --git a/engines/lastexpress/game/beetle.h b/engines/lastexpress/game/beetle.h index d3c47f39e5..034ebbd557 100644 --- a/engines/lastexpress/game/beetle.h +++ b/engines/lastexpress/game/beetle.h @@ -111,6 +111,7 @@ private: void updateFrame(SequenceFrame *frame) const; void updateData(uint32 index); void drawUpdate(); + void invertDirection(); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/game/entities.cpp b/engines/lastexpress/game/entities.cpp index f27087a609..fafbd7cb64 100644 --- a/engines/lastexpress/game/entities.cpp +++ b/engines/lastexpress/game/entities.cpp @@ -27,8 +27,6 @@ #include "lastexpress/data/sequence.h" // Entities -#include "lastexpress/entities/entity.h" - #include "lastexpress/entities/abbot.h" #include "lastexpress/entities/alexei.h" #include "lastexpress/entities/alouan.h" @@ -71,10 +69,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -185,7 +181,7 @@ Entities::Entities(LastExpressEngine *engine) : _engine(engine) { Entities::~Entities() { SAFE_DELETE(_header); - for (int i = 0; i < (int)_entities.size(); i++) + for (uint i = 0; i < _entities.size(); i++) SAFE_DELETE(_entities[i]); _entities.clear(); @@ -673,11 +669,12 @@ void Entities::executeCallbacks() { ////////////////////////////////////////////////////////////////////////// // Processing ////////////////////////////////////////////////////////////////////////// -#define INCREMENT_DIRECTION_COUNTER() { \ - data->doProcessEntity = false; \ - if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight)) \ - ++data->field_4A1; \ - } +void Entities::incrementDirectionCounter(EntityData::EntityCallData *data) const { + data->doProcessEntity = false; + + if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight)) + ++data->field_4A1; +} void Entities::processEntity(EntityIndex entityIndex) { EntityData::EntityCallData *data = getData(entityIndex); @@ -696,7 +693,7 @@ void Entities::processEntity(EntityIndex entityIndex) { getScenes()->removeAndRedraw(&data->frame, false); getScenes()->removeAndRedraw(&data->frame1, false); - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); return; } @@ -726,7 +723,7 @@ label_nosequence: processFrame(entityIndex, false, true); if (!getFlags()->flag_entities_0 && !data->doProcessEntity) { - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); return; } } else { @@ -744,7 +741,7 @@ label_nosequence: data->position = 0; } - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); } return; } @@ -754,46 +751,44 @@ label_nosequence: if (data->frame->getInfo()->field_30 > (data->field_49B + 1) || (data->direction == kDirectionLeft && data->sequence->count() == 1)) { ++data->field_49B; - } else { - if (data->frame->getInfo()->field_30 > data->field_49B && !data->frame->getInfo()->keepPreviousFrame) { - ++data->field_49B; - } else { - if (data->frame->getInfo()->keepPreviousFrame == 1) - keepPreviousFrame = true; - - // Increment current frame - ++data->currentFrame; + } else if (data->frame->getInfo()->field_30 <= data->field_49B || data->frame->getInfo()->keepPreviousFrame) { + if (data->frame->getInfo()->keepPreviousFrame == 1) + keepPreviousFrame = true; - if (data->currentFrame > (int16)(data->sequence->count() - 1) || (data->field_4A9 && checkSequenceFromPosition(entityIndex))) { + // Increment current frame + ++data->currentFrame; - if (data->direction == kDirectionLeft) { - data->currentFrame = 0; - } else { - keepPreviousFrame = true; - drawNextSequence(entityIndex); + if (data->currentFrame > (int16)(data->sequence->count() - 1) || (data->field_4A9 && checkSequenceFromPosition(entityIndex))) { - if (getFlags()->flag_entities_0 || data->doProcessEntity) - return; + if (data->direction == kDirectionLeft) { + data->currentFrame = 0; + } else { + keepPreviousFrame = true; + drawNextSequence(entityIndex); - if (!data->sequence2) { - updateEntityPosition(entityIndex); - data->doProcessEntity = false; - return; - } + if (getFlags()->flag_entities_0 || data->doProcessEntity) + return; - copySequenceData(entityIndex); + if (!data->sequence2) { + updateEntityPosition(entityIndex); + data->doProcessEntity = false; + return; } + copySequenceData(entityIndex); } - processFrame(entityIndex, keepPreviousFrame, false); - - if (getFlags()->flag_entities_0 || data->doProcessEntity) - return; } + + processFrame(entityIndex, keepPreviousFrame, false); + + if (getFlags()->flag_entities_0 || data->doProcessEntity) + return; + } else { + ++data->field_49B; } - INCREMENT_DIRECTION_COUNTER(); + incrementDirectionCounter(data); } void Entities::computeCurrentFrame(EntityIndex entityIndex) const { @@ -2283,7 +2278,7 @@ label_process_entity: if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingUp)) { getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMeCath); - } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) || getScenes()->checkCurrentPosition(false)){ + } else if (getScenes()->checkPosition(kSceneNone, SceneManager::kCheckPositionLookingDown) || getScenes()->checkCurrentPosition(false)) { getSavePoints()->push(kEntityPlayer, entity, kActionExcuseMe); if (getScenes()->checkCurrentPosition(false)) diff --git a/engines/lastexpress/game/entities.h b/engines/lastexpress/game/entities.h index eb5eae461f..81aed627aa 100644 --- a/engines/lastexpress/game/entities.h +++ b/engines/lastexpress/game/entities.h @@ -344,6 +344,7 @@ private: uint _positions[_positionsCount]; void executeCallbacks(); + void incrementDirectionCounter(EntityData::EntityCallData *data) const; void processEntity(EntityIndex entity); void drawSequence(EntityIndex entity, const char *sequence, EntityDirection direction) const; diff --git a/engines/lastexpress/game/inventory.cpp b/engines/lastexpress/game/inventory.cpp index e417b1ec0d..11e7369ee1 100644 --- a/engines/lastexpress/game/inventory.cpp +++ b/engines/lastexpress/game/inventory.cpp @@ -26,17 +26,17 @@ #include "lastexpress/data/scene.h" #include "lastexpress/data/snd.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/logic.h" +#include "lastexpress/game/savegame.h" #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" #include "lastexpress/menu/menu.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -44,7 +44,7 @@ namespace LastExpress { Inventory::Inventory(LastExpressEngine *engine) : _engine(engine), _selectedItem(kItemNone), _highlightedItemIndex(0), _itemsShown(0), - _showingHourGlass(false), _blinkingEgg(false), _blinkingTime(0), _blinkingInterval(_defaultBlinkingInterval), _blinkingBrightness(1), + _showingHourGlass(false), _blinkingDirection(1), _blinkingBrightness(0), _useMagnifier(false), _portraitHighlighted(false), _isOpened(false), _eggHightlighted(false), _itemScene(NULL) { //_inventoryRect = Common::Rect(0, 0, 32, 32); @@ -162,13 +162,11 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { getMenu()->show(true, kSavegameTypeIndex, 0); - } else if (ev.type == Common::EVENT_RBUTTONDOWN) { - if (getGlobalTimer()) { - if (getSoundQueue()->isBuffered("TIMER")) - getSoundQueue()->removeFromQueue("TIMER"); + } else if (ev.type == Common::EVENT_RBUTTONDOWN && getGlobalTimer()) { + if (getSoundQueue()->isBuffered("TIMER")) + getSoundQueue()->removeFromQueue("TIMER"); - setGlobalTimer(900); - } + setGlobalTimer(900); } } @@ -180,7 +178,7 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { if (_highlightedItemIndex) drawHighlight(_highlightedItemIndex, true); } else { - // The user released the mouse button, we need to update the inventory state (clear hightlight and items) + // The user released the mouse button, we need to update the inventory state (clear highlight and items) drawItem((CursorStyle)getProgress().portrait, 0, 0, 1); _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(0, 44, 32, (int16)(40 * _itemsShown + 40))); _isOpened = false; @@ -226,12 +224,11 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { if (getFlags()->mouseLeftPressed) { if (getState()->sceneUseBackup) { - if (getState()->sceneBackup2 - && getFirstExaminableItem() == _selectedItem) { - SceneIndex sceneIndex = getState()->sceneBackup2; - getState()->sceneBackup2 = kSceneNone; + if (getState()->sceneBackup2 && getFirstExaminableItem() == _selectedItem) { + SceneIndex sceneIndex = getState()->sceneBackup2; + getState()->sceneBackup2 = kSceneNone; - getScenes()->loadScene(sceneIndex); + getScenes()->loadScene(sceneIndex); } } else { getState()->sceneBackup = getState()->scene; @@ -261,7 +258,7 @@ void Inventory::handleMouseEvent(const Common::Event &ev) { // Change item highlight on list if (getFlags()->mouseLeftPressed) { - uint32 index = ev.mouse.y / 40; + uint32 index = (uint16)ev.mouse.y / 40; if (_highlightedItemIndex && _highlightedItemIndex != index) drawHighlight(_highlightedItemIndex, true); @@ -418,12 +415,12 @@ void Inventory::show() { drawEgg(); } -void Inventory::setPortrait(InventoryItem item) { +void Inventory::setPortrait(InventoryItem item) const { getProgress().portrait = item; drawItem((CursorStyle)getProgress().portrait, 0, 0); } -void Inventory::showHourGlass(){ +void Inventory::showHourGlass() const { if (!getMenu()->isShown()) drawItem(kCursorHourGlass, 608, 448); @@ -613,7 +610,7 @@ void Inventory::examine(InventoryItem item) { } } -void Inventory::drawEgg() { +void Inventory::drawEgg() const { if (!getMenu()->isShown()) drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, _eggHightlighted ? 0 : 1); @@ -621,40 +618,51 @@ void Inventory::drawEgg() { } // Blinking egg: we need to blink the egg for delta time, with the blinking getting faster until it's always lit. -void Inventory::drawBlinkingEgg() { +void Inventory::drawBlinkingEgg(uint ticks) { + uint globalTimer = (uint)getGlobalTimer(); + uint timerValue = (getProgress().jacket == kJacketGreen) ? 450 : 225; - warning("[Inventory::drawBlinkingEgg] Blinking not implemented"); + if (globalTimer == timerValue || globalTimer == 900) { + _blinkingBrightness = 0; + _blinkingDirection = 1; + } - //// TODO show egg (with or without mouseover) + globalTimer = globalTimer <= ticks ? 0 : globalTimer - ticks; + setGlobalTimer(globalTimer); - //// Play timer sound - //if (getGlobalTimer() < 90) { - // if (getGlobalTimer() + ticks >= 90) - // getSound()->playSoundWithSubtitles("TIMER.SND", 50331664, kEntityPlayer); + if (getFlags()->flag_0 + || (globalTimer % 5) == 0 + || (globalTimer <= 500 && (globalTimer % ((globalTimer + 100) / 100)) == 0)) + blinkEgg(); - // if (getSoundQueue()->isBuffered("TIMER")) - // setGlobalTimer(0); - //} + if (globalTimer < 90) { + if ((globalTimer + ticks) >= 90) + getSound()->playSoundWithSubtitles("TIMER", (SoundFlag)(kFlagType13|kFlagDefault), kEntityPlayer); - //// Restore egg to standard brightness - //if (!getGlobalTimer()) { - // - //} + if (!getSoundQueue()->isBuffered("TIMER")) + setGlobalTimer(0); + } + if (globalTimer == 0) { + drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, _menuEggRect.contains(getCoords()) ? 1 : -1); - //drawItem(608, 448, getMenu()->getGameId() + 39, _blinkingBrightness) + askForRedraw(); - //// TODO if delta time > _blinkingInterval, update egg & ask for redraw then adjust blinking time and remaining time - // + getSaveLoad()->saveGame(kSavegameTypeAuto, kEntityChapters, 0); + } +} - //// Reset values and stop blinking - //if (_blinkingTime == 0) - // blinkEgg(false); +void Inventory::blinkEgg() { + drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, (_blinkingBrightness == 0) ? -1 : (int16)_blinkingBrightness); askForRedraw(); + + _blinkingBrightness += _blinkingDirection; + if (_blinkingBrightness == 0 || _blinkingBrightness == 3) + _blinkingDirection = -_blinkingDirection; } -void Inventory::drawItem(CursorStyle id, uint16 x, uint16 y, int16 brightnessIndex) { +void Inventory::drawItem(CursorStyle id, uint16 x, uint16 y, int16 brightnessIndex) const { Icon icon(id); icon.setPosition(x, y); @@ -678,7 +686,7 @@ void Inventory::drawSelectedItem() { } } -void Inventory::clearSelectedItem() { +void Inventory::clearSelectedItem() const { _engine->getGraphicsManager()->clear(GraphicsManager::kBackgroundInventory, Common::Rect(44, 0, 44 + 32, 32)); } @@ -733,7 +741,7 @@ void Inventory::drawHighlight(uint32 currentIndex, bool reset) { } } -uint32 Inventory::getItemIndex(uint32 currentIndex) { +uint32 Inventory::getItemIndex(uint32 currentIndex) const { uint32 count = 0; for (uint32 i = 1; i < ARRAYSIZE(_entries); i++) { diff --git a/engines/lastexpress/game/inventory.h b/engines/lastexpress/game/inventory.h index b1995adce3..b1019a43c6 100644 --- a/engines/lastexpress/game/inventory.h +++ b/engines/lastexpress/game/inventory.h @@ -106,11 +106,10 @@ public: // UI Control void show(); - void blinkEgg(bool enabled); - void showHourGlass(); - void setPortrait(InventoryItem item); - void drawEgg(); - void drawBlinkingEgg(); + void showHourGlass() const; + void setPortrait(InventoryItem item) const; + void drawEgg() const; + void drawBlinkingEgg(uint ticks = 1); // Handle inventory UI events. void handleMouseEvent(const Common::Event &ev); @@ -133,8 +132,6 @@ public: Common::String toString(); private: - static const uint32 _defaultBlinkingInterval = 250; ///< Default blinking interval in ms - LastExpressEngine *_engine; InventoryEntry _entries[32]; @@ -144,9 +141,7 @@ private: uint32 _itemsShown; bool _showingHourGlass; - bool _blinkingEgg; - uint32 _blinkingTime; - uint32 _blinkingInterval; + int16 _blinkingDirection; uint16 _blinkingBrightness; // Flags @@ -157,8 +152,6 @@ private: Scene *_itemScene; - // Important rects - //Common::Rect _inventoryRect; Common::Rect _menuEggRect; Common::Rect _selectedItemRect; @@ -168,14 +161,15 @@ private: void close(); void examine(InventoryItem item); void drawHighlight(uint32 currentIndex, bool reset); - uint32 getItemIndex(uint32 currentIndex); + uint32 getItemIndex(uint32 currentIndex) const; bool isItemSceneParameter(InventoryItem item) const; - void drawItem(CursorStyle id, uint16 x, uint16 y, int16 brighnessIndex = -1); + void drawItem(CursorStyle id, uint16 x, uint16 y, int16 brighnessIndex = -1) const; + void blinkEgg(); void drawSelectedItem(); - void clearSelectedItem(); + void clearSelectedItem() const; }; } // End of namespace LastExpress diff --git a/engines/lastexpress/game/logic.cpp b/engines/lastexpress/game/logic.cpp index aeac8cff98..09104d1bf9 100644 --- a/engines/lastexpress/game/logic.cpp +++ b/engines/lastexpress/game/logic.cpp @@ -47,10 +47,7 @@ #include "lastexpress/menu/menu.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" -#include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -88,16 +85,6 @@ Logic::~Logic() { ////////////////////////////////////////////////////////////////////////// // Event Handling ////////////////////////////////////////////////////////////////////////// -#define REDRAW_CURSOR() { \ - if (getInventory()->isMagnifierInUse()) \ - _engine->getCursor()->setStyle(kCursorMagnifier); \ - if (getInventory()->isPortraitHighlighted() \ - || getInventory()->isOpened() \ - || getInventory()->isEggHighlighted()) \ - _engine->getCursor()->setStyle(kCursorNormal); \ - return; \ -} - void Logic::eventMouse(const Common::Event &ev) { bool hotspotHandled = false; @@ -168,7 +155,9 @@ void Logic::eventMouse(const Common::Event &ev) { getInventory()->unselectItem(); } - REDRAW_CURSOR() + redrawCursor(); + + return; } // Handle match case @@ -194,7 +183,9 @@ void Logic::eventMouse(const Common::Event &ev) { getScenes()->processScene(); } - REDRAW_CURSOR() + redrawCursor(); + + return; } // Handle entity item case @@ -315,7 +306,7 @@ void Logic::eventTick(const Common::Event &) { ////////////////////////////////////////////////////////////////////////// // Draw the blinking egg if needed if (getGlobalTimer() && !getFlags()->shouldDrawEggOrHourGlass) - getInventory()->drawBlinkingEgg(); + getInventory()->drawBlinkingEgg(ticks); ////////////////////////////////////////////////////////////////////////// // Adjust time and save game if needed @@ -411,9 +402,12 @@ void Logic::eventTick(const Common::Event &) { * Resets the game state. */ void Logic::resetState() { - getState()->scene = kSceneDefault; + getScenes()->setCoordinates(Common::Rect(80, 0, 559, 479)); + + SAFE_DELETE(_entities); + _entities = new Entities(_engine); - warning("[Logic::resetState] Not implemented! You need to restart the engine until this is implemented."); + _state->reset(); } /** @@ -595,4 +589,14 @@ void Logic::updateCursor(bool) const { /* the cursor is always updated, even whe _engine->getCursor()->setStyle(style); } +void Logic::redrawCursor() const { + if (getInventory()->isMagnifierInUse()) + _engine->getCursor()->setStyle(kCursorMagnifier); + + if (getInventory()->isPortraitHighlighted() + || getInventory()->isOpened() + || getInventory()->isEggHighlighted()) + _engine->getCursor()->setStyle(kCursorNormal); +} + } // End of namespace LastExpress diff --git a/engines/lastexpress/game/logic.h b/engines/lastexpress/game/logic.h index 8b7dcef942..efb8f1e1a3 100644 --- a/engines/lastexpress/game/logic.h +++ b/engines/lastexpress/game/logic.h @@ -25,8 +25,6 @@ #include "lastexpress/shared.h" -#include "lastexpress/game/entities.h" - #include "lastexpress/eventhandler.h" #include "common/events.h" @@ -75,6 +73,7 @@ private: void switchChapter() const; void showCredits() const; + void redrawCursor() const; // Flags & Members bool _flagActionPerformed; diff --git a/engines/lastexpress/game/object.cpp b/engines/lastexpress/game/object.cpp index d9e9e4279a..48df91ea6d 100644 --- a/engines/lastexpress/game/object.cpp +++ b/engines/lastexpress/game/object.cpp @@ -22,11 +22,11 @@ #include "lastexpress/game/object.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/scenes.h" #include "lastexpress/game/state.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" namespace LastExpress { diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp index 9c464feb6e..021dc40bb9 100644 --- a/engines/lastexpress/game/savegame.cpp +++ b/engines/lastexpress/game/savegame.cpp @@ -22,6 +22,7 @@ #include "lastexpress/game/savegame.h" +#include "lastexpress/game/entities.h" #include "lastexpress/game/inventory.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/object.h" @@ -34,13 +35,13 @@ #include "lastexpress/debug.h" #include "lastexpress/lastexpress.h" -#include "lastexpress/helpers.h" #include "common/file.h" -#include "common/system.h" namespace LastExpress { +#define DISABLE_COMPRESSION 1 + // Names of savegames static const struct { const char *saveFile; @@ -54,14 +55,305 @@ static const struct { }; ////////////////////////////////////////////////////////////////////////// +// SavegameStream +////////////////////////////////////////////////////////////////////////// + +uint32 SavegameStream::write(const void *dataPtr, uint32 dataSize) { +#if !DISABLE_COMPRESSION + if (_enableCompression) + return writeCompressed(dataPtr, dataSize); +#endif + + return Common::MemoryWriteStreamDynamic::write(dataPtr, dataSize); +} + +uint32 SavegameStream::read(void *dataPtr, uint32 dataSize) { +#if !DISABLE_COMPRESSION + if (_enableCompression) + return readCompressed(dataPtr, dataSize); +#endif + + return readUncompressed(dataPtr, dataSize); +} + +uint32 SavegameStream::readUncompressed(void *dataPtr, uint32 dataSize) { + if ((int32)dataSize > size() - pos()) { + dataSize = (uint32)(size() - pos()); + _eos = true; + } + memcpy(dataPtr, getData() + pos(), dataSize); + + seek(dataSize, SEEK_CUR); + + return dataSize; +} + +void SavegameStream::writeBuffer(uint8 value, bool onlyValue) { + if (_bufferOffset == -1) + _bufferOffset = 0; + + if (_bufferOffset == 256) { + _bufferOffset = 0; + Common::MemoryWriteStreamDynamic::write(_buffer, 256); + } + + if (onlyValue || value < 0xFB) + _buffer[_bufferOffset] = value; + else + _buffer[_bufferOffset] = 0xFE; + + _offset++; + _bufferOffset++; + + if (!onlyValue && value >= 0xFB) + { + if (_bufferOffset == 256) { + _bufferOffset = 0; + Common::MemoryWriteStreamDynamic::write(_buffer, 256); + } + + _buffer[_bufferOffset] = value; + + _bufferOffset++; + _offset++; + } +} + +uint8 SavegameStream::readBuffer() { + if (_bufferOffset == -1 || _bufferOffset >= 256) { + readUncompressed(_buffer, 256); + _bufferOffset = 0; + } + + byte val = _buffer[_bufferOffset]; + _bufferOffset++; + + return val; +} + +uint32 SavegameStream::process() { + _enableCompression = !_enableCompression; + +#if DISABLE_COMPRESSION + return 0; +#else + switch (_status) { + default: + break; + + case kStatusReading: + _status = kStatusReady; + if (_bufferOffset != -1 && _bufferOffset != 256) { + seek(_bufferOffset - 256, SEEK_CUR); + _bufferOffset = -1; + } + break; + + case kStatusWriting: + switch (_valueCount) { + default: + break; + + case 1: + writeBuffer(_previousValue, false); + break; + + case 2: + if (_previousValue) { + writeBuffer(0xFF); + writeBuffer(_repeatCount); + writeBuffer(_previousValue); + break; + } + + if (_repeatCount == 3) { + writeBuffer(0xFB); + break; + } + + if (_repeatCount == 255) { + writeBuffer(0xFC); + break; + } + + writeBuffer(0xFD); + writeBuffer(_repeatCount); + break; + } + + if (_bufferOffset != -1 && _bufferOffset != 0) { + Common::MemoryWriteStreamDynamic::write(_buffer, _bufferOffset); + _bufferOffset = -1; + } + break; + } + + _status = kStatusReady; + _valueCount = 0; + uint32 offset = _offset; + _offset = 0; + + return offset; +#endif +} + +uint32 SavegameStream::writeCompressed(const void *dataPtr, uint32 dataSize) { + if (_status == kStatusReading) + error("[SavegameStream::writeCompressed] Error: Compression buffer is in read mode."); + + _status = kStatusWriting; + const byte *data = (const byte *)dataPtr; + + while (dataSize) { + switch (_valueCount) { + default: + error("[SavegameStream::writeCompressed] Invalid value count (%d)", _valueCount); + + case 0: + _previousValue = *data++; + _valueCount = 1; + break; + + case 1: + if (*data != _previousValue) { + writeBuffer(_previousValue, false); + _previousValue = *data; + } else { + _valueCount = 2; + _repeatCount = 2; + } + + ++data; + break; + + case 2: + if (*data != _previousValue || _repeatCount >= 255) { + if (_previousValue) { + writeBuffer(0xFF, true); + writeBuffer((uint8)_repeatCount, true); + writeBuffer(_previousValue, true); + + _previousValue = *data++; + _valueCount = 1; + break; + } + + if (_repeatCount == 3) { + writeBuffer(0xFB, true); + + _previousValue = *data++; + _valueCount = 1; + break; + } + + if (_repeatCount == -1) { + writeBuffer(0xFC, true); + + _previousValue = *data++; + _valueCount = 1; + break; + } + + writeBuffer(0xFD, true); + writeBuffer((uint8)_repeatCount, true); + + _previousValue = *data++; + _valueCount = 1; + } + + ++data; + ++_repeatCount; + break; + } + + --dataSize; + } + + return _offset; +} + +uint32 SavegameStream::readCompressed(void *dataPtr, uint32 dataSize) { + if (_status == kStatusWriting) + error("[SavegameStream::writeCompressed] Error: Compression buffer is in write mode."); + + _status = kStatusReady; + byte *data = (byte *)dataPtr; + + while (dataSize) { + switch (_valueCount) { + default: + error("[SavegameStream::readCompressed] Invalid value count (%d)", _valueCount); + + case 0: + case 1: { + // Read control code + byte control = readBuffer(); + + switch (control) { + default: + // Data value + *data++ = control; + break; + + case 0xFB: + _repeatCount = 2; + _previousValue = 0; + *data++ = 0; + _valueCount = 2; + break; + + case 0xFC: + _repeatCount = 254; + _previousValue = 0; + *data++ = 0; + _valueCount = 2; + break; + + case 0xFD: + _repeatCount = readBuffer() - 1; + _previousValue = 0; + *data++ = 0; + _valueCount = 2; + break; + + case 0xFE: + *data++ = readBuffer(); + break; + + case 0xFF: + _repeatCount = readBuffer() - 1; + _previousValue = readBuffer(); + *data++ = _previousValue; + _valueCount = 2; + break; + } + } + break; + + case 2: + *data++ = _previousValue; + _repeatCount--; + if (!_repeatCount) + _valueCount = 1; + break; + } + + --dataSize; + } + + return _offset; +} + +////////////////////////////////////////////////////////////////////////// // Constructors ////////////////////////////////////////////////////////////////////////// -SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0) { +SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0), _entity(kEntityPlayer) { } SaveLoad::~SaveLoad() { clear(true); + _savegame = NULL; // Zero passed pointers _engine = NULL; @@ -81,6 +373,7 @@ void SaveLoad::flushStream(GameId id) { error("[SaveLoad::flushStream] Savegame stream is invalid"); save->write(_savegame->getData(), (uint32)_savegame->size()); + save->finalize(); delete save; } @@ -189,10 +482,10 @@ void SaveLoad::clear(bool clearStream) { // Save & Load ////////////////////////////////////////////////////////////////////////// -// Load game -void SaveLoad::loadGame(GameId id) { +// Load last saved game +void SaveLoad::loadLastGame() { if (!_savegame) - error("[SaveLoad::loadGame] No savegame stream present"); + error("[SaveLoad::loadLastGame] No savegame stream present"); // Rewind current savegame _savegame->seek(0); @@ -229,7 +522,29 @@ void SaveLoad::loadGame(GameId id) { } // Load a specific game entry -void SaveLoad::loadGame(GameId id, uint32 index) { +void SaveLoad::loadGame(uint32 index) { + if (!_savegame) + error("[SaveLoad::loadLastGame] No savegame stream present"); + + // Rewind current savegame + _savegame->seek(0); + + // Write main header (with selected index) + SavegameMainHeader header; + header.count = index; + header.brightness = getState()->brightness; + header.volume = getState()->volume; + + Common::Serializer ser(NULL, _savegame); + header.saveLoadWithSerializer(ser); + + // TODO + // Go to the entry + // Load the entry + // Get offset (main and entry) + // Write main header again with correct entry offset + // Setup game and start + error("[SaveLoad::loadGame] Not implemented! (only loading the last entry is working for now)"); } @@ -341,16 +656,52 @@ bool SaveLoad::loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *he ////////////////////////////////////////////////////////////////////////// // Entries ////////////////////////////////////////////////////////////////////////// -void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { -#define WRITE_ENTRY(name, func, val) { \ - uint32 _prevPosition = (uint32)_savegame->pos(); \ - func; \ - uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ - debugC(kLastExpressDebugSavegame, "Savegame: Writing " #name ": %d bytes", _count); \ - if (_count != val)\ - error("[SaveLoad::writeEntry] Number of bytes written (%d) differ from expected count (%d)", _count, val); \ +uint32 SaveLoad::writeValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size) { + if (!_savegame) + error("[SaveLoad::writeValue] Stream not initialized properly"); + + debugC(kLastExpressDebugSavegame, "Savegame: Writing %s: %u bytes", name, size); + + uint32 prevPosition = (uint32)_savegame->pos(); + + // Serialize data into our buffer + (*function)(ser); + + uint32 count = (uint32)_savegame->pos() - prevPosition; + +#if DISABLE_COMPRESSION + if (count != size) + error("[SaveLoad::writeValue] %s - Number of bytes written (%d) differ from expected count (%d)", name, count, size); +#endif + + return count; } +uint32 SaveLoad::readValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size) { + if (!_savegame) + error("[SaveLoad::readValue] Stream not initialized properly"); + + debugC(kLastExpressDebugSavegame, "Savegame: Reading %s: %u bytes", name, size); + + uint32 prevPosition = (uint32)_savegame->pos(); + + (*function)(ser); + + uint32 count = (uint32)_savegame->pos() - prevPosition; + +#if DISABLE_COMPRESSION + if (size != 0 && count != size) + error("[SaveLoad::readValue] %s - Number of bytes read (%d) differ from expected count (%d)", name, count, size); +#endif + + return count; +} + +void SaveLoad::syncEntity(Common::Serializer &ser) { + ser.syncAsUint32LE(_entity); +} + +void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { if (!_savegame) error("[SaveLoad::writeEntry] Savegame stream is invalid"); @@ -369,18 +720,22 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { header.saveLoadWithSerializer(ser); // Write game data - WRITE_ENTRY("entity index", ser.syncAsUint32LE(entity), 4); - WRITE_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4); - WRITE_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4); - WRITE_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000); - WRITE_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2); - WRITE_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128); - WRITE_ENTRY("events", getState()->syncEvents(ser), 512); - WRITE_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32); - WRITE_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128); - WRITE_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40); - WRITE_ENTRY("sound", getSoundQueue()->saveLoadWithSerializer(ser), 3 * 4 + getSoundQueue()->count() * 64); - WRITE_ENTRY("savepoints", getSavePoints()->saveLoadWithSerializer(ser), 128 * 16 + 4 + getSavePoints()->count() * 16); + _entity = entity; + + _savegame->process(); + writeValue(ser, "entity index", WRAP_SYNC_FUNCTION(this, SaveLoad, syncEntity), 4); + writeValue(ser, "state", WRAP_SYNC_FUNCTION(getState(), State::GameState, saveLoadWithSerializer), 4 + 4 + 4 + 4 + 1 + 4 + 4); + writeValue(ser, "selected item", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveSelectedItem), 4); + writeValue(ser, "positions", WRAP_SYNC_FUNCTION(getEntities(), Entities, savePositions), 4 * 1000); + writeValue(ser, "compartments", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveCompartments), 4 * 16 * 2); + writeValue(ser, "progress", WRAP_SYNC_FUNCTION(&getProgress(), State::GameProgress, saveLoadWithSerializer), 4 * 128); + writeValue(ser, "events", WRAP_SYNC_FUNCTION(getState(), State::GameState, syncEvents), 512); + writeValue(ser, "inventory", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveLoadWithSerializer), 7 * 32); + writeValue(ser, "objects", WRAP_SYNC_FUNCTION(getObjects(), Objects, saveLoadWithSerializer), 5 * 128); + writeValue(ser, "entities", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveLoadWithSerializer), 1262 * 40); + writeValue(ser, "sound", WRAP_SYNC_FUNCTION(getSoundQueue(), SoundQueue, saveLoadWithSerializer), 3 * 4 + getSoundQueue()->count() * 64); + writeValue(ser, "savepoints", WRAP_SYNC_FUNCTION(getSavePoints(), SavePoints, saveLoadWithSerializer), 128 * 16 + 4 + getSavePoints()->count() * 16); + _savegame->process(); header.offset = (uint32)_savegame->pos() - (originalPosition + 32); @@ -406,22 +761,6 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { } void SaveLoad::readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex) { -#define LOAD_ENTRY(name, func, val) { \ - uint32 _prevPosition = (uint32)_savegame->pos(); \ - func; \ - uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ - debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \ - if (_count != val) \ - error("[SaveLoad::readEntry] Number of bytes read (%d) differ from expected count (%d)", _count, val); \ -} - -#define LOAD_ENTRY_ONLY(name, func) { \ - uint32 _prevPosition = (uint32)_savegame->pos(); \ - func; \ - uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ - debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \ -} - if (!type || !entity || !val) error("[SaveLoad::readEntry] Invalid parameters passed"); @@ -444,20 +783,23 @@ void SaveLoad::readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, b uint32 originalPosition = (uint32)_savegame->pos(); // Load game data - LOAD_ENTRY("entity index", ser.syncAsUint32LE(*entity), 4); - LOAD_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4); - LOAD_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4); - LOAD_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000); - LOAD_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2); - LOAD_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128); - LOAD_ENTRY("events", getState()->syncEvents(ser), 512); - LOAD_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32); - LOAD_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128); - LOAD_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40); - LOAD_ENTRY_ONLY("sound", getSoundQueue()->saveLoadWithSerializer(ser)); - LOAD_ENTRY_ONLY("savepoints", getSavePoints()->saveLoadWithSerializer(ser)); + _savegame->process(); + readValue(ser, "entity index", WRAP_SYNC_FUNCTION(this, SaveLoad, syncEntity), 4); + readValue(ser, "state", WRAP_SYNC_FUNCTION(getState(), State::GameState, saveLoadWithSerializer), 4 + 4 + 4 + 4 + 1 + 4 + 4); + readValue(ser, "selected item", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveSelectedItem), 4); + readValue(ser, "positions", WRAP_SYNC_FUNCTION(getEntities(), Entities, savePositions), 4 * 1000); + readValue(ser, "compartments", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveCompartments), 4 * 16 * 2); + readValue(ser, "progress", WRAP_SYNC_FUNCTION(&getProgress(), State::GameProgress, saveLoadWithSerializer), 4 * 128); + readValue(ser, "events", WRAP_SYNC_FUNCTION(getState(), State::GameState, syncEvents), 512); + readValue(ser, "inventory", WRAP_SYNC_FUNCTION(getInventory(), Inventory, saveLoadWithSerializer), 7 * 32); + readValue(ser, "objects", WRAP_SYNC_FUNCTION(getObjects(), Objects, saveLoadWithSerializer), 5 * 128); + readValue(ser, "entities", WRAP_SYNC_FUNCTION(getEntities(), Entities, saveLoadWithSerializer), 1262 * 40); + readValue(ser, "sound", WRAP_SYNC_FUNCTION(getSoundQueue(), SoundQueue, saveLoadWithSerializer)); + readValue(ser, "savepoints", WRAP_SYNC_FUNCTION(getSavePoints(), SavePoints, saveLoadWithSerializer)); + _savegame->process(); // Update chapter + *entity = _entity; getProgress().chapter = entry.chapter; // Skip padding @@ -567,7 +909,7 @@ Common::InSaveFile *SaveLoad::openForLoading(GameId id) { } Common::OutSaveFile *SaveLoad::openForSaving(GameId id) { - Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(getFilename(id)); + Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(getFilename(id), false); // TODO Enable compression again if (!save) debugC(2, kLastExpressDebugSavegame, "Cannot open savegame for writing: %s", getFilename(id).c_str()); diff --git a/engines/lastexpress/game/savegame.h b/engines/lastexpress/game/savegame.h index 6f0408487f..361957227e 100644 --- a/engines/lastexpress/game/savegame.h +++ b/engines/lastexpress/game/savegame.h @@ -80,11 +80,68 @@ namespace LastExpress { // Savegame signatures -#define SAVEGAME_SIGNATURE 0x12001200 -#define SAVEGAME_ENTRY_SIGNATURE 0xE660E660 +#define SAVEGAME_SIGNATURE 0x12001200 // 301994496 +#define SAVEGAME_ENTRY_SIGNATURE 0xE660E660 // 3865110112 + +#define WRAP_SYNC_FUNCTION(instance, className, method) \ + new Common::Functor1Mem<Common::Serializer &, void, className>(instance, &className::method) class LastExpressEngine; +class SavegameStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream { +public: + SavegameStream() : MemoryWriteStreamDynamic(DisposeAfterUse::YES), _eos(false) { + _enableCompression = false; + _bufferOffset = -1; + _valueCount = 0; + _previousValue = 0; + _repeatCount = 0; + _offset = 0; + _status = kStatusReady; + + memset(_buffer, 0, 256); + } + + int32 pos() const { return MemoryWriteStreamDynamic::pos(); } + int32 size() const { return MemoryWriteStreamDynamic::size(); } + bool seek(int32 offset, int whence = SEEK_SET) { return MemoryWriteStreamDynamic::seek(offset, whence); } + bool eos() const { return _eos; } + uint32 read(void *dataPtr, uint32 dataSize); + uint32 write(const void *dataPtr, uint32 dataSize); + + uint32 process(); + +private: + enum CompressedStreamStatus { + kStatusReady, + kStatusReading, + kStatusWriting + }; + + uint32 readUncompressed(void *dataPtr, uint32 dataSize); + + // Compressed data + uint32 writeCompressed(const void *dataPtr, uint32 dataSize); + uint32 readCompressed(void *dataPtr, uint32 dataSize); + + void writeBuffer(uint8 value, bool onlyValue = true); + uint8 readBuffer(); + +private: + bool _eos; + + // Compression handling + bool _enableCompression; + int16 _bufferOffset; + byte _valueCount; + byte _previousValue; + int16 _repeatCount; + uint32 _offset; + CompressedStreamStatus _status; + + byte _buffer[256]; +}; + class SaveLoad { public: SaveLoad(LastExpressEngine *engine); @@ -96,8 +153,8 @@ public: uint32 init(GameId id, bool resetHeaders); // Save & Load - void loadGame(GameId id); - void loadGame(GameId id, uint32 index); + void loadLastGame(); + void loadGame(uint32 index); void saveGame(SavegameType type, EntityIndex entity, uint32 value); void loadVolumeBrightness(); @@ -116,30 +173,6 @@ public: uint32 getLastSavegameTicks() const { return _gameTicksLastSavegame; } private: - class SavegameStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream { - public: - SavegameStream() : MemoryWriteStreamDynamic(DisposeAfterUse::YES), - _eos(false) {} - - int32 pos() const { return MemoryWriteStreamDynamic::pos(); } - int32 size() const { return MemoryWriteStreamDynamic::size(); } - bool seek(int32 offset, int whence = SEEK_SET) { return MemoryWriteStreamDynamic::seek(offset, whence); } - bool eos() const { return _eos; } - uint32 read(void *dataPtr, uint32 dataSize) { - if ((int32)dataSize > size() - pos()) { - dataSize = size() - pos(); - _eos = true; - } - memcpy(dataPtr, getData() + pos(), dataSize); - - seek(dataSize, SEEK_CUR); - - return dataSize; - } - private: - bool _eos; - }; - LastExpressEngine *_engine; struct SavegameMainHeader : Common::Serializable { @@ -268,6 +301,9 @@ private: void writeEntry(SavegameType type, EntityIndex entity, uint32 val); void readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex); + uint32 writeValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size); + uint32 readValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size = 0); + SavegameEntryHeader *getEntry(uint32 index); // Opening save files @@ -279,6 +315,10 @@ private: void initStream(); void loadStream(GameId id); void flushStream(GameId id); + + // Misc + EntityIndex _entity; + void syncEntity(Common::Serializer &ser); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/game/savepoint.cpp b/engines/lastexpress/game/savepoint.cpp index 64ae26c2be..8d14ec386b 100644 --- a/engines/lastexpress/game/savepoint.cpp +++ b/engines/lastexpress/game/savepoint.cpp @@ -26,7 +26,6 @@ #include "lastexpress/game/logic.h" #include "lastexpress/game/state.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" @@ -95,7 +94,7 @@ void SavePoints::process() { if (!updateEntityFromData(savepoint)) { // Call requested callback - Entity::Callback *callback = getCallback(savepoint.entity1); + Callback *callback = getCallback(savepoint.entity1); if (callback && callback->isValid()) { debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s", ENTITY_NAME(savepoint.entity1), ACTION_NAME(savepoint.action), ENTITY_NAME(savepoint.entity2)); (*callback)(savepoint); @@ -126,7 +125,7 @@ void SavePoints::addData(EntityIndex entity, ActionIndex action, uint32 param) { ////////////////////////////////////////////////////////////////////////// // Callbacks ////////////////////////////////////////////////////////////////////////// -void SavePoints::setCallback(EntityIndex index, Entity::Callback *callback) { +void SavePoints::setCallback(EntityIndex index, Callback *callback) { if (index >= 40) error("[SavePoints::setCallback] Attempting to use an invalid entity index. Valid values 0-39, was %d", index); @@ -136,7 +135,7 @@ void SavePoints::setCallback(EntityIndex index, Entity::Callback *callback) { _callbacks[index] = callback; } -Entity::Callback *SavePoints::getCallback(EntityIndex index) const { +Callback *SavePoints::getCallback(EntityIndex index) const { if (index >= 40) error("[SavePoints::getCallback] Attempting to use an invalid entity index. Valid values 0-39, was %d", index); @@ -150,7 +149,7 @@ void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex acti point.entity2 = entity2; point.param.intValue = param; - Entity::Callback *callback = getCallback(entity1); + Callback *callback = getCallback(entity1); if (callback != NULL && callback->isValid()) { debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%d", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param); (*callback)(point); @@ -164,7 +163,7 @@ void SavePoints::call(EntityIndex entity2, EntityIndex entity1, ActionIndex acti point.entity2 = entity2; strcpy((char *)&point.param.charValue, param); - Entity::Callback *callback = getCallback(entity1); + Callback *callback = getCallback(entity1); if (callback != NULL && callback->isValid()) { debugC(8, kLastExpressDebugLogic, "Savepoint: entity1=%s, action=%s, entity2=%s, param=%s", ENTITY_NAME(entity1), ACTION_NAME(action), ENTITY_NAME(entity2), param); (*callback)(point); @@ -181,7 +180,7 @@ void SavePoints::callAndProcess() { bool isRunning = getFlags()->isGameRunning; while (isRunning) { - Entity::Callback *callback = getCallback(index); + Callback *callback = getCallback(index); if (callback != NULL && callback->isValid()) { (*callback)(savepoint); isRunning = getFlags()->isGameRunning; @@ -203,7 +202,7 @@ void SavePoints::callAndProcess() { // Misc ////////////////////////////////////////////////////////////////////////// bool SavePoints::updateEntityFromData(const SavePoint &savepoint) { - for (int i = 0; i < (int)_data.size(); i++) { + for (uint i = 0; i < _data.size(); i++) { // Not a data savepoint! if (!_data[i].entity1) @@ -211,7 +210,7 @@ bool SavePoints::updateEntityFromData(const SavePoint &savepoint) { // Found our data! if (_data[i].entity1 == savepoint.entity1 && _data[i].action == savepoint.action) { - debugC(8, kLastExpressDebugLogic, "Update entity from data: entity1=%s, action=%s, param=%d", ENTITY_NAME(_data[i].entity1), ACTION_NAME(_data[i].action), _data[i].param); + debugC(8, kLastExpressDebugLogic, "Update entity from data: entity1=%s, action=%s, param=%u", ENTITY_NAME(_data[i].entity1), ACTION_NAME(_data[i].action), _data[i].param); // the SavePoint param value is the index of the entity call parameter to update getEntities()->get(_data[i].entity1)->getParamData()->updateParameters(_data[i].param); @@ -243,7 +242,15 @@ void SavePoints::saveLoadWithSerializer(Common::Serializer &s) { } // Skip uninitialized data if any - s.skip((_savePointsMaxSize - dataSize) * 16); + // (we are using a compressed stream, so we cannot seek on load) + uint32 unusedDataSize = (_savePointsMaxSize - dataSize) * 16; + if (s.isLoading()) { + byte *empty = (byte *)malloc(unusedDataSize); + s.syncBytes(empty, unusedDataSize); + free(empty); + } else { + s.skip(unusedDataSize); + } // Number of savepoints uint32 numSavepoints = _savepoints.size(); diff --git a/engines/lastexpress/game/savepoint.h b/engines/lastexpress/game/savepoint.h index a3303b4b8a..005133891a 100644 --- a/engines/lastexpress/game/savepoint.h +++ b/engines/lastexpress/game/savepoint.h @@ -23,9 +23,8 @@ #ifndef LASTEXPRESS_SAVEPOINT_H #define LASTEXPRESS_SAVEPOINT_H -#include "lastexpress/entities/entity.h" - #include "lastexpress/helpers.h" +#include "lastexpress/shared.h" #include "common/array.h" #include "common/list.h" @@ -74,10 +73,9 @@ struct SavePoint { } }; -class SavePoints : Common::Serializable { -private: - typedef Common::Functor1<const SavePoint&, void> Callback; +typedef Common::Functor1<const SavePoint&, void> Callback; +class SavePoints : Common::Serializable { public: struct SavePointData { @@ -112,7 +110,7 @@ public: void addData(EntityIndex entity, ActionIndex action, uint32 param); // Callbacks - void setCallback(EntityIndex index, Entity::Callback *callback); + void setCallback(EntityIndex index, Callback *callback); Callback *getCallback(EntityIndex entity) const; void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, uint32 param = 0) const; void call(EntityIndex entity2, EntityIndex entity1, ActionIndex action, const char *param) const; diff --git a/engines/lastexpress/game/scenes.cpp b/engines/lastexpress/game/scenes.cpp index b886951e0b..a2c7226b93 100644 --- a/engines/lastexpress/game/scenes.cpp +++ b/engines/lastexpress/game/scenes.cpp @@ -22,8 +22,6 @@ #include "lastexpress/game/scenes.h" -#include "lastexpress/data/scene.h" - #include "lastexpress/game/action.h" #include "lastexpress/game/beetle.h" #include "lastexpress/game/entities.h" @@ -34,10 +32,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -493,7 +489,7 @@ bool SceneManager::checkCurrentPosition(bool doCheckOtherCars) const { if (position == 99) return true; - switch (car){ + switch (car) { default: break; @@ -743,24 +739,31 @@ void SceneManager::resetQueue() { _queue.clear(); } -void SceneManager::setCoordinates(SequenceFrame *frame) { +void SceneManager::setCoordinates(const Common::Rect &rect) { + _flagCoordinates = true; - if (!frame || frame->getInfo()->subType == 3) - return; + if (_coords.right > rect.right) + _coords.right = rect.right; - _flagCoordinates = true; + if (_coords.bottom > rect.bottom) + _coords.bottom = rect.bottom; + + if (_coords.left < rect.left) + _coords.left = rect.left; - if (_coords.right > (int)frame->getInfo()->xPos1) - _coords.right = (int16)frame->getInfo()->xPos1; + if (_coords.top < rect.top) + _coords.top = rect.top; +} - if (_coords.bottom > (int)frame->getInfo()->yPos1) - _coords.bottom = (int16)frame->getInfo()->yPos1; +void SceneManager::setCoordinates(SequenceFrame *frame) { - if (_coords.left < (int)frame->getInfo()->xPos2) - _coords.left = (int16)frame->getInfo()->xPos2; + if (!frame || frame->getInfo()->subType == 3) + return; - if (_coords.top < (int)frame->getInfo()->yPos2) - _coords.top = (int16)frame->getInfo()->yPos2; + setCoordinates(Common::Rect((int16)frame->getInfo()->xPos1, + (int16)frame->getInfo()->yPos1, + (int16)frame->getInfo()->xPos2, + (int16)frame->getInfo()->yPos2)); } void SceneManager::resetCoordinates() { diff --git a/engines/lastexpress/game/scenes.h b/engines/lastexpress/game/scenes.h index 172dde2683..1c7ae85f98 100644 --- a/engines/lastexpress/game/scenes.h +++ b/engines/lastexpress/game/scenes.h @@ -79,6 +79,7 @@ public: void removeAndRedraw(SequenceFrame **frame, bool doRedraw); void resetQueue(); void setCoordinates(SequenceFrame *frame); + void setCoordinates(const Common::Rect &rect); // Helpers SceneIndex getSceneIndexFromPosition(CarIndex car, Position position, int param3 = -1); diff --git a/engines/lastexpress/game/state.cpp b/engines/lastexpress/game/state.cpp index f3fd9720b1..02ede25595 100644 --- a/engines/lastexpress/game/state.cpp +++ b/engines/lastexpress/game/state.cpp @@ -49,6 +49,18 @@ State::~State() { _engine = NULL; } +void State::reset() { + SAFE_DELETE(_inventory); + SAFE_DELETE(_objects); + SAFE_DELETE(_savepoints); + SAFE_DELETE(_state); + + _inventory = new Inventory(_engine); + _objects = new Objects(_engine); + _savepoints = new SavePoints(_engine); + _state = new GameState(); +} + bool State::isNightTime() const { return (_state->progress.chapter == kChapter1 || _state->progress.chapter == kChapter4 diff --git a/engines/lastexpress/game/state.h b/engines/lastexpress/game/state.h index c937fdce9f..2c484f6976 100644 --- a/engines/lastexpress/game/state.h +++ b/engines/lastexpress/game/state.h @@ -621,6 +621,8 @@ public: State(LastExpressEngine *engine); ~State(); + void reset(); + // Accessors Inventory *getGameInventory() { return _inventory; } Objects *getGameObjects() { return _objects; } |