From e820e0a1752e91f549d5f23e26d1b128f64bc8d1 Mon Sep 17 00:00:00 2001 From: Vsevolod Kremianskii Date: Sun, 11 Oct 2020 18:16:48 +0700 Subject: [PATCH] refactor: Execute delayed actions through the ActionQueue --- src/game/action/action.cpp | 2 -- src/game/actionexecutor.cpp | 20 ++++++------- src/game/actionexecutor.h | 4 +-- src/game/actionqueue.cpp | 34 +++++++++++++++++++++- src/game/actionqueue.h | 12 +++++++- src/game/mp/area.cpp | 6 ---- src/game/mp/area.h | 2 -- src/game/object/area.cpp | 44 ++++------------------------- src/game/object/area.h | 10 ------- src/game/script/routines.cpp | 13 ++++----- src/game/script/routines_common.cpp | 27 +++++++++--------- tests/actionqueue.cpp | 4 +-- 12 files changed, 83 insertions(+), 95 deletions(-) diff --git a/src/game/action/action.cpp b/src/game/action/action.cpp index edc37add..01db7156 100644 --- a/src/game/action/action.cpp +++ b/src/game/action/action.cpp @@ -17,8 +17,6 @@ #include "action.h" -using namespace std; - namespace reone { namespace game { diff --git a/src/game/actionexecutor.cpp b/src/game/actionexecutor.cpp index cd3a58df..812e0919 100644 --- a/src/game/actionexecutor.cpp +++ b/src/game/actionexecutor.cpp @@ -43,8 +43,8 @@ ActionExecutor::ActionExecutor(Area *area) : _area(area) { } } -void ActionExecutor::executeActions(Creature &creature, float dt) { - ActionQueue &actionQueue = creature.actionQueue(); +void ActionExecutor::executeActions(Object &object, float dt) { + ActionQueue &actionQueue = object.actionQueue(); Action *action = actionQueue.currentAction(); if (!action) return; @@ -52,22 +52,22 @@ void ActionExecutor::executeActions(Creature &creature, float dt) { ActionType type = action->type(); switch (type) { case ActionType::MoveToPoint: - executeMoveToPoint(creature, *dynamic_cast(action), dt); + executeMoveToPoint(static_cast(object), *dynamic_cast(action), dt); break; case ActionType::MoveToObject: - executeMoveToObject(creature, *dynamic_cast(action), dt); + executeMoveToObject(static_cast(object), *dynamic_cast(action), dt); break; case ActionType::Follow: - executeFollow(creature, *dynamic_cast(action), dt); + executeFollow(static_cast(object), *dynamic_cast(action), dt); break; case ActionType::DoCommand: - executeDoCommand(creature, *dynamic_cast(action), dt); + executeDoCommand(object, *dynamic_cast(action), dt); break; case ActionType::StartConversation: - executeStartConversation(creature, *dynamic_cast(action), dt); + executeStartConversation(static_cast(object), *dynamic_cast(action), dt); break; default: - warn("Area: action not implemented: " + to_string(static_cast(type))); + warn("ActionExecutor: action not implemented: " + to_string(static_cast(type))); action->isCompleted(); break; } @@ -101,9 +101,9 @@ void ActionExecutor::executeFollow(Creature &creature, FollowAction &action, flo navigateCreature(creature, dest, distance, dt); } -void ActionExecutor::executeDoCommand(Creature &creature, CommandAction &action, float dt) { +void ActionExecutor::executeDoCommand(Object &object, CommandAction &action, float dt) { ExecutionContext ctx(action.context()); - ctx.callerId = creature.id(); + ctx.callerId = object.id(); ScriptExecution(ctx.savedState->program, move(ctx)).run(); action.complete(); diff --git a/src/game/actionexecutor.h b/src/game/actionexecutor.h index c3d95eb0..997800e0 100644 --- a/src/game/actionexecutor.h +++ b/src/game/actionexecutor.h @@ -36,7 +36,7 @@ class ActionExecutor { public: ActionExecutor(Area *area); - void executeActions(Creature &creature, float dt); + void executeActions(Object &object, float dt); private: Area *_area { nullptr }; @@ -54,7 +54,7 @@ private: void executeMoveToPoint(Creature &creature, MoveToPointAction &action, float dt); void executeMoveToObject(Creature &creature, MoveToObjectAction &action, float dt); void executeFollow(Creature &creature, FollowAction &action, float dt); - void executeDoCommand(Creature &creature, CommandAction &command, float dt); + void executeDoCommand(Object &object, CommandAction &command, float dt); void executeStartConversation(Creature &creature, StartConversationAction &action, float dt); // END Actions diff --git a/src/game/actionqueue.cpp b/src/game/actionqueue.cpp index 0b1a0893..40672e2c 100644 --- a/src/game/actionqueue.cpp +++ b/src/game/actionqueue.cpp @@ -17,6 +17,10 @@ #include "actionqueue.h" +#include + +#include "SDL2/SDL_timer.h" + using namespace std; namespace reone { @@ -29,11 +33,23 @@ void ActionQueue::clear() { } } -void ActionQueue::push(unique_ptr action) { +void ActionQueue::add(unique_ptr action) { _actions.push(move(action)); } +void ActionQueue::delay(unique_ptr action, float seconds) { + DelayedAction delayed; + delayed.action = move(action); + delayed.timestamp = SDL_GetTicks() + static_cast(1000.0f * seconds); + _delayed.push_back(move(delayed)); +} + void ActionQueue::update() { + removeCompletedActions(); + updateDelayedActions(); +} + +void ActionQueue::removeCompletedActions() { while (true) { const Action *action = currentAction(); if (!action || !action->isCompleted()) return; @@ -42,6 +58,22 @@ void ActionQueue::update() { } } +void ActionQueue::updateDelayedActions() { + uint32_t now = SDL_GetTicks(); + + for (auto &delayed : _delayed) { + if (now >= delayed.timestamp) { + _actions.push(move(delayed.action)); + } + } + auto delayedToRemove = remove_if( + _delayed.begin(), + _delayed.end(), + [&now](const DelayedAction &delayed) { return now >= delayed.timestamp; }); + + _delayed.erase(delayedToRemove, _delayed.end()); +} + Action *ActionQueue::currentAction() { return _actions.empty() ? nullptr : _actions.front().get(); } diff --git a/src/game/actionqueue.h b/src/game/actionqueue.h index 5451a981..e2f24d40 100644 --- a/src/game/actionqueue.h +++ b/src/game/actionqueue.h @@ -29,13 +29,23 @@ namespace game { class ActionQueue { public: void clear(); - void push(std::unique_ptr action); + void add(std::unique_ptr action); + void delay(std::unique_ptr action, float seconds); void update(); Action *currentAction(); private: + struct DelayedAction { + std::unique_ptr action; + uint32_t timestamp { 0 }; + }; + std::queue> _actions; + std::vector _delayed; + + void removeCompletedActions(); + void updateDelayedActions(); }; } // namespace game diff --git a/src/game/mp/area.cpp b/src/game/mp/area.cpp index a415bd5f..b09cc2e1 100644 --- a/src/game/mp/area.cpp +++ b/src/game/mp/area.cpp @@ -43,12 +43,6 @@ MultiplayerArea::MultiplayerArea( _scriptsEnabled = mode == MultiplayerMode::Server; } -void MultiplayerArea::updateCreature(Creature &creature, float dt) { - if (static_cast(creature).isControlled()) return; - - Area::updateCreature(creature, dt); -} - void MultiplayerArea::execute(const Command &cmd) { switch (cmd.type()) { case CommandType::LoadCreature: diff --git a/src/game/mp/area.h b/src/game/mp/area.h index 06d0bac8..e7228364 100644 --- a/src/game/mp/area.h +++ b/src/game/mp/area.h @@ -44,8 +44,6 @@ public: private: IMultiplayerCallbacks *_callbacks { nullptr }; - void updateCreature(Creature &creature, float dt) override; - void executeLoadCreature(const Command &cmd); void executeSetPlayerRole(const Command &cmd); void executeSetObjectTransform(const Command &cmd); diff --git a/src/game/object/area.cpp b/src/game/object/area.cpp index 3dc6ce35..a5b1c021 100644 --- a/src/game/object/area.cpp +++ b/src/game/object/area.cpp @@ -351,7 +351,7 @@ void Area::loadParty(const PartyConfiguration &party, const glm::vec3 &position, _partyMember1 = partyMember; unique_ptr action(new FollowAction(_partyLeader, kPartyMemberFollowDistance)); - partyMember->actionQueue().push(move(action)); + partyMember->actionQueue().add(move(action)); } if (party.memberCount > 2) { shared_ptr partyMember(makeCharacter(party.member2, kPartyMember2Tag, position, heading)); @@ -361,7 +361,7 @@ void Area::loadParty(const PartyConfiguration &party, const glm::vec3 &position, _partyMember2 = partyMember; unique_ptr action(new FollowAction(_partyLeader, kPartyMemberFollowDistance)); - partyMember->actionQueue().push(move(action)); + partyMember->actionQueue().add(move(action)); } } @@ -422,47 +422,20 @@ bool Area::getElevationAt(const glm::vec2 &position, Room *&room, float &z) cons } void Area::update(const UpdateContext &updateCtx) { - updateDelayedCommands(); + Object::update(updateCtx); + _actionExecutor.executeActions(*this, updateCtx.deltaTime); - for (auto &creature : _objectsByType[ObjectType::Creature]) { - _actionExecutor.executeActions(static_cast(*creature), updateCtx.deltaTime); - } for (auto &room : _rooms) { room.second->update(updateCtx.deltaTime); } for (auto &object : _objects) { object->update(updateCtx); + _actionExecutor.executeActions(*object, updateCtx.deltaTime); } _objectSelector.update(); _sceneGraph->prepare(); } -void Area::updateDelayedCommands() { - uint32_t now = SDL_GetTicks(); - - for (auto &command : _delayed) { - if (now >= command.timestamp) { - shared_ptr program(command.context.savedState->program); - - debug(boost::format("Area: run delayed: %s %08x") % program->name() % command.context.savedState->insOffset); - ScriptExecution(program, command.context).run(); - - command.executed = true; - } - } - - auto it = remove_if( - _delayed.begin(), - _delayed.end(), - [](const DelayedCommand &command) { return command.executed; }); - - _delayed.erase(it, _delayed.end()); -} - -void Area::updateCreature(Creature &creature, float dt) { - _actionExecutor.executeActions(creature, dt); -} - bool Area::moveCreatureTowards(Creature &creature, const glm::vec2 &dest, bool run, float dt) { glm::vec3 position(creature.position()); glm::vec2 delta(dest - glm::vec2(position)); @@ -512,13 +485,6 @@ void Area::updateTriggers(const Creature &creature) { } } -void Area::delayCommand(uint32_t timestamp, const ExecutionContext &ctx) { - DelayedCommand action; - action.timestamp = timestamp; - action.context = ctx; - _delayed.push_back(action); -} - int Area::eventUserDefined(int eventNumber) { int id = _eventCounter++; diff --git a/src/game/object/area.h b/src/game/object/area.h index dc6a5346..dbb6e13f 100644 --- a/src/game/object/area.h +++ b/src/game/object/area.h @@ -79,7 +79,6 @@ public: Camera *getCamera() const; void startDialog(Creature &creature, const std::string &resRef); - void delayCommand(uint32_t timestamp, const script::ExecutionContext &ctx); int eventUserDefined(int eventNumber); void signalEvent(int eventId); @@ -137,7 +136,6 @@ protected: virtual void add(const std::shared_ptr &object); void determineObjectRoom(SpatialObject &object); void landObject(SpatialObject &object); - virtual void updateCreature(Creature &creature, float dt); private: enum class ScriptType { @@ -147,12 +145,6 @@ private: OnUserDefined }; - struct DelayedCommand { - uint32_t timestamp { 0 }; - script::ExecutionContext context; - bool executed { false }; - }; - struct UserDefinedEvent { int eventNumber { 0 }; }; @@ -169,7 +161,6 @@ private: CameraStyle _cameraStyle; std::string _music; std::unordered_map _scripts; - std::list _delayed; std::map _events; int _eventCounter { 0 }; @@ -179,7 +170,6 @@ private: std::function _onCameraChanged; std::shared_ptr makeCharacter(const CreatureConfiguration &character, const std::string &tag, const glm::vec3 &position, float heading); - void updateDelayedCommands(); bool getElevationAt(const glm::vec2 &position, Room *&room, float &z) const; void addPartyMemberPortrait(const std::shared_ptr &object, GuiContext &ctx); void addDebugInfo(const UpdateContext &updateCtx, GuiContext &guiCtx); diff --git a/src/game/script/routines.cpp b/src/game/script/routines.cpp index a1d9cd00..4ca98a18 100644 --- a/src/game/script/routines.cpp +++ b/src/game/script/routines.cpp @@ -72,35 +72,34 @@ void RoutineManager::add( } const Routine &RoutineManager::get(int index) { - assert(index >= 0 && index < _routines.size()); return _routines[index]; } shared_ptr RoutineManager::getObjectById(uint32_t id, const ExecutionContext &ctx) const { - uint32_t finalId = 0; + uint32_t objectId = 0; switch (id) { case kObjectSelf: - finalId = ctx.callerId; + objectId = ctx.callerId; break; case kObjectInvalid: warn("Routine: invalid object id: " + to_string(id)); return nullptr; default: - finalId = id; + objectId = id; break; } shared_ptr module(_game->module()); - if (finalId == module->id()) { + if (module->id() == objectId) { return module; } shared_ptr area(module->area()); - if (finalId == area->id()) { + if (area->id() == objectId) { return area; } - return area->find(finalId); + return area->find(objectId); } } // namespace game diff --git a/src/game/script/routines_common.cpp b/src/game/script/routines_common.cpp index ea21f606..94869f1c 100644 --- a/src/game/script/routines_common.cpp +++ b/src/game/script/routines_common.cpp @@ -177,18 +177,19 @@ Variable RoutineManager::setLocalNumber(const vector &args, ExecutionC } Variable RoutineManager::delayCommand(const vector &args, ExecutionContext &ctx) { - uint32_t timestamp = SDL_GetTicks() + static_cast(args[0].floatValue * 1000.0f); - _game->module()->area()->delayCommand(timestamp, args[1].context); + unique_ptr action(new CommandAction(args[1].context)); + + shared_ptr object(getObjectById(ctx.callerId, ctx)); + object->actionQueue().delay(move(action), args[0].floatValue); return Variable(); } Variable RoutineManager::assignCommand(const vector &args, ExecutionContext &ctx) { - ExecutionContext newCtx(args[1].context); - newCtx.callerId = args[0].objectId; - newCtx.triggererId = kObjectInvalid; + unique_ptr action(new CommandAction(args[1].context)); - _game->module()->area()->delayCommand(SDL_GetTicks(), move(newCtx)); + shared_ptr object(getObjectById(args[0].objectId, ctx)); + object->actionQueue().add(move(action)); return Variable(); } @@ -224,7 +225,7 @@ Variable RoutineManager::actionDoCommand(const vector &args, Execution Creature *creature = dynamic_cast(subject.get()); if (creature) { unique_ptr action(new CommandAction(args[0].context)); - creature->actionQueue().push(move(action)); + creature->actionQueue().add(move(action)); } return Variable(); @@ -240,7 +241,7 @@ Variable RoutineManager::actionMoveToObject(const vector &args, Execut if (subject) { Creature &creature = static_cast(*subject); unique_ptr action(new MoveToObjectAction(object, distance)); - creature.actionQueue().push(move(action)); + creature.actionQueue().add(move(action)); } else { warn("Routine: object not found: " + to_string(objectId)); } @@ -258,7 +259,7 @@ Variable RoutineManager::actionStartConversation(const vector &args, E if (creature) { string dialogResRef((args.size() >= 2 && !args[1].strValue.empty()) ? args[1].strValue : creature->conversation()); unique_ptr action(new StartConversationAction(object, dialogResRef)); - creature->actionQueue().push(move(action)); + creature->actionQueue().add(move(action)); } else { warn("Routine: creature not found: " + to_string(ctx.callerId)); } @@ -272,7 +273,7 @@ Variable RoutineManager::actionPauseConversation(const vector &args, E if (creature) { unique_ptr action(new Action(ActionType::PauseConversation)); - creature->actionQueue().push(move(action)); + creature->actionQueue().add(move(action)); } else { warn("Routine: creature not found: " + to_string(ctx.callerId)); } @@ -286,7 +287,7 @@ Variable RoutineManager::actionResumeConversation(const vector &args, if (creature) { unique_ptr action(new Action(ActionType::ResumeConversation)); - creature->actionQueue().push(move(action)); + creature->actionQueue().add(move(action)); } else { warn("Routine: creature not found: " + to_string(ctx.callerId)); } @@ -303,7 +304,7 @@ Variable RoutineManager::actionOpenDoor(const vector &args, ExecutionC Creature *creature = dynamic_cast(subject.get()); if (creature) { unique_ptr action(new ObjectAction(ActionType::OpenDoor, object)); - creature->actionQueue().push(move(action)); + creature->actionQueue().add(move(action)); } Door *door = dynamic_cast(subject.get()); if (door) { @@ -325,7 +326,7 @@ Variable RoutineManager::actionCloseDoor(const vector &args, Execution Creature *creature = dynamic_cast(subject.get()); if (creature) { unique_ptr action(new ObjectAction(ActionType::CloseDoor, object)); - creature->actionQueue().push(move(action)); + creature->actionQueue().add(move(action)); } Door *door = dynamic_cast(subject.get()); if (door) { diff --git a/tests/actionqueue.cpp b/tests/actionqueue.cpp index 15e78f52..9333c5d3 100644 --- a/tests/actionqueue.cpp +++ b/tests/actionqueue.cpp @@ -27,8 +27,8 @@ using namespace reone::game; BOOST_AUTO_TEST_CASE(test_action_completion) { ActionQueue actionQueue; - actionQueue.push(make_unique(ActionType::PauseConversation)); - actionQueue.push(make_unique(ActionType::ResumeConversation)); + actionQueue.add(make_unique(ActionType::PauseConversation)); + actionQueue.add(make_unique(ActionType::ResumeConversation)); Action *currentAction = actionQueue.currentAction(); BOOST_TEST((currentAction && currentAction->type() == ActionType::PauseConversation));