1
1
forked from 0ad/0ad

Store turret positions in map files.

Follow up on 8bfb449375. Allows saving turret positions in the map file,
instead of relying on the order in which the entities are added.

Differential Revision: D2614
Reviewed by: @wraitii
Comments by: @Angen, @bb, @vladislavbelov.
This was SVN commit r24161.
This commit is contained in:
Freagarach 2020-11-11 19:40:44 +00:00
parent 9669b5f1a9
commit b5df81af76
9 changed files with 244 additions and 3 deletions

View File

@ -108,6 +108,12 @@ Scenario = element Scenario {
attribute uid { xsd:positiveInteger } &
} &
} &
element Turrets {
element Turret {
attribute turret { text } &
attribute uid { xsd:positiveInteger } &
} &
} &
element Actor {
attribute seed { xsd:integer }
}?

View File

@ -267,6 +267,20 @@
</zeroOrMore>
</element>
</optional>
<optional>
<element name="Turrets">
<zeroOrMore>
<element name="Turret">
<attribute name="turret">
<text/>
</attribute>
<attribute name="uid">
<data type="positiveInteger"/>
</attribute>
</element>
</zeroOrMore>
</element>
</optional>
<optional>
<element name="Actor">
<attribute name="seed">

View File

@ -13,6 +13,7 @@ class TurretHolder
let points = this.template.TurretPoints;
for (let point in points)
this.turretPoints.push({
"name": point,
"offset": {
"x": +points[point].X,
"y": +points[point].Y,
@ -120,6 +121,16 @@ class TurretHolder
return true;
}
/**
* @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.
@ -188,6 +199,44 @@ class TurretHolder
return this.turretPoints.find(turretPoint => turretPoint.entity == entity);
}
/**
* @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.
@ -221,6 +270,29 @@ class TurretHolder
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 =

View File

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

View File

@ -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 @@ private:
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 @@ void CXMLReader::Init(const VfsPath& xml_filename)
EL(player);
EL(position);
EL(garrison);
EL(turrets);
EL(orientation);
EL(obstruction);
EL(actor);
@ -476,6 +480,7 @@ void CXMLReader::Init(const VfsPath& xml_filename)
AT(angle);
AT(uid);
AT(seed);
AT(turret);
#undef AT
#undef EL
@ -950,6 +955,7 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time)
CStrW TemplateName;
int PlayerID = 0;
std::vector<entity_id_t> Garrison;
std::vector<std::pair<std::string, entity_id_t> > Turrets;
CFixedVector3D Position;
CFixedVector3D Orientation;
long Seed = -1;
@ -1009,6 +1015,20 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time)
Garrison.push_back(attrs.GetNamedItem(at_uid).ToInt());
}
}
// <turrets>
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()
));
}
}
// <actor>
else if (element_name == el_actor)
{
@ -1054,6 +1074,16 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time)
Garrison.clear();
}
if (!Turrets.empty())
{
CmpPtr<ICmpTurretHolder> 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<ICmpObstruction> cmpObstruction(sim, ent);
if (cmpObstruction)
{

View File

@ -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 @@ void CMapWriter::WriteXML(const VfsPath& filename,
{
XMLWriter_Element garrisonedEntityTag(xmlMapFile, "GarrisonedEntity");
garrisonedEntityTag.Attribute("uid", static_cast<int>(garr_ent_id));
// ToDo: We can store turret position as well.
}
}
}
CmpPtr<ICmpTurretHolder> cmpTurretHolder(sim, ent);
if (cmpTurretHolder)
{
std::vector<std::pair<std::string, entity_id_t> > turrets = cmpTurretHolder->GetTurrets();
if (!turrets.empty())
{
XMLWriter_Element turretTag(xmlMapFile, "Turrets");
for (const std::pair<std::string, entity_id_t>& turret : turrets)
{
XMLWriter_Element turretedEntityTag(xmlMapFile, "Turret");
turretedEntityTag.Attribute("turret", turret.first);
turretedEntityTag.Attribute("uid", static_cast<int>(turret.second));
}
}
}

View File

@ -189,6 +189,9 @@ COMPONENT(TerritoryInfluence)
INTERFACE(TerritoryManager)
COMPONENT(TerritoryManager)
INTERFACE(TurretHolder)
COMPONENT(TurretHolderScripted)
INTERFACE(UnitMotion)
COMPONENT(UnitMotion) // must be after Obstruction
COMPONENT(UnitMotionScripted)

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<std::pair<std::string, entity_id_t> > GetTurrets() const
{
std::vector<std::pair<std::string, entity_id_t> > turrets;
std::vector<entity_id_t> entities = m_Script.Call<std::vector<entity_id_t> >("GetEntities");
for (entity_id_t entity : entities)
turrets.push_back(std::make_pair(
m_Script.Call<std::string>("GetOccupiedTurretName", entity),
entity
));
return turrets;
}
/**
* Correlation between entities (ID) and the turret point they ought to occupy (name).
*/
virtual void SetInitEntities(std::vector<std::pair<std::string, entity_id_t> > entities)
{
for (const std::pair<std::string, entity_id_t>& p : entities)
m_Script.CallVoid("SetInitEntity", p.first, p.second);
}
};
REGISTER_COMPONENT_SCRIPT_WRAPPER(TurretHolderScripted)

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_ICMPTURRETHOLDER
#define INCLUDED_ICMPTURRETHOLDER
#include "simulation2/system/Interface.h"
#include <vector>
class ICmpTurretHolder : public IComponent
{
public:
/**
* Returns the correlation between garrisoned turrets (their ID) and which
* turret point they occupy (name).
*/
virtual std::vector<std::pair<std::string, entity_id_t> > GetTurrets() const = 0;
/**
* Correlation between entities (ID) and the turret point they ought to occupy (name).
*/
virtual void SetInitEntities(const std::vector<std::pair<std::string, entity_id_t> > entities) = 0;
DECLARE_INTERFACE_TYPE(TurretHolder)
};
#endif // INCLUDED_ICMPTURRETHOLDER