Ticket #52: Triggers.6.patch
File Triggers.6.patch, 42.0 KB (added by , 10 years ago) |
---|
-
binaries/data/mods/public/simulation/components/BuildingAI.js
146 146 */ 147 147 BuildingAI.prototype.OnRangeUpdate = function(msg) 148 148 { 149 150 149 var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); 151 150 if (!cmpAttack) 152 151 return; -
binaries/data/mods/public/simulation/components/Foundation.js
202 202 // (via CCmpTemplateManager). Now we need to remove that temporary 203 203 // blocker-disabling, so that we'll perform standard unit blocking instead. 204 204 cmpObstruction.SetDisableBlockMovementPathfinding(false, false, -1); 205 206 // Call the related trigger event 207 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 208 cmpTrigger.CallEvent("OnConstructionStarted", {"foundation": this.entity, "template": this.finalTemplateName}); 205 209 } 206 210 207 211 // Switch foundation to scaffold variant -
binaries/data/mods/public/simulation/components/ProductionQueue.js
288 288 "timeTotal": time*1000, 289 289 "timeRemaining": time*1000, 290 290 }); 291 292 // Call the related trigger event 293 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 294 cmpTrigger.CallEvent("OnTrainingQueued", {"playerid": cmpPlayer.GetPlayerID(), "unitTemplate": templateName, "count": count, "metadata": metadata, "trainerEntity": this.entity}); 291 295 } 292 296 else if (type == "technology") 293 297 { … … 324 328 "timeTotal": time*1000, 325 329 "timeRemaining": time*1000, 326 330 }); 331 332 // Call the related trigger event 333 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 334 cmpTrigger.CallEvent("OnResearchQueued", {"playerid": cmpPlayer.GetPlayerID(), "technologyTemplate": templateName, "researcherEntity": this.entity}); 327 335 } 328 336 else 329 337 { -
binaries/data/mods/public/simulation/components/ResourceTrickle.js
57 57 if (cmpPlayer) 58 58 for (var resource in rates) 59 59 cmpPlayer.AddResource(resource, rates[resource]); 60 61 60 }; 62 61 63 62 Engine.RegisterComponentType(IID_ResourceTrickle, "ResourceTrickle", ResourceTrickle); -
binaries/data/mods/public/simulation/components/TechnologyManager.js
286 286 return; 287 287 } 288 288 289 // Call the related trigger event 290 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 291 cmpTrigger.CallEvent("OnResearchFinished", {"researcher": this.entity, "tech": tech}); 292 289 293 var modifiedComponents = {}; 290 294 this.researchedTechs[tech] = template; 291 295 // store the modifications in an easy to access structure -
binaries/data/mods/public/simulation/components/Trigger.js
1 function Trigger() {}; 2 3 Trigger.prototype.Schema = 4 "<a:component type='system'/><empty/>"; 5 6 Trigger.prototype.Init = function() 7 { 8 this.triggerEntities = {}; 9 10 var eventNames = [ 11 "OnEntityTookDamage", 12 "OnEntityKilled", 13 "OnStructureBuilt", 14 "OnConstructionStarted", 15 "OnTrainingFinished", 16 "OnTrainingQueued", 17 "OnResearchFinished", 18 "OnResearchQueued", 19 "OnUnitIssuedOrder", 20 "OnUnitsIssuedOrder" 21 ]; 22 23 // Each event has its own set of actions determined by the map maker. 24 for each (var eventName in eventNames) 25 this["event" + eventName + "Actions"] = []; 26 27 // Special events 28 this.eventOnIntervalData = {}; 29 this.rangeTriggers = {}; 30 31 // We are going to have all of the triggers here to be able to enable/disbale them in runtime. 32 this.triggerEnabled = {}; 33 34 // To prevent the lose of trigger variables after a save, they "should" be defined in "InitTriggers" function, which is the starting point of a trigger script 35 this.DoAfterDelay(0, "InitGame", {}); 36 }; 37 38 Trigger.prototype.InitGame = function() 39 { 40 }; 41 42 Trigger.prototype.RegisterTriggerEntity = function(ref, ent) 43 { 44 if (!this.triggerEntities[ref]) 45 this.triggerEntities[ref] = []; 46 this.triggerEntities[ref].push(ent); 47 }; 48 49 Trigger.prototype.RemoveRegisteredTriggerEntity = function(ref, ent) 50 { 51 if (!this.triggerEntities[ref]) 52 return; 53 var i = this.triggerEntities[ref].indexOf(ent); 54 if (i != -1) 55 this.triggerEntities[ref].splice(i, 1); 56 }; 57 58 Trigger.prototype.GetRegisteredTriggerEntities = function(ref) 59 { 60 return this.triggerEntities[ref] || []; 61 }; 62 63 /** Binds the "action" function to one of the implemented events. 64 * 65 * @param eventName Name of the event (see the list in init) 66 * @param action Functionname of a function available under this object 67 */ 68 Trigger.prototype.RegisterTrigger = function(event, action, enabled) 69 { 70 var eventString = "event" + event + "Actions"; 71 if (this[eventString]) 72 { 73 this[eventString].push(action); 74 this.triggerEnabled[[event, action]] = enabled 75 } 76 else 77 error("Invalid trigger event \"" + event + "\".") 78 }; 79 80 // Disable trigger 81 Trigger.prototype.DisableTrigger = function(event, action) 82 { 83 if (this.triggerEnabled[[event, action]] !== undefined) 84 this.triggerEnabled[[event, action]] = false; 85 else 86 warn("Trigger key doesn't exist:" + event + " & " + action); 87 } 88 89 // Enable trigger 90 Trigger.prototype.EnableTrigger = function(event, action) 91 { 92 if (this.triggerEnabled[[event, action]] !== undefined) 93 this.triggerEnabled[[event, action]] = true; 94 else 95 warn("Trigger key doesn't exist:" + event + " & " + action); 96 } 97 98 /** 99 * This is an event with more than just the key argument, 100 * so we should have a special register function for it. 101 * 102 * @param entity Entity id around which the range is checked 103 * @param data The data is an object containing information for the range query 104 * Some of the data has sendible defaults (mentionned next to the object) 105 * data.players = [1,2,3,...] * list of player ids 106 * data.minRange = 0 * Minimum range for the query 107 * data.maxRange = -1 * Maximum range for the query (-1 = no maximum) 108 * data.requiredComponent = 0 * Required component id the entities will have 109 * data.enabled = false * If the query is enabled by default 110 * data.action * The function to execute on a range update 111 * data.key * The key of this trigger 112 */ 113 Trigger.prototype.RegisterRangeTrigger = function(entity, data) 114 { 115 var cmpTriggerEntity = Engine.QueryInterface(entity, IID_TriggerEntity); 116 if (cmpTriggerEntity) 117 { 118 var tag = cmpTriggerEntity.RegisterRangeTrigger(data) 119 if (data.key) 120 this.rangeTriggers[data.key] = tag; 121 return tag; 122 } 123 warn("Tried to register a range trigger on an entity (" + entity + ") that has no TriggerEntity component."); 124 return null; 125 } 126 127 Trigger.prototype.EnableRangeTrigger = function(key) 128 { 129 if (!this.rangeTriggers[key]) 130 return; 131 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 132 cmpRangeManager.EnableActiveQuery(this.rangeTriggers[key]); 133 }; 134 135 Trigger.prototype.DisableRangeTrigger = function(key) 136 { 137 if (!this.rangeTriggers[key]) 138 return; 139 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 140 cmpRangeManager.DisableActiveQuery(this.rangeTriggers[key]); 141 }; 142 143 /** 144 * This is an event with more than just the key argument, 145 * so we should have a special register function for it. 146 * 147 * @param action Functionname to execute on interval 148 * @param delay How many miliseconds before the first call? 149 * @param interval How many miliseconds between each call? 150 * @param enabled Determines whether if the trigger is active by default 151 */ 152 Trigger.prototype.RegisterOnIntervalTrigger = function(action, delay, interval, enabled) 153 { 154 // If the trigger was active by default, start the update timer 155 if (enabled) 156 { 157 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 158 this.eventOnIntervalData[action] = cmpTimer.SetInterval(this.entity, IID_Trigger, "TimerUpdate", delay, interval, {"action" : action}); 159 } 160 else // If not, save it for future when we activate the trigger 161 this.eventOnIntervalData[action] = [delay, interval, {"action" : action}]; 162 } 163 164 // Interval triggers are different in nature, and should have their own Enable and disable functions 165 Trigger.prototype.DisableIntervalTrigger = function(action) 166 { 167 var timer = this.eventOnIntervalData[action]; 168 // Check if it is active 169 if (typeof (timer) === 'number') 170 { 171 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 172 cmpTimer.CancelTimer(timer); 173 } 174 } 175 176 Trigger.prototype.EnableIntervalTrigger = function(action) 177 { 178 var timer = this.eventOnIntervalData[action]; 179 // Check if it is inactive 180 if (typeof (timer) !== 'number') 181 { 182 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 183 this.eventOnIntervalData[action] = cmpTimer.SetInterval(this.entity, IID_Trigger, "TimerUpdate", timer[0], timer[1], timer[2]); 184 } 185 } 186 187 /** 188 * This function executes the actions bound to the events 189 * It's either called directlty from other simulation scripts, 190 * or from message listeners in this file 191 * 192 * @param event Name of the event (see the list in init) 193 * @param data Data object that will be passed to the actions 194 */ 195 Trigger.prototype.CallEvent = function(event, data) 196 { 197 // A special case, when a unit dies, the range event associated with it should also be removed 198 if (event == "OnEntityKilled") 199 { 200 for (var i in this.eventOnEntityKilledActions) 201 { 202 if (this.triggerEnabled[[event, i]]) // Check if it is enabled 203 this[this.eventOnEntityKilledActions[i]](data); // The data for this one is {"killerEntity": killerEntity, "killedEntity": targetEntity} 204 205 // Remove the "OnUnitFromRangeEntity" triggers which have this entity as their argument 206 for (var j in this.eventOnUnitRangeFromEntityData) 207 if (this.eventOnUnitRangeFromEntityData[j].entity == data.killedEntity) 208 { 209 DisableRangeTrigger(j); 210 delete this.eventOnUnitRangeFromEntityData[j]; 211 } 212 } 213 return; 214 } 215 216 var actions = this["event" + event + "Actions"]; 217 218 if (actions === undefined) 219 { 220 warn("Unknown trigger event:\"" + event + "\"."); 221 return; 222 } 223 224 for (var i in actions) 225 { 226 if (this.triggerEnabled[[event, actions[i]]]) 227 this[this["event" + event + "Actions"][i]](data); 228 } 229 } 230 231 // Handles "OnStructureBuilt" event. 232 Trigger.prototype.OnGlobalConstructionFinished = function(msg) 233 { 234 this.CallEvent("OnStructureBuilt", {"building": msg.newentity}); // The data for this one is {"building": constructedBuilding} 235 } 236 237 // Handles "OnTrainingFinished" event. 238 Trigger.prototype.OnGlobalTrainingFinished = function(msg) 239 { 240 this.CallEvent("OnTrainingFinished", msg); 241 // The data for this one is {"entities": createdEnts, 242 // "owner": cmpOwnership.GetOwner(), 243 // "metadata": metadata} 244 // See function "SpawnUnits" in ProductionQueue for more details 245 } 246 247 // Handle "OnInterval" event. 248 Trigger.prototype.TimerUpdate = function(data, lateness) 249 { 250 this[data.action](lateness); 251 } 252 253 /** 254 * Execute a function after a certain delay 255 * @param time The delay expressed in milleseconds 256 * @param action Name of the action function 257 * @param data Data object that will be passed to the action function 258 */ 259 Trigger.prototype.DoAfterDelay = function(miliseconds, action, data) 260 { 261 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 262 return cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger, "DoAction", miliseconds, {"action": action, "data": data}); 263 } 264 265 /** 266 * Called by the trigger listeners to exucute the actual action. Including sanity checks. 267 */ 268 Trigger.prototype.DoAction = function(msg) 269 { 270 if (this[msg.action]) 271 this[msg.action](msg.data); 272 else 273 error("called a trigger action '" + msg.action + "' that wasn't added to the g_TriggersData global"); 274 } 275 276 Engine.RegisterSystemComponentType(IID_Trigger, "Trigger", Trigger); -
binaries/data/mods/public/simulation/components/TriggerEntity.js
1 function TriggerEntity() {}; 2 3 TriggerEntity.prototype.Schema = 4 "<optional>" + 5 "<element name='EntityReference'>" + 6 "<text/>" + 7 "</element>" + 8 "</optional>"; 9 10 TriggerEntity.prototype.Init = function() 11 { 12 if (this.template && this.template.EntityReference) 13 { 14 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 15 cmpTrigger.RegisterTriggerEntity(this.template.EntityReference, this.entity); 16 } 17 this.rangeTriggers = {}; 18 }; 19 20 TriggerEntity.prototype.OnDestroy = function() 21 { 22 if (this.template && this.template.EntityReference) 23 { 24 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 25 cmpTrigger.RemoveRegisteredTriggerEntity(this.template.EntityReference, this.entity); 26 } 27 }; 28 29 /** 30 * The data is an object containing information for the range query 31 * Some of the data has sendible defaults (mentionned next to the object) 32 * data.players = [1,2,3,...] * list of player ids 33 * data.minRange = 0 * Minimum range for the query 34 * data.maxRange = -1 * Maximum range for the query (-1 = no maximum) 35 * data.requiredComponent = -1 * Required component id the entities will have 36 * data.enabled = false * If the query is enabled by default 37 * data.action * The function to execute on a range update 38 */ 39 TriggerEntity.prototype.RegisterRangeTrigger = function(data) 40 { 41 if (!data.players) 42 { 43 var numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers(); 44 data.players = []; 45 for (var i = 0; i < numPlayers; i++) 46 data.players.push(i); 47 } 48 data.minRange = data.minRange || 0; 49 data.maxRange = data.maxRange || -1; 50 data.requiredComponent = data.requiredComponent || -1; 51 data.currentCollection = []; 52 53 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 54 data.tag = cmpRangeManager.CreateActiveQuery(this.entity, data.minRange, data.maxRange, data.players, data.requiredComponent, cmpRangeManager.GetEntityFlagMask("normal")); 55 56 this.rangeTriggers[data.tag] = data; 57 if (data.enabled) 58 cmpRangeManager.EnableActiveQuery(data.tag); 59 return data.tag; 60 }; 61 62 TriggerEntity.prototype.OnRangeUpdate = function(msg) 63 { 64 var trigger = this.rangeTriggers[msg.tag]; 65 if (!trigger) 66 return; 67 68 for (var ent of msg.removed) 69 { 70 var index = trigger.currentCollection.indexOf(ent); 71 if (index > -1) 72 trigger.currentCollection.splice(index, 1); 73 } 74 75 for each (var entity in msg.added) 76 trigger.currentCollection.push(entity); 77 78 trigger.added = msg.added; 79 trigger.removed = msg.removed; 80 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 81 if (cmpTrigger[trigger.action]) 82 cmpTrigger[trigger.action](trigger); 83 }; 84 85 86 Engine.RegisterComponentType(IID_TriggerEntity, "TriggerEntity", TriggerEntity); -
binaries/data/mods/public/simulation/components/UnitAI.js
199 199 cmpUnitMotion.MoveToFormationOffset(msg.data.target, msg.data.x, msg.data.z); 200 200 201 201 this.SetNextStateAlwaysEntering("FORMATIONMEMBER.WALKING"); 202 203 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 204 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.FormationWalk", "metadata": msg}); 202 205 }, 203 206 204 207 // Special orders: … … 225 228 // We are already at the target, or can't move at all 226 229 this.FinishOrder(); 227 230 } 231 232 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 233 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.LeaveFoundation", "metadata": msg}); 228 234 }, 229 235 230 236 // Individual orders: … … 247 253 this.SetNextState("ANIMAL.IDLE"); 248 254 else 249 255 this.SetNextState("INDIVIDUAL.IDLE"); 250 256 257 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 258 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Stop", "metadata": msg}); 251 259 }, 252 260 253 261 "Order.Walk": function(msg) { … … 258 266 return; 259 267 } 260 268 269 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 270 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Walk", "metadata": msg}); 271 261 272 // For packable units: 262 273 // 1. If packed, we can move. 263 274 // 2. If unpacked, we first need to pack, then follow case 1. … … 284 295 return; 285 296 } 286 297 298 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 299 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.WalkAndFight", "metadata": msg}); 300 287 301 // For packable units: 288 302 // 1. If packed, we can move. 289 303 // 2. If unpacked, we first need to pack, then follow case 1. … … 310 324 this.FinishOrder(); 311 325 return; 312 326 } 313 327 328 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 329 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.WalkToTarget", "metadata": msg, "target": this.order.data.target}); 330 314 331 // For packable units: 315 332 // 1. If packed, we can move. 316 333 // 2. If unpacked, we first need to pack, then follow case 1. … … 369 386 this.StopMoving(); 370 387 this.SetNextState("INDIVIDUAL.PICKUP.LOADING"); 371 388 } 389 390 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 391 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.PickupUnit", "metadata": msg, "target": this.order.data.target}); 372 392 }, 373 393 374 394 "Order.Guard": function(msg) { … … 382 402 this.SetNextState("INDIVIDUAL.GUARD.ESCORTING"); 383 403 else 384 404 this.SetNextState("INDIVIDUAL.GUARD.GUARDING"); 405 406 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 407 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Guard", "metadata": msg, "target": this.order.data.target}); 385 408 }, 386 409 387 410 "Order.Flee": function(msg) { … … 390 413 var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 391 414 if (cmpUnitMotion.MoveToTargetRange(this.order.data.target, distance, -1)) 392 415 { 416 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 417 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Flee", "metadata": msg, "target": this.order.data.target}); 418 393 419 // We've started fleeing from the given target 394 420 if (this.IsAnimal()) 395 421 this.SetNextState("ANIMAL.FLEEING"); … … 402 428 this.StopMoving(); 403 429 this.FinishOrder(); 404 430 } 431 432 405 433 }, 406 434 407 435 "Order.Attack": function(msg) { … … 422 450 } 423 451 this.order.data.attackType = type; 424 452 453 // Call "OnUnitIssuedOrder" event of the triggers. 454 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 455 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Attack", "metadata": msg, "target": this.order.data.target}); 456 425 457 // If we are already at the target, try attacking it from here 426 458 if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType)) 427 459 { … … 506 538 // We can't reach the target, and can't move towards it, 507 539 // so abandon this attack order 508 540 this.FinishOrder(); 541 509 542 }, 510 543 511 544 "Order.Heal": function(msg) { … … 526 559 // Check if the target is in range 527 560 if (this.CheckTargetRange(this.order.data.target, IID_Heal)) 528 561 { 562 // Call "OnUnitIssuedOrder" event of the triggers. 563 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 564 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Heal", "metadata": msg, "target": this.order.data.target}); 565 529 566 this.StopMoving(); 530 567 this.SetNextState("INDIVIDUAL.HEAL.HEALING"); 531 568 return; … … 542 579 // Try to move within heal range 543 580 if (this.MoveToTargetRange(this.order.data.target, IID_Heal)) 544 581 { 582 // Call "OnUnitIssuedOrder" event of the triggers. 583 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 584 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Heal", "metadata": msg, "target": this.order.data.target}); 585 545 586 // We've started walking to the given point 546 587 this.SetNextState("INDIVIDUAL.HEAL.APPROACHING"); 547 588 return; … … 599 640 this.StopMoving(); 600 641 this.SetNextStateAlwaysEntering("INDIVIDUAL.GATHER.GATHERING"); 601 642 } 643 644 // Call "OnUnitIssuedOrder" event of the triggers. 645 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 646 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Gather", "metadata": msg, "target": this.order.data.target}); 602 647 }, 603 648 604 649 "Order.GatherNearPosition": function(msg) { … … 605 650 // Move the unit to the position to gather from. 606 651 this.MoveToPoint(this.order.data.x, this.order.data.z); 607 652 this.SetNextState("INDIVIDUAL.GATHER.WALKING"); 653 654 // Call "OnUnitIssuedOrder" event of the triggers. 655 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 656 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.GatherNearPosition", "metadata": msg}); 608 657 }, 609 658 610 659 "Order.ReturnResource": function(msg) { … … 628 677 // Try to move to the dropsite 629 678 if (this.MoveToTargetRange(this.order.data.target, IID_ResourceGatherer)) 630 679 { 680 // Call "OnUnitIssuedOrder" event of the triggers. 681 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 682 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.ReturnResource", "metadata": msg, "target": this.order.data.target}); 683 631 684 // We've started walking to the target 632 685 this.SetNextState("INDIVIDUAL.RETURNRESOURCE.APPROACHING"); 633 686 return; … … 660 713 this.waypoints = undefined; 661 714 if (this.MoveToMarket(nextMarket)) 662 715 { 716 // Call "OnUnitIssuedOrder" event of the triggers. 717 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 718 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Trade", "metadata": msg, "target": this.order.data.target}); 719 663 720 // We've started walking to the next market 664 721 this.SetNextState(state); 665 722 } … … 682 739 this.StopMoving(); 683 740 this.SetNextState("INDIVIDUAL.REPAIR.REPAIRING"); 684 741 } 742 743 // Call "OnUnitIssuedOrder" event of the triggers. 744 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 745 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Repair", "metadata": msg, "target": this.order.data.target}); 685 746 }, 686 747 687 748 "Order.Garrison": function(msg) { 749 // Call "OnUnitIssuedOrder" event of the triggers. 750 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 751 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Garrison", "metadata": msg, "target": this.order.data.target}); 752 688 753 // For packable units: 689 754 // 1. If packed, we can move to the garrison target. 690 755 // 2. If unpacked, we first need to pack, then follow case 1. … … 709 774 710 775 "Order.Autogarrison": function(msg) { 711 776 this.SetNextState("INDIVIDUAL.AUTOGARRISON"); 777 778 // Call "OnUnitIssuedOrder" event of the triggers. 779 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 780 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Autogarrison", "metadata": msg}); 712 781 }, 713 782 714 783 "Order.Alert": function(msg) { … … 722 791 this.ReplaceOrder("Garrison", {"target": this.alertGarrisoningTarget}); 723 792 else 724 793 this.FinishOrder(); 794 795 // Call "OnUnitIssuedOrder" event of the triggers. 796 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 797 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Alert", "metadata": msg}); 725 798 }, 726 799 727 800 "Order.Cheering": function(msg) { 728 801 this.SetNextState("INDIVIDUAL.CHEERING"); 802 803 // Call "OnUnitIssuedOrder" event of the triggers. 804 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 805 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Cheering", "metadata": msg}); 729 806 }, 730 807 731 808 "Order.Pack": function(msg) { … … 733 810 { 734 811 this.StopMoving(); 735 812 this.SetNextState("INDIVIDUAL.PACKING"); 813 814 // Call "OnUnitIssuedOrder" event of the triggers. 815 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 816 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Pack", "metadata": msg}); 736 817 } 737 818 }, 738 819 … … 741 822 { 742 823 this.StopMoving(); 743 824 this.SetNextState("INDIVIDUAL.UNPACKING"); 825 826 // Call "OnUnitIssuedOrder" event of the triggers. 827 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 828 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Unpack", "metadata": msg}); 744 829 } 745 830 }, 746 831 … … 749 834 if (cmpPack && cmpPack.IsPacking() && !cmpPack.IsPacked()) 750 835 cmpPack.CancelPack(); 751 836 this.FinishOrder(); 837 838 // Call "OnUnitIssuedOrder" event of the triggers. 839 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 840 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.CancelPack", "metadata": msg}); 752 841 }, 753 842 754 843 "Order.CancelUnpack": function(msg) { … … 756 845 if (cmpPack && cmpPack.IsPacking() && cmpPack.IsPacked()) 757 846 cmpPack.CancelPack(); 758 847 this.FinishOrder(); 848 849 // Call "OnUnitIssuedOrder" event of the triggers. 850 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 851 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.CancelUnpack", "metadata": msg}); 759 852 }, 760 853 761 854 // States for the special entity representing a group of units moving in formation: … … 820 913 var cmpTargetUnitAI = Engine.QueryInterface(target, IID_UnitAI); 821 914 if (cmpTargetUnitAI && cmpTargetUnitAI.IsFormationMember()) 822 915 target = cmpTargetUnitAI.GetFormationController(); 823 916 824 917 var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); 825 918 // Check if we are already in range, otherwise walk there 826 919 if (!this.CheckTargetAttackRange(target, target)) … … 849 942 this.FinishOrder(); 850 943 return; 851 944 } 945 852 946 // Check if we are already in range, otherwise walk there 853 947 if (!this.CheckGarrisonRange(msg.data.target)) 854 948 { … … 926 1020 } 927 1021 928 1022 this.CallMemberFunction("GatherNearPosition", [msg.data.x, msg.data.z, msg.data.type, msg.data.template, false]); 929 1023 930 1024 this.SetNextStateAlwaysEntering("MEMBER"); 931 1025 }, 932 1026 … … 945 1039 } 946 1040 947 1041 this.CallMemberFunction("Heal", [msg.data.target, false]); 948 1042 949 1043 this.SetNextStateAlwaysEntering("MEMBER"); 950 1044 }, 951 1045 … … 1219 1313 1220 1314 // Stop moving as soon as the formation disbands 1221 1315 this.StopMoving(); 1222 1316 1223 1317 // If the controller handled an order but some members rejected it, 1224 1318 // they will have no orders and be in the FORMATIONMEMBER.IDLE state. 1225 1319 if (this.orderQueue.length) -
binaries/data/mods/public/simulation/components/interfaces/Trigger.js
1 Engine.RegisterInterface("Trigger"); -
binaries/data/mods/public/simulation/components/interfaces/TriggerEntity.js
1 Engine.RegisterInterface("TriggerEntity"); 2 -
binaries/data/mods/public/simulation/helpers/Commands.js
88 88 break; 89 89 90 90 case "walk": 91 // Handle OnUnitsIssuedOrder event of the triggers 92 if (entities.length > 1) 93 { 94 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 95 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Walk", "metadata": {"x": cmd.x, "z": cmd.z, "queued": cmd.queued}}); 96 } 97 91 98 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 92 99 cmpUnitAI.Walk(cmd.x, cmd.z, cmd.queued); 93 100 }); … … 94 101 break; 95 102 96 103 case "attack-walk": 104 // Handle OnUnitsIssuedOrder event of the triggers 105 if (entities.length > 1) 106 { 107 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 108 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.WalkAndFight", "metadata": {"x": cmd.x, "z": cmd.z, "queued": cmd.queued}}); 109 } 110 97 111 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 98 112 cmpUnitAI.WalkAndFight(cmd.x, cmd.z, cmd.queued); 99 113 }); … … 105 119 // This check is for debugging only! 106 120 warn("Invalid command: attack target is not owned by enemy of player "+player+": "+uneval(cmd)); 107 121 } 108 122 123 // Handle OnUnitsIssuedOrder event of the triggers 124 if (entities.length > 1) 125 { 126 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 127 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Attack", "metadata": {"target": cmd.target, "queued": cmd.queued}}); 128 } 129 109 130 // See UnitAI.CanAttack for target checks 110 131 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 111 132 cmpUnitAI.Attack(cmd.target, cmd.queued); … … 119 140 warn("Invalid command: heal target is not owned by player "+player+" or their ally: "+uneval(cmd)); 120 141 } 121 142 143 // Handle OnUnitsIssuedOrder event of the triggers 144 if (entities.length > 1) 145 { 146 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 147 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Heal", "metadata": {"target": cmd.target, "queued": cmd.queued}}); 148 } 149 122 150 // See UnitAI.CanHeal for target checks 123 151 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 124 152 cmpUnitAI.Heal(cmd.target, cmd.queued); … … 133 161 warn("Invalid command: repair target is not owned by ally of player "+player+": "+uneval(cmd)); 134 162 } 135 163 164 // Handle OnUnitsIssuedOrder event of the triggers 165 if (entities.length > 1) 166 { 167 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 168 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Repair", "metadata": {"target": cmd.target, "autocontinue": cmd.autocontinue, "queued": cmd.queued}}); 169 } 170 136 171 // See UnitAI.CanRepair for target checks 137 172 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 138 173 cmpUnitAI.Repair(cmd.target, cmd.autocontinue, cmd.queued); … … 146 181 warn("Invalid command: resource is not owned by gaia or player "+player+": "+uneval(cmd)); 147 182 } 148 183 184 // Handle OnUnitsIssuedOrder event of the triggers 185 if (entities.length > 1) 186 { 187 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 188 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Gather", "metadata": {"target": cmd.target, "queued": cmd.queued}}); 189 } 190 149 191 // See UnitAI.CanGather for target checks 150 192 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 151 193 cmpUnitAI.Gather(cmd.target, cmd.queued); … … 153 195 break; 154 196 155 197 case "gather-near-position": 198 // Handle OnUnitsIssuedOrder event of the triggers 199 if (entities.length > 1) 200 { 201 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 202 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.GatherNearPosition", "metadata": {"x": cmd.x, "z": cmd.z, "type": cmd.resourceType, "template": cmd.resourceTemplate, "queued": cmd.queued}}); 203 } 204 156 205 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 157 206 cmpUnitAI.GatherNearPosition(cmd.x, cmd.z, cmd.resourceType, cmd.resourceTemplate, cmd.queued); 158 207 }); … … 166 215 warn("Invalid command: dropsite is not owned by player "+player+": "+uneval(cmd)); 167 216 } 168 217 218 // Handle OnUnitsIssuedOrder event of the triggers 219 if (entities.length > 1) 220 { 221 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 222 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.ReturnResource", "metadata": {"target": cmd.target, "queued": cmd.queued}}); 223 } 224 169 225 // See UnitAI.CanReturnResource for target checks 170 226 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 171 227 cmpUnitAI.ReturnResource(cmd.target, cmd.queued); … … 329 385 // Verify that the building can be controlled by the player or is mutualAlly 330 386 if (CanControlUnitOrIsAlly(cmd.target, player, controlAllUnits)) 331 387 { 388 389 // Handle OnUnitsIssuedOrder event of the triggers 390 if (entities.length > 1) 391 { 392 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 393 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Garrison", "metadata": {"target": cmd.target, "queued": cmd.queued}}); 394 } 395 332 396 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 333 397 cmpUnitAI.Garrison(cmd.target, cmd.queued); 334 398 }); -
binaries/data/mods/public/simulation/helpers/Damage.js
74 74 // Damage the target 75 75 var targetState = cmpDamageReceiver.TakeDamage(data.strengths.hack * data.multiplier, data.strengths.pierce * data.multiplier, data.strengths.crush * data.multiplier, data.attacker); 76 76 77 // Call the related trigger event 78 // We have to call the event instead of listening to it by messages because the message is sent 79 // after the target gets killed, which is not our intended case 80 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 81 cmpTrigger.CallEvent("OnEntityTookDamage", {"attackerEntity": data.attacker, "attackedEntity": data.target, "type":data.type, "ammount":-targetState.change}); 82 77 83 // If the target was killed run some cleanup 78 84 if (targetState.killed) 79 85 Damage.TargetKilled(data.attacker, data.target); … … 110 116 // Call RangeManager with dummy entity and return the result. 111 117 var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 112 118 var rangeQuery = rangeManager.ExecuteQueryAroundPos(origin, 0, radius, players, IID_DamageReceiver); 119 113 120 return rangeQuery; 114 121 }; 115 122 … … 133 140 var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter); 134 141 if (cmpLooter) 135 142 cmpLooter.Collect(targetEntity); 143 144 // Call the related trigger event 145 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 146 cmpTrigger.CallEvent("OnEntityKilled", {"killerEntity": killerEntity, "killedEntity": targetEntity}); 136 147 }; 137 148 138 149 Engine.RegisterGlobal("Damage", Damage); -
binaries/data/mods/public/simulation/helpers/TriggerHelpers.js
1 // Contains standardized functions suitable for using in trigger scripts. 2 // Do not use them in any other simulation script. 3 4 /** 5 * A function to get the owner of an entity. 6 * Returns the ID of a player. Returns 0 if the owner is Gaia. 7 */ 8 function GetEntityOwner(entity) 9 { 10 return Engine.QueryInterface(entity, IID_Ownership).GetOwner() 11 } 12 13 /** 14 * A function to get the generic name of an entity 15 */ 16 function GetEntityGenericName(entity) 17 { 18 var cmpIdentity = Engine.QueryInterface(entity, IID_Identity); 19 if (!cmpIdentity) 20 return undefined; 21 return cmpIdentity.GetGenericName(); 22 } 23 24 /** 25 * A function to get a list of the classes of an entity 26 */ 27 function GetEntityClassesList(entity) 28 { 29 var cmpIdentity = Engine.QueryInterface(entity, IID_Identity); 30 if (!cmpIdentity) 31 return undefined; 32 return cmpIdentity.GetClassesList(); 33 } 34 35 /** 36 * A function to determine if an entity has a specific class 37 */ 38 function EntityHasClass(entity, classname) 39 { 40 var classes = GetEntityClassesList(entity); 41 return (classes && classes.indexOf(classname) != -1); 42 } 43 44 /** 45 * Returns the entity id of a player based on the id of the player 46 * Useful when one wants to change a player's properties. 47 */ 48 function GetPlayerEntityByID(id) 49 { 50 var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); 51 return cmpPlayerMan.GetPlayerByID(id); 52 } 53 54 /** 55 * Can be used to "force" a building to spawn a group of entities. 56 * Only works for buildings that can already train units. 57 */ 58 function BuildingSpawnUnits(entity, template, count) 59 { 60 var cmpProductionQueue = Engine.QueryInterface(entity, IID_ProductionQueue); 61 if (!cmpProductionQueue) 62 return; 63 cmpProductionQueue.SpawnUnits(template, count, null); 64 } 65 66 /** 67 * Shows a message in the top center of the screen 68 */ 69 function PushGUINotificationInTopCenter(player, message) 70 { 71 var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 72 cmpGUIInterface.PushNotification({"player": player, "message": message}); 73 } 74 75 /** 76 * Returns the resource type that can be gathered from an entity 77 */ 78 function GetEntityResourceType(entity) 79 { 80 var cmpResourceSupply = Engine.QueryInterface(entity, IID_ResourceSupply); 81 if (!cmpResourceSupply) 82 return undefined; 83 return cmpResourceSupply.GetType(); 84 } 85 86 /** 87 * Returns a list of entities owned by the player 88 */ 89 function GetEntitiesByPlayer(playerID) 90 { 91 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 92 return cmpRangeManager.GetEntitiesByPlayer(playerID); 93 } 94 95 /** 96 * Wins the game for a player 97 */ 98 function SetPlayerWon(playerID) 99 { 100 var playerEnt = GetPlayerEntityByID(playerID); 101 var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player); 102 cmpPlayer.SetState("won") 103 } 104 105 /** 106 * Defeats a player 107 */ 108 function DefeatPlayer(playerID) 109 { 110 var playerEnt = GetPlayerEntityByID(playerID); 111 Engine.PostMessage(playerEnt, MT_PlayerDefeated, { "playerId": playerID } ); 112 } 113 114 /** 115 * Returns the system trigger component. 116 */ 117 function GetTriggerComponent() 118 { 119 return Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 120 } 121 122 Engine.RegisterGlobal("GetEntityOwner", GetEntityOwner); 123 Engine.RegisterGlobal("GetEntityGenericName", GetEntityGenericName); 124 Engine.RegisterGlobal("GetEntityClassesList", GetEntityClassesList); 125 Engine.RegisterGlobal("EntityHasClass", EntityHasClass); 126 Engine.RegisterGlobal("GetPlayerEntityByID", GetPlayerEntityByID); 127 Engine.RegisterGlobal("BuildingSpawnUnits", BuildingSpawnUnits); 128 Engine.RegisterGlobal("PushGUINotificationInTopCenter", PushGUINotificationInTopCenter); 129 Engine.RegisterGlobal("GetEntityResourceType", GetEntityResourceType); 130 Engine.RegisterGlobal("GetEntitiesByPlayer", GetEntitiesByPlayer); 131 Engine.RegisterGlobal("SetPlayerWon", SetPlayerWon); 132 Engine.RegisterGlobal("DefeatPlayer", DefeatPlayer); 133 Engine.RegisterGlobal("GetTriggerComponent", GetTriggerComponent); 134 No newline at end of file -
binaries/data/mods/public/simulation/templates/template_structure.xml
97 97 <BarHeight>0.6</BarHeight> 98 98 <HeightOffset>12.0</HeightOffset> 99 99 </StatusBars> 100 <TriggerEntity/> 100 101 <TerritoryDecay> 101 102 <HealthDecayRate>5</HealthDecayRate> 102 103 </TerritoryDecay> -
binaries/data/mods/public/simulation/templates/template_unit.xml
93 93 <BarHeight>0.333</BarHeight> 94 94 <HeightOffset>5.0</HeightOffset> 95 95 </StatusBars> 96 <TriggerEntity/> 96 97 <UnitAI> 97 98 <AlertReactiveLevel>2</AlertReactiveLevel> 98 99 <DefaultStance>aggressive</DefaultStance> -
source/simulation2/Simulation2.cpp
702 702 703 703 if (!m->m_StartupScript.empty()) 704 704 GetScriptInterface().LoadScript(L"map startup script", m->m_StartupScript); 705 706 // Load the trigger script after we have loaded the simulation and the map. 707 if (GetScriptInterface().HasProperty(m->m_MapSettings.get(), "TriggerScript")) 708 { 709 std::string script_name; 710 GetScriptInterface().GetProperty(m->m_MapSettings.get(), "TriggerScript", script_name); 711 712 script_name = "maps/" + script_name; 713 m->m_ComponentManager.LoadScript(script_name.data()); 714 } 705 715 } 706 716 707 717 int CSimulation2::ProgressiveLoad()