Ticket #13: diffFormation-16-04-2011.patch

File diffFormation-16-04-2011.patch, 16.2 KB (added by Badmadblacksad, 13 months 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; 
     
    275275{ 
    276276    var formations = []; 
    277277 
    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"); 
     278    formations.push("Loose"); 
     279    formations.push("Box"); 
     280    formations.push("Column Closed"); 
     281    formations.push("Line Closed"); 
     282    formations.push("Column Open"); 
     283    formations.push("Line Open"); 
     284    formations.push("Flank"); 
     285    formations.push("Skirmish"); 
     286    formations.push("Wedge"); 
     287    formations.push("Testudo"); 
     288    formations.push("Phalanx"); 
     289    formations.push("Syntagma"); 
    290290    formations.push("Formation12"); 
    291291    return formations; 
    292292} 
  • 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: 
     
    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        if (lastFormationName) 
     374            cmpFormation.LoadFormation(lastFormationName); 
     375        else 
     376            cmpFormation.LoadFormation("default"); 
    342377    } 
    343378 
    344379    return Engine.QueryInterface(formationEnt, IID_UnitAI); 
  • 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/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 = "default"; 
    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/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 == "Column Closed") 
    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 == "Phalanx") 
    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 == "Line Closed") 
     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 == "Testudo") 
     249    { 
     250        if (count < 9 || count > 25) 
     251        { 
     252            this.LoadFormation("default"); 
     253            return offsets; 
     254        } 
     255        cols = Math.ceil(Math.sqrt(count)); 
    209256 
    210     var ranks = Math.ceil(count / cols); 
     257        shape = "square"; 
     258    } 
     259    else if (this.formationName == "Column Open") 
     260    { 
     261        cols = 2 
     262        shape = "opensquare"; 
     263    } 
     264    else if (this.formationName == "Line Open") 
     265    { 
     266        if (count <= 5) 
     267            cols = 3; 
     268        else if (count <= 11) 
     269            cols = 4; 
     270        else if (count <= 18) 
     271            cols = 5; 
     272        else 
     273            cols = 6; 
     274        shape = "opensquare"; 
     275    } 
     276    else if (this.formationName == "Loose") // Circle 
     277    { 
     278        var depth; 
     279        if (count < 5 || count > 36) 
     280        { 
     281            this.LoadFormation("default"); 
     282            return offsets; 
     283        } 
     284        depth = Math.ceil(count / 12); 
    211285 
    212     var offsets = []; 
     286        var left = count; 
     287        var radius = Math.min(left,12) * separation / (2 * Math.PI); 
     288        for (var c = 0; c < depth; ++c) 
     289        { 
     290            var ctodo = Math.min(left,12); 
     291            var cradius = radius - c * separation / 2; 
     292            var delta = 2 * Math.PI / ctodo; 
     293            for (var alpha = 0; ctodo; alpha+=delta) 
     294            { 
     295                var x = Math.cos(alpha) * cradius; 
     296                var z = Math.sin(alpha) * cradius; 
     297                offsets.push({"x": x, "z": z}); 
     298                ctodo--; 
     299                left--; 
     300            } 
     301        } 
     302    } 
     303    else if (this.formationName == "Box") // Box 
     304    { 
     305        var root; 
     306        if (count > 49) 
     307        { 
     308            this.LoadFormation("default"); 
     309            return offsets; 
     310        } 
     311        if (count <= 9) 
     312            root = 3; 
     313        else if (count > 9 && count <= 25) 
     314            root = 5; 
     315        else if (count > 25 && count <= 49) 
     316            root = 7; 
    213317 
    214     var left = count; 
    215     for (var r = 0; r < ranks; ++r) 
     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") 
    216356    { 
    217         var n = Math.min(left, cols); 
    218         for (var c = 0; c < n; ++c) 
     357        if (count < 9 || count > 25) 
    219358        { 
    220             var x = ((n-1)/2 - c) * separation; 
    221             var z = -r * separation; 
    222             offsets.push({"x": x, "z": z}); 
     359            this.LoadFormation("default"); 
     360            return offsets; 
    223361        } 
    224         left -= n; 
     362        cols = Math.ceil(count/2); 
     363        shape = "opensquare"; 
    225364    } 
     365    else if (this.formationName == "Wedge") 
     366    { 
     367        var depth; 
     368        if (count > 49) 
     369        { 
     370            this.LoadFormation("default"); 
     371            return offsets; 
     372        } 
     373        if (count <= 9) 
     374            depth = 3; 
     375        else if (count > 9 && count <= 16) 
     376            depth = 4; 
     377        else if (count > 16 && count <= 25) 
     378            depth = 5; 
     379        else if (count > 25 && count <= 49) 
     380            depth = 7; 
    226381 
     382        var left = count; 
     383        var width = 2 * depth - 1; 
     384        for (var p = 0; p < depth && left; ++p) 
     385        { 
     386            for (var r = p; r < depth && left; ++r) 
     387            { 
     388                var c1 = depth - r + p; 
     389                var c2 = depth + r - p; 
     390 
     391                if (left) 
     392                { 
     393                        var x = c1 * separation; 
     394                        var z = -r * separation; 
     395                        offsets.push({"x": x, "z": z}); 
     396                        left--; 
     397                } 
     398                if (left && c1 != c2) 
     399                { 
     400                        var x = c2 * separation; 
     401                        var z = -r * separation; 
     402                        offsets.push({"x": x, "z": z}); 
     403                        left--; 
     404                } 
     405            } 
     406        } 
     407    } 
     408    else if (this.formationName == "Flank") 
     409    { 
     410        if (count < 8 || count > 24) 
     411        { 
     412            this.LoadFormation("default"); 
     413            return offsets; 
     414        } 
     415        cols = 3; 
     416        var leftside = []; 
     417        leftside[0] = Math.ceil(count/2); 
     418        leftside[1] = Math.floor(count/2); 
     419        ranks = Math.ceil(leftside[0] / cols); 
     420        var off = - separation * 4; 
     421        for (var side = 0; side < 2; ++side) 
     422        { 
     423            var left = leftside[side]; 
     424            off += side * separation * 8; 
     425            for (var r = 0; r < ranks; ++r) 
     426            { 
     427                var n = Math.min(left, cols); 
     428                for (var c = 0; c < n; ++c) 
     429                { 
     430                    var x = off + ((n-1)/2 - c) * separation; 
     431                    var z = -r * separation; 
     432                    offsets.push({"x": x, "z": z}); 
     433                } 
     434                left -= n; 
     435            } 
     436        } 
     437    } 
     438    else if (this.formationName == "Formation12" || this.formationName == "Syntagma") 
     439    { 
     440        return offsets; //@todo 
     441    } 
     442 
     443    if (shape == "square") 
     444    { 
     445        var ranks = Math.ceil(count / cols); 
     446 
     447        var left = count; 
     448        for (var r = 0; r < ranks; ++r) 
     449        { 
     450            var n = Math.min(left, cols); 
     451            for (var c = 0; c < n; ++c) 
     452            { 
     453                var x = ((n-1)/2 - c) * separation; 
     454                var z = -r * separation; 
     455                offsets.push({"x": x, "z": z}); 
     456            } 
     457            left -= n; 
     458        } 
     459    } 
     460    else if (shape == "opensquare") 
     461    { 
     462        var left = count; 
     463        for (var r = 0; left; ++r) 
     464        { 
     465            var n = Math.min(left, cols - (r&1?1:0)); 
     466            for (var c = 0; c < 2*n; c+=2) 
     467            { 
     468                var x = (- c - (r&1)) * separation; 
     469                var z = -r * separation; 
     470                offsets.push({"x": x, "z": z}); 
     471            } 
     472            left -= n; 
     473        } 
     474    } 
     475 
     476    // TODO: assign to minimise worst-case distances or whatever 
     477    if (ordering == "default") 
     478    { 
     479        for each (var offset in offsets) 
     480        { 
     481            var ent = undefined; 
     482            for (var t in types) 
     483            { 
     484                if (types[t].length) 
     485                { 
     486                    ent = types[t].pop(); 
     487                    offset.ent = ent 
     488                    break; 
     489                } 
     490            } 
     491        } 
     492    } 
     493 
    227494    return offsets; 
    228495}; 
    229496 
     
    286553        this.RemoveMembers([msg.entity]); 
    287554}; 
    288555 
     556Formation.prototype.LoadFormation = function(formationName) 
     557{ 
     558    this.formationName = formationName; 
     559    for each(var ent in this.members) 
     560    { 
     561        var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 
     562        cmpUnitAI.SetLastFormationName(this.formationName); 
     563    } 
     564} 
     565 
    289566Engine.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>