Ticket #13: diffFormation-13-03-2011.patch

File diffFormation-13-03-2011.patch, 11.4 KB (added by Badmadblacksad, 13 years ago)
  • binaries/data/mods/public/gui/session/input.js

     
    10621062// Performs the specified formation
    10631063function performFormation(entity, formationName)
    10641064{
    1065     submitChatDirectly("FORMATIONS are not implemented yet.");
    1066 
    10671065    if (entity)
    10681066    {
    10691067        console.write(formationName);
     1068
     1069        var selection = g_Selection.toList();
     1070        Engine.PostNetworkCommand({
     1071            "type": "formation",
     1072            "entities": selection,
     1073            "name": formationName
     1074        });
     1075
    10701076    }
    10711077}
    10721078
  • 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:
  • 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

     
    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 = "default";
    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
     
    133130    var walkingDistance = cmpUnitAI.ComputeWalkingDistance();
    134131    this.columnar = (walkingDistance > g_ColumnDistanceThreshold);
    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 = {   "Hero" : [],
     180                                    "Melee" : [],
     181                                    "Ranged" : [],
     182                                    "Support" : [],
     183                                    "Unknown": []
     184                                    }; // TODO: make this work so we put ranged behind infantry etc
    185185
     186    for each (var ent in active)
     187    {
     188        var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
     189        var classes = cmpIdentity.GetClassesList();
     190        var done = false;
     191        for each (var cla in classes)
     192        {
     193            if(cla in types)
     194            {
     195                types[cla].push(ent);
     196                done = true;
     197                break;
     198            }
     199        }
     200        if (!done)
     201        {
     202            types["Unknown"].push(ent);
     203        }
     204    }
     205
     206    var count = active.length;
     207
     208    var shape = undefined;
     209    var ordering = "default";
     210
     211    var offsets = [];
     212
    186213    // Choose a sensible width for the basic default formation
    187214    var cols;
    188     if (columnar)
     215    if (columnar || this.formationName == "Formation2") // Column
    189216    {
    190217        // Have at most 3 files
    191218        if (count <= 3)
    192219            cols = count;
    193220        else
    194221            cols = 3;
     222        shape = "square";
    195223    }
    196     else
     224    else if (this.formationName == "default" || this.formationName == "Formation9") // Square
    197225    {
    198226        // Try to have at least 5 files (so batch training gives a single line),
    199227        // and at most 8
    200228        if (count <= 5)
    201229            cols = count;
    202         if (count <= 10)
     230        else if (count <= 10)
    203231            cols = 5;
    204232        else if (count <= 16)
    205233            cols = Math.ceil(count / 2);
    206234        else
    207235            cols = 8;
     236        shape = "square";
    208237    }
     238    else if (this.formationName == "Formation3") // Line
     239    {
     240        if (count <= 3)
     241            cols = count;
     242        else if(count < 30)
     243            cols = Math.ceil(count/2);
     244        else
     245            cols = Math.ceil(count/3);
     246        shape = "square";
     247    }
     248    else if (this.formationName == "Formation4") // OpenColumn
     249    {
     250        cols = 2
     251        shape = "opensquare";
     252    }
     253    else if (this.formationName == "Formation5") // OpenSquare
     254    {
     255        if (count <= 5)
     256            cols = 3;
     257        else if (count <= 11)
     258            cols = 4;
     259        else if (count <= 18)
     260            cols = 5;
     261        else
     262            cols = 6;
     263        shape = "opensquare";
     264    }
     265    else if (this.formationName == "Formation0") // Circle
     266    {
     267        var depth;
     268        if (count < 5 || count > 36)
     269        {
     270            this.LoadFormation("default");
     271            return offsets;
     272        }
     273        depth = Math.ceil(count / 12);
    209274
    210     var ranks = Math.ceil(count / cols);
     275        var left = count;
     276        var radius = Math.min(left,12) * separation / (2 * Math.PI);
     277        for (var c = 0; c < depth; ++c)
     278        {
     279            var ctodo = Math.min(left,12);
     280            var cradius = radius - c * separation / 2;
     281            var delta = 2 * Math.PI / ctodo;
     282            for (var alpha = 0; ctodo; alpha+=delta)
     283            {
     284                var x = Math.cos(alpha) * cradius;
     285                var z = Math.sin(alpha) * cradius;
     286                offsets.push({"x": x, "z": z});
     287                ctodo--;
     288                left--;
     289            }
     290        }
     291    }
     292    else if (this.formationName == "Formation1") // Box
     293    {
     294        var root;
     295        if (count > 49)
     296        {
     297            this.LoadFormation("default");
     298            return offsets;
     299        }
     300        if (count <= 9)
     301            root = 3;
     302        else if (count > 9 && count <= 25)
     303            root = 5;
     304        else if (count > 25 && count <= 49)
     305            root = 7;
    211306
    212     var offsets = [];
     307        var left = count;
     308        var meleeleft = types["Melee"].length;
     309        for (var sq = Math.floor(root/2); sq >= 0; --sq)
     310        {
     311            var width = sq * 2 + 1;
     312            var stodo;
     313            if (sq == 0)
     314            {
     315                stodo = left;
     316            }
     317            else
     318            {
     319                if (meleeleft >= width*width - (width-2)*(width-2)) // form a complete box
     320                {
     321                    stodo = width*width - (width-2)*(width-2);
     322                    meleeleft -= stodo;
     323                }
     324                else    // compact
     325                    stodo = Math.max(0,left - (width-2)*(width-2));
     326            }
    213327
    214     var left = count;
    215     for (var r = 0; r < ranks; ++r)
     328            for (var r = -sq; r <= sq && stodo; ++r)
     329            {
     330                for (var c = -sq; c <= sq && stodo; ++c)
     331                {
     332                    if (Math.abs(r) == sq || Math.abs(c) == sq)
     333                    {
     334                        var x = c * separation;
     335                        var z = -r * separation;
     336                        offsets.push({"x": x, "z": z});
     337                        stodo--;
     338                        left--;
     339                    }
     340                }
     341            }
     342        }
     343    }
     344    else if (this.formationName == "Formation7") // Skirmish
    216345    {
    217         var n = Math.min(left, cols);
    218         for (var c = 0; c < n; ++c)
     346        if (count < 9 || count > 25)
    219347        {
    220             var x = ((n-1)/2 - c) * separation;
    221             var z = -r * separation;
    222             offsets.push({"x": x, "z": z});
     348            this.LoadFormation("default");
     349            return offsets;
    223350        }
    224         left -= n;
     351        cols = Math.ceil(count/2);
     352        shape = "opensquare";
    225353    }
     354    else if (this.formationName == "Formation8") // Wedge
     355    {
     356        var depth;
     357        if (count > 49)
     358        {
     359            this.LoadFormation("default");
     360            return offsets;
     361        }
     362        if (count <= 9)
     363            depth = 3;
     364        else if (count > 9 && count <= 16)
     365            depth = 4;
     366        else if (count > 16 && count <= 25)
     367            depth = 5;
     368        else if (count > 25 && count <= 49)
     369            depth = 7;
    226370
     371        var left = count;
     372        var width = 2 * depth - 1;
     373        for (var p = 0; p < depth && left; ++p)
     374        {
     375            for (var r = p; r < depth && left; ++r)
     376            {
     377                var c1 = depth - r + p;
     378                var c2 = depth + r - p;
     379
     380                if (left)
     381                {
     382                        var x = c1 * separation;
     383                        var z = -r * separation;
     384                        offsets.push({"x": x, "z": z});
     385                        left--;
     386                }
     387                if (left && c1 != c2)
     388                {
     389                        var x = c2 * separation;
     390                        var z = -r * separation;
     391                        offsets.push({"x": x, "z": z});
     392                        left--;
     393                }
     394            }
     395        }
     396    }
     397
     398    if (shape == "square")
     399    {
     400        var ranks = Math.ceil(count / cols);
     401
     402        var left = count;
     403        for (var r = 0; r < ranks; ++r)
     404        {
     405            var n = Math.min(left, cols);
     406            for (var c = 0; c < n; ++c)
     407            {
     408                var x = ((n-1)/2 - c) * separation;
     409                var z = -r * separation;
     410                offsets.push({"x": x, "z": z});
     411            }
     412            left -= n;
     413        }
     414    }
     415    else if (shape == "opensquare")
     416    {
     417        var left = count;
     418        for (var r = 0; left; ++r)
     419        {
     420            var n = Math.min(left, cols - (r&1?1:0));
     421            for (var c = 0; c < 2*n; c+=2)
     422            {
     423                var x = (- c - (r&1)) * separation;
     424                var z = -r * separation;
     425                offsets.push({"x": x, "z": z});
     426            }
     427            left -= n;
     428        }
     429    }
     430
     431    // TODO: assign to minimise worst-case distances or whatever
     432    if (ordering == "default")
     433    {
     434        for each (var offset in offsets)
     435        {
     436            var ent = undefined;
     437            for (var t in types)
     438            {
     439                if (types[t].length)
     440                {
     441                    ent = types[t].pop();
     442                    offset.ent = ent
     443                    break;
     444                }
     445            }
     446        }
     447    }
     448
    227449    return offsets;
    228450};
    229451
     
    286508        this.RemoveMembers([msg.entity]);
    287509};
    288510
     511Formation.prototype.LoadFormation = function(formationName)
     512{
     513    this.formationName = formationName;
     514}
     515
    289516Engine.RegisterComponentType(IID_Formation, "Formation", Formation);
  • 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 Infantry</Classes>
    56  </Identity>
    67  <Health>
    78    <Max>90</Max>
  • 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 Infantry</Classes>
    56  </Identity>
    67  <Health>
    78    <Max>100</Max>