Index: ps/trunk/binaries/data/mods/public/maps/scenario.rnc
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scenario.rnc
+++ ps/trunk/binaries/data/mods/public/maps/scenario.rnc
@@ -108,6 +108,12 @@
attribute uid { xsd:positiveInteger } &
} &
} &
+ element Turrets {
+ element Turret {
+ attribute turret { text } &
+ attribute uid { xsd:positiveInteger } &
+ } &
+ } &
element Actor {
attribute seed { xsd:integer }
}?
Index: ps/trunk/binaries/data/mods/public/maps/scenario.rng
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scenario.rng
+++ ps/trunk/binaries/data/mods/public/maps/scenario.rng
@@ -268,6 +268,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: ps/trunk/binaries/data/mods/public/simulation/components/TurretHolder.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/TurretHolder.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/TurretHolder.js
@@ -13,6 +13,7 @@
let points = this.template.TurretPoints;
for (let point in points)
this.turretPoints.push({
+ "name": point,
"offset": {
"x": +points[point].X,
"y": +points[point].Y,
@@ -121,6 +122,16 @@
}
/**
+ * @param {number} entity - The entityID of the entity.
+ * @param {String} turretName - The name of the turret point to occupy.
+ * @return {boolean} - Whether the occupation has succeeded.
+ */
+ OccupyNamedTurret(entity, turretName)
+ {
+ return this.OccupyTurret(entity, this.turretPoints.find(turret => turret.name == turretName));
+ }
+
+ /**
* Remove the entity from a turret.
* @param {number} entity - The specific entity to eject.
* @param {Object} turret - Optionally the turret to abandon.
@@ -189,6 +200,44 @@
}
/**
+ * @param {number} entity - The entity's id.
+ * @return {Object} - The turret this entity is positioned on, if applicable.
+ */
+ GetOccupiedTurretName(entity)
+ {
+ return this.GetOccupiedTurret(entity).name || "";
+ }
+
+ /**
+ * @return {number[]} - The turretted entityIDs.
+ */
+ GetEntities()
+ {
+ let entities = [];
+ for (let turretPoint of this.turretPoints)
+ if (turretPoint.entity)
+ entities.push(turretPoint.entity);
+ return entities;
+ }
+
+ /**
+ * Sets an init turret, present from game start. (E.g. set in Atlas.)
+ * @param {String} turretName - The name of the turret point to be used.
+ * @param {number} entity - The entity-ID to be placed.
+ */
+ SetInitEntity(turretName, entity)
+ {
+ if (!this.initTurrets)
+ this.initTurrets = new Map();
+
+ if (this.initTurrets.has(turretName))
+ warn("The turret position " + turretName + " of entity " +
+ this.entity + " is already set! Overwriting.");
+
+ this.initTurrets.set(turretName, entity);
+ }
+
+ /**
* We process EntityRenamed here because we need to be sure that we receive
* it after it is processed by GarrisonHolder.js.
* ToDo: Make this not needed by fully separating TurretHolder from GarrisonHolder.
@@ -221,6 +270,29 @@
for (let entity of msg.added)
this.OccupyTurret(entity);
}
+
+ /**
+ * Initialise the turreted units.
+ * Really ugly, but because GarrisonHolder is processed earlier, and also turrets
+ * entities on init, we can find an entity that already is present.
+ * In that case we reject and occupy.
+ */
+ OnGlobalInitGame(msg)
+ {
+ if (!this.initTurrets)
+ return;
+
+ for (let [turretPointName, entity] of this.initTurrets)
+ {
+ if (this.OccupiesTurret(entity))
+ this.LeaveTurret(entity);
+ if (!this.OccupyNamedTurret(entity, turretPointName))
+ warn("Entity " + entity + " could not occupy the turret point " +
+ turretPointName + " of turret holder " + this.entity + ".");
+ }
+
+ delete this.initTurrets;
+ }
}
TurretHolder.prototype.Schema =
Index: ps/trunk/binaries/data/mods/public/simulation/components/interfaces/TurretHolder.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/interfaces/TurretHolder.js
+++ ps/trunk/binaries/data/mods/public/simulation/components/interfaces/TurretHolder.js
@@ -1,5 +1,3 @@
-Engine.RegisterInterface("TurretHolder");
-
/**
* Message of the form { "added": number[], "removed": number[] }
* sent from the TurretHolder component to the current entity whenever the turrets change.
Index: ps/trunk/source/graphics/MapReader.cpp
===================================================================
--- ps/trunk/source/graphics/MapReader.cpp
+++ ps/trunk/source/graphics/MapReader.cpp
@@ -48,6 +48,7 @@
#include "simulation2/components/ICmpPlayerManager.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpTerrain.h"
+#include "simulation2/components/ICmpTurretHolder.h"
#include "simulation2/components/ICmpVisual.h"
#include "simulation2/components/ICmpWaterManager.h"
@@ -418,12 +419,14 @@
int el_template, el_player;
int el_position, el_orientation, el_obstruction;
int el_garrison;
+ int el_turrets;
int el_actor;
int at_x, at_y, at_z;
int at_group, at_group2;
int at_angle;
int at_uid;
int at_seed;
+ int at_turret;
XMBElementList nodes; // children of root
@@ -468,6 +471,7 @@
EL(player);
EL(position);
EL(garrison);
+ EL(turrets);
EL(orientation);
EL(obstruction);
EL(actor);
@@ -476,6 +480,7 @@
AT(angle);
AT(uid);
AT(seed);
+ AT(turret);
#undef AT
#undef EL
@@ -950,6 +955,7 @@
CStrW TemplateName;
int PlayerID = 0;
std::vector Garrison;
+ std::vector > Turrets;
CFixedVector3D Position;
CFixedVector3D Orientation;
long Seed = -1;
@@ -1009,6 +1015,20 @@
Garrison.push_back(attrs.GetNamedItem(at_uid).ToInt());
}
}
+ //
+ else if (element_name == el_turrets)
+ {
+ XMBElementList turrets = setting.GetChildNodes();
+ Turrets.reserve(turrets.size());
+ for (const XMBElement& turretPoint : turrets)
+ {
+ XMBAttributeList attrs = turretPoint.GetAttributes();
+ Turrets.push_back(std::make_pair(
+ attrs.GetNamedItem(at_turret),
+ attrs.GetNamedItem(at_uid).ToInt()
+ ));
+ }
+ }
//
else if (element_name == el_actor)
{
@@ -1054,6 +1074,16 @@
Garrison.clear();
}
+ if (!Turrets.empty())
+ {
+ CmpPtr cmpTurretHolder(sim, ent);
+ if (cmpTurretHolder)
+ cmpTurretHolder->SetInitEntities(Turrets);
+ else
+ LOGERROR("CXMLMapReader::ReadEntities() entity '%d' of player '%d' has no TurretHolder component and thus cannot use turrets.", ent, PlayerID);
+ Turrets.clear();
+ }
+
CmpPtr cmpObstruction(sim, ent);
if (cmpObstruction)
{
Index: ps/trunk/source/graphics/MapWriter.cpp
===================================================================
--- ps/trunk/source/graphics/MapWriter.cpp
+++ ps/trunk/source/graphics/MapWriter.cpp
@@ -44,6 +44,7 @@
#include "simulation2/components/ICmpOwnership.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpTemplateManager.h"
+#include "simulation2/components/ICmpTurretHolder.h"
#include "simulation2/components/ICmpVisual.h"
#include "simulation2/components/ICmpWaterManager.h"
@@ -353,7 +354,22 @@
{
XMLWriter_Element garrisonedEntityTag(xmlMapFile, "GarrisonedEntity");
garrisonedEntityTag.Attribute("uid", static_cast(garr_ent_id));
- // ToDo: We can store turret position as well.
+ }
+ }
+ }
+
+ CmpPtr cmpTurretHolder(sim, ent);
+ if (cmpTurretHolder)
+ {
+ std::vector > turrets = cmpTurretHolder->GetTurrets();
+ if (!turrets.empty())
+ {
+ XMLWriter_Element turretTag(xmlMapFile, "Turrets");
+ for (const std::pair& turret : turrets)
+ {
+ XMLWriter_Element turretedEntityTag(xmlMapFile, "Turret");
+ turretedEntityTag.Attribute("turret", turret.first);
+ turretedEntityTag.Attribute("uid", static_cast(turret.second));
}
}
}
Index: ps/trunk/source/simulation2/TypeList.h
===================================================================
--- ps/trunk/source/simulation2/TypeList.h
+++ ps/trunk/source/simulation2/TypeList.h
@@ -189,6 +189,9 @@
INTERFACE(TerritoryManager)
COMPONENT(TerritoryManager)
+INTERFACE(TurretHolder)
+COMPONENT(TurretHolderScripted)
+
INTERFACE(UnitMotion)
COMPONENT(UnitMotion) // must be after Obstruction
COMPONENT(UnitMotionScripted)
Index: ps/trunk/source/simulation2/components/ICmpTurretHolder.h
===================================================================
--- ps/trunk/source/simulation2/components/ICmpTurretHolder.h
+++ ps/trunk/source/simulation2/components/ICmpTurretHolder.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2020 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 .
+*/
+
+#ifndef INCLUDED_ICMPTURRETHOLDER
+#define INCLUDED_ICMPTURRETHOLDER
+
+#include "simulation2/system/Interface.h"
+
+#include
+
+class ICmpTurretHolder : public IComponent
+{
+public:
+ /**
+ * Returns the correlation between garrisoned turrets (their ID) and which
+ * turret point they occupy (name).
+ */
+ virtual std::vector > GetTurrets() const = 0;
+
+ /**
+ * Correlation between entities (ID) and the turret point they ought to occupy (name).
+ */
+ virtual void SetInitEntities(const std::vector > entities) = 0;
+
+ DECLARE_INTERFACE_TYPE(TurretHolder)
+};
+
+#endif // INCLUDED_ICMPTURRETHOLDER
Index: ps/trunk/source/simulation2/components/ICmpTurretHolder.cpp
===================================================================
--- ps/trunk/source/simulation2/components/ICmpTurretHolder.cpp
+++ ps/trunk/source/simulation2/components/ICmpTurretHolder.cpp
@@ -0,0 +1,60 @@
+/* Copyright (C) 2020 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 .
+*/
+
+#include "precompiled.h"
+
+#include "ICmpTurretHolder.h"
+
+#include "simulation2/scripting/ScriptComponent.h"
+#include "simulation2/system/InterfaceScripted.h"
+
+BEGIN_INTERFACE_WRAPPER(TurretHolder)
+END_INTERFACE_WRAPPER(TurretHolder)
+
+class CCmpTurretHolderScripted : public ICmpTurretHolder
+{
+public:
+ DEFAULT_SCRIPT_WRAPPER(TurretHolderScripted)
+
+ /**
+ * @return - Correlation between garrisoned turrets (their ID) and which
+ * turret point they occupy (name).
+ */
+ virtual std::vector > GetTurrets() const
+ {
+ std::vector > turrets;
+ std::vector entities = m_Script.Call >("GetEntities");
+ for (entity_id_t entity : entities)
+ turrets.push_back(std::make_pair(
+ m_Script.Call("GetOccupiedTurretName", entity),
+ entity
+ ));
+
+ return turrets;
+ }
+
+ /**
+ * Correlation between entities (ID) and the turret point they ought to occupy (name).
+ */
+ virtual void SetInitEntities(std::vector > entities)
+ {
+ for (const std::pair& p : entities)
+ m_Script.CallVoid("SetInitEntity", p.first, p.second);
+ }
+};
+
+REGISTER_COMPONENT_SCRIPT_WRAPPER(TurretHolderScripted)