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

File diffStances-29-05-2011.patch, 26.8 KB (added by Badmadblacksad, 13 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>