This Trac instance is not used for development anymore!

We migrated our development workflow to git and Gitea.
To test the future redirection, replace trac by ariadne in the page URL.

Changeset 9657 for ps


Ignore:
Timestamp:
06/24/11 14:35:15 (14 years ago)
Author:
philip
Message:

Attempt minor improvements to stances code (see #865).
Don't chase units that are no longer visible, beyond where they were last seen (fixes #595).

Location:
ps/trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • ps/trunk/binaries/data/mods/public/gui/session/unit_commands.js

    r9645 r9657  
    275275        else if (guiName == "Stance")
    276276        {
    277             var stanceSelected = Engine.GuiInterfaceCall("StanceSelected", {
     277            var stanceSelected = Engine.GuiInterfaceCall("IsStanceSelected", {
    278278                "ents": g_Selection.toList(),
    279279                "stance": item
     
    386386        var formations = getEntityFormationsList(entState);
    387387        if (isUnit(entState) && !isAnimal(entState) && !entState.garrisonHolder && formations.length)
     388        {
    388389            setupUnitPanel("Formation", usedPanels, entState, formations,
    389390                function (item) { performFormation(entState.id, item); } );
    390 
    391         var stances = ["violent","aggressive","passive","defensive","stand"];
     391        }
     392
     393        // TODO: probably should load the stance list from a data file,
     394        // and/or vary depending on what units are selected
     395        var stances = ["violent", "aggressive", "passive", "defensive", "stand"];
    392396        if (isUnit(entState) && !isAnimal(entState) && !entState.garrisonHolder && stances.length)
     397        {
    393398            setupUnitPanel("Stance", usedPanels, entState, stances,
    394399                function (item) { performStance(entState.id, item); } );
     400        }
    395401
    396402        if (entState.buildEntities && entState.buildEntities.length)
  • ps/trunk/binaries/data/mods/public/simulation/components/GuiInterface.js

    r9631 r9657  
    299299};
    300300
    301 GuiInterface.prototype.StanceSelected = function(player, data)
     301GuiInterface.prototype.IsStanceSelected = function(player, data)
    302302{
    303303    for each (var ent in data.ents)
     
    568568
    569569    "CanMoveEntsIntoFormation": 1,
    570     "StanceSelected": 1,
     570    "IsStanceSelected": 1,
    571571
    572572    "SetSelectionHighlight": 1,
  • ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js

    r9632 r9657  
    66    "<element name='DefaultStance'>" +
    77        "<choice>" +
     8            "<value>violent</value>" +
    89            "<value>aggressive</value>" +
    910            "<value>defensive</value>" +
    1011            "<value>passive</value>" +
     12            "<value>stand</value>" +
    1113        "</choice>" +
    1214    "</element>" +
     
    4648    "</optional>";
    4749
    48 // Very basic stance support (currently just for test maps where we don't want
    49 // everyone killing each other immediately after loading, and for female citizens)
     50// Unit stances.
    5051// There some targeting options:
    5152//   targetVisibleEnemies: anything in vision range is a viable target
     
    5455//   respondFlee: run away
    5556//   respondChase: start chasing after the enemy
    56 // TODO: maybe add respondStandGround, respondHoldGround (allow chasing a short distance then return),
    57 // targetAggressiveEnemies (don't worry about lone scouts, do worry around armies slaughtering
    58 // the guy standing next to you), etc.
    59 // TODO: even this limited version isn't implemented properly yet (e.g. it can't handle
    60 // dynamic stance changes).
     57//   respondChaseBeyondVision: start chasing, and don't stop even if it's out
     58//     of this unit's vision range (though still visible to the player)
     59//   respondStandGround: attack enemy but don't move at all
     60//   respondHoldGround: attack enemy but don't move far from current position
     61// TODO: maybe add targetAggressiveEnemies (don't worry about lone scouts,
     62// do worry around armies slaughtering the guy standing next to you), etc.
    6163var g_Stances = {
    6264    "violent": {
     
    6567        respondFlee: false,
    6668        respondChase: true,
    67         respondStandGround : false,
    68         respondHoldGround : false,
    69         respondChaseToHell : true,
     69        respondChaseBeyondVision: true,
     70        respondStandGround: false,
     71        respondHoldGround: false,
    7072    },
    7173    "aggressive": {
     
    7476        respondFlee: false,
    7577        respondChase: true,
    76         respondStandGround : false,
    77         respondHoldGround : false,
     78        respondChaseBeyondVision: false,
     79        respondStandGround: false,
     80        respondHoldGround: false,
    7881    },
    7982    "defensive": {
     
    8285        respondFlee: false,
    8386        respondChase: false,
    84         respondStandGround : false,
    85         respondHoldGround : true,
     87        respondChaseBeyondVision: false,
     88        respondStandGround: false,
     89        respondHoldGround: true,
    8690    },
    8791    "passive": {
     
    9094        respondFlee: true,
    9195        respondChase: false,
    92         respondStandGround : false,
    93         respondHoldGround : false,
     96        respondChaseBeyondVision: false,
     97        respondStandGround: false,
     98        respondHoldGround: false,
    9499    },
    95100    "stand": {
     
    98103        respondFlee: false,
    99104        respondChase: false,
    100         respondStandGround : true,
    101         respondHoldGround : false,
     105        respondChaseBeyondVision: false,
     106        respondStandGround: true,
     107        respondHoldGround: false,
    102108    },
    103109};
     
    136142    "EntityRenamed": function(msg) {
    137143        // ignore
    138     },
    139 
    140     "StanceChanged": function(msg) {
    141         if (this.StanceSpecificQuery(this.stance,true))
    142             return;
    143144    },
    144145
     
    247248        this.attackType = type;
    248249
     250        // If we are already at the target, try attacking it from here
    249251        if (this.CheckTargetRange(this.order.data.target, IID_Attack, this.attackType))
    250252        {
    251             // We are already at the target
    252             // so try attacking it from here.
    253253            this.StopMoving();
    254254            if (this.IsAnimal())
     
    256256            else
    257257                this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING");
    258         }
     258            return;
     259        }
     260
     261        // If we can't reach the target, but are standing ground,
     262        // then abandon this attack order
     263        if (this.GetStance().respondStandGround && !this.order.data.force)
     264        {
     265            this.FinishOrder();
     266            return;
     267        }
     268
    259269        // Try to move within attack range
    260         else if ((!this.GetStance().respondStandGround || this.order.data.force) && this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType))
     270        if (this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType))
    261271        {
    262272            // We've started walking to the given point
     
    265275            else
    266276                this.SetNextState("INDIVIDUAL.COMBAT.APPROACHING");
    267         }
    268         else
    269             this.FinishOrder();
     277            return;
     278        }
     279
     280        // We can't reach the target, and can't move towards it,
     281        // so abandon this attack order
     282        this.FinishOrder();
    270283    },
    271284
     
    515528                // so immediately check whether there's anybody nearby to attack.
    516529                // (If anyone approaches later, it'll be handled via LosRangeUpdate.)
    517                 if (this.StanceSpecificQuery(this.stance))
    518                     return true;
     530                if (this.FindNewTargets())
     531                    return true; // (abort the FSM transition since we may have already switched state)
    519532
    520533                // Nobody to attack - stay in idle
     
    573586                }
    574587            },
    575 
    576             "StanceChanged": function(msg) {
    577                 if (this.StanceSpecificQuery(this.stance))
    578                     return;
    579             },
    580588        },
    581589
     
    635643
    636644                "Timer": function(msg) {
    637                     if (!this.ChaseTargetedEntity(this.order.data.target, this.order.data.force))
     645                    if (this.ShouldAbandonChase(this.order.data.target, this.order.data.force))
    638646                    {
    639647                        this.StopMoving();
    640648                        this.FinishOrder();
    641649
    642                         // we return to our position
     650                        // Return to our original position
    643651                        if (this.GetStance().respondHoldGround)
    644652                            this.WalkToHeldPosition();
     
    692700                        }
    693701
    694                         if (this.ChaseTargetedEntity(this.order.data.target))
     702                        // Can't reach it - try to chase after it
     703                        if (this.ShouldChaseTargetedEntity(this.order.data.target, this.order.data.force))
    695704                        {
    696705                            if (this.MoveToTargetRange(this.order.data.target, IID_Attack, this.attackType))
     
    707716                        return;
    708717
    709                     // see if we can switch to a new nearby enemy
    710                     if (this.StanceSpecificQuery(this.stance, true))
     718                    // See if we can switch to a new nearby enemy
     719                    if (this.FindNewTargets())
    711720                        return;
    712721
    713                     // we return to our position
     722                    // Return to our original position
    714723                    if (this.GetStance().respondHoldGround)
    715                     {
    716                         if (this.WalkToHeldPosition())
    717                             return;
    718                     }
     724                        this.WalkToHeldPosition();
    719725                },
    720726
     
    734740
    735741                "Timer": function(msg) {
    736                     if (!this.ChaseTargetedEntity(this.order.data.target))
     742                    if (this.ShouldAbandonChase(this.order.data.target, this.order.data.force))
    737743                    {
    738744                        this.StopMoving();
    739745                        this.FinishOrder();
    740746
    741                         // we return to our position
     747                        // Return to our original position
    742748                        if (this.GetStance().respondHoldGround)
    743749                            this.WalkToHeldPosition();
     
    13661372UnitAI.prototype.OnOwnershipChanged = function(msg)
    13671373{
    1368     this.SetupRangeQuery(msg.to);
     1374    this.SetupRangeQuery();
    13691375};
    13701376
     
    13831389// which can be attacked.
    13841390// This should be called whenever our ownership changes.
    1385 UnitAI.prototype.SetupRangeQuery = function(owner)
    1386 {
     1391UnitAI.prototype.SetupRangeQuery = function()
     1392{
     1393    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     1394    var owner = cmpOwnership.GetOwner();
     1395
    13871396    var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    13881397    var playerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     
    14111420    }
    14121421
    1413     var range = this.StanceSpecificRange();
    1414 
    1415     this.losRangeQuery = rangeMan.CreateActiveQuery(this.entity, 0, range.max, players, IID_DamageReceiver);
     1422    var range = this.GetQueryRange();
     1423
     1424    this.losRangeQuery = rangeMan.CreateActiveQuery(this.entity, range.min, range.max, players, IID_DamageReceiver);
    14161425    rangeMan.EnableActiveQuery(this.losRangeQuery);
    1417 }
     1426};
    14181427
    14191428//// FSM linkage functions ////
     
    18621871UnitAI.prototype.MoveToTarget = function(target)
    18631872{
     1873    if (!this.CheckTargetVisible(target))
     1874        return false;
     1875
    18641876    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
    18651877    return cmpUnitMotion.MoveToTargetRange(target, 0, 0);
     
    18681880UnitAI.prototype.MoveToTargetRange = function(target, iid, type)
    18691881{
     1882    if (!this.CheckTargetVisible(target))
     1883        return false;
     1884
    18701885    var cmpRanged = Engine.QueryInterface(this.entity, iid);
    18711886    var range = cmpRanged.GetRange(type);
     
    18771892UnitAI.prototype.MoveToTargetRangeExplicit = function(target, min, max)
    18781893{
     1894    if (!this.CheckTargetVisible(target))
     1895        return false;
     1896
    18791897    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
    18801898    return cmpUnitMotion.MoveToTargetRange(target, min, max);
     
    18881906    var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
    18891907    return cmpUnitMotion.IsInTargetRange(target, range.min, range.max);
     1908};
     1909
     1910/**
     1911 * Returns true if the target entity is visible through the FoW/SoD.
     1912 */
     1913UnitAI.prototype.CheckTargetVisible = function(target)
     1914{
     1915    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     1916    if (!cmpOwnership)
     1917        return false;
     1918
     1919    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     1920    if (!cmpRangeManager)
     1921        return false;
     1922
     1923    if (cmpRangeManager.GetLosVisibility(target, cmpOwnership.GetOwner()) == "hidden")
     1924        return false;
     1925
     1926    // Either visible directly, or visible in fog
     1927    return true;
    18901928};
    18911929
     
    19431981
    19441982    return distance < range;
    1945 }
     1983};
    19461984
    19471985UnitAI.prototype.GetBestAttack = function()
     
    19722010
    19732011/**
    1974  * Try to find one of the given entities which can be attacked,
    1975  * within zone.
     2012 * Try to find one of the given entities which can be attacked
     2013 * and which is close to the hold position, and start attacking it.
    19762014 * Returns true if it found something to attack.
    19772015 */
     
    20182056};
    20192057
     2058/**
     2059 * Returns true if we should stop following the target entity.
     2060 */
     2061UnitAI.prototype.ShouldAbandonChase = function(target, force)
     2062{
     2063    // Stop if we're in hold-ground mode and it's too far from the holding point
     2064    if (this.GetStance().respondHoldGround)
     2065    {
     2066        if (!this.CheckTargetDistanceFromHeldPosition(target, this.attackType))
     2067            return true;
     2068    }
     2069
     2070    // Stop if it's left our vision range, unless we're especially persistent
     2071    if (!force && !this.GetStance().respondChaseBeyondVision)
     2072    {
     2073        if (!this.CheckTargetIsInVisionRange(target))
     2074            return true;
     2075    }
     2076
     2077    // (Note that CCmpUnitMotion will detect if the target is lost in FoW,
     2078    // and will continue moving to its last seen position and then stop)
     2079
     2080    return false;
     2081};
     2082
    20202083/*
    2021  * Try to chase targeted entity given our current stance
     2084 * Returns whether we should chase the targeted entity,
     2085 * given our current stance.
    20222086 */
    2023 UnitAI.prototype.ChaseTargetedEntity = function(ent, force)
    2024 {
    2025   if (this.GetStance().respondChase)
    2026   {
    2027     if (this.CheckTargetIsInVisionRange(ent))
    2028     {
    2029       return true;
    2030     }
    2031   }
    2032 
    2033   if (this.GetStance().respondChaseToHell || force)
    2034   {
    2035     //if ( TODO : Check ent is not in Shroud of Darkness)
    2036     {
    2037       return true;
    2038     }
    2039   }
    2040 
    2041   if (this.GetStance().respondHoldGround)
    2042   {
    2043     if (this.CheckTargetDistanceFromHeldPosition(ent, this.attackType))
    2044     {
    2045       return true;
    2046     }
    2047   }
    2048 
    2049   return false;
    2050 
    2051 }
     2087UnitAI.prototype.ShouldChaseTargetedEntity = function(target, force)
     2088{
     2089    if (this.GetStance().respondChase)
     2090        return true;
     2091
     2092    if (force)
     2093        return true;
     2094
     2095    return false;
     2096};
    20522097
    20532098//// External interface functions ////
     
    22902335        this.StopMoving();
    22912336
    2292     UnitFsm.ProcessMessage(this, {"type": "StanceChanged", "stance": stance});
     2337    // Reset the range query, since the range depends on stance
     2338    this.SetupRangeQuery();
    22932339}
    22942340
    2295 UnitAI.prototype.StanceSpecificQuery = function(stance, disable)
    2296 {
    2297     if (!g_Stances[stance])
    2298     {
    2299         error("UnitAI: Setting to invalid stance '"+stance+"'");
    2300         return false;
    2301     }
    2302 
     2341/**
     2342 * Resets losRangeQuery, and if there are some targets in range that we can
     2343 * attack then we start attacking and this returns true; otherwise, returns false.
     2344 */
     2345UnitAI.prototype.FindNewTargets = function()
     2346{
    23032347    if (!this.losRangeQuery)
    23042348        return false;
    23052349
    2306     var range = this.StanceSpecificRange();
    2307 
    23082350    var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    2309     var ents = rangeMan.ModifyRangeActiveQuery(this.losRangeQuery, range.min, range.max);
    2310 
    2311     if (disable && !this.IsAnimal())
    2312         rangeMan.DisableActiveQuery(this.losRangeQuery);
     2351    var ents = rangeMan.ResetActiveQuery(this.losRangeQuery);
     2352
     2353    if (!this.GetStance().targetVisibleEnemies)
     2354        return false;
    23132355
    23142356    return this.RespondToTargetedEntities(ents);
    23152357};
    23162358
    2317 UnitAI.prototype.StanceSpecificRange = function()
     2359UnitAI.prototype.GetQueryRange = function()
    23182360{
    23192361    var ret = { "min": 0, "max": 0 };
  • ps/trunk/source/simulation2/components/CCmpRangeManager.cpp

    r9631 r9657  
    458458
    459459        return (tag_t)id;
    460     }
    461 
    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);
    477460    }
    478461
  • ps/trunk/source/simulation2/components/CCmpUnitMotion.cpp

    r9605 r9657  
    2323#include "ICmpObstruction.h"
    2424#include "ICmpObstructionManager.h"
     25#include "ICmpOwnership.h"
    2526#include "ICmpPosition.h"
    2627#include "ICmpPathfinder.h"
     28#include "ICmpRangeManager.h"
    2729#include "simulation2/MessageTypes.h"
    2830#include "simulation2/helpers/Geometry.h"
     
    10041006        return false;
    10051007
     1008    // Fail if the target is no longer visible to this entity's owner
     1009    // (in which case we'll continue moving to its last known location,
     1010    // unless it comes back into view before we reach that location)
     1011    CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), GetEntityId());
     1012    if (!cmpOwnership.null())
     1013    {
     1014        CmpPtr<ICmpRangeManager> cmpRangeManager(GetSimContext(), SYSTEM_ENTITY);
     1015        if (!cmpRangeManager.null())
     1016        {
     1017            if (cmpRangeManager->GetLosVisibility(m_TargetEntity, cmpOwnership->GetOwner()) == ICmpRangeManager::VIS_HIDDEN)
     1018                return false;
     1019        }
     1020    }
     1021
    10061022    // The target moved and we need to update our current path;
    10071023    // change the goal here and expect our caller to start the path request
  • ps/trunk/source/simulation2/components/ICmpRangeManager.cpp

    r9631 r9657  
    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)
    39 DEFINE_INTERFACE_METHOD_3("ModifyRangeActiveQuery", std::vector<entity_id_t>, ICmpRangeManager, ModifyRangeActiveQuery, ICmpRangeManager::tag_t, entity_pos_t, entity_pos_t)
    4039DEFINE_INTERFACE_METHOD_1("DestroyActiveQuery", void, ICmpRangeManager, DestroyActiveQuery, ICmpRangeManager::tag_t)
    4140DEFINE_INTERFACE_METHOD_1("EnableActiveQuery", void, ICmpRangeManager, EnableActiveQuery, ICmpRangeManager::tag_t)
  • ps/trunk/source/simulation2/components/ICmpRangeManager.h

    r9631 r9657  
    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     /**
    115105     * Destroy a query and clean up resources. This must be called when an entity no longer needs its
    116106     * query (e.g. when the entity is destroyed).
Note: See TracChangeset for help on using the changeset viewer.