Ticket #2577: 2577 Atlas Todo.patch

File 2577 Atlas Todo.patch, 24.8 KB (added by Stan, 9 years ago)

Rebased version for r17136

  • binaries/data/mods/public/art/actors/units/celts/boudicca_chariot.xml

     
    1212      <props>
    1313        <prop actor="units/celts/boudicca_chariot_h1.xml" attachpoint="horseright"/>
    1414        <prop actor="units/celts/boudicca_chariot_h2.xml" attachpoint="horseleft"/>
    15         <prop actor="units/celts/boudicca_r2.xml" attachpoint="rider"/>
    1615        <prop actor="units/celts/champion_unit_4_d.xml" attachpoint="driver"/>
    1716      </props>
    1817      <textures><texture file="structural/celt_chariot_b.dds" name="baseTex"/></textures>
  • binaries/data/mods/public/art/actors/units/persians/cavalry_archer_a.xml

     
    1414        <prop actor="units/persians/pers_chariot_archer_a_h2.xml" attachpoint="horse2"/>
    1515        <prop actor="units/persians/pers_chariot_archer_a_h3.xml" attachpoint="horse3"/>
    1616        <prop actor="units/persians/pers_chariot_archer_a_h4.xml" attachpoint="horse4"/>
    17         <prop actor="units/persians/cavalry_archer_a_r.xml" attachpoint="rider1"/>
    1817        <prop actor="units/persians/cavalry_archer_a_d.xml" attachpoint="rider2"/>
    1918      </props>
    2019      <textures><texture file="structural/pers_chariot_a.png" name="baseTex"/></textures>
  • binaries/data/mods/public/art/actors/units/persians/cavalry_archer_e.xml

     
    1515        <prop actor="units/persians/pers_chariot_archer_e_h2.xml" attachpoint="horse2"/>
    1616        <prop actor="units/persians/pers_chariot_archer_e_h3.xml" attachpoint="horse3"/>
    1717        <prop actor="units/persians/pers_chariot_archer_e_h4.xml" attachpoint="horse4"/>
    18         <prop actor="units/persians/cavalry_archer_e_r.xml" attachpoint="rider1"/>
    1918        <prop actor="units/persians/cavalry_archer_e_d.xml" attachpoint="rider2"/>
    2019      </props>
    2120      <textures><texture file="structural/pers_chariot_e.png" name="baseTex"/></textures>
  • binaries/data/mods/public/simulation/components/Attack.js

     
    155155                        "</interleave>" +
    156156                    "</element>" +
    157157                "</optional>" +
     158                "<optional>"+
     159                    "<element name='TurretsOnly' a:help='When set to \"true\", will only accept attacks coming from turrets.'><text/></element>" +
     160                "</optional>" +
    158161            "</interleave>" +
    159162        "</element>" +
    160163    "</optional>" +
     
    202205
    203206Attack.prototype.Init = function()
    204207{
     208    this.latestTarget = INVALID_ENTITY;
    205209};
    206210
    207 Attack.prototype.Serialize = null; // we have no dynamic state to save
     211Attack.prototype.Serialize = null; // we have no dynamic state to save 
    208212
    209213Attack.prototype.GetAttackTypes = function()
    210214{
     
    502506 * and should only be called after GetTimers().repeat msec has passed since the last
    503507 * call to PerformAttack.
    504508 */
    505 Attack.prototype.PerformAttack = function(type, target)
     509Attack.prototype.PerformAttack = function(type, target, turretId)
    506510{
     511    if(this.template[type].TurretsOnly)
     512        if (!turretId && this.template[type].TurretsOnly == "true")
     513            return;
     514
    507515    // If this is a ranged attack, then launch a projectile
    508516    if (type == "Ranged")
    509517    {
     
    571579        var graphicalPosition = Vector3D.mult(missileDirection, 2).add(realTargetPosition);
    572580        // Launch the graphical projectile
    573581        var cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager);
    574         var id = cmpProjectileManager.LaunchProjectileAtPoint(this.entity, realTargetPosition, horizSpeed, gravity);
     582        var id = cmpProjectileManager.LaunchProjectileAtPoint(turretId || this.entity, realTargetPosition, horizSpeed, gravity);
    575583
    576584        var playerId = Engine.QueryInterface(this.entity, IID_Ownership).GetOwner();
    577585        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
  • binaries/data/mods/public/simulation/components/interfaces/TurretAI.js

     
     1Engine.RegisterInterface("TurretAI");
  • binaries/data/mods/public/simulation/components/interfaces/TurretHolder.js

     
     1Engine.RegisterInterface("TurretHolder");
     2
  • binaries/data/mods/public/simulation/components/TurretAI.js

     
     1function TurretAI() {}
     2TurretAI.prototype.Schema =
     3    "<empty/>";
     4
     5TurretAI.prototype.g_MaxPreferenceBonus = 2;
     6TurretAI.prototype.g_LatestTarget = 0   ;
     7
     8/**
     9 * Initialize TurretAI Component.
     10 */
     11TurretAI.prototype.Init = function()
     12{
     13};
     14
     15/**
     16 * Get the turrent parent.
     17 */
     18TurretAI.prototype.GetTurretParent = function()
     19{
     20    var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     21    if (!cmpPosition)
     22        return INVALID_ENTITY;
     23    return cmpPosition.GetTurretParent();
     24};
     25
     26/**
     27 * Get the attack component
     28 */
     29TurretAI.prototype.GetCmpAttack = function()
     30{
     31    // use own attack, or turretHolder's attack
     32    var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
     33    if (cmpAttack)
     34        return cmpAttack;
     35    return Engine.QueryInterface(this.GetTurretParent(), IID_Attack);
     36};
     37
     38/**
     39 * Sends the message to change ownership
     40 */
     41TurretAI.prototype.OnOwnershipChanged = function(msg)
     42{
     43    var cmpAttack = this.GetCmpAttack();
     44    if (!cmpAttack)
     45        return;
     46    //Return the first possible Attack Type, Capturing being the last.
     47    this.bestAttack = cmpAttack.GetAttackTypes()[0];
     48   
     49    // Remove current targets, to prevent them from being added twice
     50    this.targetUnits = [];
     51
     52    if (msg.to != -1)
     53        this.SetupRangeQuery(msg.to);
     54
     55    // Non-Gaia buildings should attack certain Gaia units.
     56    if (msg.to != 0 || this.gaiaUnitsQuery)
     57        this.SetupGaiaRangeQuery(msg.to);
     58};
     59
     60/**
     61 * Change Diplomacy State
     62 */
     63TurretAI.prototype.OnDiplomacyChanged = function(msg)
     64{
     65    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     66    if (cmpOwnership && cmpOwnership.GetOwner() == msg.player)
     67    {
     68        // Remove now allied/neutral units
     69        this.targetUnits = [];
     70        this.SetupRangeQuery(msg.player);
     71    }
     72};
     73
     74/**
     75 * Cleanup on destroy
     76 */
     77TurretAI.prototype.OnDestroy = function()
     78{
     79    if (this.timer)
     80    {
     81        let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     82        cmpTimer.CancelTimer(this.timer);
     83        this.timer = undefined;
     84    }
     85
     86    // Clean up range queries
     87    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     88    if (this.enemyUnitsQuery)
     89        cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery);
     90    if (this.gaiaUnitsQuery)
     91        cmpRangeManager.DestroyActiveQuery(this.gaiaUnitsQuery);
     92};
     93
     94/**
     95 * Setup the Range Query to detect units coming in & out of range
     96 */
     97TurretAI.prototype.SetupRangeQuery = function(owner)
     98{
     99    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     100    var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     101    var cmpAttack = this.GetCmpAttack();
     102    if (!cmpAttack)
     103        return;
     104
     105    if (this.enemyUnitsQuery)
     106    {
     107        cmpRangeManager.DestroyActiveQuery(this.enemyUnitsQuery);
     108        this.enemyUnitsQuery = undefined;
     109    }
     110   
     111    var players = [];
     112    var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(owner), IID_Player);
     113    var numPlayers = cmpPlayerManager.GetNumPlayers();
     114
     115    for (let i = 1; i < numPlayers; ++i)
     116    {
     117        // TODO: How to handle neutral players - Special query to attack military only?
     118        if (cmpPlayer.IsEnemy(i))
     119            players.push(i);
     120    }
     121
     122    var range = cmpAttack.GetRange(this.bestAttack);
     123    this.enemyUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery(this.entity, range.min, range.max, range.elevationBonus, players, IID_DamageReceiver, cmpRangeManager.GetEntityFlagMask("normal"));
     124    cmpRangeManager.EnableActiveQuery(this.enemyUnitsQuery);
     125};
     126
     127/**
     128 *Set up a range query for Gaia units within LOS range which can be attacked.
     129 * This should be called whenever our ownership changes.
     130 */
     131TurretAI.prototype.SetupGaiaRangeQuery = function()
     132{
     133    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     134    var owner = cmpOwnership.GetOwner();
     135
     136    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     137    var playerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     138    var cmpAttack = this.GetCmpAttack();
     139    if (!cmpAttack)
     140        return;
     141
     142    if (this.gaiaUnitsQuery)
     143    {
     144        cmpRangeManager.DestroyActiveQuery(this.gaiaUnitsQuery);
     145        this.gaiaUnitsQuery = undefined;
     146    }
     147
     148    if (owner == -1)
     149        return;
     150
     151    var cmpPlayer = Engine.QueryInterface(playerMan.GetPlayerByID(owner), IID_Player);
     152    if (!cmpPlayer.IsEnemy(0))
     153        return;
     154
     155    var range = cmpAttack.GetRange(this.bestAttack);
     156
     157    // This query is only interested in Gaia entities that can attack.
     158    this.gaiaUnitsQuery = cmpRangeManager.CreateActiveParabolicQuery(this.entity, range.min, range.max, range.elevationBonus, [0], IID_Attack, cmpRangeManager.GetEntityFlagMask("normal"));
     159    cmpRangeManager.EnableActiveQuery(this.gaiaUnitsQuery);
     160};
     161
     162/**
     163 * Called when units enter or leave range
     164 */
     165TurretAI.prototype.OnRangeUpdate = function(msg)
     166{
     167    var cmpAttack = this.GetCmpAttack();
     168    if (!cmpAttack)
     169        return;
     170
     171    if (msg.tag == this.gaiaUnitsQuery)
     172    {
     173        const filter = function(e) {
     174            let cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI);
     175            return (cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal()));
     176        };
     177
     178        if (msg.added.length)
     179            msg.added = msg.added.filter(filter);
     180
     181        // Removed entities may not have cmpUnitAI.
     182        for (let i = 0; i < msg.removed.length; ++i)
     183            if (this.targetUnits.indexOf(msg.removed[i]) == -1)
     184                msg.removed.splice(i--, 1);
     185    }
     186    else if (msg.tag != this.enemyUnitsQuery)
     187        return;
     188
     189    if (msg.added.length > 0)
     190        for (let entity of msg.added)
     191            if (cmpAttack.CanAttack(entity))
     192                this.targetUnits.push(entity);
     193           
     194    if (msg.removed.length > 0)
     195        for (let entity of msg.removed)
     196        {   
     197            let index = this.targetUnits.indexOf(entity);
     198            if (index > -1)
     199                this.targetUnits.splice(index, 1);
     200        }
     201
     202    if (!this.targetUnits.length || this.timer)
     203        return;
     204
     205    var attackTimers = cmpAttack.GetTimers(this.bestAttack);
     206    this.SelectAnimation("attack_" + this.bestAttack.toLowerCase(), false, 1.0, "attack");
     207    this.SetAnimationSync(attackTimers.prepare, attackTimers.repeat);
     208    // units entered the range, prepare to shoot
     209    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     210    this.timer = cmpTimer.SetInterval(this.entity, IID_TurretAI, "Attack", attackTimers.prepare, attackTimers.repeat, null);
     211};
     212
     213/**
     214 * Get Previous Target
     215 */
     216TurretAI.prototype.GetPreviousTarget = function()
     217{
     218    var cmpAttack = this.GetCmpAttack();
     219    if (!cmpAttack)
     220        return INVALID_ENTITY;
     221    var previousTarget = this.g_LatestTarget;
     222    var cmpUnitAI = Engine.QueryInterface(this.GetTurretParent(), IID_UnitAI);
     223    // if a unit ai, use that to overcome the difference between rangeManager range and unitMotion range
     224    if (cmpUnitAI)
     225    {
     226        if (cmpUnitAI.CheckTargetAttackRange(previousTarget, this.bestAttack))
     227            return previousTarget;
     228        return INVALID_ENTITY;
     229    }
     230    // else just use the range manager (which gives us the list of possible targets);
     231    if (this.targetUnits.indexOf(previousTarget) != -1)
     232        return previousTarget;
     233    return INVALID_ENTITY;
     234};
     235
     236/**
     237 * Attack other entities
     238 */
     239TurretAI.prototype.Attack = function()
     240{
     241    if (!this.targetUnits.length)
     242    {
     243        if (this.timer)
     244        {
     245            // stop the timer
     246            let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     247            cmpTimer.CancelTimer(this.timer);
     248            this.timer = undefined;
     249        }
     250        this.SelectAnimation("idle");
     251        return;
     252    }
     253   
     254    var cmpAttack = this.GetCmpAttack();
     255    if (!cmpAttack)
     256        return;
     257    var target = this.GetPreviousTarget();
     258    // if no target, select a random one
     259    if (target == INVALID_ENTITY)
     260    {
     261        var selectedIndex = -1;
     262        var targets = new WeightedList();
     263        for (let i = 0; i < this.targetUnits.length; i++)
     264        {
     265            let target = this.targetUnits[i];
     266            let preference = cmpAttack.GetPreference(target);
     267            let weight = 1;
     268            if (preference !== null && preference !== undefined)
     269            {
     270                // Lower preference scores indicate a higher preference so they should result in a higher weight.
     271                weight = 1 + this.g_MaxPreferenceBonus / (1 + preference);
     272            }
     273            targets.push(target, weight);
     274        }
     275        selectedIndex = targets.randomIndex()
     276        target = targets.itemAt(selectedIndex);
     277    }
     278    // now we hope there's a target
     279    if (target != INVALID_ENTITY)
     280    {
     281        // TODO : It should only turn if the parent has more than one turret.
     282        this.TurnTowardsTarget(target);
     283        cmpAttack.PerformAttack(this.bestAttack, target, this.entity);
     284        this.g_LatestTarget = target;
     285    }
     286};
     287
     288/**
     289 * Selection the animations for the  turret
     290 */
     291TurretAI.prototype.SelectAnimation = function(name, once = false, speed = 1.0, sound)
     292{
     293    var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
     294    if (!cmpVisual)
     295        return;
     296
     297    var soundgroup;
     298    if (sound)
     299    {
     300        let cmpSound = Engine.QueryInterface(this.entity, IID_Sound);
     301        if (cmpSound)
     302            soundgroup = cmpSound.GetSoundGroup(sound);
     303    }
     304    cmpVisual.SelectAnimation(name, once, speed, soundgroup || "");
     305};
     306
     307/**
     308 * Synchronise Animations
     309 */
     310TurretAI.prototype.SetAnimationSync = function(actiontime, repeattime)
     311{
     312    var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
     313    if (!cmpVisual)
     314        return;
     315
     316    cmpVisual.SetAnimationSyncRepeat(repeattime);
     317    cmpVisual.SetAnimationSyncOffset(actiontime);
     318};
     319
     320/**
     321 * Orient the turret toward his foe.
     322 */
     323TurretAI.prototype.TurnTowardsTarget = function(target)
     324{
     325    var cmpThisPosition = Engine.QueryInterface(this.entity, IID_Position);
     326    var cmpTargetPosition = Engine.QueryInterface(target, IID_Position);
     327    if (!cmpThisPosition || !cmpTargetPosition || !cmpThisPosition.IsInWorld() || !cmpTargetPosition.IsInWorld())
     328        return;
     329
     330    var pos = cmpTargetPosition.GetPosition2D().sub(cmpThisPosition.GetPosition2D());
     331    cmpThisPosition.TurnTo(Math.atan2(pos.x, pos.y));
     332};
     333
     334Engine.RegisterComponentType(IID_TurretAI, "TurretAI", TurretAI);
  • binaries/data/mods/public/simulation/components/TurretHolder.js

     
     1function TurretHolder() {}
     2
     3TurretHolder.prototype.Schema =
     4    "<element name='TurretPoints'>" +
     5        "<zeroOrMore>" +
     6            "<element a:help='Element containing the offset coordinates and the template'>" +
     7                "<anyName/>" +
     8                "<interleave>" +
     9                    "<element name='Template'>" +
     10                        "<text/>" +
     11                    "</element>" +
     12                    "<element name='X'>" +
     13                        "<data type='decimal'/>" +
     14                    "</element>" +
     15                    "<element name='Y'>" +
     16                        "<data type='decimal'/>" +
     17                    "</element>" +
     18                    "<element name='Z'>" +
     19                        "<data type='decimal'/>" +
     20                    "</element>" +
     21                "</interleave>" +
     22            "</element>" +
     23        "</zeroOrMore>" +
     24    "</element>";
     25
     26/**
     27 * Initialize TurretHolder Component.
     28 */
     29TurretHolder.prototype.Init = function()
     30{
     31    this.turrets = [];
     32    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     33    // hack for atlas, don't create the turrets in Atlas, as the references get lost
     34    // TODO implement some sort of tag for the turrets so they never get saved by Atlas
     35/*  if (cmpTimer.GetTime() == 0)
     36        cmpTimer.SetTimeout(this.entity, IID_TurretHolder, "CreateTurrets", 100, null);
     37    else*/
     38        this.CreateTurrets();
     39};
     40
     41/**
     42 * Create the Turrets.
     43 */
     44TurretHolder.prototype.CreateTurrets = function()
     45{
     46    for each (let turretPoint in this.template.TurretPoints)
     47    {
     48        var ent = Engine.AddEntity(turretPoint.Template);
     49        var offset = new Vector3D(+turretPoint.X, +turretPoint.Y, +turretPoint.Z);
     50        var cmpPosition = Engine.QueryInterface(ent, IID_Position);
     51        if (cmpPosition)
     52            cmpPosition.SetTurretParent(this.entity, offset);
     53        this.turrets.push(ent);
     54    }
     55    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     56    if (cmpOwnership && cmpOwnership.GetOwner() != -1)
     57        this.ChangeTurretOwnership(cmpOwnership.GetOwner());
     58};
     59
     60/**
     61 * Return the list of entities garrisoned inside.
     62 */
     63TurretHolder.prototype.GetTurrets = function()
     64{
     65    return this.turrets;
     66};
     67
     68/**
     69 * Destroys the entities.
     70 */
     71TurretHolder.prototype.OnDestroy = function()
     72{
     73    for (let ent of this.turrets)
     74        Engine.DestroyEntity(ent);
     75};
     76
     77/**
     78 * Change the ownership of the turret.
     79 */
     80TurretHolder.prototype.OnOwnershipChanged = function(msg)
     81{
     82    this.ChangeTurretOwnership(msg.to);
     83};
     84
     85/**
     86 * Set the ownership of all present turrets to the same owner.
     87 */
     88TurretHolder.prototype.ChangeTurretOwnership = function(owner)
     89{
     90    for (let ent of this.turrets)
     91    {
     92        var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
     93        if (cmpOwnership)
     94            cmpOwnership.SetOwner(owner);
     95    }
     96};
     97
     98Engine.RegisterComponentType(IID_TurretHolder, "TurretHolder", TurretHolder);
     99
  • binaries/data/mods/public/simulation/templates/template_turret.xml

     
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity parent="template_entity_full">
     3    <Decay>
     4        <Active>false</Active>
     5        <SinkingAnim>false</SinkingAnim>
     6        <DelayTime>80.0</DelayTime>
     7        <SinkRate>0.01</SinkRate>
     8        <SinkAccel>0.0</SinkAccel>
     9    </Decay>
     10    <Minimap>
     11        <Type>unit</Type>
     12    </Minimap>
     13    <OverlayRenderer/>
     14    <Selectable disable=""/>
     15    <Sound>
     16        <SoundGroups>
     17            <attacked>interface/alarm/alarm_attackplayer.xml</attacked>
     18        </SoundGroups>
     19    </Sound>
     20    <TurretAI/>
     21    <Visibility>
     22        <RetainInFog>false</RetainInFog>
     23        <AlwaysVisible>false</AlwaysVisible>
     24        <Preview>false</Preview>
     25        <Corpse>false</Corpse>
     26    </Visibility>
     27    <Vision>
     28        <Range>92</Range>
     29    </Vision>
     30    <VisualActor>
     31        <SilhouetteDisplay>true</SilhouetteDisplay>
     32        <SilhouetteOccluder>false</SilhouetteOccluder>
     33        <VisibleInAtlasOnly>false</VisibleInAtlasOnly>
     34    </VisualActor>
     35</Entity>
  • binaries/data/mods/public/simulation/templates/units/brit_hero_bouddica_r.xml

    Property changes on: binaries/data/mods/public/simulation/templates/template_turret.xml
    ___________________________________________________________________
    Added: svn:mime-type
    ## -0,0 +1 ##
    +text/xml
    \ No newline at end of property
     
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity parent="template_turret">
     3  <VisualActor>
     4    <Actor>units/celts/boudicca_r2.xml</Actor>
     5  </VisualActor>
     6</Entity>
  • binaries/data/mods/public/simulation/templates/units/brit_hero_boudicca.xml

     
    11<?xml version="1.0" encoding="utf-8"?>
    22<Entity parent="template_unit_hero_cavalry_javelinist">
     3  <Attack>
     4    <Ranged>
     5      <TurretsOnly>true</TurretsOnly>
     6    </Ranged>
     7  </Attack>
    38  <Auras>
    49    <Aura1>
    510      <Type>global</Type>
     
    2631    <History>Ammianus Marcellinus described how difficult it would be for a band of foreigners to deal with a Celt if he called in the help of his wife. For she was stronger than he was and could rain blows and kicks upon the assailants equal in force to the shots of a catapult. Boudicca, queen of the Iceni, was said to be 'very tall and terrifying in appearance; her voice was very harsh and a great mass of red hair fell over her shoulders. She wore a tunic of many colors over which a thick cloak was fastened by a brooch.</History>
    2732    <Icon>units/celt_hero_boudicca.png</Icon>
    2833  </Identity>
     34    <TurretHolder>
     35        <TurretPoints>
     36            <Archer>
     37                <Template>units/brit_hero_bouddica_r</Template>
     38                    <X>0</X>
     39                    <Y>1.4</Y>
     40                    <Z>-2.5</Z>
     41            </Archer>
     42        </TurretPoints>
     43    </TurretHolder>
    2944  <VisualActor>
    3045    <Actor>units/celts/boudicca_chariot.xml</Actor>
    3146  </VisualActor>
  • binaries/data/mods/public/simulation/templates/units/pers_cavalry_archer_a.xml

     
    11<?xml version="1.0" encoding="utf-8"?>
    22<Entity parent="units/pers_cavalry_archer_b">
     3  <Attack>
     4    <Ranged>
     5      <TurretsOnly>true</TurretsOnly>
     6    </Ranged>
     7  </Attack>
    38  <Identity>
    49    <Rank>Advanced</Rank>
    510  </Identity>
     
    611  <Promotion>
    712    <Entity>units/pers_cavalry_archer_e</Entity>
    813  </Promotion>
     14  <TurretHolder>
     15    <TurretPoints>
     16      <Archer>
     17        <Template>units/pers_cavalry_archer_rider_a</Template>
     18        <X>1</X>
     19         <Y>2.2</Y>
     20         <Z>-4.0</Z>
     21      </Archer>
     22    </TurretPoints>
     23  </TurretHolder>
    924  <VisualActor>
    1025    <Actor>units/persians/cavalry_archer_a.xml</Actor>
    1126  </VisualActor>
  • binaries/data/mods/public/simulation/templates/units/pers_cavalry_archer_e.xml

     
    44    <Rank>Elite</Rank>
    55  </Identity>
    66  <Promotion disable=""/>
     7  <TurretHolder>
     8    <TurretPoints>
     9      <Archer>
     10        <Template>units/pers_cavalry_archer_rider_e</Template>
     11        <X>1</X>
     12         <Y>2.2</Y>
     13         <Z>-4.0</Z>
     14      </Archer>
     15    </TurretPoints>
     16  </TurretHolder>
    717  <VisualActor>
    818    <Actor>units/persians/cavalry_archer_e.xml</Actor>
    919  </VisualActor>
  • binaries/data/mods/public/simulation/templates/units/pers_cavalry_archer_rider_a.xml

     
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity parent="template_turret">
     3  <VisualActor>
     4    <Actor>units/persians/cavalry_archer_a_r.xml</Actor>
     5  </VisualActor>
     6</Entity>
  • binaries/data/mods/public/simulation/templates/units/pers_cavalry_archer_rider_e.xml

    Property changes on: binaries/data/mods/public/simulation/templates/units/pers_cavalry_archer_rider_a.xml
    ___________________________________________________________________
    Added: svn:mime-type
    ## -0,0 +1 ##
    +text/xml
    \ No newline at end of property
     
     1<?xml version="1.0" encoding="utf-8"?>
     2<Entity parent="template_turret">
     3  <VisualActor>
     4    <Actor>units/persians/cavalry_archer_e_r.xml</Actor>
     5  </VisualActor>
     6</Entity>