Ticket #865: diffStances-29-05-2011.patch

File diffStances-29-05-2011.patch, 26.8 KB (added by Badmadblacksad, 2 years ago)
  • source/simulation2/components/ICmpRangeManager.cpp

     
    3636BEGIN_INTERFACE_WRAPPER(RangeManager) 
    3737DEFINE_INTERFACE_METHOD_5("ExecuteQuery", std::vector<entity_id_t>, ICmpRangeManager, ExecuteQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int) 
    3838DEFINE_INTERFACE_METHOD_5("CreateActiveQuery", ICmpRangeManager::tag_t, ICmpRangeManager, CreateActiveQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int) 
     39DEFINE_INTERFACE_METHOD_3("ModifyRangeActiveQuery", std::vector<entity_id_t>, ICmpRangeManager, ModifyRangeActiveQuery, ICmpRangeManager::tag_t, entity_pos_t, entity_pos_t) 
    3940DEFINE_INTERFACE_METHOD_1("DestroyActiveQuery", void, ICmpRangeManager, DestroyActiveQuery, ICmpRangeManager::tag_t) 
    4041DEFINE_INTERFACE_METHOD_1("EnableActiveQuery", void, ICmpRangeManager, EnableActiveQuery, ICmpRangeManager::tag_t) 
    4142DEFINE_INTERFACE_METHOD_1("DisableActiveQuery", void, ICmpRangeManager, DisableActiveQuery, ICmpRangeManager::tag_t) 
  • source/simulation2/components/ICmpRangeManager.h

     
    102102        entity_pos_t minRange, entity_pos_t maxRange, std::vector<int> owners, int requiredInterface) = 0; 
    103103 
    104104    /** 
     105     * Modify an active query's range parameters and execute a ResetActiveQuery. 
     106     * @param tag identifier of query. 
     107     * @param minRange non-negative minimum distance in metres (inclusive). 
     108     * @param maxRange non-negative maximum distance in metres (inclusive); or -1.0 to ignore distance. 
     109     * @return list of entities matching the query, ordered by increasing distance from the source entity. 
     110     */ 
     111    virtual std::vector<entity_id_t> ModifyRangeActiveQuery(tag_t tag, 
     112        entity_pos_t minRange, entity_pos_t maxRange) = 0; 
     113 
     114    /** 
    105115     * Destroy a query and clean up resources. This must be called when an entity no longer needs its 
    106116     * query (e.g. when the entity is destroyed). 
    107117     * @param tag identifier of query. 
  • source/simulation2/components/CCmpRangeManager.cpp

     
    459459        return (tag_t)id; 
    460460    } 
    461461 
     462    virtual std::vector<entity_id_t> ModifyRangeActiveQuery(tag_t tag, 
     463        entity_pos_t minRange, entity_pos_t maxRange) 
     464    { 
     465        std::map<tag_t, Query>::iterator it = m_Queries.find(tag); 
     466        if (it == m_Queries.end()) 
     467        { 
     468            LOGERROR(L"CCmpRangeManager: ModifyRangeActiveQuery called with invalid tag %d", tag); 
     469            std::vector<entity_id_t> r; 
     470            return r; 
     471        } 
     472 
     473        Query& q = it->second; 
     474        q.minRange = minRange; 
     475        q.maxRange = maxRange; 
     476        return ResetActiveQuery(tag); 
     477    } 
     478 
    462479    virtual void DestroyActiveQuery(tag_t tag) 
    463480    { 
    464481        if (m_Queries.find(tag) == m_Queries.end()) 
  • binaries/data/mods/public/maps/scenarios/Combat_demo.xml

     
    4040      "Civ": "hele" 
    4141    } 
    4242  ], 
    43   "RevealMap": true 
     43  "RevealMap": true, 
     44  "DefaultStance": "stand" 
    4445} 
    4546]]></ScriptSettings> 
    4647    <Entities> 
     
    12851286        </Entity> 
    12861287    </Entities> 
    12871288    <Paths/> 
    1288 </Scenario> 
    1289  No newline at end of file 
     1289</Scenario> 
  • binaries/data/mods/public/maps/scenarios/Pathfinding_demo.xml

     
    4141    } 
    4242  ], 
    4343  "RevealMap": true, 
    44   "DefaultStance": "holdfire", 
     44  "DefaultStance": "defensive", 
    4545  "GameType": "endless" 
    4646} 
    4747]]></ScriptSettings> 
     
    684684        </Entity> 
    685685    </Entities> 
    686686    <Paths/> 
    687 </Scenario> 
    688  No newline at end of file 
     687</Scenario> 
  • binaries/data/mods/public/gui/session/session.xml

     
    408408    </object> 
    409409 
    410410    <!-- ================================  ================================ --> 
     411    <!-- Stance Selection (Temporary location) --> 
     412    <!-- ================================  ================================ --> 
     413    <object name="unitStancePanel" 
     414        style="goldPanelFrilly" 
     415        size="50 50 300 106" 
     416        type="text" 
     417    > 
     418        <object size="-5 -2 59 62" type="image" sprite="snIconSheetTab" tooltip_style="sessionToolTipBottom" 
     419             cell_id="4" tooltip="Stances"/> 
     420 
     421        <object size="56 12 100% 100%"> 
     422            <repeat count="5"> 
     423                <object name="unitStanceButton[n]" hidden="true" style="iconButton" type="button" size="0 0 36 36" tooltip_style="sessionToolTipBottomBold" z="100"> 
     424                    <object name="unitStanceIcon[n]" type="image" style="formationIcon" ghost="true" size="3 3 33 33"/> 
     425                </object> 
     426            </repeat> 
     427        </object> 
     428    </object> 
     429 
     430    <!-- ================================  ================================ --> 
    411431    <!-- Idle Worker Button --> 
    412432    <!-- ================================  ================================ --> 
    413433    <object type="image" 
     
    640660                </object> 
    641661            </object> 
    642662 
    643             <object name="unitStancePanel" 
    644                 style="goldPanelFrilly" 
    645                 size="0 100%-56 100% 100%" 
    646                 type="text" 
    647             > 
    648                 <object size="-5 -2 59 62" type="image" sprite="snIconSheetTab" tooltip_style="sessionToolTipBottom" 
    649                      cell_id="4" tooltip="Stances"/> 
    650  
    651                 [stance commands] 
    652             </object> 
    653  
    654663            <object name="unitResearchPanel" 
    655664                style="goldPanelFrilly" 
    656665                size="0 100%-56 100% 100%" 
  • binaries/data/mods/public/gui/session/styles.xml

     
    123123        ghost="true" 
    124124    /> 
    125125 
     126    <style name="stanceIcon" 
     127        sprite="snIconSheetStance" 
     128        sprite_disabled="snIconSheetStance" 
     129        ghost="true" 
     130    /> 
     131 
    126132    <style name="commandIcon" 
    127133        sprite="commands" 
    128134        ghost="true" 
  • binaries/data/mods/public/gui/session/input.js

     
    4646var doubleClicked = false; 
    4747// Store the previously clicked entity - ensure a double/triple click happens on the same entity 
    4848var prevClickedEntity = 0; 
     49var prevIsRight = false; 
    4950 
    5051// Same double-click behaviour for hotkey presses 
    5152const doublePressTime = 500; 
     
    641642            { 
    642643                dragStart = [ ev.x, ev.y ]; 
    643644                inputState = INPUT_SELECTING; 
     645                prevIsRight = false; 
    644646                return true; 
    645647            } 
    646648            else if (ev.button == SDL_BUTTON_RIGHT) 
     
    648650                var action = determineAction(ev.x, ev.y); 
    649651                if (!action) 
    650652                    break; 
    651                 return doAction(action, ev); 
     653 
     654                var v = false; 
     655 
     656                var now = new Date(); 
     657                if ((now.getTime() - doubleClickTimer < doubleClickTime) && (prevIsRight == true)) 
     658                { 
     659                    v = true; 
     660                } 
     661                else 
     662                { 
     663                    prevIsRight = true; 
     664                    doubleClickTimer = now.getTime(); 
     665                } 
     666 
     667                return doAction(action, ev, v); 
    652668            } 
    653669            break; 
    654670 
     
    861877    return false; 
    862878} 
    863879 
    864 function doAction(action, ev) 
     880function doAction(action, ev, d) 
    865881{ 
    866882    var selection = g_Selection.toList(); 
    867883 
     
    873889    { 
    874890    case "move": 
    875891        var target = Engine.GetTerrainAtPoint(ev.x, ev.y); 
    876         Engine.PostNetworkCommand({"type": "walk", "entities": selection, "x": target.x, "z": target.z, "queued": queued}); 
     892        var type = d ? "run" : "walk"; 
     893        Engine.PostNetworkCommand({"type": type, "entities": selection, "x": target.x, "z": target.z, "queued": queued}); 
    877894        Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] }); 
    878895        return true; 
    879896 
     
    11111128    } 
    11121129} 
    11131130 
     1131// Performs the specified stance 
     1132function performStance(entity, stanceName) 
     1133{ 
     1134    if (entity) 
     1135    { 
     1136        var selection = g_Selection.toList(); 
     1137        Engine.PostNetworkCommand({ 
     1138            "type": "stance", 
     1139            "entities": selection, 
     1140            "name": stanceName 
     1141        }); 
     1142    } 
     1143} 
     1144 
    11141145// Performs the specified group 
    11151146function performGroup(action, groupId) 
    11161147{ 
  • binaries/data/mods/public/gui/session/unit_commands.js

     
    66const TRAINING = "Training"; 
    77const CONSTRUCTION = "Construction"; 
    88const COMMAND = "Command"; 
     9const STANCE = "Stance"; 
    910 
    1011// Constants 
    1112const COMMANDS_PANEL_WIDTH = 228; 
     
    1314const UNIT_PANEL_HEIGHT = 44; // QUEUE: The height needed for a row of buttons 
    1415 
    1516// The number of currently visible buttons (used to optimise showing/hiding) 
    16 var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Construction": 0, "Command": 0}; 
     17var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Construction": 0, "Command": 0, "Stance": 0}; 
    1718 
    1819// Unit panels are panels with row(s) of buttons 
    1920var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Training", "Construction", "Research", "Stance", "Command"]; 
     
    153154        var item = items[i]; 
    154155        var entType = ((guiName == "Queue")? item.template : item); 
    155156        var template; 
    156         if (guiName != "Formation" && guiName != "Command") 
     157        if (guiName != "Formation" && guiName != "Command" && guiName != "Stance") 
    157158        { 
    158159            template = GetTemplateData(entType); 
    159160            if (!template) 
     
    190191            getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (count > 1 ? count : ""); 
    191192            break; 
    192193 
     194        case STANCE: 
    193195        case FORMATION: 
    194196            var tooltip = toTitleCase(item); 
    195197            break; 
     
    265267                icon.sprite = "formation"; 
    266268            } 
    267269        } 
     270        else if (guiName == "Stance") 
     271        { 
     272            icon.cell_id = i; 
     273            icon.sprite = "snIconSheetStance"; 
     274        } 
    268275        else if (guiName == "Command") 
    269276        { 
    270277            //icon.cell_id = i; 
     
    368375            setupUnitPanel("Formation", usedPanels, entState, formations, 
    369376                function (item) { performFormation(entState.id, item); } ); 
    370377 
     378        var stances = ["violent","aggressive","passive","defensive","stand"]; 
     379        if (isUnit(entState) && !isAnimal(entState) && !entState.garrisonHolder && stances.length) 
     380            setupUnitPanel("Stance", usedPanels, entState, stances, 
     381                function (item) { performStance(entState.id, item); } ); 
     382 
    371383        if (entState.buildEntities && entState.buildEntities.length) 
    372384        { 
    373385            setupUnitPanel("Construction", usedPanels, entState, entState.buildEntities, startBuildingPlacement); 
  • binaries/data/mods/public/simulation/helpers/Setup.js

     
    1414        for each (var ent in Engine.GetEntitiesWithInterface(IID_UnitAI)) 
    1515        { 
    1616            var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 
    17             cmpUnitAI.SetStance(settings.DefaultStance); 
     17            cmpUnitAI.SwitchToStance(settings.DefaultStance); 
    1818        } 
    1919    } 
    2020 
  • binaries/data/mods/public/simulation/helpers/Commands.js

     
    2727            cmpUnitAI.Walk(cmd.x, cmd.z, cmd.queued); 
    2828        break; 
    2929 
     30    case "run": 
     31        var cmpUnitAI = GetFormationUnitAI(cmd.entities); 
     32        if (cmpUnitAI) 
     33            cmpUnitAI.Run(cmd.x, cmd.z, cmd.queued); 
     34        break; 
     35 
    3036    case "attack": 
    3137        var cmpUnitAI = GetFormationUnitAI(cmd.entities); 
    3238        if (cmpUnitAI) 
     
    261267        } 
    262268        break; 
    263269 
     270    case "stance": 
     271        for each (var ent in cmd.entities) 
     272        { 
     273            var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 
     274            if (cmpUnitAI) 
     275                cmpUnitAI.SwitchToStance(cmd.name); 
     276        } 
     277        break; 
     278 
    264279    default: 
    265280        error("Ignoring unrecognised command type '" + cmd.type + "'"); 
    266281    } 
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    66    "<element name='DefaultStance'>" + 
    77        "<choice>" + 
    88            "<value>aggressive</value>" + 
    9             "<value>holdfire</value>" + 
    10             "<value>noncombat</value>" + 
     9            "<value>defensive</value>" + 
     10            "<value>passive</value>" + 
    1111        "</choice>" + 
    1212    "</element>" + 
    1313    "<element name='FormationController'>" + 
     
    5959// TODO: even this limited version isn't implemented properly yet (e.g. it can't handle 
    6060// dynamic stance changes). 
    6161var g_Stances = { 
    62     "aggressive": { 
     62    "violent": { 
    6363        targetVisibleEnemies: true, 
    6464        targetAttackers: true, 
    6565        respondFlee: false, 
    6666        respondChase: true, 
     67        respondStandGround : false, 
     68        respondHoldGround : false, 
    6769    }, 
    68     "holdfire": { 
    69         targetVisibleEnemies: false, 
     70    "aggressive": { 
     71        targetVisibleEnemies: true, 
    7072        targetAttackers: true, 
    7173        respondFlee: false, 
    7274        respondChase: true, 
     75        respondStandGround : false, 
     76        respondHoldGround : false, 
    7377    }, 
    74     "noncombat": { 
     78    "defensive": { 
     79        targetVisibleEnemies: true, 
     80        targetAttackers: true, 
     81        respondFlee: false, 
     82        respondChase: false, 
     83        respondStandGround : false, 
     84        respondHoldGround : true, 
     85    }, 
     86    "passive": { 
    7587        targetVisibleEnemies: false, 
    7688        targetAttackers: true, 
    7789        respondFlee: true, 
    7890        respondChase: false, 
     91        respondStandGround : false, 
     92        respondHoldGround : false, 
    7993    }, 
     94    "stand": { 
     95        targetVisibleEnemies: true, 
     96        targetAttackers: true, 
     97        respondFlee: false, 
     98        respondChase: false, 
     99        respondStandGround : true, 
     100        respondHoldGround : false, 
     101    }, 
    80102}; 
    81103 
    82104// See ../helpers/FSM.js for some documentation of this FSM specification syntax 
     
    114136        // ignore 
    115137    }, 
    116138 
     139    "StanceChanged": function(msg) { 
     140        if (this.StanceSpecificQuery(this.stance,true)) 
     141            return; 
     142    }, 
     143 
    117144    // Formation handlers: 
    118145 
    119146    "FormationLeave": function(msg) { 
     
    154181            return; 
    155182        } 
    156183 
     184        this.SetReturnPoint({"x": this.order.data.x, "z": this.order.data.z}); 
    157185        this.MoveToPoint(this.order.data.x, this.order.data.z); 
    158186        this.SetNextState("INDIVIDUAL.WALKING"); 
    159187    }, 
     
    180208        } 
    181209    }, 
    182210 
     211    "Order.Run": function(msg) { 
     212        if (this.IsAnimal()) 
     213        { 
     214            // TODO: let players move captured animals around 
     215            this.FinishOrder(); 
     216            return; 
     217        } 
     218 
     219        this.SetReturnPoint({"x": this.order.data.x, "z": this.order.data.z}); 
     220        this.MoveToPoint(this.order.data.x, this.order.data.z); 
     221        this.SetNextState("INDIVIDUAL.RUNNING"); 
     222    }, 
     223 
    183224    "Order.Flee": function(msg) { 
    184225        // TODO: if we were attacked by a ranged unit, we need to flee much further away 
    185226        var ok = this.MoveToTargetRangeExplicit(this.order.data.target, +this.template.FleeDistance, -1); 
     
    217258        } 
    218259        this.attackType = type; 
    219260 
     261        if (this.CheckTargetRange(this.order.data.target, IID_Attack, this.attackType)) 
     262        { 
     263            // We are already at the target 
     264            // so try attacking it from here. 
     265            this.StopMoving(); 
     266            if (this.IsAnimal()) 
     267                this.SetNextState("ANIMAL.COMBAT.ATTACKING"); 
     268            else 
     269                this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING"); 
     270        } 
    220271        // Try to move within attack range 
    221         if (this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType)) 
     272        else if (!this.order.data.stand && this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType)) 
    222273        { 
    223274            // We've started walking to the given point 
    224275            if (this.IsAnimal()) 
     
    227278                this.SetNextState("INDIVIDUAL.COMBAT.APPROACHING"); 
    228279        } 
    229280        else 
    230         { 
    231             // We are already at the target, or can't move at all, 
    232             // so try attacking it from here. 
    233             // TODO: need better handling of the can't-reach-target case 
    234             this.StopMoving(); 
    235             if (this.IsAnimal()) 
    236                 this.SetNextState("ANIMAL.COMBAT.ATTACKING"); 
    237             else 
    238                 this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING"); 
    239         } 
     281            this.FinisOrder(); 
    240282    }, 
    241283 
    242284    "Order.Gather": function(msg) { 
     
    484526                // If we entered the idle state we must have nothing better to do, 
    485527                // so immediately check whether there's anybody nearby to attack. 
    486528                // (If anyone approaches later, it'll be handled via LosRangeUpdate.) 
    487                 if (this.losRangeQuery) 
    488                 { 
    489                     var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 
    490                     var ents = rangeMan.ResetActiveQuery(this.losRangeQuery); 
    491                     if (this.GetStance().targetVisibleEnemies) 
    492                     { 
    493                         if (this.RespondToTargetedEntities(ents)) 
    494                             return true; // (abort the FSM transition since we may have already switched state) 
    495                     } 
    496                 } 
     529                if (this.StanceSpecificQuery(this.stance)) 
     530                    return true; 
    497531 
    498532                // Nobody to attack - stay in idle 
    499533                return false; 
     
    551585                } 
    552586            }, 
    553587 
     588            "StanceChanged": function(msg) { 
     589                if (this.StanceSpecificQuery(this.stance)) 
     590                    return; 
     591            }, 
    554592        }, 
    555593 
    556594        "WALKING": { 
     
    563601            }, 
    564602        }, 
    565603 
     604        "RUNNING": { 
     605            "enter": function () { 
     606                // Run quickly 
     607                var speed = this.GetRunSpeed(); 
     608                this.SelectAnimation("move"); 
     609                this.SetMoveSpeed(speed); 
     610                // TODO: stamina 
     611            }, 
     612 
     613            "leave": function() { 
     614                var speed = this.GetWalkSpeed(); 
     615                this.SelectAnimation("move"); 
     616                this.SetMoveSpeed(speed); 
     617            }, 
     618 
     619            "MoveCompleted": function() { 
     620                this.FinishOrder(); 
     621            }, 
     622        }, 
     623 
    566624        "FLEEING": { 
    567625            "enter": function() { 
    568626                this.PlaySound("panic"); 
     
    605663                "MoveCompleted": function() { 
    606664                    this.SetNextState("ATTACKING"); 
    607665                }, 
     666 
     667                "Attacked": function(msg) { 
     668                    // If we're attacked by a close enemy, we should try to defend ourself 
     669                    if (this.GetStance().targetAttackers && msg.data.type == "Melee") 
     670                    { 
     671                        this.RespondToTargetedEntities([msg.data.attacker]); 
     672                    } 
     673                }, 
    608674            }, 
    609675 
    610676            "ATTACKING": { 
     
    638704                        } 
    639705 
    640706                        // Can't reach it - try to chase after it 
    641                         if (this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType)) 
     707                        if (this.GetStance().respondChase && this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType)) 
    642708                        { 
    643                             this.SetNextState("COMBAT.CHASING"); 
     709                            this.SetNextState("COMBAT.APPROACHING"); 
    644710                            return; 
    645711                        } 
     712 
     713                        if (this.GetStance().respondHoldGround && this.CheckTargetDistance(this.order.data.target, this.attackType, 50)) 
     714                        { 
     715                            if (this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType)) 
     716                            { 
     717                                this.SetNextState("COMBAT.APPROACHING"); 
     718                                return; 
     719                            } 
     720                        } 
    646721                    } 
    647722 
    648723                    // Can't reach it, or it doesn't exist any more - give up 
    649                     this.FinishOrder(); 
     724                    if (this.FinishOrder()) 
     725                        return; 
    650726 
    651                     // TODO: see if we can switch to a new nearby enemy 
     727                    // see if we can switch to a new nearby enemy 
     728                    if (this.StanceSpecificQuery(this.stance, true)) 
     729                        return; 
     730 
     731                    if (this.GetStance().respondHoldGround) 
     732                        this.Walk(this.returnPoint.x, this.returnPoint.z, false); 
    652733                }, 
    653734 
    654735                // TODO: respond to target deaths immediately, rather than waiting 
    655736                // until the next Timer event 
    656737            }, 
    657  
    658             "CHASING": { 
    659                 "enter": function () { 
    660                     this.SelectAnimation("move"); 
    661                 }, 
    662              
    663                 "MoveCompleted": function() { 
    664                     this.SetNextState("ATTACKING"); 
    665                 }, 
    666             }, 
    667738        }, 
    668739 
    669740        "GATHER": { 
     
    13031374// This should be called whenever our ownership changes. 
    13041375UnitAI.prototype.SetupRangeQuery = function(owner) 
    13051376{ 
    1306     var cmpVision = Engine.QueryInterface(this.entity, IID_Vision); 
    1307     if (!cmpVision) 
    1308         return; 
    1309      
    13101377    var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 
    13111378    var playerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); 
    1312      
     1379 
    13131380    if (this.losRangeQuery) 
    13141381        rangeMan.DestroyActiveQuery(this.losRangeQuery); 
    13151382 
    1316     var range = cmpVision.GetRange(); 
    1317      
    13181383    var players = []; 
    1319      
     1384 
    13201385    if (owner != -1) 
    13211386    { 
    13221387        // If unit not just killed, get enemy players via diplomacy 
     
    13341399                players.push(i); 
    13351400        } 
    13361401    } 
    1337      
    1338     this.losRangeQuery = rangeMan.CreateActiveQuery(this.entity, 0, range, players, IID_DamageReceiver); 
     1402 
     1403    var range = this.StanceSpecificRange(); 
     1404 
     1405    this.losRangeQuery = rangeMan.CreateActiveQuery(this.entity, 0, range.max, players, IID_DamageReceiver); 
    13391406    rangeMan.EnableActiveQuery(this.losRangeQuery); 
    1340 }; 
     1407} 
    13411408 
    13421409//// FSM linkage functions //// 
    13431410 
     
    18121879    return cmpUnitMotion.IsInTargetRange(target, range.min, range.max); 
    18131880}; 
    18141881 
     1882UnitAI.prototype.CheckTargetDistance = function(target, type, distance) 
     1883{ 
     1884    var cmpRanged = Engine.QueryInterface(this.entity, IID_Attack); 
     1885    var range = cmpRanged.GetRange(type); 
     1886 
     1887    var cmpPosition = Engine.QueryInterface(target, IID_Position); 
     1888    if (!cmpPosition || !cmpPosition.IsInWorld()) 
     1889        return false; 
     1890 
     1891    var pos = cmpPosition.GetPosition(); 
     1892    var dx = this.returnPoint.x - pos.x; 
     1893    var dz = this.returnPoint.z - pos.z; 
     1894    var dist = Math.sqrt(dx*dx + dz*dz); 
     1895 
     1896    return dist < distance + range.max; 
     1897}; 
     1898 
    18151899UnitAI.prototype.GetBestAttack = function() 
    18161900{ 
    18171901    var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); 
     
    18391923}; 
    18401924 
    18411925/** 
     1926 * Try to find one of the given entities which can be attacked, 
     1927 * without moving. 
     1928 * Returns true if it found something to attack. 
     1929 */ 
     1930UnitAI.prototype.AttackEntityInRange = function(ents) 
     1931{ 
     1932    var type = this.GetBestAttack(); 
     1933    for each (var target in ents) 
     1934    { 
     1935        if (this.CanAttack(target) && this.CheckTargetRange(target, IID_Attack, type)) 
     1936        { 
     1937            this.PushOrderFront("Attack", { "target": target, "stand": true }); 
     1938            return true; 
     1939        } 
     1940    } 
     1941    return false; 
     1942}; 
     1943 
     1944/** 
     1945 * Try to find one of the given entities which can be attacked, 
     1946 * within zone. 
     1947 * Returns true if it found something to attack. 
     1948 */ 
     1949UnitAI.prototype.AttackEntityInZone = function(ents) 
     1950{ 
     1951    var type = this.GetBestAttack(); 
     1952    for each (var target in ents) 
     1953    { 
     1954        if (this.CanAttack(target) && this.CheckTargetDistance(target, type, 50)) 
     1955        { 
     1956            this.PushOrderFront("Attack", { "target": target }); 
     1957            return true; 
     1958        } 
     1959    } 
     1960    return false; 
     1961}; 
     1962 
     1963/** 
    18421964 * Try to respond appropriately given our current stance, 
    18431965 * given a list of entities that match our stance's target criteria. 
    18441966 * Returns true if it responded. 
     
    18511973    if (this.GetStance().respondChase) 
    18521974        return this.AttackVisibleEntity(ents); 
    18531975 
     1976    if (this.GetStance().respondStandGround) 
     1977        return this.AttackEntityInRange(ents); 
     1978 
     1979    if (this.GetStance().respondHoldGround) 
     1980        return this.AttackEntityInZone(ents); 
     1981 
    18541982    if (this.GetStance().respondFlee) 
    18551983    { 
    18561984        this.PushOrderFront("Flee", { "target": ents[0] }); 
     
    19192047        switch (order.type) 
    19202048        { 
    19212049        case "Walk": 
     2050        case "Run": 
    19222051            // Add the distance to the target point 
    19232052            var dx = order.data.x - pos.x; 
    19242053            var dz = order.data.z - pos.z; 
     
    19812110    this.AddOrder("WalkToTarget", { "target": target }, queued); 
    19822111}; 
    19832112 
     2113UnitAI.prototype.Run = function(x, z, queued) 
     2114{ 
     2115    if (this.IsFormationController()) 
     2116        this.AddOrder("Walk", { "x": x, "z": z }, queued); 
     2117    else 
     2118        this.AddOrder("Run", { "x": x, "z": z }, queued); 
     2119}; 
     2120 
    19842121UnitAI.prototype.LeaveFoundation = function(target) 
    19852122{ 
    19862123    // TODO: we should verify this is a friendly foundation, otherwise 
     
    20882225        this.stance = stance; 
    20892226    else 
    20902227        error("UnitAI: Setting to invalid stance '"+stance+"'"); 
     2228} 
     2229 
     2230UnitAI.prototype.SwitchToStance = function(stance) 
     2231{ 
     2232    var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 
     2233    if (!cmpPosition || !cmpPosition.IsInWorld()) 
     2234        return; 
     2235    var pos = cmpPosition.GetPosition(); 
     2236    this.SetReturnPoint({"x": pos.x, "z": pos.z}); 
     2237 
     2238    this.SetStance(stance); 
     2239    if (stance == "stand" || stance == "defensive" || stance == "passive") 
     2240        this.StopMoving(); 
     2241 
     2242    UnitFsm.ProcessMessage(this, {"type": "StanceChanged", "stance": stance}); 
     2243} 
     2244 
     2245UnitAI.prototype.StanceSpecificQuery = function(stance, disable) 
     2246{ 
     2247    if (!g_Stances[stance]) 
     2248    { 
     2249        error("UnitAI: Setting to invalid stance '"+stance+"'"); 
     2250        return false; 
     2251    } 
     2252 
     2253    if (!this.losRangeQuery) 
     2254        return false; 
     2255 
     2256    var range = this.StanceSpecificRange(); 
     2257 
     2258    var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 
     2259    var ents = rangeMan.ModifyRangeActiveQuery(this.losRangeQuery, range.min, range.max); 
     2260 
     2261    if (disable && !this.IsAnimal()) 
     2262        rangeMan.DisableActiveQuery(this.losRangeQuery); 
     2263 
     2264    return this.RespondToTargetedEntities(ents); 
    20912265}; 
    20922266 
     2267UnitAI.prototype.StanceSpecificRange = function() 
     2268{ 
     2269    var ret = { "min": 0, "max": 0 }; 
     2270    if (this.GetStance().respondStandGround) 
     2271    { 
     2272        var cmpRanged = Engine.QueryInterface(this.entity, IID_Attack); 
     2273        if (!cmpRanged) 
     2274            return ret; 
     2275        var range = cmpRanged.GetRange(cmpRanged.GetBestAttack()); 
     2276        ret.min = range.min; 
     2277        ret.max = range.max; 
     2278    } 
     2279    else if (this.GetStance().respondChase) 
     2280    { 
     2281        var cmpVision = Engine.QueryInterface(this.entity, IID_Vision); 
     2282        if (!cmpVision) 
     2283            return ret; 
     2284        var range = cmpVision.GetRange(); 
     2285        ret.max = range; 
     2286    } 
     2287    else if (this.GetStance().respondHoldGround) 
     2288    { 
     2289        var cmpRanged = Engine.QueryInterface(this.entity, IID_Attack); 
     2290        if (!cmpRanged) 
     2291            return ret; 
     2292        var range = cmpRanged.GetRange(cmpRanged.GetBestAttack()); 
     2293        ret.max = range.max + 50; 
     2294    } 
     2295    return ret; 
     2296}; 
     2297 
    20932298UnitAI.prototype.GetStance = function() 
    20942299{ 
    20952300    return g_Stances[this.stance]; 
     
    22342439    cmpMotion.SetSpeed(speed); 
    22352440}; 
    22362441 
     2442UnitAI.prototype.SetReturnPoint = function(pos) 
     2443{ 
     2444    this.returnPoint = {"x": pos.x, "z": pos.z}; 
     2445}; 
     2446 
    22372447Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI); 
  • binaries/data/mods/public/simulation/components/Attack.js

     
    245245    } 
    246246 
    247247    Engine.PostMessage(data.target, MT_Attacked, 
    248         { "attacker": this.entity, "target": data.target }); 
     248        { "attacker": this.entity, "target": data.target, "type": data.type }); 
    249249 
    250250    PlaySound("attack_impact", this.entity); 
    251251}; 
  • binaries/data/mods/public/simulation/templates/template_unit_support.xml

     
    88    <Type>support</Type> 
    99  </Minimap> 
    1010  <UnitAI> 
    11     <DefaultStance>noncombat</DefaultStance> 
     11    <DefaultStance>passive</DefaultStance> 
    1212  </UnitAI> 
    1313  <Cost> 
    1414    <BuildTime>15</BuildTime>