1
0
forked from 0ad/0ad

Added unit IDs, so Atlas can store references to units without just using pointers and without crashing.

This was SVN commit r3214.
This commit is contained in:
Ykkrosh 2005-12-09 03:15:00 +00:00
parent bd2c1d73a3
commit 2eac4af3a4
9 changed files with 116 additions and 79 deletions

View File

@ -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++;

View File

@ -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

View File

@ -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;i<m_Units.size();i++) {
CUnit* unit=m_Units[i];
float tmin,tmax;
for (uint i=0; i<m_Units.size(); i++) {
CUnit* unit = m_Units[i];
float tmin, tmax;
if (unit->GetModel()->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;
}

View File

@ -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<CUnit*> m_Units;
// next ID number to be assigned to a unit created in the editor
int m_NextID;
};
#endif

View File

@ -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"));

View File

@ -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())

View File

@ -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<std::string, msgHandler> msgHandlers;
extern msgHandlers& GetMsgHandlers();

View File

@ -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<CUnit*>(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<void*>(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<CUnit*>(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<CUnit*>(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<CUnit*>(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<CUnit*>(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<CUnit*>(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<CUnit*>(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<CUnit*>(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)

View File

@ -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))