Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_trireme.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_trireme.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_trireme.xml (revision 14550)
@@ -1,70 +1,70 @@
0.035.00.055.010.060.0100020001.531Infantry Ranged3302001508.0300
- false
+ Female InfantrySupport Infantry Cavalry Siege110true1400Medium WarshipClasses: Mechanical Warship Medium Ranged Melee.
Secondary Attack: Ramming.Warship Medium Rangedphase_townattack/weapon/arrowfly.xmlattack/impact/arrow_metal.xml6.00.56.014.520.0
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_tower.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_tower.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_tower.xml (revision 14550)
@@ -1,98 +1,98 @@
52020.010.05.055.02.01075.0120020002.0StructureStructure2.000.5Infantry56050030020.0200.1
- true
+ UnitSupport Infantry02500Siege TowerRanged SiegeTowerClasses: Ranged SiegeTower.
Garrison up to 20 infantry inside to increase arrow count from 0 to 10.
Counters: 2x vs. Buildings.
Countered by: Melee Cavalry.circle/256x256.pngcircle/256x256_mask.pngattack/siege/ram_move.xmlattack/siege/ram_attack.xmlattack/siege/ram_move.xml4.00.512.05.010.080
Index: ps/trunk/source/simulation2/components/ICmpFootprint.cpp
===================================================================
--- ps/trunk/source/simulation2/components/ICmpFootprint.cpp (revision 14549)
+++ ps/trunk/source/simulation2/components/ICmpFootprint.cpp (revision 14550)
@@ -1,66 +1,67 @@
/* Copyright (C) 2010 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 "ICmpFootprint.h"
#include "simulation2/system/InterfaceScripted.h"
#include "simulation2/system/SimContext.h"
#include "maths/FixedVector3D.h"
CScriptVal ICmpFootprint::GetShape_wrapper()
{
EShape shape;
entity_pos_t size0, size1, height;
GetShape(shape, size0, size1, height);
JSContext* cx = GetSimContext().GetScriptInterface().GetContext();
JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
if (!obj)
return JSVAL_VOID;
if (shape == CIRCLE)
{
jsval ptype = ScriptInterface::ToJSVal(cx, "circle");
jsval pradius = ScriptInterface::ToJSVal(cx, size0);
jsval pheight = ScriptInterface::ToJSVal(cx, height);
JS_SetProperty(cx, obj, "type", &ptype);
JS_SetProperty(cx, obj, "radius", &pradius);
JS_SetProperty(cx, obj, "height", &pheight);
}
else
{
jsval ptype = ScriptInterface::ToJSVal(cx, "square");
jsval pwidth = ScriptInterface::ToJSVal(cx, size0);
jsval pdepth = ScriptInterface::ToJSVal(cx, size1);
jsval pheight = ScriptInterface::ToJSVal(cx, height);
JS_SetProperty(cx, obj, "type", &ptype);
JS_SetProperty(cx, obj, "width", &pwidth);
JS_SetProperty(cx, obj, "depth", &pdepth);
JS_SetProperty(cx, obj, "height", &pheight);
}
return OBJECT_TO_JSVAL(obj);
}
BEGIN_INTERFACE_WRAPPER(Footprint)
DEFINE_INTERFACE_METHOD_1("PickSpawnPoint", CFixedVector3D, ICmpFootprint, PickSpawnPoint, entity_id_t)
+DEFINE_INTERFACE_METHOD_1("PickSpawnPointBothPass", CFixedVector3D, ICmpFootprint, PickSpawnPointBothPass, entity_id_t)
DEFINE_INTERFACE_METHOD_0("GetShape", CScriptVal, ICmpFootprint, GetShape_wrapper)
END_INTERFACE_WRAPPER(Footprint)
Index: ps/trunk/binaries/data/mods/public/simulation/components/GarrisonHolder.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/GarrisonHolder.js (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/components/GarrisonHolder.js (revision 14550)
@@ -1,625 +1,651 @@
function GarrisonHolder() {}
GarrisonHolder.prototype.Schema =
"" +
"" +
"" +
"" +
"" +
"tokens" +
"" +
"" +
"" +
"" +
"" +
"" +
- "" +
- "" +
+ "" +
+ "" +
+ "tokens" +
+ "" +
+ "" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"";
/**
* Initialize GarrisonHolder Component
*/
GarrisonHolder.prototype.Init = function()
{
// Garrisoned Units
this.entities = [];
this.timer = undefined;
this.allowGarrisoning = {};
};
/**
* Return range at which entities can garrison here
*/
GarrisonHolder.prototype.GetLoadingRange = function()
{
var max = +this.template.LoadingRange;
return { "max": max, "min": 0 };
};
/**
* Return true if this garrisonHolder can pickup ent
*/
GarrisonHolder.prototype.CanPickup = function(ent)
{
if (!this.template.Pickup || this.IsFull())
return false;
var cmpOwner = Engine.QueryInterface(this.entity, IID_Ownership);
if (!cmpOwner)
return false;
var player = cmpOwner.GetOwner();
return IsOwnedByPlayer(player, ent);
};
/**
* Return the list of entities garrisoned inside
*/
GarrisonHolder.prototype.GetEntities = function()
{
return this.entities;
};
/**
* Returns an array of unit classes which can be garrisoned inside this
* particualar entity. Obtained from the entity's template
*/
GarrisonHolder.prototype.GetAllowedClassesList = function()
{
var classes = this.template.List._string;
return classes ? classes.split(/\s+/) : [];
};
/**
* Get Maximum pop which can be garrisoned
*/
GarrisonHolder.prototype.GetCapacity = function()
{
return ApplyValueModificationsToEntity("GarrisonHolder/Max", +this.template.Max, this.entity);
};
/**
* Return true if this garrisonHolder is full
*/
GarrisonHolder.prototype.IsFull = function()
{
return this.GetGarrisonedEntitiesCount() >= this.GetCapacity();
};
/**
* Get the heal rate with which garrisoned units will be healed
*/
GarrisonHolder.prototype.GetHealRate = function()
{
return ApplyValueModificationsToEntity("GarrisonHolder/BuffHeal", +this.template.BuffHeal, this.entity);
};
-GarrisonHolder.prototype.EjectEntitiesOnDestroy = function()
-{
- return this.template.EjectEntitiesOnDestroy == "true";
-};
-
/**
* Set this entity to allow or disallow garrisoning in
* Every component calling this function should do it with its own ID, and as long as one
* component doesn't allow this entity to garrison, it can't be garrisoned
* When this entity already contains garrisoned soldiers,
* these will not be able to ungarrison until the flag is set to true again.
*
* This more useful for modern-day features. For example you can't garrison or ungarrison
* a driving vehicle or plane.
*/
GarrisonHolder.prototype.AllowGarrisoning = function(allow, callerID)
{
this.allowGarrisoning[callerID] = allow;
};
/**
* Check if no component of this entity blocks garrisoning
* (f.e. because the vehicle is moving too fast)
*/
GarrisonHolder.prototype.IsGarrisoningAllowed = function()
{
for each (var allow in this.allowGarrisoning)
{
if (!allow)
return false;
}
return true;
};
/**
* Return the number of recursively garrisoned units
*/
GarrisonHolder.prototype.GetGarrisonedEntitiesCount = function()
{
var count = 0;
for each (var ent in this.entities)
{
count++;
var cmpGarrisonHolder = Engine.QueryInterface(ent, IID_GarrisonHolder);
if (cmpGarrisonHolder)
count += cmpGarrisonHolder.GetGarrisonedEntitiesCount();
}
return count;
};
/**
* Get number of garrisoned units capable of shooting arrows
* Not necessarily archers
*/
GarrisonHolder.prototype.GetGarrisonedArcherCount = function(garrisonArrowClasses)
{
var count = 0;
for each (var entity in this.entities)
{
var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
var classes = cmpIdentity.GetClassesList();
if (classes.some(function(c){return garrisonArrowClasses.indexOf(c) > -1;}))
count++;
}
return count;
};
/**
* Checks if an entity can be allowed to garrison in the building
* based on its class
*/
GarrisonHolder.prototype.AllowedToGarrison = function(entity)
{
if (!this.IsGarrisoningAllowed())
return false;
var allowedClasses = this.GetAllowedClassesList();
var entityClasses = (Engine.QueryInterface(entity, IID_Identity)).GetClassesList();
// Check if the unit is allowed to be garrisoned inside the building
for each (var allowedClass in allowedClasses)
{
if (entityClasses.indexOf(allowedClass) != -1)
{
return true;
}
}
return false;
};
/**
* Garrison a unit inside.
* Returns true if successful, false if not
* The timer for AutoHeal is started here
*/
GarrisonHolder.prototype.Garrison = function(entity)
{
var cmpPosition = Engine.QueryInterface(entity, IID_Position);
if (!cmpPosition)
return false;
if (!this.PerformGarrison(entity))
return false;
cmpPosition.MoveOutOfWorld();
return true;
};
GarrisonHolder.prototype.PerformGarrison = function(entity)
{
if (!this.HasEnoughHealth())
return false;
// Check if the unit is allowed to be garrisoned inside the building
if(!this.AllowedToGarrison(entity))
return false;
// check the capacity
var extraCount = 0;
var cmpGarrisonHolder = Engine.QueryInterface(entity, IID_GarrisonHolder);
if (cmpGarrisonHolder)
extraCount += cmpGarrisonHolder.GetGarrisonedEntitiesCount();
if (this.GetGarrisonedEntitiesCount() + extraCount >= this.GetCapacity())
return false;
if (!this.timer && this.GetHealRate() > 0)
{
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
this.timer = cmpTimer.SetTimeout(this.entity, IID_GarrisonHolder, "HealTimeout", 1000, {});
}
// Actual garrisoning happens here
this.entities.push(entity);
this.UpdateGarrisonFlag();
var cmpProductionQueue = Engine.QueryInterface(entity, IID_ProductionQueue);
if (cmpProductionQueue)
cmpProductionQueue.PauseProduction();
var cmpAura = Engine.QueryInterface(entity, IID_Auras);
if (cmpAura && cmpAura.HasGarrisonAura())
cmpAura.ApplyGarrisonBonus(this.entity);
Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {});
var cmpUnitAI = Engine.QueryInterface(entity, IID_UnitAI);
if (cmpUnitAI && cmpUnitAI.GetAlertRaiser())
Engine.PostMessage(cmpUnitAI.GetAlertRaiser(), MT_UnitGarrisonedAfterAlert, {"holder": this.entity, "unit": entity});
return true;
};
/**
* Simply eject the unit from the garrisoning entity without
* moving it
* Returns true if successful, false if not
*/
GarrisonHolder.prototype.Eject = function(entity, forced)
{
var entityIndex = this.entities.indexOf(entity);
// Error: invalid entity ID, usually it's already been ejected
if (entityIndex == -1)
return false; // Fail
// Find spawning location
var cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint);
- var pos = cmpFootprint.PickSpawnPoint(entity);
+ var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
+ var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
+ // If the garrisonHolder is a sinking ship, restrict the location to the intersection of both passabilities
+ // TODO: should use passability classes to be more generic
+ if ((!cmpHealth || cmpHealth.GetHitpoints() == 0) && cmpIdentity && cmpIdentity.HasClass("Ship"))
+ var pos = cmpFootprint.PickSpawnPointBothPass(entity);
+ else
+ var pos = cmpFootprint.PickSpawnPoint(entity);
+
if (pos.y < 0)
{
// Error: couldn't find space satisfying the unit's passability criteria
if (forced)
{ // If ejection is forced, we need to continue, so use center of the building
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
pos = cmpPosition.GetPosition();
}
else
{ // Fail
return false;
}
}
this.entities.splice(entityIndex, 1);
var cmpUnitAI = Engine.QueryInterface(entity, IID_UnitAI);
if (cmpUnitAI)
cmpUnitAI.Ungarrison();
var cmpProductionQueue = Engine.QueryInterface(entity, IID_ProductionQueue);
if (cmpProductionQueue)
cmpProductionQueue.UnpauseProduction();
var cmpAura = Engine.QueryInterface(entity, IID_Auras);
if (cmpAura && cmpAura.HasGarrisonAura())
cmpAura.RemoveGarrisonBonus(this.entity);
var cmpNewPosition = Engine.QueryInterface(entity, IID_Position);
cmpNewPosition.JumpTo(pos.x, pos.z);
// TODO: what direction should they face in?
Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {});
return true;
};
/**
* Order entities to walk to the Rally Point
*/
GarrisonHolder.prototype.OrderWalkToRallyPoint = function(entities)
{
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
var cmpRallyPoint = Engine.QueryInterface(this.entity, IID_RallyPoint);
if (cmpRallyPoint)
{
var rallyPos = cmpRallyPoint.GetPositions()[0];
if (rallyPos)
{
var commands = GetRallyPointCommands(cmpRallyPoint, entities);
// ignore the rally point if it is autogarrison
if (commands[0].type == "garrison" && commands[0].target == this.entity)
return;
for each (var com in commands)
{
ProcessCommand(cmpOwnership.GetOwner(), com);
}
}
}
};
/**
* Ejects units and orders them to move to the Rally Point.
* Returns true if successful, false if not
*/
GarrisonHolder.prototype.PerformEject = function(entities, forced)
{
if (!this.IsGarrisoningAllowed() && !forced)
return false;
var ejectedEntities = [];
var success = true;
for each (var entity in entities)
{
if (this.Eject(entity, forced))
{
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
var cmpEntOwnership = Engine.QueryInterface(entity, IID_Ownership);
if (cmpOwnership && cmpEntOwnership && cmpOwnership.GetOwner() == cmpEntOwnership.GetOwner())
ejectedEntities.push(entity);
}
else
success = false;
}
this.OrderWalkToRallyPoint(ejectedEntities);
this.UpdateGarrisonFlag();
return success;
};
/**
* Unload unit from the garrisoning entity and order them
* to move to the Rally Point
* Returns true if successful, false if not
*/
GarrisonHolder.prototype.Unload = function(entity, forced)
{
return this.PerformEject([entity], forced);
};
/**
* Unload one or all units that match a template and owner from
* the garrisoning entity and order them to move to the Rally Point
* Returns true if successful, false if not
*
* extendedTemplate has the format "p"+ownerid+"&"+template
*/
GarrisonHolder.prototype.UnloadTemplate = function(extendedTemplate, all, forced)
{
var index = extendedTemplate.indexOf("&");
if (index == -1)
return false;
var owner = +extendedTemplate.slice(1,index);
var template = extendedTemplate.slice(index+1);
var entities = [];
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
for each (var entity in this.entities)
{
var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
// Units with multiple ranks are grouped together.
var name = cmpIdentity.GetSelectionGroupName()
|| cmpTemplateManager.GetCurrentTemplateName(entity);
if (name != template)
continue;
if (owner != Engine.QueryInterface(entity, IID_Ownership).GetOwner())
continue;
entities.push(entity);
// If 'all' is false, only ungarrison the first matched unit.
if (!all)
break;
}
return this.PerformEject(entities, forced);
};
/**
* Unload all units with same owner as the entity
* and order them to move to the Rally Point
* Returns true if all successful, false if not
*/
GarrisonHolder.prototype.UnloadAllOwn = function(forced)
{
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (!cmpOwnership)
return false;
var owner = cmpOwnership.GetOwner();
// Make copy of entity list
var entities = [];
for each (var entity in this.entities)
{
var cmpOwnership = Engine.QueryInterface(entity, IID_Ownership);
if (cmpOwnership && cmpOwnership.GetOwner() == owner)
entities.push(entity);
}
return this.PerformEject(entities, forced);
};
/**
* Unload all units from the entity
* and order them to move to the Rally Point
* Returns true if all successful, false if not
*/
GarrisonHolder.prototype.UnloadAll = function(forced)
{
var entities = this.entities.slice();
return this.PerformEject(entities, forced);
};
/**
* Used to check if the garrisoning entity's health has fallen below
* a certain limit after which all garrisoned units are unloaded
*/
GarrisonHolder.prototype.OnHealthChanged = function(msg)
{
if (!this.HasEnoughHealth() && this.entities.length)
{
var entities = this.entities.slice();
this.EjectOrKill(entities);
}
};
/**
* Check if this entity has enough health to garrison units inside it
*/
GarrisonHolder.prototype.HasEnoughHealth = function()
{
var cmpHealth = Engine.QueryInterface(this.entity, IID_Health)
var hitpoints = cmpHealth.GetHitpoints();
var maxHitpoints = cmpHealth.GetMaxHitpoints();
var ejectHitpoints = Math.floor((+this.template.EjectHealth) * maxHitpoints);
return hitpoints > ejectHitpoints;
};
/**
* Called every second. Heals garrisoned units
*/
GarrisonHolder.prototype.HealTimeout = function(data)
{
if (this.entities.length == 0)
{
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(this.timer);
this.timer = undefined;
}
else
{
for each (var entity in this.entities)
{
var cmpHealth = Engine.QueryInterface(entity, IID_Health);
if (cmpHealth)
{
// We do not want to heal unhealable units
if (!cmpHealth.IsUnhealable())
cmpHealth.Increase(this.GetHealRate());
}
}
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
this.timer = cmpTimer.SetTimeout(this.entity, IID_GarrisonHolder, "HealTimeout", 1000, {});
}
};
GarrisonHolder.prototype.UpdateGarrisonFlag = function()
{
var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
if (!cmpVisual)
return;
cmpVisual.SelectAnimation("garrisoned", true, 0, "");
// TODO: ought to extend ICmpVisual to let us just select variant
// keywords without changing the animation too
if (this.entities.length)
cmpVisual.SelectAnimation("garrisoned", false, 1.0, "");
else
cmpVisual.SelectAnimation("idle", false, 1.0, "");
};
/**
* Cancel timer when destroyed
*/
GarrisonHolder.prototype.OnDestroy = function()
{
if (this.timer)
{
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(this.timer);
}
};
/**
* If a garrisoned entity is captured, or about to be killed (so its owner
* changes to '-1'), remove it from the building so we only ever contain valid
* entities
*/
GarrisonHolder.prototype.OnGlobalOwnershipChanged = function(msg)
{
// the ownership change may be on the garrisonholder
if (this.entity == msg.entity)
{
var entities = [];
for each (var entity in this.entities)
{
if (msg.to == -1 || !IsOwnedByMutualAllyOfEntity(this.entity, entity))
entities.push(entity);
}
this.EjectOrKill(entities);
return;
}
// or on some of its garrisoned units
var entityIndex = this.entities.indexOf(msg.entity);
if (entityIndex != -1)
{
// If the entity is dead, remove it directly instead of ejecting the corpse
var cmpHealth = Engine.QueryInterface(msg.entity, IID_Health);
if (cmpHealth && cmpHealth.GetHitpoints() == 0)
{
this.entities.splice(entityIndex, 1);
Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {});
this.UpdateGarrisonFlag();
}
else if(!IsOwnedByMutualAllyOfEntity(this.entity, this.entities[entityIndex]))
this.EjectOrKill([this.entities[entityIndex]]);
}
};
/**
* Update list of garrisoned entities if one gets renamed (e.g. by promotion)
*/
GarrisonHolder.prototype.OnGlobalEntityRenamed = function(msg)
{
var entityIndex = this.entities.indexOf(msg.entity);
if (entityIndex != -1)
{
this.entities[entityIndex] = msg.newentity;
Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {});
}
};
/**
* Eject all foreign garrisoned entities which are no more allied
*/
GarrisonHolder.prototype.OnDiplomacyChanged = function()
{
var entities = []
for each (var entity in this.entities)
{
if (!IsOwnedByMutualAllyOfEntity(this.entity, entity))
entities.push(entity);
}
this.EjectOrKill(entities);
};
/**
* Eject or kill a garrisoned unit which can no more be garrisoned
* (garrisonholder's health too small or ownership changed)
*/
GarrisonHolder.prototype.EjectOrKill = function(entities)
{
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
// Eject the units which can be ejected (if not in world, it generally means this holder
// is inside a holder which kills its entities, so do not eject)
- if (cmpPosition.IsInWorld() && this.EjectEntitiesOnDestroy())
- this.PerformEject(entities, true);
+ if (cmpPosition.IsInWorld())
+ {
+ var cmpGarrisonHolder = this;
+ var ejectables = entities.filter(function(ent) { return cmpGarrisonHolder.IsEjectable(ent) });
+ if (ejectables.length)
+ this.PerformEject(ejectables, false);
+ }
// And destroy all remaining entities
for each (var entity in entities)
{
var entityIndex = this.entities.indexOf(entity);
if (entityIndex == -1)
continue;
var cmpHealth = Engine.QueryInterface(entity, IID_Health);
if (cmpHealth)
cmpHealth.Kill();
this.entities.splice(entityIndex, 1);
}
Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {});
this.UpdateGarrisonFlag();
};
+/**
+ * Checks if an entity is ejectable on destroy if possible
+ */
+GarrisonHolder.prototype.IsEjectable = function(entity)
+{
+ var ejectableClasses = this.template.EjectClassesOnDestroy._string;
+ ejectableClasses = ejectableClasses ? ejectableClasses.split(/\s+/) : [];
+ var entityClasses = (Engine.QueryInterface(entity, IID_Identity)).GetClassesList();
+ for each (var ejectableClass in ejectableClasses)
+ if (entityClasses.indexOf(ejectableClass) != -1)
+ return true;
+
+ return false;
+};
+
Engine.RegisterComponentType(IID_GarrisonHolder, "GarrisonHolder", GarrisonHolder);
Index: ps/trunk/binaries/data/mods/public/simulation/templates/other/plane.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/other/plane.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/other/plane.xml (revision 14550)
@@ -1,74 +1,74 @@
0.0100.025.0482460.0020001.531Infantry10Support Infantry15
- false
+ 0.03.07.0heleP-51 MustangThis may be anachronistic.A World War 2 American fighter plane.units/global_mustang.png100truetrue1.0true60.050.040.025.05.010.01.02.050.015.0true100units/global/plane.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/athen_wonder.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/athen_wonder.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/athen_wonder.xml (revision 14550)
@@ -1,29 +1,29 @@
12.0300.1
- true
+ UnitSupport Infantry Cavalry32athenNeṓs ParthenosThe Hellenes built marvelous temples in order to honour their polytheistic pantheon. While all gods were venerated, a specific patron deity was supposed to watch over each polis.Bring glory to your civilization and add large tracts of land to your empire. Garrison up to 30 units to heal them at a quick rate.structures/fndn_8x8.xmlstructures/hellenes/temple_epic.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/cart_wonder.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/cart_wonder.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/cart_wonder.xml (revision 14550)
@@ -1,28 +1,28 @@
12.0300.1
- true
+ UnitSupport Infantry Cavalry32cartTemple of Ba'al HammonDating from the 2nd Century BC, the Mausoleum of Atban in northern Tunisia is over twenty metres high and was built by the inhabitants of Dougga for a Numidian prince.structures/fndn_8x8.xmlstructures/carthaginians/wonder.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/hele_wonder.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/hele_wonder.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/hele_wonder.xml (revision 14550)
@@ -1,29 +1,29 @@
12.0300.1
- true
+ UnitSupport Infantry Cavalry32athenNeṓs ParthenosThe Hellenes built marvelous temples in order to honour their polytheistic pantheon. While all gods were venerated, a specific patron deity was supposed to watch over each polis.Bring glory to your civilization and add large tracts of land to your empire. Garrison up to 30 units to heal them at a quick rate.structures/fndn_8x8.xmlstructures/hellenes/temple_epic.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/mace_wonder.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/mace_wonder.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/mace_wonder.xml (revision 14550)
@@ -1,29 +1,29 @@
12.0300.1
- true
+ UnitSupport Infantry Cavalry32athenNeṓs ParthenosThe Hellenes built marvelous temples in order to honour their polytheistic pantheon. While all gods were venerated, a specific patron deity was supposed to watch over each polis.Bring glory to your civilization and add large tracts of land to your empire. Garrison up to 30 units to heal them at a quick rate.structures/fndn_8x8.xmlstructures/hellenes/temple_epic.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/ptol_wonder.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/ptol_wonder.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/ptol_wonder.xml (revision 14550)
@@ -1,28 +1,28 @@
20.0300.1
- true
+ UnitSupport Infantry Cavalry32ptolTemple of EdfuThe Temple of Edfu is an ancient Egyptian temple located on the west bank of the Nile in the city of Edfu which was known in Greco-Roman times as Apollonopolis Magna, after the chief god Horus-Apollo.The temple, dedicated to the falcon god Horus, was built in the Ptolemaic period between 237 and 57 BCE. In modern times, it is one of the best preserved temples of Egypt.structures/fndn_8x8.xmlstructures/ptolemies/wonder.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/rome_tent.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/rome_tent.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/rome_tent.xml (revision 14550)
@@ -1,60 +1,60 @@
own neutral enemy30505.050.1
- true
+ UnitSupport Infantry02200romeTentTabernāculum-VillageA temporary shelter for soldiers. +5 population bonus.1.0
-units/{civ}_support_female_citizen_house
-unlock_females_house
interface/complete/building/complete_universal.xmlattack/destruction/building_collapse_large.xml1props/structures/romans/rome_tent.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/rome_wonder.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/rome_wonder.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/rome_wonder.xml (revision 14550)
@@ -1,28 +1,28 @@
12.0300.1
- true
+ UnitSupport Infantry Cavalry32romeAedes Iovis Optimi Maximi.structures/fndn_4x6.xmlstructures/romans/temple_mars.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart_wonder.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart_wonder.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/structures/spart_wonder.xml (revision 14550)
@@ -1,29 +1,29 @@
12.0300.1
- true
+ UnitSupport Infantry Cavalry32athenNeṓs ParthenosThe Hellenes built marvelous temples in order to honour their polytheistic pantheon. While all gods were venerated, a specific patron deity was supposed to watch over each polis.Bring glory to your civilization and add large tracts of land to your empire. Garrison up to 30 units to heal them at a quick rate.structures/fndn_8x8.xmlstructures/hellenes/temple_epic.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml (revision 14550)
@@ -1,121 +1,121 @@
51530.020.00.060.010.075.0120020001.531own neutralCivilCentreCivilCentre1802050005005005008.0200.1
- true
+ UnitSupport Infantry Cavalry113000rubble/rubble_stone_6x6Civic CenterBuild to acquire large tracts of territory. Train citizens. Garrison: 20.
Defensive
CivCentre
CivilCentre
structures/civic_centre.pngphase_town20005050500.8
units/{civ}_support_female_citizen
phase_town_generic
phase_city_generic
food wood stone metalinterface/complete/building/complete_civ_center.xmlattack/weapon/arrowfly.xmlattack/destruction/building_collapse_large.xmlinterface/alarm/alarm_alert_0.xmlinterface/alarm/alarm_alert_1.xmlinterface/alarm/alarm_alert_2.xmltrue14065536214090structures/fndn_6x6.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml (revision 14550)
@@ -1,77 +1,77 @@
House530755.0300.1
- true
+ UnitSupport1800rubble/rubble_stone_3x3HouseIncrease the population limit.Village House -ConquestCriticalstructures/house.png1000101001.0
units/{civ}_support_female_citizen_house
unlock_females_house
interface/complete/building/complete_house.xmlattack/destruction/building_collapse_large.xml6.00.68.0false206553620structures/fndn_3x3.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_civic_temple.xml (revision 14550)
@@ -1,84 +1,84 @@
range40Temple520030012.0200.1
- true
+ UnitSupport Infantry Cavalry322000rubble/rubble_stone_4x6TempleTrain healers. Garrison up to 20 units to heal them at a quick rate. Research healing and religious improvements. Heals nearby units, but slower than garrisoned units.
Town
Temple
structures/temple.pngphase_town10001050500interface/complete/building/complete_temple.xmlattack/destruction/building_collapse_large.xmlfalse40655360.8
units/{civ}_support_healer_b
pair_heal_01
pair_heal_02
pair_heal_03
40structures/fndn_4x6.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defense_defense_tower.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defense_defense_tower.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defense_defense_tower.xml (revision 14550)
@@ -1,105 +1,105 @@
0.020.00.057.08.01575.0120020001.5OrganicSiege0.511DefenseTowerDefenseTower5015010010015.050.1
- true
+ UnitSupport Infantry021000rubble/rubble_stone_2x2Defense TowerShoots arrows. Garrison to provide extra defense.DefenseTower Town Tower GarrisonTower -ConquestCriticalstructures/defense_tower.pngphase_town1000101000.7
pair_tower_01
interface/complete/building/complete_tower.xmlattack/weapon/arrowfly.xmlattack/destruction/building_collapse_large.xml6.00.621.0false326553680structures/fndn_2x2.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defense_outpost.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defense_outpost.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defense_outpost.xml (revision 14550)
@@ -1,101 +1,101 @@
52013510.020.00.055.013.075.0120020002.01own neutralHouse4080015.010.1
- true
+ UnitSupport Infantry02800rubble/rubble_stone_2x2OutpostBuild in neutral and friendly territories to scout areas of the map. Slowly loses health while in neutral territory.Outpost Village Defensive -ConquestCriticalstructures/outpost.png10008000.7
vision_outpost
decay_outpost
interface/complete/building/complete_tower.xmlattack/destruction/building_collapse_large.xml6.00.618.0280props/special/palisade_rocks_outpost.xmlstructures/fndn_2x2.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defense_wall_tower.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defense_wall_tower.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_defense_wall_tower.xml (revision 14550)
@@ -1,99 +1,99 @@
0.020.00.050.08.075.0120020001.5OrganicSiege0.511land-shoreWall201008.050.1
- true
+ UnitSupport Infantry025000rubble/rubble_stone_wall_towerWall TurretShoots arrows. Garrison to defend a city wall against attackers.Defensive -ConquestCritical StoneWall Towerstructures/tower.pngphase_town1000101500.8
pair_walls_01
interface/complete/building/complete_tower.xmlattack/weapon/arrowfly.xmlattack/destruction/building_collapse_large.xml20.0false206553660structures/fndn_2x2.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_barracks.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_barracks.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_barracks.xml (revision 14550)
@@ -1,74 +1,74 @@
Barracks20030012.0100.1
- true
+ UnitInfantry Cavalry022000rubble/rubble_stone_4x4BarracksTrain citizen-soldiers. Research training improvements. Garrison: 10.
Village
Barracks
structures/barracks.pngphase_village1000301000.8
pair_levy_01
training_conscription
unlock_champion_units
interface/complete/building/complete_barracks.xmlattack/destruction/building_collapse_large.xmlfalse506553632structures/fndn_4x4.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_blacksmith.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_blacksmith.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_blacksmith.xml (revision 14550)
@@ -1,79 +1,79 @@
Barracks20020012.010.1
- true
+ UnitInfantry022000rubble/rubble_stone_4x4BlacksmithResearch weapons and armor improvements.
Town
Blacksmith
structures/blacksmith.pngphase_town10005001000.8
pair_inf_01
pair_inf_02
pair_inf_armor_01
pair_inf_armor_02
pair_inf_armor_03
pair_inf_armor_04
pair_cav_01
armor_hero_01
interface/complete/building/complete_blacksmith.xmlattack/destruction/building_collapse_large.xmlfalse386553632structures/fndn_4x4.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml (revision 14550)
@@ -1,105 +1,105 @@
0.025.00.085.08.075.0120020001.5OrganicSiege0.531FortressFortress601030006508.0200.075
- true
+ UnitSupport Infantry Cavalry Siege064200rubble/rubble_stone_6x6FortressTrain heroes, champions, and siege weapons. Research siege weapon improvements. Garrison: 20.
City
Defensive
GarrisonFortress
Fortress
structures/fortress.pngphase_city100006500.8
pair_champ_02
interface/complete/building/complete_fortress.xmlattack/weapon/arrowfly.xmlattack/destruction/building_collapse_large.xmlfalse1006553680structures/fndn_6x6.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_resource_corral.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_resource_corral.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_resource_corral.xml (revision 14550)
@@ -1,66 +1,66 @@
501005.0100.1
- true
+ UnitAnimal12500rubble/rubble_2x4CorralRaise herd animals for food. Task domestic animals here to gain a trickle of food or other bonus (Not yet implemented).
Village
Corral
structures/corral.png100010000.7
gaia/fauna_sheep
gather_animals_stockbreeding
interface/complete/building/complete_corral.xmlattack/destruction/building_collapse_large.xml20structures/fndn_2x4.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_structure_special.xml (revision 14550)
@@ -1,69 +1,69 @@
52553103Special200002002008.050.1
- true
+ UnitSupport Infantry Cavalry122000rubble/rubble_stone_6x6Special BuildingThis is a special building unique to a particular civilization.
City
SpecialBuilding
.pngphase_cityinterface/complete/building/complete_broch.xmlattack/destruction/building_collapse_large.xmlfalse3865536structures/fndn_5x5.xml
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_bireme.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_bireme.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_bireme.xml (revision 14550)
@@ -1,69 +1,69 @@
0.035.00.045.010.060.0200020001.521Infantry Ranged22010010010.0200
- false
+ Female InfantrySupport Infantry Cavalry110true800Light WarshipClasses: Mechanical Warship Light Ranged.Warship Light Bow Rangedphase_townattack/weapon/arrowfly.xmlattack/impact/arrow_metal.xml6.00.56.012.518.0
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fishing.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fishing.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fishing.xml (revision 14550)
@@ -1,63 +1,63 @@
25210.00.00.05.01000Ship Structure6.010
- false
+ Female InfantrySupport Infantry110trueFishing BoatClasses: Mechanical Ship FishingBoat.
Fish the waters for Food. Garrison a support or infantry unit inside to boost fishing rate.FishingBoat6.01.02actor/ship/boat_move.xmlactor/ship/boat_move.xmlfalse8.524
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml (revision 14550)
@@ -1,60 +1,60 @@
2520100150
- false
+ Female InfantrySupport Infantry Cavalry110true400MerchantmanClasses: Mechanical Ship Trader.
Trade between docks. Garrison a Trader aboard for additional profit (+20% for each garrisoned). Gather profitable aquatic treasures.Traderphase_town12.0757550506.00.56.010.01.0false10.550
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_quinquereme.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_quinquereme.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_quinquereme.xml (revision 14550)
@@ -1,81 +1,81 @@
30.00.030.065.020.028.0200050006.0Circular8true40.00.040.001Catapult5452502008.0500
- false
+ Female InfantrySupport Infantry Cavalry Siege110true2000Heavy WarshipClasses: Mechanical Warship Heavy Ranged Melee.
Garrison with catapults to increase ranged fire power.
Secondary Attack: Ramming.Warship Heavy Rangedphase_cityattack/siege/ballist_attack.xml6.00.56.014.520110
Index: ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ram.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ram.xml (revision 14549)
+++ ps/trunk/binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_ram.xml (revision 14550)
@@ -1,88 +1,88 @@
230100.00.080.06.51500Gates1.5OrganicGates Structure0.00.0100.06.50.0Gates1.5OrganicGates Structure20520010050.1
- true
+ UnitSupport Infantry12200Battering RamMelee RamClasses: Siege Melee Ram.
Counters: 1.5x vs. Gates. Generally good vs. Buildings.
Countered by: Melee Cavalry. Cannot attack organic units (but can attack other siege and ships).attack/siege/ram_move.xmlattack/siege/ram_attack_order.xmlattack/siege/ram_move.xmlattack/siege/ram_attack.xml4.00.56.511.048
Index: ps/trunk/source/simulation2/components/CCmpFootprint.cpp
===================================================================
--- ps/trunk/source/simulation2/components/CCmpFootprint.cpp (revision 14549)
+++ ps/trunk/source/simulation2/components/CCmpFootprint.cpp (revision 14550)
@@ -1,255 +1,393 @@
/* Copyright (C) 2013 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 "simulation2/system/Component.h"
#include "ICmpFootprint.h"
#include "simulation2/components/ICmpObstruction.h"
#include "simulation2/components/ICmpObstructionManager.h"
#include "simulation2/components/ICmpPathfinder.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpUnitMotion.h"
#include "simulation2/MessageTypes.h"
#include "graphics/Terrain.h" // For TERRAIN_TILE_SIZE
#include "maths/FixedVector2D.h"
class CCmpFootprint : public ICmpFootprint
{
public:
static void ClassInit(CComponentManager& UNUSED(componentManager))
{
}
DEFAULT_COMPONENT_ALLOCATOR(Footprint)
EShape m_Shape;
entity_pos_t m_Size0; // width/radius
entity_pos_t m_Size1; // height/radius
entity_pos_t m_Height;
static std::string GetSchema()
{
return
"Approximation of the entity's shape, for collision detection and outline rendering. "
"Shapes are flat horizontal squares or circles, extended vertically to a given height."
""
""
"0.0"
""
""
""
"0.0"
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
"";
}
virtual void Init(const CParamNode& paramNode)
{
if (paramNode.GetChild("Square").IsOk())
{
m_Shape = SQUARE;
m_Size0 = paramNode.GetChild("Square").GetChild("@width").ToFixed();
m_Size1 = paramNode.GetChild("Square").GetChild("@depth").ToFixed();
}
else if (paramNode.GetChild("Circle").IsOk())
{
m_Shape = CIRCLE;
m_Size0 = m_Size1 = paramNode.GetChild("Circle").GetChild("@radius").ToFixed();
}
else
{
// Error - pick some default
m_Shape = CIRCLE;
m_Size0 = m_Size1 = entity_pos_t::FromInt(1);
}
m_Height = paramNode.GetChild("Height").ToFixed();
}
virtual void Deinit()
{
}
virtual void Serialize(ISerializer& UNUSED(serialize))
{
// No dynamic state to serialize
}
virtual void Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
{
Init(paramNode);
}
virtual void GetShape(EShape& shape, entity_pos_t& size0, entity_pos_t& size1, entity_pos_t& height)
{
shape = m_Shape;
size0 = m_Size0;
size1 = m_Size1;
height = m_Height;
}
virtual CFixedVector3D PickSpawnPoint(entity_id_t spawned)
{
// Try to find a free space around the building's footprint.
// (Note that we use the footprint, not the obstruction shape - this might be a bit dodgy
// because the footprint might be inside the obstruction, but it hopefully gives us a nicer
// shape.)
const CFixedVector3D error(fixed::FromInt(-1), fixed::FromInt(-1), fixed::FromInt(-1));
CmpPtr cmpPosition(GetEntityHandle());
if (!cmpPosition || !cmpPosition->IsInWorld())
return error;
CmpPtr cmpObstructionManager(GetSystemEntity());
if (!cmpObstructionManager)
return error;
entity_pos_t spawnedRadius;
ICmpObstructionManager::tag_t spawnedTag;
CmpPtr cmpSpawnedObstruction(GetSimContext(), spawned);
if (cmpSpawnedObstruction)
{
spawnedRadius = cmpSpawnedObstruction->GetUnitRadius();
spawnedTag = cmpSpawnedObstruction->GetObstruction();
}
// else use zero radius
// Get passability class from UnitMotion
CmpPtr cmpUnitMotion(GetSimContext(), spawned);
if (!cmpUnitMotion)
return error;
ICmpPathfinder::pass_class_t spawnedPass = cmpUnitMotion->GetPassabilityClass();
CmpPtr cmpPathfinder(GetSystemEntity());
if (!cmpPathfinder)
return error;
-
+
CFixedVector2D initialPos = cmpPosition->GetPosition2D();
entity_angle_t initialAngle = cmpPosition->GetRotation().Y;
// Max spawning distance in tiles
const i32 maxSpawningDistance = 4;
if (m_Shape == CIRCLE)
{
// Expand outwards from foundation
for (i32 dist = 0; dist <= maxSpawningDistance; ++dist)
{
// The spawn point should be far enough from this footprint to fit the unit, plus a little gap
entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(2 + (int)TERRAIN_TILE_SIZE*dist);
entity_pos_t radius = m_Size0 + clearance;
// Try equally-spaced points around the circle in alternating directions, starting from the front
const i32 numPoints = 31 + 2*dist;
for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2]
{
entity_angle_t angle = initialAngle + (entity_angle_t::Pi()*2).Multiply(entity_angle_t::FromInt(i)/(int)numPoints);
fixed s, c;
sincos_approx(angle, s, c);
CFixedVector3D pos (initialPos.X + s.Multiply(radius), fixed::Zero(), initialPos.Y + c.Multiply(radius));
SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Z, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
return pos; // this position is okay, so return it
}
}
}
else
{
fixed s, c;
sincos_approx(initialAngle, s, c);
// Expand outwards from foundation
for (i32 dist = 0; dist <= maxSpawningDistance; ++dist)
{
// The spawn point should be far enough from this footprint to fit the unit, plus a little gap
entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(2 + (int)TERRAIN_TILE_SIZE*dist);
for (i32 edge = 0; edge < 4; ++edge)
{
// Try equally-spaced points along the edge in alternating directions, starting from the middle
const i32 numPoints = 9 + 2*dist;
// Compute the direction and length of the current edge
CFixedVector2D dir;
fixed sx, sy;
switch (edge)
{
case 0:
dir = CFixedVector2D(c, -s);
sx = m_Size0;
sy = m_Size1;
break;
case 1:
dir = CFixedVector2D(-s, -c);
sx = m_Size1;
sy = m_Size0;
break;
case 2:
dir = CFixedVector2D(s, c);
sx = m_Size1;
sy = m_Size0;
break;
case 3:
dir = CFixedVector2D(-c, s);
sx = m_Size0;
sy = m_Size1;
break;
}
CFixedVector2D center = initialPos - dir.Perpendicular().Multiply(sy/2 + clearance);
dir = dir.Multiply((sx + clearance*2) / (int)(numPoints-1));
for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2]
{
CFixedVector2D pos (center + dir*i);
SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
return CFixedVector3D(pos.X, fixed::Zero(), pos.Y); // this position is okay, so return it
}
}
}
}
return error;
}
+
+ virtual CFixedVector3D PickSpawnPointBothPass(entity_id_t spawned)
+ {
+ // Try to find a free space inside and around this footprint
+ // at the intersection between the footprint passability and the unit passability.
+ // (useful for example for destroyed ships where the spawning point should be in the intersection
+ // of the unit and ship passabilities).
+ // As the overlap between these passabilities regions may be narrow, we need a small step (1 meter)
+
+ const CFixedVector3D error(fixed::FromInt(-1), fixed::FromInt(-1), fixed::FromInt(-1));
+
+ CmpPtr cmpPosition(GetEntityHandle());
+ if (!cmpPosition || !cmpPosition->IsInWorld())
+ return error;
+
+ CmpPtr cmpObstructionManager(GetSystemEntity());
+ if (!cmpObstructionManager)
+ return error;
+
+ entity_pos_t spawnedRadius;
+ ICmpObstructionManager::tag_t spawnedTag;
+
+ CmpPtr cmpSpawnedObstruction(GetSimContext(), spawned);
+ if (cmpSpawnedObstruction)
+ {
+ spawnedRadius = cmpSpawnedObstruction->GetUnitRadius();
+ spawnedTag = cmpSpawnedObstruction->GetObstruction();
+ }
+ // else use zero radius
+
+ // Get passability class from UnitMotion
+ CmpPtr cmpUnitMotion(GetSimContext(), spawned);
+ if (!cmpUnitMotion)
+ return error;
+
+ ICmpPathfinder::pass_class_t spawnedPass = cmpUnitMotion->GetPassabilityClass();
+ CmpPtr cmpPathfinder(GetSystemEntity());
+ if (!cmpPathfinder)
+ return error;
+
+ // Get the Footprint entity passability
+ CmpPtr cmpEntityMotion(GetEntityHandle());
+ if (!cmpEntityMotion)
+ return error;
+ ICmpPathfinder::pass_class_t entityPass = cmpEntityMotion->GetPassabilityClass();
+
+ CFixedVector2D initialPos = cmpPosition->GetPosition2D();
+ entity_angle_t initialAngle = cmpPosition->GetRotation().Y;
+
+ // Max spawning distance + 1 (in meters)
+ const i32 maxSpawningDistance = 13;
+
+ if (m_Shape == CIRCLE)
+ {
+ // Expand outwards from foundation with a fixed step of 1 meter
+ for (i32 dist = 0; dist <= maxSpawningDistance; ++dist)
+ {
+ // The spawn point should be far enough from this footprint to fit the unit, plus a little gap
+ entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(1+dist);
+ entity_pos_t radius = m_Size0 + clearance;
+
+ // Try equally-spaced points around the circle in alternating directions, starting from the front
+ const i32 numPoints = 31 + 2*dist;
+ for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2]
+ {
+ entity_angle_t angle = initialAngle + (entity_angle_t::Pi()*2).Multiply(entity_angle_t::FromInt(i)/(int)numPoints);
+
+ fixed s, c;
+ sincos_approx(angle, s, c);
+
+ CFixedVector3D pos (initialPos.X + s.Multiply(radius), fixed::Zero(), initialPos.Y + c.Multiply(radius));
+
+ SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
+ if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Z, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS &&
+ cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Z, spawnedRadius, entityPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
+ return pos; // this position is okay, so return it
+ }
+ }
+ }
+ else
+ {
+ fixed s, c;
+ sincos_approx(initialAngle, s, c);
+
+ // Expand outwards from foundation with a fixed step of 1 meter
+ for (i32 dist = 0; dist <= maxSpawningDistance; ++dist)
+ {
+ // The spawn point should be far enough from this footprint to fit the unit, plus a little gap
+ entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(1+dist);
+
+ for (i32 edge = 0; edge < 4; ++edge)
+ {
+ // Compute the direction and length of the current edge
+ CFixedVector2D dir;
+ fixed sx, sy;
+ switch (edge)
+ {
+ case 0:
+ dir = CFixedVector2D(c, -s);
+ sx = m_Size0;
+ sy = m_Size1;
+ break;
+ case 1:
+ dir = CFixedVector2D(-s, -c);
+ sx = m_Size1;
+ sy = m_Size0;
+ break;
+ case 2:
+ dir = CFixedVector2D(s, c);
+ sx = m_Size1;
+ sy = m_Size0;
+ break;
+ case 3:
+ dir = CFixedVector2D(-c, s);
+ sx = m_Size0;
+ sy = m_Size1;
+ break;
+ }
+ sx = sx/2 + clearance;
+ sy = sy/2 + clearance;
+ // Try equally-spaced (1 meter) points along the edge in alternating directions, starting from the middle
+ i32 numPoints = 1 + 2*sx.ToInt_RoundToNearest();
+ CFixedVector2D center = initialPos - dir.Perpendicular().Multiply(sy);
+ for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2]
+ {
+ CFixedVector2D pos (center + dir*i);
+
+ SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
+ if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS &&
+ cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, spawnedRadius, entityPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
+ return CFixedVector3D(pos.X, fixed::Zero(), pos.Y); // this position is okay, so return it
+ }
+ }
+ }
+ }
+
+ return error;
+ }
};
REGISTER_COMPONENT_TYPE(Footprint)
Index: ps/trunk/source/simulation2/components/ICmpFootprint.h
===================================================================
--- ps/trunk/source/simulation2/components/ICmpFootprint.h (revision 14549)
+++ ps/trunk/source/simulation2/components/ICmpFootprint.h (revision 14550)
@@ -1,70 +1,77 @@
/* Copyright (C) 2010 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_ICMPFOOTPRINT
#define INCLUDED_ICMPFOOTPRINT
#include "simulation2/system/Interface.h"
#include "simulation2/helpers/Position.h"
class CFixedVector3D;
/**
* Footprints - an approximation of the entity's shape, used for collision detection and for
* rendering selection outlines.
* A footprint is either a circle (of some radius) or square (of some width and depth (actually
* it's a rectangle)), horizontally aligned, extruded to a given height.
*/
class ICmpFootprint : public IComponent
{
public:
enum EShape
{
CIRCLE,
SQUARE
};
/**
* Return the shape of this footprint.
* Shapes are horizontal circles or squares, extended vertically upwards to make cylinders or boxes.
* @param[out] shape either CIRCLE or SQUARE
* @param[out] size0 if CIRCLE then radius, else width (size in X axis)
* @param[out] size1 if CIRCLE then radius, else depth (size in Z axis)
* @param[out] height size in Y axis
*/
virtual void GetShape(EShape& shape, entity_pos_t& size0, entity_pos_t& size1, entity_pos_t& height) = 0;
/**
* GetShape wrapper for script calls.
* Returns { "type": "circle", "radius": 5.0, "height": 1.0 }
* or { "type": "square", "width": 5.0, "depth": 5.0, "height": 1.0 }
*/
CScriptVal GetShape_wrapper();
/**
* Pick a sensible position to place a newly-spawned entity near this footprint,
* such that it won't be in an invalid (obstructed) location regardless of the spawned unit's
* orientation.
* @return the X and Z coordinates of the spawn point, with Y = 0; or the special value (-1, -1, -1) if there's no space
*/
virtual CFixedVector3D PickSpawnPoint(entity_id_t spawned) = 0;
+ /**
+ * Pick a sensible position to place a newly-spawned entity near this footprint,
+ * at the intersection between the footprint passability and the entity one.
+ * @return the X and Z coordinates of the spawn point, with Y = 0; or the special value (-1, -1, -1) if there's no space
+ */
+ virtual CFixedVector3D PickSpawnPointBothPass(entity_id_t spawned) = 0;
+
DECLARE_INTERFACE_TYPE(Footprint)
};
#endif // INCLUDED_ICMPFOOTPRINT