Atlas: Fixed entities-don't-move-when-terrain-is-altered. Unbroke and fixed entity memory management. Added flatten tool. Added object movement/rotation/deletion tool - but not yet finished, since 'redo' usually crashes (for known reasons).

This was SVN commit r3207.
This commit is contained in:
Ykkrosh 2005-12-08 02:50:55 +00:00
parent ce3bd33d76
commit 5250814397
19 changed files with 709 additions and 52 deletions

View File

@ -154,7 +154,7 @@ void CEntity::kill()
m_bounds = NULL;
m_destroyed = true;
Shutdown();
//Shutdown(); // PT: tentatively removed - this seems to be called by ~CJSComplex, and we don't want to do it twice
if( m_actor )
{
@ -617,6 +617,11 @@ void CEntity::interpolate( float relativeoffset )
}
}
void CEntity::invalidateActor()
{
m_actor_transform_valid = false;
}
void CEntity::render()
{
if( !m_orderQueue.empty() )
@ -988,6 +993,7 @@ bool CEntity::Order( JSContext* cx, uintN argc, jsval* argv, bool Queued )
break;
case CEntityOrder::ORDER_ATTACK_MELEE:
case CEntityOrder::ORDER_GATHER:
case CEntityOrder::ORDER_HEAL:
if( argc < 1 )
{
JS_ReportError( cx, "Too few parameters" );

View File

@ -112,7 +112,7 @@ public:
float m_graphics_orientation;
// If the actor's current transform data is valid (i.e. the entity hasn't
// moved since it was last calculated).
// moved since it was last calculated, and the terrain hasn't been changed).
bool m_actor_transform_valid;
//-- Scripts
@ -167,6 +167,10 @@ public:
// Updates graphical information for a point between the last and current simulation frame; 0 < relativeoffset < 1.
void interpolate( float relativeoffset );
// Forces update of actor information during next call to 'interpolate'.
// (Necessary when terrain might move underneath the actor.)
void invalidateActor();
// Updates auras
void UpdateAuras( size_t timestep_millis );

View File

@ -36,6 +36,13 @@ CEntityManager::~CEntityManager()
m_entities[i].m_entity = 0;
m_entities[i].m_refcount = 0;
}
// Delete entities that were killed, but not yet reaped by a call to updateAll,
// to avoid memory leak warnings upon exiting
std::vector<CEntity*>::iterator it;
for( it = m_reaper.begin(); it < m_reaper.end(); it++ )
delete( *it );
m_reaper.clear();
}
void CEntityManager::deleteAll()
@ -45,6 +52,7 @@ void CEntityManager::deleteAll()
if( m_entities[i].m_refcount )
{
delete( m_entities[i].m_entity );
m_entities[i].m_entity = 0;
m_entities[i].m_refcount = 0;
}
m_nextalloc = 0;
@ -179,13 +187,17 @@ void CEntityManager::renderAll()
m_entities[i].m_entity->render();
}
void CEntityManager::invalidateAll()
{
for( int i = 0; i < MAX_HANDLES; i++ )
if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed )
m_entities[i].m_entity->invalidateActor();
}
void CEntityManager::destroy( u16 handle )
{
if (m_entities[handle].m_entity->me.m_handle != INVALID_HANDLE)
{
m_reaper.push_back( m_entities[handle].m_entity );
m_entities[handle].m_entity->me.m_handle = INVALID_HANDLE;
}
m_reaper.push_back( m_entities[handle].m_entity );
m_entities[handle].m_entity->me.m_handle = INVALID_HANDLE;
}
bool CEntityManager::m_extant = false;

View File

@ -53,6 +53,7 @@ public:
void InitializeAll();
void TickAll();
void renderAll();
void invalidateAll();
void deleteAll();

View File

