feat: Object activation adds actions to the action queue
This commit is contained in:
parent
bd4e42fba9
commit
f1262595d0
22 changed files with 121 additions and 55 deletions
|
@ -33,7 +33,8 @@ enum class ActionType {
|
|||
StartConversation = 0x1001,
|
||||
PauseConversation = 0x1002,
|
||||
ResumeConversation = 0x1003,
|
||||
MoveToObject = 0x1004
|
||||
MoveToObject = 0x1004,
|
||||
OpenContainer = 0x1005
|
||||
};
|
||||
|
||||
class Action {
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ public:
|
|||
void delay(std::unique_ptr<Action> action, float seconds);
|
||||
void update();
|
||||
|
||||
bool empty() const;
|
||||
|
||||
Action *currentAction();
|
||||
|
||||
private:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in a new issue