feat: Object activation adds actions to the action queue

This commit is contained in:
Vsevolod Kremianskii 2020-10-30 10:35:03 +07:00
parent bd4e42fba9
commit f1262595d0
22 changed files with 121 additions and 55 deletions

View file

@ -33,7 +33,8 @@ enum class ActionType {
StartConversation = 0x1001,
PauseConversation = 0x1002,
ResumeConversation = 0x1003,
MoveToObject = 0x1004
MoveToObject = 0x1004,
OpenContainer = 0x1005
};
class Action {

View file

@ -23,7 +23,7 @@ namespace reone {
namespace game {
FollowAction::FollowAction(const shared_ptr<Object> &object, float distance) :
FollowAction::FollowAction(Object *object, float distance) :
ObjectAction(ActionType::Follow, object),
_distance(distance) {
}

View file

@ -25,7 +25,7 @@ namespace game {
class FollowAction : public ObjectAction {
public:
FollowAction(const std::shared_ptr<Object> &object, float distance);
FollowAction(Object *object, float distance);
float distance() const;

View file

@ -23,7 +23,7 @@ namespace reone {
namespace game {
MoveToObjectAction::MoveToObjectAction(const shared_ptr<Object> &object, float distance) :
MoveToObjectAction::MoveToObjectAction(Object *object, float distance) :
ObjectAction(ActionType::MoveToObject, object),
_distance(distance) {
}

View file

@ -25,7 +25,7 @@ namespace game {
class MoveToObjectAction : public ObjectAction {
public:
MoveToObjectAction(const std::shared_ptr<Object> &object, float distance);
MoveToObjectAction(Object *object, float distance);
float distance() const;

View file

@ -17,17 +17,22 @@
#include "objectaction.h"
#include <stdexcept>
using namespace std;
namespace reone {
namespace game {
ObjectAction::ObjectAction(ActionType type, const shared_ptr<Object> &object) : Action(type), _object(object) {
ObjectAction::ObjectAction(ActionType type, Object *object) : Action(type), _object(object) {
if (!object) {
throw invalid_argument("Object must not be null");
}
}
const Object *ObjectAction::object() const {
return _object.get();
Object *ObjectAction::object() const {
return _object;
}
} // namespace game

View file

@ -29,12 +29,12 @@ class Object;
class ObjectAction : public Action {
public:
ObjectAction(ActionType type, const std::shared_ptr<Object> &object);
ObjectAction(ActionType type, Object *object);
const Object *object() const;
Object *object() const;
protected:
std::shared_ptr<Object> _object;
Object *_object { nullptr };
};
} // namespace game

View file

@ -23,9 +23,14 @@ namespace reone {
namespace game {
StartConversationAction::StartConversationAction(const shared_ptr<Object> &object, const string &dialogResRef) :
StartConversationAction::StartConversationAction(Object *object, const string &dialogResRef, bool ignoreStartRange) :
ObjectAction(ActionType::StartConversation, object),
_dialogResRef(dialogResRef) {
_dialogResRef(dialogResRef),
_ignoreStartRange(ignoreStartRange) {
}
bool StartConversationAction::isStartRangeIgnored() const {
return _ignoreStartRange;
}
const string &StartConversationAction::dialogResRef() const {

View file

@ -27,12 +27,15 @@ namespace game {
class StartConversationAction : public ObjectAction {
public:
StartConversationAction(const std::shared_ptr<Object> &object, const std::string &dialogResRef);
StartConversationAction(Object *object, const std::string &dialogResRef, bool ignoreStartRange = false);
bool isStartRangeIgnored() const;
const std::string &dialogResRef() const;
private:
std::string _dialogResRef;
bool _ignoreStartRange { false };
};
} // namespace game

View file

@ -24,8 +24,11 @@
#include "../script/execution.h"
#include "../system/log.h"
#include "game.h"
#include "object/area.h"
#include "object/creature.h"
#include "object/door.h"
#include "object/placeable.h"
using namespace std;
@ -37,9 +40,9 @@ namespace game {
static const float kKeepPathDuration = 1000.0f;
ActionExecutor::ActionExecutor(Area *area) : _area(area) {
if (!area) {
throw invalid_argument("area must not be null");
ActionExecutor::ActionExecutor(Game *game) : _game(game) {
if (!game) {
throw invalid_argument("game must not be null");
}
}
@ -66,6 +69,12 @@ void ActionExecutor::executeActions(Object &object, float dt) {
case ActionType::StartConversation:
executeStartConversation(static_cast<Creature &>(object), *dynamic_cast<StartConversationAction *>(action), dt);
break;
case ActionType::OpenDoor:
executeOpenDoor(static_cast<Creature &>(object), *dynamic_cast<ObjectAction *>(action), dt);
break;
case ActionType::OpenContainer:
executeOpenContainer(static_cast<Creature &>(object), *dynamic_cast<ObjectAction *>(action), dt);
break;
default:
warn("ActionExecutor: action not implemented: " + to_string(static_cast<int>(type)));
action->isCompleted();
@ -83,10 +92,8 @@ void ActionExecutor::executeMoveToPoint(Creature &creature, MoveToPointAction &a
}
void ActionExecutor::executeMoveToObject(Creature &creature, MoveToObjectAction &action, float dt) {
const SpatialObject *object = dynamic_cast<const SpatialObject *>(action.object());
if (!object) return;
glm::vec3 dest(object->position());
SpatialObject &object = *static_cast<SpatialObject *>(action.object());
glm::vec3 dest(object.position());
float distance = action.distance();
bool reached = navigateCreature(creature, dest, distance, dt);
@ -96,8 +103,8 @@ void ActionExecutor::executeMoveToObject(Creature &creature, MoveToObjectAction
}
void ActionExecutor::executeFollow(Creature &creature, FollowAction &action, float dt) {
const SpatialObject *object = dynamic_cast<const SpatialObject *>(action.object());
glm::vec3 dest(object->position());
SpatialObject &object = *static_cast<SpatialObject *>(action.object());
glm::vec3 dest(object.position());
float distance = action.distance();
navigateCreature(creature, dest, distance, dt);
@ -112,8 +119,12 @@ void ActionExecutor::executeDoCommand(Object &object, CommandAction &action, flo
}
void ActionExecutor::executeStartConversation(Creature &creature, StartConversationAction &action, float dt) {
_area->startDialog(creature, action.dialogResRef());
action.complete();
Creature &target = static_cast<Creature &>(*action.object());
bool reached = action.isStartRangeIgnored() || navigateCreature(creature, target.position(), 1.0f, dt);
if (reached) {
_game->module()->area()->startDialog(target, action.dialogResRef());
action.complete();
}
}
bool ActionExecutor::navigateCreature(Creature &creature, const glm::vec3 &dest, float distance, float dt) {
@ -172,7 +183,7 @@ void ActionExecutor::advanceCreatureOnPath(Creature &creature, float dt) {
if (distToDest <= 1.0f) {
selectNextPathPoint(*path);
} else if (_area->moveCreatureTowards(creature, dest, true, dt)) {
} else if (_game->module()->area()->moveCreatureTowards(creature, dest, true, dt)) {
creature.setMovementType(Creature::MovementType::Run);
} else {
@ -189,12 +200,30 @@ void ActionExecutor::selectNextPathPoint(Creature::Path &path) {
void ActionExecutor::updateCreaturePath(Creature &creature, const glm::vec3 &dest) {
const glm::vec3 &origin = creature.position();
vector<glm::vec3> points(_area->pathfinder().findPath(origin, dest));
vector<glm::vec3> points(_game->module()->area()->pathfinder().findPath(origin, dest));
uint32_t now = SDL_GetTicks();
creature.setPath(dest, move(points), now);
}
void ActionExecutor::executeOpenDoor(Creature &creature, ObjectAction &action, float dt) {
Door &door = *static_cast<Door *>(action.object());
bool reached = navigateCreature(creature, door.position(), 1.0f, dt);
if (reached) {
door.open(&creature);
action.complete();
}
}
void ActionExecutor::executeOpenContainer(Creature &creature, ObjectAction &action, float dt) {
Placeable &placeable = *static_cast<Placeable *>(action.object());
bool reached = navigateCreature(creature, placeable.position(), 1.0f, dt);
if (reached) {
_game->openContainer(&placeable);
action.complete();
}
}
} // namespace game
} // namespace reone

View file

@ -30,16 +30,16 @@ namespace reone {
namespace game {
class Area;
class Game;
class ActionExecutor {
public:
ActionExecutor(Area *area);
ActionExecutor(Game *game);
void executeActions(Object &object, float dt);
private:
Area *_area { nullptr };
Game *_game { nullptr };
ActionExecutor(const ActionExecutor &) = delete;
ActionExecutor &operator=(const ActionExecutor &) = delete;
@ -56,6 +56,8 @@ private:
void executeFollow(Creature &creature, FollowAction &action, float dt);
void executeDoCommand(Object &object, CommandAction &command, float dt);
void executeStartConversation(Creature &creature, StartConversationAction &action, float dt);
void executeOpenDoor(Creature &creature, ObjectAction &action, float dt);
void executeOpenContainer(Creature &creature, ObjectAction &action, float dt);
// END Actions
};

View file

@ -74,6 +74,10 @@ void ActionQueue::updateDelayedActions() {
_delayed.erase(delayedToRemove, _delayed.end());
}
bool ActionQueue::empty() const {
return _actions.empty();
}
Action *ActionQueue::currentAction() {
return _actions.empty() ? nullptr : _actions.front().get();
}

View file

@ -33,6 +33,8 @@ public:
void delay(std::unique_ptr<Action> action, float seconds);
void update();
bool empty() const;
Action *currentAction();
private:

View file

@ -208,7 +208,7 @@ void MainMenu::onListBoxItemClick(const string &control, const string &item) {
shared_ptr<Creature> companion(_game->objectFactory().newCreature());
companion->load(companionCfg);
companion->actionQueue().add(make_unique<FollowAction>(player, 1.0f));
companion->actionQueue().add(make_unique<FollowAction>(player.get(), 1.0f));
party.addMember(companion);
_game->loadModule(item);

View file

@ -223,7 +223,7 @@ void PartySelection::changeParty() {
shared_ptr<Creature> creature(_game->objectFactory().newCreature());
creature->load(blueprint);
creature->actionQueue().add(make_unique<FollowAction>(player, 1.0f));
creature->actionQueue().add(make_unique<FollowAction>(player.get(), 1.0f));
party.addMember(creature);
}

View file

@ -65,7 +65,7 @@ Area::Area(uint32_t id, Game *game) :
_game(game),
_collisionDetector(this),
_objectSelector(this, &game->party()),
_actionExecutor(this) {
_actionExecutor(game) {
if (!game) {
throw invalid_argument("Game must not be null");
@ -490,6 +490,9 @@ bool Area::moveCreatureTowards(Creature &creature, const glm::vec2 &dest, bool r
if (getElevationAt(position, room, position.z)) {
creature.setRoom(room);
creature.setPosition(position);
if (&creature == _game->party().leader().get()) {
onPartyLeaderMoved();
}
return true;
}

View file

@ -79,7 +79,7 @@ void Door::loadBlueprint(const string &resRef) {
_walkmesh = Walkmeshes::instance().get(model + "0", ResourceType::DoorWalkmesh);
}
void Door::open(const shared_ptr<Object> &triggerrer) {
void Door::open(Object *triggerrer) {
if (_model) {
_model->setDefaultAnimation("opened1");
_model->playAnimation("opening1");
@ -88,7 +88,7 @@ void Door::open(const shared_ptr<Object> &triggerrer) {
_selectable = false;
}
void Door::close(const shared_ptr<Object> &triggerrer) {
void Door::close(Object *triggerrer) {
if (_model) {
_model->setDefaultAnimation("closed1");
_model->playAnimation("closing1");

View file

@ -32,8 +32,8 @@ public:
Door(uint32_t id, scene::SceneGraph *sceneGraph);
void load(const resource::GffStruct &gffs);
virtual void open(const std::shared_ptr<Object> &triggerrer);
void close(const std::shared_ptr<Object> &triggerrer);
void open(Object *triggerrer);
void close(Object *triggerrer);
bool isOpen() const;
bool isStatic() const;

View file

@ -169,27 +169,34 @@ void Module::onObjectClick(SpatialObject &object) {
}
void Module::onCreatureClick(Creature &creature) {
if (!creature.conversation().empty()) {
_player->stopMovement();
_game->getActiveCamera()->stopMovement();
_game->startDialog(creature, creature.conversation());
}
if (creature.conversation().empty()) return;
shared_ptr<Creature> partyLeader(_game->party().leader());
ActionQueue &actions = partyLeader->actionQueue();
actions.clear();
actions.add(make_unique<StartConversationAction>(&creature, creature.conversation()));
}
void Module::onDoorClick(Door &door) {
if (!door.linkedToModule().empty()) {
_game->scheduleModuleTransition(door.linkedToModule(), door.linkedTo());
} else if (!door.isOpen() && !door.isStatic()) {
return;
}
if (!door.isOpen() && !door.isStatic()) {
shared_ptr<Creature> partyLeader(_game->party().leader());
door.open(partyLeader);
ActionQueue &actions = partyLeader->actionQueue();
actions.clear();
actions.add(make_unique<ObjectAction>(ActionType::OpenDoor, &door));
}
}
void Module::onPlaceableClick(Placeable &placeable) {
if (placeable.blueprint().hasInventory()) {
_game->openContainer(&placeable);
return;
}
if (!placeable.blueprint().hasInventory()) return;
shared_ptr<Creature> partyLeader(_game->party().leader());
ActionQueue &actions = partyLeader->actionQueue();
actions.clear();
actions.add(make_unique<ObjectAction>(ActionType::OpenContainer, &placeable));
}
bool Module::handleKeyUp(const SDL_KeyboardEvent &event) {

View file

@ -107,7 +107,7 @@ void Party::switchLeader() {
for (int i = 1; i < count; ++i) {
_members[i]->actionQueue().clear();
_members[i]->actionQueue().add(make_unique<FollowAction>(_members[0], 1.0f));
_members[i]->actionQueue().add(make_unique<FollowAction>(_members[0].get(), 1.0f));
}
_game->module()->area()->onPartyLeaderMoved();
}

View file

@ -130,16 +130,20 @@ void Player::update(float dt) {
} else {
movement = false;
}
ActionQueue &actions = partyLeader->actionQueue();
if (movement) {
actions.clear();
glm::vec2 dest(partyLeader->position());
dest.x -= 100.0f * glm::sin(heading);
dest.y += 100.0f * glm::cos(heading);
if (_area->moveCreatureTowards(*partyLeader, dest, true, dt)) {
partyLeader->setMovementType(Creature::MovementType::Run);
_area->onPartyLeaderMoved();
}
} else {
} else if (actions.empty()) {
partyLeader->setMovementType(Creature::MovementType::None);
}
}

View file

@ -256,7 +256,7 @@ Variable Routines::actionMoveToObject(const vector<Variable> &args, ExecutionCon
if (subject) {
Creature &creature = static_cast<Creature &>(*subject);
unique_ptr<MoveToObjectAction> action(new MoveToObjectAction(object, distance));
unique_ptr<MoveToObjectAction> action(new MoveToObjectAction(object.get(), distance));
creature.actionQueue().add(move(action));
} else {
warn("Routine: object not found: " + to_string(objectId));
@ -274,7 +274,8 @@ Variable Routines::actionStartConversation(const vector<Variable> &args, Executi
if (creature) {
string dialogResRef((args.size() >= 2 && !args[1].strValue.empty()) ? args[1].strValue : creature->conversation());
unique_ptr<StartConversationAction> action(new StartConversationAction(object, dialogResRef));
bool ignoreStartRange = args.size() >= 4 ? (args[4].intValue != 0) : false;
unique_ptr<StartConversationAction> action(new StartConversationAction(subject.get(), dialogResRef, ignoreStartRange));
creature->actionQueue().add(move(action));
} else {
warn("Routine: creature not found: " + to_string(ctx.callerId));
@ -319,7 +320,7 @@ Variable Routines::actionOpenDoor(const vector<Variable> &args, ExecutionContext
if (subject) {
Creature *creature = dynamic_cast<Creature *>(subject.get());
if (creature) {
unique_ptr<ObjectAction> action(new ObjectAction(ActionType::OpenDoor, object));
unique_ptr<ObjectAction> action(new ObjectAction(ActionType::OpenDoor, object.get()));
creature->actionQueue().add(move(action));
}
Door *door = dynamic_cast<Door *>(subject.get());
@ -341,7 +342,7 @@ Variable Routines::actionCloseDoor(const vector<Variable> &args, ExecutionContex
if (subject) {
Creature *creature = dynamic_cast<Creature *>(subject.get());
if (creature) {
unique_ptr<ObjectAction> action(new ObjectAction(ActionType::CloseDoor, object));
unique_ptr<ObjectAction> action(new ObjectAction(ActionType::CloseDoor, object.get()));
creature->actionQueue().add(move(action));
}
Door *door = dynamic_cast<Door *>(subject.get());