Ticket #2577: turrets.diff

File turrets.diff, 16.9 KB (added by sanderd17, 10 years ago)

A proposal

  • binaries/data/mods/public/simulation/components/TurretHolder.js

     
     1function TurretHolder() {}
     2
     3TurretHolder.prototype.Schema =
     4    "<zeroOrMore>" +
     5        "<element>" +
     6            "<anyName/>" +
     7            "<interleave>" +
     8                "<optional>" +
     9                    "<element name='Template'>" +
     10                        "<text/>" +
     11                    "</element>" +
     12                "</optional>" +
     13                "<element name='Offset'>" +
     14                    "<interleave>" +
     15                        "<element name='X'>" +
     16                            "<data type='decimal'/>" +
     17                        "</element>" +
     18                        "<element name='Y'>" +
     19                            "<data type='decimal'/>" +
     20                        "</element>" +
     21                        "<element name='Z'>" +
     22                            "<data type='decimal'/>" +
     23                        "</element>" +
     24                    "</interleave>" +
     25                "</element>" +
     26            "</interleave>" +
     27        "</element>" +
     28    "</zeroOrMore>";
     29
     30TurretHolder.prototype.Init = function()
     31{
     32    this.turrets = {};
     33    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     34    cmpTimer.SetTimeout(this.entity, IID_TurretHolder, "InitReal", 0, null);
     35   
     36};
     37
     38/**
     39 * The real init function. But this should not be loaded in Atlas,
     40 * unless a simulation is started.
     41 */
     42TurretHolder.prototype.InitReal = function(msg)
     43{
     44    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     45    for (var positionCode in this.template)
     46    {
     47        if (!this.template[positionCode].Template)
     48            continue;
     49        var ent = Engine.AddEntity(this.template[positionCode].Template);
     50        var cmpTurretOwnership = Engine.QueryInterface(ent, IID_Ownership);
     51        cmpTurretOwnership.SetOwner(cmpOwnership.GetOwner());
     52        this.AddTurret(positionCode, ent);
     53    }
     54};
     55
     56/**
     57 * Bind an existing entity to a positionCode of this turretHolder
     58 * Some components will be notified that this entity shouldn't move on its own anymore
     59 */
     60TurretHolder.prototype.AddTurret = function(positionCode, ent)
     61{
     62    if (!this.template[positionCode])
     63    {
     64        warn("TurretHolder.JS: entity assigned to unknown turret positionCode");
     65        return;
     66    }
     67    if (this.turrets[positionCode])
     68    {
     69        warn("TurretHolder.JS: entity assigned to occupied turret positionCode");
     70        return;
     71    }
     72
     73    var cmpPosition = Engine.QueryInterface(ent, IID_Position);
     74    if (cmpPosition)
     75    {
     76        var offset = this.template[positionCode].Offset;
     77        var pos = new Vector3D(+offset.X, +offset.Y, +offset.Z);
     78        cmpPosition.SetTurretParent(this.entity, pos);
     79    }
     80
     81    var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
     82    if (cmpUnitAI)
     83        cmpUnitAI.SetTurret(true);
     84    this.turrets[positionCode] = ent;
     85};
     86
     87/**
     88 * Remove a turret from the turret holder
     89 * This does not reset its position, nor does it kill the turret
     90 * It just unbinds it from its turret holder
     91 */
     92TurretHolder.prototype.RemoveTurret = function(positionCode)
     93{
     94    var ent = this.turrets[positionCode];
     95    if (!ent)
     96        return;
     97    var cmpPosition = Engine.QueryInterface(ent, IID_Position);
     98    if (cmpPosition)
     99        cmpPosition.SetTurretParent(INVALID_ENTITY, new Vector3D());
     100    var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
     101    if (cmpUnitAI)
     102        cmpUnitAI.SetTurret(false);
     103    delete this.turrets[positionCode];
     104};
     105
     106TurretHolder.prototype.GetAllTurretPositions = function()
     107{
     108    return Object.keys(this.template);
     109};
     110
     111TurretHolder.prototype.GetOccupiedTurretPositions = function()
     112{
     113    return Object.keys(this.turrets);
     114};
     115
     116TurretHolder.prototype.GetFreeTurretPositions = function()
     117{
     118    var r = [];
     119    for (var positionCode in this.template)
     120        if (!(positionCode in this.turrets))
     121            r.push(positionCode);
     122    return r;
     123};
     124
     125TurretHolder.prototype.GetTurretOnPosition = function(positionCode)
     126{
     127    return this.turrets[positionCode];
     128};
     129
     130TurretHolder.prototype.OnOwnershipChanged = function(msg)
     131{
     132    for each (var ent in this.turrets)
     133    {
     134        var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
     135        cmpOwnership.SetOwner(msg.to);
     136    }
     137}
     138
     139/**
     140 * Also destroy all turrets
     141 */
     142TurretHolder.prototype.OnDestroy = function(msg)
     143{
     144    for each (var ent in this.turrets)
     145        Engine.DestroyEntity(ent);
     146}
     147
     148Engine.RegisterComponentType(IID_TurretHolder, "TurretHolder", TurretHolder);
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    179179    // Called when being told to walk as part of a formation
    180180    "Order.FormationWalk": function(msg) {
    181181        // Let players move captured domestic animals around
    182         if (this.IsAnimal() && !this.IsDomestic())
     182        if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
    183183        {
    184184            this.FinishOrder();
    185185            return;
     
    207207    "Order.LeaveFoundation": function(msg) {
    208208        // If foundation is not ally of entity, or if entity is unpacked siege,
    209209        // ignore the order
    210         if (!IsOwnedByAllyOfEntity(this.entity, msg.data.target) || this.IsPacking() || this.CanPack())
     210        if (!IsOwnedByAllyOfEntity(this.entity, msg.data.target) || this.IsPacking() || this.CanPack() || this.IsTurret())
    211211        {
    212212            this.FinishOrder();
    213213            return;
     
    252252
    253253    "Order.Walk": function(msg) {
    254254        // Let players move captured domestic animals around
    255         if (this.IsAnimal() && !this.IsDomestic())
     255        if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
    256256        {
    257257            this.FinishOrder();
    258258            return;
     
    278278
    279279    "Order.WalkAndFight": function(msg) {
    280280        // Let players move captured domestic animals around
    281         if (this.IsAnimal() && !this.IsDomestic())
     281        if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
    282282        {
    283283            this.FinishOrder();
    284284            return;
     
    305305
    306306    "Order.WalkToTarget": function(msg) {
    307307        // Let players move captured domestic animals around
    308         if (this.IsAnimal() && !this.IsDomestic())
     308        if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
    309309        {
    310310            this.FinishOrder();
    311311            return;
     
    486486
    487487        // If we can't reach the target, but are standing ground, then abandon this attack order.
    488488        // Unless we're hunting, that's a special case where we should continue attacking our target.
    489         if (this.GetStance().respondStandGround && !this.order.data.force && !this.order.data.hunting)
     489        if (this.GetStance().respondStandGround && !this.order.data.force && !this.order.data.hunting || this.IsTurret())
    490490        {
    491491            this.FinishOrder();
    492492            return;
     
    685685    },
    686686
    687687    "Order.Garrison": function(msg) {
     688        if (this.IsTurret())
     689        {
     690            this.FinishOrder();
     691            return;
     692        }
    688693        // For packable units:
    689694        // 1. If packed, we can move to the garrison target.
    690695        // 2. If unpacked, we first need to pack, then follow case 1.
     
    12421247        "Order.LeaveFoundation": function(msg) {
    12431248            // If foundation is not ally of entity, or if entity is unpacked siege,
    12441249            // ignore the order
    1245             if (!IsOwnedByAllyOfEntity(this.entity, msg.data.target) || this.IsPacking() || this.CanPack())
     1250            if (!IsOwnedByAllyOfEntity(this.entity, msg.data.target) || this.IsPacking() || this.CanPack() || this.IsTurret())
    12461251            {
    12471252                this.FinishOrder();
    12481253                return;
     
    31193124    this.lastHealed = undefined;
    31203125
    31213126    this.SetStance(this.template.DefaultStance);
     3127    this.SetTurret(false);
    31223128};
    31233129
     3130/**
     3131 * Set the flag to true to use this unit as a turret
     3132 * This means no moving is allowed, only turning
     3133 */
     3134UnitAI.prototype.SetTurret = function(flag)
     3135{
     3136    this.isTurret = flag;
     3137};
     3138
     3139UnitAI.prototype.IsTurret = function()
     3140{
     3141    return this.isTurret;
     3142};
     3143
    31243144UnitAI.prototype.ReactsToAlert = function(level)
    31253145{
    31263146    return this.template.AlertReactiveLevel <= level;
     
    41524172
    41534173UnitAI.prototype.MoveToTargetRange = function(target, iid, type)
    41544174{
    4155     if (!this.CheckTargetVisible(target))
     4175    if (!this.CheckTargetVisible(target) || this.IsTurret())
    41564176        return false;
    41574177
    41584178    var cmpRanged = Engine.QueryInterface(this.entity, iid);
     
    45834603 */
    45844604UnitAI.prototype.ShouldChaseTargetedEntity = function(target, force)
    45854605{
     4606    if (this.IsTurret())
     4607        return false;
     4608
    45864609    // TODO: use special stances instead?
    45874610    var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
    45884611    if (cmpPack)
  • binaries/data/mods/public/simulation/components/interfaces/TurretHolder.js

     
     1Engine.RegisterInterface("TurretHolder");
     2
  • binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml

     
    6969    <Radius>20</Radius>
    7070    <Weight>40000</Weight>
    7171  </TerritoryInfluence>
     72  <TurretHolder>
     73    <tree>
     74      <Template>units/athen_infantry_archer_a</Template>
     75      <Offset>
     76        <X>5</X>
     77        <Y>5</Y>
     78        <Z>5</Z>
     79      </Offset>
     80    </tree>
     81  </TurretHolder>
    7282  <Vision>
    7383    <Range>20</Range>
    7484  </Vision>
  • source/simulation2/components/CCmpPosition.cpp

     
    7575    entity_pos_t m_YOffset, m_LastYOffset;
    7676    bool m_RelativeToGround; // whether m_YOffset is relative to terrain/water plane, or an absolute height
    7777
     78    // when the entity is a turret, only m_RotY is used, and this is the rotation
     79    // relative to the parent entity
    7880    entity_angle_t m_RotX, m_RotY, m_RotZ;
    7981
    8082    player_id_t m_Territory;
    8183
     84    entity_id_t m_TurretParent;
     85    CFixedVector3D m_TurretPosition;
     86    std::set<entity_id_t> m_Turrets;
     87
    8288    // not serialized:
    8389    float m_InterpolatedRotX, m_InterpolatedRotY, m_InterpolatedRotZ;
    8490    float m_LastInterpolatedRotX, m_LastInterpolatedRotZ; // not serialized
     
    140146        m_Territory = INVALID_PLAYER;
    141147
    142148        m_NeedInitialXZRotation = false;
     149        m_TurretParent = INVALID_ENTITY;
     150        m_TurretPosition = CFixedVector3D();
    143151    }
    144152
    145153    virtual void Deinit()
     
    217225            UpdateXZRotation();
    218226    }
    219227
     228    virtual void UpdateTurretPosition()
     229    {
     230        if (m_TurretParent == INVALID_ENTITY)
     231            return;
     232        CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
     233        if (!cmpPosition)
     234        {
     235            LOGERROR(L"Turret with parent without position component");
     236            return;
     237        }
     238        if (!cmpPosition->IsInWorld())
     239            MoveOutOfWorld();
     240        else
     241        {
     242            CFixedVector2D rotatedPosition = CFixedVector2D(m_TurretPosition.X, m_TurretPosition.Z);
     243            rotatedPosition = rotatedPosition.Rotate(cmpPosition->GetRotation().Y);
     244            CFixedVector2D rootPosition = cmpPosition->GetPosition2D();
     245            entity_pos_t x = rootPosition.X + rotatedPosition.X;
     246            entity_pos_t z = rootPosition.Y + rotatedPosition.Y;
     247            if (!m_InWorld || m_X != x || m_Z != z)
     248                MoveTo(x, z);
     249            entity_pos_t y = cmpPosition->GetHeightOffset() + m_TurretPosition.Y;
     250            if (!m_InWorld || m_YOffset != y)
     251                SetHeightOffset(y);
     252        }
     253    }
     254
     255    virtual std::set<entity_id_t>* GetTurrets()
     256    {
     257        return &m_Turrets;
     258    }
     259
     260    virtual void SetTurretParent(entity_id_t id, CFixedVector3D offset)
     261    {
     262        if (m_TurretParent != INVALID_ENTITY)
     263        {
     264            CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
     265            if (cmpPosition)
     266                cmpPosition->GetTurrets()->erase(GetEntityId());
     267        }
     268
     269        m_TurretParent = id;
     270        m_TurretPosition = offset;
     271
     272        if (m_TurretParent != INVALID_ENTITY)
     273        {
     274            CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
     275            if (cmpPosition)
     276                cmpPosition->GetTurrets()->insert(GetEntityId());
     277        }
     278        UpdateTurretPosition();
     279    }
     280
    220281    virtual bool IsInWorld()
    221282    {
    222283        return m_InWorld;
     
    249310    {
    250311        m_X = x;
    251312        m_Z = z;
    252         m_RotY = ry;
    253313       
    254314        if (!m_InWorld)
    255315        {
     
    259319            m_LastYOffset = m_YOffset;
    260320        }
    261321       
    262         AdvertisePositionChanges();
     322        // TurnTo will advertise the position changes
     323        TurnTo(ry);
    263324    }
    264325
    265326    virtual void JumpTo(entity_pos_t x, entity_pos_t z)
     
    392453
    393454    virtual void TurnTo(entity_angle_t y)
    394455    {
     456        if (m_TurretParent != INVALID_ENTITY)
     457        {
     458            CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
     459            if (cmpPosition)
     460                y -= cmpPosition->GetRotation().Y;
     461        }
    395462        m_RotY = y;
    396463
    397464        AdvertisePositionChanges();
     
    399466
    400467    virtual void SetYRotation(entity_angle_t y)
    401468    {
     469        if (m_TurretParent != INVALID_ENTITY)
     470        {
     471            CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
     472            if (cmpPosition)
     473                y -= cmpPosition->GetRotation().Y;
     474        }
    402475        m_RotY = y;
    403476        m_InterpolatedRotY = m_RotY.ToFloat();
    404477
     
    431504
    432505    virtual CFixedVector3D GetRotation()
    433506    {
     507        entity_angle_t y = m_RotY;
     508        if (m_TurretParent != INVALID_ENTITY)
     509        {
     510            CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
     511            if (cmpPosition)
     512                y += cmpPosition->GetRotation().Y;
     513        }
    434514        return CFixedVector3D(m_RotX, m_RotY, m_RotZ);
    435515    }
    436516
     
    461541
    462542    virtual CMatrix3D GetInterpolatedTransform(float frameOffset, bool forceFloating)
    463543    {
     544        if (m_TurretParent != INVALID_ENTITY)
     545        {
     546            CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
     547            if (!cmpPosition)
     548            {
     549                LOGERROR(L"Turret with parent without position component");
     550                CMatrix3D m;
     551                m.SetIdentity();
     552                return m;
     553            }
     554            if (!cmpPosition->IsInWorld())
     555            {
     556                LOGERROR(L"CCmpPosition::GetInterpolatedTransform called on turret entity when IsInWorld is false");
     557                CMatrix3D m;
     558                m.SetIdentity();
     559                return m;
     560            }
     561            else
     562            {
     563                CMatrix3D parentTransformMatrix = cmpPosition->GetInterpolatedTransform(frameOffset, forceFloating);
     564                CMatrix3D ownTransformation = CMatrix3D();
     565                ownTransformation.SetYRotation(m_InterpolatedRotY);
     566                ownTransformation.Translate(-m_TurretPosition.X.ToFloat(), m_TurretPosition.Y.ToFloat(), -m_TurretPosition.Z.ToFloat());
     567                return parentTransformMatrix * ownTransformation;
     568            }
     569        }
    464570        if (!m_InWorld)
    465571        {
    466572            LOGERROR(L"CCmpPosition::GetInterpolatedTransform called on entity when IsInWorld is false");
     
    548654        }
    549655        case MT_TurnStart:
    550656        {
     657           
    551658            m_LastInterpolatedRotX = m_InterpolatedRotX;
    552659            m_LastInterpolatedRotZ = m_InterpolatedRotZ;
    553660
     
    592699private:
    593700    void AdvertisePositionChanges()
    594701    {
     702        for (std::set<entity_id_t>::const_iterator it = m_Turrets.begin(); it != m_Turrets.end(); ++it)
     703        {
     704            CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *it);
     705            if (cmpPosition)
     706                cmpPosition->UpdateTurretPosition();
     707        }
    595708        if (m_InWorld)
    596709        {
    597710            CMessagePositionChanged msg(GetEntityId(), true, m_X, m_Z, m_RotY);
  • source/simulation2/components/ICmpPosition.cpp

     
    2222#include "simulation2/system/InterfaceScripted.h"
    2323
    2424BEGIN_INTERFACE_WRAPPER(Position)
     25DEFINE_INTERFACE_METHOD_2("SetTurretParent", void, ICmpPosition, SetTurretParent, entity_id_t, CFixedVector3D)
    2526DEFINE_INTERFACE_METHOD_0("IsInWorld", bool, ICmpPosition, IsInWorld)
    2627DEFINE_INTERFACE_METHOD_0("MoveOutOfWorld", void, ICmpPosition, MoveOutOfWorld)
    2728DEFINE_INTERFACE_METHOD_2("MoveTo", void, ICmpPosition, MoveTo, entity_pos_t, entity_pos_t)
  • source/simulation2/components/ICmpPosition.h

     
    5959{
    6060public:
    6161    /**
     62     * Set this as a turret of an other entity
     63     */
     64    virtual void SetTurretParent(entity_id_t parent, CFixedVector3D offset) = 0;
     65
     66    /**
     67     * Has to be called to update the simulation position of the turret
     68     */
     69    virtual void UpdateTurretPosition() = 0;
     70
     71    /**
     72     * Get the list of turrets to read or edit
     73     */
     74    virtual std::set<entity_id_t>* GetTurrets() = 0;
     75
     76    /**
    6277     * Returns true if the entity currently exists at a defined position in the world.
    6378     */
    6479    virtual bool IsInWorld() = 0;
  • source/simulation2/components/tests/test_RangeManager.h

     
    4040public:
    4141    DEFAULT_MOCK_COMPONENT()
    4242
     43    virtual void SetTurretParent(entity_id_t UNUSED(id), CFixedVector3D UNUSED(pos)) {}
     44    virtual void UpdateTurretPosition() {}
     45    virtual std::set<entity_id_t>* GetTurrets() { return NULL; }
    4346    virtual bool IsInWorld() { return true; }
    4447    virtual void MoveOutOfWorld() { }
    4548    virtual void MoveTo(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z)) { }