refactor: Refactor game objects

- Split Object class into Object and SpatialObject
- Inherit Module and Area from Object
- Move object creation into (Multiplayer)ObjectFactory class
This commit is contained in:
Vsevolod Kremianskii 2020-08-26 18:19:56 +07:00
parent c34eba71d0
commit 8890a1bd82
37 changed files with 562 additions and 357 deletions

View file

@ -69,12 +69,14 @@ set(HEADERS
src/game/multiplayer/creature.h
src/game/multiplayer/door.h
src/game/multiplayer/game.h
src/game/multiplayer/module.h
src/game/multiplayer/objectfactory.h
src/game/multiplayer/util.h
src/game/object/creature.h
src/game/object/door.h
src/game/object/factory.h
src/game/object/object.h
src/game/object/placeable.h
src/game/object/spatial.h
src/game/object/trigger.h
src/game/object/waypoint.h
src/game/room.h
@ -179,13 +181,15 @@ set(SOURCES
src/game/multiplayer/creature.cpp
src/game/multiplayer/door.cpp
src/game/multiplayer/game.cpp
src/game/multiplayer/module.cpp
src/game/multiplayer/objectfactory.cpp
src/game/multiplayer/util.cpp
src/game/navmesh.cpp
src/game/object/creature.cpp
src/game/object/door.cpp
src/game/object/factory.cpp
src/game/object/object.cpp
src/game/object/placeable.cpp
src/game/object/spatial.cpp
src/game/object/trigger.cpp
src/game/object/waypoint.cpp
src/game/room.cpp

View file

@ -17,6 +17,8 @@
#include "area.h"
#include <cassert>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
@ -33,10 +35,7 @@
#include "../resources/visfile.h"
#include "../script/execution.h"
#include "object/door.h"
#include "object/placeable.h"
#include "object/trigger.h"
#include "object/waypoint.h"
#include "object/factory.h"
#include "script/routines.h"
#include "script/util.h"
@ -63,7 +62,13 @@ static const char kPartyLeaderTag[] = "party-leader";
static const char kPartyMember1Tag[] = "party-member-1";
static const char kPartyMember2Tag[] = "party-member-2";
Area::Area(GameVersion version, const string &name) : _version(version), _name(name), _navMesh(new NavMesh()) {
Area::Area(uint32_t id, GameVersion version, ObjectFactory *factory) :
Object(id), _version(version), _objectFactory(factory), _navMesh(new NavMesh()) {
assert(_objectFactory);
_type = ObjectType::Area;
_objects.insert(make_pair(ObjectType::Creature, ObjectList()));
_objects.insert(make_pair(ObjectType::Door, ObjectList()));
_objects.insert(make_pair(ObjectType::Placeable, ObjectList()));
@ -74,7 +79,9 @@ Area::Area(GameVersion version, const string &name) : _version(version), _name(n
_renderLists.insert(make_pair(RenderListName::Transparent, RenderList()));
}
void Area::load(const GffStruct &are, const GffStruct &git) {
void Area::load(const string &name, const GffStruct &are, const GffStruct &git) {
_name = name;
loadProperties(git.getStruct("AreaProperties"));
loadVisibility();
loadLayout();
@ -82,20 +89,20 @@ void Area::load(const GffStruct &are, const GffStruct &git) {
loadScripts(are);
for (auto &gffs : git.getList("Creature List")) {
shared_ptr<Creature> creature(makeCreature());
shared_ptr<Creature> creature(_objectFactory->newCreature());
creature->load(gffs);
landObject(*creature);
creature->setSynchronize(true);
_objects[ObjectType::Creature].push_back(move(creature));
}
for (auto &gffs : git.getList("Door List")) {
shared_ptr<Door> door(makeDoor());
shared_ptr<Door> door(_objectFactory->newDoor());
door->load(gffs);
door->setSynchronize(true);
_objects[ObjectType::Door].push_back(move(door));
}
for (auto &gffs : git.getList("Placeable List")) {
shared_ptr<Placeable> placeable(makePlaceable());
shared_ptr<Placeable> placeable(_objectFactory->newPlaceable());
placeable->load(gffs);
if (placeable->walkmesh()) {
_navMesh->add(placeable->walkmesh(), placeable->transform());
@ -103,12 +110,12 @@ void Area::load(const GffStruct &are, const GffStruct &git) {
_objects[ObjectType::Placeable].push_back(move(placeable));
}
for (auto &gffs : git.getList("WaypointList")) {
shared_ptr<Waypoint> waypoint(makeWaypoint());
shared_ptr<Waypoint> waypoint(_objectFactory->newWaypoint());
waypoint->load(gffs);
_objects[ObjectType::Waypoint].push_back(move(waypoint));
}
for (auto &gffs : git.getList("TriggerList")) {
shared_ptr<Trigger> trigger(makeTrigger());
shared_ptr<Trigger> trigger(_objectFactory->newTrigger());
trigger->load(gffs);
_objects[ObjectType::Trigger].push_back(move(trigger));
}
@ -120,26 +127,6 @@ void Area::load(const GffStruct &are, const GffStruct &git) {
});
}
shared_ptr<Creature> Area::makeCreature(uint32_t id) {
return make_unique<Creature>(id > 0 ? id : _idCounter++);
}
shared_ptr<Door> Area::makeDoor() {
return make_unique<Door>(_idCounter++);
}
shared_ptr<Placeable> Area::makePlaceable() {
return make_unique<Placeable>(_idCounter++);
}
shared_ptr<Waypoint> Area::makeWaypoint() {
return make_unique<Waypoint>(_idCounter++);
}
shared_ptr<Trigger> Area::makeTrigger() {
return make_unique<Trigger>(_idCounter++);
}
void Area::loadProperties(const GffStruct &gffs) {
ResourceManager &resources = ResourceManager::instance();
shared_ptr<TwoDaTable> musicTable(resources.find2DA("ambientmusic"));
@ -198,7 +185,7 @@ void Area::loadScripts(const GffStruct &are) {
_scripts[ScriptType::OnUserDefined] = are.getString("OnUserDefined");
}
void Area::landObject(Object &object) {
void Area::landObject(SpatialObject &object) {
glm::vec3 position(object.position());
if (findElevationAt(position, position.z)) {
object.setPosition(position);
@ -239,7 +226,7 @@ void Area::loadParty(const PartyConfiguration &party, const glm::vec3 &position,
}
shared_ptr<Creature> Area::makeCharacter(const CreatureConfiguration &character, const string &tag, const glm::vec3 &position, float heading) {
shared_ptr<Creature> creature(makeCreature());
shared_ptr<Creature> creature(_objectFactory->newCreature());
creature->setTag(tag);
creature->load(character);
creature->setPosition(position);
@ -341,7 +328,7 @@ bool Area::moveCreatureTowards(Creature &creature, const glm::vec3 &point, float
newPosition.x += creature.runSpeed() * dir.x * dt;
newPosition.y += creature.runSpeed() * dir.y * dt;
Object *obstacle = nullptr;
SpatialObject *obstacle = nullptr;
glm::vec3 intersection(0.0f);
if (findObstacleByAABB(position, newPosition + 1.0f * dir, kObstacleCreature, &creature, &obstacle)) {
@ -404,7 +391,7 @@ void Area::signalEvent(int eventId) {
return;
}
if (!_scripts[ScriptType::OnUserDefined].empty()) {
runScript(_scripts[ScriptType::OnUserDefined], kObjectArea, kObjectInvalid, it->second.eventNumber);
runScript(_scripts[ScriptType::OnUserDefined], _id, kObjectInvalid, it->second.eventNumber);
}
_events.erase(it);
}
@ -412,7 +399,7 @@ void Area::signalEvent(int eventId) {
void Area::runOnEnterScript() {
if (_scriptsEnabled) {
if (!_scripts[ScriptType::OnEnter].empty()) {
runScript(_scripts[ScriptType::OnEnter], kObjectArea, _player->id(), -1);
runScript(_scripts[ScriptType::OnEnter], _id, _player->id(), -1);
}
}
}
@ -431,7 +418,7 @@ void Area::addToDebugContext(const RenderListItem &item, const UpdateContext &up
debugCtx.objects.push_back(move(object));
}
void Area::addToDebugContext(const Object &object, const UpdateContext &updateCtx, DebugContext &debugCtx) const {
void Area::addToDebugContext(const SpatialObject &object, const UpdateContext &updateCtx, DebugContext &debugCtx) const {
glm::vec4 viewport(0.0f, 0.0f, 1.0f, 1.0f);
glm::vec3 position(object.position());
@ -476,8 +463,8 @@ void Area::loadState(const GameState &state) {
}
}
shared_ptr<Object> Area::find(uint32_t id) const {
shared_ptr<Object> object;
shared_ptr<SpatialObject> Area::find(uint32_t id) const {
shared_ptr<SpatialObject> object;
for (auto &list : _objects) {
object = find(id, list.first);
if (object) return object;
@ -486,8 +473,8 @@ shared_ptr<Object> Area::find(uint32_t id) const {
return nullptr;
}
shared_ptr<Object> Area::find(const string &tag, int nth) const {
shared_ptr<Object> object;
shared_ptr<SpatialObject> Area::find(const string &tag, int nth) const {
shared_ptr<SpatialObject> object;
for (auto &list : _objects) {
object = find(tag, list.first, nth);
if (object) return object;
@ -496,18 +483,18 @@ shared_ptr<Object> Area::find(const string &tag, int nth) const {
return nullptr;
}
shared_ptr<Object> Area::find(uint32_t id, ObjectType type) const {
shared_ptr<SpatialObject> Area::find(uint32_t id, ObjectType type) const {
const ObjectList &list = _objects.find(type)->second;
auto it = find_if(
list.begin(),
list.end(),
[&id](const shared_ptr<Object> &object) { return object->id() == id; });
[&id](const shared_ptr<SpatialObject> &object) { return object->id() == id; });
return it != list.end() ? *it : nullptr;
}
shared_ptr<Object> Area::find(const string &tag, ObjectType type, int nth) const {
shared_ptr<SpatialObject> Area::find(const string &tag, ObjectType type, int nth) const {
const ObjectList &list = _objects.find(type)->second;
int idx = 0;
@ -518,8 +505,8 @@ shared_ptr<Object> Area::find(const string &tag, ObjectType type, int nth) const
return nullptr;
}
bool Area::findObstacleByWalkmesh(const glm::vec3 &from, const glm::vec3 &to, int mask, glm::vec3 &intersection, Object **obstacle) const {
vector<pair<Object *, float>> candidates;
bool Area::findObstacleByWalkmesh(const glm::vec3 &from, const glm::vec3 &to, int mask, glm::vec3 &intersection, SpatialObject **obstacle) const {
vector<pair<SpatialObject *, float>> candidates;
for (auto &list : _objects) {
if (list.first != ObjectType::Door && list.first != ObjectType::Placeable) continue;
@ -540,10 +527,10 @@ bool Area::findObstacleByWalkmesh(const glm::vec3 &from, const glm::vec3 &to, in
sort(
candidates.begin(),
candidates.end(),
[](const pair<Object *, float> &left, const pair<Object *, float> &right) { return left.second < right.second; });
[](const pair<SpatialObject *, float> &left, const pair<SpatialObject *, float> &right) { return left.second < right.second; });
for (auto &pair : candidates) {
Object &object = *pair.first;
SpatialObject &object = *pair.first;
glm::mat4 invObjectTransform(glm::inverse(object.transform()));
glm::vec3 relFrom(invObjectTransform * glm::vec4(from, 1.0f));
@ -573,8 +560,8 @@ bool Area::findObstacleByWalkmesh(const glm::vec3 &from, const glm::vec3 &to, in
return false;
}
bool Area::findObstacleByAABB(const glm::vec3 &from, const glm::vec3 &to, int mask, const Object *except, Object **obstacle) const {
vector<pair<Object *, float>> candidates;
bool Area::findObstacleByAABB(const glm::vec3 &from, const glm::vec3 &to, int mask, const SpatialObject *except, SpatialObject **obstacle) const {
vector<pair<SpatialObject *, float>> candidates;
for (auto &list : _objects) {
if (list.first != ObjectType::Creature &&
@ -600,10 +587,10 @@ bool Area::findObstacleByAABB(const glm::vec3 &from, const glm::vec3 &to, int ma
sort(
candidates.begin(),
candidates.end(),
[](const pair<Object *, float> &left, const pair<Object *, float> &right) { return left.second < right.second; });
[](const pair<SpatialObject *, float> &left, const pair<SpatialObject *, float> &right) { return left.second < right.second; });
for (auto &pair : candidates) {
Object *object = pair.first;
SpatialObject *object = pair.first;
AABB aabb(object->model()->model()->aabb() * object->transform());
float distance = 0.0f;
@ -620,7 +607,7 @@ bool Area::findElevationAt(const glm::vec3 &position, float &z) const {
glm::vec3 from(position + glm::vec3(0.0f, 0.0f, kElevationTestOffset));
glm::vec3 to(from + glm::vec3(0.0f, 0.0f, -kElevationTestDistance));
glm::vec3 intersection(0.0f);
Object *obstacle = nullptr;
SpatialObject *obstacle = nullptr;
if (findObstacleByWalkmesh(from, to, kObstacleDoor | kObstaclePlaceable, intersection, &obstacle)) {
return false;
@ -648,19 +635,19 @@ const string &Area::music() const {
return _music;
}
shared_ptr<Object> Area::player() const {
shared_ptr<SpatialObject> Area::player() const {
return _player;
}
shared_ptr<Object> Area::partyLeader() const {
shared_ptr<SpatialObject> Area::partyLeader() const {
return _partyLeader;
}
shared_ptr<Object> Area::partyMember1() const {
shared_ptr<SpatialObject> Area::partyMember1() const {
return _partyMember1;
}
shared_ptr<Object> Area::partyMember2() const {
shared_ptr<SpatialObject> Area::partyMember2() const {
return _partyMember2;
}

View file

@ -42,19 +42,15 @@ namespace reone {
namespace game {
typedef std::vector<std::shared_ptr<Object>> ObjectList;
typedef std::vector<std::shared_ptr<SpatialObject>> ObjectList;
/*
* Game object container. Building block of a module.
*
* @see reone::game::Module
* @see reone::game::Object
*/
class Area {
class ObjectFactory;
class Area : public Object {
public:
Area(resources::GameVersion version, const std::string &name);
Area(uint32_t id, resources::GameVersion version, ObjectFactory *objectFactory);
void load(const resources::GffStruct &are, const resources::GffStruct &git);
void load(const std::string &name, const resources::GffStruct &are, const resources::GffStruct &git);
void loadParty(const PartyConfiguration &party, const glm::vec3 &position, float heading);
void runOnEnterScript();
@ -76,25 +72,25 @@ public:
void loadState(const GameState &state);
// Object search
std::shared_ptr<Object> find(uint32_t id) const;
std::shared_ptr<Object> find(const std::string &tag, int nth = 0) const;
std::shared_ptr<Object> find(uint32_t id, ObjectType type) const;
std::shared_ptr<Object> find(const std::string &tag, ObjectType type, int nth = 0) const;
bool findObstacleByWalkmesh(const glm::vec3 &from, const glm::vec3 &to, int mask, glm::vec3 &intersection, Object **obstacle) const;
bool findObstacleByAABB(const glm::vec3 &from, const glm::vec3 &to, int mask, const Object *except, Object **obstacle) const;
// Setters
void setDebugMode(DebugMode mode);
std::shared_ptr<SpatialObject> find(uint32_t id) const;
std::shared_ptr<SpatialObject> find(const std::string &tag, int nth = 0) const;
std::shared_ptr<SpatialObject> find(uint32_t id, ObjectType type) const;
std::shared_ptr<SpatialObject> find(const std::string &tag, ObjectType type, int nth = 0) const;
bool findObstacleByWalkmesh(const glm::vec3 &from, const glm::vec3 &to, int mask, glm::vec3 &intersection, SpatialObject **obstacle) const;
bool findObstacleByAABB(const glm::vec3 &from, const glm::vec3 &to, int mask, const SpatialObject *except, SpatialObject **obstacle) const;
// General getters
const render::CameraStyle &cameraStyle() const;
const std::string &music() const;
// Party getters
std::shared_ptr<Object> player() const;
std::shared_ptr<Object> partyLeader() const;
std::shared_ptr<Object> partyMember1() const;
std::shared_ptr<Object> partyMember2() const;
std::shared_ptr<SpatialObject> player() const;
std::shared_ptr<SpatialObject> partyLeader() const;
std::shared_ptr<SpatialObject> partyMember1() const;
std::shared_ptr<SpatialObject> partyMember2() const;
// Setters
void setDebugMode(DebugMode mode);
// Callbacks
void setOnModuleTransition(const std::function<void(const std::string &, const std::string &)> &fn);
@ -102,24 +98,19 @@ public:
void setOnStartDialog(const std::function<void(const Object &, const std::string &)> &fn);
protected:
uint32_t _idCounter { 1000 };
ObjectFactory *_objectFactory { nullptr };
std::map<ObjectType, ObjectList> _objects;
bool _scriptsEnabled { true };
std::function<void()> _onPlayerChanged;
// Party
std::shared_ptr<Object> _player;
std::shared_ptr<Object> _partyLeader;
std::shared_ptr<Object> _partyMember1;
std::shared_ptr<Object> _partyMember2;
std::shared_ptr<SpatialObject> _player;
std::shared_ptr<SpatialObject> _partyLeader;
std::shared_ptr<SpatialObject> _partyMember1;
std::shared_ptr<SpatialObject> _partyMember2;
void landObject(Object &object);
void landObject(SpatialObject &object);
virtual std::shared_ptr<Creature> makeCreature(uint32_t id = 0);
virtual std::shared_ptr<Door> makeDoor();
virtual std::shared_ptr<Placeable> makePlaceable();
virtual std::shared_ptr<Waypoint> makeWaypoint();
virtual std::shared_ptr<Trigger> makeTrigger();
virtual void updateCreature(Creature &creature, float dt);
private:
@ -171,7 +162,7 @@ private:
void updateCreaturePath(Creature &creature, const glm::vec3 &dest);
void fillRenderLists(const glm::vec3 &cameraPosition);
void addToDebugContext(const render::RenderListItem &item, const UpdateContext &updateCtx, DebugContext &debugCtx) const;
void addToDebugContext(const Object &object, const UpdateContext &updateCtx, DebugContext &debugCtx) const;
void addToDebugContext(const SpatialObject &object, const UpdateContext &updateCtx, DebugContext &debugCtx) const;
bool findElevationAt(const glm::vec3 &position, float &z) const;
// Loading

View file

@ -40,7 +40,10 @@ void Area::updateCreature(Creature &creature, float dt) {
switch (action.type) {
case Creature::ActionType::MoveToPoint:
case Creature::ActionType::Follow: {
glm::vec3 dest = (action.type == Creature::ActionType::Follow || action.object) ? action.object->position() : action.point;
SpatialObject *spatial = dynamic_cast<SpatialObject *>(action.object.get());
assert(spatial);
glm::vec3 dest = (action.type == Creature::ActionType::Follow || action.object) ? spatial->position() : action.point;
bool reached = navigateCreature(creature, dest, action.distance, dt);
if (reached && action.type == Creature::ActionType::MoveToPoint) {
creature.popCurrentAction();

View file

@ -86,7 +86,7 @@ void Area::render() const {
for (auto &object : list.second) {
shared_ptr<ModelInstance> model(object->model());
if (!model) return;
if (!model) continue;
aabb.render(model->model()->aabb(), object->transform());
}

View file

@ -27,6 +27,7 @@
#include "../core/streamutil.h"
#include "../resources/resources.h"
#include "object/factory.h"
#include "script/routines.h"
#include "util.h"
@ -56,6 +57,12 @@ Game::Game(GameVersion version, const fs::path &path, const Options &opts) :
_path(path),
_opts(opts),
_renderWindow(opts.graphics, this) {
initObjectFactory();
}
void Game::initObjectFactory() {
_objectFactory = make_unique<ObjectFactory>(_version, _opts);
}
int Game::run() {
@ -188,10 +195,10 @@ void Game::loadModule(const string &name, const PartyConfiguration &party, strin
shared_ptr<GffStruct> ifo(ResMan.findGFF("module", ResourceType::ModuleInfo));
_module = makeModule(name);
_module = _objectFactory->newModule();
configureModule();
_module->load(*ifo);
_module->load(name, *ifo);
_module->loadParty(party, entry);
_module->area().loadState(_state);
_module->initGL();
@ -254,10 +261,10 @@ void Game::onDialogReplyPicked(uint32_t index) {
}
void Game::onDialogSpeakerChanged(uint32_t from, uint32_t to) {
shared_ptr<Object> player(_module->area().player());
shared_ptr<Object> partyLeader(_module->area().partyLeader());
shared_ptr<Object> prevSpeaker = from != 0 ? _module->area().find(from, ObjectType::Creature) : nullptr;
shared_ptr<Object> speaker = to != 0 ? _module->area().find(to, ObjectType::Creature) : nullptr;
shared_ptr<SpatialObject> player(_module->area().player());
shared_ptr<SpatialObject> partyLeader(_module->area().partyLeader());
shared_ptr<SpatialObject> prevSpeaker = from != 0 ? _module->area().find(from, ObjectType::Creature) : nullptr;
shared_ptr<SpatialObject> speaker = to != 0 ? _module->area().find(to, ObjectType::Creature) : nullptr;
if (speaker == partyLeader) return;
debug(boost::format("Game: dialog speaker: \"%s\"") % (speaker ? speaker->tag() : ""));
@ -282,10 +289,6 @@ void Game::onDialogFinished() {
_screen = Screen::InGame;
}
const shared_ptr<Module> Game::makeModule(const string &name) {
return make_shared<Module>(name, _version, _opts.graphics);
}
void Game::configureModule() {
_module->setOnCameraChanged([this](CameraType type) {
_renderWindow.setRelativeMouseMode(type == CameraType::FirstPerson);
@ -490,6 +493,14 @@ void Game::delayCommand(uint32_t timestamp, const ExecutionContext &ctx) {
_module->area().delayCommand(timestamp, ctx);
}
Module *Game::getModule() {
return _module.get();
}
Area *Game::getArea() {
return &_module->area();
}
shared_ptr<Object> Game::getObjectById(uint32_t id) {
return _module->area().find(id);
}
@ -510,8 +521,14 @@ int Game::eventUserDefined(int eventNumber) {
return _module->area().eventUserDefined(eventNumber);
}
void Game::signalEvent(int eventId) {
_module->area().signalEvent(eventId);
void Game::signalEvent(uint32_t objectId, int eventId) {
assert(objectId >= 2);
Area &area = _module->area();
if (objectId != area.id()) {
warn("Game: event object is not an area");
return;
}
area.signalEvent(eventId);
}
bool Game::getGlobalBoolean(const string &name) const {

View file

@ -31,6 +31,7 @@
#include "gui/mainmenu.h"
#include "gui/portraitsel.h"
#include "area.h"
#include "module.h"
namespace reone {
@ -64,9 +65,11 @@ public:
// Events
int eventUserDefined(int eventNumber) override;
void signalEvent(int eventId) override;
void signalEvent(uint32_t objectId, int eventId) override;
// Objects
Module *getModule() override;
Area *getArea() override;
std::shared_ptr<Object> getObjectById(uint32_t id) override;
std::shared_ptr<Object> getObjectByTag(const std::string &tag, int nth) override;
std::shared_ptr<Object> getWaypointByTag(const std::string &tag) override;
@ -101,14 +104,15 @@ protected:
resources::GameVersion _version { resources::GameVersion::KotOR };
Options _opts;
std::unique_ptr<ObjectFactory> _objectFactory;
std::shared_ptr<Module> _module;
std::string _nextModule;
Screen _screen { Screen::None };
std::shared_ptr<DialogGui> _dialogGui;
bool _pickDialogReplyEnabled { true };
virtual void initObjectFactory();
virtual void configure();
virtual const std::shared_ptr<Module> makeModule(const std::string &name);
virtual void configureModule();
virtual void update();
virtual void loadNextModule();

View file

@ -25,6 +25,7 @@
#include "../resources/resources.h"
#include "object/door.h"
#include "object/factory.h"
using namespace std;
using namespace std::placeholders;
@ -40,18 +41,17 @@ namespace game {
static const float kDefaultFieldOfView = 75.0f;
Module::Module(
const string &name,
GameVersion version,
const GraphicsOptions &opts
) :
_name(name),
_version(version),
_opts(opts),
_cameraAspect(opts.width / static_cast<float>(opts.height)) {
Module::Module(uint32_t id, GameVersion version, ObjectFactory *objectFactory, const GraphicsOptions &opts) :
Object(id), _version(version), _objectFactory(objectFactory), _opts(opts) {
assert(_objectFactory);
_type = ObjectType::Module;
_cameraAspect = opts.width / static_cast<float>(opts.height);
}
void Module::load(const GffStruct &ifo) {
void Module::load(const string &name, const GffStruct &ifo) {
_name = name;
loadInfo(ifo);
loadArea(ifo);
loadCameras();
@ -78,7 +78,7 @@ void Module::loadArea(const GffStruct &ifo) {
shared_ptr<GffStruct> are(resources.findGFF(_info.entryArea, ResourceType::Area));
shared_ptr<GffStruct> git(resources.findGFF(_info.entryArea, ResourceType::GameInstance));
shared_ptr<Area> area(makeArea());
shared_ptr<Area> area(_objectFactory->newArea());
area->setOnModuleTransition([this](const string &module, const string &entry) {
if (_onModuleTransition) {
_onModuleTransition(module, entry);
@ -99,14 +99,10 @@ void Module::loadArea(const GffStruct &ifo) {
_startDialog(object, finalResRef);
});
area->load(*are, *git);
area->load(_info.entryArea, *are, *git);
_area = move(area);
}
const shared_ptr<Area> Module::makeArea() const {
return make_shared<Area>(_version, _info.entryArea);
}
void Module::loadCameras() {
glm::vec3 position(_info.entryPosition);
position.z += 1.7f;
@ -130,7 +126,7 @@ void Module::loadCameras() {
}
bool Module::findObstacle(const glm::vec3 &from, const glm::vec3 &to, glm::vec3 &intersection) const {
Object *obstacle = nullptr;
SpatialObject *obstacle = nullptr;
if (_area->findObstacleByWalkmesh(from, to, kObstacleRoom | kObstacleDoor, intersection, &obstacle)) {
return true;
}
@ -159,7 +155,7 @@ void Module::getEntryPoint(const string &waypoint, glm::vec3 &position, float &h
heading = _info.entryHeading;
if (!waypoint.empty()) {
shared_ptr<Object> object(_area->find(waypoint, ObjectType::Waypoint));
shared_ptr<SpatialObject> object(_area->find(waypoint, ObjectType::Waypoint));
if (object) {
position = object->position();
heading = object->heading();
@ -168,14 +164,14 @@ void Module::getEntryPoint(const string &waypoint, glm::vec3 &position, float &h
}
void Module::update3rdPersonCameraTarget() {
shared_ptr<Object> player(_area->player());
shared_ptr<SpatialObject> player(_area->player());
if (!player) return;
_thirdPersonCamera->setTargetPosition(player->position() + player->model()->getNodeAbsolutePosition("camerahook"));
}
void Module::update3rdPersonCameraHeading() {
shared_ptr<Object> player(_area->player());
shared_ptr<SpatialObject> player(_area->player());
if (!player) return;
_thirdPersonCamera->setHeading(player->heading());
@ -218,9 +214,9 @@ bool Module::handleMouseButtonUp(const SDL_MouseButtonEvent &event) {
glm::vec3 fromWorld(glm::unProject(glm::vec3(event.x, _opts.height - event.y, 0.0f), camera->view(), camera->projection(), viewport));
glm::vec3 toWorld(glm::unProject(glm::vec3(event.x, _opts.height - event.y, 1.0f), camera->view(), camera->projection(), viewport));
shared_ptr<Object> player(_area->player());
Object *except = player ? player.get() : nullptr;
Object *obstacle = nullptr;
shared_ptr<SpatialObject> player(_area->player());
SpatialObject *except = player ? player.get() : nullptr;
SpatialObject *obstacle = nullptr;
if (_area->findObstacleByAABB(fromWorld, toWorld, kObstacleCreature | kObstacleDoor | kObstaclePlaceable, except, &obstacle)) {
assert(obstacle);

View file

@ -39,33 +39,23 @@ struct ModuleInfo {
float entryHeading { 0.0f };
};
/**
* Isolated portion of the game.
*
* @see reone::game::Area
*/
class Module : public render::IEventHandler {
class Module : public Object {
public:
Module(
const std::string &name,
resources::GameVersion version,
const render::GraphicsOptions &opts);
Module(uint32_t id, resources::GameVersion version, ObjectFactory *objectFactory, const render::GraphicsOptions &opts);
// Loading
void load(const resources::GffStruct &ifo);
void load(const std::string &name, const resources::GffStruct &ifo);
void loadParty(const PartyConfiguration &party, const std::string &entry = "");
// Rendering
void initGL();
void render() const;
bool handle(const SDL_Event &event) override;
bool handle(const SDL_Event &event);
void update(float dt, GuiContext &guiCtx);
void update3rdPersonCameraHeading();
void saveTo(GameState &state) const;
virtual const std::shared_ptr<Area> makeArea() const;
// Getters
const std::string &name() const;
bool loaded() const;
@ -85,6 +75,7 @@ protected:
ModuleInfo _info;
private:
ObjectFactory *_objectFactory { nullptr };
std::string _name;
bool _loaded { false };
render::GraphicsOptions _opts;

View file

@ -17,11 +17,11 @@
#include "area.h"
#include "creature.h"
#include "door.h"
#include "../object/factory.h"
using namespace std;
using namespace reone::resources;
using namespace reone::net;
namespace reone {
@ -29,24 +29,17 @@ namespace reone {
namespace game {
MultiplayerArea::MultiplayerArea(
uint32_t id,
GameVersion version,
MultiplayerMode mode,
resources::GameVersion version,
const string &name,
IMultiplayerCallbacks *mpCallbacks
ObjectFactory *objectFactory,
IMultiplayerCallbacks *callbacks
) :
Area(version, name), _callbacks(mpCallbacks) {
Area(id, version, objectFactory), _callbacks(callbacks) {
_scriptsEnabled = mode == MultiplayerMode::Server;
}
shared_ptr<Creature> MultiplayerArea::makeCreature(uint32_t id) {
return make_unique<MultiplayerCreature>(id > 0 ? id : _idCounter++, _callbacks);
}
shared_ptr<Door> MultiplayerArea::makeDoor() {
return make_unique<MultiplayerDoor>(_idCounter++, _callbacks);
}
void MultiplayerArea::updateCreature(Creature &creature, float dt) {
if (static_cast<MultiplayerCreature &>(creature).isControlled()) return;
@ -81,7 +74,7 @@ void MultiplayerArea::execute(const Command &cmd) {
}
}
void MultiplayerArea::executeLoadCreature(const Command &cmd) {
shared_ptr<Creature> creature(makeCreature(cmd.objectId()));
shared_ptr<Creature> creature(new MultiplayerCreature(cmd.objectId(), _callbacks));
creature->setTag(cmd.tag());
for (auto &item : cmd.equipment()) {
@ -136,7 +129,7 @@ void MultiplayerArea::executeSetPlayerRole(const Command &cmd) {
}
void MultiplayerArea::executeSetObjectTransform(const Command &cmd) {
shared_ptr<Object> object(find(cmd.objectId(), ObjectType::Creature));
shared_ptr<SpatialObject> object(find(cmd.objectId(), ObjectType::Creature));
if (object) {
object->setSynchronize(false);
object->setPosition(cmd.position());
@ -146,7 +139,7 @@ void MultiplayerArea::executeSetObjectTransform(const Command &cmd) {
}
void MultiplayerArea::executeSetObjectAnimation(const Command &cmd) {
shared_ptr<Object> object(find(cmd.objectId(), ObjectType::Creature));
shared_ptr<SpatialObject> object(find(cmd.objectId(), ObjectType::Creature));
if (object) {
object->setSynchronize(false);
object->animate(cmd.animation(), cmd.animationFlags());
@ -155,7 +148,7 @@ void MultiplayerArea::executeSetObjectAnimation(const Command &cmd) {
}
void MultiplayerArea::executeSetCreatureMovementType(const Command &cmd) {
shared_ptr<Object> creature(find(cmd.objectId(), ObjectType::Creature));
shared_ptr<SpatialObject> creature(find(cmd.objectId(), ObjectType::Creature));
if (creature) {
creature->setSynchronize(false);
static_cast<Creature &>(*creature).setMovementType(cmd.movementType());
@ -164,7 +157,7 @@ void MultiplayerArea::executeSetCreatureMovementType(const Command &cmd) {
}
void MultiplayerArea::executeSetCreatureTalking(const Command &cmd) {
shared_ptr<Object> creature(find(cmd.objectId(), ObjectType::Creature));
shared_ptr<SpatialObject> creature(find(cmd.objectId(), ObjectType::Creature));
if (creature) {
creature->setSynchronize(false);
static_cast<Creature &>(*creature).setTalking(cmd.talking());

View file

@ -28,9 +28,10 @@ namespace game {
class MultiplayerArea : public Area {
public:
MultiplayerArea(
MultiplayerMode mode,
uint32_t id,
resources::GameVersion version,
const std::string &name,
MultiplayerMode mode,
ObjectFactory *objectFactory,
IMultiplayerCallbacks *callbacks);
void execute(const Command &cmd);
@ -40,8 +41,6 @@ public:
private:
IMultiplayerCallbacks *_callbacks { nullptr };
std::shared_ptr<Creature> makeCreature(uint32_t id) override;
std::shared_ptr<Door> makeDoor() override;
void updateCreature(Creature &creature, float dt) override;
void executeLoadCreature(const Command &cmd);

View file

@ -41,14 +41,14 @@ const string &MultiplayerCreature::clientTag() const {
}
void MultiplayerCreature::animate(const string &anim, int flags, float speed) {
Object::animate(anim, flags);
SpatialObject::animate(anim, flags);
if (_synchronize) {
_callbacks->onObjectAnimationChanged(*this, anim, flags, speed);
}
}
void MultiplayerCreature::updateTransform() {
Object::updateTransform();
SpatialObject::updateTransform();
if (_synchronize) {
_callbacks->onObjectTransformChanged(*this, _position, _heading);
}

View file

@ -20,7 +20,7 @@
#include "../../core/log.h"
#include "area.h"
#include "module.h"
#include "objectfactory.h"
#include "util.h"
using namespace std;
@ -46,6 +46,10 @@ MultiplayerGame::MultiplayerGame(
_pickDialogReplyEnabled = _mode == MultiplayerMode::Server;
}
void MultiplayerGame::initObjectFactory() {
_objectFactory = unique_ptr<ObjectFactory>(new MultiplayerObjectFactory(_version, _mode, this, _opts));
}
void MultiplayerGame::configure() {
switch (_mode) {
case MultiplayerMode::Server:
@ -380,10 +384,6 @@ void MultiplayerGame::sendFinishDialog() {
sendCommand(cmd);
}
const shared_ptr<Module> MultiplayerGame::makeModule(const string &name) {
return shared_ptr<Module>(new MultiplayerModule(name, _mode, _version, _opts.graphics, this));
}
} // namespace game
} // namespace reone

View file

@ -52,10 +52,10 @@ private:
std::recursive_mutex _commandsInMutex;
// Game overrides
void initObjectFactory() override;
void configure() override;
void update() override;
void loadNextModule() override;
const std::shared_ptr<Module> makeModule(const std::string &name) override;
void startDialog(uint32_t ownerId, const std::string &resRef) override;
void onDialogReplyPicked(uint32_t index) override;
void onDialogFinished() override;

View file

@ -15,31 +15,39 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "module.h"
#include "objectfactory.h"
#include "area.h"
#include "creature.h"
#include "door.h"
using namespace std;
using namespace reone::render;
using namespace reone::resources;
namespace reone {
namespace game {
MultiplayerModule::MultiplayerModule(
const string &name,
MultiplayerMode mode,
MultiplayerObjectFactory::MultiplayerObjectFactory(
GameVersion version,
const GraphicsOptions &opts,
IMultiplayerCallbacks *callbacks
MultiplayerMode mode,
IMultiplayerCallbacks *callbacks,
const Options &opts
) :
Module(name, version, opts), _callbacks(callbacks) {
ObjectFactory(version, opts), _mode(mode), _callbacks(callbacks) {
}
const shared_ptr<Area> MultiplayerModule::makeArea() const {
return shared_ptr<Area>(new MultiplayerArea(_mode, _version, _info.entryArea, _callbacks));
unique_ptr<Area> MultiplayerObjectFactory::newArea() {
return make_unique<MultiplayerArea>(_counter++, _version, _mode, this, _callbacks);
}
unique_ptr<Creature> MultiplayerObjectFactory::newCreature() {
return make_unique<MultiplayerCreature>(_counter++, _callbacks);
}
unique_ptr<Door> MultiplayerObjectFactory::newDoor() {
return make_unique<MultiplayerDoor>(_counter++, _callbacks);
}
} // namespace game

View file

@ -17,28 +17,25 @@
#pragma once
#include "../module.h"
#include "../object/factory.h"
#include "callbacks.h"
namespace reone {
namespace game {
class IMultiplayerCallbacks;
class MultiplayerModule : public Module {
class MultiplayerObjectFactory : public ObjectFactory {
public:
MultiplayerModule(
const std::string &name,
MultiplayerMode mode,
resources::GameVersion version,
const render::GraphicsOptions &opts,
IMultiplayerCallbacks *callbacks);
MultiplayerObjectFactory(resources::GameVersion version, MultiplayerMode mode, IMultiplayerCallbacks *callbacks, const Options &opts);
std::unique_ptr<Area> newArea();
std::unique_ptr<Creature> newCreature();
std::unique_ptr<Door> newDoor();
private:
MultiplayerMode _mode { MultiplayerMode::None };
IMultiplayerCallbacks *_callbacks { nullptr };
const std::shared_ptr<Area> makeArea() const override;
};
} // namespace game

View file

@ -60,7 +60,7 @@ Creature::Action::Action(ActionType type, const shared_ptr<Object> &object, floa
Creature::Action::Action(ActionType type, const ExecutionContext &ctx) : type(type), context(ctx) {
}
Creature::Creature(uint32_t id) : Object(id) {
Creature::Creature(uint32_t id) : SpatialObject(id) {
_type = ObjectType::Creature;
_drawDistance = 2048.0f;
_fadeDistance = 0.25f * _drawDistance;
@ -229,7 +229,7 @@ void Creature::loadPortrait(int appearance) {
}
void Creature::initGL() {
Object::initGL();
SpatialObject::initGL();
if (_portrait) _portrait->initGL();
}

View file

@ -25,13 +25,13 @@
#include "../item.h"
#include "object.h"
#include "spatial.h"
namespace reone {
namespace game {
class Creature : public Object {
class Creature : public SpatialObject {
public:
enum class ActionType {
MoveToPoint = 0,

View file

@ -32,7 +32,7 @@ namespace reone {
namespace game {
Door::Door(uint32_t id) : Object(id) {
Door::Door(uint32_t id) : SpatialObject(id) {
_type = ObjectType::Door;
_drawDistance = FLT_MAX;
_fadeDistance = 0.25f * _drawDistance;

View file

@ -17,13 +17,15 @@
#pragma once
#include "object.h"
#include "spatial.h"
#include "../../resources/gfffile.h"
namespace reone {
namespace game {
class Door : public Object {
class Door : public SpatialObject {
public:
Door(uint32_t id);

View file

@ -0,0 +1,62 @@
/*
* Copyright © 2020 Vsevolod Kremianskii
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "factory.h"
using namespace std;
using namespace reone::resources;
namespace reone {
namespace game {
ObjectFactory::ObjectFactory(GameVersion version, const Options &opts) :
_version(version), _options(opts) {
}
unique_ptr<Module> ObjectFactory::newModule() {
return make_unique<Module>(_counter++, _version, this, _options.graphics);
}
unique_ptr<Area> ObjectFactory::newArea() {
return make_unique<Area>(_counter++, _version, this);
}
unique_ptr<Creature> ObjectFactory::newCreature() {
return make_unique<Creature>(_counter++);
}
unique_ptr<Placeable> ObjectFactory::newPlaceable() {
return make_unique<Placeable>(_counter++);
}
unique_ptr<Door> ObjectFactory::newDoor() {
return make_unique<Door>(_counter++);
}
unique_ptr<Waypoint> ObjectFactory::newWaypoint() {
return make_unique<Waypoint>(_counter++);
}
unique_ptr<Trigger> ObjectFactory::newTrigger() {
return make_unique<Trigger>(_counter++);
}
} // namespace game
} // namespace reone

63
src/game/object/factory.h Normal file
View file

@ -0,0 +1,63 @@
/*
* Copyright © 2020 Vsevolod Kremianskii
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <memory>
#include "../../resources/types.h"
#include "../area.h"
#include "../module.h"
#include "../types.h"
#include "creature.h"
#include "door.h"
#include "placeable.h"
#include "trigger.h"
#include "waypoint.h"
namespace reone {
namespace game {
class ObjectFactory {
public:
ObjectFactory(resources::GameVersion version, const Options &opts);
virtual std::unique_ptr<Module> newModule();
virtual std::unique_ptr<Area> newArea();
virtual std::unique_ptr<Creature> newCreature();
virtual std::unique_ptr<Placeable> newPlaceable();
virtual std::unique_ptr<Door> newDoor();
virtual std::unique_ptr<Waypoint> newWaypoint();
virtual std::unique_ptr<Trigger> newTrigger();
protected:
resources::GameVersion _version { resources::GameVersion::KotOR };
Options _options;
uint32_t _counter { 2 }; // ids 0 and 1 are reserved
private:
ObjectFactory(const ObjectFactory &) = delete;
ObjectFactory &operator=(const ObjectFactory &) = delete;
};
} // namespace game
} // namespace reone

View file

@ -17,9 +17,6 @@
#include "object.h"
#include "glm/gtx/euler_angles.hpp"
#include "glm/gtx/norm.hpp"
using namespace std;
using namespace reone::render;
@ -32,43 +29,7 @@ namespace game {
Object::Object(uint32_t id) : _id(id) {
}
void Object::animate(const string &parent, const string &anim, int flags, float speed) {
if (_model) {
_model->animate(parent, anim, flags, speed);
}
}
void Object::animate(const string &anim, int flags, float speed) {
if (_model) {
_model->animate(anim, flags, speed);
}
}
void Object::update(const UpdateContext &ctx) {
if (_model) {
glm::vec4 viewport(-1.0f, -1.0f, 1.0f, 1.0f);
glm::vec3 screenCoords = glm::project(_position, ctx.view, ctx.projection, viewport);
float distanceToCamera = glm::distance2(_position, ctx.cameraPosition);
bool visible = distanceToCamera < _drawDistance && screenCoords.z < 1.0f;
float alpha = 1.0f;
if (_drawDistance != _fadeDistance && distanceToCamera > _fadeDistance) {
alpha = 1.0f - (distanceToCamera - _fadeDistance) / (_drawDistance - _fadeDistance);
}
if (visible) {
_model->show();
} else {
_model->hide();
}
_model->setAlpha(alpha);
_model->update(ctx.deltaTime);
}
}
void Object::initGL() {
if (_model) {
_model->initGL();
}
}
void Object::saveTo(AreaState &state) const {
@ -77,38 +38,10 @@ void Object::saveTo(AreaState &state) const {
void Object::loadState(const AreaState &state) {
}
void Object::face(const Object &other) {
glm::vec2 dir(glm::normalize(other._position - _position));
_heading = -glm::atan(dir.x, dir.y);
updateTransform();
}
float Object::distanceTo(const glm::vec3 &point) const {
return glm::distance2(_position, point);
}
bool Object::contains(const glm::vec3 &point) const {
if (!_model) return false;
const AABB &aabb = _model->model()->aabb();
return (aabb * _transform).contains(point);
}
void Object::setPosition(const glm::vec3 &position) {
_position = position;
updateTransform();
}
void Object::setSynchronize(bool synchronize) {
_synchronize = synchronize;
}
void Object::setHeading(float heading) {
_heading = heading;
updateTransform();
}
uint32_t Object::id() const {
return _id;
}
@ -121,31 +54,6 @@ const string &Object::tag() const {
return _tag;
}
const glm::vec3 &Object::position() const {
return _position;
}
float Object::heading() const {
return _heading;
}
const glm::mat4 &Object::transform() const {
return _transform;
}
shared_ptr<ModelInstance> Object::model() const {
return _model;
}
shared_ptr<Walkmesh> Object::walkmesh() const {
return _walkmesh;
}
void Object::updateTransform() {
_transform = glm::translate(glm::mat4(1.0f), _position);
_transform *= glm::eulerAngleZ(_heading);
}
} // namespace game
} // namespace reone

View file

@ -17,63 +17,35 @@
#pragma once
#include "../../render/modelinstance.h"
#include "../../render/walkmesh.h"
#include "../../resources/gfffile.h"
#include "../types.h"
#include <cstdint>
#include <string>
namespace reone {
namespace game {
class Object {
public:
virtual void animate(const std::string &parent, const std::string &anim, int flags = 0, float speed = 1.0f);
virtual void animate(const std::string &anim, int flags = 0, float speed = 1.0f);
virtual void update(const UpdateContext &ctx);
virtual void initGL();
virtual void saveTo(AreaState &state) const;
virtual void loadState(const AreaState &state);
void face(const Object &other);
float distanceTo(const glm::vec3 &point) const;
bool contains(const glm::vec3 &point) const;
// Setters
void setPosition(const glm::vec3 &position);
void setHeading(float heading);
void setSynchronize(bool synchronize);
// Getters
uint32_t id() const;
ObjectType type() const;
const std::string &tag() const;
const glm::vec3 &position() const;
float heading() const;
const glm::mat4 &transform() const;
std::shared_ptr<render::ModelInstance> model() const;
std::shared_ptr<render::Walkmesh> walkmesh() const;
void setSynchronize(bool synchronize);
protected:
uint32_t _id { 0 };
ObjectType _type { ObjectType::None };
float _drawDistance { 1024.0f };
float _fadeDistance { 256.0f };
std::string _tag;
glm::vec3 _position { 0.0f };
float _heading { 0.0f };
glm::mat4 _transform { 1.0f };
std::shared_ptr<render::ModelInstance> _model;
std::shared_ptr<render::Walkmesh> _walkmesh;
bool _synchronize { false };
Object(uint32_t id);
Object(Object &&) = default;
Object &operator=(Object &&) = default;
virtual void updateTransform();
private:
Object(const Object &) = delete;

View file

@ -34,7 +34,7 @@ namespace reone {
namespace game {
Placeable::Placeable(uint32_t id) : Object(id) {
Placeable::Placeable(uint32_t id) : SpatialObject(id) {
_type = ObjectType::Placeable;
_drawDistance = 4096.0f;
_fadeDistance = 0.25f * _drawDistance;

View file

@ -19,15 +19,15 @@
#include <vector>
#include "object.h"
#include "../item.h"
#include "spatial.h"
namespace reone {
namespace game {
class Placeable : public Object {
class Placeable : public SpatialObject {
public:
Placeable(uint32_t id);

127
src/game/object/spatial.cpp Normal file
View file

@ -0,0 +1,127 @@
/*
* Copyright © 2020 Vsevolod Kremianskii
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "spatial.h"
#include "glm/gtx/euler_angles.hpp"
#include "glm/gtx/norm.hpp"
using namespace std;
using namespace reone::render;
namespace reone {
namespace game {
SpatialObject::SpatialObject(uint32_t id) : Object(id) {
}
float SpatialObject::distanceTo(const glm::vec3 &point) const {
return glm::distance2(_position, point);
}
bool SpatialObject::contains(const glm::vec3 &point) const {
if (!_model) return false;
const AABB &aabb = _model->model()->aabb();
return (aabb * _transform).contains(point);
}
void SpatialObject::face(const SpatialObject &other) {
glm::vec2 dir(glm::normalize(other._position - _position));
_heading = -glm::atan(dir.x, dir.y);
updateTransform();
}
void SpatialObject::update(const UpdateContext &ctx) {
if (!_model) return;
glm::vec4 viewport(-1.0f, -1.0f, 1.0f, 1.0f);
glm::vec3 screenCoords = glm::project(_position, ctx.view, ctx.projection, viewport);
float distanceToCamera = glm::distance2(_position, ctx.cameraPosition);
bool visible = distanceToCamera < _drawDistance && screenCoords.z < 1.0f;
float alpha = 1.0f;
if (_drawDistance != _fadeDistance && distanceToCamera > _fadeDistance) {
alpha = 1.0f - (distanceToCamera - _fadeDistance) / (_drawDistance - _fadeDistance);
}
if (visible) {
_model->show();
} else {
_model->hide();
}
_model->setAlpha(alpha);
_model->update(ctx.deltaTime);
}
void SpatialObject::initGL() {
if (!_model) return;
_model->initGL();
}
void SpatialObject::animate(const string &anim, int flags, float speed) {
if (!_model) return;
_model->animate(anim, flags, speed);
}
void SpatialObject::animate(const string &parent, const string &anim, int flags, float speed) {
if (!_model) return;
_model->animate(parent, anim, flags, speed);
}
const glm::vec3 &SpatialObject::position() const {
return _position;
}
float SpatialObject::heading() const {
return _heading;
}
const glm::mat4 &SpatialObject::transform() const {
return _transform;
}
void SpatialObject::setPosition(const glm::vec3 &position) {
_position = position;
updateTransform();
}
void SpatialObject::updateTransform() {
_transform = glm::translate(glm::mat4(1.0f), _position);
_transform *= glm::eulerAngleZ(_heading);
}
void SpatialObject::setHeading(float heading) {
_heading = heading;
updateTransform();
}
shared_ptr<ModelInstance> SpatialObject::model() const {
return _model;
}
shared_ptr<Walkmesh> SpatialObject::walkmesh() const {
return _walkmesh;
}
} // namespace game
} // namespace reone

74
src/game/object/spatial.h Normal file
View file

@ -0,0 +1,74 @@
/*
* Copyright © 2020 Vsevolod Kremianskii
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "object.h"
#include "glm/mat4x4.hpp"
#include "glm/vec3.hpp"
#include "../../render/modelinstance.h"
#include "../../render/walkmesh.h"
namespace reone {
namespace game {
static const float kDefaultDrawDistance = 1024.0f;
static const float kDefaultFadeDistance = 256.0f;
class SpatialObject : public Object {
public:
void update(const UpdateContext &ctx) override;
float distanceTo(const glm::vec3 &point) const;
bool contains(const glm::vec3 &point) const;
void face(const SpatialObject &other);
virtual void initGL();
virtual void animate(const std::string &anim, int flags = 0, float speed = 1.0f);
virtual void animate(const std::string &parent, const std::string &anim, int flags = 0, float speed = 1.0f);
const glm::vec3 &position() const;
float heading() const;
const glm::mat4 &transform() const;
void setPosition(const glm::vec3 &position);
void setHeading(float heading);
std::shared_ptr<render::ModelInstance> model() const;
std::shared_ptr<render::Walkmesh> walkmesh() const;
protected:
glm::vec3 _position { 0.0f };
float _heading { 0.0f };
glm::mat4 _transform { 1.0f };
std::shared_ptr<render::ModelInstance> _model;
std::shared_ptr<render::Walkmesh> _walkmesh;
float _drawDistance { kDefaultDrawDistance };
float _fadeDistance { kDefaultFadeDistance };
SpatialObject(uint32_t id);
virtual void updateTransform();
};
} // namespace game
} // namespace reone

View file

@ -29,7 +29,7 @@ namespace reone {
namespace game {
Trigger::Trigger(uint32_t id) : Object(id) {
Trigger::Trigger(uint32_t id) : SpatialObject(id) {
_type = ObjectType::Trigger;
}

View file

@ -17,13 +17,15 @@
#pragma once
#include "object.h"
#include "spatial.h"
#include "../../resources/gfffile.h"
namespace reone {
namespace game {
class Trigger : public Object {
class Trigger : public SpatialObject {
public:
Trigger(uint32_t id);

View file

@ -19,13 +19,15 @@
#include <boost/algorithm/string.hpp>
#include "glm/glm.hpp"
using namespace reone::resources;
namespace reone {
namespace game {
Waypoint::Waypoint(uint32_t id) : Object(id) {
Waypoint::Waypoint(uint32_t id) : SpatialObject(id) {
_type = ObjectType::Waypoint;
}

View file

@ -17,13 +17,15 @@
#pragma once
#include "object.h"
#include "spatial.h"
#include "../../resources/gfffile.h"
namespace reone {
namespace game {
class Waypoint : public Object {
class Waypoint : public SpatialObject {
public:
Waypoint(uint32_t id);

View file

@ -28,19 +28,23 @@ namespace reone {
namespace game {
class Area;
class Module;
class IRoutineCallbacks {
public:
virtual ~IRoutineCallbacks() {
}
virtual ~IRoutineCallbacks() = default;
// Commands
virtual void delayCommand(uint32_t timestamp, const script::ExecutionContext &ctx) = 0;
// Events
virtual int eventUserDefined(int eventNumber) = 0;
virtual void signalEvent(int eventId) = 0;
virtual void signalEvent(uint32_t objectId, int eventId) = 0;
// Objects
virtual Module *getModule() = 0;
virtual Area *getArea() = 0;
virtual std::shared_ptr<Object> getObjectById(uint32_t id) = 0;
virtual std::shared_ptr<Object> getObjectByTag(const std::string &tag, int nth = 0) = 0;
virtual std::shared_ptr<Object> getWaypointByTag(const std::string &tag) = 0;

View file

@ -84,8 +84,6 @@ shared_ptr<Object> RoutineManager::getObjectById(uint32_t id, const ExecutionCon
finalId = ctx.callerId;
break;
case kObjectInvalid:
case kObjectModule:
case kObjectArea:
warn("Routine: invalid object id: " + to_string(id));
return nullptr;
default:

View file

@ -22,6 +22,7 @@
#include "../../core/log.h"
#include "../../core/random.h"
#include "../area.h"
#include "../object/creature.h"
#include "../object/door.h"
@ -133,7 +134,7 @@ Variable RoutineManager::getArea(const vector<Variable> &args, ExecutionContext
assert(!args.empty() && args[0].type == VariableType::Object);
Variable result(VariableType::Object);
result.objectId = kObjectArea;
result.objectId = _callbacks->getArea()->id();
return move(result);
}
@ -254,13 +255,11 @@ Variable RoutineManager::signalEvent(const vector<Variable> &args, ExecutionCont
args[0].type == VariableType::Object &&
args[1].type == VariableType::Event);
if (args[0].objectId == kObjectArea ||
(args[0].objectId == kObjectSelf && ctx.callerId == kObjectArea)) {
_callbacks->signalEvent(args[1].engineTypeId);
shared_ptr<Object> subject(getObjectById(args[0].objectId, ctx));
if (subject) {
_callbacks->signalEvent(subject->id(), args[1].engineTypeId);
} else {
warn("Routine: invalid callee: " + to_string(args[0].objectId));
warn("Routine: object not found by id: " + to_string(args[0].objectId));
}
return Variable();

View file

@ -56,6 +56,8 @@ enum class ClassType {
enum class ObjectType {
None,
Module,
Area,
Creature,
Door,
Placeable,

View file

@ -28,8 +28,6 @@ namespace script {
const uint32_t kObjectSelf = 0;
const uint32_t kObjectInvalid = 1;
const uint32_t kObjectModule = 2;
const uint32_t kObjectArea = 3;
struct Variable;
class ScriptProgram;