refactor: Execute delayed actions through the ActionQueue
This commit is contained in:
parent
33bd87fe2d
commit
e820e0a175
12 changed files with 83 additions and 95 deletions
|
@ -17,8 +17,6 @@
|
|||
|
||||
#include "action.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace game {
|
||||
|
|
|
@ -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<MoveToPointAction *>(action), dt);
|
||||
executeMoveToPoint(static_cast<Creature &>(object), *dynamic_cast<MoveToPointAction *>(action), dt);
|
||||
break;
|
||||
case ActionType::MoveToObject:
|
||||
executeMoveToObject(creature, *dynamic_cast<MoveToObjectAction *>(action), dt);
|
||||
executeMoveToObject(static_cast<Creature &>(object), *dynamic_cast<MoveToObjectAction *>(action), dt);
|
||||
break;
|
||||
case ActionType::Follow:
|
||||
executeFollow(creature, *dynamic_cast<FollowAction *>(action), dt);
|
||||
executeFollow(static_cast<Creature &>(object), *dynamic_cast<FollowAction *>(action), dt);
|
||||
break;
|
||||
case ActionType::DoCommand:
|
||||
executeDoCommand(creature, *dynamic_cast<CommandAction *>(action), dt);
|
||||
executeDoCommand(object, *dynamic_cast<CommandAction *>(action), dt);
|
||||
break;
|
||||
case ActionType::StartConversation:
|
||||
executeStartConversation(creature, *dynamic_cast<StartConversationAction *>(action), dt);
|
||||
executeStartConversation(static_cast<Creature &>(object), *dynamic_cast<StartConversationAction *>(action), dt);
|
||||
break;
|
||||
default:
|
||||
warn("Area: action not implemented: " + to_string(static_cast<int>(type)));
|
||||
warn("ActionExecutor: action not implemented: " + to_string(static_cast<int>(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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
#include "actionqueue.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "SDL2/SDL_timer.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace reone {
|
||||
|
@ -29,11 +33,23 @@ void ActionQueue::clear() {
|
|||
}
|
||||
}
|
||||
|
||||
void ActionQueue::push(unique_ptr<Action> action) {
|
||||
void ActionQueue::add(unique_ptr<Action> action) {
|
||||
_actions.push(move(action));
|
||||
}
|
||||
|
||||
void ActionQueue::delay(unique_ptr<Action> action, float seconds) {
|
||||
DelayedAction delayed;
|
||||
delayed.action = move(action);
|
||||
delayed.timestamp = SDL_GetTicks() + static_cast<int>(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();
|
||||
}
|
||||
|
|
|
@ -29,13 +29,23 @@ namespace game {
|
|||
class ActionQueue {
|
||||
public:
|
||||
void clear();
|
||||
void push(std::unique_ptr<Action> action);
|
||||
void add(std::unique_ptr<Action> action);
|
||||
void delay(std::unique_ptr<Action> action, float seconds);
|
||||
void update();
|
||||
|
||||
Action *currentAction();
|
||||
|
||||
private:
|
||||
struct DelayedAction {
|
||||
std::unique_ptr<Action> action;
|
||||
uint32_t timestamp { 0 };
|
||||
};
|
||||
|
||||
std::queue<std::unique_ptr<Action>> _actions;
|
||||
std::vector<DelayedAction> _delayed;
|
||||
|
||||
void removeCompletedActions();
|
||||
void updateDelayedActions();
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
|
|
|
@ -43,12 +43,6 @@ MultiplayerArea::MultiplayerArea(
|
|||
_scriptsEnabled = mode == MultiplayerMode::Server;
|
||||
}
|
||||
|
||||
void MultiplayerArea::updateCreature(Creature &creature, float dt) {
|
||||
if (static_cast<MultiplayerCreature &>(creature).isControlled()) return;
|
||||
|
||||
Area::updateCreature(creature, dt);
|
||||
}
|
||||
|
||||
void MultiplayerArea::execute(const Command &cmd) {
|
||||
switch (cmd.type()) {
|
||||
case CommandType::LoadCreature:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -351,7 +351,7 @@ void Area::loadParty(const PartyConfiguration &party, const glm::vec3 &position,
|
|||
_partyMember1 = partyMember;
|
||||
|
||||
unique_ptr<FollowAction> action(new FollowAction(_partyLeader, kPartyMemberFollowDistance));
|
||||
partyMember->actionQueue().push(move(action));
|
||||
partyMember->actionQueue().add(move(action));
|
||||
}
|
||||
if (party.memberCount > 2) {
|
||||
shared_ptr<Creature> 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<FollowAction> 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 &>(*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<ScriptProgram> 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++;
|
||||
|
||||
|
|
|
@ -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<SpatialObject> &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<ScriptType, std::string> _scripts;
|
||||
std::list<DelayedCommand> _delayed;
|
||||
std::map<int, UserDefinedEvent> _events;
|
||||
int _eventCounter { 0 };
|
||||
|
||||
|
@ -179,7 +170,6 @@ private:
|
|||
std::function<void(CameraType)> _onCameraChanged;
|
||||
|
||||
std::shared_ptr<Creature> 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<SpatialObject> &object, GuiContext &ctx);
|
||||
void addDebugInfo(const UpdateContext &updateCtx, GuiContext &guiCtx);
|
||||
|
|
|
@ -72,35 +72,34 @@ void RoutineManager::add(
|
|||
}
|
||||
|
||||
const Routine &RoutineManager::get(int index) {
|
||||
assert(index >= 0 && index < _routines.size());
|
||||
return _routines[index];
|
||||
}
|
||||
|
||||
shared_ptr<Object> 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> module(_game->module());
|
||||
if (finalId == module->id()) {
|
||||
if (module->id() == objectId) {
|
||||
return module;
|
||||
}
|
||||
|
||||
shared_ptr<Area> area(module->area());
|
||||
if (finalId == area->id()) {
|
||||
if (area->id() == objectId) {
|
||||
return area;
|
||||
}
|
||||
|
||||
return area->find(finalId);
|
||||
return area->find(objectId);
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
|
|
|
@ -177,18 +177,19 @@ Variable RoutineManager::setLocalNumber(const vector<Variable> &args, ExecutionC
|
|||
}
|
||||
|
||||
Variable RoutineManager::delayCommand(const vector<Variable> &args, ExecutionContext &ctx) {
|
||||
uint32_t timestamp = SDL_GetTicks() + static_cast<int>(args[0].floatValue * 1000.0f);
|
||||
_game->module()->area()->delayCommand(timestamp, args[1].context);
|
||||
unique_ptr<CommandAction> action(new CommandAction(args[1].context));
|
||||
|
||||
shared_ptr<Object> object(getObjectById(ctx.callerId, ctx));
|
||||
object->actionQueue().delay(move(action), args[0].floatValue);
|
||||
|
||||
return Variable();
|
||||
}
|
||||
|
||||
Variable RoutineManager::assignCommand(const vector<Variable> &args, ExecutionContext &ctx) {
|
||||
ExecutionContext newCtx(args[1].context);
|
||||
newCtx.callerId = args[0].objectId;
|
||||
newCtx.triggererId = kObjectInvalid;
|
||||
unique_ptr<CommandAction> action(new CommandAction(args[1].context));
|
||||
|
||||
_game->module()->area()->delayCommand(SDL_GetTicks(), move(newCtx));
|
||||
shared_ptr<Object> object(getObjectById(args[0].objectId, ctx));
|
||||
object->actionQueue().add(move(action));
|
||||
|
||||
return Variable();
|
||||
}
|
||||
|
@ -224,7 +225,7 @@ Variable RoutineManager::actionDoCommand(const vector<Variable> &args, Execution
|
|||
Creature *creature = dynamic_cast<Creature *>(subject.get());
|
||||
if (creature) {
|
||||
unique_ptr<CommandAction> 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<Variable> &args, Execut
|
|||
if (subject) {
|
||||
Creature &creature = static_cast<Creature &>(*subject);
|
||||
unique_ptr<MoveToObjectAction> 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<Variable> &args, E
|
|||
if (creature) {
|
||||
string dialogResRef((args.size() >= 2 && !args[1].strValue.empty()) ? args[1].strValue : creature->conversation());
|
||||
unique_ptr<StartConversationAction> 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<Variable> &args, E
|
|||
|
||||
if (creature) {
|
||||
unique_ptr<Action> 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<Variable> &args,
|
|||
|
||||
if (creature) {
|
||||
unique_ptr<Action> 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<Variable> &args, ExecutionC
|
|||
Creature *creature = dynamic_cast<Creature *>(subject.get());
|
||||
if (creature) {
|
||||
unique_ptr<ObjectAction> action(new ObjectAction(ActionType::OpenDoor, object));
|
||||
creature->actionQueue().push(move(action));
|
||||
creature->actionQueue().add(move(action));
|
||||
}
|
||||
Door *door = dynamic_cast<Door *>(subject.get());
|
||||
if (door) {
|
||||
|
@ -325,7 +326,7 @@ Variable RoutineManager::actionCloseDoor(const vector<Variable> &args, Execution
|
|||
Creature *creature = dynamic_cast<Creature *>(subject.get());
|
||||
if (creature) {
|
||||
unique_ptr<ObjectAction> action(new ObjectAction(ActionType::CloseDoor, object));
|
||||
creature->actionQueue().push(move(action));
|
||||
creature->actionQueue().add(move(action));
|
||||
}
|
||||
Door *door = dynamic_cast<Door *>(subject.get());
|
||||
if (door) {
|
||||
|
|
|
@ -27,8 +27,8 @@ using namespace reone::game;
|
|||
|
||||
BOOST_AUTO_TEST_CASE(test_action_completion) {
|
||||
ActionQueue actionQueue;
|
||||
actionQueue.push(make_unique<Action>(ActionType::PauseConversation));
|
||||
actionQueue.push(make_unique<Action>(ActionType::ResumeConversation));
|
||||
actionQueue.add(make_unique<Action>(ActionType::PauseConversation));
|
||||
actionQueue.add(make_unique<Action>(ActionType::ResumeConversation));
|
||||
|
||||
Action *currentAction = actionQueue.currentAction();
|
||||
BOOST_TEST((currentAction && currentAction->type() == ActionType::PauseConversation));
|
||||
|
|
Loading…
Reference in a new issue