Ticket #996: capture.4.diff

File capture.4.diff, 55.7 KB (added by sanderd17, 9 years ago)
  • binaries/data/mods/public/globalscripts/Templates.js

     
    9090
    9191    if (template.Attack)
    9292    {
     93        let getAttackStat = function(type, stat)
     94        {
     95            return func("Attack/"+type+"/"+stat, +(template.Attack[type][stat] || 0), player, template);
     96        };
     97
    9398        ret.attack = {};
    94         for (var type in template.Attack)
     99        for (let type in template.Attack)
    95100        {
    96             ret.attack[type] = {
    97                 "hack": func("Attack/"+type+"/Hack", +(template.Attack[type].Hack || 0), player, template),
    98                 "pierce": func("Attack/"+type+"/Pierce", +(template.Attack[type].Pierce || 0), player, template),
    99                 "crush": func("Attack/"+type+"/Crush", +(template.Attack[type].Crush || 0), player, template),
    100                 "minRange": func("Attack/"+type+"/MinRange", +(template.Attack[type].MinRange || 0), player, template),
    101                 "maxRange": func("Attack/"+type+"/MaxRange", +template.Attack[type].MaxRange, player, template),
    102                 "elevationBonus": func("Attack/"+type+"/ElevationBonus", +(template.Attack[type].ElevationBonus || 0), player, template),
    103                 "repeatTime": +(template.Attack[type].RepeatTime || 0),
    104             };
     101            if (type == "Capture")
     102                ret.attack.Capture = {
     103                    "value": getAttackStat(type,"Value"),
     104                };
     105            else
     106                ret.attack[type] = {
     107                    "hack": getAttackStat(type, "Hack"),
     108                    "pierce": getAttackStat(type, "Pierce"),
     109                    "crush": getAttackStat(type, "Crush"),
     110                    "minRange": getAttackStat(type, "MinRange"),
     111                    "maxRange": getAttackStat(type, "MaxRange"),
     112                    "elevationBonus": getAttackStat(type, "ElevationBonus"),
     113                };
     114            ret.attack[type].repeatTime = +(template.Attack[type].RepeatTime || 0);
    105115        }
    106116    }
    107117
  • binaries/data/mods/public/gui/common/functions_utility.js

     
    166166// ====================================================================
    167167
    168168// Convert integer color values to string (for use in GUI objects)
    169 function rgbToGuiColor(color)
     169function rgbToGuiColor(color, alpha)
    170170{
     171    var ret;
    171172    if (color && ("r" in color) && ("g" in color) && ("b" in color))
    172         return color.r + " " + color.g + " " + color.b;
    173 
    174     return "0 0 0";
     173        ret = color.r + " " + color.g + " " + color.b;
     174    else
     175        ret = "0 0 0";
     176    if (alpha)
     177        ret += " " + alpha;
     178    return ret;
    175179}
    176180
    177181// ====================================================================
  • binaries/data/mods/public/gui/common/tooltips.js

     
    140140    if (type === "Charge") return translate("Charge Attack:");
    141141    if (type === "Melee") return translate("Melee Attack:");
    142142    if (type === "Ranged") return translate("Ranged Attack:");
     143    if (type === "Capture") return translate("Capture Attack:");
    143144
    144145    warn(sprintf("Internationalization: Unexpected attack type found with code ‘%(attackType)s’. This attack type must be internationalized.", { attackType: type }));
    145146    return translate("Attack:");
     
    169170        });
    170171
    171172        var attackLabel = txtFormats.header[0] + getAttackTypeLabel(type) + txtFormats.header[1];
     173        if (type == "Capture")
     174        {
     175            attacks.push(sprintf(translate("%(attackLabel)s %(details)s, %(rate)s"), {
     176                attackLabel: attackLabel,
     177                details: template.attack[type].value,
     178                rate: rate
     179            }));
     180            continue;
     181        }
    172182        if (type != "Ranged")
    173183        {
    174184            attacks.push(sprintf(translate("%(attackLabel)s %(details)s, %(rate)s"), {
     
    206216            }));
    207217    }
    208218
    209     return attacks.join(translate(", "));
     219    return attacks.join("\n");
    210220}
    211221
    212222/**
  • binaries/data/mods/public/gui/session/selection_details.js

     
    5858    }
    5959
    6060    // Hitpoints
     61    Engine.GetGUIObjectByName("healthSection").hidden = !entState.hitpoints;
    6162    if (entState.hitpoints)
    6263    {
    6364        var unitHealthBar = Engine.GetGUIObjectByName("healthBar");
     
    7980            hitpoints: Math.ceil(entState.hitpoints),
    8081            maxHitpoints: entState.maxHitpoints
    8182        });
    82         Engine.GetGUIObjectByName("healthSection").hidden = false;
    8383    }
    84     else
     84
     85    // CapturePoints
     86    Engine.GetGUIObjectByName("captureSection").hidden = !entState.capturePoints;
     87    if (entState.capturePoints)
    8588    {
    86         Engine.GetGUIObjectByName("healthSection").hidden = true;
     89        let setCaptureBarPart = function(playerID, startSize)
     90        {
     91            var unitCaptureBar = Engine.GetGUIObjectByName("captureBar["+playerID+"]");
     92            var sizeObj = unitCaptureBar.size;
     93            sizeObj.rleft = startSize;
     94
     95            var size = 100*Math.max(0, Math.min(1, entState.capturePoints[playerID] / entState.maxCapturePoints));
     96            sizeObj.rright = startSize + size;
     97            unitCaptureBar.size = sizeObj;
     98            unitCaptureBar.sprite = "color: " + rgbToGuiColor(g_Players[playerID].color, 128);
     99            unitCaptureBar.hidden=false;
     100            return startSize + size;
     101        }
     102
     103        // first handle the owner's points, to keep those points on the left for clarity
     104        let size = setCaptureBarPart(entState.player, 0);
     105
     106        for (let i in entState.capturePoints)
     107            if (i != entState.player)
     108                size = setCaptureBarPart(i, size);
     109
     110
     111        Engine.GetGUIObjectByName("captureStats").caption = sprintf(translate("%(capturePoints)s / %(maxCapturePoints)s"), {
     112            capturePoints: Math.ceil(entState.capturePoints[entState.player]),
     113            maxCapturePoints: entState.maxCapturePoints
     114        });
    87115    }
    88116
    89117    // TODO: Stamina
    90     var player = Engine.GetPlayerID();
    91     if (entState.stamina && (entState.player == player || g_DevSettings.controlAll))
    92         Engine.GetGUIObjectByName("staminaSection").hidden = false;
    93     else
    94         Engine.GetGUIObjectByName("staminaSection").hidden = true;
    95118
    96119    // Experience
     120    Engine.GetGUIObjectByName("experience").hidden = !entState.promotion;
    97121    if (entState.promotion)
    98122    {
    99123        var experienceBar = Engine.GetGUIObjectByName("experienceBar");
     
    112136                experience: "[font=\"sans-bold-13\"]" + translate("Experience:") + "[/font]",
    113137                current: Math.floor(entState.promotion.curr)
    114138            });
    115         Engine.GetGUIObjectByName("experience").hidden = false;
    116139    }
    117     else
    118     {
    119         Engine.GetGUIObjectByName("experience").hidden = true;
    120     }
    121140
    122141    // Resource stats
     142    Engine.GetGUIObjectByName("resourceSection").hidden = !entState.resourceSupply;
    123143    if (entState.resourceSupply)
    124144    {
    125145        var resources = entState.resourceSupply.isInfinite ? translate("∞") :  // Infinity symbol
     
    136156        Engine.GetGUIObjectByName("resourceStats").caption = resources;
    137157
    138158        if (entState.hitpoints)
    139             Engine.GetGUIObjectByName("resourceSection").size = Engine.GetGUIObjectByName("staminaSection").size;
     159            Engine.GetGUIObjectByName("resourceSection").size = Engine.GetGUIObjectByName("captureSection").size;
    140160        else
    141161            Engine.GetGUIObjectByName("resourceSection").size = Engine.GetGUIObjectByName("healthSection").size;
    142162
    143         Engine.GetGUIObjectByName("resourceSection").hidden = false;
    144163    }
    145     else
    146     {
    147         Engine.GetGUIObjectByName("resourceSection").hidden = true;
    148     }
    149164
    150165    // Resource carrying
    151166    if (entState.resourceCarrying && entState.resourceCarrying.length)
     
    290305{
    291306    var averageHealth = 0;
    292307    var maxHealth = 0;
     308    var maxCapturePoints = 0;
     309    var capturePoints = (new Array(9)).fill(0);
     310    var playerID = 0;
    293311
    294312    for (var i = 0; i < selection.length; i++)
    295313    {
    296314        var entState = GetEntityState(selection[i])
    297         if (entState)
     315        if (!entState)
     316            continue;
     317        playerID = entState.player; // trust that all selected entities have the same owner
     318        if (entState.hitpoints)
    298319        {
    299             if (entState.hitpoints)
    300             {
    301                 averageHealth += entState.hitpoints;
    302                 maxHealth += entState.maxHitpoints;
    303             }
     320            averageHealth += entState.hitpoints;
     321            maxHealth += entState.maxHitpoints;
    304322        }
     323        if (entState.capturePoints)
     324        {
     325            maxCapturePoints += entState.maxCapturePoints;
     326            capturePoints = entState.capturePoints.map(function(v, i) { return v + capturePoints[i]; });
     327        }
    305328    }
    306329
     330    Engine.GetGUIObjectByName("healthMultiple").hidden = averageHealth <= 0;
    307331    if (averageHealth > 0)
    308332    {
    309333        var unitHealthBar = Engine.GetGUIObjectByName("healthBarMultiple");
     
    313337
    314338        var hitpointsLabel = "[font=\"sans-bold-13\"]" + translate("Hitpoints:") + "[/font]"
    315339        var hitpoints = sprintf(translate("%(label)s %(current)s / %(max)s"), { label: hitpointsLabel, current: averageHealth, max: maxHealth });
    316         var healthMultiple = Engine.GetGUIObjectByName("healthMultiple");
    317         healthMultiple.tooltip = hitpoints;
    318         healthMultiple.hidden = false;
     340        Engine.GetGUIObjectByName("healthMultiple").tooltip = hitpoints;
    319341    }
    320     else
     342
     343    Engine.GetGUIObjectByName("captureMultiple").hidden = maxCapturePoints <= 0;
     344    if (maxCapturePoints > 0)
    321345    {
    322         Engine.GetGUIObjectByName("healthMultiple").hidden = true;
     346        let setCaptureBarPart = function(playerID, startSize)
     347        {
     348            var unitCaptureBar = Engine.GetGUIObjectByName("captureBarMultiple["+playerID+"]");
     349            var sizeObj = unitCaptureBar.size;
     350            sizeObj.rtop = startSize;
     351
     352            var size = 100*Math.max(0, Math.min(1, capturePoints[playerID] / maxCapturePoints));
     353            sizeObj.rbottom = startSize + size;
     354            unitCaptureBar.size = sizeObj;
     355            unitCaptureBar.sprite = "color: " + rgbToGuiColor(g_Players[playerID].color, 128);
     356            unitCaptureBar.hidden=false;
     357            return startSize + size;
     358        }
     359
     360        let size = 0;
     361        for (let i in entState.capturePoints)
     362            if (i != playerID)
     363                size = setCaptureBarPart(i, size);
     364
     365        // last handle the owner's points, to keep those points on the bottom for clarity
     366        setCaptureBarPart(playerID, size);
     367
     368        var capturePointsLabel = "[font=\"sans-bold-13\"]" + translate("Capture points:") + "[/font]"
     369        var capturePointsTooltip = sprintf(translate("%(label)s %(current)s / %(max)s"), { label: capturePointsLabel, current: Math.ceil(capturePoints[playerID]), max: Math.ceil(maxCapturePoints) });
     370        Engine.GetGUIObjectByName("captureMultiple").tooltip = capturePointsTooltip;
    323371    }
    324372   
    325373    // TODO: Stamina
  • binaries/data/mods/public/gui/session/selection_panels_middle/multiple_details_area.xml

     
    3333        <object type="image" sprite="statsBarShaderVertical" ghost="true"/>
    3434    </object>
    3535
    36     <!-- Stamina bar -->
    37     <object size="15 0 22 100%" type="image" name="staminaMultiple" tooltip_style="sessionToolTipBold">
    38         <translatableAttribute id="tooltip">Stamina</translatableAttribute>
     36    <!-- Capture bar -->
     37    <object size="15 0 22 100%" type="image" name="captureMultiple" tooltip_style="sessionToolTipBold">
     38        <translatableAttribute id="tooltip">Capture points</translatableAttribute>
    3939        <object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
    40         <object type="image" sprite="staminaBackground" ghost="true"/>
    41         <object type="image" sprite="staminaForeground" ghost="true" name="staminaBarMultiple"/>
     40        <repeat count="9">
     41            <object type="image" sprite="playerColorBackground" ghost="true" name="captureBarMultiple[n]" hidden="true"/>
     42        </repeat>
    4243        <object type="image" sprite="statsBarShaderVertical" ghost="true"/>
    4344    </object>
    4445    </object>
  • binaries/data/mods/public/gui/session/selection_panels_middle/single_details_area.xml

     
    2020        </object>
    2121        </object>
    2222
    23         <!-- Stamina bar -->
    24         <object size="88 28 100% 52" name="staminaSection">
    25         <object size="0 0 100% 16" name="staminaLabel" type="text" style="StatsTextLeft" ghost="true">
    26             <translatableAttribute id="tooltip">Stamina:</translatableAttribute>
     23        <!-- Capture bar -->
     24        <object size="88 28 100% 52" name="captureSection">
     25        <object size="0 0 100% 16" name="captureLabel" type="text" style="StatsTextLeft" ghost="true">
     26            <translatableAttribute id="tooltip">Capture points:</translatableAttribute>
    2727        </object>
    28         <object size="0 0 100% 16" name="staminaStats" type="text" style="StatsTextRight" ghost="true"/>
    29         <object size="1 16 100% 23" name="stamina" type="image">
     28        <object size="0 0 100% 16" name="captureStats" type="text" style="StatsTextRight" ghost="true"/>
     29        <object size="1 16 100% 23" name="capture" type="image">
    3030            <object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
    31             <object type="image" sprite="staminaBackground" ghost="true"/>
    32             <object type="image" sprite="staminaForeground" ghost="true" name="staminaBar"/>
     31            <repeat count="9">
     32                <object type="image" sprite="playerColorBackground" ghost="true" name="captureBar[n]" hidden="true"/>
     33            </repeat>
    3334            <object type="image" sprite="statsBarShaderHorizontal" ghost="true"/>
    3435        </object>
    3536        </object>
  • binaries/data/mods/public/gui/session/unit_actions.js

     
    9999        {
    100100            if (!entState.attack || !targetState.hitpoints)
    101101                return false;
    102             if (playerCheck(entState, targetState, ["Neutral", "Enemy"]))
    103                 return {"possible": Engine.GuiInterfaceCall("CanAttack", {"entity": entState.id, "target": targetState.id})};
    104             return false;
     102            return {"possible": Engine.GuiInterfaceCall("CanAttack", {"entity": entState.id, "target": targetState.id})};
    105103        },
    106104        "hotkeyActionCheck": function(target)
    107105        {
     
    672670                    "icon": "kill_small.png"
    673671                };
    674672
     673            if (entState.capturePoints && entState.capturePoints[entState.player] < entState.maxCapturePoints / 2)
     674                return {
     675                    "tooltip": translate("You cannot destroy this entity as you own less than half the capture points"),
     676                    "icon": "kill_small.png"
     677                };
     678                   
     679
    675680            return {
    676681                "tooltip": translate("Delete"),
    677682                "icon": "kill_small.png"
  • binaries/data/mods/public/simulation/components/AIProxy.js

     
    111111    this.changes.hitpoints = msg.to;
    112112};
    113113
     114AIProxy.prototype.OnCapturePointsChanged = function(msg)
     115{
     116    if (!this.NotifyChange())
     117        return;
     118    this.changes.capturePoints = msg.capturePoints;
     119};
     120
    114121AIProxy.prototype.OnUnitIdleChanged = function(msg)
    115122{
    116123    if (!this.NotifyChange())
  • binaries/data/mods/public/simulation/components/Attack.js

     
    159159        "</element>" +
    160160    "</optional>" +
    161161    "<optional>" +
     162        "<element name='Capture'>" +
     163            "<interleave>" +
     164                "<element name='Value' a:help='Capture points value'><ref name='nonNegativeDecimal'/></element>" +
     165                "<element name='MaxRange' a:help='Maximum attack range (in meters)'><ref name='nonNegativeDecimal'/></element>" +
     166                "<element name='RepeatTime' a:help='Time between attacks (in milliseconds). The attack animation will be stretched to match this time'>" + // TODO: it shouldn't be stretched
     167                    "<data type='positiveInteger'/>" +
     168                "</element>" +
     169                Attack.prototype.bonusesSchema +
     170                Attack.prototype.preferredClassesSchema +
     171                Attack.prototype.restrictedClassesSchema +
     172            "</interleave>" +
     173        "</element>" +
     174    "</optional>" +
     175    "<optional>" +
    162176        "<element name='Charge'>" +
    163177            "<interleave>" +
    164178                "<element name='Hack' a:help='Hack damage strength'><ref name='nonNegativeDecimal'/></element>" +
     
    198212    if (this.template.Charge) ret.push("Charge");
    199213    if (this.template.Melee) ret.push("Melee");
    200214    if (this.template.Ranged) ret.push("Ranged");
     215    if (this.template.Capture) ret.push("Capture");
    201216    return ret;
    202217};
    203218
     
    223238
    224239Attack.prototype.CanAttack = function(target)
    225240{
     241    var cmpArmour = Engine.QueryInterface(target, IID_DamageReceiver);
     242    if (!cmpArmour)
     243        return false;
     244
    226245    var cmpFormation = Engine.QueryInterface(target, IID_Formation);
    227246    if (cmpFormation)
    228247        return true;
     
    309328    if (cmpFormation)
    310329        return this.GetBestAttack();
    311330
    312     const cmpIdentity = Engine.QueryInterface(target, IID_Identity);
     331    var cmpIdentity = Engine.QueryInterface(target, IID_Identity);
    313332    if (!cmpIdentity)
    314333        return undefined;
    315334
    316     const targetClasses = cmpIdentity.GetClassesList();
    317     const isTargetClass = function (value, i, a) { return targetClasses.indexOf(value) != -1; };
    318     const types = this.GetAttackTypes();
    319     const attack = this;
    320     const isAllowed = function (value, i, a) { return !attack.GetRestrictedClasses(value).some(isTargetClass); }
    321     const isPreferred = function (value, i, a) { return attack.GetPreferredClasses(value).some(isTargetClass); }
    322     const byPreference = function (a, b) { return (types.indexOf(a) + (isPreferred(a) ? types.length : 0) ) - (types.indexOf(b) + (isPreferred(b) ? types.length : 0) ); }
    323335
     336    var targetClasses = cmpIdentity.GetClassesList();
     337    var isTargetClass = function (className) { return targetClasses.indexOf(className) != -1; };
     338
    324339    // Always slaughter domestic animals instead of using a normal attack
    325340    if (isTargetClass("Domestic") && this.template.Slaughter)
    326341        return "Slaughter";
    327342
    328     return types.filter(isAllowed).sort(byPreference).pop();
     343    var attack = this;
     344    var isAllowed = function (type) { return !attack.GetRestrictedClasses(type).some(isTargetClass); }
     345
     346    var types = this.GetAttackTypes().filter(isAllowed);
     347
     348    // check if the target is capturable
     349    var captureIndex = types.indexOf("Capture")
     350    if (captureIndex != -1)
     351    {
     352        var cmpCapturable = Engine.QueryInterface(target, IID_Capturable);
     353        var cmpPlayer = QueryOwnerInterface(this.entity);
     354        if (cmpPlayer && cmpCapturable && cmpCapturable.CanCapture(cmpPlayer.GetPlayerID()))
     355            return "Capture";
     356        // not captureable, so remove this attack
     357        types.splice(captureIndex, 1);
     358    }
     359
     360    var isPreferred = function (className) { return attack.GetPreferredClasses(className).some(isTargetClass); }
     361    var byPreference = function (a, b) { return (types.indexOf(a) + (isPreferred(a) ? types.length : 0) ) - (types.indexOf(b) + (isPreferred(b) ? types.length : 0) ); }
     362
     363
     364    return types.sort(byPreference).pop();
    329365};
    330366
    331367Attack.prototype.CompareEntitiesByPreference = function(a, b)
     
    367403    {
    368404        return ApplyValueModificationsToEntity("Attack/" + type + splash + "/" + damageType, +(template[damageType] || 0), self.entity);
    369405    };
    370    
     406
     407    if (type == "Capture")
     408        return {value: applyMods("Value")};
     409
    371410    return {
    372411        hack: applyMods("Hack"),
    373412        pierce: applyMods("Pierce"),
     
    517556        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    518557        cmpTimer.SetTimeout(this.entity, IID_Attack, "MissileHit", timeToTarget*1000, {"type": type, "target": target, "position": realTargetPosition, "direction": missileDirection, "projectileId": id, "playerId":playerId});
    519558    }
     559    else if (type == "Capture")
     560    {
     561        var multiplier = this.GetAttackBonus(type, target);
     562        var cmpHealth = Engine.QueryInterface(target, IID_Health);
     563        if (!cmpHealth || cmpHealth.GetHitpoints() == 0)
     564            return;
     565        multiplier *= cmpHealth.GetMaxHitpoints() / cmpHealth.GetHitpoints();
     566
     567        var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     568        if (!cmpOwnership || cmpOwnership.GetOwner() == -1)
     569            return;
     570        var owner = cmpOwnership.GetOwner();
     571        var cmpCapturable = Engine.QueryInterface(target, IID_Capturable);
     572        if (!cmpCapturable || !cmpCapturable.CanCapture(owner))
     573            return;
     574       
     575        var strength = this.GetAttackStrengths("Capture").value;
     576        cmpCapturable.Reduce(strength * multiplier, owner);
     577    }
    520578    else
    521579    {
    522580        // Melee attack - hurt the target immediately
     
    585643        // If friendlyFire isn't enabled, get all player enemies to pass to "Damage.CauseSplashDamage".
    586644        if (friendlyFire == "false")
    587645        {
    588             var cmpPlayer = Engine.QueryInterface(Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(data.playerId), IID_Player)
     646            var cmpPlayer = QueryPlayerIDInterface(data.playerId);
    589647            playersToDamage = cmpPlayer.GetEnemies();
    590648        }
    591649        // Damage the units.
     
    607665    else
    608666    {
    609667        // If we didn't hit the main target look for nearby units
    610         var cmpPlayer = Engine.QueryInterface(Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(data.playerId), IID_Player)
     668        var cmpPlayer = QueryPlayerIDInterface(data.playerId);
    611669        var ents = Damage.EntitiesNearPoint(Vector2D.from3D(data.position), targetPosition.horizDistanceTo(data.position) * 2, cmpPlayer.GetEnemies());
    612670
    613671        for (var i = 0; i < ents.length; i++)
  • binaries/data/mods/public/simulation/components/Capturable.js

     
     1function Capturable() {}
     2
     3Capturable.prototype.Schema =
     4    "<element name='CapturePoints' a:help='Maximum capture points'>" +
     5        "<ref name='positiveDecimal'/>" +
     6    "</element>" +
     7    "<element name='RegenRate' a:help='Number of capture are regenerated per second in favour of the owner'>" +
     8        "<ref name='nonNegativeDecimal'/>" +
     9    "</element>" +
     10    "<element name='GarrisonRegenRate' a:help='Number of capture are regenerated per second and per garrisoned unit in favour of the owner'>" +
     11        "<ref name='nonNegativeDecimal'/>" +
     12    "</element>";
     13
     14Capturable.prototype.Init = function()
     15{
     16    // Cache this value
     17    this.maxCp = +this.template.CapturePoints;
     18    this.cp = [];
     19    this.startRegenTimer();
     20};
     21
     22//// Interface functions ////
     23
     24/**
     25 * Returns the current capture points array
     26 */
     27Capturable.prototype.GetCapturePoints = function()
     28{
     29    return this.cp;
     30};
     31
     32Capturable.prototype.GetMaxCapturePoints = function()
     33{
     34    return this.maxCp;
     35};
     36
     37/**
     38 * Set the new capture points, used for cloning entities
     39 * The caller should assure that the sum of capture points
     40 * matches the max.
     41 */
     42Capturable.prototype.SetCapturePoints = function(capturePointsArray)
     43{
     44    this.cp = capturePointsArray;
     45};
     46
     47/**
     48 * Reduces the amount of capture points of an entity,
     49 * in favour of the player of the source
     50 * Returns the number of capture points actually taken
     51 */
     52Capturable.prototype.Reduce = function(amount, playerID)
     53{
     54    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     55    if (!cmpOwnership || cmpOwnership.GetOwner() == -1)
     56        return 0;
     57
     58    var cmpPlayerSource = QueryPlayerIDInterface(playerID);
     59    if (!cmpPlayerSource)
     60        return 0;
     61
     62    // Before changing the value, activate Fogging if necessary to hide changes
     63    var cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging);
     64    if (cmpFogging)
     65        cmpFogging.Activate();
     66
     67    var enemiesFilter = function(v, i) { return v > 0 && !cmpPlayerSource.IsAlly(i); };
     68    var numberOfEnemies = this.cp.filter(enemiesFilter).length;
     69
     70    if (numberOfEnemies == 0)
     71        return 0;
     72
     73    // distribute the capture points over all enemies
     74    var distributedAmount = amount / numberOfEnemies;
     75    for (let i in this.cp)
     76    {
     77        if (cmpPlayerSource.IsAlly(i))
     78            continue;
     79        if (this.cp[i] > distributedAmount)
     80            this.cp[i] -= distributedAmount;
     81        else
     82            this.cp[i] = 0;
     83    }
     84
     85    // give all cp taken to the player
     86    var takenCp = this.maxCp - this.cp.reduce(function(a, b) { return a + b; });
     87    this.cp[playerID] += takenCp;
     88
     89    this.startRegenTimer();
     90
     91    Engine.PostMessage(this.entity, MT_CapturePointsChanged, { "capturePoints": this.cp })
     92
     93    if (this.cp[cmpOwnership.GetOwner()] > 0)
     94        return takenCp;
     95
     96    // if all cp has been taken from the owner, convert it to the best player
     97    var bestPlayer = 0;
     98    for (let i in this.cp)
     99        if (this.cp[i] >= this.cp[bestPlayer])
     100            bestPlayer = +i;
     101
     102    cmpOwnership.SetOwner(bestPlayer);
     103
     104    return takenCp;
     105};
     106
     107/**
     108 * Check if the source can (re)capture points from this building
     109 */
     110Capturable.prototype.CanCapture = function(playerID)
     111{
     112    var cmpPlayerSource = QueryPlayerIDInterface(playerID);
     113
     114    if (!cmpPlayerSource)
     115        warn(source + " has no player component defined on its owner ");
     116    var cp = this.GetCapturePoints()
     117    var sourceEnemyCp = 0;
     118    for (let i in this.GetCapturePoints())
     119        if (!cmpPlayerSource.IsAlly(i))
     120            sourceEnemyCp += cp[i];
     121    return sourceEnemyCp > 0;
     122};
     123
     124//// Private functions ////
     125
     126Capturable.prototype.GetRegenRate = function()
     127{
     128    var regenRate = +this.template.RegenRate;
     129    regenRate = ApplyValueModificationsToEntity("Capturable/RegenRate", regenRate, this.entity);
     130
     131    var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
     132    if (!cmpGarrisonHolder)
     133        return regenRate;
     134
     135    var garrisonRegenRate = +this.template.GarrisonRegenRate;
     136    garrisonRegenRate = ApplyValueModificationsToEntity("Capturable/GarrisonRegenRate", garrisonRegenRate, this.entity);
     137    var garrisonedUnits = cmpGarrisonHolder.GetEntities().length;
     138    return regenRate + garrisonedUnits * garrisonRegenRate;
     139};
     140
     141Capturable.prototype.RegenCapturePoints = function()
     142{
     143    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     144    if (!cmpOwnership || cmpOwnership.GetOwner() == -1)
     145        return;
     146
     147    var takenCp = this.Reduce(this.GetRegenRate(), cmpOwnership.GetOwner())
     148    if (takenCp > 0)
     149        return;
     150
     151    // no capture points taken, stop the timer
     152    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     153    cmpTimer.CancelTimer(this.regenTimer);
     154    this.regenTimer = 0;
     155};
     156
     157/**
     158 * Start the regeneration timer when no timer exists
     159 * When nothing can be regenerated (f.e. because the
     160 * rate is 0, or because it is fully regenerated),
     161 * the timer stops automatically after one execution.
     162 */
     163Capturable.prototype.startRegenTimer = function()
     164{
     165    if (this.regenTimer)
     166        return;
     167    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     168    this.regenTimer = cmpTimer.SetInterval(this.entity, IID_Capturable, "RegenCapturePoints", 1000, 1000, null);
     169};
     170
     171//// Message Listeners ////
     172
     173Capturable.prototype.OnValueModification = function(msg)
     174{
     175    if (msg.component != "Capturable")
     176        return;
     177
     178    var oldMaxCp = this.GetMaxCapturePoints();
     179    this.maxCp = ApplyValueModificationsToEntity("Capturable/Max", +this.template.Max, this.entity);
     180    if (oldMaxCp == this.maxCp)
     181        return;
     182
     183    var scale = this.maxCp / oldMaxCp;
     184    for (let i in this.cp)
     185        this.cp[i] *= scale;
     186    Engine.PostMessage(this.entity, MT_CapturePointsChanged, { "capturePoints": this.cp });
     187    this.startRegenTimer();
     188};
     189
     190Capturable.prototype.OnGarrisonedUnitsChanged = function(msg)
     191{
     192    this.startRegenTimer();
     193};
     194
     195Capturable.prototype.OnOwnershipChanged = function(msg)
     196{
     197    this.startRegenTimer();
     198
     199    if (msg.from != -1)
     200        return;
     201
     202    // initialise the capture points when created
     203    this.cp = [];
     204    var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     205    for (let i = 0; i < cmpPlayerManager.GetNumPlayers(); ++i)
     206        if (i == msg.to)
     207            this.cp[i] = this.maxCp;
     208        else
     209            this.cp[i] = 0;
     210};
     211
     212Engine.RegisterComponentType(IID_Capturable, "Capturable", Capturable);
  • binaries/data/mods/public/simulation/components/Fogging.js

     
    125125            cmpHealth.IsRepairable() && (cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints())
    126126        );
    127127
     128    var cmpCapturable = Engine.QueryInterface(this.entity, IID_Capturable);
     129    if (cmpCapturable)
     130        cmpMirage.CopyCapturable(
     131            cmpCapturable.GetCapturePoints(),
     132            cmpCapturable.GetMaxCapturePoints()
     133        );
     134
    128135    var cmpResourceSupply = Engine.QueryInterface(this.entity, IID_ResourceSupply);
    129136    if (cmpResourceSupply)
    130137        cmpMirage.CopyResourceSupply(
  • binaries/data/mods/public/simulation/components/GuiInterface.js

     
    267267        ret.needsRepair = cmpMirage.NeedsRepair();
    268268    }
    269269
     270    var cmpCapturable = Engine.QueryInterface(ent, IID_Capturable);
     271    if (cmpCapturable)
     272    {
     273        ret.capturePoints = cmpCapturable.GetCapturePoints();
     274        ret.maxCapturePoints = cmpCapturable.GetMaxCapturePoints();
     275    }
     276    if (cmpMirage && cmpMirage.Capturable())
     277    {
     278        ret.capturePoints = cmpMirage.GetCapturePoints();
     279        ret.maxCapturePoints = cmpMirage.GetMaxCapturePoints();
     280    }
     281
    270282    var cmpBuilder = Engine.QueryInterface(ent, IID_Builder);
    271283    if (cmpBuilder)
    272284        ret.builder = true;
     
    17011713    var cmpAttack = Engine.QueryInterface(data.entity, IID_Attack);
    17021714    if (!cmpAttack)
    17031715        return false;
     1716    var cmpEntityPlayer = QueryOwnerInterface(data.entity, IID_Player);
     1717    var cmpTargetPlayer = QueryOwnerInterface(data.target, IID_Player);
     1718    if (!cmpEntityPlayer || !cmpTargetPlayer)
     1719        return false;
    17041720
    1705     return cmpAttack.CanAttack(data.target);
     1721
     1722    // if the owner is an enemy, it's up to the attack component to decide
     1723    if (!cmpEntityPlayer.IsAlly(cmpTargetPlayer.GetPlayerID()))
     1724        return cmpAttack.CanAttack(data.target);
     1725
     1726    // if the owner is an ally, we could still want to capture some capture points back
     1727    var cmpCapturable = Engine.QueryInterface(data.target, IID_Capturable);
     1728    if (cmpCapturable && cmpCapturable.CanCapture(cmpEntityPlayer.GetPlayerID()) && cmpAttack.GetAttackTypes().indexOf("Capture") != -1)
     1729        return cmpAttack.CanAttack(data.target);
     1730
     1731    return false;
    17061732};
    17071733
    17081734/*
  • binaries/data/mods/public/simulation/components/Mirage.js

     
    2121    this.hitpoints = null;
    2222    this.needsRepair = null;
    2323
     24    this.capturable = false;
     25    this.capturePoints = [];
     26    this.maxCapturePoints = 0;
     27
    2428    this.resourceSupply = false;
    2529    this.maxAmount = null;
    2630    this.amount = null;
     
    9498    return this.needsRepair;
    9599};
    96100
     101// Capture data
     102
     103Mirage.prototype.CopyCapturable = function(capturePoints, maxCapturePoints)
     104{
     105    this.capturable = true;
     106    this.capturePoints = capturePoints;
     107    this.maxCapturePoints = maxCapturePoints;
     108};
     109
     110Mirage.prototype.Capturable = function()
     111{
     112    return this.capturable;
     113};
     114
     115Mirage.prototype.GetMaxCapturePoints = function()
     116{
     117    return this.maxCapturePoints;
     118};
     119
     120Mirage.prototype.GetCapturePoints = function()
     121{
     122    return this.capturePoints;
     123};
     124
    97125// ResourceSupply data
    98126
    99127Mirage.prototype.CopyResourceSupply = function(maxAmount, amount, type, isInfinite)
  • binaries/data/mods/public/simulation/components/Pack.js

     
    148148        var cmpNewOwnership = Engine.QueryInterface(newEntity, IID_Ownership);
    149149        cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
    150150
     151        // rescale capture points
     152        var cmpCapturable = Engine.QueryInterface(this.entity, IID_Capturable);
     153        var cmpNewCapturable = Engine.QueryInterface(newEntity, IID_Capturable);
     154        if (cmpCapturable && cmpNewCapturable)
     155        {
     156            let scale = cmpCapturable.GetMaxCapturePoints() / cmpNewCapturable.GetMaxCapturePoints();
     157            let newCp = cmpCapturable.GetCapturePoints().map(function (v) { return v / scale; });
     158            cmpNewCapturable.SetCapturePoints(newCp);
     159        }
     160
    151161        // Maintain current health level
    152162        var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
    153163        var cmpNewHealth = Engine.QueryInterface(newEntity, IID_Health);
  • binaries/data/mods/public/simulation/components/TerritoryDecay.js

     
    11function TerritoryDecay() {}
    22
    33TerritoryDecay.prototype.Schema =
    4     "<element name='HealthDecayRate' a:help='Decay rate in hitpoints per second'>" +
     4    "<element name='DecayRate' a:help='Decay rate in hitpoints per second'>" +
    55        "<data type='positiveInteger'/>" +
    66    "</element>";
    77
     
    3333    var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y);
    3434    if (tileOwner != cmpOwnership.GetOwner())
    3535        return false;
    36     // TODO: this should probably use the same territory restriction
    37     // logic as BuildRestrictions, to handle allies etc
    3836
    3937    return cmpTerritoryManager.IsConnected(pos.x, pos.y);
    4038};
     
    6462    if (connected)
    6563        var decaying = false;
    6664    else
    67         var decaying = (Math.round(ApplyValueModificationsToEntity("TerritoryDecay/HealthDecayRate", +this.template.HealthDecayRate, this.entity)) > 0);
     65        var decaying = (Math.round(ApplyValueModificationsToEntity("TerritoryDecay/DecayRate", +this.template.DecayRate, this.entity)) > 0);
    6866    if (decaying === this.decaying)
    6967        return;
    7068    this.decaying = decaying;
     
    8886
    8987TerritoryDecay.prototype.Decay = function()
    9088{
    91     var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
    92     if (!cmpHealth)
     89    var cmpCapturable = Engine.QueryInterface(this.entity, IID_Capturable);
     90    if (!cmpCapturable)
    9391        return; // error
    9492
    95     var decayRate = ApplyValueModificationsToEntity("TerritoryDecay/HealthDecayRate", +this.template.HealthDecayRate, this.entity);
     93    var decayRate = ApplyValueModificationsToEntity(
     94        "TerritoryDecay/DecayRate",
     95        +this.template.DecayRate,
     96        this.entity);
    9697
    97     cmpHealth.Reduce(Math.round(decayRate));
     98    // Reduce capture points in favour of Gaia
     99    cmpCapturable.Reduce(decayRate, 0);
    98100};
    99101
    100102Engine.RegisterComponentType(IID_TerritoryDecay, "TerritoryDecay", TerritoryDecay);
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    55595559        return false;
    55605560
    55615561    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
    5562     if (!cmpOwnership)
     5562    if (!cmpOwnership || cmpOwnership.GetOwner() < 0)
    55635563        return false;
     5564    var owner = cmpOwnership.GetOwner();
    55645565
    55655566    // Verify that the target is an attackable resource supply like a domestic animal
    55665567    // or that it isn't owned by an ally of this entity's player or is responding to
    55675568    // an attack.
    5568     var owner = cmpOwnership.GetOwner();
    5569     if (!this.MustKillGatherTarget(target)
    5570         && !(IsOwnedByEnemyOfPlayer(owner, target)
    5571              || IsOwnedByNeutralOfPlayer(owner, target)
    5572              || (forceResponse && !IsOwnedByPlayer(owner, target))))
    5573         return false;
     5569    if (this.MustKillGatherTarget(target))
     5570        return true;
    55745571
    5575     return true;
     5572    var cmpCapturable = Engine.QueryInterface(target, IID_Capturable);
     5573    if (cmpCapturable && cmpCapturable.CanCapture(owner) && cmpAttack.GetAttackTypes().indexOf("Capture") != -1)
     5574        return true;
     5575
     5576    if (IsOwnedByEnemyOfPlayer(owner, target) || IsOwnedByNeutralOfPlayer(owner, target))
     5577        return true;
     5578    if (forceResponse && !IsOwnedByPlayer(owner, target))
     5579        return true;
     5580    return false;
    55765581};
    55775582
    55785583UnitAI.prototype.CanGarrison = function(target)
  • binaries/data/mods/public/simulation/components/interfaces/Capturable.js

     
     1Engine.RegisterInterface("Capturable");
     2
     3// Message in the form of {"capturePoints": [gaia, p1, p2, ...]}
     4Engine.RegisterMessageType("CapturePointsChanged");
     5
  • binaries/data/mods/public/simulation/data/technologies/decay_outpost.json

     
    77    "icon": "blocks_three.png",
    88    "researchTime": 40,
    99    "tooltip": "Territory decay -50% for Outposts.",
    10     "modifications": [{"value": "TerritoryDecay/HealthDecayRate", "multiply": 0.5}],
     10    "modifications": [{"value": "TerritoryDecay/DecayRate", "multiply": 0.5}],
    1111    "affects": ["Outpost"],
    1212    "soundComplete": "interface/alarm/alarm_upgradearmory.xml"
    1313}
  • binaries/data/mods/public/simulation/data/technologies/romans/decay_logistics.json

     
    77    "icon": "handcart_empty.png",
    88    "researchTime": 40,
    99    "tooltip": "Entrenched Camps and Siege Walls decay 50% slower.",
    10     "modifications": [{"value": "TerritoryDecay/HealthDecayRate", "multiply": 0.5}],
     10    "modifications": [{"value": "TerritoryDecay/DecayRate", "multiply": 0.5}],
    1111    "affects": ["ArmyCamp", "SiegeWall"],
    1212    "soundComplete": "interface/alarm/alarm_upgradearmory.xml"
    1313}
  • binaries/data/mods/public/simulation/helpers/Commands.js

     
    349349
    350350    "delete-entities": function(player, cmd, data)
    351351    {
    352         for each (var ent in data.entities)
     352        for (let ent of data.entities)
    353353        {
     354            // don't allow to delete entities who are half-captured
     355            var cmpCapturable = Engine.QueryInterface(ent, IID_Capturable);
     356            if (cmpCapturable)
     357            {
     358                var capturePoints = cmpCapturable.GetCapturePoints();
     359                var maxCapturePoints = cmpCapturable.GetMaxCapturePoints();
     360                if (capturePoints[player] < maxCapturePoints / 2)
     361                    return;
     362            }
     363            // either kill or delete the entity
    354364            var cmpHealth = Engine.QueryInterface(ent, IID_Health);
    355365            if (cmpHealth)
    356366            {
  • binaries/data/mods/public/simulation/templates/structures/merc_camp_egyptian.xml

     
    6060    </SoundGroups>
    6161  </Sound>
    6262  <TerritoryDecay>
    63     <HealthDecayRate>1</HealthDecayRate>
     63    <DecayRate>1</DecayRate>
    6464  </TerritoryDecay>
    6565  <TerritoryInfluence disable=""/>
    6666  <VisualActor>
  • binaries/data/mods/public/simulation/templates/structures/ptol_mercenary_camp.xml

     
    6262    </SoundGroups>
    6363  </Sound>
    6464  <TerritoryDecay>
    65     <HealthDecayRate>1</HealthDecayRate>
     65    <DecayRate>1</DecayRate>
    6666  </TerritoryDecay>
    6767  <TerritoryInfluence disable=""/>
    6868  <VisualActor>
  • binaries/data/mods/public/simulation/templates/structures/ptol_military_colony.xml

     
    7676    </SoundGroups>
    7777  </Sound>
    7878  <TerritoryDecay>
    79     <HealthDecayRate>1</HealthDecayRate>
     79    <DecayRate>1</DecayRate>
    8080  </TerritoryDecay>
    8181  <TerritoryInfluence>
    8282    <Radius>80</Radius>
  • binaries/data/mods/public/simulation/templates/structures/rome_army_camp.xml

     
    7676    </SoundGroups>
    7777  </Sound>
    7878  <TerritoryDecay>
    79     <HealthDecayRate>10</HealthDecayRate>
     79    <DecayRate>10</DecayRate>
    8080  </TerritoryDecay>
    8181  <TerritoryInfluence disable=""/>
    8282  <ProductionQueue>
  • binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_gate.xml

     
    4444    </Obstructions>
    4545  </Obstruction>
    4646  <TerritoryDecay>
    47     <HealthDecayRate>1</HealthDecayRate>
     47    <DecayRate>1</DecayRate>
    4848  </TerritoryDecay>
    4949  <TerritoryInfluence disable=""/>
    5050  <VisualActor>
  • binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml

     
    6262    <Static width="37.0" depth="5.0"/>
    6363  </Obstruction>
    6464  <TerritoryDecay>
    65     <HealthDecayRate>1</HealthDecayRate>
     65    <DecayRate>1</DecayRate>
    6666  </TerritoryDecay>
    6767  <TerritoryInfluence disable=""/>
    6868  <VisualActor>
  • binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_medium.xml

     
    5555    <Static width="25.0" depth="5.0"/>
    5656  </Obstruction>
    5757  <TerritoryDecay>
    58     <HealthDecayRate>1</HealthDecayRate>
     58    <DecayRate>1</DecayRate>
    5959  </TerritoryDecay>
    6060  <TerritoryInfluence disable=""/>
    6161  <VisualActor>
  • binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_short.xml

     
    4242    <Static width="13.0" depth="5.0"/>
    4343  </Obstruction>
    4444  <TerritoryDecay>
    45     <HealthDecayRate>1</HealthDecayRate>
     45    <DecayRate>1</DecayRate>
    4646  </TerritoryDecay>
    4747  <TerritoryInfluence disable=""/>
    4848  <VisualActor>
  • binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_tower.xml

     
    3636    <Static width="7.0" depth="7.0"/>
    3737  </Obstruction>
    3838  <TerritoryDecay>
    39     <HealthDecayRate>1</HealthDecayRate>
     39    <DecayRate>1</DecayRate>
    4040  </TerritoryDecay>
    4141  <TerritoryInfluence disable=""/>
    4242  <VisualActor>
  • binaries/data/mods/public/simulation/templates/structures/rome_tent.xml

     
    5050    </SoundGroups>
    5151  </Sound>
    5252  <TerritoryDecay>
    53     <HealthDecayRate>1</HealthDecayRate>
     53    <DecayRate>1</DecayRate>
    5454  </TerritoryDecay>
    5555  <TerritoryInfluence disable=""/>
    5656  <VisualActor>
  • binaries/data/mods/public/simulation/templates/structures/sele_military_colony.xml

     
    7575    </SoundGroups>
    7676  </Sound>
    7777  <TerritoryDecay>
    78     <HealthDecayRate>1</HealthDecayRate>
     78    <DecayRate>1</DecayRate>
    7979  </TerritoryDecay>
    8080  <TerritoryInfluence>
    8181    <Radius>80</Radius>
  • binaries/data/mods/public/simulation/templates/template_structure.xml

     
    2020    <PlacementType>land</PlacementType>
    2121    <Territory>own</Territory>
    2222  </BuildRestrictions>
     23  <Capturable>
     24    <CapturePoints>1000</CapturePoints>
     25    <RegenRate>0</RegenRate>
     26    <GarrisonRegenRate>3</GarrisonRegenRate>
     27  </Capturable>
    2328  <Cost>
    2429    <Population>0</Population>
    2530    <PopulationBonus>0</PopulationBonus>
     
    100105    <HeightOffset>12.0</HeightOffset>
    101106  </StatusBars>
    102107  <TerritoryDecay>
    103     <HealthDecayRate>5</HealthDecayRate>
     108    <DecayRate>5</DecayRate>
    104109  </TerritoryDecay>
    105110  <Visibility>
    106111    <RetainInFog>true</RetainInFog>
  • binaries/data/mods/public/simulation/templates/template_structure_civic_civil_centre.xml

     
    3535      <MinDistance>200</MinDistance>
    3636    </Distance>
    3737  </BuildRestrictions>
     38  <Capturable>
     39    <CapturePoints>3000</CapturePoints>
     40  </Capturable>
    3841  <Cost>
    3942    <PopulationBonus>20</PopulationBonus>
    4043    <BuildTime>500</BuildTime>
  • binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml

     
    33  <BuildRestrictions>
    44    <Category>House</Category>
    55  </BuildRestrictions>
     6  <Capturable>
     7    <CapturePoints>300</CapturePoints>
     8  </Capturable>
    69  <Cost>
    710    <PopulationBonus>5</PopulationBonus>
    811    <BuildTime>30</BuildTime>
  • binaries/data/mods/public/simulation/templates/template_structure_defense_outpost.xml

     
    9090    <HeightOffset>18.0</HeightOffset>
    9191  </StatusBars>
    9292  <TerritoryDecay>
    93     <HealthDecayRate>2</HealthDecayRate>
     93    <DecayRate>2</DecayRate>
    9494  </TerritoryDecay>
    9595  <Vision>
    9696    <Range>80</Range>
  • binaries/data/mods/public/simulation/templates/template_structure_defense_wall.xml

     
    44    <PlacementType>land-shore</PlacementType>
    55    <Category>Wall</Category>
    66  </BuildRestrictions>
     7  <Capturable disable=""/>
    78  <Cost>
    89    <BuildTime>10</BuildTime>
    910    <Resources>
  • binaries/data/mods/public/simulation/templates/template_structure_defense_wall_gate.xml

     
    66  <BuildRestrictions>
    77    <Category>Wall</Category>
    88  </BuildRestrictions>
     9  <Capturable disable=""/>
    910  <Cost>
    1011    <BuildTime>0</BuildTime>
    1112    <Resources>
  • binaries/data/mods/public/simulation/templates/template_structure_defense_wall_tower.xml

     
    2323    <PlacementType>land-shore</PlacementType>
    2424    <Category>Wall</Category>
    2525  </BuildRestrictions>
     26  <Capturable disable=""/>
    2627  <Cost>
    2728    <BuildTime>120</BuildTime>
    2829    <Resources>
  • binaries/data/mods/public/simulation/templates/template_structure_economic_farmstead.xml

     
    33  <BuildRestrictions>
    44    <Category>Farmstead</Category>
    55  </BuildRestrictions>
     6  <Capturable>
     7    <CapturePoints>300</CapturePoints>
     8  </Capturable>
    69  <Cost>
    710    <BuildTime>45</BuildTime>
    811    <Resources>
  • binaries/data/mods/public/simulation/templates/template_structure_economic_storehouse.xml

     
    33  <BuildRestrictions>
    44    <Category>Storehouse</Category>
    55  </BuildRestrictions>
     6  <Capturable>
     7    <CapturePoints>300</CapturePoints>
     8  </Capturable>
    69  <Cost>
    710    <BuildTime>40</BuildTime>
    811    <Resources>
  • binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml

     
    2626      <MinDistance>80</MinDistance>
    2727    </Distance>
    2828  </BuildRestrictions>
     29  <Capturable>
     30    <CapturePoints>4000</CapturePoints>
     31  </Capturable>
    2932  <Cost>
    3033    <PopulationBonus>10</PopulationBonus>
    3134    <BuildTime>300</BuildTime>
  • binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml

     
    88  <BuildRestrictions>
    99    <Category>Field</Category>
    1010  </BuildRestrictions>
     11  <Capturable disable=""/>
    1112  <Cost>
    1213    <BuildTime>50</BuildTime>
    1314    <Resources>
  • binaries/data/mods/public/simulation/templates/template_structure_wonder.xml

     
    1313  <BuildRestrictions>
    1414    <Category>Wonder</Category>
    1515  </BuildRestrictions>
     16  <Capturable>
     17    <CapturePoints>5000</CapturePoints>
     18  </Capturable>
    1619  <Cost>
    1720    <BuildTime>1000</BuildTime>
    1821    <Resources>
  • binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml

     
    66    <Crush>15</Crush>
    77  </Armour>
    88  <Attack>
     9    <Capture>
     10      <Value>3</Value>
     11      <MaxRange>4</MaxRange>
     12      <RepeatTime>1000</RepeatTime>
     13    </Capture>
    914    <Slaughter>
    1015      <Hack>100.0</Hack>
    1116      <Pierce>0.0</Pierce>
  • binaries/data/mods/public/simulation/templates/template_unit_champion.xml

     
    11<?xml version="1.0" encoding="utf-8"?>
    22<Entity parent="template_unit">
     3  <Attack>
     4    <Capture>
     5      <Value>3</Value>
     6      <MaxRange>4</MaxRange>
     7      <RepeatTime>1000</RepeatTime>
     8    </Capture>
     9  </Attack>
    310  <Identity>
    411    <GenericName>Champion Unit</GenericName>
    512    <Classes datatype="tokens">Organic Human</Classes>
  • binaries/data/mods/public/simulation/templates/template_unit_champion_elephant_melee.xml

     
    11<?xml version="1.0" encoding="utf-8"?>
    22<Entity parent="template_unit_champion_elephant">
    3   <Attack>
     3  <Attack replace="">
    44    <Melee>
    55      <Hack>20</Hack>
    66      <Pierce>0</Pierce>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_elephant_melee.xml

     
    55    <Pierce>10</Pierce>
    66    <Crush>12</Crush>
    77  </Armour>
    8   <Attack>
     8  <Attack replace="">
    99    <Melee>
    1010      <Hack>17.5</Hack>
    1111      <Pierce>0</Pierce>
  • binaries/data/mods/public/simulation/templates/template_unit_infantry.xml

     
    66    <Crush>15</Crush>
    77  </Armour>
    88  <Attack>
     9    <Capture>
     10      <Value>3</Value>
     11      <MaxRange>4</MaxRange>
     12      <RepeatTime>1000</RepeatTime>
     13    </Capture>
    914    <Slaughter>
    1015      <Hack>50.0</Hack>
    1116      <Pierce>0.0</Pierce>
  • binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege.xml

     
    55    <Pierce>5</Pierce>
    66    <Crush>5</Crush>
    77  </Armour>
     8  <Capturable>
     9    <CapturePoints>100</CapturePoints>
     10    <RegenRate>0</RegenRate>
     11    <GarrisonRegenRate>1.5</GarrisonRegenRate>
     12  </Capturable>
    813  <Cost>
    914    <Population>5</Population>
    1015  </Cost>