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:
parent
9669b5f1a9
commit
b5df81af76
@ -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 }
|
||||
}?
|
||||
|
@ -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">
|
||||
|
@ -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 =
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -189,6 +189,9 @@ COMPONENT(TerritoryInfluence)
|
||||
INTERFACE(TerritoryManager)
|
||||
COMPONENT(TerritoryManager)
|
||||
|
||||
INTERFACE(TurretHolder)
|
||||
COMPONENT(TurretHolderScripted)
|
||||
|
||||
INTERFACE(UnitMotion)
|
||||
COMPONENT(UnitMotion) // must be after Obstruction
|
||||
COMPONENT(UnitMotionScripted)
|
||||
|
60
source/simulation2/components/ICmpTurretHolder.cpp
Normal file
60
source/simulation2/components/ICmpTurretHolder.cpp
Normal 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)
|
42
source/simulation2/components/ICmpTurretHolder.h
Normal file
42
source/simulation2/components/ICmpTurretHolder.h
Normal 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
|
Loading…
Reference in New Issue
Block a user