Line data Source code
1 : function AttackDetection() {} 2 : 3 0 : AttackDetection.prototype.Schema = 4 : "<a:help>Detects incoming attacks.</a:help>" + 5 : "<a:example/>" + 6 : "<element name='SuppressionTransferRange' a:help='Any attacks within this range in meters will replace the previous attack suppression'>" + 7 : "<ref name='positiveDecimal'/>" + 8 : "</element>" + 9 : "<element name='SuppressionRange' a:help='Other attacks within this range in meters will not be registered'>" + 10 : "<ref name='positiveDecimal'/>" + 11 : "</element>" + 12 : "<element name='SuppressionTime' a:help='Other attacks within this time in milliseconds will not be registered'>" + 13 : "<data type='positiveInteger'/>" + 14 : "</element>"; 15 : 16 0 : AttackDetection.prototype.Init = function() 17 : { 18 0 : this.suppressionTime = +this.template.SuppressionTime; 19 : // Use squared distance to avoid sqrts 20 0 : this.suppressionTransferRangeSquared = +this.template.SuppressionTransferRange * +this.template.SuppressionTransferRange; 21 0 : this.suppressionRangeSquared = +this.template.SuppressionRange * +this.template.SuppressionRange; 22 0 : this.suppressedList = []; 23 : }; 24 : 25 0 : AttackDetection.prototype.ActivateTimer = function() 26 : { 27 0 : Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).SetTimeout(this.entity, IID_AttackDetection, "HandleTimeout", this.suppressionTime); 28 : }; 29 : 30 0 : AttackDetection.prototype.AddSuppression = function(event) 31 : { 32 0 : this.suppressedList.push(event); 33 0 : this.ActivateTimer(); 34 : }; 35 : 36 0 : AttackDetection.prototype.UpdateSuppressionEvent = function(index, event) 37 : { 38 0 : this.suppressedList[index] = event; 39 0 : this.ActivateTimer(); 40 : }; 41 : 42 : // Message handlers 43 : 44 0 : AttackDetection.prototype.OnGlobalAttacked = function(msg) 45 : { 46 0 : var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); 47 0 : var cmpOwnership = Engine.QueryInterface(msg.target, IID_Ownership); 48 0 : if (cmpOwnership.GetOwner() != cmpPlayer.GetPlayerID()) 49 0 : return; 50 : 51 0 : Engine.PostMessage(msg.target, MT_MinimapPing); 52 : 53 0 : this.AttackAlert(msg.target, msg.attacker, msg.type, msg.attackerOwner); 54 : }; 55 : 56 : // External interface 57 : 58 0 : AttackDetection.prototype.AttackAlert = function(target, attacker, type, attackerOwner) 59 : { 60 0 : let playerID = Engine.QueryInterface(this.entity, IID_Player).GetPlayerID(); 61 : 62 : // Don't register attacks dealt against other players 63 0 : if (Engine.QueryInterface(target, IID_Ownership).GetOwner() != playerID) 64 0 : return; 65 : 66 0 : let cmpAttackerOwnership = Engine.QueryInterface(attacker, IID_Ownership); 67 0 : let atkOwner = cmpAttackerOwnership && cmpAttackerOwnership.GetOwner() != INVALID_PLAYER ? cmpAttackerOwnership.GetOwner() : attackerOwner; 68 : // Don't register attacks dealt by myself 69 0 : if (atkOwner == playerID) 70 0 : return; 71 : 72 : // Since livestock can be attacked/gathered by other players 73 : // and generally are not so valuable as other units/buildings, 74 : // we have a lower priority notification for it, which can be 75 : // overriden by a regular one. 76 0 : var cmpTargetIdentity = Engine.QueryInterface(target, IID_Identity); 77 0 : var targetIsDomesticAnimal = cmpTargetIdentity && cmpTargetIdentity.HasClass("Animal") && cmpTargetIdentity.HasClass("Domestic"); 78 : 79 0 : var cmpPosition = Engine.QueryInterface(target, IID_Position); 80 0 : if (!cmpPosition || !cmpPosition.IsInWorld()) 81 0 : return; 82 0 : var event = { 83 : "target": target, 84 : "position": cmpPosition.GetPosition(), 85 : "time": Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).GetTime(), 86 : "targetIsDomesticAnimal": targetIsDomesticAnimal 87 : }; 88 : 89 : // If we already have a low priority livestock event in suppressed list, 90 : // and now a more important target is attacked, we want to upgrade the 91 : // suppressed event and send the new notification 92 0 : var isPriorityIncreased = false; 93 0 : for (var i = 0; i < this.suppressedList.length; ++i) 94 : { 95 0 : var element = this.suppressedList[i]; 96 : 97 : // If the new attack is within suppression distance of this element, 98 : // then check if the element should be updated and return 99 0 : var dist = event.position.horizDistanceToSquared(element.position); 100 0 : if (dist >= this.suppressionRangeSquared) 101 0 : continue; 102 : 103 0 : isPriorityIncreased = element.targetIsDomesticAnimal && !targetIsDomesticAnimal; 104 0 : var isPriorityDescreased = !element.targetIsDomesticAnimal && targetIsDomesticAnimal; 105 : 106 0 : if (isPriorityIncreased || 107 : (!isPriorityDescreased && dist < this.suppressionTransferRangeSquared)) 108 0 : this.UpdateSuppressionEvent(i, event); 109 : 110 : // If priority has increased, exit the loop to send the upgraded notification below 111 0 : if (isPriorityIncreased) 112 0 : break; 113 : 114 0 : return; 115 : } 116 : 117 : // If priority has increased for an existing event, then we already have it 118 : // in the suppression list 119 0 : if (!isPriorityIncreased) 120 0 : this.AddSuppression(event); 121 : 122 0 : Engine.PostMessage(this.entity, MT_AttackDetected, { "player": playerID, "event": event }); 123 0 : Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface).PushNotification({ 124 : "type": "attack", 125 : "target": target, 126 : "players": [playerID], 127 : "attacker": atkOwner, 128 : "position": event.position, 129 : "targetIsDomesticAnimal": targetIsDomesticAnimal 130 : }); 131 : 132 0 : let soundGroup = "attacked"; 133 0 : if (type == "capture") 134 0 : soundGroup += "_capture"; 135 : 136 0 : if (attackerOwner === 0) 137 0 : soundGroup += "_gaia"; 138 : 139 0 : PlaySound(soundGroup, target); 140 : }; 141 : 142 0 : AttackDetection.prototype.GetSuppressionTime = function() 143 : { 144 0 : return this.suppressionTime; 145 : }; 146 : 147 0 : AttackDetection.prototype.HandleTimeout = function() 148 : { 149 0 : var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 150 0 : var now = cmpTimer.GetTime(); 151 0 : for (var i = 0; i < this.suppressedList.length; ++i) 152 : { 153 0 : var event = this.suppressedList[i]; 154 : 155 : // Check if this event has timed out 156 0 : if (now - event.time >= this.suppressionTime) 157 : { 158 0 : this.suppressedList.splice(i, 1); 159 0 : return; 160 : } 161 : } 162 : }; 163 : 164 0 : AttackDetection.prototype.GetIncomingAttacks = function() 165 : { 166 0 : return this.suppressedList; 167 : }; 168 : 169 0 : Engine.RegisterComponentType(IID_AttackDetection, "AttackDetection", AttackDetection);