diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index 9188100cc1..51bdf344bb 100755 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -50,6 +50,7 @@ void CMapReader::LoadMap(const char* filename, CTerrain *pTerrain_, CUnitManager g_EntityManager.deleteAll(); // delete all remaining non-entity units pUnitMan->DeleteAll(); + g_UnitMan.SetNextID(0); // unpack the data RegMemFun(this, &CMapReader::UnpackMap, L"CMapReader::UnpackMap", 1250); @@ -437,7 +438,13 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time) if (! ent) LOG(ERROR, LOG_CATEGORY, "Failed to create entity of type '%ls'", TemplateName.c_str()); else + { ent->SetPlayer(g_Game->GetPlayer(PlayerID)); + + // TODO: save object IDs in the map file, and load them again, + // so that triggers have a persistent identifier for objects + ent->m_actor->SetID(g_UnitMan.GetNewID()); + } } completed_jobs++; @@ -492,7 +499,7 @@ int CXMLReader::ReadNonEntities(XMBElement parent, double end_time) CUnit* unit = g_UnitMan.CreateUnit(ActorName, NULL); - if (unit && unit->GetModel()) + if (unit) { // Copied from CEntity::updateActorTransforms(): float s = sin(Orientation); @@ -503,6 +510,10 @@ int CXMLReader::ReadNonEntities(XMBElement parent, double end_time) m._31 = s; m._32 = 0.0f; m._33 = -c; m._34 = Position.Z; m._41 = 0.0f; m._42 = 0.0f; m._43 = 0.0f; m._44 = 1.0f; unit->GetModel()->SetTransform(m); + + // TODO: save object IDs in the map file, and load them again, + // so that triggers have a persistent identifier for objects + unit->SetID(g_UnitMan.GetNewID()); } completed_jobs++; diff --git a/source/graphics/Unit.h b/source/graphics/Unit.h index 53307ba11f..b869b1ac6a 100755 --- a/source/graphics/Unit.h +++ b/source/graphics/Unit.h @@ -15,10 +15,14 @@ class CUnit { public: // constructor - unit invalid without a model and object - CUnit(CObjectEntry* object, CModel* model) : m_Object(object), m_Model(model), m_Entity(NULL) { + CUnit(CObjectEntry* object, CModel* model) + : m_Object(object), m_Model(model), m_Entity(NULL), m_ID(-1) + { debug_assert(object && model); } - CUnit(CObjectEntry* object, CModel* model, CEntity* entity) : m_Object(object), m_Model(model), m_Entity(entity) { + CUnit(CObjectEntry* object, CModel* model, CEntity* entity) + : m_Object(object), m_Model(model), m_Entity(entity), m_ID(-1) + { debug_assert(object && model); } @@ -26,11 +30,11 @@ public: ~CUnit(); - // get unit's template object + // get unit's template object; never NULL CObjectEntry* GetObject() { return m_Object; } - // get unit's model data + // get unit's model data; never NULL CModel* GetModel() { return m_Model; } - // get actor's entity + // get actor's entity; can be NULL CEntity* GetEntity() { return m_Entity; } // Put here as it conveniently references both the model and the ObjectEntry @@ -49,6 +53,9 @@ public: // matchin 'name'. bool IsPlayingAnimation(const CStr8& name); + int GetID() const { return m_ID; } + void SetID(int id) { m_ID = id; } + private: // object from which unit was created CObjectEntry* m_Object; @@ -56,6 +63,9 @@ private: CModel* m_Model; // the entity that this actor represents, if any CEntity* m_Entity; + // unique (per CGame) ID number for units created in the editor, as a + // permanent way of referencing them. -1 for non-editor units. + int m_ID; }; #endif diff --git a/source/graphics/UnitManager.cpp b/source/graphics/UnitManager.cpp index 07f375fc33..c32e678a29 100755 --- a/source/graphics/UnitManager.cpp +++ b/source/graphics/UnitManager.cpp @@ -24,6 +24,7 @@ extern CConsole* g_Console; /////////////////////////////////////////////////////////////////////////////// // CUnitManager constructor CUnitManager::CUnitManager() +: m_NextID(0) { } @@ -77,36 +78,36 @@ void CUnitManager::DeleteAll() /////////////////////////////////////////////////////////////////////////////// // PickUnit: iterate through units testing given ray against bounds of each // unit; return the closest unit, or null if everything missed -CUnit* CUnitManager::PickUnit(const CVector3D& origin,const CVector3D& dir) const +CUnit* CUnitManager::PickUnit(const CVector3D& origin, const CVector3D& dir) const { CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager(); // closest object found so far CUnit* hit = 0; // distance to closest object found so far - float dist = 1.0e30f; + float dist = FLT_MAX; // closest approach offset (easier to pick small stuff in forests than standard ScEd style selection) - float minrel = 1.0e30f; + float minrel = FLT_MAX; - for (uint i=0;iGetModel()->GetBounds().RayIntersect(origin,dir,tmin,tmax) + if (unit->GetModel()->GetBounds().RayIntersect(origin, dir, tmin, tmax) && losMgr->GetUnitStatus(unit, g_Game->GetLocalPlayer()) != UNIT_HIDDEN) { // Point of closest approach CVector3D obj; - unit->GetModel()->GetBounds().GetCentre( obj ); + unit->GetModel()->GetBounds().GetCentre(obj); CVector3D delta = obj - origin; - float distance = delta.Dot( dir ); + float distance = delta.Dot(dir); CVector3D closest = origin + dir * distance; CVector3D offset = obj - closest; float rel = offset.GetLength(); - if (!hit || rel < minrel ) { - hit=unit; - dist=tmin; + if (rel < minrel) { + hit = unit; + dist = tmin; minrel = rel; } } @@ -127,3 +128,17 @@ CUnit* CUnitManager::CreateUnit(const CStr& actorName, CEntity* entity) AddUnit(unit); return unit; } + +/////////////////////////////////////////////////////////////////////////////// +// FindByID +CUnit* CUnitManager::FindByID(int id) const +{ + if (id < 0) + return NULL; + + for (size_t i = 0; i < m_Units.size(); ++i) + if (m_Units[i]->GetID() == id) + return m_Units[i]; + + return NULL; +} diff --git a/source/graphics/UnitManager.h b/source/graphics/UnitManager.h index 3b3b339c75..910c3e6d14 100755 --- a/source/graphics/UnitManager.h +++ b/source/graphics/UnitManager.h @@ -47,11 +47,19 @@ public: // iterate through units testing given ray against bounds of each unit; // return the closest unit, or null if everything missed - CUnit* PickUnit(const CVector3D& origin,const CVector3D& dir) const; + CUnit* PickUnit(const CVector3D& origin, const CVector3D& dir) const; + + CUnit* FindByID(int id) const; + + int GetNewID() { return m_NextID++; } + + void SetNextID(int n) { m_NextID = n; } private: // list of all known units std::vector m_Units; + // next ID number to be assigned to a unit created in the editor + int m_NextID; }; #endif diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp index 02a19c0bed..ce64c8c506 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp @@ -391,6 +391,7 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent) // Toolbar: ToolButtonBar* toolbar = new ToolButtonBar(this, ID_Toolbar); + // TODO: configurable small vs large icon images // (button label; tooltip text; image; internal tool name) toolbar->AddToolButton(_("Default"), _("Default"), _T("default.png"), _T("")); toolbar->AddToolButton(_("Move"), _("Move/rotate object"), _T("moveobject.png"), _T("TransformObject")); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp index e8baec4125..895b2c0a46 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp @@ -39,12 +39,15 @@ public: AtlasMessage::qSelectObject qry(Position(evt.GetPosition())); qry.Post(); obj->m_Selection.clear(); - obj->m_Selection.push_back(qry.id); - obj->m_dx = qry.offsetx; - obj->m_dy = qry.offsety; + if (AtlasMessage::ObjectIDIsValid(qry.id)) + { + obj->m_Selection.push_back(qry.id); + obj->m_dx = qry.offsetx; + obj->m_dy = qry.offsety; + SET_STATE(Dragging); + } POST_MESSAGE(SetSelectionPreview(obj->m_Selection)); ScenarioEditor::GetCommandProc().FinaliseLastCommand(); - SET_STATE(Dragging); return true; } else if (evt.Dragging() && evt.RightIsDown() || evt.RightDown()) diff --git a/source/tools/atlas/GameInterface/Handlers/MessageHandler.h b/source/tools/atlas/GameInterface/Handlers/MessageHandler.h index 1118f5d00e..8d03199825 100644 --- a/source/tools/atlas/GameInterface/Handlers/MessageHandler.h +++ b/source/tools/atlas/GameInterface/Handlers/MessageHandler.h @@ -8,6 +8,9 @@ namespace AtlasMessage // (Random note: Be careful not to give handler .cpp files the same name // as any other file in the project, because it makes everything very confused) +// TODO: measure how long this static initialisation stuff takes, and if it's +// non-negligible then make it do as little work as possible if Atlas is not run + typedef void (*msgHandler)(IMessage*); typedef std::map msgHandlers; extern msgHandlers& GetMsgHandlers(); diff --git a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp index e565bf22bf..385ad5cc3f 100644 --- a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp @@ -39,11 +39,13 @@ void AtlasRenderSelection() glDisable(GL_DEPTH_TEST); for (size_t i = 0; i < g_Selection.size(); ++i) { - if (g_Selection[i]) + CUnit* unit = g_UnitMan.FindByID(g_Selection[i]); + if (unit) { - CUnit* unit = static_cast(g_Selection[i]); if (unit->GetEntity()) + { unit->GetEntity()->renderSelectionOutline(); + } else { const CBound& bound = unit->GetModel()->GetBounds(); @@ -118,7 +120,7 @@ MESSAGEHANDLER(EntityPreview) CBaseEntity* base = g_EntityTemplateCollection.getTemplate(msg->id); if (base) // (ignore errors) { - g_PreviewUnit = g_UnitMan.CreateUnit(base->m_actorName, 0); + g_PreviewUnit = g_UnitMan.CreateUnit(base->m_actorName, NULL); // TODO: set player (for colour) // TODO: variations } @@ -159,9 +161,9 @@ MESSAGEHANDLER(EntityPreview) BEGIN_COMMAND(CreateEntity) - HEntity m_Entity; CVector3D m_Pos; float m_Angle; + int m_ID; void Do() { @@ -179,6 +181,8 @@ BEGIN_COMMAND(CreateEntity) m_Angle = d->angle; } + m_ID = g_UnitMan.GetNewID(); + Redo(); } @@ -195,18 +199,19 @@ BEGIN_COMMAND(CreateEntity) LOG(ERROR, LOG_CATEGORY, "Failed to create entity of type '%ls'", d->id.c_str()); else { - // TODO: player ID + // TODO: proper player ID ent->SetPlayer(g_Game->GetLocalPlayer()); - m_Entity = ent; + ent->m_actor->SetID(m_ID); } } } void Undo() { - m_Entity->kill(); - m_Entity = HEntity(); + CUnit* unit = g_UnitMan.FindByID(m_ID); + if (unit && unit->GetEntity()) + unit->GetEntity()->kill(); } END_COMMAND(CreateEntity) @@ -222,7 +227,10 @@ QUERYHANDLER(SelectObject) CUnit* target = g_UnitMan.PickUnit(rayorigin, raydir); - msg->id = static_cast(target); + if (target) + msg->id = target->GetID(); + else + msg->id = -1; if (target) { @@ -246,10 +254,8 @@ BEGIN_COMMAND(MoveObject) void Do() { - if (! d->id) - return; - - CUnit* unit = static_cast(d->id); + CUnit* unit = g_UnitMan.FindByID(d->id); + if (! unit) return; if (unit->GetEntity()) { @@ -268,10 +274,8 @@ BEGIN_COMMAND(MoveObject) void SetPos(CVector3D& pos) { - if (! d->id) - return; - - CUnit* unit = static_cast(d->id); + CUnit* unit = g_UnitMan.FindByID(d->id); + if (! unit) return; if (unit->GetEntity()) { @@ -311,10 +315,8 @@ BEGIN_COMMAND(RotateObject) void Do() { - if (! d->id) - return; - - CUnit* unit = static_cast(d->id); + CUnit* unit = g_UnitMan.FindByID(d->id); + if (! unit) return; if (unit->GetEntity()) { @@ -365,10 +367,8 @@ BEGIN_COMMAND(RotateObject) void SetAngle(float angle, CMatrix3D& transform) { - if (! d->id) - return; - - CUnit* unit = static_cast(d->id); + CUnit* unit = g_UnitMan.FindByID(d->id); + if (! unit) return; if (unit->GetEntity()) { @@ -402,29 +402,21 @@ END_COMMAND(RotateObject) BEGIN_COMMAND(DeleteObject) - bool m_ObjectAlive; + CUnit* m_UnitInLimbo; void Construct() { - m_ObjectAlive = true; + m_UnitInLimbo = NULL; } void Destruct() { - if (! m_ObjectAlive) + if (m_UnitInLimbo) { - if (! d->id) - return; - - CUnit* unit = static_cast(d->id); - - if (unit->GetEntity()) - unit->GetEntity()->kill(); + if (m_UnitInLimbo->GetEntity()) + m_UnitInLimbo->GetEntity()->kill(); else - { - g_UnitMan.RemoveUnit(unit); - delete unit; - } + delete m_UnitInLimbo; } } @@ -435,33 +427,26 @@ BEGIN_COMMAND(DeleteObject) void Redo() { - if (! d->id) - return; - - CUnit* unit = static_cast(d->id); + CUnit* unit = g_UnitMan.FindByID(d->id); + if (! unit) return; if (unit->GetEntity()) + { // HACK: I don't know the proper way of undoably deleting entities... unit->GetEntity()->m_destroyed = true; + } g_UnitMan.RemoveUnit(unit); - - m_ObjectAlive = false; + m_UnitInLimbo = unit; } void Undo() { - if (! d->id) - return; + if (m_UnitInLimbo->GetEntity()) + m_UnitInLimbo->GetEntity()->m_destroyed = false; - CUnit* unit = static_cast(d->id); - - if (unit->GetEntity()) - unit->GetEntity()->m_destroyed = false; - - g_UnitMan.AddUnit(unit); - - m_ObjectAlive = true; + g_UnitMan.AddUnit(m_UnitInLimbo); + m_UnitInLimbo = NULL; } END_COMMAND(DeleteObject) diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index a51c72c807..21b23643d2 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -167,7 +167,8 @@ COMMAND(PaintTerrain, MERGE, ////////////////////////////////////////////////////////////////////////// -typedef void* ObjectID; +typedef int ObjectID; +inline bool ObjectIDIsValid(ObjectID id) { return (id >= 0); } QUERY(SelectObject, ((Position, pos))