restore random arrows.

This commit is contained in:
rca 2024-06-04 22:10:51 -07:00 committed by rts
parent ce2febaeba
commit e8ba5dffcf
11 changed files with 1093 additions and 3319 deletions

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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