@ -393,8 +393,9 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent)
ToolButtonBar* toolbar = new ToolButtonBar(this, ID_Toolbar);
// (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("MoveObject"));
toolbar->AddToolButton(_("Move"), _("Move/rotate object"), _T("moveobject.png"), _T("TransformObject"));
toolbar->AddToolButton(_("Elevation"), _("Alter terrain elevation"), _T("alterelevation.png"), _T("AlterElevation"));
toolbar->AddToolButton(_("Flatten"), _("Flatten terrain elevation"), _T("flattenelevation.png"), _T("FlattenElevation"));
toolbar->AddToolButton(_("Paint Terrain"), _("Paint terrain texture"), _T("paintterrain.png"), _T("PaintTerrain"));
toolbar->Realize();
SetToolBar(toolbar);

View File

@ -38,7 +38,7 @@ ObjectSidebar::ObjectSidebar(wxWindow* parent)
}
wxWindow* ObjectSidebar::GetBottomBar(wxWindow* parent)
wxWindow* ObjectSidebar::GetBottomBar(wxWindow* WXUNUSED(parent))
{
if (m_BottomBar)
return m_BottomBar;

View File

@ -23,6 +23,7 @@ TerrainSidebar::TerrainSidebar(wxWindow* parent)
{
wxSizer* sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Elevation tools"));
sizer->Add(new ToolButton(this, _("Modify"), _T("AlterElevation"), wxSize(50,20)));
sizer->Add(new ToolButton(this, _("Flatten"), _T("FlattenElevation"), wxSize(50,20)));
// sizer->Add(new ToolButton(this, _("Smooth"), _T(""), wxSize(50,20)));
// sizer->Add(new ToolButton(this, _("Sample"), _T(""), wxSize(50,20)));
sizer->Add(new ToolButton(this, _("Paint"), _T("PaintTerrain"), wxSize(50,20)));

View File

@ -29,6 +29,7 @@ void SetCurrentTool(const wxString& name, void* initData)
{
g_CurrentTool->Shutdown();
delete g_CurrentTool;
g_CurrentTool = &dummy;
}
SetActive(false, g_CurrentToolName);
@ -38,13 +39,13 @@ void SetCurrentTool(const wxString& name, void* initData)
{
tool = wxDynamicCast(wxCreateDynamicObject(name), ITool);
wxASSERT(tool);
tool->Init(initData);
}
if (tool == NULL)
g_CurrentTool = &dummy;
else
if (tool)
{
g_CurrentTool = tool;
tool->Init(initData);
}
g_CurrentToolName = name;
SetActive(true, g_CurrentToolName);

View File

@ -0,0 +1,98 @@
#include "stdafx.h"
#include "Common/Tools.h"
#include "Common/Brushes.h"
#include "GameInterface/Messages.h"
using AtlasMessage::Position;
class FlattenElevation : public StateDrivenTool<FlattenElevation>
{
DECLARE_DYNAMIC_CLASS(FlattenElevation);
Position m_Pos;
public:
FlattenElevation()
{
SetState(&Waiting);
}
void OnEnable()
{
g_Brush_Elevation.MakeActive();
}
void OnDisable()
{
POST_MESSAGE(BrushPreview(false, Position()));
}
struct sWaiting : public State
{
bool OnMouse(FlattenElevation* obj, wxMouseEvent& evt)
{
if (evt.LeftDown())
{
obj->m_Pos = Position(evt.GetPosition());
SET_STATE(Flattening);
return true;
}
else if (evt.Moving())
{
POST_MESSAGE(BrushPreview(true, Position(evt.GetPosition())));
return true;
}
else
{
return false;
}
}
}
Waiting;
struct sFlattening : public State
{
void OnEnter(FlattenElevation* obj)
{
POST_MESSAGE(BrushPreview(true, obj->m_Pos));
}
void OnLeave(FlattenElevation*)
{
ScenarioEditor::GetCommandProc().FinaliseLastCommand();
}
bool OnMouse(FlattenElevation* obj, wxMouseEvent& evt)
{
if (evt.LeftUp())
{
SET_STATE(Waiting);
return true;
}
else if (evt.Dragging())
{
wxPoint pos = evt.GetPosition();
obj->m_Pos = Position(pos);
POST_MESSAGE(BrushPreview(true, obj->m_Pos));
return true;
}
else
{
return false;
}
}
void OnTick(FlattenElevation* obj, float dt)
{
POST_COMMAND(FlattenElevation, (obj->m_Pos, dt*4096.f*g_Brush_Elevation.GetStrength()));
obj->m_Pos = Position::Unchanged();
}
}
Flattening;
};
IMPLEMENT_DYNAMIC_CLASS(FlattenElevation, StateDrivenTool<FlattenElevation>);

View File

@ -0,0 +1,97 @@
#include "stdafx.h"
#include "Common/Tools.h"
#include "Common/Brushes.h"
#include "Common/MiscState.h"
#include "GameInterface/Messages.h"
using AtlasMessage::Position;
class TransformObject : public StateDrivenTool<TransformObject>
{
DECLARE_DYNAMIC_CLASS(TransformObject);
std::vector<AtlasMessage::ObjectID> m_Selection;
public:
TransformObject()
{
SetState(&Waiting);
}
void OnDisable()
{
m_Selection.clear();
POST_MESSAGE(SetSelectionPreview(m_Selection));
}
// TODO: keys to rotate/move object?
struct sWaiting : public State
{
bool OnMouse(TransformObject* obj, wxMouseEvent& evt)
{
if (evt.LeftDown())
{
// TODO: multiple selection
AtlasMessage::qSelectObject qry(Position(evt.GetPosition()));
qry.Post();
obj->m_Selection.clear();
obj->m_Selection.push_back(qry.id);
POST_MESSAGE(SetSelectionPreview(obj->m_Selection));
ScenarioEditor::GetCommandProc().FinaliseLastCommand();
SET_STATE(Dragging);
return true;
}
else if (evt.Dragging() && evt.RightIsDown() || evt.RightDown())
{
Position pos (evt.GetPosition());
for (size_t i = 0; i < obj->m_Selection.size(); ++i)
POST_COMMAND(RotateObject, (obj->m_Selection[i], true, pos, 0.f));
return true;
}
else
return false;
}
bool OnKey(TransformObject* obj, wxKeyEvent& evt, KeyEventType type)
{
if (type == KEY_CHAR && evt.GetKeyCode() == WXK_DELETE)
{
for (size_t i = 0; i < obj->m_Selection.size(); ++i)
POST_COMMAND(DeleteObject, (obj->m_Selection[i]));
obj->m_Selection.clear();
POST_MESSAGE(SetSelectionPreview(obj->m_Selection));
return true;
}
else
return false;
}
}
Waiting;
struct sDragging : public State
{
bool OnMouse(TransformObject* obj, wxMouseEvent& evt)
{
if (evt.LeftUp())
{
SET_STATE(Waiting);
return true;
}
else if (evt.Dragging())
{
Position pos (evt.GetPosition());
for (size_t i = 0; i < obj->m_Selection.size(); ++i)
POST_COMMAND(MoveObject, (obj->m_Selection[i], pos));
return true;
}
else
return false;
}
}
Dragging;
};
IMPLEMENT_DYNAMIC_CLASS(TransformObject, StateDrivenTool<TransformObject>);

View File

@ -28,7 +28,7 @@ void Brush::SetData(int w, int h, const float* data)
m_Data = data;
}
void Brush::GetBottomRight(int& x, int& y) const
void Brush::GetCentre(int& x, int& y) const
{
CVector3D c = m_Centre;
if (m_W % 2) c.X += CELL_SIZE/2.f;
@ -36,8 +36,15 @@ void Brush::GetBottomRight(int& x, int& y) const
i32 cx, cy;
CTerrain::CalcFromPosition(c, cx, cy);
x = cx - (m_W-1)/2;
y = cy - (m_H-1)/2;
x = cx;
y = cy;
}
void Brush::GetBottomRight(int& x, int& y) const
{
GetCentre(x, y);
x -= (m_W-1)/2;
y -= (m_H-1)/2;
}
void Brush::SetRenderEnabled(bool enabled)

View File

@ -12,6 +12,7 @@ struct Brush
void SetRenderEnabled(bool enabled); // initial state is disabled
void Render(); // only does anything if enabled
void GetCentre(int& x, int& y) const;
void GetBottomRight(int& x, int& y) const;
float Get(int x, int y) const

View File

@ -44,6 +44,11 @@ CommandProc::CommandProc()
}
CommandProc::~CommandProc()
{
debug_assert(!m_Commands.size());
}
void CommandProc::Destroy()
{
for_each(m_Commands.begin(), m_Commands.end(), delete_fn<Command>);
m_Commands.clear();

View File

@ -20,6 +20,10 @@ public:
CommandProc();
~CommandProc();
// Should be called before shutting down, so it can free
// references to entities/etc that are stored in commands
void Destroy();
void Submit(Command* cmd);
void Undo();
@ -42,6 +46,7 @@ struct DataCommand : public Command // so commands can optionally override (De|C
{
void Destruct() {};
void Construct() {};
// MergeWithSelf should update 'prev' to include the effects of 'this'
void MergeWithSelf(void*) { debug_warn("MergeWithSelf unimplemented in some command"); }
};

View File

@ -22,10 +22,16 @@
using namespace AtlasMessage;
namespace AtlasMessage
{
extern void AtlasRenderSelection();
}
void AtlasRender()
{
Render();
g_CurrentBrush.Render();
AtlasRenderSelection();
}

View File

@ -7,45 +7,76 @@
#include "graphics/Terrain.h"
#include "ps/Game.h"
#include "maths/MathUtil.h"
#include "simulation/EntityManager.h"
#include "../Brushes.h"
#include "../DeltaArray.h"
namespace AtlasMessage {
BEGIN_COMMAND(AlterElevation)
class TerrainArray : public DeltaArray2D<u16>
class TerrainArray : public DeltaArray2D<u16>
{
public:
void Init()
{
public:
void Init()
{
m_Heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap();
m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
}
m_Heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap();
m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
}
void RaiseVertex(int x, int y, int amount)
{
// Ignore out-of-bounds vertices
if ((unsigned)x >= m_VertsPerSide || (unsigned)y >= m_VertsPerSide)
return;
void RaiseVertex(int x, int y, int amount)
{
// Ignore out-of-bounds vertices
if ((unsigned)x >= m_VertsPerSide || (unsigned)y >= m_VertsPerSide)
return;
set(x,y, (u16)clamp(get(x,y) + amount, 0, 65535));
}
set(x,y, (u16)clamp(get(x,y) + amount, 0, 65535));
}
protected:
u16 getOld(int x, int y)
{
return m_Heightmap[y*m_VertsPerSide + x];
}
void setNew(int x, int y, const u16& val)
{
m_Heightmap[y*m_VertsPerSide + x] = val;
}
void MoveVertexTowards(int x, int y, int target, int amount)
{
if ((unsigned)x >= m_VertsPerSide || (unsigned)y >= m_VertsPerSide)
return;
u16* m_Heightmap;
size_t m_VertsPerSide;
};
int h = get(x,y);
if (h < target)
h = std::min(target, h + amount);
else if (h > target)
h = std::max(target, h - amount);
else
return;
set(x,y, (u16)clamp(h, 0, 65535));
}
void SetVertex(int x, int y, u16 value)
{
if ((unsigned)x >= m_VertsPerSide || (unsigned)y >= m_VertsPerSide)
return;
set(x,y, value);
}
u16 GetVertex(int x, int y)
{
return get(clamp(x, 0, (int)m_VertsPerSide-1), clamp(y, 0, (int)m_VertsPerSide-1));
}
protected:
u16 getOld(int x, int y)
{
return m_Heightmap[y*m_VertsPerSide + x];
}
void setNew(int x, int y, const u16& val)
{
m_Heightmap[y*m_VertsPerSide + x] = val;
}
u16* m_Heightmap;
size_t m_VertsPerSide;
};
BEGIN_COMMAND(AlterElevation)
TerrainArray m_TerrainDelta;
@ -59,7 +90,6 @@ BEGIN_COMMAND(AlterElevation)
void Do()
{
int amount = (int)d->amount;
// If the framerate is very high, 'amount' is often very
@ -89,18 +119,21 @@ BEGIN_COMMAND(AlterElevation)
}
g_Game->GetWorld()->GetTerrain()->MakeDirty(x0, y0, x0+g_CurrentBrush.m_W, y0+g_CurrentBrush.m_H, RENDERDATA_UPDATE_VERTICES);
g_EntityManager.invalidateAll();
}
void Undo()
{
m_TerrainDelta.Undo();
g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_VERTICES);
g_EntityManager.invalidateAll();
}
void Redo()
{
m_TerrainDelta.Redo();
g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_VERTICES);
g_EntityManager.invalidateAll();
}
void MergeWithSelf(cAlterElevation* prev)
@ -110,4 +143,66 @@ BEGIN_COMMAND(AlterElevation)
END_COMMAND(AlterElevation)
//////////////////////////////////////////////////////////////////////////
BEGIN_COMMAND(FlattenElevation)
TerrainArray m_TerrainDelta;
void Construct()
{
m_TerrainDelta.Init();
}
void Destruct()
{
}
void Do()
{
int amount = (int)d->amount;
static CVector3D previousPosition;
d->pos.GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition);
previousPosition = g_CurrentBrush.m_Centre;
int xc, yc;
g_CurrentBrush.GetCentre(xc, yc);
u16 height = m_TerrainDelta.GetVertex(xc, yc);
int x0, y0;
g_CurrentBrush.GetBottomRight(x0, y0);
for (int dy = 0; dy < g_CurrentBrush.m_H; ++dy)
for (int dx = 0; dx < g_CurrentBrush.m_W; ++dx)
{
float b = g_CurrentBrush.Get(dx, dy);
if (b)
m_TerrainDelta.MoveVertexTowards(x0+dx, y0+dy, height, 1 + (int)(b*amount));
}
g_Game->GetWorld()->GetTerrain()->MakeDirty(x0, y0, x0+g_CurrentBrush.m_W, y0+g_CurrentBrush.m_H, RENDERDATA_UPDATE_VERTICES);
g_EntityManager.invalidateAll();
}
void Undo()
{
m_TerrainDelta.Undo();
g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_VERTICES);
g_EntityManager.invalidateAll();
}
void Redo()
{
m_TerrainDelta.Redo();
g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_VERTICES);
g_EntityManager.invalidateAll();
}
void MergeWithSelf(cFlattenElevation* prev)
{
prev->m_TerrainDelta.OverlayWith(m_TerrainDelta);
}
END_COMMAND(FlattenElevation)
}

