Ticket #13: diffFormation-01-05-2011.patch

File diffFormation-01-05-2011.patch, 27.8 KB (added by Badmadblacksad, 13 years ago)
  • binaries/data/mods/public/gui/session/utility_functions.js

     
    223223{
    224224    switch (formationName)
    225225    {
    226     case "Formation0":
     226    case "Loose":
    227227        return 0;
    228     case "Formation1":
     228    case "Box":
    229229        return 1;
    230     case "Formation2":
     230    case "Column Closed":
    231231        return 2;
    232     case "Formation3":
     232    case "Line Closed":
    233233        return 3;
    234     case "Formation4":
     234    case "Column Open":
    235235        return 4;
    236     case "Formation5":
     236    case "Line Open":
    237237        return 5;
    238     case "Formation6":
     238    case "Flank":
    239239        return 6;
    240     case "Formation7":
     240    case "Skirmish":
    241241        return 7;
    242     case "Formation8":
     242    case "Wedge":
    243243        return 8;
    244     case "Formation9":
     244    case "Testudo":
    245245        return 9;
    246     case "Formation10":
    247         return 10; 
    248     case "Formation11":
     246    case "Phalanx":
     247        return 10;
     248    case "Syntagma":
    249249        return 11;
    250250    case "Formation12":
    251251        return 12;
     
    273273
    274274function getEntityFormationsList(entState)
    275275{
    276     var formations = [];
    277 
    278     formations.push("Formation0");
    279     formations.push("Formation1");
    280     formations.push("Formation2");
    281     formations.push("Formation3");
    282     formations.push("Formation4");
    283     formations.push("Formation5");
    284     formations.push("Formation6");
    285     formations.push("Formation7");
    286     formations.push("Formation8");
    287     formations.push("Formation9");
    288     formations.push("Formation10");
    289     formations.push("Formation11");
    290     formations.push("Formation12");
     276    var civ = g_Players[entState.player].civ;
     277    var formations = getCivFormations(civ);
    291278    return formations;
    292279}
    293280
     281function getCivFormations(civ)
     282{
     283    var civFormations = ["Loose","Box","Column Closed","Line Closed","Column Open","Line Open","Flank","Skirmish","Wedge","Formation12"];
     284    if (civ == "hele")
     285    {
     286        civFormations.push("Phalanx");
     287        civFormations.push("Syntagma");
     288    }
     289    else if (civ == "rome")
     290    {
     291        civFormations.push("Testudo");
     292    }
     293    return civFormations;
     294}
     295
    294296function getEntityCommandsList(entState)
    295297{
    296298    var commands = [];
  • binaries/data/mods/public/gui/session/input.js

     
    10901090// Performs the specified formation
    10911091function performFormation(entity, formationName)
    10921092{
    1093     submitChatDirectly("FORMATIONS are not implemented yet.");
    1094 
    10951093    if (entity)
    10961094    {
    1097         console.write(formationName);
     1095        var selection = g_Selection.toList();
     1096        Engine.PostNetworkCommand({
     1097            "type": "formation",
     1098            "entities": selection,
     1099            "name": formationName
     1100        });
    10981101    }
    10991102}
    11001103
  • binaries/data/mods/public/gui/session/unit_commands.js

     
    248248        if (guiName == "Formation")
    249249        {
    250250            icon.cell_id = getFormationCellId(item);
    251             icon.enabled = false;
     251            //var formationOk = CanMoveEntsIntoFormation(g_Selection.toList(),item);
     252            var formationOk = Engine.GuiInterfaceCall("CanMoveEntsIntoFormation", {
     253                "ents": g_Selection.toList(),
     254                "formationName": item
     255            });
     256
     257            icon.enabled = formationOk;
     258            button.enabled = formationOk;
    252259            if (!icon.enabled)
     260            {
    253261                icon.sprite = "formation_disabled";
     262                button.tooltip += " (disabled)";
     263            }
     264            else
     265            {
     266                icon.sprite = "formation";
     267            }
    254268        }
    255269        else if (guiName == "Command")
    256270        {
     
    355369            setupUnitPanel("Formation", usedPanels, entState, formations,
    356370                function (item) { performFormation(entState.id, item); } );
    357371
     372
    358373        if (entState.buildEntities && entState.buildEntities.length)
    359374        {
    360375            setupUnitPanel("Construction", usedPanels, entState, entState.buildEntities, startBuildingPlacement);
     
    399414{
    400415    for each (var panelName in g_unitPanels)
    401416        getGUIObjectByName("unit" + panelName + "Panel").hidden = true;
    402 }
    403  No newline at end of file
     417}
  • binaries/data/mods/public/simulation/helpers/Commands.js

     
    236236       
    237237        var cmpGarrisonHolder = Engine.QueryInterface(cmd.garrisonHolder, IID_GarrisonHolder);
    238238        cmpGarrisonHolder.UnloadAll();
     239        break;     
     240
     241    case "formation":
     242        var cmpUnitAI = GetFormationUnitAI(cmd.entities);
     243        if(!cmpUnitAI)
     244            break;
     245        var cmpFormation = Engine.QueryInterface(cmpUnitAI.entity, IID_Formation);
     246        if (!cmpFormation)
     247            break;
     248        cmpFormation.LoadFormation(cmd.name);
     249        cmpFormation.MoveMembersIntoFormation(true);
    239250        break;
    240251       
    241252    default:
     
    339350        formationEnt = Engine.AddEntity("special/formation");
    340351        var cmpFormation = Engine.QueryInterface(formationEnt, IID_Formation);
    341352        cmpFormation.SetMembers(formation.entities);
     353
     354        // We retrieve the last common formation name
     355        var lastFormationName = undefined;
     356        for each (var ent in formation.entities)
     357        {
     358            var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
     359            if (cmpUnitAI)
     360            {
     361                var name = cmpUnitAI.GetLastFormationName();
     362                if (lastFormationName == undefined)
     363                {
     364                    lastFormationName = name;
     365                }
     366                else if (lastFormationName != name)
     367                {
     368                    lastFormationName = undefined;
     369                    break;
     370                }
     371            }
     372        }
     373        var formationName;
     374        if (lastFormationName)
     375            formationName = lastFormationName;
     376        else
     377            formationName = "Line Closed";
     378
     379        if (CanMoveEntsIntoFormation(formation.entities,formationName))
     380        {
     381            cmpFormation.LoadFormation(formationName);
     382        }
     383        else
     384        {
     385            cmpFormation.LoadFormation("Loose");
     386        }
    342387    }
    343388
    344389    return Engine.QueryInterface(formationEnt, IID_UnitAI);
    345390}
    346391
     392function CanMoveEntsIntoFormation(ents,formationName)
     393{
     394    var count = ents.length;
     395    var classesRequired;
     396
     397    if (formationName == "Loose")
     398    {
     399        return true;
     400    }
     401    else if (formationName == "Box")
     402    {
     403        if (count < 4)
     404            return false;
     405    }
     406    else if (formationName == "Column Closed")
     407    {
     408    }
     409    else if (formationName == "Line Closed")
     410    {
     411    }
     412    else if (formationName == "Column Open")
     413    {
     414    }
     415    else if (formationName == "Line Open")
     416    {
     417    }
     418    else if (formationName == "Flank")
     419    {
     420        if (count < 8)
     421            return false;
     422    }
     423    else if (formationName == "Skirmish")
     424    {
     425        classesRequired = ["Ranged"];
     426    }
     427    else if (formationName == "Wedge")
     428    {
     429        if (count < 3)
     430            return false;
     431        classesRequired = ["Cavalry"];
     432    }
     433    else if (formationName == "Formation12")
     434    {
     435    }
     436    else if (formationName == "Phalanx")
     437    {
     438        if (count < 10)
     439            return false;
     440        classesRequired = ["Melee","Infantry"];
     441    }
     442    else if (formationName == "Syntagma")
     443    {
     444        if (count < 9)
     445            return false;
     446        classesRequired = ["Melee","Infantry"]; //todo: pike only
     447    }
     448    else if (formationName == "Testudo")
     449    {
     450        if (count < 9)
     451            return false;
     452        classesRequired = ["Melee","Infantry"];
     453    }
     454    else
     455    {
     456        return false;
     457    }
     458
     459    var looseOnlyUnits = true;
     460    for each (var ent in ents)
     461    {
     462        var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
     463        if (cmpIdentity)
     464        {
     465            //var classes = GetEntityState(ent).identity.classes;
     466            var classes = cmpIdentity.GetClassesList();
     467            if (looseOnlyUnits && (classes.indexOf("Worker") == -1 || classes.indexOf("Support") == -1))
     468                looseOnlyUnits = false;
     469            for each (var classRequired in classesRequired)
     470            {
     471                if (classes.indexOf(classRequired) == -1)
     472                {
     473                    return false;
     474                }
     475            }
     476        }
     477    }
     478
     479    if (looseOnlyUnits)
     480        return false;
     481
     482    return true;
     483}
     484
     485Engine.RegisterGlobal("CanMoveEntsIntoFormation", CanMoveEntsIntoFormation);
    347486Engine.RegisterGlobal("ProcessCommand", ProcessCommand);
  • binaries/data/mods/public/simulation/components/GuiInterface.js

     
    264264        return "";
    265265};
    266266
     267GuiInterface.prototype.CanMoveEntsIntoFormation = function(player, data)
     268{
     269    return CanMoveEntsIntoFormation(data.ents, data.formationName);
     270}
     271
    267272GuiInterface.prototype.SetSelectionHighlight = function(player, cmd)
    268273{
    269274    var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     
    510515    "GetTemplateData": 1,
    511516    "GetNextNotification": 1,
    512517
     518    "CanMoveEntsIntoFormation":1,
     519
    513520    "SetSelectionHighlight": 1,
    514521    "SetStatusBars": 1,
    515522    "DisplayRallyPoint": 1,
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    11611161    this.order = undefined; // always == this.orderQueue[0]
    11621162    this.formationController = INVALID_ENTITY; // entity with IID_Formation that we belong to
    11631163    this.isIdle = false;
     1164    this.lastFormationName = "Line Closed";
    11641165
    11651166    this.SetStance(this.template.DefaultStance);
    11661167};
     
    17401741    return this.formationController;
    17411742};
    17421743
     1744UnitAI.prototype.SetLastFormationName = function(name)
     1745{
     1746    this.lastFormationName = name;
     1747};
     1748
     1749UnitAI.prototype.GetLastFormationName = function()
     1750{
     1751  return this.lastFormationName;
     1752};
     1753
    17431754/**
    17441755 * Returns the estimated distance that this unit will travel before either
    17451756 * finishing all of its orders, or reaching a non-walk target (attack, gather, etc).
  • binaries/data/mods/public/simulation/components/Identity.js

     
    6868                        "<value>Infantry</value>" +
    6969                        "<value>Cavalry</value>" +
    7070                        "<value>Ranged</value>" +
     71                        "<value>Melee</value>" +
    7172                        "<value>Mechanical</value>" +
    7273                        "<value>Ship</value>" +
    7374                        "<value>Siege</value>" +
  • binaries/data/mods/public/simulation/components/Formation.js

     
    33Formation.prototype.Schema =
    44    "<a:component type='system'/><empty/>";
    55
    6 var g_ColumnDistanceThreshold = 48; // distance at which we'll switch between column/box formations
     6var g_ColumnDistanceThreshold = 96; // distance at which we'll switch between column/box formations
    77
    88Formation.prototype.Init = function()
    99{
    1010    this.members = []; // entity IDs currently belonging to this formation
    1111    this.columnar = false; // whether we're travelling in column (vs box) formation
     12    this.formationName = "Line Closed";
    1213};
    1314
    1415Formation.prototype.GetMemberCount = function()
     
    114115    var active = [];
    115116    var positions = [];
    116117
    117     var types = { "Unknown": 0 }; // TODO: make this work so we put ranged behind infantry etc
    118 
    119118    for each (var ent in this.members)
    120119    {
    121120        var cmpPosition = Engine.QueryInterface(ent, IID_Position);
     
    124123
    125124        active.push(ent);
    126125        positions.push(cmpPosition.GetPosition());
    127 
    128         types["Unknown"] += 1; // TODO
    129126    }
    130127
    131128    // Work out whether this should be a column or box formation
    132129    var cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
    133130    var walkingDistance = cmpUnitAI.ComputeWalkingDistance();
    134     this.columnar = (walkingDistance > g_ColumnDistanceThreshold);
     131    this.columnar = (walkingDistance > g_ColumnDistanceThreshold) && this.formationName != "Column Open";
    135132
    136     var offsets = this.ComputeFormationOffsets(types, this.columnar);
     133    var offsets = this.ComputeFormationOffsets(active, this.columnar);
    137134
    138135    var avgpos = this.ComputeAveragePosition(positions);
    139136    var avgoffset = this.ComputeAveragePosition(offsets);
     
    143140    if (moveCenter || !cmpPosition.IsInWorld())
    144141        cmpPosition.JumpTo(avgpos.x, avgpos.z);
    145142
    146     // TODO: assign to minimise worst-case distances or whatever
    147 
    148     for (var i = 0; i < active.length; ++i)
     143    for (var i = 0; i < offsets.length; ++i)
    149144    {
    150145        var offset = offsets[i];
    151146
    152         var cmpUnitAI = Engine.QueryInterface(active[i], IID_UnitAI);
     147        var cmpUnitAI = Engine.QueryInterface(offset.ent, IID_UnitAI);
    153148        cmpUnitAI.ReplaceOrder("FormationWalk", {
    154149            "target": this.entity,
    155150            "x": offset.x - avgoffset.x,
     
    177172    cmpPosition.JumpTo(avgpos.x, avgpos.z);
    178173};
    179174
    180 Formation.prototype.ComputeFormationOffsets = function(types, columnar)
     175Formation.prototype.ComputeFormationOffsets = function(active, columnar)
    181176{
    182177    var separation = 4; // TODO: don't hardcode this
    183178
    184     var count = types["Unknown"];
     179    var types = {   "Cavalry" : [],
     180                                    "Hero" : [],
     181                                    "Melee" : [],
     182                                    "Ranged" : [],
     183                                    "Support" : [],
     184                                    "Unknown": []
     185                                    }; // TODO: make this work so we put ranged behind infantry etc
    185186
     187    for each (var ent in active)
     188    {
     189        var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
     190        var classes = cmpIdentity.GetClassesList();
     191        var done = false;
     192        for each (var cla in classes)
     193        {
     194            if(cla in types)
     195            {
     196                types[cla].push(ent);
     197                done = true;
     198                break;
     199            }
     200        }
     201        if (!done)
     202        {
     203            types["Unknown"].push(ent);
     204        }
     205    }
     206
     207    var count = active.length;
     208
     209    var shape = undefined;
     210    var ordering = "default";
     211
     212    var offsets = [];
     213
    186214    // Choose a sensible width for the basic default formation
    187215    var cols;
    188     if (columnar)
     216    if (columnar || this.formationName == "Column Closed")
    189217    {
    190218        // Have at most 3 files
    191219        if (count <= 3)
    192220            cols = count;
    193221        else
    194222            cols = 3;
     223        shape = "square";
    195224    }
    196     else
     225    else if (this.formationName == "Phalanx")
    197226    {
    198227        // Try to have at least 5 files (so batch training gives a single line),
    199228        // and at most 8
    200229        if (count <= 5)
    201230            cols = count;
    202         if (count <= 10)
     231        else if (count <= 10)
    203232            cols = 5;
    204233        else if (count <= 16)
    205234            cols = Math.ceil(count / 2);
     235        else if(count <= 48)
     236            cols = 8;
    206237        else
     238            cols = Math.ceil(count/6);
     239        shape = "square";
     240    }
     241    else if (this.formationName == "Line Closed")
     242    {
     243        if (count <= 3)
     244            cols = count;
     245        else if(count < 30)
     246            cols = Math.max(Math.ceil(count/2),3);
     247        else
     248            cols = Math.ceil(count/3);
     249        shape = "square";
     250    }
     251    else if (this.formationName == "Testudo")
     252    {
     253        cols = Math.ceil(Math.sqrt(count));
     254        shape = "square";
     255    }
     256    else if (this.formationName == "Column Open")
     257    {
     258        cols = 2
     259        shape = "opensquare";
     260    }
     261    else if (this.formationName == "Line Open")
     262    {
     263        if (count <= 5)
     264            cols = 3;
     265        else if (count <= 11)
     266            cols = 4;
     267        else if (count <= 18)
     268            cols = 5;
     269        else
     270            cols = 6;
     271        shape = "opensquare";
     272    }
     273    else if (this.formationName == "Loose")
     274    {
     275        var width = Math.sqrt(count) * separation * 5;
     276
     277        for (var i = 0; i < count; ++i)
     278        {
     279            offsets.push({"x": Math.random()*width, "z": Math.random()*width});
     280        }
     281    }
     282    else if (this.formationName == "Circle")
     283    {
     284        var depth;
     285        var pop;
     286        if (count <= 36)
     287        {
     288            pop = 12;
     289            depth = Math.ceil(count / pop);
     290        }
     291        else
     292        {
     293            depth = 3
     294            pop = Math.ceil(count / depth);
     295        }
     296
     297        var left = count;
     298        var radius = Math.min(left,pop) * separation / (2 * Math.PI);
     299        for (var c = 0; c < depth; ++c)
     300        {
     301            var ctodo = Math.min(left,pop);
     302            var cradius = radius - c * separation / 2;
     303            var delta = 2 * Math.PI / ctodo;
     304            for (var alpha = 0; ctodo; alpha+=delta)
     305            {
     306                var x = Math.cos(alpha) * cradius;
     307                var z = Math.sin(alpha) * cradius;
     308                offsets.push({"x": x, "z": z});
     309                ctodo--;
     310                left--;
     311            }
     312        }
     313    }
     314    else if (this.formationName == "Box") // Box
     315    {
     316        var root = Math.ceil(Math.sqrt(count));
     317
     318        var left = count;
     319        var meleeleft = types["Melee"].length;
     320        for (var sq = Math.floor(root/2); sq >= 0; --sq)
     321        {
     322            var width = sq * 2 + 1;
     323            var stodo;
     324            if (sq == 0)
     325            {
     326                stodo = left;
     327            }
     328            else
     329            {
     330                if (meleeleft >= width*width - (width-2)*(width-2)) // form a complete box
     331                {
     332                    stodo = width*width - (width-2)*(width-2);
     333                    meleeleft -= stodo;
     334                }
     335                else    // compact
     336                    stodo = Math.max(0,left - (width-2)*(width-2));
     337            }
     338
     339            for (var r = -sq; r <= sq && stodo; ++r)
     340            {
     341                for (var c = -sq; c <= sq && stodo; ++c)
     342                {
     343                    if (Math.abs(r) == sq || Math.abs(c) == sq)
     344                    {
     345                        var x = c * separation;
     346                        var z = -r * separation;
     347                        offsets.push({"x": x, "z": z});
     348                        stodo--;
     349                        left--;
     350                    }
     351                }
     352            }
     353        }
     354    }
     355    else if (this.formationName == "Skirmish")
     356    {
     357        cols = Math.ceil(count/2);
     358        shape = "opensquare";
     359    }
     360    else if (this.formationName == "Wedge")
     361    {
     362        var depth = Math.ceil(Math.sqrt(count));
     363
     364        var left = count;
     365        var width = 2 * depth - 1;
     366        for (var p = 0; p < depth && left; ++p)
     367        {
     368            for (var r = p; r < depth && left; ++r)
     369            {
     370                var c1 = depth - r + p;
     371                var c2 = depth + r - p;
     372
     373                if (left)
     374                {
     375                        var x = c1 * separation;
     376                        var z = -r * separation;
     377                        offsets.push({"x": x, "z": z});
     378                        left--;
     379                }
     380                if (left && c1 != c2)
     381                {
     382                        var x = c2 * separation;
     383                        var z = -r * separation;
     384                        offsets.push({"x": x, "z": z});
     385                        left--;
     386                }
     387            }
     388        }
     389    }
     390    else if (this.formationName == "Flank")
     391    {
     392        cols = 3;
     393        var leftside = [];
     394        leftside[0] = Math.ceil(count/2);
     395        leftside[1] = Math.floor(count/2);
     396        ranks = Math.ceil(leftside[0] / cols);
     397        var off = - separation * 4;
     398        for (var side = 0; side < 2; ++side)
     399        {
     400            var left = leftside[side];
     401            off += side * separation * 8;
     402            for (var r = 0; r < ranks; ++r)
     403            {
     404                var n = Math.min(left, cols);
     405                for (var c = 0; c < n; ++c)
     406                {
     407                    var x = off + ((n-1)/2 - c) * separation;
     408                    var z = -r * separation;
     409                    offsets.push({"x": x, "z": z});
     410                }
     411                left -= n;
     412            }
     413        }
     414    }
     415    else if (this.formationName == "Syntagma")
     416    {
     417        var cols = Math.ceil(Math.sqrt(count));
     418        shape = "square";
     419    }
     420    else if (this.formationName == "Formation12")
     421    {
     422        if (count <= 5)
     423            cols = count;
     424        else if (count <= 10)
     425            cols = 5;
     426        else if (count <= 16)
     427            cols = Math.ceil(count / 2);
     428        else if(count <= 48)
    207429            cols = 8;
     430        else
     431            cols = Math.ceil(count/6);
     432        shape = "opensquare";
     433        separation /= 1.5;
     434        ordering = "cavalryOnTheSides";
    208435    }
    209436
    210     var ranks = Math.ceil(count / cols);
     437    if (shape == "square")
     438    {
     439        var ranks = Math.ceil(count / cols);
    211440
    212     var offsets = [];
     441        var left = count;
     442        for (var r = 0; r < ranks; ++r)
     443        {
     444            var n = Math.min(left, cols);
     445            for (var c = 0; c < n; ++c)
     446            {
     447                var x = ((n-1)/2 - c) * separation;
     448                var z = -r * separation;
     449                offsets.push({"x": x, "z": z});
     450            }
     451            left -= n;
     452        }
     453    }
     454    else if (shape == "opensquare")
     455    {
     456        var left = count;
     457        for (var r = 0; left; ++r)
     458        {
     459            var n = Math.min(left, cols - (r&1?1:0));
     460            for (var c = 0; c < 2*n; c+=2)
     461            {
     462                var x = (- c - (r&1)) * separation;
     463                var z = -r * separation;
     464                offsets.push({"x": x, "z": z});
     465            }
     466            left -= n;
     467        }
     468    }
    213469
    214     var left = count;
    215     for (var r = 0; r < ranks; ++r)
     470    // TODO: assign to minimise worst-case distances or whatever
     471    if (ordering == "default")
    216472    {
    217         var n = Math.min(left, cols);
    218         for (var c = 0; c < n; ++c)
     473        for each (var offset in offsets)
    219474        {
    220             var x = ((n-1)/2 - c) * separation;
    221             var z = -r * separation;
    222             offsets.push({"x": x, "z": z});
     475            var ent = undefined;
     476            for (var t in types)
     477            {
     478                if (types[t].length)
     479                {
     480                    ent = types[t].pop();
     481                    offset.ent = ent;
     482                    break;
     483                }
     484            }
    223485        }
    224         left -= n;
    225486    }
     487    else if (ordering == "cavalryOnTheSides")
     488    {
     489        var noffset = [];
     490        var cavalrycount = types["Cavalry"].length;
     491        offsets.sort(function (a,b) {
     492            if (a.x < b.x) return -1;
     493            else if (a.x == b.x && a.z < b.z) return -1;
     494            return 1;
     495        });
    226496
     497        while (offsets.length && types["Cavalry"].length && types["Cavalry"].length > cavalrycount/2)
     498        {
     499            var offset = offsets.pop();
     500            offset.ent = types["Cavalry"].pop();
     501            noffset.push(offset);
     502        }
     503
     504        offsets.sort(function (a,b) {
     505            if (a.x > b.x) return -1;
     506            else if (a.x == b.x && a.z < b.z) return -1;
     507            return 1;
     508        });
     509
     510        while (offsets.length && types["Cavalry"].length)
     511        {
     512            var offset = offsets.pop();
     513            offset.ent = types["Cavalry"].pop();
     514            noffset.push(offset);
     515        }
     516
     517        offsets.sort(function (a,b) {
     518            if (a.z < b.z) return -1;
     519            else if (a.z == b.z && a.x < b.x) return -1;
     520            return 1;
     521        });
     522
     523        while (offsets.length)
     524        {
     525            var offset = offsets.pop();
     526            for (var t in types)
     527            {
     528                if (types[t].length)
     529                {
     530                    offset.ent = types[t].pop();
     531                    break;
     532                }
     533            }
     534            noffset.push(offset);
     535        }
     536        offsets = noffset;
     537    }
     538
    227539    return offsets;
    228540};
    229541
     
    286598        this.RemoveMembers([msg.entity]);
    287599};
    288600
     601Formation.prototype.LoadFormation = function(formationName)
     602{
     603    this.formationName = formationName;
     604    for each(var ent in this.members)
     605    {
     606        var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
     607        cmpUnitAI.SetLastFormationName(this.formationName);
     608    }
     609};
     610
    289611Engine.RegisterComponentType(IID_Formation, "Formation", Formation);
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_swordsman.xml

     
    22<Entity parent="template_unit_hero_infantry">
    33  <Identity>
    44    <GenericName>Hero</GenericName>
    5     <Classes datatype="tokens">Hero Infantry</Classes>
     5    <Classes datatype="tokens">Hero Infantry Melee</Classes>
    66  </Identity>
    77  <Health>
    88    <Max>400</Max>
  • binaries/data/mods/public/simulation/templates/template_unit_infantry_ranged.xml

     
    22<Entity parent="template_unit_infantry">
    33  <Identity>
    44    <GenericName>Ranged</GenericName>
     5    <Classes datatype="tokens">Ranged</Classes>
    56  </Identity>
    67  <Health>
    78    <Max>90</Max>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_pikeman.xml

     
    22<Entity parent="template_unit_hero_infantry">
    33  <Identity>
    44    <GenericName>Hero</GenericName>
    5     <Classes datatype="tokens">Hero Infantry</Classes>
     5    <Classes datatype="tokens">Hero Infantry Melee</Classes>
    66  </Identity>
    77  <Cost>
    88    <Resources>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_spearman.xml

     
    22<Entity parent="template_unit_hero_infantry">
    33  <Identity>
    44    <GenericName>Hero</GenericName>
    5     <Classes datatype="tokens">Hero Infantry</Classes>
     5    <Classes datatype="tokens">Hero Infantry Melee</Classes>
    66  </Identity>
    77  <Minimap>
    88    <Type>hero</Type>
  • binaries/data/mods/public/simulation/templates/template_unit_super_infantry_spearman.xml

     
    22<Entity parent="template_unit_super_infantry">
    33  <Identity>
    44    <GenericName>Super Infantry Spearman</GenericName>
     5    <Classes datatype="tokens">Melee</Classes>
    56  </Identity>
    67  <Cost>
    78    <Resources>
  • binaries/data/mods/public/simulation/templates/template_unit_super_infantry_swordsman.xml

     
    22<Entity parent="template_unit_super_infantry">
    33  <Identity>
    44    <GenericName>Super Infantry Swordsman</GenericName>
     5    <Classes datatype="tokens">Melee</Classes>
    56  </Identity>
    67  <Cost>
    78    <Resources>
  • binaries/data/mods/public/simulation/templates/template_unit_infantry_melee.xml

     
    22<Entity parent="template_unit_infantry">
    33  <Identity>
    44    <GenericName>Melee Infantry</GenericName>
     5    <Classes datatype="tokens">Melee</Classes>
    56  </Identity>
    67  <Health>
    78    <Max>100</Max>
  • binaries/data/mods/public/simulation/templates/template_unit_super_cavalry_javelinist.xml

     
    22<Entity parent="template_unit_super_cavalry">
    33  <Identity>
    44    <GenericName>Super Cavalry Javelinist</GenericName>
    5     <Classes datatype="tokens">Cavalry Ranged</Classes>
     5    <Classes datatype="tokens">Ranged</Classes>
    66  </Identity>
    77  <Cost>
    88    <Resources>
  • binaries/data/mods/public/simulation/templates/template_unit_super_cavalry_swordsman.xml

     
    22<Entity parent="template_unit_super_cavalry">
    33  <Identity>
    44    <GenericName>Super Cavalry Spearman</GenericName>
     5    <Classes datatype="tokens">Melee</Classes>
    56  </Identity>
    67  <Cost>
    78    <BuildTime>16</BuildTime>
  • binaries/data/mods/public/simulation/templates/template_unit_super_cavalry_spearman.xml

     
    22<Entity parent="template_unit_super_cavalry">
    33  <Identity>
    44    <GenericName>Super Cavalry Spearman</GenericName>
     5    <Classes datatype="tokens">Melee</Classes>
    56  </Identity>
    67  <Cost>
    78    <BuildTime>16</BuildTime>
  • binaries/data/mods/public/simulation/templates/template_unit_super_infantry_pikeman.xml

     
    22<Entity parent="template_unit_super_infantry">
    33  <Identity>
    44    <GenericName>Super Infantry Pikeman</GenericName>
     5    <Classes datatype="tokens">Melee</Classes>
    56  </Identity>
    67  <Cost>
    78    <Resources>