LCOV - code coverage report
Current view: top level - source/tools/atlas/GameInterface/Handlers - ObjectHandlers.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 4 525 0.8 %
Date: 2023-01-19 00:18:29 Functions: 2 148 1.4 %

          Line data    Source code
       1             : /* Copyright (C) 2023 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include <cfloat>
      21             : #include <map>
      22             : 
      23             : #include "MessageHandler.h"
      24             : #include "../CommandProc.h"
      25             : #include "../SimState.h"
      26             : #include "../View.h"
      27             : 
      28             : #include "graphics/GameView.h"
      29             : #include "graphics/Model.h"
      30             : #include "graphics/ObjectBase.h"
      31             : #include "graphics/ObjectEntry.h"
      32             : #include "graphics/ObjectManager.h"
      33             : #include "graphics/Terrain.h"
      34             : #include "graphics/Unit.h"
      35             : #include "lib/utf8.h"
      36             : #include "maths/MathUtil.h"
      37             : #include "maths/Matrix3D.h"
      38             : #include "ps/CLogger.h"
      39             : #include "ps/Game.h"
      40             : #include "ps/World.h"
      41             : #include "renderer/Renderer.h"
      42             : #include "renderer/WaterManager.h"
      43             : #include "simulation2/Simulation2.h"
      44             : #include "simulation2/components/ICmpObstruction.h"
      45             : #include "simulation2/components/ICmpOwnership.h"
      46             : #include "simulation2/components/ICmpPosition.h"
      47             : #include "simulation2/components/ICmpPlayer.h"
      48             : #include "simulation2/components/ICmpPlayerManager.h"
      49             : #include "simulation2/components/ICmpSelectable.h"
      50             : #include "simulation2/components/ICmpTemplateManager.h"
      51             : #include "simulation2/components/ICmpVisual.h"
      52             : #include "simulation2/helpers/Selection.h"
      53             : #include "ps/XML/XMLWriter.h"
      54             : 
      55             : namespace AtlasMessage
      56             : {
      57             : 
      58             : namespace
      59             : {
      60           0 :     bool SortObjectsList(const sObjectsListItem& a, const sObjectsListItem& b)
      61             :     {
      62           0 :         return wcscmp(a.name.c_str(), b.name.c_str()) < 0;
      63             :     }
      64             : } // anonymous namespace
      65             : 
      66             : // Helpers for object constraints
      67           0 : bool CheckEntityObstruction(entity_id_t ent)
      68             : {
      69           0 :     CmpPtr<ICmpObstruction> cmpObstruction(*g_Game->GetSimulation2(), ent);
      70           0 :     if (cmpObstruction)
      71             :     {
      72           0 :         ICmpObstruction::EFoundationCheck result = cmpObstruction->CheckFoundation("default");
      73           0 :         if (result != ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
      74           0 :             return false;
      75             :     }
      76           0 :     return true;
      77             : }
      78             : 
      79           0 : void CheckObstructionAndUpdateVisual(entity_id_t id)
      80             : {
      81           0 :     CmpPtr<ICmpVisual> cmpVisual(*g_Game->GetSimulation2(), id);
      82           0 :     if (cmpVisual)
      83             :     {
      84           0 :         if (!CheckEntityObstruction(id))
      85           0 :             cmpVisual->SetShadingColor(fixed::FromDouble(1.4), fixed::FromDouble(0.4), fixed::FromDouble(0.4), fixed::FromDouble(1));
      86             :         else
      87           0 :             cmpVisual->SetShadingColor(fixed::FromDouble(1), fixed::FromDouble(1), fixed::FromDouble(1), fixed::FromDouble(1));
      88             :     }
      89           0 : }
      90             : 
      91           0 : QUERYHANDLER(GetObjectsList)
      92             : {
      93           0 :     std::vector<sObjectsListItem> objects;
      94             : 
      95           0 :     CmpPtr<ICmpTemplateManager> cmpTemplateManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
      96           0 :     if (cmpTemplateManager)
      97             :     {
      98           0 :         std::vector<std::string> names = cmpTemplateManager->FindTemplatesWithRootName(true, "Entity");
      99             : 
     100           0 :         for (std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it)
     101             :         {
     102           0 :             std::wstring name(it->begin(), it->end());
     103             : 
     104           0 :             sObjectsListItem e;
     105           0 :             e.id = name;
     106           0 :             if (name.substr(0, 6) == L"actor|")
     107             :             {
     108           0 :                 e.name = name.substr(6);
     109           0 :                 e.type = 1;
     110             :             }
     111             :             else
     112             :             {
     113           0 :                 e.name = name;
     114           0 :                 e.type = 0;
     115             :             }
     116           0 :             objects.push_back(e);
     117             :         }
     118             :     }
     119             : 
     120           0 :     std::sort(objects.begin(), objects.end(), SortObjectsList);
     121           0 :     msg->objects = objects;
     122           0 : }
     123             : 
     124             : 
     125           1 : static std::vector<entity_id_t> g_Selection;
     126             : typedef std::map<player_id_t, CColor> PlayerColorMap;
     127             : 
     128             : // Helper function to find color of player owning the given entity,
     129             : //  returns white if entity has no owner. Uses caching to avoid
     130             : //  expensive script calls.
     131           0 : static CColor GetOwnerPlayerColor(PlayerColorMap& colorMap, entity_id_t id)
     132             : {
     133             :     // Default color - white
     134           0 :     CColor color(1.0f, 1.0f, 1.0f, 1.0f);
     135             : 
     136           0 :     CSimulation2& sim = *g_Game->GetSimulation2();
     137           0 :     CmpPtr<ICmpOwnership> cmpOwnership(sim, id);
     138           0 :     if (cmpOwnership)
     139             :     {
     140           0 :         player_id_t owner = cmpOwnership->GetOwner();
     141           0 :         if (colorMap.find(owner) != colorMap.end())
     142           0 :             return colorMap[owner];
     143             :         else
     144             :         {
     145           0 :             CmpPtr<ICmpPlayerManager> cmpPlayerManager(sim, SYSTEM_ENTITY);
     146           0 :             entity_id_t playerEnt = cmpPlayerManager->GetPlayerByID(owner);
     147           0 :             CmpPtr<ICmpPlayer> cmpPlayer(sim, playerEnt);
     148           0 :             if (cmpPlayer)
     149             :             {
     150           0 :                 colorMap[owner] = cmpPlayer->GetDisplayedColor();
     151           0 :                 color = colorMap[owner];
     152             :             }
     153             :         }
     154             :     }
     155           0 :     return color;
     156             : }
     157             : 
     158           0 : MESSAGEHANDLER(SetSelectionPreview)
     159             : {
     160           0 :     CSimulation2& sim = *g_Game->GetSimulation2();
     161             : 
     162             :     // Cache player colors for performance
     163           0 :     PlayerColorMap playerColors;
     164             : 
     165             :     // Clear old selection rings
     166           0 :     for (size_t i = 0; i < g_Selection.size(); ++i)
     167             :     {
     168             :         // We can't set only alpha here, because that won't trigger desaturation
     169             :         //  so we set the complete color (not too evil since it's cached)
     170           0 :         CmpPtr<ICmpSelectable> cmpSelectable(sim, g_Selection[i]);
     171           0 :         if (cmpSelectable)
     172             :         {
     173           0 :             CColor color = GetOwnerPlayerColor(playerColors, g_Selection[i]);
     174           0 :             color.a = 0.0f;
     175           0 :             cmpSelectable->SetSelectionHighlight(color, false);
     176             :         }
     177             :     }
     178             : 
     179           0 :     g_Selection = *msg->ids;
     180             : 
     181             :     // Set new selection rings
     182           0 :     for (size_t i = 0; i < g_Selection.size(); ++i)
     183             :     {
     184           0 :         CmpPtr<ICmpSelectable> cmpSelectable(sim, g_Selection[i]);
     185           0 :         if (cmpSelectable)
     186           0 :             cmpSelectable->SetSelectionHighlight(GetOwnerPlayerColor(playerColors, g_Selection[i]), true);
     187             :     }
     188           0 : }
     189             : 
     190           0 : QUERYHANDLER(GetObjectSettings)
     191             : {
     192           0 :     AtlasView* view = AtlasView::GetView(msg->view);
     193           0 :     CSimulation2* simulation = view->GetSimulation2();
     194             : 
     195           0 :     sObjectSettings settings;
     196           0 :     settings.player = 0;
     197             : 
     198           0 :     CmpPtr<ICmpOwnership> cmpOwnership(*simulation, view->GetEntityId(msg->id));
     199           0 :     if (cmpOwnership)
     200             :     {
     201           0 :         int32_t player = cmpOwnership->GetOwner();
     202           0 :         if (player != -1)
     203           0 :             settings.player = player;
     204             :     }
     205             : 
     206             :     // TODO: selections
     207             : 
     208             : /*
     209             :     // Get the unit's possible variants and selected variants
     210             :     std::vector<std::vector<CStr> > groups = unit->GetObject().m_Base->GetVariantGroups();
     211             :     const std::set<CStr>& selections = unit->GetActorSelections();
     212             : 
     213             :     // Iterate over variant groups
     214             :     std::vector<std::vector<std::wstring> > variantgroups;
     215             :     std::set<std::wstring> selections_set;
     216             :     variantgroups.reserve(groups.size());
     217             :     for (size_t i = 0; i < groups.size(); ++i)
     218             :     {
     219             :         // Copy variants into output structure
     220             : 
     221             :         std::vector<std::wstring> group;
     222             :         group.reserve(groups[i].size());
     223             :         int choice = -1;
     224             : 
     225             :         for (size_t j = 0; j < groups[i].size(); ++j)
     226             :         {
     227             :             group.push_back(CStrW(groups[i][j]));
     228             : 
     229             :             // Find the first string in 'selections' that matches one of this
     230             :             // group's variants
     231             :             if (choice == -1)
     232             :                 if (selections.find(groups[i][j]) != selections.end())
     233             :                     choice = (int)j;
     234             :         }
     235             : 
     236             :         // Assuming one of the variants was selected (which it really ought
     237             :         // to be), remember that one's name
     238             :         if (choice != -1)
     239             :             selections_set.insert(CStrW(groups[i][choice]));
     240             : 
     241             :         variantgroups.push_back(group);
     242             :     }
     243             : 
     244             :     settings.variantgroups = variantgroups;
     245             :     settings.selections = std::vector<std::wstring> (selections_set.begin(), selections_set.end()); // convert set->vector
     246             : */
     247             : 
     248           0 :     msg->settings = settings;
     249           0 : }
     250             : 
     251           0 : QUERYHANDLER(GetObjectMapSettings)
     252             : {
     253           0 :     std::vector<entity_id_t> ids = *msg->ids;
     254             : 
     255           0 :     CmpPtr<ICmpTemplateManager> cmpTemplateManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     256           0 :     ENSURE(cmpTemplateManager);
     257             : 
     258           0 :     XMLWriter_File exampleFile;
     259             :     {
     260           0 :         XMLWriter_Element entitiesTag(exampleFile, "Entities");
     261             :         {
     262           0 :             for (entity_id_t id : ids)
     263             :             {
     264           0 :                 XMLWriter_Element entityTag(exampleFile, "Entity");
     265             :                 {
     266             :                     //Template name
     267           0 :                     entityTag.Setting("Template", cmpTemplateManager->GetCurrentTemplateName(id));
     268             : 
     269             :                     //Player
     270           0 :                     CmpPtr<ICmpOwnership> cmpOwnership(*g_Game->GetSimulation2(), id);
     271           0 :                     if (cmpOwnership)
     272           0 :                         entityTag.Setting("Player", static_cast<int>(cmpOwnership->GetOwner()));
     273             : 
     274             :                     //Adding position to make some relative position later
     275           0 :                     CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), id);
     276           0 :                     if (cmpPosition)
     277             :                     {
     278           0 :                         CFixedVector3D pos = cmpPosition->GetPosition();
     279           0 :                         CFixedVector3D rot = cmpPosition->GetRotation();
     280             :                         {
     281           0 :                             XMLWriter_Element positionTag(exampleFile, "Position");
     282           0 :                             positionTag.Attribute("x", pos.X);
     283           0 :                             positionTag.Attribute("z", pos.Z);
     284             :                             // TODO: height offset etc
     285             :                         }
     286             :                         {
     287           0 :                             XMLWriter_Element orientationTag(exampleFile, "Orientation");
     288           0 :                             orientationTag.Attribute("y", rot.Y);
     289             :                             // TODO: X, Z maybe
     290             :                         }
     291             :                     }
     292             : 
     293             :                     // Adding actor seed
     294           0 :                     CmpPtr<ICmpVisual> cmpVisual(*g_Game->GetSimulation2(), id);
     295           0 :                     if (cmpVisual)
     296           0 :                         entityTag.Setting("ActorSeed", static_cast<unsigned int>(cmpVisual->GetActorSeed()));
     297             :                 }
     298             :             }
     299             :         }
     300             :     }
     301             : 
     302           0 :     const CStr& data = exampleFile.GetOutput();
     303           0 :     msg->xmldata = data.FromUTF8();
     304           0 : }
     305             : 
     306             : 
     307           0 : BEGIN_COMMAND(SetObjectSettings)
     308             : {
     309             :     player_id_t m_PlayerOld, m_PlayerNew;
     310             :     std::set<CStr> m_SelectionsOld, m_SelectionsNew;
     311             : 
     312           0 :     void Do()
     313             :     {
     314           0 :         sObjectSettings settings = msg->settings;
     315             : 
     316           0 :         AtlasView* view = AtlasView::GetView(msg->view);
     317           0 :         CSimulation2* simulation = view->GetSimulation2();
     318             : 
     319           0 :         CmpPtr<ICmpOwnership> cmpOwnership(*simulation, view->GetEntityId(msg->id));
     320           0 :         m_PlayerOld = 0;
     321           0 :         if (cmpOwnership)
     322             :         {
     323           0 :             int32_t player = cmpOwnership->GetOwner();
     324           0 :             if (player != -1)
     325           0 :                 m_PlayerOld = player;
     326             :         }
     327             : 
     328             :         // TODO: selections
     329             : //      m_SelectionsOld = unit->GetActorSelections();
     330             : 
     331           0 :         m_PlayerNew = (player_id_t)settings.player;
     332             : 
     333           0 :         std::vector<std::wstring> selections = *settings.selections;
     334           0 :         for (std::vector<std::wstring>::iterator it = selections.begin(); it != selections.end(); ++it)
     335             :         {
     336           0 :             m_SelectionsNew.insert(CStrW(*it).ToUTF8());
     337             :         }
     338             : 
     339           0 :         Redo();
     340           0 :     }
     341             : 
     342           0 :     void Redo()
     343             :     {
     344           0 :         Set(m_PlayerNew, m_SelectionsNew);
     345           0 :     }
     346             : 
     347           0 :     void Undo()
     348             :     {
     349           0 :         Set(m_PlayerOld, m_SelectionsOld);
     350           0 :     }
     351             : 
     352             : private:
     353           0 :     void Set(player_id_t player, const std::set<CStr>& UNUSED(selections))
     354             :     {
     355           0 :         AtlasView* view = AtlasView::GetView(msg->view);
     356           0 :         CSimulation2* simulation = view->GetSimulation2();
     357             : 
     358           0 :         CmpPtr<ICmpOwnership> cmpOwnership(*simulation, view->GetEntityId(msg->id));
     359           0 :         if (cmpOwnership)
     360           0 :             cmpOwnership->SetOwner(player);
     361             : 
     362             :         // TODO: selections
     363             : //      unit->SetActorSelections(selections);
     364           0 :     }
     365             : };
     366           0 : END_COMMAND(SetObjectSettings);
     367             : 
     368             : //////////////////////////////////////////////////////////////////////////
     369             : 
     370           1 : static CStrW g_PreviewUnitName;
     371             : static entity_id_t g_PreviewEntityID = INVALID_ENTITY;
     372           1 : static std::vector<entity_id_t> g_PreviewEntitiesID;
     373             : 
     374           0 : static CVector3D GetUnitPos(const Position& pos, bool floating)
     375             : {
     376           0 :     static CVector3D vec;
     377           0 :     vec = pos.GetWorldSpace(vec, floating); // if msg->pos is 'Unchanged', use the previous pos
     378             : 
     379             :     // Clamp the position to the edges of the world:
     380             : 
     381             :     // Use 'Clamp' with a value slightly less than the width, so that converting
     382             :     // to integer (rounding towards zero) will put it on the tile inside the edge
     383             :     // instead of just outside
     384           0 :     float mapWidth = (g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide()-1)*TERRAIN_TILE_SIZE;
     385           0 :     float delta = 1e-6f; // fraction of map width - must be > FLT_EPSILON
     386             : 
     387           0 :     float xOnMap = Clamp(vec.X, 0.f, mapWidth * (1.f - delta));
     388           0 :     float zOnMap = Clamp(vec.Z, 0.f, mapWidth * (1.f - delta));
     389             : 
     390             :     // Don't waste time with GetExactGroundLevel unless we've changed
     391           0 :     if (xOnMap != vec.X || zOnMap != vec.Z)
     392             :     {
     393           0 :         vec.X = xOnMap;
     394           0 :         vec.Z = zOnMap;
     395           0 :         vec.Y = g_Game->GetWorld()->GetTerrain()->GetExactGroundLevel(xOnMap, zOnMap);
     396             :     }
     397             : 
     398           0 :     return vec;
     399             : }
     400             : 
     401           0 : QUERYHANDLER(GetCurrentSelection)
     402             : {
     403           0 :     msg->ids = g_Selection;
     404           0 : }
     405             : 
     406           0 : MESSAGEHANDLER(ObjectPreviewToEntity)
     407             : {
     408             :     UNUSED2(msg);
     409             : 
     410           0 :     if (g_PreviewEntitiesID.size() == 0)
     411           0 :         return;
     412             : 
     413           0 :     CmpPtr<ICmpTemplateManager> cmpTemplateManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     414           0 :     ENSURE(cmpTemplateManager);
     415             : 
     416           0 :     PlayerColorMap playerColor;
     417             : 
     418             :     //I need to re create the objects finally delete preview objects
     419           0 :     for (entity_id_t ent : g_PreviewEntitiesID)
     420             :     {
     421             :         //Get template name (without the "preview|" prefix)
     422           0 :         std::wstring wTemplateName = wstring_from_utf8(cmpTemplateManager->GetCurrentTemplateName(ent).substr(8));
     423             :         //Create new entity
     424           0 :         entity_id_t new_ent = g_Game->GetSimulation2()->AddEntity(wTemplateName);
     425           0 :         if (new_ent == INVALID_ENTITY)
     426           0 :             continue;
     427             : 
     428             :         //get position, get rotation
     429           0 :         CmpPtr<ICmpPosition> cmpPositionNew(*g_Game->GetSimulation2(), new_ent);
     430           0 :         CmpPtr<ICmpPosition> cmpPositionOld(*g_Game->GetSimulation2(), ent);
     431             : 
     432           0 :         if (cmpPositionNew && cmpPositionOld)
     433             :         {
     434           0 :             CVector3D pos = cmpPositionOld->GetPosition();
     435           0 :             cmpPositionNew->JumpTo(entity_pos_t::FromFloat(pos.X), entity_pos_t::FromFloat(pos.Z));
     436             : 
     437             :             //now rotate
     438           0 :             CFixedVector3D rotation = cmpPositionOld->GetRotation();
     439           0 :             cmpPositionNew->SetYRotation(rotation.Y);
     440             :         }
     441             : 
     442             :         //get owner
     443           0 :         CmpPtr<ICmpOwnership> cmpOwnershipNew(*g_Game->GetSimulation2(), new_ent);
     444           0 :         CmpPtr<ICmpOwnership> cmpOwnershipOld(*g_Game->GetSimulation2(), ent);
     445           0 :         if (cmpOwnershipNew && cmpOwnershipOld)
     446           0 :             cmpOwnershipNew->SetOwner(cmpOwnershipOld->GetOwner());
     447             : 
     448             :         //getVisual
     449           0 :         CmpPtr<ICmpVisual> cmpVisualNew(*g_Game->GetSimulation2(), new_ent);
     450           0 :         CmpPtr<ICmpVisual> cmpVisualOld(*g_Game->GetSimulation2(), ent);
     451           0 :         if (cmpVisualNew && cmpVisualOld)
     452           0 :             cmpVisualNew->SetActorSeed(cmpVisualOld->GetActorSeed());
     453             : 
     454             :         //Update g_selectedObject and higligth
     455           0 :         g_Selection.push_back(new_ent);
     456           0 :         CmpPtr<ICmpSelectable> cmpSelectable(*g_Game->GetSimulation2(), new_ent);
     457           0 :         if (cmpSelectable)
     458           0 :             cmpSelectable->SetSelectionHighlight(GetOwnerPlayerColor(playerColor, new_ent), true);
     459             : 
     460           0 :         g_Game->GetSimulation2()->DestroyEntity(ent);
     461             :     }
     462           0 :     g_PreviewEntitiesID.clear();
     463             : 
     464             : }
     465             : 
     466           0 : MESSAGEHANDLER(MoveObjectPreview)
     467             : {
     468           0 :     if (g_PreviewEntitiesID.size()==0)
     469           0 :         return;
     470             : 
     471             :     //TODO:Change pivot
     472           0 :     entity_id_t referenceEntity = *g_PreviewEntitiesID.begin();
     473             : 
     474             :     // All selected objects move relative to a pivot object,
     475             :     //  so get its position and whether it's floating
     476           0 :     CFixedVector3D referencePos;
     477             : 
     478           0 :     CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), referenceEntity);
     479           0 :     if (cmpPosition && cmpPosition->IsInWorld())
     480           0 :         referencePos = cmpPosition->GetPosition();
     481             : 
     482             : 
     483             :     // Calculate directional vector of movement for pivot object,
     484             :     //  we apply the same movement to all objects
     485           0 :     CVector3D targetPos = GetUnitPos(msg->pos, true);
     486           0 :     CFixedVector3D fTargetPos(entity_pos_t::FromFloat(targetPos.X), entity_pos_t::FromFloat(targetPos.Y), entity_pos_t::FromFloat(targetPos.Z));
     487           0 :     CFixedVector3D dir = fTargetPos - referencePos;
     488             : 
     489           0 :     for (const entity_id_t id : g_PreviewEntitiesID)
     490             :     {
     491           0 :         CmpPtr<ICmpPosition> cmpPreviewPosition(*g_Game->GetSimulation2(), id);
     492           0 :         if (cmpPreviewPosition)
     493             :         {
     494           0 :             CFixedVector3D posFinal;
     495           0 :             if (cmpPreviewPosition->IsInWorld())
     496             :             {
     497             :                 // Calculate this object's position
     498           0 :                 CFixedVector3D posFixed = cmpPreviewPosition->GetPosition();
     499           0 :                 posFinal = posFixed + dir;
     500             :             }
     501           0 :             cmpPreviewPosition->JumpTo(posFinal.X, posFinal.Z);
     502             :         }
     503             : 
     504           0 :         CheckObstructionAndUpdateVisual(id);
     505             :     }
     506             : }
     507             : 
     508           0 : MESSAGEHANDLER(ObjectPreview)
     509             : {
     510             :     // If the selection has changed...
     511           0 :     if (*msg->id != g_PreviewUnitName || (!msg->cleanObjectPreviews))
     512             :     {
     513             :         // Delete old entity
     514           0 :         if (g_PreviewEntityID != INVALID_ENTITY && msg->cleanObjectPreviews)
     515             :         {
     516             :             //Time to delete all preview objects
     517           0 :             for (entity_id_t ent : g_PreviewEntitiesID)
     518           0 :                 g_Game->GetSimulation2()->DestroyEntity(ent);
     519           0 :             g_PreviewEntitiesID.clear();
     520             :         }
     521             : 
     522             :         // Create the new entity
     523           0 :         if ((*msg->id).empty())
     524           0 :             g_PreviewEntityID = INVALID_ENTITY;
     525             :         else
     526             :         {
     527           0 :             g_PreviewEntityID = g_Game->GetSimulation2()->AddLocalEntity(L"preview|" + *msg->id);
     528           0 :             g_PreviewEntitiesID.push_back(g_PreviewEntityID);
     529             :         }
     530             : 
     531             : 
     532           0 :         g_PreviewUnitName = *msg->id;
     533             :     }
     534             : 
     535           0 :     if (g_PreviewEntityID != INVALID_ENTITY)
     536             :     {
     537             :         // Update the unit's position and orientation:
     538             : 
     539           0 :         CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), g_PreviewEntityID);
     540           0 :         if (cmpPosition)
     541             :         {
     542           0 :             CVector3D pos = GetUnitPos(msg->pos, cmpPosition->CanFloat());
     543           0 :             cmpPosition->JumpTo(entity_pos_t::FromFloat(pos.X), entity_pos_t::FromFloat(pos.Z));
     544             : 
     545             :             float angle;
     546           0 :             if (msg->usetarget)
     547             :             {
     548             :                 // Aim from pos towards msg->target
     549           0 :                 CVector3D target = msg->target->GetWorldSpace(pos.Y);
     550           0 :                 angle = atan2(target.X-pos.X, target.Z-pos.Z);
     551             :             }
     552             :             else
     553             :             {
     554           0 :                 angle = msg->angle;
     555             :             }
     556             : 
     557           0 :             cmpPosition->SetYRotation(entity_angle_t::FromFloat(angle));
     558             :         }
     559             : 
     560             :         // TODO: handle random variations somehow
     561             : 
     562           0 :         CmpPtr<ICmpVisual> cmpVisual(*g_Game->GetSimulation2(), g_PreviewEntityID);
     563           0 :         if (cmpVisual)
     564           0 :             cmpVisual->SetActorSeed(msg->actorseed);
     565             : 
     566           0 :         CmpPtr<ICmpOwnership> cmpOwnership(*g_Game->GetSimulation2(), g_PreviewEntityID);
     567           0 :         if (cmpOwnership)
     568           0 :             cmpOwnership->SetOwner((player_id_t)msg->settings->player);
     569             : 
     570           0 :         CheckObstructionAndUpdateVisual(g_PreviewEntityID);
     571             :     }
     572           0 : }
     573             : 
     574           0 : BEGIN_COMMAND(CreateObject)
     575             : {
     576             :     CVector3D m_Pos;
     577             :     float m_Angle;
     578             :     player_id_t m_Player;
     579             :     entity_id_t m_EntityID;
     580             :     u32 m_ActorSeed;
     581             : 
     582           0 :     void Do()
     583             :     {
     584             :         // Calculate the position/orientation to create this unit with
     585             : 
     586           0 :         m_Pos = GetUnitPos(msg->pos, true); // don't really care about floating
     587             : 
     588           0 :         if (msg->usetarget)
     589             :         {
     590             :             // Aim from m_Pos towards msg->target
     591           0 :             CVector3D target = msg->target->GetWorldSpace(m_Pos.Y);
     592           0 :             m_Angle = atan2(target.X-m_Pos.X, target.Z-m_Pos.Z);
     593             :         }
     594             :         else
     595             :         {
     596           0 :             m_Angle = msg->angle;
     597             :         }
     598             : 
     599           0 :         m_Player = (player_id_t)msg->settings->player;
     600           0 :         m_ActorSeed = msg->actorseed;
     601             :         // TODO: variation/selection strings
     602             : 
     603           0 :         Redo();
     604           0 :     }
     605             : 
     606           0 :     void Redo()
     607             :     {
     608           0 :         m_EntityID = g_Game->GetSimulation2()->AddEntity(*msg->id);
     609           0 :         if (m_EntityID == INVALID_ENTITY)
     610           0 :             return;
     611             : 
     612           0 :         CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), m_EntityID);
     613           0 :         if (cmpPosition)
     614             :         {
     615           0 :             cmpPosition->JumpTo(entity_pos_t::FromFloat(m_Pos.X), entity_pos_t::FromFloat(m_Pos.Z));
     616           0 :             cmpPosition->SetYRotation(entity_angle_t::FromFloat(m_Angle));
     617             :         }
     618             : 
     619           0 :         CmpPtr<ICmpOwnership> cmpOwnership(*g_Game->GetSimulation2(), m_EntityID);
     620           0 :         if (cmpOwnership)
     621           0 :             cmpOwnership->SetOwner(m_Player);
     622             : 
     623           0 :         CmpPtr<ICmpVisual> cmpVisual(*g_Game->GetSimulation2(), m_EntityID);
     624           0 :         if (cmpVisual)
     625             :         {
     626           0 :             cmpVisual->SetActorSeed(m_ActorSeed);
     627             :             // TODO: variation/selection strings
     628             :         }
     629             :     }
     630             : 
     631           0 :     void Undo()
     632             :     {
     633           0 :         if (m_EntityID != INVALID_ENTITY)
     634             :         {
     635           0 :             g_Game->GetSimulation2()->DestroyEntity(m_EntityID);
     636           0 :             m_EntityID = INVALID_ENTITY;
     637             :         }
     638           0 :     }
     639             : };
     640           0 : END_COMMAND(CreateObject)
     641             : 
     642             : 
     643           0 : QUERYHANDLER(PickObject)
     644             : {
     645             :     float x, y;
     646           0 :     msg->pos->GetScreenSpace(x, y);
     647             : 
     648             :     // Normally this function would be called with a player ID to check LOS,
     649             :     //  but in Atlas the entire map is revealed, so just pass INVALID_PLAYER
     650           0 :     entity_id_t ent = EntitySelection::PickEntityAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, INVALID_PLAYER, msg->selectActors);;
     651             : 
     652           0 :     if (ent == INVALID_ENTITY)
     653           0 :         msg->id = INVALID_ENTITY;
     654             :     else
     655             :     {
     656           0 :         msg->id = ent;
     657             :         // Calculate offset of object from original mouse click position
     658             :         //  so it gets moved by that offset
     659           0 :         CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), ent);
     660           0 :         if (!cmpPosition || !cmpPosition->IsInWorld())
     661             :         {
     662             :             // error
     663           0 :             msg->offsetx = msg->offsety = 0;
     664             :         }
     665             :         else
     666             :         {
     667           0 :             CFixedVector3D fixed = cmpPosition->GetPosition();
     668           0 :             CVector3D centre = CVector3D(fixed.X.ToFloat(), fixed.Y.ToFloat(), fixed.Z.ToFloat());
     669             : 
     670             :             float cx, cy;
     671           0 :             g_Game->GetView()->GetCamera()->GetScreenCoordinates(centre, cx, cy);
     672             : 
     673           0 :             msg->offsetx = (int)(cx - x);
     674           0 :             msg->offsety = (int)(cy - y);
     675             :         }
     676             :     }
     677           0 : }
     678             : 
     679             : 
     680           0 : QUERYHANDLER(PickObjectsInRect)
     681             : {
     682             :     float x0, y0, x1, y1;
     683           0 :     msg->start->GetScreenSpace(x0, y0);
     684           0 :     msg->end->GetScreenSpace(x1, y1);
     685             : 
     686             :     // Since owner selections are meaningless in Atlas, use INVALID_PLAYER
     687           0 :     msg->ids = EntitySelection::PickEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x0, y0, x1, y1, INVALID_PLAYER, msg->selectActors);
     688           0 : }
     689             : 
     690             : 
     691           0 : QUERYHANDLER(PickSimilarObjects)
     692             : {
     693           0 :     CmpPtr<ICmpTemplateManager> cmpTemplateManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     694           0 :     ENSURE(cmpTemplateManager);
     695             : 
     696           0 :     entity_id_t ent = msg->id;
     697           0 :     std::string templateName = cmpTemplateManager->GetCurrentTemplateName(ent);
     698             : 
     699             :     // If unit has ownership, only pick units from the same player
     700           0 :     player_id_t owner = INVALID_PLAYER;
     701           0 :     CmpPtr<ICmpOwnership> cmpOwnership(*g_Game->GetSimulation2(), ent);
     702           0 :     if (cmpOwnership)
     703           0 :         owner = cmpOwnership->GetOwner();
     704             : 
     705           0 :     msg->ids = EntitySelection::PickSimilarEntities(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), templateName, owner, false, true, true, false);
     706           0 : }
     707             : 
     708           0 : MESSAGEHANDLER(ResetSelectionColor)
     709             : {
     710             :     UNUSED2(msg);
     711             : 
     712           0 :     for (entity_id_t ent : g_Selection)
     713             :     {
     714           0 :         CmpPtr<ICmpVisual> cmpVisual(*g_Game->GetSimulation2(), ent);
     715           0 :         if (cmpVisual)
     716           0 :             cmpVisual->SetShadingColor(fixed::FromDouble(1), fixed::FromDouble(1), fixed::FromDouble(1), fixed::FromDouble(1));
     717             :     }
     718           0 : }
     719             : 
     720           0 : BEGIN_COMMAND(MoveObjects)
     721             : {
     722             :     // Mapping from object to position
     723             :     std::map<entity_id_t, CVector3D> m_PosOld, m_PosNew;
     724             : 
     725           0 :     void Do()
     726             :     {
     727           0 :         std::vector<entity_id_t> ids = *msg->ids;
     728             : 
     729             :         // All selected objects move relative to a pivot object,
     730             :         //  so get its position and whether it's floating
     731           0 :         CVector3D pivotPos(0, 0, 0);
     732           0 :         bool pivotFloating = false;
     733             : 
     734           0 :         CmpPtr<ICmpPosition> cmpPositionPivot(*g_Game->GetSimulation2(), (entity_id_t)msg->pivot);
     735           0 :         if (cmpPositionPivot && cmpPositionPivot->IsInWorld())
     736             :         {
     737           0 :             pivotFloating = cmpPositionPivot->CanFloat();
     738           0 :             CFixedVector3D pivotFixed = cmpPositionPivot->GetPosition();
     739           0 :             pivotPos = CVector3D(pivotFixed.X.ToFloat(), pivotFixed.Y.ToFloat(), pivotFixed.Z.ToFloat());
     740             :         }
     741             : 
     742             :         // Calculate directional vector of movement for pivot object,
     743             :         //  we apply the same movement to all objects
     744           0 :         CVector3D targetPos = GetUnitPos(msg->pos, pivotFloating);
     745           0 :         CVector3D dir = targetPos - pivotPos;
     746             : 
     747           0 :         for (entity_id_t id : ids)
     748             :         {
     749           0 :             CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), id);
     750           0 :             if (!cmpPosition || !cmpPosition->IsInWorld())
     751             :             {
     752             :                 // error
     753           0 :                 m_PosOld[id] = m_PosNew[id] = CVector3D(0, 0, 0);
     754             :             }
     755             :             else
     756             :             {
     757             :                 // Calculate this object's position
     758           0 :                 CFixedVector3D posFixed = cmpPosition->GetPosition();
     759           0 :                 CVector3D pos = CVector3D(posFixed.X.ToFloat(), posFixed.Y.ToFloat(), posFixed.Z.ToFloat());
     760           0 :                 m_PosNew[id] = pos + dir;
     761           0 :                 m_PosOld[id] = pos;
     762             :             }
     763             :         }
     764             : 
     765           0 :         SetPos(m_PosNew);
     766           0 :     }
     767             : 
     768           0 :     void SetPos(const std::map<entity_id_t, CVector3D>& map)
     769             :     {
     770           0 :         for (const std::pair<const entity_id_t, CVector3D>& p : map)
     771             :         {
     772           0 :             CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), p.first);
     773           0 :             if (!cmpPosition)
     774           0 :                 return;
     775             : 
     776             :             // Set 2D position, ignoring height
     777           0 :             cmpPosition->JumpTo(entity_pos_t::FromFloat(p.second.X), entity_pos_t::FromFloat(p.second.Z));
     778             : 
     779           0 :             CheckObstructionAndUpdateVisual(p.first);
     780             :         }
     781             :     }
     782             : 
     783           0 :     void Redo()
     784             :     {
     785           0 :         SetPos(m_PosNew);
     786           0 :     }
     787             : 
     788           0 :     void Undo()
     789             :     {
     790           0 :         SetPos(m_PosOld);
     791           0 :     }
     792             : 
     793           0 :     void MergeIntoPrevious(cMoveObjects* prev)
     794             :     {
     795             :         // TODO: do something valid if prev selection != this selection
     796           0 :         ENSURE(*(prev->msg->ids) == *(msg->ids));
     797           0 :         prev->m_PosNew = m_PosNew;
     798           0 :     }
     799             : };
     800           0 : END_COMMAND(MoveObjects)
     801             : 
     802           0 : BEGIN_COMMAND(RotateObjectsFromCenterPoint)
     803             : {
     804             :     std::map<entity_id_t, CVector3D> m_PosOld, m_PosNew;
     805             :     std::map<entity_id_t, float> m_AngleOld, m_AngleNew;
     806             :     CVector3D m_CenterPoint;
     807             :     float m_AngleInitialRotation;
     808             : 
     809           0 :     void Do()
     810             :     {
     811           0 :         std::vector<entity_id_t> ids = *msg->ids;
     812             : 
     813           0 :         CVector3D minPos;
     814           0 :         CVector3D maxPos;
     815             : 
     816           0 :         bool first = true;
     817             : 
     818             :         // Compute min position and max position
     819           0 :         for (entity_id_t id : ids)
     820             :         {
     821           0 :             CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), id);
     822           0 :             if (!cmpPosition)
     823           0 :                 continue;
     824             : 
     825           0 :             CVector3D pos = cmpPosition->GetPosition();
     826             : 
     827           0 :             m_PosOld[id] = cmpPosition->GetPosition();
     828           0 :             m_AngleOld[id] = cmpPosition->GetRotation().Y.ToFloat();
     829             : 
     830           0 :             if (first)
     831             :             {
     832           0 :                 first = false;
     833           0 :                 minPos = pos;
     834           0 :                 maxPos = pos;
     835           0 :                 m_CenterPoint.Y = pos.Y;
     836           0 :                 continue;
     837             :             }
     838             : 
     839           0 :             if (pos.X < minPos.X)
     840           0 :                 minPos.X = pos.X;
     841             : 
     842           0 :             if (pos.X > maxPos.X)
     843           0 :                 maxPos.X = pos.X;
     844             : 
     845           0 :             if (pos.Z < minPos.Z)
     846           0 :                 minPos.Z = pos.Z;
     847             : 
     848           0 :             if (pos.Z > maxPos.Z)
     849           0 :                 maxPos.Z = pos.Z;
     850             :         }
     851             : 
     852             :         // Calculate objects center point
     853           0 :         m_CenterPoint.X = minPos.X + ((maxPos.X - minPos.X) * 0.5);
     854           0 :         m_CenterPoint.Z = minPos.Z + ((maxPos.Z - minPos.Z) * 0.5);
     855             : 
     856           0 :         CVector3D target = msg->target->GetWorldSpace(m_CenterPoint.Y);
     857           0 :         m_AngleInitialRotation = atan2(target.X-m_CenterPoint.X, target.Z-m_CenterPoint.Z);
     858           0 :     }
     859             : 
     860           0 :     void SetPos(const std::map<entity_id_t, CVector3D>& position, const std::map<entity_id_t, float>& angle)
     861             :     {
     862           0 :         for (const std::pair<const entity_id_t, CVector3D>& p : position)
     863             :         {
     864           0 :             CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), p.first);
     865           0 :             if (!cmpPosition)
     866           0 :                 return;
     867             : 
     868             :             // Set 2D position, ignoring height
     869           0 :             cmpPosition->JumpTo(entity_pos_t::FromFloat(p.second.X), entity_pos_t::FromFloat(p.second.Z));
     870             : 
     871           0 :             if (msg->rotateObject)
     872           0 :                 cmpPosition->SetYRotation(entity_angle_t::FromFloat(angle.at(p.first)));
     873             : 
     874             :         }
     875             : 
     876           0 :         for (const std::pair<const entity_id_t, CVector3D>& p : position)
     877           0 :             CheckObstructionAndUpdateVisual(p.first);
     878             :     }
     879             : 
     880           0 :     void Redo()
     881             :     {
     882           0 :         SetPos(m_PosNew, m_AngleNew);
     883           0 :     }
     884             : 
     885           0 :     void RecalculateRotation(Position newPoint)
     886             :     {
     887           0 :         std::vector<entity_id_t> ids = *msg->ids;
     888             : 
     889           0 :         CVector3D target = newPoint.GetWorldSpace(m_CenterPoint.Y);
     890           0 :         float newAngle = atan2(target.X-m_CenterPoint.X, target.Z-m_CenterPoint.Z);
     891             : 
     892           0 :         float globalAngle = m_AngleInitialRotation - newAngle;
     893             : 
     894             :         // Recalculate positions
     895           0 :         for (entity_id_t id : ids)
     896             :         {
     897           0 :             CVector3D pos = m_PosOld[id];
     898           0 :             float angle = atan2(pos.X - m_CenterPoint.X, pos.Z - m_CenterPoint.Z);
     899           0 :             float localAngle = angle + (globalAngle - angle);
     900           0 :             float xCos = cosf(localAngle);
     901           0 :             float xSin = sinf(localAngle);
     902             : 
     903           0 :             pos.X -= m_CenterPoint.X;
     904           0 :             pos.Z -= m_CenterPoint.Z;
     905             : 
     906           0 :             float newX = pos.X * xCos - pos.Z * xSin;
     907           0 :             float newZ = pos.X * xSin + pos.Z * xCos;
     908             : 
     909           0 :             pos.X = newX + m_CenterPoint.X;
     910           0 :             pos.Z = newZ + m_CenterPoint.Z;
     911             : 
     912           0 :             m_PosNew[id] = pos;
     913             : 
     914           0 :             m_AngleNew[id] = m_AngleOld[id] - globalAngle;
     915             :         }
     916             : 
     917           0 :         SetPos(m_PosNew, m_AngleNew);
     918           0 :     }
     919             : 
     920           0 :     void Undo()
     921             :     {
     922           0 :         SetPos(m_PosOld, m_AngleOld);
     923           0 :     }
     924             : 
     925           0 :     void MergeIntoPrevious(cRotateObjectsFromCenterPoint* prev)
     926             :     {
     927             :         // TODO: do something valid if prev unit != this unit
     928           0 :         ENSURE(*prev->msg->ids == *msg->ids);
     929           0 :         m_PosOld = prev->m_PosOld;
     930           0 :         m_AngleInitialRotation = prev->m_AngleInitialRotation;
     931           0 :         m_AngleOld = prev->m_AngleOld;
     932           0 :         m_CenterPoint = prev->m_CenterPoint;
     933             : 
     934           0 :         RecalculateRotation(msg->target);
     935           0 :     }
     936             : };
     937           0 : END_COMMAND(RotateObjectsFromCenterPoint)
     938             : 
     939           0 : BEGIN_COMMAND(RotateObject)
     940             : {
     941             :     std::map<entity_id_t, float> m_AngleOld, m_AngleNew;
     942             : 
     943           0 :     void Do()
     944             :     {
     945           0 :         std::vector<entity_id_t> ids = *msg->ids;
     946             : 
     947           0 :         for (entity_id_t id : ids)
     948             :         {
     949           0 :             CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), id);
     950           0 :             if (!cmpPosition)
     951           0 :                 return;
     952             : 
     953           0 :             m_AngleOld[id] = cmpPosition->GetRotation().Y.ToFloat();
     954             : 
     955           0 :             CMatrix3D transform = cmpPosition->GetInterpolatedTransform(0.f);
     956           0 :             CVector3D pos = transform.GetTranslation();
     957           0 :             CVector3D target = msg->target->GetWorldSpace(pos.Y);
     958           0 :             m_AngleNew[id] = atan2(target.X-pos.X, target.Z-pos.Z);
     959             :         }
     960             : 
     961           0 :         SetAngle(m_AngleNew);
     962             :     }
     963             : 
     964           0 :     void SetAngle(const std::map<entity_id_t, float>& angles)
     965             :     {
     966           0 :         for (const std::pair<const entity_id_t, float>& p : angles)
     967             :         {
     968           0 :             CmpPtr<ICmpPosition> cmpPosition(*g_Game->GetSimulation2(), p.first);
     969           0 :             if (!cmpPosition)
     970           0 :                 return;
     971             : 
     972           0 :             cmpPosition->SetYRotation(entity_angle_t::FromFloat(p.second));
     973             :         }
     974             :     }
     975             : 
     976           0 :     void Redo()
     977             :     {
     978           0 :         SetAngle(m_AngleNew);
     979           0 :     }
     980             : 
     981           0 :     void Undo()
     982             :     {
     983           0 :         SetAngle(m_AngleOld);
     984           0 :     }
     985             : 
     986           0 :     void MergeIntoPrevious(cRotateObject* prev)
     987             :     {
     988             :         // TODO: do something valid if prev unit != this unit
     989           0 :         ENSURE(*prev->msg->ids == *msg->ids);
     990           0 :         prev->m_AngleNew = m_AngleNew;
     991           0 :     }
     992             : };
     993           0 : END_COMMAND(RotateObject)
     994             : 
     995             : 
     996           0 : BEGIN_COMMAND(DeleteObjects)
     997             : {
     998             :     // Saved copy of the important aspects of a unit, to allow undo
     999           0 :     struct OldObject
    1000             :     {
    1001             :         entity_id_t entityID;
    1002             :         CStr templateName;
    1003             :         player_id_t owner;
    1004             :         CFixedVector3D pos;
    1005             :         CFixedVector3D rot;
    1006             :         u32 actorSeed;
    1007             :     };
    1008             : 
    1009             :     std::vector<OldObject> oldObjects;
    1010             : 
    1011           0 :     cDeleteObjects()
    1012           0 :     {
    1013           0 :     }
    1014             : 
    1015           0 :     void Do()
    1016             :     {
    1017           0 :         Redo();
    1018           0 :     }
    1019             : 
    1020           0 :     void Redo()
    1021             :     {
    1022           0 :         CSimulation2& sim = *g_Game->GetSimulation2();
    1023           0 :         CmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY);
    1024           0 :         ENSURE(cmpTemplateManager);
    1025             : 
    1026           0 :         std::vector<entity_id_t> ids = *msg->ids;
    1027           0 :         for (size_t i = 0; i < ids.size(); ++i)
    1028             :         {
    1029           0 :             OldObject obj;
    1030             : 
    1031           0 :             obj.entityID = (entity_id_t)ids[i];
    1032           0 :             obj.templateName = cmpTemplateManager->GetCurrentTemplateName(obj.entityID);
    1033             : 
    1034           0 :             CmpPtr<ICmpOwnership> cmpOwnership(sim, obj.entityID);
    1035           0 :             if (cmpOwnership)
    1036           0 :                 obj.owner = cmpOwnership->GetOwner();
    1037             : 
    1038           0 :             CmpPtr<ICmpPosition> cmpPosition(sim, obj.entityID);
    1039           0 :             if (cmpPosition)
    1040             :             {
    1041           0 :                 obj.pos = cmpPosition->GetPosition();
    1042           0 :                 obj.rot = cmpPosition->GetRotation();
    1043             :             }
    1044             : 
    1045           0 :             CmpPtr<ICmpVisual> cmpVisual(sim, obj.entityID);
    1046           0 :             if (cmpVisual)
    1047           0 :                 obj.actorSeed = cmpVisual->GetActorSeed();
    1048             : 
    1049           0 :             oldObjects.push_back(obj);
    1050           0 :             g_Game->GetSimulation2()->DestroyEntity(obj.entityID);
    1051             :         }
    1052           0 :         g_Game->GetSimulation2()->FlushDestroyedEntities();
    1053           0 :     }
    1054             : 
    1055           0 :     void Undo()
    1056             :     {
    1057           0 :         CSimulation2& sim = *g_Game->GetSimulation2();
    1058             : 
    1059           0 :         for (size_t i = 0; i < oldObjects.size(); ++i)
    1060             :         {
    1061           0 :             entity_id_t ent = sim.AddEntity(oldObjects[i].templateName.FromUTF8(), oldObjects[i].entityID);
    1062           0 :             if (ent == INVALID_ENTITY)
    1063             :             {
    1064           0 :                 LOGERROR("Failed to load entity template '%s'", oldObjects[i].templateName.c_str());
    1065             :             }
    1066             :             else
    1067             :             {
    1068           0 :                 CmpPtr<ICmpPosition> cmpPosition(sim, oldObjects[i].entityID);
    1069           0 :                 if (cmpPosition)
    1070             :                 {
    1071           0 :                     cmpPosition->JumpTo(oldObjects[i].pos.X, oldObjects[i].pos.Z);
    1072           0 :                     cmpPosition->SetXZRotation(oldObjects[i].rot.X, oldObjects[i].rot.Z);
    1073           0 :                     cmpPosition->SetYRotation(oldObjects[i].rot.Y);
    1074             :                 }
    1075             : 
    1076           0 :                 CmpPtr<ICmpOwnership> cmpOwnership(sim, oldObjects[i].entityID);
    1077           0 :                 if (cmpOwnership)
    1078           0 :                     cmpOwnership->SetOwner(oldObjects[i].owner);
    1079             : 
    1080           0 :                 CmpPtr<ICmpVisual> cmpVisual(sim, oldObjects[i].entityID);
    1081           0 :                 if (cmpVisual)
    1082           0 :                     cmpVisual->SetActorSeed(oldObjects[i].actorSeed);
    1083             :             }
    1084             :         }
    1085             : 
    1086           0 :         oldObjects.clear();
    1087           0 :     }
    1088             : };
    1089           0 : END_COMMAND(DeleteObjects)
    1090             : 
    1091           0 : QUERYHANDLER(GetPlayerObjects)
    1092             : {
    1093           0 :     std::vector<entity_id_t> ids;
    1094           0 :     player_id_t playerID = msg->player;
    1095             : 
    1096           0 :     const CSimulation2::InterfaceListUnordered& cmps = g_Game->GetSimulation2()->GetEntitiesWithInterfaceUnordered(IID_Ownership);
    1097           0 :     for (CSimulation2::InterfaceListUnordered::const_iterator eit = cmps.begin(); eit != cmps.end(); ++eit)
    1098             :     {
    1099           0 :         if (static_cast<ICmpOwnership*>(eit->second)->GetOwner() == playerID)
    1100             :         {
    1101           0 :             ids.push_back(eit->first);
    1102             :         }
    1103             :     }
    1104             : 
    1105           0 :     msg->ids = ids;
    1106           0 : }
    1107             : 
    1108           0 : MESSAGEHANDLER(SetBandbox)
    1109             : {
    1110           0 :     AtlasView::GetView_Game()->SetBandbox(msg->show, (float)msg->sx0, (float)msg->sy0, (float)msg->sx1, (float)msg->sy1);
    1111           0 : }
    1112             : 
    1113           0 : QUERYHANDLER(GetSelectedObjectsTemplateNames)
    1114             : {
    1115           0 :     std::vector<entity_id_t> ids = *msg->ids;
    1116           0 :     std::vector<std::string> names;
    1117             : 
    1118           0 :     CmpPtr<ICmpTemplateManager> cmpTemplateManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
    1119           0 :     ENSURE(cmpTemplateManager);
    1120             : 
    1121           0 :     for (size_t i = 0; i < ids.size(); ++i)
    1122             :     {
    1123           0 :         entity_id_t id = (entity_id_t)ids[i];
    1124           0 :         std::string templateName = cmpTemplateManager->GetCurrentTemplateName(id);
    1125           0 :         names.push_back(templateName);
    1126             :     }
    1127             : 
    1128           0 :     std::sort(names.begin(), names.end());
    1129           0 :     msg->names = names;
    1130           0 : }
    1131             : 
    1132           3 : } // namespace AtlasMessage

Generated by: LCOV version 1.13