feat: Add script actions to the action queue

This commit is contained in:
Vsevolod Kremianskii 2020-08-14 09:08:32 +07:00
parent e7de31ffa5
commit e5163aa787
10 changed files with 128 additions and 54 deletions

View file

@ -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) {

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -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() {

View file

@ -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 &currentAction() 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 };

View file

@ -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;

View file

@ -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;

View file

@ -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);
};

View file

@ -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();