refactor: Extract object management from Area into ObjectContainer

Also optimize searching objects by tag and id.
This commit is contained in:
Vsevolod Kremianskii 2020-08-27 07:57:52 +07:00
parent 06c9ec0513
commit f26ed703fc
10 changed files with 160 additions and 116 deletions

View file

@ -79,6 +79,7 @@ set(HEADERS
src/game/object/spatial.h
src/game/object/trigger.h
src/game/object/waypoint.h
src/game/objectcontainer.h
src/game/room.h
src/game/script/callbacks.h
src/game/script/routines.h
@ -197,6 +198,7 @@ set(SOURCES
src/game/object/spatial.cpp
src/game/object/trigger.cpp
src/game/object/waypoint.cpp
src/game/objectcontainer.cpp
src/game/room.cpp
src/game/script/routines.cpp
src/game/script/routines_common.cpp

View file

@ -69,12 +69,6 @@ Area::Area(uint32_t id, GameVersion version, ObjectFactory *factory) :
_navMesh(new NavMesh()) {
assert(_objectFactory);
_objects.insert(make_pair(ObjectType::Creature, ObjectList()));
_objects.insert(make_pair(ObjectType::Door, ObjectList()));
_objects.insert(make_pair(ObjectType::Placeable, ObjectList()));
_objects.insert(make_pair(ObjectType::Waypoint, ObjectList()));
_objects.insert(make_pair(ObjectType::Trigger, ObjectList()));
}
void Area::load(const string &name, const GffStruct &are, const GffStruct &git) {
@ -91,13 +85,13 @@ void Area::load(const string &name, const GffStruct &are, const GffStruct &git)
creature->load(gffs);
landObject(*creature);
creature->setSynchronize(true);
_objects[ObjectType::Creature].push_back(move(creature));
add(creature);
}
for (auto &gffs : git.getList("Door List")) {
shared_ptr<Door> door(_objectFactory->newDoor());
door->load(gffs);
door->setSynchronize(true);
_objects[ObjectType::Door].push_back(move(door));
add(door);
}
for (auto &gffs : git.getList("Placeable List")) {
shared_ptr<Placeable> placeable(_objectFactory->newPlaceable());
@ -105,17 +99,17 @@ void Area::load(const string &name, const GffStruct &are, const GffStruct &git)
if (placeable->walkmesh()) {
_navMesh->add(placeable->walkmesh(), placeable->transform());
}
_objects[ObjectType::Placeable].push_back(move(placeable));
add(placeable);
}
for (auto &gffs : git.getList("WaypointList")) {
shared_ptr<Waypoint> waypoint(_objectFactory->newWaypoint());
waypoint->load(gffs);
_objects[ObjectType::Waypoint].push_back(move(waypoint));
add(waypoint);
}
for (auto &gffs : git.getList("TriggerList")) {
shared_ptr<Trigger> trigger(_objectFactory->newTrigger());
trigger->load(gffs);
_objects[ObjectType::Trigger].push_back(move(trigger));
add(trigger);
}
TheJobExecutor.enqueue([this](const atomic_bool &cancel) {
@ -126,7 +120,7 @@ void Area::load(const string &name, const GffStruct &are, const GffStruct &git)
}
void Area::loadProperties(const GffStruct &gffs) {
ResourceManager &resources = ResourceManager::instance();
ResourceManager &resources = ResMan;
shared_ptr<TwoDaTable> musicTable(resources.find2DA("ambientmusic"));
int musicIdx = gffs.getInt("MusicDay");
@ -136,7 +130,7 @@ void Area::loadProperties(const GffStruct &gffs) {
}
void Area::loadLayout() {
ResourceManager &resources = ResourceManager::instance();
ResourceManager &resources = ResMan;
LytFile lyt;
lyt.load(wrap(resources.find(_name, ResourceType::AreaLayout)));
@ -156,19 +150,15 @@ void Area::loadLayout() {
}
void Area::loadVisibility() {
ResourceManager &resources = ResourceManager::instance();
VisFile vis;
vis.load(wrap(resources.find(_name, ResourceType::Vis)));
vis.load(wrap(ResMan.find(_name, ResourceType::Vis)));
_visibility = make_unique<Visibility>(vis.visibility());
}
void Area::loadCameraStyle(const GffStruct &are) {
int styleIdx = are.getInt("CameraStyle");
ResourceManager &resources = ResourceManager::instance();
shared_ptr<TwoDaTable> styleTable(resources.find2DA("camerastyle"));
shared_ptr<TwoDaTable> styleTable(ResMan.find2DA("camerastyle"));
_cameraStyle.distance = styleTable->getFloat(styleIdx, "distance", 0.0f);
_cameraStyle.pitch = styleTable->getFloat(styleIdx, "pitch", 0.0f);
@ -193,9 +183,9 @@ void Area::landObject(SpatialObject &object) {
void Area::loadParty(const PartyConfiguration &party, const glm::vec3 &position, float heading) {
if (party.memberCount > 0) {
shared_ptr<Creature> partyLeader(makeCharacter(party.leader, kPartyLeaderTag, position, heading));
_objects[ObjectType::Creature].push_back(partyLeader);
landObject(*partyLeader);
partyLeader->setSynchronize(true);
add(partyLeader);
_player = partyLeader;
_partyLeader = partyLeader;
}
@ -203,7 +193,7 @@ void Area::loadParty(const PartyConfiguration &party, const glm::vec3 &position,
shared_ptr<Creature> partyMember(makeCharacter(party.member1, kPartyMember1Tag, position, heading));
landObject(*partyMember);
partyMember->setSynchronize(true);
_objects[ObjectType::Creature].push_back(partyMember);
add(partyMember);
_partyMember1 = partyMember;
Creature::Action action(Creature::ActionType::Follow, _partyLeader, kPartyMemberFollowDistance);
@ -213,7 +203,7 @@ void Area::loadParty(const PartyConfiguration &party, const glm::vec3 &position,
shared_ptr<Creature> partyMember(makeCharacter(party.member2, kPartyMember2Tag, position, heading));
landObject(*partyMember);
partyMember->setSynchronize(true);
_objects[ObjectType::Creature].push_back(partyMember);
add(partyMember);
_partyMember2 = partyMember;
Creature::Action action(Creature::ActionType::Follow, _partyLeader, kPartyMemberFollowDistance);
@ -238,7 +228,7 @@ bool Area::handle(const SDL_Event &event) {
void Area::update(const UpdateContext &updateCtx, GuiContext &guiCtx) {
updateDelayedCommands();
for (auto &creature : _objects[ObjectType::Creature]) {
for (auto &creature : _objectsByType[ObjectType::Creature]) {
updateCreature(static_cast<Creature &>(*creature), updateCtx.deltaTime);
}
if (_partyLeader) {
@ -256,29 +246,25 @@ void Area::update(const UpdateContext &updateCtx, GuiContext &guiCtx) {
model->update(updateCtx.deltaTime);
}
}
for (auto &list : _objects) {
for (auto &object : list.second) {
object->update(updateCtx);
}
for (auto &object : _objects) {
object->update(updateCtx);
}
updateSceneGraph(updateCtx.cameraPosition);
if (_debugMode != DebugMode::None) {
if (_debugMode == DebugMode::GameObjects) {
guiCtx.debug.objects.clear();
glm::vec4 viewport(0.0f, 0.0f, 1.0f, 1.0f);
for (auto &list : _objects) {
for (auto &object : list.second) {
glm::vec3 position(object->position());
if (glm::distance2(position, updateCtx.cameraPosition) > kDrawDebugDistance) continue;
for (auto &object : _objects) {
glm::vec3 position(object->position());
if (glm::distance2(position, updateCtx.cameraPosition) > kDrawDebugDistance) continue;
DebugObject debugObj;
debugObj.tag = object->tag();
debugObj.text = object->tag();
debugObj.screenCoords = glm::project(position, updateCtx.view, updateCtx.projection, viewport);
DebugObject debugObj;
debugObj.tag = object->tag();
debugObj.text = object->tag();
debugObj.screenCoords = glm::project(position, updateCtx.view, updateCtx.projection, viewport);
guiCtx.debug.objects.push_back(move(debugObj));
}
guiCtx.debug.objects.push_back(move(debugObj));
}
}
}
@ -339,7 +325,7 @@ void Area::updateTriggers(const Creature &creature) {
glm::vec2 intersection;
float distance;
for (auto &object : _objects[ObjectType::Trigger]) {
for (auto &object : _objectsByType[ObjectType::Trigger]) {
Trigger &trigger = static_cast<Trigger &>(*object);
if (trigger.distanceTo(liftedPosition) > kMaxDistanceToTestCollision) continue;
@ -400,7 +386,7 @@ void Area::setDebugMode(DebugMode mode) {
void Area::saveTo(GameState &state) const {
AreaState areaState;
for (auto &list : _objects) {
for (auto &list : _objectsByType) {
if (list.first != ObjectType::Creature && list.first != ObjectType::Door) continue;
for (auto &object : list.second) {
@ -417,7 +403,7 @@ void Area::loadState(const GameState &state) {
const AreaState &areaState = it->second;
for (auto &list : _objects) {
for (auto &list : _objectsByType) {
if (list.first != ObjectType::Creature && list.first != ObjectType::Door) continue;
for (auto &object : list.second) {
@ -426,52 +412,10 @@ void Area::loadState(const GameState &state) {
}
}
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;
}
return nullptr;
}
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;
}
return nullptr;
}
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<SpatialObject> &object) { return object->id() == id; });
return it != list.end() ? *it : nullptr;
}
shared_ptr<SpatialObject> Area::find(const string &tag, ObjectType type, int nth) const {
const ObjectList &list = _objects.find(type)->second;
int idx = 0;
for (auto &object : list) {
if (object->tag() == tag && idx++ == nth) return object;
}
return nullptr;
}
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) {
for (auto &list : _objectsByType) {
if (list.first != ObjectType::Door && list.first != ObjectType::Placeable) continue;
if (list.first == ObjectType::Door && (mask & kObstacleDoor) == 0) continue;
if (list.first == ObjectType::Placeable && (mask & kObstaclePlaceable) == 0) continue;
@ -526,7 +470,7 @@ bool Area::findObstacleByWalkmesh(const glm::vec3 &from, const glm::vec3 &to, in
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) {
for (auto &list : _objectsByType) {
if (list.first != ObjectType::Creature &&
list.first != ObjectType::Door &&
list.first != ObjectType::Placeable) continue;

View file

@ -18,7 +18,6 @@
#pragma once
#include <list>
#include <map>
#include "../gui/types.h"
#include "../net/types.h"
@ -34,6 +33,7 @@
#include "object/placeable.h"
#include "object/trigger.h"
#include "object/waypoint.h"
#include "objectcontainer.h"
#include "room.h"
#include "types.h"
@ -45,7 +45,7 @@ typedef std::vector<std::shared_ptr<SpatialObject>> ObjectList;
class ObjectFactory;
class Area : public Object {
class Area : public Object, public ObjectContainer {
public:
Area(uint32_t id, resources::GameVersion version, ObjectFactory *objectFactory);
@ -71,10 +71,6 @@ public:
void loadState(const GameState &state);
// Object search
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;
@ -98,7 +94,6 @@ public:
protected:
ObjectFactory *_objectFactory { nullptr };
std::map<ObjectType, ObjectList> _objects;
bool _scriptsEnabled { true };
std::function<void()> _onPlayerChanged;

View file

@ -35,10 +35,8 @@ void Area::initGL() {
shared_ptr<ModelInstance> model(room->model());
if (model) model->initGL();
}
for (auto &pair : _objects) {
for (auto &object : pair.second) {
object->initGL();
}
for (auto &object : _objects) {
object->initGL();
}
}
@ -52,17 +50,15 @@ void Area::updateSceneGraph(const glm::vec3 &cameraPosition) {
glm::mat4 transform(glm::translate(glm::mat4(1.0f), room->position()));
model->fill(_sceneGraph, transform, _debugMode == DebugMode::ModelNodes);
}
for (auto &list : _objects) {
for (auto &object : list.second) {
shared_ptr<ModelInstance> model(object->model());
if (!model) continue;
for (auto &object : _objects) {
shared_ptr<ModelInstance> model(object->model());
if (!model) continue;
model->fill(_sceneGraph, object->transform(), _debugMode == DebugMode::ModelNodes);
}
model->fill(_sceneGraph, object->transform(), _debugMode == DebugMode::ModelNodes);
}
switch (_debugMode) {
case DebugMode::GameObjects:
for (auto &list : _objects) {
for (auto &list : _objectsByType) {
ObjectType type = list.first;
if (type != ObjectType::Creature && type != ObjectType::Placeable && type != ObjectType::Door) continue;

View file

@ -238,7 +238,7 @@ void Game::loadDialogGui() {
dialog->initGL();
dialog->setPickReplyEnabled(_pickDialogReplyEnabled);
dialog->setGetObjectIdByTagFunc([this](const string &tag) {
shared_ptr<Object> object(_module->area().find(tag, ObjectType::Creature));
shared_ptr<Object> object(_module->area().find(tag));
return object ? object->id() : 0;
});
dialog->setOnReplyPicked(bind(&Game::onDialogReplyPicked, this, _1));
@ -263,8 +263,8 @@ void Game::onDialogReplyPicked(uint32_t index) {
void Game::onDialogSpeakerChanged(uint32_t from, uint32_t to) {
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;
shared_ptr<SpatialObject> prevSpeaker = from != 0 ? _module->area().find(from) : nullptr;
shared_ptr<SpatialObject> speaker = to != 0 ? _module->area().find(to) : nullptr;
if (speaker == partyLeader) return;
debug(boost::format("Game: dialog speaker: \"%s\"") % (speaker ? speaker->tag() : ""));
@ -510,7 +510,7 @@ shared_ptr<Object> Game::getObjectByTag(const string &tag, int nth) {
}
shared_ptr<Object> Game::getWaypointByTag(const string &tag) {
return _module->area().find(tag, ObjectType::Waypoint);
return _module->area().find(tag);
}
shared_ptr<Object> Game::getPlayer() {

View file

@ -157,7 +157,7 @@ void Module::getEntryPoint(const string &waypoint, glm::vec3 &position, float &h
heading = _info.entryHeading;
if (!waypoint.empty()) {
shared_ptr<SpatialObject> object(_area->find(waypoint, ObjectType::Waypoint));
shared_ptr<SpatialObject> object(_area->find(waypoint));
if (object) {
position = object->position();
heading = object->heading();

View file

@ -105,7 +105,7 @@ void MultiplayerArea::executeLoadCreature(const Command &cmd) {
}
landObject(*creature);
_objects[ObjectType::Creature].push_back(move(creature));
add(creature);
}
void MultiplayerArea::executeSetPlayerRole(const Command &cmd) {
@ -128,7 +128,7 @@ void MultiplayerArea::executeSetPlayerRole(const Command &cmd) {
}
void MultiplayerArea::executeSetObjectTransform(const Command &cmd) {
shared_ptr<SpatialObject> object(find(cmd.objectId(), ObjectType::Creature));
shared_ptr<SpatialObject> object(find(cmd.objectId()));
if (object) {
object->setSynchronize(false);
object->setPosition(cmd.position());
@ -138,7 +138,7 @@ void MultiplayerArea::executeSetObjectTransform(const Command &cmd) {
}
void MultiplayerArea::executeSetObjectAnimation(const Command &cmd) {
shared_ptr<SpatialObject> object(find(cmd.objectId(), ObjectType::Creature));
shared_ptr<SpatialObject> object(find(cmd.objectId()));
if (object) {
object->setSynchronize(false);
object->animate(cmd.animation(), cmd.animationFlags());
@ -147,7 +147,7 @@ void MultiplayerArea::executeSetObjectAnimation(const Command &cmd) {
}
void MultiplayerArea::executeSetCreatureMovementType(const Command &cmd) {
shared_ptr<SpatialObject> creature(find(cmd.objectId(), ObjectType::Creature));
shared_ptr<SpatialObject> creature(find(cmd.objectId()));
if (creature) {
creature->setSynchronize(false);
static_cast<Creature &>(*creature).setMovementType(cmd.movementType());
@ -156,7 +156,7 @@ void MultiplayerArea::executeSetCreatureMovementType(const Command &cmd) {
}
void MultiplayerArea::executeSetCreatureTalking(const Command &cmd) {
shared_ptr<SpatialObject> creature(find(cmd.objectId(), ObjectType::Creature));
shared_ptr<SpatialObject> creature(find(cmd.objectId()));
if (creature) {
creature->setSynchronize(false);
static_cast<Creature &>(*creature).setTalking(cmd.talking());
@ -165,7 +165,7 @@ void MultiplayerArea::executeSetCreatureTalking(const Command &cmd) {
}
void MultiplayerArea::executeSetDoorOpen(const Command &cmd) {
shared_ptr<Object> door(find(cmd.objectId(), ObjectType::Door));
shared_ptr<Object> door(find(cmd.objectId()));
shared_ptr<Object> trigerrer(find(cmd.triggerrer()));
if (door) {
door->setSynchronize(false);
@ -175,7 +175,7 @@ void MultiplayerArea::executeSetDoorOpen(const Command &cmd) {
}
const shared_ptr<Object> MultiplayerArea::findCreatureByClientTag(const string &clientTag) const {
auto creatures = _objects.find(ObjectType::Creature)->second;
auto creatures = _objectsByType.find(ObjectType::Creature)->second;
auto it = find_if(
creatures.begin(),
creatures.end(),

View file

@ -18,8 +18,8 @@
#pragma once
#include "../area.h"
#include "../multiplayer/command.h"
#include "command.h"
#include "creature.h"
namespace reone {

View file

@ -0,0 +1,57 @@
/*
* 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 "objectcontainer.h"
using namespace std;
namespace reone {
namespace game {
void ObjectContainer::clear() {
_objects.clear();
_objectsByType.clear();
_objectById.clear();
_objectsByTag.clear();
}
void ObjectContainer::add(const shared_ptr<SpatialObject> &object) {
_objects.push_back(object);
_objectsByType[object->type()].push_back(object);
_objectById.insert(make_pair(object->id(), object));
_objectsByTag[object->tag()].push_back(object);
}
shared_ptr<SpatialObject> ObjectContainer::find(uint32_t id) const {
auto object = _objectById.find(id);
if (object == _objectById.end()) return nullptr;
return object->second;
}
shared_ptr<SpatialObject> ObjectContainer::find(const string &tag, int nth) const {
auto objects = _objectsByTag.find(tag);
if (objects == _objectsByTag.end()) return nullptr;
if (nth >= objects->second.size()) return nullptr;
return objects->second[nth];
}
} // namespace game
} // namespace reone

View file

@ -0,0 +1,50 @@
/*
* 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 <map>
#include <memory>
#include "object/spatial.h"
#include "types.h"
namespace reone {
namespace game {
typedef std::vector<std::shared_ptr<SpatialObject>> ObjectList;
class ObjectContainer {
public:
std::shared_ptr<SpatialObject> find(uint32_t id) const;
std::shared_ptr<SpatialObject> find(const std::string &tag, int nth = 0) const;
protected:
ObjectList _objects;
std::map<ObjectType, ObjectList> _objectsByType;
std::map<uint32_t, std::shared_ptr<SpatialObject>> _objectById;
std::map<std::string, ObjectList> _objectsByTag;
void clear();
void add(const std::shared_ptr<SpatialObject> &object);
};
} // namespace game
} // namespace reone