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 roundCount = 10;
const attackType = "Ranged"; const attackType = "Ranged";
function BuildingAI() {} function BuildingAI() { }
BuildingAI.prototype.Schema = BuildingAI.prototype.Schema =
"<element name='DefaultArrowCount'>" + "<element name='DefaultArrowCount'>" +
"<data type='nonNegativeInteger'/>" + "<data type='nonNegativeInteger'/>" +
"</element>" + "</element>" +
"<optional>" + "<optional>" +
"<element name='MaxArrowCount' a:help='Limit the number of arrows to a certain amount'>" + "<element name='MaxArrowCount' a:help='Limit the number of arrows to a certain amount'>" +
"<data type='nonNegativeInteger'/>" + "<data type='nonNegativeInteger'/>" +
"</element>" + "</element>" +
"</optional>" + "</optional>" +
"<element name='GarrisonArrowMultiplier'>" + "<element name='GarrisonArrowMultiplier'>" +
"<ref name='nonNegativeDecimal'/>" + "<ref name='nonNegativeDecimal'/>" +
"</element>" + "</element>" +
"<element name='GarrisonArrowClasses' a:help='Add extra arrows for this class list'>" + "<element name='GarrisonArrowClasses' a:help='Add extra arrows for this class list'>" +
"<text/>" + "<text/>" +
"</element>"; "</element>";
BuildingAI.prototype.MAX_PREFERENCE_BONUS = 2; BuildingAI.prototype.MAX_PREFERENCE_BONUS = 2;
BuildingAI.prototype.Init = function() BuildingAI.prototype.Init = function () {
{ this.currentRound = 0;
this.currentRound = 0; this.archersGarrisoned = 0;
this.archersGarrisoned = 0; this.arrowsLeft = 0;
this.arrowsLeft = 0; this.targetUnits = [];
this.targetUnits = [];
this.focusTargets = [];
}; };
BuildingAI.prototype.OnGarrisonedUnitsChanged = function(msg) BuildingAI.prototype.OnGarrisonedUnitsChanged = function (msg) {
{ let classes = this.template.GarrisonArrowClasses;
let classes = this.template.GarrisonArrowClasses; for (let ent of msg.added) {
for (let ent of msg.added) let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
{ if (cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), classes))
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); ++this.archersGarrisoned;
if (cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), classes)) }
++this.archersGarrisoned; for (let ent of msg.removed) {
} let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
for (let ent of msg.removed) if (cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), classes))
{ --this.archersGarrisoned;
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); }
if (cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), classes))
--this.archersGarrisoned;
}
}; };
BuildingAI.prototype.OnOwnershipChanged = function(msg) BuildingAI.prototype.OnOwnershipChanged = function (msg) {
{ this.targetUnits = [];
this.targetUnits = []; this.SetupRangeQuery();
this.focusTargets = []; this.SetupGaiaRangeQuery();
this.SetupRangeQuery();
this.SetupGaiaRangeQuery();
}; };
BuildingAI.prototype.OnDiplomacyChanged = function(msg) BuildingAI.prototype.OnDiplomacyChanged = function (msg) {
{ if (!IsOwnedByPlayer(msg.player, this.entity))
if (!IsOwnedByPlayer(msg.player, this.entity)) return;
return;
// Remove maybe now allied/neutral units. // Remove maybe now allied/neutral units.
this.targetUnits = []; this.targetUnits = [];
this.SetupRangeQuery(); this.SetupRangeQuery();
this.SetupGaiaRangeQuery(); this.SetupGaiaRangeQuery();
}; };
BuildingAI.prototype.OnDestroy = function() BuildingAI.prototype.OnDestroy = function () {
{ if (this.timer) {
if (this.timer) let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
{ cmpTimer.CancelTimer(this.timer);
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); this.timer = undefined;
cmpTimer.CancelTimer(this.timer); }
this.timer = undefined;
}
// Clean up range queries. // Clean up range queries.
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (this.enemyUnitsQuery) if (this.enemyUnitsQuery)
cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery); cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery);
if (this.gaiaUnitsQuery) if (this.gaiaUnitsQuery)
cmpRangeManager.DestroyActiveQuery(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) BuildingAI.prototype.OnValueModification = function (msg) {
{ if (msg.component != "Attack")
if (msg.component != "Attack") return;
return;
this.targetUnits = []; this.targetUnits = [];
this.SetupRangeQuery(); this.SetupRangeQuery();
this.SetupGaiaRangeQuery(); 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() BuildingAI.prototype.SetupRangeQuery = function () {
{ var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); if (!cmpAttack)
if (!cmpAttack) return;
return;
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (this.enemyUnitsQuery) if (this.enemyUnitsQuery) {
{ cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery);
cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery); this.enemyUnitsQuery = undefined;
this.enemyUnitsQuery = undefined; }
}
var cmpPlayer = QueryOwnerInterface(this.entity); var cmpPlayer = QueryOwnerInterface(this.entity);
if (!cmpPlayer) if (!cmpPlayer)
return; return;
var enemies = cmpPlayer.GetEnemies(); var enemies = cmpPlayer.GetEnemies();
// Remove gaia. // Remove gaia.
if (enemies.length && enemies[0] == 0) if (enemies.length && enemies[0] == 0)
enemies.shift(); enemies.shift();
if (!enemies.length) if (!enemies.length)
return; return;
const range = cmpAttack.GetRange(attackType); const range = cmpAttack.GetRange(attackType);
const yOrigin = cmpAttack.GetAttackYOrigin(attackType); const yOrigin = cmpAttack.GetAttackYOrigin(attackType);
// This takes entity sizes into accounts, so no need to compensate for structure size. // This takes entity sizes into accounts, so no need to compensate for structure size.
this.enemyUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery( this.enemyUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery(
this.entity, range.min, range.max, yOrigin, this.entity, range.min, range.max, yOrigin,
enemies, IID_Resistance, cmpRangeManager.GetEntityFlagMask("normal")); 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. // Set up a range query for Gaia units within LOS range which can be attacked.
// This should be called whenever our ownership changes. // This should be called whenever our ownership changes.
BuildingAI.prototype.SetupGaiaRangeQuery = function() BuildingAI.prototype.SetupGaiaRangeQuery = function () {
{ var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); if (!cmpAttack)
if (!cmpAttack) return;
return;
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (this.gaiaUnitsQuery) if (this.gaiaUnitsQuery) {
{ cmpRangeManager.DestroyActiveQuery(this.gaiaUnitsQuery);
cmpRangeManager.DestroyActiveQuery(this.gaiaUnitsQuery); this.gaiaUnitsQuery = undefined;
this.gaiaUnitsQuery = undefined; }
}
var cmpPlayer = QueryOwnerInterface(this.entity); var cmpPlayer = QueryOwnerInterface(this.entity);
if (!cmpPlayer || !cmpPlayer.IsEnemy(0)) if (!cmpPlayer || !cmpPlayer.IsEnemy(0))
return; return;
const range = cmpAttack.GetRange(attackType); const range = cmpAttack.GetRange(attackType);
const yOrigin = cmpAttack.GetAttackYOrigin(attackType); const yOrigin = cmpAttack.GetAttackYOrigin(attackType);
// This query is only interested in Gaia entities that can attack. // 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 takes entity sizes into accounts, so no need to compensate for structure size.
this.gaiaUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery( this.gaiaUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery(
this.entity, range.min, range.max, yOrigin, this.entity, range.min, range.max, yOrigin,
[0], IID_Attack, cmpRangeManager.GetEntityFlagMask("normal")); [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); var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
if (!cmpAttack) if (!cmpAttack)
return; return;
// Target enemy units except non-dangerous animals. // Target enemy units except non-dangerous animals.
if (msg.tag == this.gaiaUnitsQuery) if (msg.tag == this.gaiaUnitsQuery) {
{ msg.added = msg.added.filter(e => {
msg.added = msg.added.filter(e => { let cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI);
let cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI); return cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal());
return cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal()); });
}); }
} else if (msg.tag != this.enemyUnitsQuery)
else if (msg.tag != this.enemyUnitsQuery) return;
return;
// Add new targets. // Add new targets.
for (let entity of msg.added) for (let entity of msg.added)
if (cmpAttack.CanAttack(entity)) if (cmpAttack.CanAttack(entity))
this.targetUnits.push(entity); this.targetUnits.push(entity);
// Remove targets outside of vision-range. // Remove targets outside of vision-range.
for (let entity of msg.removed) for (let entity of msg.removed) {
{ let index = this.targetUnits.indexOf(entity);
let index = this.targetUnits.indexOf(entity); if (index > -1)
if (index > -1) this.targetUnits.splice(index, 1);
this.targetUnits.splice(index, 1); }
}
if (this.targetUnits.length) if (this.targetUnits.length)
this.StartTimer(); this.StartTimer();
}; };
BuildingAI.prototype.StartTimer = function() BuildingAI.prototype.StartTimer = function () {
{ if (this.timer)
if (this.timer) return;
return;
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
if (!cmpAttack) if (!cmpAttack)
return; return;
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
var attackTimers = cmpAttack.GetTimers(attackType); var attackTimers = cmpAttack.GetTimers(attackType);
this.timer = cmpTimer.SetInterval(this.entity, IID_BuildingAI, "FireArrows", this.timer = cmpTimer.SetInterval(this.entity, IID_BuildingAI, "FireArrows",
attackTimers.prepare, attackTimers.repeat / roundCount, null); attackTimers.prepare, attackTimers.repeat / roundCount, null);
}; };
BuildingAI.prototype.GetDefaultArrowCount = function() BuildingAI.prototype.GetDefaultArrowCount = function () {
{ var arrowCount = +this.template.DefaultArrowCount;
var arrowCount = +this.template.DefaultArrowCount; return Math.round(ApplyValueModificationsToEntity("BuildingAI/DefaultArrowCount", arrowCount, this.entity));
return Math.round(ApplyValueModificationsToEntity("BuildingAI/DefaultArrowCount", arrowCount, this.entity));
}; };
BuildingAI.prototype.GetMaxArrowCount = function() BuildingAI.prototype.GetMaxArrowCount = function () {
{ if (!this.template.MaxArrowCount)
if (!this.template.MaxArrowCount) return Infinity;
return Infinity;
let maxArrowCount = +this.template.MaxArrowCount; let maxArrowCount = +this.template.MaxArrowCount;
return Math.round(ApplyValueModificationsToEntity("BuildingAI/MaxArrowCount", maxArrowCount, this.entity)); return Math.round(ApplyValueModificationsToEntity("BuildingAI/MaxArrowCount", maxArrowCount, this.entity));
}; };
BuildingAI.prototype.GetGarrisonArrowMultiplier = function() BuildingAI.prototype.GetGarrisonArrowMultiplier = function () {
{ var arrowMult = +this.template.GarrisonArrowMultiplier;
var arrowMult = +this.template.GarrisonArrowMultiplier; return ApplyValueModificationsToEntity("BuildingAI/GarrisonArrowMultiplier", arrowMult, this.entity);
return ApplyValueModificationsToEntity("BuildingAI/GarrisonArrowMultiplier", arrowMult, this.entity);
}; };
BuildingAI.prototype.GetGarrisonArrowClasses = function() BuildingAI.prototype.GetGarrisonArrowClasses = function () {
{ var string = this.template.GarrisonArrowClasses;
var string = this.template.GarrisonArrowClasses; if (string)
if (string) return string.split(/\s+/);
return string.split(/\s+/); return [];
return [];
}; };
/** /**
* Returns the number of arrows which needs to be fired. * Returns the number of arrows which needs to be fired.
* DefaultArrowCount + Garrisoned Archers (i.e., any unit capable * DefaultArrowCount + Garrisoned Archers (i.e., any unit capable
* of shooting arrows from inside buildings). * of shooting arrows from inside buildings).
*/ */
BuildingAI.prototype.GetArrowCount = function() BuildingAI.prototype.GetArrowCount = function () {
{ let count = this.GetDefaultArrowCount() +
let count = this.GetDefaultArrowCount() + Math.round(this.archersGarrisoned * this.GetGarrisonArrowMultiplier());
Math.round(this.archersGarrisoned * this.GetGarrisonArrowMultiplier());
return Math.min(count, this.GetMaxArrowCount()); return Math.min(count, this.GetMaxArrowCount());
}; };
BuildingAI.prototype.SetUnitAITarget = function(ent) BuildingAI.prototype.SetUnitAITarget = function (ent) {
{ this.unitAITarget = ent;
this.unitAITarget = ent; if (ent)
if (ent) this.StartTimer();
this.StartTimer();
}; };
/** /**
* Adds index to keep track of the user-targeted units supporting a queue * Fire arrows with random temporal distribution on prefered targets.
* @param {ent} - Target of rallypoint selection when selection is an enemy unit from unit_actions.js * Called 'roundCount' times every 'RepeatTime' seconds when there are units in the range.
*/ */
BuildingAI.prototype.AddFocusTarget = function(ent, queued, push) BuildingAI.prototype.FireArrows = function () {
{ if (!this.targetUnits.length && !this.unitAITarget) {
if (!ent || this.targetUnits.indexOf(ent) === -1) if (!this.timer)
return; return;
if (queued)
this.focusTargets.push({"entityId": ent});
else if (push)
this.focusTargets.unshift({"entityId": ent});
else
this.focusTargets = [{"entityId": ent}];
};
/** let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
* Fire arrows with random temporal distribution on prefered targets. cmpTimer.CancelTimer(this.timer);
* Called 'roundCount' times every 'RepeatTime' seconds when there are units in the range. this.timer = undefined;
*/ return;
BuildingAI.prototype.FireArrows = function() }
{
if (!this.targetUnits.length && !this.unitAITarget)
{
if (!this.timer)
return;
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
cmpTimer.CancelTimer(this.timer); if (!cmpAttack)
this.timer = undefined; return;
return;
}
let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); if (this.currentRound > roundCount - 1)
if (!cmpAttack) this.currentRound = 0;
return;
if (this.currentRound > roundCount - 1) if (this.currentRound == 0)
this.currentRound = 0; this.arrowsLeft = this.GetArrowCount();
if (this.currentRound == 0) let arrowsToFire = 0;
this.arrowsLeft = this.GetArrowCount(); if (this.currentRound == roundCount - 1)
arrowsToFire = this.arrowsLeft;
else
arrowsToFire = Math.min(
randIntInclusive(0, 2 * this.GetArrowCount() / roundCount),
this.arrowsLeft
);
let arrowsToFire = 0; if (arrowsToFire <= 0) {
if (this.currentRound == roundCount - 1) ++this.currentRound;
arrowsToFire = this.arrowsLeft; return;
else }
arrowsToFire = Math.min(
randIntInclusive(0, 2 * this.GetArrowCount() / roundCount),
this.arrowsLeft
);
if (arrowsToFire <= 0) // Add targets to a weighted list, to allow preferences.
{ let targets = new WeightedList();
++this.currentRound; let maxPreference = this.MAX_PREFERENCE_BONUS;
return; 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. // Add the UnitAI target separately, as the UnitMotion and RangeManager implementations differ.
if (this.unitAITarget && this.targetUnits.indexOf(this.unitAITarget) == -1) if (this.unitAITarget && this.targetUnits.indexOf(this.unitAITarget) == -1)
addTarget(this.unitAITarget); addTarget(this.unitAITarget);
else if (this.unitAITarget && this.targetUnits.indexOf(this.unitAITarget) != -1) for (let target of this.targetUnits)
this.focusTargets = [{"entityId": this.unitAITarget}]; addTarget(target);
if (!this.focusTargets.length)
{ // The obstruction manager performs approximate range checks.
for (let target of this.targetUnits) // so we need to verify them here.
addTarget(target); // TODO: perhaps an optional 'precise' mode to range queries would be more performant.
// Sort targets by preference and then by proximity. const cmpObstructionManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ObstructionManager);
targets.sort( (a,b) => { const range = cmpAttack.GetRange(attackType);
if (a.preference > b.preference) return 1; const yOrigin = cmpAttack.GetAttackYOrigin(attackType);
else if (a.preference < b.preference) return -1;
else if (PositionHelper.DistanceBetweenEntities(this.entity,a.entityId) > PositionHelper.DistanceBetweenEntities(this.entity,b.entityId)) return 1; let firedArrows = 0;
return -1; 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. this.arrowsLeft -= firedArrows;
// so we need to verify them here. ++this.currentRound;
// 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;
}; };
/** /**
* 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) BuildingAI.prototype.CheckTargetVisible = function (target) {
{ var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); if (!cmpOwnership)
if (!cmpOwnership) return false;
return false;
// Entities that are hidden and miraged are considered visible. // Entities that are hidden and miraged are considered visible.
var cmpFogging = Engine.QueryInterface(target, IID_Fogging); var cmpFogging = Engine.QueryInterface(target, IID_Fogging);
if (cmpFogging && cmpFogging.IsMiraged(cmpOwnership.GetOwner())) if (cmpFogging && cmpFogging.IsMiraged(cmpOwnership.GetOwner()))
return true; return true;
// Either visible directly, or visible in fog. // Either visible directly, or visible in fog.
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
return cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner()) != "hidden"; return cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner()) != "hidden";
}; };
Engine.RegisterComponentType(IID_BuildingAI, "BuildingAI", BuildingAI); 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"?> <?xml version="1.0" encoding="utf-8"?>
<Entity parent="structures/han/civil_centre"> <Entity parent="structures/han/civil_centre">
<BuildRestrictions> <BuildRestrictions>
<Category>ImperialCourt</Category> <Category>ImperialCourt</Category>
</BuildRestrictions> </BuildRestrictions>
<Capturable> <Capturable>
<CapturePoints op="mul">1.5</CapturePoints> <CapturePoints op="mul">1.5</CapturePoints>
</Capturable> </Capturable>
<GarrisonHolder> <GarrisonHolder>
<Max op="mul">1.5</Max> <Max op="mul">1.5</Max>
</GarrisonHolder> </GarrisonHolder>
<BuildingAI> <Health>
<DefaultArrowCount>10</DefaultArrowCount> <Max op="mul">1.5</Max>
<MaxArrowCount>24</MaxArrowCount> </Health>
</BuildingAI> <Identity>
<Health> <Civ>han</Civ>
<Max op="mul">1.5</Max> <GenericName>Imperial Court</GenericName>
</Health> <SpecificName>Cháotíng</SpecificName>
<Identity> <VisibleClasses datatype="tokens">Defensive ImperialCourt City</VisibleClasses>
<Civ>han</Civ> <Classes datatype="tokens">CivCentre CivSpecific</Classes>
<GenericName>Imperial Court</GenericName> <RequiredTechnology>phase_city</RequiredTechnology>
<SpecificName>Cháotíng</SpecificName> <Icon>structures/military_settlement.png</Icon>
<VisibleClasses datatype="tokens">Defensive ImperialCourt City</VisibleClasses> </Identity>
<Classes datatype="tokens">CivCentre CivSpecific</Classes> <Population>
<RequiredTechnology>phase_city</RequiredTechnology> <Bonus>30</Bonus>
<Icon>structures/military_settlement.png</Icon> </Population>
</Identity> <Researcher>
<Population> <Technologies datatype="tokens">
<Bonus>30</Bonus> -phase_town_{civ}
</Population> </Technologies>
<Researcher> </Researcher>
<Technologies datatype="tokens"> <Trainer>
-phase_town_{civ} <BatchTimeModifier op="mul">0.5</BatchTimeModifier>
</Technologies> <Entities datatype="tokens">
</Researcher> units/{civ}/hero_han_xin_horse
<Trainer> units/{civ}/hero_liu_bang_horse
<BatchTimeModifier op="mul">0.75</BatchTimeModifier> units/{civ}/hero_wei_qing_chariot
<Entities datatype="tokens"> </Entities>
units/{civ}/hero_han_xin_horse </Trainer>
units/{civ}/hero_liu_bang_horse <Upgrade disable=""/>
units/{civ}/hero_wei_qing_chariot <VisualActor>
</Entities> <Actor>structures/han/imperial_court.xml</Actor>
</Trainer> </VisualActor>
<Upgrade disable=""/>
<VisualActor>
<Actor>structures/han/imperial_court.xml</Actor>
</VisualActor>
</Entity> </Entity>

View File

@ -1,50 +1,50 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Entity parent="template_structure_defensive_tower_stone"> <Entity parent="template_structure_defensive_tower_stone">
<Attack> <Attack>
<Ranged> <Ranged>
<MinRange>12</MinRange> <MinRange>12</MinRange>
</Ranged> </Ranged>
</Attack> </Attack>
<BuildingAI> <BuildingAI>
<DefaultArrowCount>4</DefaultArrowCount> <DefaultArrowCount>2</DefaultArrowCount>
</BuildingAI> </BuildingAI>
<Cost> <Cost>
<BuildTime>200</BuildTime> <BuildTime>200</BuildTime>
<Resources> <Resources>
<wood>50</wood> <wood>50</wood>
<stone>250</stone> <stone>250</stone>
</Resources> </Resources>
</Cost> </Cost>
<Footprint replace=""> <Footprint replace="">
<Circle radius="8.0"/> <Circle radius="8.0"/>
<Height>12.0</Height> <Height>12.0</Height>
</Footprint> </Footprint>
<GarrisonHolder> <GarrisonHolder>
<Max>8</Max> <Max>8</Max>
</GarrisonHolder> </GarrisonHolder>
<Health> <Health>
<Max>2400</Max> <Max>2400</Max>
<SpawnEntityOnDeath>decay|rubble/rubble_stone_3x3</SpawnEntityOnDeath> <SpawnEntityOnDeath>decay|rubble/rubble_stone_3x3</SpawnEntityOnDeath>
</Health> </Health>
<Identity> <Identity>
<Civ>iber</Civ> <Civ>iber</Civ>
<SpecificName>Dorre</SpecificName> <SpecificName>Dorre</SpecificName>
</Identity> </Identity>
<Loot> <Loot>
<wood>10</wood> <wood>10</wood>
<stone>50</stone> <stone>50</stone>
</Loot> </Loot>
<Obstruction> <Obstruction>
<Static width="14.0" depth="14.0"/> <Static width="14.0" depth="14.0"/>
</Obstruction> </Obstruction>
<StatusBars> <StatusBars>
<HeightOffset>17.0</HeightOffset> <HeightOffset>17.0</HeightOffset>
</StatusBars> </StatusBars>
<TerritoryInfluence> <TerritoryInfluence>
<Radius>38</Radius> <Radius>38</Radius>
</TerritoryInfluence> </TerritoryInfluence>
<VisualActor> <VisualActor>
<Actor>structures/iberians/scout_tower.xml</Actor> <Actor>structures/iberians/scout_tower.xml</Actor>
<FoundationActor>structures/fndn_4x4.xml</FoundationActor> <FoundationActor>structures/fndn_4x4.xml</FoundationActor>
</VisualActor> </VisualActor>
</Entity> </Entity>

View File

@ -1,152 +1,151 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Entity parent="template_structure_civic"> <Entity parent="template_structure_civic">
<AlertRaiser> <AlertRaiser>
<List datatype="tokens">FemaleCitizen</List> <List datatype="tokens">FemaleCitizen</List>
<RaiseAlertRange>120</RaiseAlertRange> <RaiseAlertRange>120</RaiseAlertRange>
<EndOfAlertRange>180</EndOfAlertRange> <EndOfAlertRange>180</EndOfAlertRange>
<SearchRange>100</SearchRange> <SearchRange>100</SearchRange>
</AlertRaiser> </AlertRaiser>
<Attack> <Attack>
<Ranged> <Ranged>
<AttackName>Bow</AttackName> <AttackName>Bow</AttackName>
<Damage> <Damage>
<Pierce>10</Pierce> <Pierce>10</Pierce>
</Damage> </Damage>
<MaxRange>60</MaxRange> <MaxRange>60</MaxRange>
<PrepareTime>1200</PrepareTime> <PrepareTime>1200</PrepareTime>
<RepeatTime>4000</RepeatTime> <RepeatTime>2000</RepeatTime>
<Projectile> <Projectile>
<Speed>100</Speed> <Speed>100</Speed>
<Spread>2</Spread> <Spread>1.5</Spread>
<Gravity>50</Gravity> <Gravity>50</Gravity>
<FriendlyFire>false</FriendlyFire> <FriendlyFire>false</FriendlyFire>
<LaunchPoint y="3"/> <LaunchPoint y="3"/>
</Projectile> </Projectile>
<PreferredClasses datatype="tokens">Human</PreferredClasses> <PreferredClasses datatype="tokens">Human</PreferredClasses>
<RangeOverlay> <RangeOverlay>
<LineTexture>outline_border.png</LineTexture> <LineTexture>outline_border.png</LineTexture>
<LineTextureMask>outline_border_mask.png</LineTextureMask> <LineTextureMask>outline_border_mask.png</LineTextureMask>
<LineThickness>0.175</LineThickness> <LineThickness>0.175</LineThickness>
</RangeOverlay> </RangeOverlay>
</Ranged> </Ranged>
</Attack> </Attack>
<BuildRestrictions> <BuildRestrictions>
<Territory>own neutral</Territory> <Territory>own neutral</Territory>
<Category>CivilCentre</Category> <Category>CivilCentre</Category>
<Distance> <Distance>
<FromClass>CivilCentre</FromClass> <FromClass>CivilCentre</FromClass>
<MinDistance>200</MinDistance> <MinDistance>200</MinDistance>
</Distance> </Distance>
</BuildRestrictions> </BuildRestrictions>
<BuildingAI> <BuildingAI>
<DefaultArrowCount>8</DefaultArrowCount> <DefaultArrowCount>3</DefaultArrowCount>
<GarrisonArrowMultiplier>1</GarrisonArrowMultiplier> <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
<GarrisonArrowClasses>Soldier</GarrisonArrowClasses> <GarrisonArrowClasses>Soldier</GarrisonArrowClasses>
<MaxArrowCount>18</MaxArrowCount> </BuildingAI>
</BuildingAI> <Capturable>
<Capturable> <CapturePoints>2500</CapturePoints>
<CapturePoints>2500</CapturePoints> <RegenRate>5.0</RegenRate>
<RegenRate>5.0</RegenRate> </Capturable>
</Capturable> <Cost>
<Cost> <BuildTime>500</BuildTime>
<BuildTime>500</BuildTime> <Resources>
<Resources> <wood>300</wood>
<wood>300</wood> <stone>300</stone>
<stone>300</stone> <metal>250</metal>
<metal>250</metal> </Resources>
</Resources> </Cost>
</Cost> <Footprint>
<Footprint> <Square depth="32.0" width="32.0"/>
<Square depth="32.0" width="32.0"/> <Height>8.0</Height>
<Height>8.0</Height> </Footprint>
</Footprint> <GarrisonHolder>
<GarrisonHolder> <Max>20</Max>
<Max>20</Max> <EjectHealth>0.1</EjectHealth>
<EjectHealth>0.1</EjectHealth> <EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy> <List datatype="tokens">Support Infantry Cavalry</List>
<List datatype="tokens">Support Infantry Cavalry</List> <BuffHeal>1</BuffHeal>
<BuffHeal>1</BuffHeal> <LoadingRange>1</LoadingRange>
<LoadingRange>1</LoadingRange> </GarrisonHolder>
</GarrisonHolder> <Health>
<Health> <Max>3000</Max>
<Max>3000</Max> <SpawnEntityOnDeath>decay|rubble/rubble_stone_6x6</SpawnEntityOnDeath>
<SpawnEntityOnDeath>decay|rubble/rubble_stone_6x6</SpawnEntityOnDeath> </Health>
</Health> <Identity>
<Identity> <GenericName>Civic Center</GenericName>
<GenericName>Civic Center</GenericName> <SelectionGroupName>template_structure_civic_civil_centre</SelectionGroupName>
<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>
<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>
<Classes datatype="tokens">CivCentre</Classes> <VisibleClasses datatype="tokens">Defensive CivilCentre</VisibleClasses>
<VisibleClasses datatype="tokens">Defensive CivilCentre</VisibleClasses> <Icon>structures/civic_centre.png</Icon>
<Icon>structures/civic_centre.png</Icon> </Identity>
</Identity> <Loot>
<Loot> <wood>60</wood>
<wood>60</wood> <stone>60</stone>
<stone>60</stone> <metal>50</metal>
<metal>50</metal> </Loot>
</Loot> <Minimap>
<Minimap> <Type>structure</Type>
<Type>structure</Type> <Icon size="16.0">civil_centre.png</Icon>
<Icon size="16.0">civil_centre.png</Icon> </Minimap>
</Minimap> <Obstruction>
<Obstruction> <Static depth="30.0" width="30.0"/>
<Static depth="30.0" width="30.0"/> </Obstruction>
</Obstruction> <Population>
<Population> <Bonus>20</Bonus>
<Bonus>20</Bonus> </Population>
</Population> <ProductionQueue/>
<ProductionQueue/> <Researcher>
<Researcher> <Technologies datatype="tokens">
<Technologies datatype="tokens"> phase_town_{civ}
phase_town_{civ} phase_city_{civ}
phase_city_{civ} unlock_shared_los
unlock_shared_los unlock_shared_dropsites
unlock_shared_dropsites unlock_spies
unlock_spies spy_counter
spy_counter archery_tradition
archery_tradition hoplite_tradition
hoplite_tradition hellenistic_metropolis
hellenistic_metropolis </Technologies>
</Technologies> </Researcher>
</Researcher> <Resistance>
<Resistance> <Entity>
<Entity> <Damage>
<Damage> <Hack op="add">5</Hack>
<Hack op="add">5</Hack> <Pierce op="add">5</Pierce>
<Pierce op="add">5</Pierce> </Damage>
</Damage> </Entity>
</Entity> </Resistance>
</Resistance> <ResourceDropsite>
<ResourceDropsite> <Types>food wood stone metal</Types>
<Types>food wood stone metal</Types> <Sharable>true</Sharable>
<Sharable>true</Sharable> </ResourceDropsite>
</ResourceDropsite> <Sound>
<Sound> <SoundGroups>
<SoundGroups> <select>interface/select/building/sel_civ_center.xml</select>
<select>interface/select/building/sel_civ_center.xml</select> <constructed>interface/complete/building/complete_civ_center.xml</constructed>
<constructed>interface/complete/building/complete_civ_center.xml</constructed> <upgraded>interface/complete/building/complete_civ_center.xml</upgraded>
<upgraded>interface/complete/building/complete_civ_center.xml</upgraded> <alert_raise>interface/alarm/alarm_alert_0.xml</alert_raise>
<alert_raise>interface/alarm/alarm_alert_0.xml</alert_raise> <alert_end>interface/alarm/alarm_alert_1.xml</alert_end>
<alert_end>interface/alarm/alarm_alert_1.xml</alert_end> <attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged> <attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged>
<attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged> </SoundGroups>
</SoundGroups> </Sound>
</Sound> <TerritoryInfluence>
<TerritoryInfluence> <Root>true</Root>
<Root>true</Root> <Radius>140</Radius>
<Radius>140</Radius> <Weight>10000</Weight>
<Weight>10000</Weight> </TerritoryInfluence>
</TerritoryInfluence> <Trainer>
<Trainer> <BatchTimeModifier>0.8</BatchTimeModifier>
<BatchTimeModifier>0.8</BatchTimeModifier> <Entities datatype="tokens">
<Entities datatype="tokens"> units/{civ}/support_female_citizen
units/{civ}/support_female_citizen </Entities>
</Entities> </Trainer>
</Trainer> <Vision>
<Vision> <Range>90</Range>
<Range>90</Range> </Vision>
</Vision> <VisualActor>
<VisualActor> <FoundationActor>structures/fndn_8x8.xml</FoundationActor>
<FoundationActor>structures/fndn_8x8.xml</FoundationActor> </VisualActor>
</VisualActor>
</Entity> </Entity>

View File

@ -1,55 +1,54 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Entity parent="template_structure_civic_civil_centre"> <Entity parent="template_structure_civic_civil_centre">
<BuildRestrictions> <BuildRestrictions>
<Territory>own neutral</Territory> <Territory>own neutral</Territory>
<Category>Colony</Category> <Category>Colony</Category>
<Distance> <Distance>
<FromClass>CivilCentre</FromClass> <FromClass>CivilCentre</FromClass>
<MinDistance>120</MinDistance> <MinDistance>120</MinDistance>
</Distance> </Distance>
</BuildRestrictions> </BuildRestrictions>
<BuildingAI> <BuildingAI>
<DefaultArrowCount>6</DefaultArrowCount> <DefaultArrowCount>1</DefaultArrowCount>
<MaxArrowCount>16</MaxArrowCount> </BuildingAI>
</BuildingAI> <Cost>
<Cost> <BuildTime>300</BuildTime>
<BuildTime>300</BuildTime> <Resources>
<Resources> <wood>200</wood>
<wood>200</wood> <stone>200</stone>
<stone>200</stone> <metal>150</metal>
<metal>150</metal> </Resources>
</Resources> </Cost>
</Cost> <Health>
<Health> <Max>2000</Max>
<Max>2000</Max> <SpawnEntityOnDeath>decay|rubble/rubble_stone_5x5</SpawnEntityOnDeath>
<SpawnEntityOnDeath>decay|rubble/rubble_stone_5x5</SpawnEntityOnDeath> </Health>
</Health> <Identity>
<Identity> <GenericName>Military Colony</GenericName>
<GenericName>Military Colony</GenericName> <SelectionGroupName>template_structure_civic_civil_centre_military_colony</SelectionGroupName>
<SelectionGroupName>template_structure_civic_civil_centre_military_colony</SelectionGroupName> <VisibleClasses datatype="tokens">Colony</VisibleClasses>
<VisibleClasses datatype="tokens">Colony</VisibleClasses> <Icon>structures/military_settlement.png</Icon>
<Icon>structures/military_settlement.png</Icon> <RequiredTechnology>phase_town</RequiredTechnology>
<RequiredTechnology>phase_town</RequiredTechnology> </Identity>
</Identity> <Loot>
<Loot> <wood>40</wood>
<wood>40</wood> <stone>40</stone>
<stone>40</stone> <metal>30</metal>
<metal>30</metal> </Loot>
</Loot> <Researcher>
<Researcher> <Technologies datatype="tokens">
<Technologies datatype="tokens"> -phase_town_{civ}
-phase_town_{civ} -phase_city_{civ}
-phase_city_{civ} -hellenistic_metropolis
-hellenistic_metropolis </Technologies>
</Technologies> </Researcher>
</Researcher> <Sound>
<Sound> <SoundGroups>
<SoundGroups> <select>interface/select/building/sel_gymnasium.xml</select>
<select>interface/select/building/sel_gymnasium.xml</select> <constructed>interface/complete/building/complete_gymnasium.xml</constructed>
<constructed>interface/complete/building/complete_gymnasium.xml</constructed> </SoundGroups>
</SoundGroups> </Sound>
</Sound> <TerritoryInfluence>
<TerritoryInfluence> <Radius>75</Radius>
<Radius>75</Radius> </TerritoryInfluence>
</TerritoryInfluence>
</Entity> </Entity>

View File

@ -1,72 +1,72 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Entity parent="template_structure_defensive"> <Entity parent="template_structure_defensive">
<Attack> <Attack>
<Ranged> <Ranged>
<AttackName>Bow</AttackName> <AttackName>Bow</AttackName>
<Damage> <Damage>
<Pierce>11</Pierce> <Pierce>11</Pierce>
</Damage> </Damage>
<MaxRange>60</MaxRange> <MaxRange>60</MaxRange>
<MinRange>10</MinRange> <MinRange>10</MinRange>
<PrepareTime>1200</PrepareTime> <PrepareTime>1200</PrepareTime>
<RepeatTime>4000</RepeatTime> <RepeatTime>2000</RepeatTime>
<Projectile> <Projectile>
<Speed>100</Speed> <Speed>100</Speed>
<Spread>1.5</Spread> <Spread>1.5</Spread>
<Gravity>50</Gravity> <Gravity>50</Gravity>
<FriendlyFire>false</FriendlyFire> <FriendlyFire>false</FriendlyFire>
<LaunchPoint y="3"/> <LaunchPoint y="3"/>
</Projectile> </Projectile>
<PreferredClasses datatype="tokens">Human</PreferredClasses> <PreferredClasses datatype="tokens">Human</PreferredClasses>
<RangeOverlay> <RangeOverlay>
<LineTexture>outline_border.png</LineTexture> <LineTexture>outline_border.png</LineTexture>
<LineTextureMask>outline_border_mask.png</LineTextureMask> <LineTextureMask>outline_border_mask.png</LineTextureMask>
<LineThickness>0.175</LineThickness> <LineThickness>0.175</LineThickness>
</RangeOverlay> </RangeOverlay>
</Ranged> </Ranged>
</Attack> </Attack>
<BuildingAI> <BuildingAI>
<DefaultArrowCount>2</DefaultArrowCount> <DefaultArrowCount>1</DefaultArrowCount>
<GarrisonArrowMultiplier>1</GarrisonArrowMultiplier> <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
<GarrisonArrowClasses>Infantry</GarrisonArrowClasses> <GarrisonArrowClasses>Infantry</GarrisonArrowClasses>
</BuildingAI> </BuildingAI>
<BuildRestrictions> <BuildRestrictions>
<Category>Tower</Category> <Category>Tower</Category>
<Distance> <Distance>
<FromClass>Tower</FromClass> <FromClass>Tower</FromClass>
<MinDistance>60</MinDistance> <MinDistance>60</MinDistance>
</Distance> </Distance>
</BuildRestrictions> </BuildRestrictions>
<GarrisonHolder> <GarrisonHolder>
<EjectHealth>0.1</EjectHealth> <EjectHealth>0.1</EjectHealth>
<EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy> <EjectClassesOnDestroy datatype="tokens">Unit</EjectClassesOnDestroy>
<List datatype="tokens">Support Infantry</List> <List datatype="tokens">Support Infantry</List>
<BuffHeal>0</BuffHeal> <BuffHeal>0</BuffHeal>
<LoadingRange>2</LoadingRange> <LoadingRange>2</LoadingRange>
</GarrisonHolder> </GarrisonHolder>
<Health> <Health>
<Max>1000</Max> <Max>1000</Max>
<SpawnEntityOnDeath>decay|rubble/rubble_stone_2x2</SpawnEntityOnDeath> <SpawnEntityOnDeath>decay|rubble/rubble_stone_2x2</SpawnEntityOnDeath>
</Health> </Health>
<Identity> <Identity>
<GenericName>Tower</GenericName> <GenericName>Tower</GenericName>
<VisibleClasses datatype="tokens">Tower</VisibleClasses> <VisibleClasses datatype="tokens">Tower</VisibleClasses>
</Identity> </Identity>
<Sound> <Sound>
<SoundGroups> <SoundGroups>
<constructed>interface/complete/building/complete_tower.xml</constructed> <constructed>interface/complete/building/complete_tower.xml</constructed>
<select>interface/select/building/sel_tower.xml</select> <select>interface/select/building/sel_tower.xml</select>
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged> <attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
<attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged> <attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged>
</SoundGroups> </SoundGroups>
</Sound> </Sound>
<StatusBars> <StatusBars>
<HeightOffset>18.0</HeightOffset> <HeightOffset>18.0</HeightOffset>
</StatusBars> </StatusBars>
<Vision> <Vision>
<Range>80</Range> <Range>80</Range>
</Vision> </Vision>
<VisualActor> <VisualActor>
<FoundationActor>structures/fndn_3x3.xml</FoundationActor> <FoundationActor>structures/fndn_3x3.xml</FoundationActor>
</VisualActor> </VisualActor>
</Entity> </Entity>

View File

@ -1,69 +1,69 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Entity parent="template_structure_defensive_tower"> <Entity parent="template_structure_defensive_tower">
<Attack> <Attack>
<Ranged> <Ranged>
<Origin> <Origin>
<X>0</X> <X>0</X>
<Y>9</Y> <Y>9</Y>
<Z>0</Z> <Z>0</Z>
</Origin> </Origin>
</Ranged> </Ranged>
</Attack> </Attack>
<BuildingAI> <BuildingAI>
<MaxArrowCount>5</MaxArrowCount> <MaxArrowCount>4</MaxArrowCount>
</BuildingAI> </BuildingAI>
<Cost> <Cost>
<BuildTime>40</BuildTime> <BuildTime>40</BuildTime>
<Resources> <Resources>
<wood>125</wood> <wood>100</wood>
</Resources> </Resources>
</Cost> </Cost>
<Footprint> <Footprint>
<Square width="9.5" depth="8.5"/> <Square width="9.5" depth="8.5"/>
<Height>9.0</Height> <Height>9.0</Height>
</Footprint> </Footprint>
<GarrisonHolder> <GarrisonHolder>
<Max>3</Max> <Max>3</Max>
</GarrisonHolder> </GarrisonHolder>
<Health> <Health>
<Max>400</Max> <Max>400</Max>
</Health> </Health>
<Identity> <Identity>
<GenericName>Sentry Tower</GenericName> <GenericName>Sentry Tower</GenericName>
<SelectionGroupName>template_structure_defensive_tower_sentry</SelectionGroupName> <SelectionGroupName>template_structure_defensive_tower_sentry</SelectionGroupName>
<Tooltip>Garrison Infantry for additional arrows. Needs the “Murder Holes” technology to protect its foot.</Tooltip> <Tooltip>Garrison Infantry for additional arrows. Needs the “Murder Holes” technology to protect its foot.</Tooltip>
<VisibleClasses datatype="tokens">SentryTower</VisibleClasses> <VisibleClasses datatype="tokens">SentryTower</VisibleClasses>
<Icon>structures/sentry_tower.png</Icon> <Icon>structures/sentry_tower.png</Icon>
<RequiredTechnology>phase_village</RequiredTechnology> <RequiredTechnology>phase_village</RequiredTechnology>
</Identity> </Identity>
<Loot> <Loot>
<wood>25</wood> <wood>20</wood>
</Loot> </Loot>
<Obstruction> <Obstruction>
<Static width="9.0" depth="7.5"/> <Static width="9.0" depth="7.5"/>
</Obstruction> </Obstruction>
<ProductionQueue/> <ProductionQueue/>
<Researcher> <Researcher>
<Technologies datatype="tokens"> <Technologies datatype="tokens">
tower_watch tower_watch
</Technologies> </Technologies>
</Researcher> </Researcher>
<TerritoryInfluence> <TerritoryInfluence>
<Root>false</Root> <Root>false</Root>
<Radius>16</Radius> <Radius>16</Radius>
<Weight>30000</Weight> <Weight>30000</Weight>
</TerritoryInfluence> </TerritoryInfluence>
<Upgrade> <Upgrade>
<Tower> <Tower>
<Entity>structures/{civ}/defense_tower</Entity> <Entity>structures/{civ}/defense_tower</Entity>
<Tooltip>Reinforce with stone and upgrade to a defense tower.</Tooltip> <Tooltip>Reinforce with stone and upgrade to a defense tower.</Tooltip>
<RequiredTechnology>phase_town</RequiredTechnology> <RequiredTechnology>phase_town</RequiredTechnology>
<Cost> <Cost>
<wood>25</wood> <wood>50</wood>
<stone>100</stone> <stone>100</stone>
</Cost> </Cost>
<Variant>upgrading</Variant> <Variant>upgrading</Variant>
<Time>100</Time> <Time>100</Time>
</Tower> </Tower>
</Upgrade> </Upgrade>
</Entity> </Entity>

View File

@ -1,62 +1,59 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Entity parent="template_structure_defensive_tower"> <Entity parent="template_structure_defensive_tower">
<Attack> <Attack>
<Ranged> <Ranged>
<Origin> <Origin>
<X>0</X> <X>0</X>
<Y>15</Y> <Y>15</Y>
<Z>0</Z> <Z>0</Z>
</Origin> </Origin>
</Ranged> </Ranged>
</Attack> </Attack>
<Cost> <Cost>
<BuildTime>150</BuildTime> <BuildTime>150</BuildTime>
<Resources> <Resources>
<wood>100</wood> <wood>100</wood>
<stone>100</stone> <stone>100</stone>
</Resources> </Resources>
</Cost> </Cost>
<Footprint> <Footprint>
<Square width="10.0" depth="10.0"/> <Square width="10.0" depth="10.0"/>
<Height>15.0</Height> <Height>15.0</Height>
</Footprint> </Footprint>
<BuildingAI> <GarrisonHolder>
<DefaultArrowCount>3</DefaultArrowCount> <Max>5</Max>
</BuildingAI> </GarrisonHolder>
<GarrisonHolder> <Health>
<Max>5</Max> <Max>1000</Max>
</GarrisonHolder> </Health>
<Health> <Identity>
<Max>1000</Max> <GenericName>Stone Tower</GenericName>
</Health> <SelectionGroupName>template_structure_defensive_tower_stone</SelectionGroupName>
<Identity> <Tooltip>Garrison Infantry for additional arrows. Needs the “Murder Holes” technology to protect its foot.</Tooltip>
<GenericName>Stone Tower</GenericName> <VisibleClasses datatype="tokens">StoneTower</VisibleClasses>
<SelectionGroupName>template_structure_defensive_tower_stone</SelectionGroupName> <Icon>structures/defense_tower.png</Icon>
<Tooltip>Garrison Infantry for additional arrows. Needs the “Murder Holes” technology to protect its foot.</Tooltip> <RequiredTechnology>phase_town</RequiredTechnology>
<VisibleClasses datatype="tokens">StoneTower</VisibleClasses> </Identity>
<Icon>structures/defense_tower.png</Icon> <Loot>
<RequiredTechnology>phase_town</RequiredTechnology> <wood>20</wood>
</Identity> <stone>20</stone>
<Loot> </Loot>
<wood>20</wood> <Obstruction>
<stone>20</stone> <Static width="7.0" depth="7.0"/>
</Loot> </Obstruction>
<Obstruction> <ProductionQueue/>
<Static width="7.0" depth="7.0"/> <Researcher>
</Obstruction> <Technologies datatype="tokens">
<ProductionQueue/> tower_watch
<Researcher> tower_crenellations
<Technologies datatype="tokens"> tower_range
tower_watch tower_murderholes
tower_crenellations tower_health
tower_range </Technologies>
tower_murderholes </Researcher>
tower_health <TerritoryInfluence>
</Technologies> <Root>false</Root>
</Researcher> <Radius>32</Radius>
<TerritoryInfluence> <Weight>30000</Weight>
<Root>false</Root> </TerritoryInfluence>
<Radius>32</Radius>
<Weight>30000</Weight>
</TerritoryInfluence>
</Entity> </Entity>

View File

@ -1,112 +1,112 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Entity parent="template_structure_military"> <Entity parent="template_structure_military">
<Attack> <Attack>
<Ranged> <Ranged>
<AttackName>Bow</AttackName> <AttackName>Bow</AttackName>
<Damage> <Damage>
<Pierce>10</Pierce> <Pierce>10</Pierce>
</Damage> </Damage>
<MaxRange>60</MaxRange> <MaxRange>60</MaxRange>
<PrepareTime>1200</PrepareTime> <PrepareTime>1200</PrepareTime>
<RepeatTime>4000</RepeatTime> <RepeatTime>2000</RepeatTime>
<Projectile> <Projectile>
<Speed>100</Speed> <Speed>100</Speed>
<Spread>2.5</Spread> <Spread>1.5</Spread>
<Gravity>50</Gravity> <Gravity>50</Gravity>
<FriendlyFire>false</FriendlyFire> <FriendlyFire>false</FriendlyFire>
<LaunchPoint y="3"/> <LaunchPoint y="3"/>
</Projectile> </Projectile>
<PreferredClasses datatype="tokens">Human</PreferredClasses> <PreferredClasses datatype="tokens">Human</PreferredClasses>
<RangeOverlay> <RangeOverlay>
<LineTexture>outline_border.png</LineTexture> <LineTexture>outline_border.png</LineTexture>
<LineTextureMask>outline_border_mask.png</LineTextureMask> <LineTextureMask>outline_border_mask.png</LineTextureMask>
<LineThickness>0.175</LineThickness> <LineThickness>0.175</LineThickness>
</RangeOverlay> </RangeOverlay>
</Ranged> </Ranged>
</Attack> </Attack>
<BuildRestrictions> <BuildRestrictions>
<Category>Fortress</Category> <Category>Fortress</Category>
<Distance> <Distance>
<FromClass>Fortress</FromClass> <FromClass>Fortress</FromClass>
<MinDistance>80</MinDistance> <MinDistance>80</MinDistance>
</Distance> </Distance>
</BuildRestrictions> </BuildRestrictions>
<BuildingAI> <BuildingAI>
<DefaultArrowCount>12</DefaultArrowCount> <DefaultArrowCount>4</DefaultArrowCount>
<GarrisonArrowMultiplier>1</GarrisonArrowMultiplier> <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
<GarrisonArrowClasses>Soldier</GarrisonArrowClasses> <GarrisonArrowClasses>Soldier</GarrisonArrowClasses>
</BuildingAI> </BuildingAI>
<Capturable> <Capturable>
<CapturePoints op="mul">8</CapturePoints> <CapturePoints op="mul">8</CapturePoints>
<RegenRate>10.0</RegenRate> <RegenRate>10.0</RegenRate>
</Capturable> </Capturable>
<Cost> <Cost>
<BuildTime>450</BuildTime> <BuildTime>450</BuildTime>
<Resources> <Resources>
<wood>300</wood> <wood>300</wood>
<stone>600</stone> <stone>600</stone>
</Resources> </Resources>
</Cost> </Cost>
<Footprint> <Footprint>
<Square width="30.0" depth="30.0"/> <Square width="30.0" depth="30.0"/>
<Height>8.0</Height> <Height>8.0</Height>
</Footprint> </Footprint>
<GarrisonHolder> <GarrisonHolder>
<Max>20</Max> <Max>20</Max>
<EjectHealth>0.075</EjectHealth> <EjectHealth>0.075</EjectHealth>
<List datatype="tokens">Support Infantry Cavalry Siege</List> <List datatype="tokens">Support Infantry Cavalry Siege</List>
<LoadingRange>6</LoadingRange> <LoadingRange>6</LoadingRange>
</GarrisonHolder> </GarrisonHolder>
<Health> <Health>
<Max>5200</Max> <Max>5200</Max>
<SpawnEntityOnDeath>decay|rubble/rubble_stone_6x6</SpawnEntityOnDeath> <SpawnEntityOnDeath>decay|rubble/rubble_stone_6x6</SpawnEntityOnDeath>
</Health> </Health>
<Identity> <Identity>
<GenericName>Fortress</GenericName> <GenericName>Fortress</GenericName>
<SelectionGroupName>template_structure_military_fortress</SelectionGroupName> <SelectionGroupName>template_structure_military_fortress</SelectionGroupName>
<Tooltip>Garrison Soldiers for additional arrows.</Tooltip> <Tooltip>Garrison Soldiers for additional arrows.</Tooltip>
<Classes datatype="tokens">GarrisonFortress</Classes> <Classes datatype="tokens">GarrisonFortress</Classes>
<VisibleClasses datatype="tokens">Defensive Fortress</VisibleClasses> <VisibleClasses datatype="tokens">Defensive Fortress</VisibleClasses>
<Icon>structures/fortress.png</Icon> <Icon>structures/fortress.png</Icon>
<RequiredTechnology>phase_city</RequiredTechnology> <RequiredTechnology>phase_city</RequiredTechnology>
</Identity> </Identity>
<Loot> <Loot>
<wood>60</wood> <wood>60</wood>
<stone>120</stone> <stone>120</stone>
</Loot> </Loot>
<Obstruction> <Obstruction>
<Static width="25.0" depth="25.0"/> <Static width="25.0" depth="25.0"/>
</Obstruction> </Obstruction>
<ProductionQueue/> <ProductionQueue/>
<Researcher> <Researcher>
<Technologies datatype="tokens"> <Technologies datatype="tokens">
attack_soldiers_will attack_soldiers_will
art_of_war art_of_war
poison_arrows poison_arrows
poison_blades poison_blades
</Technologies> </Technologies>
</Researcher> </Researcher>
<Sound> <Sound>
<SoundGroups> <SoundGroups>
<select>interface/select/building/sel_fortress.xml</select> <select>interface/select/building/sel_fortress.xml</select>
<constructed>interface/complete/building/complete_fortress.xml</constructed> <constructed>interface/complete/building/complete_fortress.xml</constructed>
<attack_ranged>attack/weapon/bow_attack.xml</attack_ranged> <attack_ranged>attack/weapon/bow_attack.xml</attack_ranged>
<attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged> <attack_impact_ranged>attack/impact/arrow_impact.xml</attack_impact_ranged>
</SoundGroups> </SoundGroups>
</Sound> </Sound>
<TerritoryDecay> <TerritoryDecay>
<DecayRate op="mul">2</DecayRate> <DecayRate op="mul">2</DecayRate>
</TerritoryDecay> </TerritoryDecay>
<TerritoryInfluence> <TerritoryInfluence>
<Radius>80</Radius> <Radius>80</Radius>
</TerritoryInfluence> </TerritoryInfluence>
<Trainer> <Trainer>
<BatchTimeModifier>0.8</BatchTimeModifier> <BatchTimeModifier>0.8</BatchTimeModifier>
</Trainer> </Trainer>
<Vision> <Vision>
<Range>90</Range> <Range>90</Range>
</Vision> </Vision>
<VisualActor> <VisualActor>
<FoundationActor>structures/fndn_8x8.xml</FoundationActor> <FoundationActor>structures/fndn_8x8.xml</FoundationActor>
</VisualActor> </VisualActor>
</Entity> </Entity>