mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-07-06 23:15:47 -07:00
Delete the old standalone actor viewer. Import some terrain textures that are useful for tools. Fixes #483. This was SVN commit r7428.
1081 lines
25 KiB
C++
1081 lines
25 KiB
C++
/* Copyright (C) 2009 Wildfire Games.
|
|
* This file is part of 0 A.D.
|
|
*
|
|
* 0 A.D. 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
|
|
#include <cfloat>
|
|
|
|
#include "MessageHandler.h"
|
|
#include "../CommandProc.h"
|
|
#include "../SimState.h"
|
|
#include "../View.h"
|
|
|
|
#include "graphics/GameView.h"
|
|
#include "graphics/Model.h"
|
|
#include "graphics/ObjectBase.h"
|
|
#include "graphics/ObjectEntry.h"
|
|
#include "graphics/ObjectManager.h"
|
|
#include "graphics/Terrain.h"
|
|
#include "graphics/Unit.h"
|
|
#include "graphics/UnitManager.h"
|
|
#include "lib/ogl.h"
|
|
#include "maths/MathUtil.h"
|
|
#include "maths/Matrix3D.h"
|
|
#include "ps/CLogger.h"
|
|
#include "ps/Game.h"
|
|
#include "ps/World.h"
|
|
#include "renderer/Renderer.h"
|
|
#include "renderer/WaterManager.h"
|
|
#include "simulation/EntityTemplateCollection.h"
|
|
#include "simulation/EntityTemplate.h"
|
|
#include "simulation/Entity.h"
|
|
#include "simulation/EntityManager.h"
|
|
#include "simulation/TerritoryManager.h"
|
|
#include "simulation2/Simulation2.h"
|
|
#include "simulation2/components/ICmpOwnership.h"
|
|
#include "simulation2/components/ICmpPosition.h"
|
|
#include "simulation2/components/ICmpTemplateManager.h"
|
|
|
|
#define LOG_CATEGORY L"editor"
|
|
|
|
namespace AtlasMessage {
|
|
|
|
namespace
|
|
{
|
|
bool SortObjectsList(const sObjectsListItem& a, const sObjectsListItem& b)
|
|
{
|
|
return wcscmp(a.name.c_str(), b.name.c_str()) < 0;
|
|
}
|
|
|
|
bool IsFloating(const CUnit* unit)
|
|
{
|
|
if (! unit)
|
|
return false;
|
|
|
|
if (unit->GetEntity())
|
|
return (unit->GetEntity()->m_base->m_anchorType != L"Ground");
|
|
else
|
|
return unit->GetObject()->m_Base->m_Properties.m_FloatOnWater;
|
|
}
|
|
|
|
CUnitManager& GetUnitManager()
|
|
{
|
|
return g_Game->GetWorld()->GetUnitManager();
|
|
}
|
|
}
|
|
|
|
QUERYHANDLER(GetObjectsList)
|
|
{
|
|
std::vector<sObjectsListItem> objects;
|
|
|
|
if (g_UseSimulation2)
|
|
{
|
|
CmpPtr<ICmpTemplateManager> cmp(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
|
if (!cmp.null())
|
|
{
|
|
std::vector<std::wstring> names = cmp->FindAllTemplates();
|
|
|
|
for (std::vector<std::wstring>::iterator it = names.begin(); it != names.end(); ++it)
|
|
{
|
|
sObjectsListItem e;
|
|
e.id = *it;
|
|
if (it->substr(0, 6) == L"actor|")
|
|
{
|
|
e.name = it->substr(6);
|
|
e.type = 1;
|
|
}
|
|
else
|
|
{
|
|
e.name = *it;
|
|
e.type = 0;
|
|
}
|
|
objects.push_back(e);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CEntityTemplateCollection::IsInitialised())
|
|
{
|
|
std::vector<CStrW> names;
|
|
g_EntityTemplateCollection.GetEntityTemplateNames(names);
|
|
for (std::vector<CStrW>::iterator it = names.begin(); it != names.end(); ++it)
|
|
{
|
|
//CEntityTemplate* baseent = g_EntityTemplateCollection.GetTemplate(*it);
|
|
sObjectsListItem e;
|
|
e.id = L"(e) " + *it;
|
|
e.name = *it; //baseent->m_Tag
|
|
e.type = 0;
|
|
objects.push_back(e);
|
|
}
|
|
}
|
|
|
|
{
|
|
std::vector<CStr> names;
|
|
//CObjectManager::GetPropObjectNames(names);
|
|
CObjectManager::GetAllObjectNames(names);
|
|
for (std::vector<CStr>::iterator it = names.begin(); it != names.end(); ++it)
|
|
{
|
|
sObjectsListItem e;
|
|
e.id = L"(n) " + CStrW(*it);
|
|
e.name = CStrW(*it).AfterFirst(/*L"props/"*/ L"actors/");
|
|
e.type = 1;
|
|
objects.push_back(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort(objects.begin(), objects.end(), SortObjectsList);
|
|
msg->objects = objects;
|
|
}
|
|
|
|
|
|
static std::vector<ObjectID> g_Selection;
|
|
void AtlasRenderSelection()
|
|
{
|
|
glDisable(GL_DEPTH_TEST);
|
|
for (size_t i = 0; i < g_Selection.size(); ++i)
|
|
{
|
|
CUnit* unit = GetUnitManager().FindByID(g_Selection[i]);
|
|
if (unit)
|
|
{
|
|
if (unit->GetEntity())
|
|
{
|
|
unit->GetEntity()->RenderSelectionOutline();
|
|
}
|
|
else
|
|
{
|
|
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);
|
|
if (IsFloating(unit))
|
|
h = std::max(h, g_Renderer.GetWaterManager()->m_WaterHeight);
|
|
|
|
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;
|
|
}
|
|
|
|
QUERYHANDLER(GetObjectSettings)
|
|
{
|
|
View* view = View::GetView(msg->view);
|
|
CSimulation2* simulation = view->GetSimulation2();
|
|
if (simulation)
|
|
{
|
|
sObjectSettings settings;
|
|
settings.player = 0;
|
|
|
|
CmpPtr<ICmpOwnership> cmpOwner (*simulation, view->GetEntityId(msg->id));
|
|
if (!cmpOwner.null())
|
|
{
|
|
int32_t player = cmpOwner->GetOwner();
|
|
if (player != -1)
|
|
settings.player = player;
|
|
}
|
|
|
|
// TODO: selections
|
|
|
|
msg->settings = settings;
|
|
|
|
return;
|
|
}
|
|
|
|
CUnit* unit = view->GetUnit(msg->id);
|
|
if (! unit) return;
|
|
|
|
sObjectSettings settings;
|
|
settings.player = unit->GetPlayerID();
|
|
|
|
// Get the unit's possible variants and selected variants
|
|
std::vector<std::vector<CStr> > groups = unit->GetObject()->m_Base->GetVariantGroups();
|
|
const std::set<CStr>& selections = unit->GetActorSelections();
|
|
|
|
// Iterate over variant groups
|
|
std::vector<std::vector<std::wstring> > variantgroups;
|
|
std::set<std::wstring> selections_set;
|
|
variantgroups.reserve(groups.size());
|
|
for (size_t i = 0; i < groups.size(); ++i)
|
|
{
|
|
// Copy variants into output structure
|
|
|
|
std::vector<std::wstring> group;
|
|
group.reserve(groups[i].size());
|
|
int choice = -1;
|
|
|
|
for (size_t j = 0; j < groups[i].size(); ++j)
|
|
{
|
|
group.push_back(CStrW(groups[i][j]));
|
|
|
|
// Find the first string in 'selections' that matches one of this
|
|
// group's variants
|
|
if (choice == -1)
|
|
if (selections.find(groups[i][j]) != selections.end())
|
|
choice = (int)j;
|
|
}
|
|
|
|
// Assuming one of the variants was selected (which it really ought
|
|
// to be), remember that one's name
|
|
if (choice != -1)
|
|
selections_set.insert(CStrW(groups[i][choice]));
|
|
|
|
variantgroups.push_back(group);
|
|
}
|
|
|
|
settings.variantgroups = variantgroups;
|
|
settings.selections = std::vector<std::wstring> (selections_set.begin(), selections_set.end()); // convert set->vector
|
|
msg->settings = settings;
|
|
}
|
|
|
|
BEGIN_COMMAND(SetObjectSettings)
|
|
{
|
|
size_t m_PlayerOld, m_PlayerNew;
|
|
std::set<CStr> m_SelectionsOld, m_SelectionsNew;
|
|
|
|
void Do()
|
|
{
|
|
sObjectSettings settings = msg->settings;
|
|
|
|
View* view = View::GetView(msg->view);
|
|
CSimulation2* simulation = view->GetSimulation2();
|
|
if (simulation)
|
|
{
|
|
CmpPtr<ICmpOwnership> cmpOwner (*simulation, view->GetEntityId(msg->id));
|
|
m_PlayerOld = 0;
|
|
if (!cmpOwner.null())
|
|
{
|
|
int32_t player = cmpOwner->GetOwner();
|
|
if (player != -1)
|
|
m_PlayerOld = player;
|
|
}
|
|
|
|
// TODO: selections
|
|
}
|
|
else
|
|
{
|
|
CUnit* unit = view->GetUnit(msg->id);
|
|
if (! unit) return;
|
|
m_PlayerOld = unit->GetPlayerID();
|
|
m_SelectionsOld = unit->GetActorSelections();
|
|
}
|
|
|
|
m_PlayerNew = (size_t)settings.player;
|
|
|
|
std::vector<std::wstring> selections = *settings.selections;
|
|
for (std::vector<std::wstring>::iterator it = selections.begin(); it != selections.end(); ++it)
|
|
{
|
|
m_SelectionsNew.insert(CStr(*it));
|
|
}
|
|
|
|
Redo();
|
|
}
|
|
|
|
void Redo()
|
|
{
|
|
Set(m_PlayerNew, m_SelectionsNew);
|
|
}
|
|
|
|
void Undo()
|
|
{
|
|
Set(m_PlayerOld, m_SelectionsOld);
|
|
}
|
|
|
|
private:
|
|
void Set(size_t player, const std::set<CStr>& selections)
|
|
{
|
|
View* view = View::GetView(msg->view);
|
|
CSimulation2* simulation = view->GetSimulation2();
|
|
if (simulation)
|
|
{
|
|
CmpPtr<ICmpOwnership> cmpOwner (*simulation, view->GetEntityId(msg->id));
|
|
if (!cmpOwner.null())
|
|
cmpOwner->SetOwner(player);
|
|
// TODO: selections
|
|
|
|
return;
|
|
}
|
|
|
|
CUnit* unit = view->GetUnit(msg->id);
|
|
if (! unit) return;
|
|
|
|
unit->SetPlayerID(player);
|
|
|
|
unit->SetActorSelections(selections);
|
|
|
|
if (m_PlayerOld != m_PlayerNew)
|
|
g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate();
|
|
}
|
|
};
|
|
END_COMMAND(SetObjectSettings);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
static CStrW g_PreviewUnitName;
|
|
static entity_id_t g_PreviewEntityID = INVALID_ENTITY; // used if g_UseSimulation2
|
|
static size_t g_PreviewUnitID = invalidUnitId; // old simulation
|
|
static bool g_PreviewUnitFloating;
|
|
|
|
static CVector3D GetUnitPos(const Position& pos, bool floating)
|
|
{
|
|
static CVector3D vec;
|
|
vec = pos.GetWorldSpace(vec, floating); // if msg->pos is 'Unchanged', use the previous pos
|
|
|
|
// Clamp the position to the edges of the world:
|
|
|
|
// Use 'clamp' with a value slightly less than the width, so that converting
|
|
// to integer (rounding towards zero) will put it on the tile inside the edge
|
|
// instead of just outside
|
|
float mapWidth = (g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide()-1)*CELL_SIZE;
|
|
float delta = 1e-6f; // fraction of map width - must be > FLT_EPSILON
|
|
|
|
float xOnMap = clamp(vec.X, 0.f, mapWidth * (1.f - delta));
|
|
float zOnMap = clamp(vec.Z, 0.f, mapWidth * (1.f - delta));
|
|
|
|
// Don't waste time with GetExactGroundLevel unless we've changed
|
|
if (xOnMap != vec.X || zOnMap != vec.Z)
|
|
{
|
|
vec.X = xOnMap;
|
|
vec.Z = zOnMap;
|
|
vec.Y = g_Game->GetWorld()->GetTerrain()->GetExactGroundLevel(xOnMap, zOnMap);
|
|
}
|
|
|
|
return vec;
|
|
}
|
|
|
|
static bool ParseObjectName(const CStrW& obj, bool& isEntity, CStrW& name)
|
|
{
|
|
if (obj.substr(0, 4) == L"(e) ")
|
|
{
|
|
isEntity = true;
|
|
name = obj.substr(4);
|
|
return true;
|
|
}
|
|
else if (obj.substr(0, 4) == L"(n) ")
|
|
{
|
|
isEntity = false;
|
|
name = obj.substr(4);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
MESSAGEHANDLER(ObjectPreview)
|
|
{
|
|
if (g_UseSimulation2)
|
|
{
|
|
// If the selection has changed...
|
|
if (*msg->id != g_PreviewUnitName)
|
|
{
|
|
// Delete old entity
|
|
if (g_PreviewEntityID != INVALID_ENTITY)
|
|
g_Game->GetSimulation2()->DestroyEntity(g_PreviewEntityID);
|
|
|
|
// Create the new entity
|
|
if ((*msg->id).empty())
|
|
g_PreviewEntityID = INVALID_ENTITY;
|
|
else
|
|
g_PreviewEntityID = g_Game->GetSimulation2()->AddLocalEntity(L"preview|" + *msg->id);
|
|
|
|
g_PreviewUnitName = *msg->id;
|
|
}
|
|
|
|
if (g_PreviewEntityID != INVALID_ENTITY)
|
|
{
|
|
// Update the unit's position and orientation:
|
|
|
|
CmpPtr<ICmpPosition> cmpPos (*g_Game->GetSimulation2(), g_PreviewEntityID);
|
|
if (!cmpPos.null())
|
|
{
|
|
CVector3D pos = GetUnitPos(msg->pos, false);
|
|
cmpPos->JumpTo(entity_pos_t::FromFloat(pos.X), entity_pos_t::FromFloat(pos.Z));
|
|
|
|
float angle;
|
|
if (msg->usetarget)
|
|
{
|
|
// Aim from pos towards msg->target
|
|
CVector3D target = msg->target->GetWorldSpace(pos.Y);
|
|
angle = atan2(target.X-pos.X, target.Z-pos.Z);
|
|
}
|
|
else
|
|
{
|
|
angle = msg->angle;
|
|
}
|
|
|
|
cmpPos->SetYRotation(entity_angle_t::FromFloat(angle));
|
|
}
|
|
|
|
// TODO: handle random variations somehow
|
|
|
|
CmpPtr<ICmpOwnership> cmpOwner (*g_Game->GetSimulation2(), g_PreviewEntityID);
|
|
if (!cmpOwner.null())
|
|
cmpOwner->SetOwner(msg->settings->player);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Old simulation system:
|
|
|
|
CUnit* previewUnit = GetUnitManager().FindByID(g_PreviewUnitID);
|
|
|
|
// Don't recreate the unit unless it's changed
|
|
if (*msg->id != g_PreviewUnitName)
|
|
{
|
|
// Delete old unit
|
|
if (previewUnit)
|
|
{
|
|
GetUnitManager().DeleteUnit(previewUnit);
|
|
previewUnit = NULL;
|
|
}
|
|
|
|
g_PreviewUnitID = invalidUnitId;
|
|
|
|
bool isEntity;
|
|
CStrW name;
|
|
if (ParseObjectName(*msg->id, isEntity, name))
|
|
{
|
|
std::set<CStr> selections; // TODO: get selections from user
|
|
|
|
// Create new unit
|
|
if (isEntity)
|
|
{
|
|
CEntityTemplate* base = g_EntityTemplateCollection.GetTemplate(name);
|
|
if (base) // (ignore errors)
|
|
{
|
|
previewUnit = GetUnitManager().CreateUnit(base->m_actorName, NULL, selections);
|
|
if (previewUnit)
|
|
{
|
|
g_PreviewUnitID = GetUnitManager().GetNewID();
|
|
previewUnit->SetID(g_PreviewUnitID);
|
|
}
|
|
g_PreviewUnitFloating = (base->m_anchorType != L"Ground");
|
|
// TODO: variations
|
|
}
|
|
}
|
|
else
|
|
{
|
|
previewUnit = GetUnitManager().CreateUnit(CStr(name), NULL, selections);
|
|
if (previewUnit)
|
|
{
|
|
g_PreviewUnitID = GetUnitManager().GetNewID();
|
|
previewUnit->SetID(g_PreviewUnitID);
|
|
}
|
|
g_PreviewUnitFloating = IsFloating(previewUnit);
|
|
}
|
|
}
|
|
|
|
g_PreviewUnitName = *msg->id;
|
|
}
|
|
|
|
if (previewUnit)
|
|
{
|
|
// Update the unit's position and orientation:
|
|
|
|
CVector3D pos = GetUnitPos(msg->pos, g_PreviewUnitFloating);
|
|
|
|
float s, c;
|
|
|
|
if (msg->usetarget)
|
|
{
|
|
// Aim from pos towards msg->target
|
|
CVector3D target = msg->target->GetWorldSpace(pos.Y);
|
|
CVector2D dir(target.X-pos.X, target.Z-pos.Z);
|
|
dir = dir.Normalize();
|
|
s = dir.x;
|
|
c = dir.y;
|
|
}
|
|
else
|
|
{
|
|
s = sin(msg->angle);
|
|
c = cos(msg->angle);
|
|
}
|
|
|
|
CMatrix3D m;
|
|
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;
|
|
previewUnit->GetModel()->SetTransform(m);
|
|
|
|
// Update the unit's player colour:
|
|
previewUnit->SetPlayerID(msg->settings->player);
|
|
}
|
|
}
|
|
|
|
BEGIN_COMMAND(CreateObject)
|
|
{
|
|
CVector3D m_Pos;
|
|
float m_Angle;
|
|
size_t m_ID; // old simulation system
|
|
size_t m_Player;
|
|
entity_id_t m_EntityID; // new simulation system
|
|
|
|
void Do()
|
|
{
|
|
// Calculate the position/orientation to create this unit with
|
|
|
|
m_Pos = GetUnitPos(msg->pos, false); // TODO: set 'floating' properly
|
|
|
|
if (msg->usetarget)
|
|
{
|
|
// Aim from m_Pos towards msg->target
|
|
CVector3D target = msg->target->GetWorldSpace(m_Pos.Y);
|
|
m_Angle = atan2(target.X-m_Pos.X, target.Z-m_Pos.Z);
|
|
}
|
|
else
|
|
{
|
|
m_Angle = msg->angle;
|
|
}
|
|
|
|
// TODO: variations too
|
|
m_Player = msg->settings->player;
|
|
|
|
if (!g_UseSimulation2)
|
|
{
|
|
// Get a new ID, for future reference to this unit
|
|
m_ID = GetUnitManager().GetNewID();
|
|
}
|
|
|
|
Redo();
|
|
}
|
|
|
|
void Redo()
|
|
{
|
|
if (g_UseSimulation2)
|
|
{
|
|
m_EntityID = g_Game->GetSimulation2()->AddEntity(*msg->id);
|
|
if (m_EntityID == INVALID_ENTITY)
|
|
return;
|
|
|
|
CmpPtr<ICmpPosition> cmpPos (*g_Game->GetSimulation2(), m_EntityID);
|
|
if (!cmpPos.null())
|
|
{
|
|
cmpPos->JumpTo(entity_pos_t::FromFloat(m_Pos.X), entity_pos_t::FromFloat(m_Pos.Z));
|
|
cmpPos->SetYRotation(entity_angle_t::FromFloat(m_Angle));
|
|
}
|
|
|
|
CmpPtr<ICmpOwnership> cmpOwner (*g_Game->GetSimulation2(), m_EntityID);
|
|
if (!cmpOwner.null())
|
|
cmpOwner->SetOwner(m_Player);
|
|
|
|
// TODO: handle random variations somehow
|
|
|
|
return;
|
|
}
|
|
|
|
// Old simulation system:
|
|
|
|
bool isEntity;
|
|
CStrW name;
|
|
if (ParseObjectName(*msg->id, isEntity, name))
|
|
{
|
|
std::set<CStr> selections;
|
|
|
|
if (isEntity)
|
|
{
|
|
CEntityTemplate* base = g_EntityTemplateCollection.GetTemplate(name);
|
|
if (! base)
|
|
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to load entity template '%ls'", name.c_str());
|
|
else
|
|
{
|
|
HEntity ent = g_EntityManager.Create(base, m_Pos, m_Angle, selections);
|
|
|
|
if (! ent)
|
|
{
|
|
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to create entity of type '%ls'", name.c_str());
|
|
}
|
|
else if (! ent->m_actor)
|
|
{
|
|
// We don't want to allow entities with no actors, because
|
|
// they'll be be invisible and will confuse scenario designers
|
|
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to create entity of type '%ls'", name.c_str());
|
|
ent->Kill();
|
|
}
|
|
else
|
|
{
|
|
ent->m_actor->SetPlayerID(m_Player);
|
|
ent->m_actor->SetID(m_ID);
|
|
|
|
if (ent->m_base->m_isTerritoryCentre)
|
|
g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate();
|
|
|
|
ent->Initialize();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CUnit* unit = GetUnitManager().CreateUnit(CStr(name), NULL, selections);
|
|
if (! unit)
|
|
{
|
|
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to load nonentity actor '%ls'", name.c_str());
|
|
}
|
|
else
|
|
{
|
|
unit->SetID(m_ID);
|
|
|
|
float s = sin(m_Angle);
|
|
float c = cos(m_Angle);
|
|
|
|
CMatrix3D m;
|
|
m._11 = -c; m._12 = 0.0f; m._13 = -s; m._14 = m_Pos.X;
|
|
m._21 = 0.0f; m._22 = 1.0f; m._23 = 0.0f; m._24 = m_Pos.Y;
|
|
m._31 = s; m._32 = 0.0f; m._33 = -c; m._34 = m_Pos.Z;
|
|
m._41 = 0.0f; m._42 = 0.0f; m._43 = 0.0f; m._44 = 1.0f;
|
|
unit->GetModel()->SetTransform(m);
|
|
|
|
unit->SetPlayerID(m_Player);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Undo()
|
|
{
|
|
if (g_UseSimulation2)
|
|
{
|
|
if (m_EntityID != INVALID_ENTITY)
|
|
{
|
|
g_Game->GetSimulation2()->DestroyEntity(m_EntityID);
|
|
m_EntityID = INVALID_ENTITY;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Old simulation system:
|
|
|
|
CUnit* unit = GetUnitManager().FindByID(m_ID);
|
|
if (unit)
|
|
{
|
|
if (unit->GetEntity())
|
|
{
|
|
bool wasTerritoryCentre = unit->GetEntity()->m_base->m_isTerritoryCentre;
|
|
|
|
unit->GetEntity()->Kill();
|
|
|
|
if (wasTerritoryCentre)
|
|
g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate();
|
|
}
|
|
else
|
|
{
|
|
GetUnitManager().RemoveUnit(unit);
|
|
delete unit;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
END_COMMAND(CreateObject)
|
|
|
|
|
|
QUERYHANDLER(PickObject)
|
|
{
|
|
float x, y;
|
|
msg->pos->GetScreenSpace(x, y);
|
|
|
|
CVector3D rayorigin, raydir;
|
|
g_Game->GetView()->GetCamera()->BuildCameraRay((int)x, (int)y, rayorigin, raydir);
|
|
|
|
CUnit* target = GetUnitManager().PickUnit(rayorigin, raydir, false);
|
|
|
|
if (target)
|
|
msg->id = target->GetID();
|
|
else
|
|
msg->id = invalidUnitId;
|
|
|
|
if (target)
|
|
{
|
|
// Get screen coordinates of the point on the ground underneath the
|
|
// object's model-centre, so that callers know the offset to use when
|
|
// working out the screen coordinates to move the object to.
|
|
// (TODO: http://trac.wildfiregames.com/ticket/99)
|
|
|
|
CVector3D centre = target->GetModel()->GetTransform().GetTranslation();
|
|
|
|
centre.Y = g_Game->GetWorld()->GetTerrain()->GetExactGroundLevel(centre.X, centre.Z);
|
|
if (IsFloating(target))
|
|
centre.Y = std::max(centre.Y, g_Renderer.GetWaterManager()->m_WaterHeight);
|
|
|
|
float cx, cy;
|
|
g_Game->GetView()->GetCamera()->GetScreenCoordinates(centre, cx, cy);
|
|
|
|
msg->offsetx = (int)(cx - x);
|
|
msg->offsety = (int)(cy - y);
|
|
}
|
|
else
|
|
{
|
|
msg->offsetx = msg->offsety = 0;
|
|
}
|
|
}
|
|
|
|
|
|
BEGIN_COMMAND(MoveObject)
|
|
{
|
|
CVector3D m_PosOld, m_PosNew;
|
|
|
|
void Do()
|
|
{
|
|
if (g_UseSimulation2)
|
|
{
|
|
m_PosNew = GetUnitPos(msg->pos, false); // TODO: set 'floating' properly
|
|
|
|
CmpPtr<ICmpPosition> cmpPos(*g_Game->GetSimulation2(), msg->id);
|
|
if (cmpPos.null())
|
|
m_PosOld = m_PosNew; // error
|
|
else
|
|
{
|
|
CFixedVector3D pos = cmpPos->GetPosition();
|
|
m_PosOld = CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CUnit* unit = GetUnitManager().FindByID(msg->id);
|
|
if (! unit) return;
|
|
|
|
m_PosNew = GetUnitPos(msg->pos, IsFloating(unit));
|
|
|
|
if (unit->GetEntity())
|
|
{
|
|
m_PosOld = unit->GetEntity()->m_position;
|
|
}
|
|
else
|
|
{
|
|
CMatrix3D m = unit->GetModel()->GetTransform();
|
|
m_PosOld = m.GetTranslation();
|
|
}
|
|
}
|
|
|
|
SetPos(m_PosNew);
|
|
}
|
|
|
|
void SetPos(CVector3D& pos)
|
|
{
|
|
if (g_UseSimulation2)
|
|
{
|
|
CmpPtr<ICmpPosition> cmpPos(*g_Game->GetSimulation2(), msg->id);
|
|
if (cmpPos.null())
|
|
return;
|
|
|
|
cmpPos->JumpTo(entity_pos_t::FromFloat(pos.X), entity_pos_t::FromFloat(pos.Z));
|
|
}
|
|
else
|
|
{
|
|
CUnit* unit = GetUnitManager().FindByID(msg->id);
|
|
if (! unit) return;
|
|
|
|
if (unit->GetEntity())
|
|
{
|
|
unit->GetEntity()->m_position = pos;
|
|
unit->GetEntity()->Teleport();
|
|
|
|
if (unit->GetEntity()->m_base->m_isTerritoryCentre)
|
|
g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate();
|
|
}
|
|
else
|
|
{
|
|
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 MergeIntoPrevious(cMoveObject* prev)
|
|
{
|
|
// TODO: do something valid if prev unit != this unit
|
|
debug_assert(prev->msg->id == msg->id);
|
|
prev->m_PosNew = m_PosNew;
|
|
}
|
|
};
|
|
END_COMMAND(MoveObject)
|
|
|
|
|
|
BEGIN_COMMAND(RotateObject)
|
|
{
|
|
float m_AngleOld, m_AngleNew;
|
|
CMatrix3D m_TransformOld, m_TransformNew;
|
|
|
|
void Do()
|
|
{
|
|
if (g_UseSimulation2)
|
|
{
|
|
CmpPtr<ICmpPosition> cmpPos(*g_Game->GetSimulation2(), msg->id);
|
|
if (cmpPos.null())
|
|
return;
|
|
|
|
m_AngleOld = cmpPos->GetRotation().Y.ToFloat();
|
|
if (msg->usetarget)
|
|
{
|
|
CMatrix3D transform = cmpPos->GetInterpolatedTransform(0.f);
|
|
CVector3D pos = transform.GetTranslation();
|
|
CVector3D target = msg->target->GetWorldSpace(pos.Y);
|
|
CVector2D dir(target.X-pos.X, target.Z-pos.Z);
|
|
m_AngleNew = atan2(dir.x, dir.y);
|
|
}
|
|
else
|
|
{
|
|
m_AngleNew = msg->angle;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CUnit* unit = GetUnitManager().FindByID(msg->id);
|
|
if (! unit) return;
|
|
|
|
if (unit->GetEntity())
|
|
{
|
|
m_AngleOld = unit->GetEntity()->m_orientation.Y;
|
|
if (msg->usetarget)
|
|
{
|
|
CVector3D& pos = unit->GetEntity()->m_position;
|
|
CVector3D target = msg->target->GetWorldSpace(pos.Y);
|
|
CVector2D dir(target.X-pos.X, target.Z-pos.Z);
|
|
m_AngleNew = atan2(dir.x, dir.y);
|
|
}
|
|
else
|
|
{
|
|
m_AngleNew = msg->angle;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_TransformOld = unit->GetModel()->GetTransform();
|
|
|
|
CVector3D pos = unit->GetModel()->GetTransform().GetTranslation();
|
|
|
|
float s, c;
|
|
if (msg->usetarget)
|
|
{
|
|
CVector3D target = msg->target->GetWorldSpace(pos.Y);
|
|
CVector2D dir(target.X-pos.X, target.Z-pos.Z);
|
|
dir = dir.Normalize();
|
|
s = dir.x;
|
|
c = dir.y;
|
|
}
|
|
else
|
|
{
|
|
s = sinf(msg->angle);
|
|
c = cosf(msg->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 (g_UseSimulation2)
|
|
{
|
|
CmpPtr<ICmpPosition> cmpPos(*g_Game->GetSimulation2(), msg->id);
|
|
if (cmpPos.null())
|
|
return;
|
|
|
|
cmpPos->SetYRotation(CFixed_23_8::FromFloat(angle));
|
|
}
|
|
else
|
|
{
|
|
CUnit* unit = GetUnitManager().FindByID(msg->id);
|
|
if (! unit) return;
|
|
|
|
if (unit->GetEntity())
|
|
{
|
|
unit->GetEntity()->m_orientation.Y = angle;
|
|
unit->GetEntity()->Reorient();
|
|
}
|
|
else
|
|
{
|
|
unit->GetModel()->SetTransform(transform);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Redo()
|
|
{
|
|
SetAngle(m_AngleNew, m_TransformNew);
|
|
}
|
|
|
|
void Undo()
|
|
{
|
|
SetAngle(m_AngleOld, m_TransformOld);
|
|
}
|
|
|
|
void MergeIntoPrevious(cRotateObject* prev)
|
|
{
|
|
// TODO: do something valid if prev unit != this unit
|
|
debug_assert(prev->msg->id == msg->id);
|
|
prev->m_AngleNew = m_AngleNew;
|
|
prev->m_TransformNew = m_TransformNew;
|
|
}
|
|
};
|
|
END_COMMAND(RotateObject)
|
|
|
|
|
|
BEGIN_COMMAND(DeleteObject)
|
|
{
|
|
// Old simulation: These two values are never both non-NULL
|
|
std::auto_ptr<SimState::Entity> m_FrozenEntity;
|
|
std::auto_ptr<SimState::Nonentity> m_FrozenNonentity;
|
|
|
|
// New simulation:
|
|
// Saved copy of the important aspects of a unit, to allow undo
|
|
entity_id_t m_EntityID;
|
|
std::wstring m_TemplateName;
|
|
int32_t m_Owner;
|
|
CFixedVector3D m_Pos;
|
|
CFixedVector3D m_Rot;
|
|
// TODO: random selections
|
|
|
|
cDeleteObject()
|
|
: m_FrozenEntity(NULL), m_FrozenNonentity(NULL), m_EntityID(INVALID_ENTITY), m_Owner(-1)
|
|
{
|
|
}
|
|
|
|
void Do()
|
|
{
|
|
Redo();
|
|
}
|
|
|
|
void Redo()
|
|
{
|
|
if (g_UseSimulation2)
|
|
{
|
|
CSimulation2& sim = *g_Game->GetSimulation2();
|
|
CmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY);
|
|
debug_assert(!cmpTemplateManager.null());
|
|
|
|
m_EntityID = msg->id;
|
|
m_TemplateName = cmpTemplateManager->GetCurrentTemplateName(m_EntityID);
|
|
|
|
CmpPtr<ICmpOwnership> cmpOwner(sim, m_EntityID);
|
|
if (!cmpOwner.null())
|
|
m_Owner = cmpOwner->GetOwner();
|
|
|
|
CmpPtr<ICmpPosition> cmpPosition(sim, m_EntityID);
|
|
if (!cmpPosition.null())
|
|
{
|
|
m_Pos = cmpPosition->GetPosition();
|
|
m_Rot = cmpPosition->GetRotation();
|
|
}
|
|
|
|
g_Game->GetSimulation2()->DestroyEntity(m_EntityID);
|
|
|
|
return;
|
|
}
|
|
|
|
CUnit* unit = GetUnitManager().FindByID(msg->id);
|
|
if (! unit) return;
|
|
|
|
if (unit->GetEntity())
|
|
{
|
|
bool wasTerritoryCentre = unit->GetEntity()->m_base->m_isTerritoryCentre;
|
|
|
|
m_FrozenEntity.reset(new SimState::Entity( SimState::Entity::Freeze(unit) ));
|
|
unit->GetEntity()->Kill();
|
|
|
|
if (wasTerritoryCentre)
|
|
g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate();
|
|
}
|
|
else
|
|
{
|
|
m_FrozenNonentity.reset(new SimState::Nonentity( SimState::Nonentity::Freeze(unit) ));
|
|
GetUnitManager().RemoveUnit(unit);
|
|
}
|
|
}
|
|
|
|
void Undo()
|
|
{
|
|
if (g_UseSimulation2)
|
|
{
|
|
CSimulation2& sim = *g_Game->GetSimulation2();
|
|
entity_id_t ent = sim.AddEntity(m_TemplateName, m_EntityID);
|
|
if (ent == INVALID_ENTITY)
|
|
LOGERROR(L"Failed to load entity template '%ls'", m_TemplateName.c_str());
|
|
else
|
|
{
|
|
CmpPtr<ICmpPosition> cmpPosition(sim, m_EntityID);
|
|
if (!cmpPosition.null())
|
|
{
|
|
cmpPosition->JumpTo(m_Pos.X, m_Pos.Z);
|
|
cmpPosition->SetXZRotation(m_Rot.X, m_Rot.Z);
|
|
cmpPosition->SetYRotation(m_Rot.Y);
|
|
}
|
|
|
|
CmpPtr<ICmpOwnership> cmpOwner(sim, m_EntityID);
|
|
if (!cmpOwner.null())
|
|
cmpOwner->SetOwner(m_Owner);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (m_FrozenEntity.get())
|
|
{
|
|
CEntity* entity = m_FrozenEntity->Thaw();
|
|
|
|
if (entity && entity->m_base->m_isTerritoryCentre)
|
|
g_Game->GetWorld()->GetTerritoryManager()->DelayedRecalculate();
|
|
|
|
m_FrozenEntity.reset();
|
|
}
|
|
else if (m_FrozenNonentity.get())
|
|
{
|
|
m_FrozenNonentity->Thaw();
|
|
m_FrozenNonentity.reset();
|
|
}
|
|
}
|
|
};
|
|
END_COMMAND(DeleteObject)
|
|
|
|
|
|
}
|