restore random arrows.
This commit is contained in:
parent
ce2febaeba
commit
e8ba5dffcf
File diff suppressed because it is too large
Load Diff
@ -1,414 +1,354 @@
|
||||
// Number of rounds of firing per 2 seconds.
|
||||
// Number of rounds of firing per 2 seconds.
|
||||
const roundCount = 10;
|
||||
const attackType = "Ranged";
|
||||
|
||||
function BuildingAI() {}
|
||||
function BuildingAI() { }
|
||||
|
||||
BuildingAI.prototype.Schema =
|
||||
"<element name='DefaultArrowCount'>" +
|
||||
"<data type='nonNegativeInteger'/>" +
|
||||
"</element>" +
|
||||
"<optional>" +
|
||||
"<element name='MaxArrowCount' a:help='Limit the number of arrows to a certain amount'>" +
|
||||
"<data type='nonNegativeInteger'/>" +
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
"<element name='GarrisonArrowMultiplier'>" +
|
||||
"<ref name='nonNegativeDecimal'/>" +
|
||||
"</element>" +
|
||||
"<element name='GarrisonArrowClasses' a:help='Add extra arrows for this class list'>" +
|
||||
"<text/>" +
|
||||
"</element>";
|
||||
"<element name='DefaultArrowCount'>" +
|
||||
"<data type='nonNegativeInteger'/>" +
|
||||
"</element>" +
|
||||
"<optional>" +
|
||||
"<element name='MaxArrowCount' a:help='Limit the number of arrows to a certain amount'>" +
|
||||
"<data type='nonNegativeInteger'/>" +
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
"<element name='GarrisonArrowMultiplier'>" +
|
||||
"<ref name='nonNegativeDecimal'/>" +
|
||||
"</element>" +
|
||||
"<element name='GarrisonArrowClasses' a:help='Add extra arrows for this class list'>" +
|
||||
"<text/>" +
|
||||
"</element>";
|
||||
|
||||
BuildingAI.prototype.MAX_PREFERENCE_BONUS = 2;
|
||||
|
||||
BuildingAI.prototype.Init = function()
|
||||
{
|
||||
this.currentRound = 0;
|
||||
this.archersGarrisoned = 0;
|
||||
this.arrowsLeft = 0;
|
||||
this.targetUnits = [];
|
||||
this.focusTargets = [];
|
||||
BuildingAI.prototype.Init = function () {
|
||||
this.currentRound = 0;
|
||||
this.archersGarrisoned = 0;
|
||||
this.arrowsLeft = 0;
|
||||
this.targetUnits = [];
|
||||
};
|
||||
|
||||
BuildingAI.prototype.OnGarrisonedUnitsChanged = function(msg)
|
||||
{
|
||||
let classes = this.template.GarrisonArrowClasses;
|
||||
for (let ent of msg.added)
|
||||
{
|
||||
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
|
||||
if (cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), classes))
|
||||
++this.archersGarrisoned;
|
||||
}
|
||||
for (let ent of msg.removed)
|
||||
{
|
||||
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
|
||||
if (cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), classes))
|
||||
--this.archersGarrisoned;
|
||||
}
|
||||
BuildingAI.prototype.OnGarrisonedUnitsChanged = function (msg) {
|
||||
let classes = this.template.GarrisonArrowClasses;
|
||||
for (let ent of msg.added) {
|
||||
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
|
||||
if (cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), classes))
|
||||
++this.archersGarrisoned;
|
||||
}
|
||||
for (let ent of msg.removed) {
|
||||
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
|
||||
if (cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), classes))
|
||||
--this.archersGarrisoned;
|
||||
}
|
||||
};
|
||||
|
||||
BuildingAI.prototype.OnOwnershipChanged = function(msg)
|
||||
{
|
||||
this.targetUnits = [];
|
||||
this.focusTargets = [];
|
||||
this.SetupRangeQuery();
|
||||
this.SetupGaiaRangeQuery();
|
||||
BuildingAI.prototype.OnOwnershipChanged = function (msg) {
|
||||
this.targetUnits = [];
|
||||
this.SetupRangeQuery();
|
||||
this.SetupGaiaRangeQuery();
|
||||
};
|
||||
|
||||
BuildingAI.prototype.OnDiplomacyChanged = function(msg)
|
||||
{
|
||||
if (!IsOwnedByPlayer(msg.player, this.entity))
|
||||
return;
|
||||
BuildingAI.prototype.OnDiplomacyChanged = function (msg) {
|
||||
if (!IsOwnedByPlayer(msg.player, this.entity))
|
||||
return;
|
||||
|
||||
// Remove maybe now allied/neutral units.
|
||||
this.targetUnits = [];
|
||||
this.SetupRangeQuery();
|
||||
this.SetupGaiaRangeQuery();
|
||||
// Remove maybe now allied/neutral units.
|
||||
this.targetUnits = [];
|
||||
this.SetupRangeQuery();
|
||||
this.SetupGaiaRangeQuery();
|
||||
};
|
||||
|
||||
BuildingAI.prototype.OnDestroy = function()
|
||||
{
|
||||
if (this.timer)
|
||||
{
|
||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
cmpTimer.CancelTimer(this.timer);
|
||||
this.timer = undefined;
|
||||
}
|
||||
BuildingAI.prototype.OnDestroy = function () {
|
||||
if (this.timer) {
|
||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
cmpTimer.CancelTimer(this.timer);
|
||||
this.timer = undefined;
|
||||
}
|
||||
|
||||
// Clean up range queries.
|
||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
if (this.enemyUnitsQuery)
|
||||
cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery);
|
||||
if (this.gaiaUnitsQuery)
|
||||
cmpRangeManager.DestroyActiveQuery(this.gaiaUnitsQuery);
|
||||
// Clean up range queries.
|
||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
if (this.enemyUnitsQuery)
|
||||
cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery);
|
||||
if (this.gaiaUnitsQuery)
|
||||
cmpRangeManager.DestroyActiveQuery(this.gaiaUnitsQuery);
|
||||
};
|
||||
|
||||
/**
|
||||
* React on Attack value modifications, as it might influence the range.
|
||||
/**
|
||||
* React on Attack value modifications, as it might influence the range.
|
||||
*/
|
||||
BuildingAI.prototype.OnValueModification = function(msg)
|
||||
{
|
||||
if (msg.component != "Attack")
|
||||
return;
|
||||
BuildingAI.prototype.OnValueModification = function (msg) {
|
||||
if (msg.component != "Attack")
|
||||
return;
|
||||
|
||||
this.targetUnits = [];
|
||||
this.SetupRangeQuery();
|
||||
this.SetupGaiaRangeQuery();
|
||||
this.targetUnits = [];
|
||||
this.SetupRangeQuery();
|
||||
this.SetupGaiaRangeQuery();
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup the Range Query to detect units coming in & out of range.
|
||||
/**
|
||||
* Setup the Range Query to detect units coming in & out of range.
|
||||
*/
|
||||
BuildingAI.prototype.SetupRangeQuery = function()
|
||||
{
|
||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||
if (!cmpAttack)
|
||||
return;
|
||||
BuildingAI.prototype.SetupRangeQuery = function () {
|
||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||
if (!cmpAttack)
|
||||
return;
|
||||
|
||||
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
if (this.enemyUnitsQuery)
|
||||
{
|
||||
cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery);
|
||||
this.enemyUnitsQuery = undefined;
|
||||
}
|
||||
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
if (this.enemyUnitsQuery) {
|
||||
cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery);
|
||||
this.enemyUnitsQuery = undefined;
|
||||
}
|
||||
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity);
|
||||
if (!cmpPlayer)
|
||||
return;
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity);
|
||||
if (!cmpPlayer)
|
||||
return;
|
||||
|
||||
var enemies = cmpPlayer.GetEnemies();
|
||||
// Remove gaia.
|
||||
if (enemies.length && enemies[0] == 0)
|
||||
enemies.shift();
|
||||
var enemies = cmpPlayer.GetEnemies();
|
||||
// Remove gaia.
|
||||
if (enemies.length && enemies[0] == 0)
|
||||
enemies.shift();
|
||||
|
||||
if (!enemies.length)
|
||||
return;
|
||||
if (!enemies.length)
|
||||
return;
|
||||
|
||||
const range = cmpAttack.GetRange(attackType);
|
||||
const yOrigin = cmpAttack.GetAttackYOrigin(attackType);
|
||||
// This takes entity sizes into accounts, so no need to compensate for structure size.
|
||||
this.enemyUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery(
|
||||
this.entity, range.min, range.max, yOrigin,
|
||||
enemies, IID_Resistance, cmpRangeManager.GetEntityFlagMask("normal"));
|
||||
const range = cmpAttack.GetRange(attackType);
|
||||
const yOrigin = cmpAttack.GetAttackYOrigin(attackType);
|
||||
// This takes entity sizes into accounts, so no need to compensate for structure size.
|
||||
this.enemyUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery(
|
||||
this.entity, range.min, range.max, yOrigin,
|
||||
enemies, IID_Resistance, cmpRangeManager.GetEntityFlagMask("normal"));
|
||||
|
||||
cmpRangeManager.EnableActiveQuery(this.enemyUnitsQuery);
|
||||
cmpRangeManager.EnableActiveQuery(this.enemyUnitsQuery);
|
||||
};
|
||||
|
||||
// Set up a range query for Gaia units within LOS range which can be attacked.
|
||||
// This should be called whenever our ownership changes.
|
||||
BuildingAI.prototype.SetupGaiaRangeQuery = function()
|
||||
{
|
||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||
if (!cmpAttack)
|
||||
return;
|
||||
// Set up a range query for Gaia units within LOS range which can be attacked.
|
||||
// This should be called whenever our ownership changes.
|
||||
BuildingAI.prototype.SetupGaiaRangeQuery = function () {
|
||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||
if (!cmpAttack)
|
||||
return;
|
||||
|
||||
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
if (this.gaiaUnitsQuery)
|
||||
{
|
||||
cmpRangeManager.DestroyActiveQuery(this.gaiaUnitsQuery);
|
||||
this.gaiaUnitsQuery = undefined;
|
||||
}
|
||||
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
if (this.gaiaUnitsQuery) {
|
||||
cmpRangeManager.DestroyActiveQuery(this.gaiaUnitsQuery);
|
||||
this.gaiaUnitsQuery = undefined;
|
||||
}
|
||||
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity);
|
||||
if (!cmpPlayer || !cmpPlayer.IsEnemy(0))
|
||||
return;
|
||||
var cmpPlayer = QueryOwnerInterface(this.entity);
|
||||
if (!cmpPlayer || !cmpPlayer.IsEnemy(0))
|
||||
return;
|
||||
|
||||
const range = cmpAttack.GetRange(attackType);
|
||||
const yOrigin = cmpAttack.GetAttackYOrigin(attackType);
|
||||
const range = cmpAttack.GetRange(attackType);
|
||||
const yOrigin = cmpAttack.GetAttackYOrigin(attackType);
|
||||
|
||||
// This query is only interested in Gaia entities that can attack.
|
||||
// This takes entity sizes into accounts, so no need to compensate for structure size.
|
||||
this.gaiaUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery(
|
||||
this.entity, range.min, range.max, yOrigin,
|
||||
[0], IID_Attack, cmpRangeManager.GetEntityFlagMask("normal"));
|
||||
// This query is only interested in Gaia entities that can attack.
|
||||
// This takes entity sizes into accounts, so no need to compensate for structure size.
|
||||
this.gaiaUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery(
|
||||
this.entity, range.min, range.max, yOrigin,
|
||||
[0], IID_Attack, cmpRangeManager.GetEntityFlagMask("normal"));
|
||||
|
||||
cmpRangeManager.EnableActiveQuery(this.gaiaUnitsQuery);
|
||||
cmpRangeManager.EnableActiveQuery(this.gaiaUnitsQuery);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when units enter or leave range.
|
||||
/**
|
||||
* Called when units enter or leave range.
|
||||
*/
|
||||
BuildingAI.prototype.OnRangeUpdate = function(msg)
|
||||
{
|
||||
BuildingAI.prototype.OnRangeUpdate = function (msg) {
|
||||
|
||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||
if (!cmpAttack)
|
||||
return;
|
||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||
if (!cmpAttack)
|
||||
return;
|
||||
|
||||
// Target enemy units except non-dangerous animals.
|
||||
if (msg.tag == this.gaiaUnitsQuery)
|
||||
{
|
||||
msg.added = msg.added.filter(e => {
|
||||
let cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI);
|
||||
return cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal());
|
||||
});
|
||||
}
|
||||
else if (msg.tag != this.enemyUnitsQuery)
|
||||
return;
|
||||
// Target enemy units except non-dangerous animals.
|
||||
if (msg.tag == this.gaiaUnitsQuery) {
|
||||
msg.added = msg.added.filter(e => {
|
||||
let cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI);
|
||||
return cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal());
|
||||
});
|
||||
}
|
||||
else if (msg.tag != this.enemyUnitsQuery)
|
||||
return;
|
||||
|
||||
// Add new targets.
|
||||
for (let entity of msg.added)
|
||||
if (cmpAttack.CanAttack(entity))
|
||||
this.targetUnits.push(entity);
|
||||
// Add new targets.
|
||||
for (let entity of msg.added)
|
||||
if (cmpAttack.CanAttack(entity))
|
||||
this.targetUnits.push(entity);
|
||||
|
||||
// Remove targets outside of vision-range.
|
||||
for (let entity of msg.removed)
|
||||
{
|
||||
let index = this.targetUnits.indexOf(entity);
|
||||
if (index > -1)
|
||||
this.targetUnits.splice(index, 1);
|
||||
}
|
||||
// Remove targets outside of vision-range.
|
||||
for (let entity of msg.removed) {
|
||||
let index = this.targetUnits.indexOf(entity);
|
||||
if (index > -1)
|
||||
this.targetUnits.splice(index, 1);
|
||||
}
|
||||
|
||||
if (this.targetUnits.length)
|
||||
this.StartTimer();
|
||||
if (this.targetUnits.length)
|
||||
this.StartTimer();
|
||||
};
|
||||
|
||||
BuildingAI.prototype.StartTimer = function()
|
||||
{
|
||||
if (this.timer)
|
||||
return;
|
||||
BuildingAI.prototype.StartTimer = function () {
|
||||
if (this.timer)
|
||||
return;
|
||||
|
||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||
if (!cmpAttack)
|
||||
return;
|
||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||
if (!cmpAttack)
|
||||
return;
|
||||
|
||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
var attackTimers = cmpAttack.GetTimers(attackType);
|
||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
var attackTimers = cmpAttack.GetTimers(attackType);
|
||||
|
||||
this.timer = cmpTimer.SetInterval(this.entity, IID_BuildingAI, "FireArrows",
|
||||
attackTimers.prepare, attackTimers.repeat / roundCount, null);
|
||||
this.timer = cmpTimer.SetInterval(this.entity, IID_BuildingAI, "FireArrows",
|
||||
attackTimers.prepare, attackTimers.repeat / roundCount, null);
|
||||
};
|
||||
|
||||
BuildingAI.prototype.GetDefaultArrowCount = function()
|
||||
{
|
||||
var arrowCount = +this.template.DefaultArrowCount;
|
||||
return Math.round(ApplyValueModificationsToEntity("BuildingAI/DefaultArrowCount", arrowCount, this.entity));
|
||||
BuildingAI.prototype.GetDefaultArrowCount = function () {
|
||||
var arrowCount = +this.template.DefaultArrowCount;
|
||||
return Math.round(ApplyValueModificationsToEntity("BuildingAI/DefaultArrowCount", arrowCount, this.entity));
|
||||
};
|
||||
|
||||
BuildingAI.prototype.GetMaxArrowCount = function()
|
||||
{
|
||||
if (!this.template.MaxArrowCount)
|
||||
return Infinity;
|
||||
BuildingAI.prototype.GetMaxArrowCount = function () {
|
||||
if (!this.template.MaxArrowCount)
|
||||
return Infinity;
|
||||
|
||||
let maxArrowCount = +this.template.MaxArrowCount;
|
||||
return Math.round(ApplyValueModificationsToEntity("BuildingAI/MaxArrowCount", maxArrowCount, this.entity));
|
||||
let maxArrowCount = +this.template.MaxArrowCount;
|
||||
return Math.round(ApplyValueModificationsToEntity("BuildingAI/MaxArrowCount", maxArrowCount, this.entity));
|
||||
};
|
||||
|
||||
BuildingAI.prototype.GetGarrisonArrowMultiplier = function()
|
||||
{
|
||||
var arrowMult = +this.template.GarrisonArrowMultiplier;
|
||||
return ApplyValueModificationsToEntity("BuildingAI/GarrisonArrowMultiplier", arrowMult, this.entity);
|
||||
BuildingAI.prototype.GetGarrisonArrowMultiplier = function () {
|
||||
var arrowMult = +this.template.GarrisonArrowMultiplier;
|
||||
return ApplyValueModificationsToEntity("BuildingAI/GarrisonArrowMultiplier", arrowMult, this.entity);
|
||||
};
|
||||
|
||||
BuildingAI.prototype.GetGarrisonArrowClasses = function()
|
||||
{
|
||||
var string = this.template.GarrisonArrowClasses;
|
||||
if (string)
|
||||
return string.split(/\s+/);
|
||||
return [];
|
||||
BuildingAI.prototype.GetGarrisonArrowClasses = function () {
|
||||
var string = this.template.GarrisonArrowClasses;
|
||||
if (string)
|
||||
return string.split(/\s+/);
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the number of arrows which needs to be fired.
|
||||
* DefaultArrowCount + Garrisoned Archers (i.e., any unit capable
|
||||
* of shooting arrows from inside buildings).
|
||||
/**
|
||||
* Returns the number of arrows which needs to be fired.
|
||||
* DefaultArrowCount + Garrisoned Archers (i.e., any unit capable
|
||||
* of shooting arrows from inside buildings).
|
||||
*/
|
||||
BuildingAI.prototype.GetArrowCount = function()
|
||||
{
|
||||
let count = this.GetDefaultArrowCount() +
|
||||
Math.round(this.archersGarrisoned * this.GetGarrisonArrowMultiplier());
|
||||
BuildingAI.prototype.GetArrowCount = function () {
|
||||
let count = this.GetDefaultArrowCount() +
|
||||
Math.round(this.archersGarrisoned * this.GetGarrisonArrowMultiplier());
|
||||
|
||||
return Math.min(count, this.GetMaxArrowCount());
|
||||
return Math.min(count, this.GetMaxArrowCount());
|
||||
};
|
||||
|
||||
BuildingAI.prototype.SetUnitAITarget = function(ent)
|
||||
{
|
||||
this.unitAITarget = ent;
|
||||
if (ent)
|
||||
this.StartTimer();
|
||||
BuildingAI.prototype.SetUnitAITarget = function (ent) {
|
||||
this.unitAITarget = ent;
|
||||
if (ent)
|
||||
this.StartTimer();
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds index to keep track of the user-targeted units supporting a queue
|
||||
* @param {ent} - Target of rallypoint selection when selection is an enemy unit from unit_actions.js
|
||||
/**
|
||||
* Fire arrows with random temporal distribution on prefered targets.
|
||||
* Called 'roundCount' times every 'RepeatTime' seconds when there are units in the range.
|
||||
*/
|
||||
BuildingAI.prototype.AddFocusTarget = function(ent, queued, push)
|
||||
{
|
||||
if (!ent || this.targetUnits.indexOf(ent) === -1)
|
||||
return;
|
||||
if (queued)
|
||||
this.focusTargets.push({"entityId": ent});
|
||||
else if (push)
|
||||
this.focusTargets.unshift({"entityId": ent});
|
||||
else
|
||||
this.focusTargets = [{"entityId": ent}];
|
||||
};
|
||||
BuildingAI.prototype.FireArrows = function () {
|
||||
if (!this.targetUnits.length && !this.unitAITarget) {
|
||||
if (!this.timer)
|
||||
return;
|
||||
|
||||
/**
|
||||
* Fire arrows with random temporal distribution on prefered targets.
|
||||
* Called 'roundCount' times every 'RepeatTime' seconds when there are units in the range.
|
||||
*/
|
||||
BuildingAI.prototype.FireArrows = function()
|
||||
{
|
||||
if (!this.targetUnits.length && !this.unitAITarget)
|
||||
{
|
||||
if (!this.timer)
|
||||
return;
|
||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
cmpTimer.CancelTimer(this.timer);
|
||||
this.timer = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
cmpTimer.CancelTimer(this.timer);
|
||||
this.timer = undefined;
|
||||
return;
|
||||
}
|
||||
let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||
if (!cmpAttack)
|
||||
return;
|
||||
|
||||
let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
|
||||
if (!cmpAttack)
|
||||
return;
|
||||
if (this.currentRound > roundCount - 1)
|
||||
this.currentRound = 0;
|
||||
|
||||
if (this.currentRound > roundCount - 1)
|
||||
this.currentRound = 0;
|
||||
if (this.currentRound == 0)
|
||||
this.arrowsLeft = this.GetArrowCount();
|
||||
|
||||
if (this.currentRound == 0)
|
||||
this.arrowsLeft = this.GetArrowCount();
|
||||
let arrowsToFire = 0;
|
||||
if (this.currentRound == roundCount - 1)
|
||||
arrowsToFire = this.arrowsLeft;
|
||||
else
|
||||
arrowsToFire = Math.min(
|
||||
randIntInclusive(0, 2 * this.GetArrowCount() / roundCount),
|
||||
this.arrowsLeft
|
||||
);
|
||||
|
||||
let arrowsToFire = 0;
|
||||
if (this.currentRound == roundCount - 1)
|
||||
arrowsToFire = this.arrowsLeft;
|
||||
else
|
||||
arrowsToFire = Math.min(
|
||||
randIntInclusive(0, 2 * this.GetArrowCount() / roundCount),
|
||||
this.arrowsLeft
|
||||
);
|
||||
if (arrowsToFire <= 0) {
|
||||
++this.currentRound;
|
||||
return;
|
||||
}
|
||||
|
||||
if (arrowsToFire <= 0)
|
||||
{
|
||||
++this.currentRound;
|
||||
return;
|
||||
}
|
||||
// Add targets to a weighted list, to allow preferences.
|
||||
let targets = new WeightedList();
|
||||
let maxPreference = this.MAX_PREFERENCE_BONUS;
|
||||
let addTarget = function (target) {
|
||||
let preference = cmpAttack.GetPreference(target);
|
||||
let weight = 1;
|
||||
|
||||
if (preference !== null && preference !== undefined)
|
||||
weight += maxPreference / (1 + preference);
|
||||
|
||||
targets.push(target, weight);
|
||||
};
|
||||
|
||||
// Add targets to a list.
|
||||
let targets = [];
|
||||
let addTarget = function(target)
|
||||
{
|
||||
const pref = (cmpAttack.GetPreference(target) ?? 49);
|
||||
targets.push({"entityId": target, "preference": pref});
|
||||
};
|
||||
// Add the UnitAI target separately, as the UnitMotion and RangeManager implementations differ.
|
||||
if (this.unitAITarget && this.targetUnits.indexOf(this.unitAITarget) == -1)
|
||||
addTarget(this.unitAITarget);
|
||||
else if (this.unitAITarget && this.targetUnits.indexOf(this.unitAITarget) != -1)
|
||||
this.focusTargets = [{"entityId": this.unitAITarget}];
|
||||
if (!this.focusTargets.length)
|
||||
{
|
||||
for (let target of this.targetUnits)
|
||||
addTarget(target);
|
||||
// Sort targets by preference and then by proximity.
|
||||
targets.sort( (a,b) => {
|
||||
if (a.preference > b.preference) return 1;
|
||||
else if (a.preference < b.preference) return -1;
|
||||
else if (PositionHelper.DistanceBetweenEntities(this.entity,a.entityId) > PositionHelper.DistanceBetweenEntities(this.entity,b.entityId)) return 1;
|
||||
return -1;
|
||||
});
|
||||
addTarget(this.unitAITarget);
|
||||
for (let target of this.targetUnits)
|
||||
addTarget(target);
|
||||
|
||||
// The obstruction manager performs approximate range checks.
|
||||
// so we need to verify them here.
|
||||
// TODO: perhaps an optional 'precise' mode to range queries would be more performant.
|
||||
const cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
|
||||
const range = cmpAttack.GetRange(attackType);
|
||||
const yOrigin = cmpAttack.GetAttackYOrigin(attackType);
|
||||
|
||||
let firedArrows = 0;
|
||||
while (firedArrows < arrowsToFire && targets.length()) {
|
||||
const selectedTarget = targets.randomItem();
|
||||
if (this.CheckTargetVisible(selectedTarget) && cmpObstructionManager.IsInTargetParabolicRange(
|
||||
this.entity,
|
||||
selectedTarget,
|
||||
range.min,
|
||||
range.max,
|
||||
yOrigin,
|
||||
false)) {
|
||||
cmpAttack.PerformAttack(attackType, selectedTarget);
|
||||
PlaySound("attack_" + attackType.toLowerCase(), this.entity);
|
||||
++firedArrows;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Could not attack target, try a different target.
|
||||
targets.remove(selectedTarget);
|
||||
}
|
||||
else
|
||||
targets = this.focusTargets;
|
||||
|
||||
// The obstruction manager performs approximate range checks.
|
||||
// so we need to verify them here.
|
||||
// TODO: perhaps an optional 'precise' mode to range queries would be more performant.
|
||||
const cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
|
||||
const range = cmpAttack.GetRange(attackType);
|
||||
const yOrigin = cmpAttack.GetAttackYOrigin(attackType);
|
||||
|
||||
let firedArrows = 0;
|
||||
let killedTargets = 0;
|
||||
while (firedArrows < arrowsToFire && killedTargets < targets.length)
|
||||
{
|
||||
|
||||
let selectedTarget = targets[killedTargets].entityId;
|
||||
if (this.CheckTargetVisible(selectedTarget) && cmpObstructionManager.IsInTargetParabolicRange(
|
||||
this.entity,
|
||||
selectedTarget,
|
||||
range.min,
|
||||
range.max,
|
||||
yOrigin,
|
||||
false))
|
||||
{
|
||||
cmpAttack.PerformAttack(attackType, selectedTarget);
|
||||
PlaySound("attack_" + attackType.toLowerCase(), this.entity);
|
||||
++firedArrows;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Could not attack target, try a different target.
|
||||
++killedTargets;
|
||||
}
|
||||
}
|
||||
targets.splice(0,killedTargets);
|
||||
killedTargets = 0;//not sure if this is necessary
|
||||
this.arrowsLeft -= firedArrows;
|
||||
++this.currentRound;
|
||||
this.arrowsLeft -= firedArrows;
|
||||
++this.currentRound;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the target entity is visible through the FoW/SoD.
|
||||
/**
|
||||
* Returns true if the target entity is visible through the FoW/SoD.
|
||||
*/
|
||||
BuildingAI.prototype.CheckTargetVisible = function(target)
|
||||
{
|
||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
if (!cmpOwnership)
|
||||
return false;
|
||||
BuildingAI.prototype.CheckTargetVisible = function (target) {
|
||||
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
|
||||
if (!cmpOwnership)
|
||||
return false;
|
||||
|
||||
// Entities that are hidden and miraged are considered visible.
|
||||
var cmpFogging = Engine.QueryInterface(target, IID_Fogging);
|
||||
if (cmpFogging && cmpFogging.IsMiraged(cmpOwnership.GetOwner()))
|
||||
return true;
|
||||
// Entities that are hidden and miraged are considered visible.
|
||||
var cmpFogging = Engine.QueryInterface(target, IID_Fogging);
|
||||
if (cmpFogging && cmpFogging.IsMiraged(cmpOwnership.GetOwner()))
|
||||
return true;
|
||||
|
||||
// Either visible directly, or visible in fog.
|
||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
return cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner()) != "hidden";
|
||||
// Either visible directly, or visible in fog.
|
||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
|
||||
return cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner()) != "hidden";
|
||||
};
|
||||
|
||||
Engine.RegisterComponentType(IID_BuildingAI, "BuildingAI", BuildingAI);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,48 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="structures/han/civil_centre">
|
||||
<BuildRestrictions>
|
||||
<Category>ImperialCourt</Category>
|
||||
</BuildRestrictions>
|
||||
<Capturable>
|
||||
<CapturePoints op="mul">1.5</CapturePoints>
|
||||
</Capturable>
|
||||
<GarrisonHolder>
|
||||
<Max op="mul">1.5</Max>
|
||||
</GarrisonHolder>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>10</DefaultArrowCount>
|
||||
<MaxArrowCount>24</MaxArrowCount>
|
||||
</BuildingAI>
|
||||
<Health>
|
||||
<Max op="mul">1.5</Max>
|
||||
</Health>
|
||||
<Identity>
|
||||
<Civ>han</Civ>
|
||||
<GenericName>Imperial Court</GenericName>
|
||||
<SpecificName>Cháotíng</SpecificName>
|
||||
<VisibleClasses datatype="tokens">Defensive ImperialCourt City</VisibleClasses>
|
||||
<Classes datatype="tokens">CivCentre CivSpecific</Classes>
|
||||
<RequiredTechnology>phase_city</RequiredTechnology>
|
||||
<Icon>structures/military_settlement.png</Icon>
|
||||
</Identity>
|
||||
<Population>
|
||||
<Bonus>30</Bonus>
|
||||
</Population>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
-phase_town_{civ}
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<Trainer>
|
||||
<BatchTimeModifier op="mul">0.75</BatchTimeModifier>
|
||||
<Entities datatype="tokens">
|
||||
units/{civ}/hero_han_xin_horse
|
||||
units/{civ}/hero_liu_bang_horse
|
||||
units/{civ}/hero_wei_qing_chariot
|
||||
</Entities>
|
||||
</Trainer>
|
||||
<Upgrade disable=""/>
|
||||
<VisualActor>
|
||||
<Actor>structures/han/imperial_court.xml</Actor>
|
||||
</VisualActor>
|
||||
<BuildRestrictions>
|
||||
<Category>ImperialCourt</Category>
|
||||
</BuildRestrictions>
|
||||
<Capturable>
|
||||
<CapturePoints op="mul">1.5</CapturePoints>
|
||||
</Capturable>
|
||||
<GarrisonHolder>
|
||||
<Max op="mul">1.5</Max>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max op="mul">1.5</Max>
|
||||
</Health>
|
||||
<Identity>
|
||||
<Civ>han</Civ>
|
||||
<GenericName>Imperial Court</GenericName>
|
||||
<SpecificName>Cháotíng</SpecificName>
|
||||
<VisibleClasses datatype="tokens">Defensive ImperialCourt City</VisibleClasses>
|
||||
<Classes datatype="tokens">CivCentre CivSpecific</Classes>
|
||||
<RequiredTechnology>phase_city</RequiredTechnology>
|
||||
<Icon>structures/military_settlement.png</Icon>
|
||||
</Identity>
|
||||
<Population>
|
||||
<Bonus>30</Bonus>
|
||||
</Population>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
-phase_town_{civ}
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<Trainer>
|
||||
<BatchTimeModifier op="mul">0.5</BatchTimeModifier>
|
||||
<Entities datatype="tokens">
|
||||
units/{civ}/hero_han_xin_horse
|
||||
units/{civ}/hero_liu_bang_horse
|
||||
units/{civ}/hero_wei_qing_chariot
|
||||
</Entities>
|
||||
</Trainer>
|
||||
<Upgrade disable=""/>
|
||||
<VisualActor>
|
||||
<Actor>structures/han/imperial_court.xml</Actor>
|
||||
</VisualActor>
|
||||
</Entity>
|
||||
|
@ -1,50 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive_tower_stone">
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<MinRange>12</MinRange>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>4</DefaultArrowCount>
|
||||
</BuildingAI>
|
||||
<Cost>
|
||||
<BuildTime>200</BuildTime>
|
||||
<Resources>
|
||||
<wood>50</wood>
|
||||
<stone>250</stone>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Footprint replace="">
|
||||
<Circle radius="8.0"/>
|
||||
<Height>12.0</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<Max>8</Max>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>2400</Max>
|
||||
<SpawnEntityOnDeath>decay|rubble/rubble_stone_3x3</SpawnEntityOnDeath>
|
||||
</Health>
|
||||
<Identity>
|
||||
<Civ>iber</Civ>
|
||||
<SpecificName>Dorre</SpecificName>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>10</wood>
|
||||
<stone>50</stone>
|
||||
</Loot>
|
||||
<Obstruction>
|
||||
<Static width="14.0" depth="14.0"/>
|
||||
</Obstruction>
|
||||
<StatusBars>
|
||||
<HeightOffset>17.0</HeightOffset>
|
||||
</StatusBars>
|
||||
<TerritoryInfluence>
|
||||
<Radius>38</Radius>
|
||||
</TerritoryInfluence>
|
||||
<VisualActor>
|
||||
<Actor>structures/iberians/scout_tower.xml</Actor>
|
||||
<FoundationActor>structures/fndn_4x4.xml</FoundationActor>
|
||||
</VisualActor>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<MinRange>12</MinRange>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>2</DefaultArrowCount>
|
||||
</BuildingAI>
|
||||
<Cost>
|
||||
<BuildTime>200</BuildTime>
|
||||
<Resources>
|
||||
<wood>50</wood>
|
||||
<stone>250</stone>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Footprint replace="">
|
||||
<Circle radius="8.0"/>
|
||||
<Height>12.0</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<Max>8</Max>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>2400</Max>
|
||||
<SpawnEntityOnDeath>decay|rubble/rubble_stone_3x3</SpawnEntityOnDeath>
|
||||
</Health>
|
||||
<Identity>
|
||||
<Civ>iber</Civ>
|
||||
<SpecificName>Dorre</SpecificName>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>10</wood>
|
||||
<stone>50</stone>
|
||||
</Loot>
|
||||
<Obstruction>
|
||||
<Static width="14.0" depth="14.0"/>
|
||||
</Obstruction>
|
||||
<StatusBars>
|
||||
<HeightOffset>17.0</HeightOffset>
|
||||
</StatusBars>
|
||||
<TerritoryInfluence>
|
||||
<Radius>38</Radius>
|
||||
</TerritoryInfluence>
|
||||
<VisualActor>
|
||||
<Actor>structures/iberians/scout_tower.xml</Actor>
|
||||
<FoundationActor>structures/fndn_4x4.xml</FoundationActor>
|
||||
</VisualActor>
|
||||
</Entity>
|
||||
|
@ -1,152 +1,151 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_civic">
|
||||
<AlertRaiser>
|
||||
<List datatype="tokens">FemaleCitizen</List>
|
||||
<RaiseAlertRange>120</RaiseAlertRange>
|
||||
<EndOfAlertRange>180</EndOfAlertRange>
|
||||
<SearchRange>100</SearchRange>
|
||||
</AlertRaiser>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<AttackName>Bow</AttackName>
|
||||
<Damage>
|
||||
<Pierce>10</Pierce>
|
||||
</Damage>
|
||||
<MaxRange>60</MaxRange>
|
||||
<PrepareTime>1200</PrepareTime>
|
||||
<RepeatTime>4000</RepeatTime>
|
||||
<Projectile>
|
||||
<Speed>100</Speed>
|
||||
<Spread>2</Spread>
|
||||
<Gravity>50</Gravity>
|
||||
<FriendlyFire>false</FriendlyFire>
|
||||
<LaunchPoint y="3"/>
|
||||
</Projectile>
|
||||
<PreferredClasses datatype="tokens">Human</PreferredClasses>
|
||||
<RangeOverlay>
|
||||
<LineTexture>outline_border.png</LineTexture>
|
||||
<LineTextureMask>outline_border_mask.png</LineTextureMask>
|
||||
<LineThickness>0.175</LineThickness>
|
||||
</RangeOverlay>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<BuildRestrictions>
|
||||
<Territory>own neutral</Territory>
|
||||
<Category>CivilCentre</Category>
|
||||
<Distance>
|
||||
<FromClass>CivilCentre</FromClass>
|
||||
<MinDistance>200</MinDistance>
|
||||
</Distance>
|
||||
</BuildRestrictions>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>8</DefaultArrowCount>
|
||||
<GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
|
||||
<GarrisonArrowClasses>Soldier</GarrisonArrowClasses>
|
||||
<MaxArrowCount>18</MaxArrowCount>
|
||||
</BuildingAI>
|
||||
<Capturable>
|
||||
<CapturePoints>2500</CapturePoints>
|
||||
<RegenRate>5.0</RegenRate>
|
||||
</Capturable>
|
||||
<Cost>
|
||||
<BuildTime>500</BuildTime>
|
||||
<Resources>
|
||||
<wood>300</wood>
|
||||
<stone>300</stone>
|
||||
<metal>250</metal>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Footprint>
|
||||
<Square depth="32.0" width="32.0"/>
|
||||
<Height>8.0</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<Max>20</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>1</LoadingRange>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>3000</Max>
|
||||
<SpawnEntityOnDeath>decay|rubble/rubble_stone_6x6</SpawnEntityOnDeath>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Civic Center</GenericName>
|
||||
<SelectionGroupName>template_structure_civic_civil_centre</SelectionGroupName>
|
||||
<Tooltip>Build in own or neutral territory. Acquire large tracts of territory. Territory root. Train Citizens and research technologies. Garrison Soldiers for additional arrows.</Tooltip>
|
||||
<Classes datatype="tokens">CivCentre</Classes>
|
||||
<VisibleClasses datatype="tokens">Defensive CivilCentre</VisibleClasses>
|
||||
<Icon>structures/civic_centre.png</Icon>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>60</wood>
|
||||
<stone>60</stone>
|
||||
<metal>50</metal>
|
||||
</Loot>
|
||||
<Minimap>
|
||||
<Type>structure</Type>
|
||||
<Icon size="16.0">civil_centre.png</Icon>
|
||||
</Minimap>
|
||||
<Obstruction>
|
||||
<Static depth="30.0" width="30.0"/>
|
||||
</Obstruction>
|
||||
<Population>
|
||||
<Bonus>20</Bonus>
|
||||
</Population>
|
||||
<ProductionQueue/>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
phase_town_{civ}
|
||||
phase_city_{civ}
|
||||
unlock_shared_los
|
||||
unlock_shared_dropsites
|
||||
unlock_spies
|
||||
spy_counter
|
||||
archery_tradition
|
||||
hoplite_tradition
|
||||
hellenistic_metropolis
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack op="add">5</Hack>
|
||||
<Pierce op="add">5</Pierce>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<ResourceDropsite>
|
||||
<Types>food wood stone metal</Types>
|
||||
<Sharable>true</Sharable>
|
||||
</ResourceDropsite>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/building/sel_civ_center.xml</select>
|
||||
<constructed>interface/complete/building/complete_civ_center.xml</constructed>
|
||||
<upgraded>interface/complete/building/complete_civ_center.xml</upgraded>
|
||||
<alert_raise>interface/alarm/alarm_alert_0.xml</alert_raise>
|
||||
<alert_end>interface/alarm/alarm_alert_1.xml</alert_end>
|
||||
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
|
||||
<attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryInfluence>
|
||||
<Root>true</Root>
|
||||
<Radius>140</Radius>
|
||||
<Weight>10000</Weight>
|
||||
</TerritoryInfluence>
|
||||
<Trainer>
|
||||
<BatchTimeModifier>0.8</BatchTimeModifier>
|
||||
<Entities datatype="tokens">
|
||||
units/{civ}/support_female_citizen
|
||||
</Entities>
|
||||
</Trainer>
|
||||
<Vision>
|
||||
<Range>90</Range>
|
||||
</Vision>
|
||||
<VisualActor>
|
||||
<FoundationActor>structures/fndn_8x8.xml</FoundationActor>
|
||||
</VisualActor>
|
||||
<AlertRaiser>
|
||||
<List datatype="tokens">FemaleCitizen</List>
|
||||
<RaiseAlertRange>120</RaiseAlertRange>
|
||||
<EndOfAlertRange>180</EndOfAlertRange>
|
||||
<SearchRange>100</SearchRange>
|
||||
</AlertRaiser>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<AttackName>Bow</AttackName>
|
||||
<Damage>
|
||||
<Pierce>10</Pierce>
|
||||
</Damage>
|
||||
<MaxRange>60</MaxRange>
|
||||
<PrepareTime>1200</PrepareTime>
|
||||
<RepeatTime>2000</RepeatTime>
|
||||
<Projectile>
|
||||
<Speed>100</Speed>
|
||||
<Spread>1.5</Spread>
|
||||
<Gravity>50</Gravity>
|
||||
<FriendlyFire>false</FriendlyFire>
|
||||
<LaunchPoint y="3"/>
|
||||
</Projectile>
|
||||
<PreferredClasses datatype="tokens">Human</PreferredClasses>
|
||||
<RangeOverlay>
|
||||
<LineTexture>outline_border.png</LineTexture>
|
||||
<LineTextureMask>outline_border_mask.png</LineTextureMask>
|
||||
<LineThickness>0.175</LineThickness>
|
||||
</RangeOverlay>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<BuildRestrictions>
|
||||
<Territory>own neutral</Territory>
|
||||
<Category>CivilCentre</Category>
|
||||
<Distance>
|
||||
<FromClass>CivilCentre</FromClass>
|
||||
<MinDistance>200</MinDistance>
|
||||
</Distance>
|
||||
</BuildRestrictions>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>3</DefaultArrowCount>
|
||||
<GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
|
||||
<GarrisonArrowClasses>Soldier</GarrisonArrowClasses>
|
||||
</BuildingAI>
|
||||
<Capturable>
|
||||
<CapturePoints>2500</CapturePoints>
|
||||
<RegenRate>5.0</RegenRate>
|
||||
</Capturable>
|
||||
<Cost>
|
||||
<BuildTime>500</BuildTime>
|
||||
<Resources>
|
||||
<wood>300</wood>
|
||||
<stone>300</stone>
|
||||
<metal>250</metal>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Footprint>
|
||||
<Square depth="32.0" width="32.0"/>
|
||||
<Height>8.0</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<Max>20</Max>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry Cavalry</List>
|
||||
<BuffHeal>1</BuffHeal>
|
||||
<LoadingRange>1</LoadingRange>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>3000</Max>
|
||||
<SpawnEntityOnDeath>decay|rubble/rubble_stone_6x6</SpawnEntityOnDeath>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Civic Center</GenericName>
|
||||
<SelectionGroupName>template_structure_civic_civil_centre</SelectionGroupName>
|
||||
<Tooltip>Build in own or neutral territory. Acquire large tracts of territory. Territory root. Train Citizens and research technologies. Garrison Soldiers for additional arrows.</Tooltip>
|
||||
<Classes datatype="tokens">CivCentre</Classes>
|
||||
<VisibleClasses datatype="tokens">Defensive CivilCentre</VisibleClasses>
|
||||
<Icon>structures/civic_centre.png</Icon>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>60</wood>
|
||||
<stone>60</stone>
|
||||
<metal>50</metal>
|
||||
</Loot>
|
||||
<Minimap>
|
||||
<Type>structure</Type>
|
||||
<Icon size="16.0">civil_centre.png</Icon>
|
||||
</Minimap>
|
||||
<Obstruction>
|
||||
<Static depth="30.0" width="30.0"/>
|
||||
</Obstruction>
|
||||
<Population>
|
||||
<Bonus>20</Bonus>
|
||||
</Population>
|
||||
<ProductionQueue/>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
phase_town_{civ}
|
||||
phase_city_{civ}
|
||||
unlock_shared_los
|
||||
unlock_shared_dropsites
|
||||
unlock_spies
|
||||
spy_counter
|
||||
archery_tradition
|
||||
hoplite_tradition
|
||||
hellenistic_metropolis
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<Resistance>
|
||||
<Entity>
|
||||
<Damage>
|
||||
<Hack op="add">5</Hack>
|
||||
<Pierce op="add">5</Pierce>
|
||||
</Damage>
|
||||
</Entity>
|
||||
</Resistance>
|
||||
<ResourceDropsite>
|
||||
<Types>food wood stone metal</Types>
|
||||
<Sharable>true</Sharable>
|
||||
</ResourceDropsite>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/building/sel_civ_center.xml</select>
|
||||
<constructed>interface/complete/building/complete_civ_center.xml</constructed>
|
||||
<upgraded>interface/complete/building/complete_civ_center.xml</upgraded>
|
||||
<alert_raise>interface/alarm/alarm_alert_0.xml</alert_raise>
|
||||
<alert_end>interface/alarm/alarm_alert_1.xml</alert_end>
|
||||
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
|
||||
<attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryInfluence>
|
||||
<Root>true</Root>
|
||||
<Radius>140</Radius>
|
||||
<Weight>10000</Weight>
|
||||
</TerritoryInfluence>
|
||||
<Trainer>
|
||||
<BatchTimeModifier>0.8</BatchTimeModifier>
|
||||
<Entities datatype="tokens">
|
||||
units/{civ}/support_female_citizen
|
||||
</Entities>
|
||||
</Trainer>
|
||||
<Vision>
|
||||
<Range>90</Range>
|
||||
</Vision>
|
||||
<VisualActor>
|
||||
<FoundationActor>structures/fndn_8x8.xml</FoundationActor>
|
||||
</VisualActor>
|
||||
</Entity>
|
||||
|
@ -1,55 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_civic_civil_centre">
|
||||
<BuildRestrictions>
|
||||
<Territory>own neutral</Territory>
|
||||
<Category>Colony</Category>
|
||||
<Distance>
|
||||
<FromClass>CivilCentre</FromClass>
|
||||
<MinDistance>120</MinDistance>
|
||||
</Distance>
|
||||
</BuildRestrictions>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>6</DefaultArrowCount>
|
||||
<MaxArrowCount>16</MaxArrowCount>
|
||||
</BuildingAI>
|
||||
<Cost>
|
||||
<BuildTime>300</BuildTime>
|
||||
<Resources>
|
||||
<wood>200</wood>
|
||||
<stone>200</stone>
|
||||
<metal>150</metal>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Health>
|
||||
<Max>2000</Max>
|
||||
<SpawnEntityOnDeath>decay|rubble/rubble_stone_5x5</SpawnEntityOnDeath>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Military Colony</GenericName>
|
||||
<SelectionGroupName>template_structure_civic_civil_centre_military_colony</SelectionGroupName>
|
||||
<VisibleClasses datatype="tokens">Colony</VisibleClasses>
|
||||
<Icon>structures/military_settlement.png</Icon>
|
||||
<RequiredTechnology>phase_town</RequiredTechnology>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>40</wood>
|
||||
<stone>40</stone>
|
||||
<metal>30</metal>
|
||||
</Loot>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
-phase_town_{civ}
|
||||
-phase_city_{civ}
|
||||
-hellenistic_metropolis
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/building/sel_gymnasium.xml</select>
|
||||
<constructed>interface/complete/building/complete_gymnasium.xml</constructed>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryInfluence>
|
||||
<Radius>75</Radius>
|
||||
</TerritoryInfluence>
|
||||
<BuildRestrictions>
|
||||
<Territory>own neutral</Territory>
|
||||
<Category>Colony</Category>
|
||||
<Distance>
|
||||
<FromClass>CivilCentre</FromClass>
|
||||
<MinDistance>120</MinDistance>
|
||||
</Distance>
|
||||
</BuildRestrictions>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>1</DefaultArrowCount>
|
||||
</BuildingAI>
|
||||
<Cost>
|
||||
<BuildTime>300</BuildTime>
|
||||
<Resources>
|
||||
<wood>200</wood>
|
||||
<stone>200</stone>
|
||||
<metal>150</metal>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Health>
|
||||
<Max>2000</Max>
|
||||
<SpawnEntityOnDeath>decay|rubble/rubble_stone_5x5</SpawnEntityOnDeath>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Military Colony</GenericName>
|
||||
<SelectionGroupName>template_structure_civic_civil_centre_military_colony</SelectionGroupName>
|
||||
<VisibleClasses datatype="tokens">Colony</VisibleClasses>
|
||||
<Icon>structures/military_settlement.png</Icon>
|
||||
<RequiredTechnology>phase_town</RequiredTechnology>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>40</wood>
|
||||
<stone>40</stone>
|
||||
<metal>30</metal>
|
||||
</Loot>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
-phase_town_{civ}
|
||||
-phase_city_{civ}
|
||||
-hellenistic_metropolis
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/building/sel_gymnasium.xml</select>
|
||||
<constructed>interface/complete/building/complete_gymnasium.xml</constructed>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryInfluence>
|
||||
<Radius>75</Radius>
|
||||
</TerritoryInfluence>
|
||||
</Entity>
|
||||
|
@ -1,72 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive">
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<AttackName>Bow</AttackName>
|
||||
<Damage>
|
||||
<Pierce>11</Pierce>
|
||||
</Damage>
|
||||
<MaxRange>60</MaxRange>
|
||||
<MinRange>10</MinRange>
|
||||
<PrepareTime>1200</PrepareTime>
|
||||
<RepeatTime>4000</RepeatTime>
|
||||
<Projectile>
|
||||
<Speed>100</Speed>
|
||||
<Spread>1.5</Spread>
|
||||
<Gravity>50</Gravity>
|
||||
<FriendlyFire>false</FriendlyFire>
|
||||
<LaunchPoint y="3"/>
|
||||
</Projectile>
|
||||
<PreferredClasses datatype="tokens">Human</PreferredClasses>
|
||||
<RangeOverlay>
|
||||
<LineTexture>outline_border.png</LineTexture>
|
||||
<LineTextureMask>outline_border_mask.png</LineTextureMask>
|
||||
<LineThickness>0.175</LineThickness>
|
||||
</RangeOverlay>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>2</DefaultArrowCount>
|
||||
<GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
|
||||
<GarrisonArrowClasses>Infantry</GarrisonArrowClasses>
|
||||
</BuildingAI>
|
||||
<BuildRestrictions>
|
||||
<Category>Tower</Category>
|
||||
<Distance>
|
||||
<FromClass>Tower</FromClass>
|
||||
<MinDistance>60</MinDistance>
|
||||
</Distance>
|
||||
</BuildRestrictions>
|
||||
<GarrisonHolder>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry</List>
|
||||
<BuffHeal>0</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>1000</Max>
|
||||
<SpawnEntityOnDeath>decay|rubble/rubble_stone_2x2</SpawnEntityOnDeath>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Tower</GenericName>
|
||||
<VisibleClasses datatype="tokens">Tower</VisibleClasses>
|
||||
</Identity>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<constructed>interface/complete/building/complete_tower.xml</constructed>
|
||||
<select>interface/select/building/sel_tower.xml</select>
|
||||
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
|
||||
<attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<StatusBars>
|
||||
<HeightOffset>18.0</HeightOffset>
|
||||
</StatusBars>
|
||||
<Vision>
|
||||
<Range>80</Range>
|
||||
</Vision>
|
||||
<VisualActor>
|
||||
<FoundationActor>structures/fndn_3x3.xml</FoundationActor>
|
||||
</VisualActor>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<AttackName>Bow</AttackName>
|
||||
<Damage>
|
||||
<Pierce>11</Pierce>
|
||||
</Damage>
|
||||
<MaxRange>60</MaxRange>
|
||||
<MinRange>10</MinRange>
|
||||
<PrepareTime>1200</PrepareTime>
|
||||
<RepeatTime>2000</RepeatTime>
|
||||
<Projectile>
|
||||
<Speed>100</Speed>
|
||||
<Spread>1.5</Spread>
|
||||
<Gravity>50</Gravity>
|
||||
<FriendlyFire>false</FriendlyFire>
|
||||
<LaunchPoint y="3"/>
|
||||
</Projectile>
|
||||
<PreferredClasses datatype="tokens">Human</PreferredClasses>
|
||||
<RangeOverlay>
|
||||
<LineTexture>outline_border.png</LineTexture>
|
||||
<LineTextureMask>outline_border_mask.png</LineTextureMask>
|
||||
<LineThickness>0.175</LineThickness>
|
||||
</RangeOverlay>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>1</DefaultArrowCount>
|
||||
<GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
|
||||
<GarrisonArrowClasses>Infantry</GarrisonArrowClasses>
|
||||
</BuildingAI>
|
||||
<BuildRestrictions>
|
||||
<Category>Tower</Category>
|
||||
<Distance>
|
||||
<FromClass>Tower</FromClass>
|
||||
<MinDistance>60</MinDistance>
|
||||
</Distance>
|
||||
</BuildRestrictions>
|
||||
<GarrisonHolder>
|
||||
<EjectHealth>0.1</EjectHealth>
|
||||
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
|
||||
<List datatype="tokens">Support Infantry</List>
|
||||
<BuffHeal>0</BuffHeal>
|
||||
<LoadingRange>2</LoadingRange>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>1000</Max>
|
||||
<SpawnEntityOnDeath>decay|rubble/rubble_stone_2x2</SpawnEntityOnDeath>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Tower</GenericName>
|
||||
<VisibleClasses datatype="tokens">Tower</VisibleClasses>
|
||||
</Identity>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<constructed>interface/complete/building/complete_tower.xml</constructed>
|
||||
<select>interface/select/building/sel_tower.xml</select>
|
||||
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
|
||||
<attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<StatusBars>
|
||||
<HeightOffset>18.0</HeightOffset>
|
||||
</StatusBars>
|
||||
<Vision>
|
||||
<Range>80</Range>
|
||||
</Vision>
|
||||
<VisualActor>
|
||||
<FoundationActor>structures/fndn_3x3.xml</FoundationActor>
|
||||
</VisualActor>
|
||||
</Entity>
|
||||
|
@ -1,69 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive_tower">
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<Origin>
|
||||
<X>0</X>
|
||||
<Y>9</Y>
|
||||
<Z>0</Z>
|
||||
</Origin>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<BuildingAI>
|
||||
<MaxArrowCount>5</MaxArrowCount>
|
||||
</BuildingAI>
|
||||
<Cost>
|
||||
<BuildTime>40</BuildTime>
|
||||
<Resources>
|
||||
<wood>125</wood>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Footprint>
|
||||
<Square width="9.5" depth="8.5"/>
|
||||
<Height>9.0</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<Max>3</Max>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>400</Max>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Sentry Tower</GenericName>
|
||||
<SelectionGroupName>template_structure_defensive_tower_sentry</SelectionGroupName>
|
||||
<Tooltip>Garrison Infantry for additional arrows. Needs the “Murder Holes” technology to protect its foot.</Tooltip>
|
||||
<VisibleClasses datatype="tokens">SentryTower</VisibleClasses>
|
||||
<Icon>structures/sentry_tower.png</Icon>
|
||||
<RequiredTechnology>phase_village</RequiredTechnology>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>25</wood>
|
||||
</Loot>
|
||||
<Obstruction>
|
||||
<Static width="9.0" depth="7.5"/>
|
||||
</Obstruction>
|
||||
<ProductionQueue/>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
tower_watch
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>16</Radius>
|
||||
<Weight>30000</Weight>
|
||||
</TerritoryInfluence>
|
||||
<Upgrade>
|
||||
<Tower>
|
||||
<Entity>structures/{civ}/defense_tower</Entity>
|
||||
<Tooltip>Reinforce with stone and upgrade to a defense tower.</Tooltip>
|
||||
<RequiredTechnology>phase_town</RequiredTechnology>
|
||||
<Cost>
|
||||
<wood>25</wood>
|
||||
<stone>100</stone>
|
||||
</Cost>
|
||||
<Variant>upgrading</Variant>
|
||||
<Time>100</Time>
|
||||
</Tower>
|
||||
</Upgrade>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<Origin>
|
||||
<X>0</X>
|
||||
<Y>9</Y>
|
||||
<Z>0</Z>
|
||||
</Origin>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<BuildingAI>
|
||||
<MaxArrowCount>4</MaxArrowCount>
|
||||
</BuildingAI>
|
||||
<Cost>
|
||||
<BuildTime>40</BuildTime>
|
||||
<Resources>
|
||||
<wood>100</wood>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Footprint>
|
||||
<Square width="9.5" depth="8.5"/>
|
||||
<Height>9.0</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<Max>3</Max>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>400</Max>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Sentry Tower</GenericName>
|
||||
<SelectionGroupName>template_structure_defensive_tower_sentry</SelectionGroupName>
|
||||
<Tooltip>Garrison Infantry for additional arrows. Needs the “Murder Holes” technology to protect its foot.</Tooltip>
|
||||
<VisibleClasses datatype="tokens">SentryTower</VisibleClasses>
|
||||
<Icon>structures/sentry_tower.png</Icon>
|
||||
<RequiredTechnology>phase_village</RequiredTechnology>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>20</wood>
|
||||
</Loot>
|
||||
<Obstruction>
|
||||
<Static width="9.0" depth="7.5"/>
|
||||
</Obstruction>
|
||||
<ProductionQueue/>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
tower_watch
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>16</Radius>
|
||||
<Weight>30000</Weight>
|
||||
</TerritoryInfluence>
|
||||
<Upgrade>
|
||||
<Tower>
|
||||
<Entity>structures/{civ}/defense_tower</Entity>
|
||||
<Tooltip>Reinforce with stone and upgrade to a defense tower.</Tooltip>
|
||||
<RequiredTechnology>phase_town</RequiredTechnology>
|
||||
<Cost>
|
||||
<wood>50</wood>
|
||||
<stone>100</stone>
|
||||
</Cost>
|
||||
<Variant>upgrading</Variant>
|
||||
<Time>100</Time>
|
||||
</Tower>
|
||||
</Upgrade>
|
||||
</Entity>
|
||||
|
@ -1,62 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_defensive_tower">
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<Origin>
|
||||
<X>0</X>
|
||||
<Y>15</Y>
|
||||
<Z>0</Z>
|
||||
</Origin>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<Cost>
|
||||
<BuildTime>150</BuildTime>
|
||||
<Resources>
|
||||
<wood>100</wood>
|
||||
<stone>100</stone>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Footprint>
|
||||
<Square width="10.0" depth="10.0"/>
|
||||
<Height>15.0</Height>
|
||||
</Footprint>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>3</DefaultArrowCount>
|
||||
</BuildingAI>
|
||||
<GarrisonHolder>
|
||||
<Max>5</Max>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>1000</Max>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Stone Tower</GenericName>
|
||||
<SelectionGroupName>template_structure_defensive_tower_stone</SelectionGroupName>
|
||||
<Tooltip>Garrison Infantry for additional arrows. Needs the “Murder Holes” technology to protect its foot.</Tooltip>
|
||||
<VisibleClasses datatype="tokens">StoneTower</VisibleClasses>
|
||||
<Icon>structures/defense_tower.png</Icon>
|
||||
<RequiredTechnology>phase_town</RequiredTechnology>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>20</wood>
|
||||
<stone>20</stone>
|
||||
</Loot>
|
||||
<Obstruction>
|
||||
<Static width="7.0" depth="7.0"/>
|
||||
</Obstruction>
|
||||
<ProductionQueue/>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
tower_watch
|
||||
tower_crenellations
|
||||
tower_range
|
||||
tower_murderholes
|
||||
tower_health
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>32</Radius>
|
||||
<Weight>30000</Weight>
|
||||
</TerritoryInfluence>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<Origin>
|
||||
<X>0</X>
|
||||
<Y>15</Y>
|
||||
<Z>0</Z>
|
||||
</Origin>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<Cost>
|
||||
<BuildTime>150</BuildTime>
|
||||
<Resources>
|
||||
<wood>100</wood>
|
||||
<stone>100</stone>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Footprint>
|
||||
<Square width="10.0" depth="10.0"/>
|
||||
<Height>15.0</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<Max>5</Max>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>1000</Max>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Stone Tower</GenericName>
|
||||
<SelectionGroupName>template_structure_defensive_tower_stone</SelectionGroupName>
|
||||
<Tooltip>Garrison Infantry for additional arrows. Needs the “Murder Holes” technology to protect its foot.</Tooltip>
|
||||
<VisibleClasses datatype="tokens">StoneTower</VisibleClasses>
|
||||
<Icon>structures/defense_tower.png</Icon>
|
||||
<RequiredTechnology>phase_town</RequiredTechnology>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>20</wood>
|
||||
<stone>20</stone>
|
||||
</Loot>
|
||||
<Obstruction>
|
||||
<Static width="7.0" depth="7.0"/>
|
||||
</Obstruction>
|
||||
<ProductionQueue/>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
tower_watch
|
||||
tower_crenellations
|
||||
tower_range
|
||||
tower_murderholes
|
||||
tower_health
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<TerritoryInfluence>
|
||||
<Root>false</Root>
|
||||
<Radius>32</Radius>
|
||||
<Weight>30000</Weight>
|
||||
</TerritoryInfluence>
|
||||
</Entity>
|
||||
|
@ -1,112 +1,112 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Entity parent="template_structure_military">
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<AttackName>Bow</AttackName>
|
||||
<Damage>
|
||||
<Pierce>10</Pierce>
|
||||
</Damage>
|
||||
<MaxRange>60</MaxRange>
|
||||
<PrepareTime>1200</PrepareTime>
|
||||
<RepeatTime>4000</RepeatTime>
|
||||
<Projectile>
|
||||
<Speed>100</Speed>
|
||||
<Spread>2.5</Spread>
|
||||
<Gravity>50</Gravity>
|
||||
<FriendlyFire>false</FriendlyFire>
|
||||
<LaunchPoint y="3"/>
|
||||
</Projectile>
|
||||
<PreferredClasses datatype="tokens">Human</PreferredClasses>
|
||||
<RangeOverlay>
|
||||
<LineTexture>outline_border.png</LineTexture>
|
||||
<LineTextureMask>outline_border_mask.png</LineTextureMask>
|
||||
<LineThickness>0.175</LineThickness>
|
||||
</RangeOverlay>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<BuildRestrictions>
|
||||
<Category>Fortress</Category>
|
||||
<Distance>
|
||||
<FromClass>Fortress</FromClass>
|
||||
<MinDistance>80</MinDistance>
|
||||
</Distance>
|
||||
</BuildRestrictions>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>12</DefaultArrowCount>
|
||||
<GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
|
||||
<GarrisonArrowClasses>Soldier</GarrisonArrowClasses>
|
||||
</BuildingAI>
|
||||
<Capturable>
|
||||
<CapturePoints op="mul">8</CapturePoints>
|
||||
<RegenRate>10.0</RegenRate>
|
||||
</Capturable>
|
||||
<Cost>
|
||||
<BuildTime>450</BuildTime>
|
||||
<Resources>
|
||||
<wood>300</wood>
|
||||
<stone>600</stone>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Footprint>
|
||||
<Square width="30.0" depth="30.0"/>
|
||||
<Height>8.0</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<Max>20</Max>
|
||||
<EjectHealth>0.075</EjectHealth>
|
||||
<List datatype="tokens">Support Infantry Cavalry Siege</List>
|
||||
<LoadingRange>6</LoadingRange>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>5200</Max>
|
||||
<SpawnEntityOnDeath>decay|rubble/rubble_stone_6x6</SpawnEntityOnDeath>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Fortress</GenericName>
|
||||
<SelectionGroupName>template_structure_military_fortress</SelectionGroupName>
|
||||
<Tooltip>Garrison Soldiers for additional arrows.</Tooltip>
|
||||
<Classes datatype="tokens">GarrisonFortress</Classes>
|
||||
<VisibleClasses datatype="tokens">Defensive Fortress</VisibleClasses>
|
||||
<Icon>structures/fortress.png</Icon>
|
||||
<RequiredTechnology>phase_city</RequiredTechnology>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>60</wood>
|
||||
<stone>120</stone>
|
||||
</Loot>
|
||||
<Obstruction>
|
||||
<Static width="25.0" depth="25.0"/>
|
||||
</Obstruction>
|
||||
<ProductionQueue/>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
attack_soldiers_will
|
||||
art_of_war
|
||||
poison_arrows
|
||||
poison_blades
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/building/sel_fortress.xml</select>
|
||||
<constructed>interface/complete/building/complete_fortress.xml</constructed>
|
||||
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
|
||||
<attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryDecay>
|
||||
<DecayRate op="mul">2</DecayRate>
|
||||
</TerritoryDecay>
|
||||
<TerritoryInfluence>
|
||||
<Radius>80</Radius>
|
||||
</TerritoryInfluence>
|
||||
<Trainer>
|
||||
<BatchTimeModifier>0.8</BatchTimeModifier>
|
||||
</Trainer>
|
||||
<Vision>
|
||||
<Range>90</Range>
|
||||
</Vision>
|
||||
<VisualActor>
|
||||
<FoundationActor>structures/fndn_8x8.xml</FoundationActor>
|
||||
</VisualActor>
|
||||
<Attack>
|
||||
<Ranged>
|
||||
<AttackName>Bow</AttackName>
|
||||
<Damage>
|
||||
<Pierce>10</Pierce>
|
||||
</Damage>
|
||||
<MaxRange>60</MaxRange>
|
||||
<PrepareTime>1200</PrepareTime>
|
||||
<RepeatTime>2000</RepeatTime>
|
||||
<Projectile>
|
||||
<Speed>100</Speed>
|
||||
<Spread>1.5</Spread>
|
||||
<Gravity>50</Gravity>
|
||||
<FriendlyFire>false</FriendlyFire>
|
||||
<LaunchPoint y="3"/>
|
||||
</Projectile>
|
||||
<PreferredClasses datatype="tokens">Human</PreferredClasses>
|
||||
<RangeOverlay>
|
||||
<LineTexture>outline_border.png</LineTexture>
|
||||
<LineTextureMask>outline_border_mask.png</LineTextureMask>
|
||||
<LineThickness>0.175</LineThickness>
|
||||
</RangeOverlay>
|
||||
</Ranged>
|
||||
</Attack>
|
||||
<BuildRestrictions>
|
||||
<Category>Fortress</Category>
|
||||
<Distance>
|
||||
<FromClass>Fortress</FromClass>
|
||||
<MinDistance>80</MinDistance>
|
||||
</Distance>
|
||||
</BuildRestrictions>
|
||||
<BuildingAI>
|
||||
<DefaultArrowCount>4</DefaultArrowCount>
|
||||
<GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
|
||||
<GarrisonArrowClasses>Soldier</GarrisonArrowClasses>
|
||||
</BuildingAI>
|
||||
<Capturable>
|
||||
<CapturePoints op="mul">8</CapturePoints>
|
||||
<RegenRate>10.0</RegenRate>
|
||||
</Capturable>
|
||||
<Cost>
|
||||
<BuildTime>450</BuildTime>
|
||||
<Resources>
|
||||
<wood>300</wood>
|
||||
<stone>600</stone>
|
||||
</Resources>
|
||||
</Cost>
|
||||
<Footprint>
|
||||
<Square width="30.0" depth="30.0"/>
|
||||
<Height>8.0</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<Max>20</Max>
|
||||
<EjectHealth>0.075</EjectHealth>
|
||||
<List datatype="tokens">Support Infantry Cavalry Siege</List>
|
||||
<LoadingRange>6</LoadingRange>
|
||||
</GarrisonHolder>
|
||||
<Health>
|
||||
<Max>5200</Max>
|
||||
<SpawnEntityOnDeath>decay|rubble/rubble_stone_6x6</SpawnEntityOnDeath>
|
||||
</Health>
|
||||
<Identity>
|
||||
<GenericName>Fortress</GenericName>
|
||||
<SelectionGroupName>template_structure_military_fortress</SelectionGroupName>
|
||||
<Tooltip>Garrison Soldiers for additional arrows.</Tooltip>
|
||||
<Classes datatype="tokens">GarrisonFortress</Classes>
|
||||
<VisibleClasses datatype="tokens">Defensive Fortress</VisibleClasses>
|
||||
<Icon>structures/fortress.png</Icon>
|
||||
<RequiredTechnology>phase_city</RequiredTechnology>
|
||||
</Identity>
|
||||
<Loot>
|
||||
<wood>60</wood>
|
||||
<stone>120</stone>
|
||||
</Loot>
|
||||
<Obstruction>
|
||||
<Static width="25.0" depth="25.0"/>
|
||||
</Obstruction>
|
||||
<ProductionQueue/>
|
||||
<Researcher>
|
||||
<Technologies datatype="tokens">
|
||||
attack_soldiers_will
|
||||
art_of_war
|
||||
poison_arrows
|
||||
poison_blades
|
||||
</Technologies>
|
||||
</Researcher>
|
||||
<Sound>
|
||||
<SoundGroups>
|
||||
<select>interface/select/building/sel_fortress.xml</select>
|
||||
<constructed>interface/complete/building/complete_fortress.xml</constructed>
|
||||
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
|
||||
<attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged>
|
||||
</SoundGroups>
|
||||
</Sound>
|
||||
<TerritoryDecay>
|
||||
<DecayRate op="mul">2</DecayRate>
|
||||
</TerritoryDecay>
|
||||
<TerritoryInfluence>
|
||||
<Radius>80</Radius>
|
||||
</TerritoryInfluence>
|
||||
<Trainer>
|
||||
<BatchTimeModifier>0.8</BatchTimeModifier>
|
||||
</Trainer>
|
||||
<Vision>
|
||||
<Range>90</Range>
|
||||
</Vision>
|
||||
<VisualActor>
|
||||
<FoundationActor>structures/fndn_8x8.xml</FoundationActor>
|
||||
</VisualActor>
|
||||
</Entity>
|
||||
|
Loading…
Reference in New Issue
Block a user