View File

@ -2,6 +2,7 @@
#include "MessageHandler.h"
#include "../GameLoop.h"
#include "../CommandProc.h"
#include "renderer/Renderer.h"
#include "gui/GUI.h"
@ -34,7 +35,12 @@ MESSAGEHANDLER_STR(init)
MESSAGEHANDLER_STR(shutdown)
{
// Empty the CommandProc, to get rid of its references to entities before
// we kill the EntityManager
GetCommandProc().Destroy();
Shutdown();
g_GameLoop->rendering = false;
}

View File

@ -11,6 +11,7 @@
#include "maths/Matrix3D.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#include "lib/ogl.h"
#define LOG_CATEGORY "editor"
@ -31,6 +32,47 @@ QUERYHANDLER(GetEntitiesList)
}
static std::vector<ObjectID> g_Selection;
void AtlasRenderSelection()
{
glDisable(GL_DEPTH_TEST);
for (size_t i = 0; i < g_Selection.size(); ++i)
{
if (g_Selection[i])
{
CUnit* unit = static_cast<CUnit*>(g_Selection[i]);
if (unit->GetEntity())
unit->GetEntity()->renderSelectionOutline();
else if (unit->GetModel())
{
const CBound& bound = unit->GetModel()->GetBounds();
// Expand bounds by 10% around the centre
CVector3D centre;
bound.GetCentre(centre);
CVector3D a = (bound[0] - centre) * 1.1f + centre;
CVector3D b = (bound[1] - centre) * 1.1f + centre;
float h = g_Game->GetWorld()->GetTerrain()->getExactGroundLevel(centre.X, centre.Z);
glColor3f(0.8f, 0.8f, 0.8f);
glBegin(GL_LINE_LOOP);
glVertex3f(a.X, h, a.Z);
glVertex3f(a.X, h, b.Z);
glVertex3f(b.X, h, b.Z);
glVertex3f(b.X, h, a.Z);
glEnd();
}
}
}
glEnable(GL_DEPTH_TEST);
}
MESSAGEHANDLER(SetSelectionPreview)
{
g_Selection = msg->ids;
}
//////////////////////////////////////////////////////////////////////////
static CUnit* g_PreviewUnit = NULL;
static CStrW g_PreviewUnitID;
@ -104,13 +146,6 @@ BEGIN_COMMAND(CreateEntity)
CVector3D m_Pos;
float m_Angle;
void Construct()
{
}
void Destruct()
{
}
void Do()
{
m_Pos = GetUnitPos(d->pos);
@ -160,4 +195,245 @@ BEGIN_COMMAND(CreateEntity)
END_COMMAND(CreateEntity)
QUERYHANDLER(SelectObject)
{
float x, y;
msg->pos.GetScreenSpace(x, y);
CVector3D rayorigin, raydir;
g_Game->GetView()->GetCamera()->BuildCameraRay(x, y, rayorigin, raydir);
CUnit* target = g_UnitMan.PickUnit(rayorigin, raydir);
msg->id = static_cast<void*>(target);
}
BEGIN_COMMAND(MoveObject)
CVector3D m_PosOld, m_PosNew;
void Do()
{
if (! d->id)
return;
CUnit* unit = static_cast<CUnit*>(d->id);
if (unit->GetEntity())
{
m_PosOld = unit->GetEntity()->m_position;
}
else if (unit->GetModel())
{
CMatrix3D m = unit->GetModel()->GetTransform();
m_PosOld = m.GetTranslation();
}
m_PosNew = GetUnitPos(d->pos);
SetPos(m_PosNew);
}
void SetPos(CVector3D& pos)
{
if (! d->id)
return;
CUnit* unit = static_cast<CUnit*>(d->id);
if (unit->GetEntity())
{
unit->GetEntity()->m_position = pos;
}
else if (unit->GetModel())
{
CMatrix3D m = unit->GetModel()->GetTransform();
m.Translate(pos - m.GetTranslation());
unit->GetModel()->SetTransform(m);
}
}
void Redo()
{
SetPos(m_PosNew);
}
void Undo()
{
SetPos(m_PosOld);
}
void MergeWithSelf(cMoveObject* prev)
{
// TODO: merge correctly when prev unit != this unit
prev->m_PosNew = m_PosNew;
}
END_COMMAND(MoveObject)
BEGIN_COMMAND(RotateObject)
float m_AngleOld, m_AngleNew;
CMatrix3D m_TransformOld, m_TransformNew;
void Do()
{
if (! d->id)
return;
CUnit* unit = static_cast<CUnit*>(d->id);
if (unit->GetEntity())
{
m_AngleOld = unit->GetEntity()->m_orientation;
if (d->usetarget)
{
CVector3D& pos = unit->GetEntity()->m_position;
CVector3D target;
d->target.GetWorldSpace(target, pos.Y);
CVector2D dir(target.X-pos.X, target.Z-pos.Z);
m_AngleNew = atan2(dir.x, dir.y);
}
else
{
m_AngleNew = d->angle;
}
}
else if (unit->GetModel())
{
m_TransformOld = unit->GetModel()->GetTransform();
CVector3D pos = unit->GetModel()->GetTransform().GetTranslation();
float s, c;
if (d->usetarget)
{
CVector3D target;
d->target.GetWorldSpace(target, pos.Y);
CVector2D dir(target.X-pos.X, target.Z-pos.Z);
dir = dir.normalize();
s = dir.x;
c = dir.y;
}
else
{
s = sinf(d->angle);
c = cosf(d->angle);
}
CMatrix3D& m = m_TransformNew;
m._11 = -c; m._12 = 0.0f; m._13 = -s; m._14 = pos.X;
m._21 = 0.0f; m._22 = 1.0f; m._23 = 0.0f; m._24 = pos.Y;
m._31 = s; m._32 = 0.0f; m._33 = -c; m._34 = pos.Z;
m._41 = 0.0f; m._42 = 0.0f; m._43 = 0.0f; m._44 = 1.0f;
}
SetAngle(m_AngleNew, m_TransformNew);
}
void SetAngle(float angle, CMatrix3D& transform)
{
if (! d->id)
return;
CUnit* unit = static_cast<CUnit*>(d->id);
if (unit->GetEntity())
{
unit->GetEntity()->m_orientation = angle;
}
else if (unit->GetModel())
{
unit->GetModel()->SetTransform(transform);
}
}
void Redo()
{
SetAngle(m_AngleNew, m_TransformNew);
}
void Undo()
{
SetAngle(m_AngleOld, m_TransformOld);
}
void MergeWithSelf(cRotateObject* prev)
{
// TODO: merge correctly when prev unit != this unit
prev->m_AngleNew = m_AngleNew;
prev->m_TransformNew = m_TransformNew;
}
END_COMMAND(RotateObject)
BEGIN_COMMAND(DeleteObject)
bool m_ObjectAlive;
void Construct()
{
m_ObjectAlive = true;
}
void Destruct()
{
if (! m_ObjectAlive)
{
if (! d->id)
return;
CUnit* unit = static_cast<CUnit*>(d->id);
if (unit->GetEntity())
unit->GetEntity()->kill();
else
{
g_UnitMan.RemoveUnit(unit);
delete unit;
}
}
}
void Do()
{
Redo();
}
void Redo()
{
if (! d->id)
return;
CUnit* unit = static_cast<CUnit*>(d->id);
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;
}
void Undo()
{
if (! d->id)
return;
CUnit* unit = static_cast<CUnit*>(d->id);
if (unit->GetEntity())
unit->GetEntity()->m_destroyed = false;
g_UnitMan.AddUnit(unit);
m_ObjectAlive = true;
}
END_COMMAND(DeleteObject)
}

View File

@ -153,6 +153,11 @@ COMMAND(AlterElevation, MERGE,
((float, amount))
);
COMMAND(FlattenElevation, MERGE,
((Position, pos))
((float, amount))
);
struct ePaintTerrainPriority { enum { HIGH, LOW }; };
COMMAND(PaintTerrain, MERGE,
((Position, pos))
@ -162,6 +167,36 @@ COMMAND(PaintTerrain, MERGE,
//////////////////////////////////////////////////////////////////////////
typedef void* ObjectID;
QUERY(SelectObject,
((Position, pos))
,
((ObjectID, id))
);
COMMAND(MoveObject, MERGE,
((ObjectID, id))
((Position, pos))
);
COMMAND(RotateObject, MERGE,
((ObjectID, id))
((bool, usetarget)) // true => use 'target' for orientation; false => use 'angle'
((Position, target))
((float, angle))
);
COMMAND(DeleteObject, NOMERGE,
((ObjectID, id))
);
MESSAGE(SetSelectionPreview,
((std::vector<ObjectID>, ids))
);
//////////////////////////////////////////////////////////////////////////
#include "MessagesSetup.h"
#endif // MESSAGES_H__