feat: Add script actions to the action queue
This commit is contained in:
parent
e7de31ffa5
commit
e5163aa787
10 changed files with 128 additions and 54 deletions
|
@ -219,7 +219,7 @@ void Area::loadParty(const PartyConfiguration &party, const glm::vec3 &position,
|
|||
_partyMember1 = partyMember;
|
||||
|
||||
Creature::Action action(Creature::ActionType::Follow, _partyLeader, kPartyMemberFollowDistance);
|
||||
partyMember->enqueue(move(action));
|
||||
partyMember->enqueueAction(move(action));
|
||||
}
|
||||
|
||||
if (party.memberCount > 1) {
|
||||
|
@ -229,7 +229,7 @@ void Area::loadParty(const PartyConfiguration &party, const glm::vec3 &position,
|
|||
_partyMember2 = partyMember;
|
||||
|
||||
Creature::Action action(Creature::ActionType::Follow, _partyLeader, kPartyMemberFollowDistance);
|
||||
partyMember->enqueue(move(action));
|
||||
partyMember->enqueueAction(move(action));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,30 +345,46 @@ void Area::updateDelayedCommands() {
|
|||
}
|
||||
|
||||
void Area::updateCreature(Creature &creature, float dt) {
|
||||
if (!creature.hasActions()) return;
|
||||
|
||||
const Creature::Action &action = creature.currentAction();
|
||||
if (action.type == Creature::ActionType::QueueEmpty) return;
|
||||
|
||||
glm::vec3 dest;
|
||||
|
||||
switch (action.type) {
|
||||
case Creature::ActionType::MoveToPoint:
|
||||
case Creature::ActionType::Follow:
|
||||
dest = (action.type == Creature::ActionType::Follow || action.object) ? action.object->position() : action.point;
|
||||
navigateCreature(creature, dest, action.distance, dt);
|
||||
case Creature::ActionType::Follow: {
|
||||
glm::vec3 dest = (action.type == Creature::ActionType::Follow || action.object) ? action.object->position() : action.point;
|
||||
bool reached = navigateCreature(creature, dest, action.distance, dt);
|
||||
if (reached && action.type == Creature::ActionType::MoveToPoint) {
|
||||
creature.popCurrentAction();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Creature::ActionType::DoCommand: {
|
||||
ExecutionContext ctx(action.context);
|
||||
ctx.callerId = creature.id();
|
||||
|
||||
ScriptExecution(action.context.savedState->program, move(ctx)).run();
|
||||
break;
|
||||
}
|
||||
case Creature::ActionType::StartConversation:
|
||||
if (_onStartDialog) {
|
||||
_onStartDialog(creature, action.resRef);
|
||||
}
|
||||
creature.popCurrentAction();
|
||||
break;
|
||||
default:
|
||||
warn("Area: action not implemented: " + to_string(static_cast<int>(action.type)));
|
||||
creature.popCurrentAction();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Area::navigateCreature(Creature &creature, const glm::vec3 &dest, float distance, float dt) {
|
||||
bool Area::navigateCreature(Creature &creature, const glm::vec3 &dest, float distance, float dt) {
|
||||
glm::vec3 origin(creature.position());
|
||||
float distToDest = glm::distance2(glm::vec2(origin), glm::vec2(dest));
|
||||
|
||||
if (distToDest <= distance) {
|
||||
creature.setMovementType(MovementType::None);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool updatePath = true;
|
||||
|
@ -384,6 +400,8 @@ void Area::navigateCreature(Creature &creature, const glm::vec3 &dest, float dis
|
|||
if (updatePath) {
|
||||
updateCreaturePath(creature, dest);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Area::advanceCreatureOnPath(Creature &creature, float dt) {
|
||||
|
|
|
@ -164,7 +164,7 @@ private:
|
|||
|
||||
std::shared_ptr<Creature> makeCharacter(const CreatureConfiguration &character, const std::string &tag, const glm::vec3 &position, float heading);
|
||||
void updateDelayedCommands();
|
||||
void navigateCreature(Creature &creature, const glm::vec3 &dest, float distance, float dt);
|
||||
bool navigateCreature(Creature &creature, const glm::vec3 &dest, float distance, float dt);
|
||||
void advanceCreatureOnPath(Creature &creature, float dt);
|
||||
void selectNextPathPoint(Creature::Path &path);
|
||||
void updateCreaturePath(Creature &creature, const glm::vec3 &dest);
|
||||
|
|
|
@ -335,22 +335,6 @@ shared_ptr<Object> Game::getPlayer() {
|
|||
return _module->area().player();
|
||||
}
|
||||
|
||||
void Game::actionStartConversation(uint32_t objectId, const string &resRef) {
|
||||
shared_ptr<Object> object(_module->area().find(objectId));
|
||||
if (!object) return;
|
||||
|
||||
string finalResRef(resRef);
|
||||
if (finalResRef.empty()) {
|
||||
Creature *creature = dynamic_cast<Creature *>(object.get());
|
||||
if (!creature->conversation().empty()) {
|
||||
finalResRef = creature->conversation();
|
||||
}
|
||||
}
|
||||
|
||||
_screen = Screen::Dialog;
|
||||
_dialogGui->startDialog(*object, finalResRef);
|
||||
}
|
||||
|
||||
int Game::eventUserDefined(int eventNumber) {
|
||||
return _module->area().eventUserDefined(eventNumber);
|
||||
}
|
||||
|
|
|
@ -57,9 +57,8 @@ public:
|
|||
|
||||
// Routine callbacks
|
||||
|
||||
// Commands/actions
|
||||
// Commands
|
||||
void delayCommand(uint32_t timestamp, const script::ExecutionContext &ctx) override;
|
||||
void actionStartConversation(uint32_t objectId, const std::string &resRef) override;
|
||||
|
||||
// Events
|
||||
int eventUserDefined(int eventNumber) override;
|
||||
|
|
|
@ -52,6 +52,9 @@ Creature::Action::Action(ActionType type) : type(type) {
|
|||
Creature::Action::Action(ActionType type, const shared_ptr<Object> &object, float distance) : type(type), object(object), distance(distance) {
|
||||
}
|
||||
|
||||
Creature::Action::Action(ActionType type, const ExecutionContext &ctx) : type(type), context(ctx) {
|
||||
}
|
||||
|
||||
Creature::Creature(uint32_t id) : Object(id) {
|
||||
_type = ObjectType::Creature;
|
||||
_drawDistance = 2048.0f;
|
||||
|
@ -243,15 +246,18 @@ void Creature::playTalkAnimation() {
|
|||
}
|
||||
|
||||
void Creature::clearActions() {
|
||||
while (!_actions.empty() && _actions.back().type != ActionType::QueueEmpty) {
|
||||
_actions.pop_back();
|
||||
}
|
||||
_actions.clear();
|
||||
}
|
||||
|
||||
void Creature::enqueue(const Action &action) {
|
||||
void Creature::enqueueAction(const Action &action) {
|
||||
_actions.push_back(action);
|
||||
}
|
||||
|
||||
void Creature::popCurrentAction() {
|
||||
assert(!_actions.empty());
|
||||
_actions.pop_front();
|
||||
}
|
||||
|
||||
void Creature::equip(const string &resRef) {
|
||||
TemplateManager &templates = TemplateManager::instance();
|
||||
shared_ptr<Item> item(templates.findItem(resRef));
|
||||
|
@ -386,8 +392,13 @@ const map<InventorySlot, shared_ptr<Item>> &Creature::equipment() const {
|
|||
return _equipment;
|
||||
}
|
||||
|
||||
bool Creature::hasActions() const {
|
||||
return !_actions.empty();
|
||||
}
|
||||
|
||||
const Creature::Action &Creature::currentAction() const {
|
||||
return _actions.back();
|
||||
assert(!_actions.empty());
|
||||
return _actions.front();
|
||||
}
|
||||
|
||||
shared_ptr<Creature::Path> &Creature::path() {
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <queue>
|
||||
|
||||
#include "../../resources/2dafile.h"
|
||||
#include "../../script/types.h"
|
||||
|
||||
#include "../item.h"
|
||||
|
||||
#include "object.h"
|
||||
|
@ -36,7 +38,12 @@ public:
|
|||
CloseDoor = 6,
|
||||
Follow = 35,
|
||||
FollowLeader = 38,
|
||||
QueueEmpty = 65534
|
||||
QueueEmpty = 65534,
|
||||
|
||||
DoCommand = 0x1000,
|
||||
StartConversation = 0x1001,
|
||||
PauseConversation = 0x1002,
|
||||
ResumeConversation = 0x1003
|
||||
};
|
||||
|
||||
struct Action {
|
||||
|
@ -44,9 +51,12 @@ public:
|
|||
glm::vec3 point { 0.0f };
|
||||
std::shared_ptr<Object> object;
|
||||
float distance { 1.0f };
|
||||
script::ExecutionContext context;
|
||||
std::string resRef;
|
||||
|
||||
Action(ActionType type);
|
||||
Action(ActionType, const std::shared_ptr<Object> &object, float distance);
|
||||
Action(ActionType type, const std::shared_ptr<Object> &object, float distance);
|
||||
Action(ActionType type, const script::ExecutionContext &ctx);
|
||||
};
|
||||
|
||||
struct Path {
|
||||
|
@ -91,7 +101,8 @@ public:
|
|||
|
||||
// Actions
|
||||
void clearActions();
|
||||
void enqueue(const Action &action);
|
||||
void enqueueAction(const Action &action);
|
||||
void popCurrentAction();
|
||||
|
||||
// Load/save
|
||||
void saveTo(AreaState &state) const override;
|
||||
|
@ -110,6 +121,7 @@ public:
|
|||
std::shared_ptr<render::Texture> portrait() const;
|
||||
const std::string &conversation() const;
|
||||
const std::map<InventorySlot, std::shared_ptr<Item>> &equipment() const;
|
||||
bool hasActions() const;
|
||||
const Action ¤tAction() const;
|
||||
std::shared_ptr<Path> &path();
|
||||
bool isPathUpdating() const;
|
||||
|
@ -131,7 +143,7 @@ private:
|
|||
std::shared_ptr<render::Texture> _portrait;
|
||||
std::map<InventorySlot, std::shared_ptr<Item>> _equipment;
|
||||
std::string _conversation;
|
||||
std::vector<Action> _actions;
|
||||
std::list<Action> _actions;
|
||||
std::shared_ptr<Path> _path;
|
||||
std::atomic_bool _pathUpdating { false };
|
||||
float _walkSpeed { 0.0f };
|
||||
|
|
|
@ -33,9 +33,8 @@ public:
|
|||
virtual ~IRoutineCallbacks() {
|
||||
}
|
||||
|
||||
// Commands/actions
|
||||
// Commands
|
||||
virtual void delayCommand(uint32_t timestamp, const script::ExecutionContext &ctx) = 0;
|
||||
virtual void actionStartConversation(uint32_t objectId, const std::string &resRef) = 0;
|
||||
|
||||
// Events
|
||||
virtual int eventUserDefined(int eventNumber) = 0;
|
||||
|
|
|
@ -86,7 +86,7 @@ shared_ptr<Object> RoutineManager::getObjectById(uint32_t id, const ExecutionCon
|
|||
case kObjectInvalid:
|
||||
case kObjectModule:
|
||||
case kObjectArea:
|
||||
throw logic_error("Invalid object id: " + to_string(id));
|
||||
throw logic_error("Routine: invalid object id: " + to_string(id));
|
||||
default:
|
||||
finalId = id;
|
||||
break;
|
||||
|
|
|
@ -102,6 +102,8 @@ private:
|
|||
script::Variable actionDoCommand(const std::vector<script::Variable> &args, script::ExecutionContext &ctx);
|
||||
script::Variable actionMoveToObject(const std::vector<script::Variable> &args, script::ExecutionContext &ctx);
|
||||
script::Variable actionStartConversation(const std::vector<script::Variable> &args, script::ExecutionContext &ctx);
|
||||
script::Variable actionPauseConversation(const std::vector<script::Variable> &args, script::ExecutionContext &ctx);
|
||||
script::Variable actionResumeConversation(const std::vector<script::Variable> &args, script::ExecutionContext &ctx);
|
||||
script::Variable actionOpenDoor(const std::vector<script::Variable> &args, script::ExecutionContext &ctx);
|
||||
script::Variable actionCloseDoor(const std::vector<script::Variable> &args, script::ExecutionContext &ctx);
|
||||
};
|
||||
|
|
|
@ -106,7 +106,7 @@ Variable RoutineManager::getLevelByClass(const vector<Variable> &args, Execution
|
|||
int objectId = args.size() < 2 ? kObjectSelf : args[1].objectId;
|
||||
shared_ptr<Object> object(getObjectById(objectId, ctx));
|
||||
if (!object) {
|
||||
warn("Object not found by id: " + to_string(objectId));
|
||||
warn("Routine: object not found: " + to_string(objectId));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -256,7 +256,7 @@ Variable RoutineManager::signalEvent(const vector<Variable> &args, ExecutionCont
|
|||
_callbacks->signalEvent(args[1].engineTypeId);
|
||||
|
||||
} else {
|
||||
warn("Invalid event callee: " + to_string(args[0].objectId));
|
||||
warn("Routine: invalid callee: " + to_string(args[0].objectId));
|
||||
}
|
||||
|
||||
return Variable();
|
||||
|
@ -270,8 +270,12 @@ Variable RoutineManager::getUserDefinedEventNumber(const vector<Variable> &args,
|
|||
Variable RoutineManager::actionDoCommand(const vector<Variable> &args, ExecutionContext &ctx) {
|
||||
assert(!args.empty() && args[0].type == VariableType::Action);
|
||||
|
||||
// TODO: add to creature action queue
|
||||
_callbacks->delayCommand(SDL_GetTicks(), args[0].context);
|
||||
shared_ptr<Object> subject(getObjectById(ctx.callerId, ctx));
|
||||
Creature *creature = dynamic_cast<Creature *>(subject.get());
|
||||
if (creature) {
|
||||
Creature::Action action(Creature::ActionType::DoCommand, args[0].context);
|
||||
creature->enqueueAction(move(action));
|
||||
}
|
||||
|
||||
return Variable();
|
||||
}
|
||||
|
@ -290,9 +294,9 @@ Variable RoutineManager::actionMoveToObject(const vector<Variable> &args, Execut
|
|||
if (subject) {
|
||||
Creature &creature = static_cast<Creature &>(*subject);
|
||||
Creature::Action action(Creature::ActionType::MoveToPoint, object, distance);
|
||||
creature.enqueue(move(action));
|
||||
creature.enqueueAction(move(action));
|
||||
} else {
|
||||
warn("Object not found: " + to_string(objectId));
|
||||
warn("Routine: object not found: " + to_string(objectId));
|
||||
}
|
||||
|
||||
return Variable();
|
||||
|
@ -301,9 +305,54 @@ Variable RoutineManager::actionMoveToObject(const vector<Variable> &args, Execut
|
|||
Variable RoutineManager::actionStartConversation(const vector<Variable> &args, ExecutionContext &ctx) {
|
||||
assert(!args.empty() && args[0].type == VariableType::Object);
|
||||
|
||||
shared_ptr<Object> subject(getObjectById(ctx.callerId, ctx));
|
||||
Creature *creature = dynamic_cast<Creature *>(subject ? subject.get() : nullptr);
|
||||
|
||||
int objectId = args[0].objectId == kObjectSelf ? ctx.callerId : args[0].objectId;
|
||||
string resRef(args.size() >= 2 ? args[1].strValue : "");
|
||||
_callbacks->actionStartConversation(ctx.callerId /* objectId */, resRef);
|
||||
shared_ptr<Object> object(getObjectById(objectId, ctx));
|
||||
|
||||
if (creature) {
|
||||
Creature::Action action(Creature::ActionType::StartConversation, ctx);
|
||||
action.object = object;
|
||||
action.resRef = (args.size() >= 2 && !args[1].strValue.empty()) ? args[1].strValue : creature->conversation();
|
||||
|
||||
creature->enqueueAction(move(action));
|
||||
|
||||
} else {
|
||||
warn("Routine: creature not found: " + to_string(ctx.callerId));
|
||||
}
|
||||
|
||||
return Variable();
|
||||
}
|
||||
|
||||
Variable RoutineManager::actionPauseConversation(const vector<Variable> &args, ExecutionContext &ctx) {
|
||||
assert(args.empty());
|
||||
|
||||
shared_ptr<Object> subject(getObjectById(ctx.callerId, ctx));
|
||||
Creature *creature = dynamic_cast<Creature *>(subject ? subject.get() : nullptr);
|
||||
|
||||
if (creature) {
|
||||
Creature::Action action(Creature::ActionType::PauseConversation, ctx);
|
||||
creature->enqueueAction(move(action));
|
||||
} else {
|
||||
warn("Routine: creature not found: " + to_string(ctx.callerId));
|
||||
}
|
||||
|
||||
return Variable();
|
||||
}
|
||||
|
||||
Variable RoutineManager::actionResumeConversation(const vector<Variable> &args, ExecutionContext &ctx) {
|
||||
assert(args.empty());
|
||||
|
||||
shared_ptr<Object> subject(getObjectById(ctx.callerId, ctx));
|
||||
Creature *creature = dynamic_cast<Creature *>(subject ? subject.get() : nullptr);
|
||||
|
||||
if (creature) {
|
||||
Creature::Action action(Creature::ActionType::ResumeConversation, ctx);
|
||||
creature->enqueueAction(move(action));
|
||||
} else {
|
||||
warn("Routine: creature not found: " + to_string(ctx.callerId));
|
||||
}
|
||||
|
||||
return Variable();
|
||||
}
|
||||
|
@ -318,14 +367,14 @@ Variable RoutineManager::actionOpenDoor(const vector<Variable> &args, ExecutionC
|
|||
Creature *creature = dynamic_cast<Creature *>(subject.get());
|
||||
if (creature) {
|
||||
Creature::Action action(Creature::ActionType::OpenDoor, object, 1.0f);
|
||||
creature->enqueue(move(action));
|
||||
creature->enqueueAction(move(action));
|
||||
}
|
||||
Door *door = dynamic_cast<Door *>(subject.get());
|
||||
if (door) {
|
||||
door->open(nullptr);
|
||||
}
|
||||
} else {
|
||||
warn("Object not found: " + to_string(objectId));
|
||||
warn("Routine: object not found: " + to_string(objectId));
|
||||
}
|
||||
|
||||
return Variable();
|
||||
|
@ -341,14 +390,14 @@ Variable RoutineManager::actionCloseDoor(const vector<Variable> &args, Execution
|
|||
Creature *creature = dynamic_cast<Creature *>(subject.get());
|
||||
if (creature) {
|
||||
Creature::Action action(Creature::ActionType::CloseDoor, object, 1.0f);
|
||||
creature->enqueue(move(action));
|
||||
creature->enqueueAction(move(action));
|
||||
}
|
||||
Door *door = dynamic_cast<Door *>(subject.get());
|
||||
if (door) {
|
||||
door->close(nullptr);
|
||||
}
|
||||
} else {
|
||||
warn("Object not found: " + to_string(objectId));
|
||||
warn("Routine: object not found: " + to_string(objectId));
|
||||
}
|
||||
|
||||
return Variable();
|
||||
|
|
Loading…
Reference in a new issue