Ticket #1551: gate-close-3.patch

File gate-close-3.patch, 12.9 KB (added by Deiz, 12 years ago)
  • binaries/data/mods/public/simulation/components/Foundation.js

     
    109109            // Otherwise enable this obstruction so it blocks any further
    110110            // units, and continue building.
    111111
    112             var collisions = cmpObstruction.GetConstructionCollisions();
     112            var collisions = cmpObstruction.GetEntityCollisions(true, true);
    113113            if (collisions.length)
    114114            {
    115115                for each (var ent in collisions)
  • binaries/data/mods/public/simulation/components/Gate.js

     
    1414 */
    1515Gate.prototype.Init = function()
    1616{
    17     this.units = [];
     17    this.allies = [];
    1818    this.opened = false;
    1919    this.locked = false;
    2020};
     
    3939    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    4040    if (this.unitsQuery)
    4141        cmpRangeManager.DestroyActiveQuery(this.unitsQuery);
     42
     43    // Cancel the closing-blocked timer if it's running.
     44    if (this.timer)
     45    {
     46        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     47        cmpTimer.CancelTimer(this.timer);
     48        this.timer = undefined;
     49    }
    4250};
    4351
    4452/**
     
    4856{
    4957    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    5058    var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     59    var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(owner), IID_Player);
    5160    if (this.unitsQuery)
    5261        cmpRangeManager.DestroyActiveQuery(this.unitsQuery);
    53     var players = []
    5462
    55     var numPlayers = cmpPlayerManager.GetNumPlayers();
     63    // Only allied units can make the gate open.
     64    var players = [];
     65    for (var i = 0; i < cmpPlayer.GetDiplomacy().length; ++i)
     66        if (cmpPlayer.IsAlly(i))
     67            players.push(i);
    5668
    57     // Ignore gaia units
    58     for (var i = 1; i < numPlayers; ++i)
    59         players.push(i);
    60 
    6169    var range = this.GetPassRange();
    6270    if (range > 0)
    6371    {
     
    7785
    7886    if (msg.added.length > 0)
    7987        for each (var entity in msg.added)
    80             this.units.push(entity);
     88            this.allies.push(entity);
    8189
    8290    if (msg.removed.length > 0)
    8391        for each (var entity in msg.removed)
    84             this.units.splice(this.units.indexOf(entity), 1);
     92            this.allies.splice(this.allies.indexOf(entity), 1);
    8593
    8694    this.OperateGate();
    8795};
     
    96104
    97105/**
    98106 * Attempt to open or close the gate.
    99  * An ally unit must be in range to open the gate, but there must be
    100  *  no units (including enemies) in range to close it again.
     107 * An ally must be in range to open the gate, but an unlocked gate will only close
     108 * if there are no allies in range and no units are inside the gate's obstruction.
    101109 */
    102110Gate.prototype.OperateGate = function()
    103111{
    104     if (this.opened == true )
     112    // Cancel the closing-blocked timer if it's running.
     113    if (this.timer)
    105114    {
    106         // If no units are in range, close the gate
    107         if (this.units.length == 0)
    108             this.CloseGate();
     115        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     116        cmpTimer.CancelTimer(this.timer);
     117        this.timer = undefined;
    109118    }
    110     else
    111     {
    112         // If one units in range is owned by an ally, open the gate
    113         for each (var ent in this.units)
    114         {
    115             if (IsOwnedByAllyOfEntity(this.entity, ent))
    116             {
    117                 this.OpenGate();
    118                 break;
    119             }
    120         }
    121     }
     119
     120    if (this.opened && (this.allies.length == 0 || this.locked))
     121        this.CloseGate();
     122    else if (!this.opened && this.allies.length)
     123        this.OpenGate();
    122124};
    123125
    124126Gate.prototype.IsLocked = function()
     
    141143            return;
    142144        cmpObstruction.SetDisableBlockMovementPathfinding(false, false, 0);
    143145    }
     146    else
     147        this.OperateGate();
    144148
    145149    // TODO: Possibly move the lock/unlock sounds to UI? Needs testing
    146150    PlaySound("gate_locked", this.entity);
     
    157161        return;
    158162
    159163    // Disable 'block pathfinding'
    160     cmpObstruction.SetDisableBlockMovementPathfinding(false, true, 0);
     164    cmpObstruction.SetDisableBlockMovementPathfinding(this.opened, true, 0);
    161165    this.locked = false;
    162166
    163167    // TODO: Possibly move the lock/unlock sounds to UI? Needs testing
     
    194198
    195199/**
    196200 * Close the gate, with sound and animation.
     201 *
     202 * The gate may fail to close due to unit obstruction. If this occurs, the
     203 * gate will start a timer and attempt to close on each simulation update.
    197204 */
    198205Gate.prototype.CloseGate = function()
    199206{
     
    201208    if (!cmpObstruction)
    202209        return;
    203210
     211    // The gate can't be closed if there are entities colliding with it.
     212    var collisions = cmpObstruction.GetEntityCollisions(false, true);
     213    if (collisions.length)
     214    {       
     215        if (!this.timer)
     216        {
     217            // Set an "instant" timer which will run on the next simulation turn.
     218            var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     219            this.timer = cmpTimer.SetTimeout(this.entity, IID_Gate, "OperateGate", 0, {});
     220        }
     221        return;
     222    }
     223
    204224    // If we ordered the gate to be locked, enable 'block movement' and 'block pathfinding'
    205225    if (this.locked)
    206226        cmpObstruction.SetDisableBlockMovementPathfinding(false, false, 0);
  • source/simulation2/components/CCmpObstruction.cpp

     
    549549            return !cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, NULL);
    550550    }
    551551
    552     virtual std::vector<entity_id_t> GetConstructionCollisions()
     552    virtual std::vector<entity_id_t> GetEntityCollisions(bool checkStructures, bool checkUnits)
    553553    {
    554554        std::vector<entity_id_t> ret;
    555555
     
    569569        // required precondition to use SkipControlGroupsRequireFlagObstructionFilter
    570570        if (m_ControlGroup == INVALID_ENTITY)
    571571        {
    572             LOGERROR(L"[CmpObstruction] Cannot test for construction obstructions; primary control group must be valid");
     572            LOGERROR(L"[CmpObstruction] Cannot test for unit or structure obstructions; primary control group must be valid");
    573573            return ret;
    574574        }
    575575
    576         // Ignore collisions within the same control group, or with other non-construction-blocking shapes.
     576        flags_t flags = 0;
     577        bool invertMatch = false;
     578
     579        // There are four 'block' flags: construction, foundation, movement,
     580        // and pathfinding. Structures have all of these flags, while units
     581        // block only movement and construction.
     582
     583        // The 'block construction' flag is common to both units and structures.
     584        if (checkStructures && checkUnits)
     585            flags = ICmpObstructionManager::FLAG_BLOCK_CONSTRUCTION;
     586        // The 'block foundation' flag is exclusive to structures.
     587        else if (checkStructures)
     588            flags = ICmpObstructionManager::FLAG_BLOCK_FOUNDATION;
     589        else if (checkUnits)
     590        {
     591            // As structures block a superset of what units do, matching units
     592            // but not structures means excluding entities that contain any of
     593            // the structure-specific flags (foundation and pathfinding).
     594            invertMatch = true;
     595            flags = ICmpObstructionManager::FLAG_BLOCK_FOUNDATION | ICmpObstructionManager::FLAG_BLOCK_PATHFINDING;
     596        }
     597
     598        // Ignore collisions within the same control group, or with other shapes that don't match the filter.
    577599        // Note that, since the control group for each entity defaults to the entity's ID, this is typically
    578         // equivalent to only ignoring the entity's own shape and other non-construction-blocking shapes.
    579         SkipControlGroupsRequireFlagObstructionFilter filter(m_ControlGroup, m_ControlGroup2,
    580             ICmpObstructionManager::FLAG_BLOCK_CONSTRUCTION);
     600        // equivalent to only ignoring the entity's own shape and other shapes that don't match the filter.
     601        SkipControlGroupsRequireFlagObstructionFilter filter(invertMatch,
     602                m_ControlGroup, m_ControlGroup2, flags);
    581603
    582604        if (m_Type == STATIC)
    583605            cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, &ret);
  • source/simulation2/components/ICmpObstruction.h

     
    5959    virtual bool CheckDuplicateFoundation() = 0;
    6060
    6161    /**
    62      * Returns a list of entities that are colliding with this entity, and that
    63      * are set to block construction.
     62     * Returns a list of entities that are colliding with this entity,
     63     * filtered depending on type of entities that are requested.
    6464     * @return vector of blocking entities
    6565     */
    66     virtual std::vector<entity_id_t> GetConstructionCollisions() = 0;
     66    virtual std::vector<entity_id_t> GetEntityCollisions(bool checkStructures, bool checkUnits) = 0;
    6767
    6868    /**
    6969     * Detects collisions between foundation-blocking entities and
  • source/simulation2/components/ICmpObstruction.cpp

     
    2525DEFINE_INTERFACE_METHOD_0("GetUnitRadius", entity_pos_t, ICmpObstruction, GetUnitRadius)
    2626DEFINE_INTERFACE_METHOD_1("CheckFoundation", bool, ICmpObstruction, CheckFoundation, std::string)
    2727DEFINE_INTERFACE_METHOD_0("CheckDuplicateFoundation", bool, ICmpObstruction, CheckDuplicateFoundation)
    28 DEFINE_INTERFACE_METHOD_0("GetConstructionCollisions", std::vector<entity_id_t>, ICmpObstruction, GetConstructionCollisions)
     28DEFINE_INTERFACE_METHOD_2("GetEntityCollisions", std::vector<entity_id_t>, ICmpObstruction, GetEntityCollisions, bool, bool)
    2929DEFINE_INTERFACE_METHOD_1("SetActive", void, ICmpObstruction, SetActive, bool)
    3030DEFINE_INTERFACE_METHOD_3("SetDisableBlockMovementPathfinding", void, ICmpObstruction, SetDisableBlockMovementPathfinding, bool, bool, int32_t)
    3131DEFINE_INTERFACE_METHOD_0("GetBlockMovementFlag", bool, ICmpObstruction, GetBlockMovementFlag)
  • source/simulation2/components/ICmpObstructionManager.h

     
    355355/**
    356356 * Obstruction test filter that will test only against shapes that:
    357357 *     - are part of neither one of the specified control groups
    358  *     - AND have at least one of the specified flags set.
     358 *     - AND, depending on the value of the 'exclude' argument:
     359 *       - have at least one of the specified flags set.
     360 *       - OR have none of the specified flags set.
    359361 *
    360362 * The first (primary) control group to reject shapes from must be specified and valid. The secondary
    361363 * control group to reject entities from may be set to INVALID_ENTITY to not use it.
     
    365367 */
    366368class SkipControlGroupsRequireFlagObstructionFilter : public IObstructionTestFilter
    367369{
     370    bool m_Exclude;
    368371    entity_id_t m_Group;
    369372    entity_id_t m_Group2;
    370373    flags_t m_Mask;
    371374
    372375public:
     376    SkipControlGroupsRequireFlagObstructionFilter(bool exclude, entity_id_t group1, entity_id_t group2, flags_t mask) :
     377        m_Exclude(exclude), m_Group(group1), m_Group2(group2), m_Mask(mask)
     378    {
     379        Init();
     380    }
     381
    373382    SkipControlGroupsRequireFlagObstructionFilter(entity_id_t group1, entity_id_t group2, flags_t mask) :
    374         m_Group(group1), m_Group2(group2), m_Mask(mask)
     383        m_Exclude(false), m_Group(group1), m_Group2(group2), m_Mask(mask)
    375384    {
     385        Init();
     386    }
     387
     388    virtual bool TestShape(tag_t UNUSED(tag), flags_t flags, entity_id_t group, entity_id_t group2) const
     389    {
     390        // Don't test shapes that share one or more of our control groups.
     391        if (group == m_Group || group == m_Group2 || (group2 != INVALID_ENTITY &&
     392                (group2 == m_Group || group2 == m_Group2)))
     393            return false;
     394
     395        // If m_Exclude is true, don't test against shapes that have any of the
     396        // obstruction flags specified in m_Mask.
     397        if (m_Exclude)
     398            return (flags & m_Mask) == 0;
     399
     400        // Otherwise, only include shapes that match at least one flag in m_Mask.
     401        return (flags & m_Mask) != 0;
     402    }
     403private:
     404    void Init()
     405    {
    376406        // the primary control group to filter out must be valid
    377407        ENSURE(m_Group != INVALID_ENTITY);
    378408
     
    381411        if (m_Group2 == INVALID_ENTITY)
    382412            m_Group2 = m_Group;
    383413    }
    384 
    385     virtual bool TestShape(tag_t UNUSED(tag), flags_t flags, entity_id_t group, entity_id_t group2) const
    386     {
    387         // To be included in the testing, a shape must have at least one of the flags in m_Mask set, and its
    388         // primary control group must be valid and must equal neither our primary nor secondary control group.
    389         bool includeInTesting = ((flags & m_Mask) != 0 && group != m_Group && group != m_Group2);
    390 
    391         // If the shape being tested has a valid secondary control group, exclude it from testing if it
    392         // matches either our primary or secondary control group.
    393         if (group2 != INVALID_ENTITY)
    394             includeInTesting = (includeInTesting && group2 != m_Group && group2 != m_Group2);
    395 
    396         return includeInTesting;
    397     }
    398414};
    399415
    400416/**