Ticket #52: Triggers.5.patch
File Triggers.5.patch, 39.9 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/interfaces/Trigger.js
1 Engine.RegisterInterface("Trigger"); -
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
283 283 return; 284 284 } 285 285 286 // Call the related trigger event 287 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 288 cmpTrigger.CallEvent("OnResearchFinished", {"researcher": this.entity, "tech": tech}); 289 286 290 var modifiedComponents = {}; 287 291 this.researchedTechs[tech] = template; 288 292 // 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 var eventNames = [ 9 "OnEntityTookDamage", 10 "OnEntityKilled", 11 "OnStructureBuilt", 12 "OnConstructionStarted", 13 "OnTrainingFinished", 14 "OnTrainingQueued", 15 "OnResearchFinished", 16 "OnResearchQueued", 17 "OnUnitIssuedOrder", 18 "OnUnitsIssuedOrder" 19 ]; 20 21 // Each event has its own set of actions determined by the map maker. 22 for each (var eventName in eventNames) 23 this["event" + eventName + "Actions"] = []; 24 25 // Special events 26 this.eventOnUnitRangeFromEntityData = {}; 27 this.eventOnIntervalData = {}; 28 29 // We are going to have all of the triggers here to be able to enable/disbale them in runtime. 30 this.triggerEnabled = {}; 31 32 // We should define the trigger variables here so that they can be serialized 33 this.triggerVariables = {}; 34 35 // 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 36 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 37 cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger, "DoAction", 0, {"action": "InitTriggers", "data": {}}); 38 }; 39 40 // Global variables that are going to be used in triggers should be defined here. If not, They'll be lost after by saving/loading. 41 Trigger.prototype.SetVariable = function(name, value) 42 { 43 this.triggerVariables[name] = value; 44 } 45 46 Trigger.prototype.GetVariable = function(name) 47 { 48 return this.triggerVariables[name]; 49 } 50 51 /** Binds the "action" function to one of the implemented events. 52 * 53 * @param eventName Name of the event (see the list in init) 54 * @param action Functionname of a function available under the g_TriggersData global object 55 */ 56 Trigger.prototype.RegisterTrigger = function(event, action, enabled) 57 { 58 var eventString = "event" + event + "Actions"; 59 if (this[eventString]) 60 { 61 this[eventString].push(action); 62 this.triggerEnabled[[event, action]] = enabled 63 } 64 else 65 error("Invalid trigger event \"" + event + "\".") 66 }; 67 68 // Disable trigger 69 Trigger.prototype.DisableTrigger = function(event, action) 70 { 71 if (this.triggerEnabled[[event, action]] !== undefined) 72 this.triggerEnabled[[event, action]] = false; 73 else 74 warn("Trigger key doesn't exist:" + event + " & " + action); 75 } 76 77 // Enable trigger 78 Trigger.prototype.EnableTrigger = function(event, action) 79 { 80 if (this.triggerEnabled[[event, action]] !== undefined) 81 this.triggerEnabled[[event, action]] = true; 82 else 83 warn("Trigger key doesn't exist:" + event + " & " + action); 84 } 85 86 /** 87 * This is an event with more than just the key argument, 88 * so we should have a special register function for it. 89 * 90 * @param event Either "entered" or "left" based on the wanter behaviour. 91 * @param entity Entity id around which the range is checked 92 * @param radius Radius of the range 93 * @param action Functionname to execute on a range change (entities entered or left) 94 * @param players Array of player ids (0 to 8). Or null if you want to check for all players 95 * @param componentID Only entities with this component id will be noticed. 0 for all entities 96 * @param enabled Determines whether if the trigger is active by default 97 */ 98 Trigger.prototype.RegisterOnUnitRangeFromEntityTrigger = function(event, entity, radius, action, players, component_id, enabled) 99 { 100 // If the players parameter is not specified use all players. 101 if (!players) 102 { 103 var playerEntities = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayerEntities(); 104 players = []; 105 for each (var pentity in playerEntities) 106 players.push(Engine.QueryInterface(pentity, IID_Player).GetPlayerID()); 107 } 108 109 // To determine if a unit has "moved" or "was inside" the range, we should know if it was in the range before. We have "currentCollection" for this purpose. 110 // For the purpose of finding out the intersection of the previous and the current rangeQuery faster, we are sorting the current one. 111 this.eventOnUnitRangeFromEntityData[action] = {"event": event, "action": action, "entity": entity, "radius": radius, "currentCollection": [], "players": players}; 112 113 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 114 115 this.eventOnUnitRangeFromEntityData[action].rangeQuery = cmpRangeManager.CreateActiveQuery(entity, 0, radius, players, component_id, cmpRangeManager.GetEntityFlagMask("normal")); 116 if (enabled) 117 cmpRangeManager.EnableActiveQuery(this.eventOnUnitRangeFromEntityData[action].rangeQuery); 118 } 119 120 121 // Range triggers are different in nature, and should have their own Enable and disable functions 122 Trigger.prototype.DisableRangeTrigger = function(action) 123 { 124 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 125 cmpRangeManager.DisableActiveQuery(this.eventOnUnitRangeFromEntityData[action].rangeQuery); 126 } 127 128 Trigger.prototype.EnableRangeTrigger = function(action) 129 { 130 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 131 cmpRangeManager.EnableActiveQuery(this.eventOnUnitRangeFromEntityData[action].rangeQuery); 132 } 133 134 /** 135 * This is an event with more than just the key argument, 136 * so we should have a special register function for it. 137 * 138 * @param action Functionname to execute on interval 139 * @param delay How many miliseconds before the first call? 140 * @param interval How many miliseconds between each call? 141 * @param enabled Determines whether if the trigger is active by default 142 */ 143 Trigger.prototype.RegisterOnIntervalTrigger = function(action, delay, interval, enabled) 144 { 145 // If the trigger was active by default, start the update timer 146 if (enabled) 147 { 148 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 149 this.eventOnIntervalData[action] = cmpTimer.SetInterval(this.entity, IID_Trigger, "TimerUpdate", delay, interval, {"action" : action}); 150 } 151 else // If not, save it for future when we activate the trigger 152 this.eventOnIntervalData[action] = [delay, interval, {"action" : action}]; 153 } 154 155 // Interval triggers are different in nature, and should have their own Enable and disable functions 156 Trigger.prototype.DisableIntervalTrigger = function(action) 157 { 158 var timer = this.eventOnIntervalData[action]; 159 // Check if it is active 160 if (typeof (timer) === 'number') 161 { 162 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 163 cmpTimer.CancelTimer(timer); 164 } 165 } 166 167 Trigger.prototype.EnableIntervalTrigger = function(action) 168 { 169 var timer = this.eventOnIntervalData[action]; 170 // Check if it is inactive 171 if (typeof (timer) !== 'number') 172 { 173 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 174 this.eventOnIntervalData[action] = cmpTimer.SetInterval(this.entity, IID_Trigger, "TimerUpdate", timer[0], timer[1], timer[2]); 175 } 176 } 177 178 /** 179 * This function executes the actions bound to the events 180 * It's either called directlty from other simulation scripts, 181 * or from message listeners in this file 182 * 183 * @param event Name of the event (see the list in init) 184 * @param data Data object that will be passed to the actions 185 */ 186 Trigger.prototype.CallEvent = function(event, data) 187 { 188 // A special case, when a unit dies, the range event associated with it should also be removed 189 if (event == "OnEntityKilled") 190 { 191 for (var i in this.eventOnEntityKilledActions) 192 { 193 if (this.triggerEnabled[[event, i]]) // Check if it is enabled 194 g_TriggerData[this.eventOnEntityKilledActions[i]](data); // The data for this one is {"killerEntity": killerEntity, "killedEntity": targetEntity} 195 196 // Remove the "OnUnitFromRangeEntity" triggers which have this entity as their argument 197 for (var j in this.eventOnUnitRangeFromEntityData) 198 if (this.eventOnUnitRangeFromEntityData[j].entity == data.killedEntity) 199 { 200 DisableRangeTrigger(j); 201 delete this.eventOnUnitRangeFromEntityData[j]; 202 } 203 } 204 return; 205 } 206 207 var actions = this["event" + event + "Actions"]; 208 209 if (actions === undefined) 210 { 211 warn("Unknown trigger event:\"" + event + "\"."); 212 return; 213 } 214 215 for (var i in actions) 216 { 217 if (this.triggerEnabled[[event, actions[i]]]) 218 g_TriggerData[this["event" + event + "Actions"][i]](data); 219 } 220 } 221 222 // Handles "OnStructureBuilt" event. 223 Trigger.prototype.OnGlobalConstructionFinished = function(msg) 224 { 225 this.CallEvent("OnStructureBuilt", {"building": msg.newentity}); // The data for this one is {"building": constructedBuilding} 226 } 227 228 // Handles "OnTrainingFinished" event. 229 Trigger.prototype.OnGlobalTrainingFinished = function(msg) 230 { 231 this.CallEvent("OnTrainingFinished", msg); 232 // The data for this one is {"entities": createdEnts, 233 // "owner": cmpOwnership.GetOwner(), 234 // "metadata": metadata} 235 // See function "SpawnUnits" in ProductionQueue for more details 236 } 237 238 // Handle "OnInterval" event. 239 Trigger.prototype.TimerUpdate = function(data, lateness) 240 { 241 g_TriggerData[data.action](lateness); 242 } 243 244 // Handles "OnUnitRangeFromEntity" 245 Trigger.prototype.OnGlobalRangeUpdate = function(msg) 246 { 247 // search the right query 248 for (var action in this.eventOnUnitRangeFromEntityData) 249 { 250 if (msg.tag == this.eventOnUnitRangeFromEntityData[action].rangeQuery) 251 { 252 var updatedQuery = this.eventOnUnitRangeFromEntityData[action]; 253 break; 254 } 255 } 256 257 if (!updatedQuery) 258 return; 259 260 for each (var entity in msg.removed) 261 { 262 var index = updatedQuery.currentCollection.indexOf(entity); 263 if (index > -1) 264 updatedQuery.currentCollection.splice(index, 1); 265 } 266 267 for each (var entity in msg.added) 268 updatedQuery.currentCollection.push(entity); 269 270 if (updatedQuery.event == "entered") 271 for each (var entity in msg.added) 272 g_TriggerData[updatedQuery.action]({"entity": entity}); 273 else if (updatedQuery.event == "left") 274 for each (var entity in msg.removed) 275 g_TriggerData[updatedQuery.action]({"entity": entity}); 276 } 277 278 /** 279 * Execute a function after a certain delay 280 * @param time The delay expressed in milleseconds 281 * @param action Name of the action function 282 * @param data Data object that will be passed to the action function 283 */ 284 Trigger.prototype.DoAfterDelay = function(miliseconds, action, data) 285 { 286 var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 287 return cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger, "DoAction", miliseconds, {"action": action, "data": data}); 288 } 289 290 /** 291 * Called by the trigger listeners to exucute the actual action. Including sanity checks. 292 */ 293 Trigger.prototype.DoAction = function(msg) 294 { 295 if (typeof g_TriggerData === 'undefined') 296 { 297 // We don't raise an error if g_TriggerData is undefined because it always happens in maps without triggers 298 log ("No trigger scripts loaded for this map."); 299 } 300 else 301 { 302 if (g_TriggerData[msg.action]) 303 g_TriggerData[msg.action](msg.data); 304 else 305 error("called a trigger action '" + msg.action + "' that wasn't added to the g_TriggersData global"); 306 } 307 308 } 309 310 Engine.RegisterComponentType(IID_Trigger, "Trigger", Trigger); -
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. … … 358 375 this.StopMoving(); 359 376 this.SetNextState("INDIVIDUAL.PICKUP.LOADING"); 360 377 } 378 379 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 380 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.PickupUnit", "metadata": msg, "target": this.order.data.target}); 361 381 }, 362 382 363 383 "Order.Guard": function(msg) { … … 371 391 this.SetNextState("INDIVIDUAL.GUARD.ESCORTING"); 372 392 else 373 393 this.SetNextState("INDIVIDUAL.GUARD.GUARDING"); 394 395 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 396 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Guard", "metadata": msg, "target": this.order.data.target}); 374 397 }, 375 398 376 399 "Order.Flee": function(msg) { … … 379 402 var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 380 403 if (cmpUnitMotion.MoveToTargetRange(this.order.data.target, distance, -1)) 381 404 { 405 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 406 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Flee", "metadata": msg, "target": this.order.data.target}); 407 382 408 // We've started fleeing from the given target 383 409 if (this.IsAnimal()) 384 410 this.SetNextState("ANIMAL.FLEEING"); … … 391 417 this.StopMoving(); 392 418 this.FinishOrder(); 393 419 } 420 421 394 422 }, 395 423 396 424 "Order.Attack": function(msg) { … … 411 439 } 412 440 this.order.data.attackType = type; 413 441 442 // Call "OnUnitIssuedOrder" event of the triggers. 443 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 444 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Attack", "metadata": msg, "target": this.order.data.target}); 445 414 446 // If we are already at the target, try attacking it from here 415 447 if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType)) 416 448 { … … 495 527 // We can't reach the target, and can't move towards it, 496 528 // so abandon this attack order 497 529 this.FinishOrder(); 530 498 531 }, 499 532 500 533 "Order.Heal": function(msg) { … … 515 548 // Check if the target is in range 516 549 if (this.CheckTargetRange(this.order.data.target, IID_Heal)) 517 550 { 551 // Call "OnUnitIssuedOrder" event of the triggers. 552 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 553 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Heal", "metadata": msg, "target": this.order.data.target}); 554 518 555 this.StopMoving(); 519 556 this.SetNextState("INDIVIDUAL.HEAL.HEALING"); 520 557 return; … … 531 568 // Try to move within heal range 532 569 if (this.MoveToTargetRange(this.order.data.target, IID_Heal)) 533 570 { 571 // Call "OnUnitIssuedOrder" event of the triggers. 572 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 573 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Heal", "metadata": msg, "target": this.order.data.target}); 574 534 575 // We've started walking to the given point 535 576 this.SetNextState("INDIVIDUAL.HEAL.APPROACHING"); 536 577 return; … … 588 629 this.StopMoving(); 589 630 this.SetNextStateAlwaysEntering("INDIVIDUAL.GATHER.GATHERING"); 590 631 } 632 633 // Call "OnUnitIssuedOrder" event of the triggers. 634 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 635 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Gather", "metadata": msg, "target": this.order.data.target}); 591 636 }, 592 637 593 638 "Order.GatherNearPosition": function(msg) { 594 639 // Move the unit to the position to gather from. 595 640 this.MoveToPoint(this.order.data.x, this.order.data.z); 596 641 this.SetNextState("INDIVIDUAL.GATHER.WALKING"); 642 643 // Call "OnUnitIssuedOrder" event of the triggers. 644 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 645 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.GatherNearPosition", "metadata": msg}); 597 646 }, 598 647 599 648 "Order.ReturnResource": function(msg) { … … 617 666 // Try to move to the dropsite 618 667 if (this.MoveToTargetRange(this.order.data.target, IID_ResourceGatherer)) 619 668 { 669 // Call "OnUnitIssuedOrder" event of the triggers. 670 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 671 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.ReturnResource", "metadata": msg, "target": this.order.data.target}); 672 620 673 // We've started walking to the target 621 674 this.SetNextState("INDIVIDUAL.RETURNRESOURCE.APPROACHING"); 622 675 return; … … 649 702 this.waypoints = undefined; 650 703 if (this.MoveToMarket(nextMarket)) 651 704 { 705 // Call "OnUnitIssuedOrder" event of the triggers. 706 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 707 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Trade", "metadata": msg, "target": this.order.data.target}); 708 652 709 // We've started walking to the next market 653 710 this.SetNextState(state); 654 711 } … … 671 728 this.StopMoving(); 672 729 this.SetNextState("INDIVIDUAL.REPAIR.REPAIRING"); 673 730 } 731 732 // Call "OnUnitIssuedOrder" event of the triggers. 733 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 734 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Repair", "metadata": msg, "target": this.order.data.target}); 674 735 }, 675 736 676 737 "Order.Garrison": function(msg) { 738 // Call "OnUnitIssuedOrder" event of the triggers. 739 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 740 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Garrison", "metadata": msg, "target": this.order.data.target}); 741 677 742 // For packable units: 678 743 // 1. If packed, we can move to the garrison target. 679 744 // 2. If unpacked, we first need to pack, then follow case 1. … … 698 763 699 764 "Order.Autogarrison": function(msg) { 700 765 this.SetNextState("INDIVIDUAL.AUTOGARRISON"); 766 767 // Call "OnUnitIssuedOrder" event of the triggers. 768 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 769 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Autogarrison", "metadata": msg}); 701 770 }, 702 771 703 772 "Order.Alert": function(msg) { … … 711 780 this.ReplaceOrder("Garrison", {"target": this.alertGarrisoningTarget}); 712 781 else 713 782 this.FinishOrder(); 783 784 // Call "OnUnitIssuedOrder" event of the triggers. 785 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 786 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Alert", "metadata": msg}); 714 787 }, 715 788 716 789 "Order.Cheering": function(msg) { 717 790 this.SetNextState("INDIVIDUAL.CHEERING"); 791 792 // Call "OnUnitIssuedOrder" event of the triggers. 793 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 794 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Cheering", "metadata": msg}); 718 795 }, 719 796 720 797 "Order.Pack": function(msg) { … … 722 799 { 723 800 this.StopMoving(); 724 801 this.SetNextState("INDIVIDUAL.PACKING"); 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.Pack", "metadata": msg}); 725 806 } 726 807 }, 727 808 … … 730 811 { 731 812 this.StopMoving(); 732 813 this.SetNextState("INDIVIDUAL.UNPACKING"); 814 815 // Call "OnUnitIssuedOrder" event of the triggers. 816 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 817 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Unpack", "metadata": msg}); 733 818 } 734 819 }, 735 820 … … 738 823 if (cmpPack && cmpPack.IsPacking() && !cmpPack.IsPacked()) 739 824 cmpPack.CancelPack(); 740 825 this.FinishOrder(); 826 827 // Call "OnUnitIssuedOrder" event of the triggers. 828 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 829 cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.CancelPack", "metadata": msg}); 741 830 }, 742 831 743 832 "Order.CancelUnpack": function(msg) { … … 745 834 if (cmpPack && cmpPack.IsPacking() && cmpPack.IsPacked()) 746 835 cmpPack.CancelPack(); 747 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.CancelUnpack", "metadata": msg}); 748 841 }, 749 842 750 843 // States for the special entity representing a group of units moving in formation: … … 809 902 var cmpTargetUnitAI = Engine.QueryInterface(target, IID_UnitAI); 810 903 if (cmpTargetUnitAI && cmpTargetUnitAI.IsFormationMember()) 811 904 target = cmpTargetUnitAI.GetFormationController(); 812 905 813 906 var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); 814 907 // Check if we are already in range, otherwise walk there 815 908 if (!this.CheckTargetAttackRange(target, target)) … … 838 931 this.FinishOrder(); 839 932 return; 840 933 } 934 841 935 // Check if we are already in range, otherwise walk there 842 936 if (!this.CheckGarrisonRange(msg.data.target)) 843 937 { … … 915 1009 } 916 1010 917 1011 this.CallMemberFunction("GatherNearPosition", [msg.data.x, msg.data.z, msg.data.type, msg.data.template, false]); 918 1012 919 1013 this.SetNextStateAlwaysEntering("MEMBER"); 920 1014 }, 921 1015 … … 934 1028 } 935 1029 936 1030 this.CallMemberFunction("Heal", [msg.data.target, false]); 937 1031 938 1032 this.SetNextStateAlwaysEntering("MEMBER"); 939 1033 }, 940 1034 … … 1188 1282 1189 1283 // Stop moving as soon as the formation disbands 1190 1284 this.StopMoving(); 1191 1285 1192 1286 // If the controller handled an order but some members rejected it, 1193 1287 // they will have no orders and be in the FORMATIONMEMBER.IDLE state. 1194 1288 if (this.orderQueue.length) -
binaries/data/mods/public/simulation/helpers/Commands.js
87 87 break; 88 88 89 89 case "walk": 90 // Handle OnUnitsIssuedOrder event of the triggers 91 if (entities.length > 1) 92 { 93 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 94 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Walk", "metadata": {"x": cmd.x, "z": cmd.z, "queued": cmd.queued}}); 95 } 96 90 97 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 91 98 cmpUnitAI.Walk(cmd.x, cmd.z, cmd.queued); 92 99 }); 93 100 break; 94 101 95 102 case "attack-walk": 103 // Handle OnUnitsIssuedOrder event of the triggers 104 if (entities.length > 1) 105 { 106 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 107 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.WalkAndFight", "metadata": {"x": cmd.x, "z": cmd.z, "queued": cmd.queued}}); 108 } 109 96 110 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 97 111 cmpUnitAI.WalkAndFight(cmd.x, cmd.z, cmd.queued); 98 112 }); … … 104 118 // This check is for debugging only! 105 119 warn("Invalid command: attack target is not owned by enemy of player "+player+": "+uneval(cmd)); 106 120 } 107 121 122 // Handle OnUnitsIssuedOrder event of the triggers 123 if (entities.length > 1) 124 { 125 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 126 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Attack", "metadata": {"target": cmd.target, "queued": cmd.queued}}); 127 } 128 108 129 // See UnitAI.CanAttack for target checks 109 130 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 110 131 cmpUnitAI.Attack(cmd.target, cmd.queued); … … 118 139 warn("Invalid command: heal target is not owned by player "+player+" or their ally: "+uneval(cmd)); 119 140 } 120 141 142 // Handle OnUnitsIssuedOrder event of the triggers 143 if (entities.length > 1) 144 { 145 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 146 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Heal", "metadata": {"target": cmd.target, "queued": cmd.queued}}); 147 } 148 121 149 // See UnitAI.CanHeal for target checks 122 150 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 123 151 cmpUnitAI.Heal(cmd.target, cmd.queued); … … 132 160 warn("Invalid command: repair target is not owned by ally of player "+player+": "+uneval(cmd)); 133 161 } 134 162 163 // Handle OnUnitsIssuedOrder event of the triggers 164 if (entities.length > 1) 165 { 166 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 167 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Repair", "metadata": {"target": cmd.target, "autocontinue": cmd.autocontinue, "queued": cmd.queued}}); 168 } 169 135 170 // See UnitAI.CanRepair for target checks 136 171 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 137 172 cmpUnitAI.Repair(cmd.target, cmd.autocontinue, cmd.queued); … … 145 180 warn("Invalid command: resource is not owned by gaia or player "+player+": "+uneval(cmd)); 146 181 } 147 182 183 // Handle OnUnitsIssuedOrder event of the triggers 184 if (entities.length > 1) 185 { 186 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 187 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Gather", "metadata": {"target": cmd.target, "queued": cmd.queued}}); 188 } 189 148 190 // See UnitAI.CanGather for target checks 149 191 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 150 192 cmpUnitAI.Gather(cmd.target, cmd.queued); … … 152 194 break; 153 195 154 196 case "gather-near-position": 197 // Handle OnUnitsIssuedOrder event of the triggers 198 if (entities.length > 1) 199 { 200 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 201 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.GatherNearPosition", "metadata": {"x": cmd.x, "z": cmd.z, "type": cmd.resourceType, "template": cmd.resourceTemplate, "queued": cmd.queued}}); 202 } 203 155 204 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 156 205 cmpUnitAI.GatherNearPosition(cmd.x, cmd.z, cmd.resourceType, cmd.resourceTemplate, cmd.queued); 157 206 }); … … 165 214 warn("Invalid command: dropsite is not owned by player "+player+": "+uneval(cmd)); 166 215 } 167 216 217 // Handle OnUnitsIssuedOrder event of the triggers 218 if (entities.length > 1) 219 { 220 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 221 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.ReturnResource", "metadata": {"target": cmd.target, "queued": cmd.queued}}); 222 } 223 168 224 // See UnitAI.CanReturnResource for target checks 169 225 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 170 226 cmpUnitAI.ReturnResource(cmd.target, cmd.queued); … … 325 381 // Verify that the building can be controlled by the player or is mutualAlly 326 382 if (CanControlUnitOrIsAlly(cmd.target, player, controlAllUnits)) 327 383 { 384 385 // Handle OnUnitsIssuedOrder event of the triggers 386 if (entities.length > 1) 387 { 388 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 389 cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Garrison", "metadata": {"target": cmd.target, "queued": cmd.queued}}); 390 } 391 328 392 GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) { 329 393 cmpUnitAI.Garrison(cmd.target, cmd.queued); 330 394 }); -
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 -
source/simulation2/Simulation2.cpp
133 133 LOAD_SCRIPTED_COMPONENT("PlayerManager"); 134 134 LOAD_SCRIPTED_COMPONENT("TechnologyTemplateManager"); 135 135 LOAD_SCRIPTED_COMPONENT("Timer"); 136 LOAD_SCRIPTED_COMPONENT("Trigger"); 136 137 LOAD_SCRIPTED_COMPONENT("ValueModificationManager"); 137 138 138 139 #undef LOAD_SCRIPTED_COMPONENT … … 748 749 749 750 if (!m->m_StartupScript.empty()) 750 751 GetScriptInterface().LoadScript(L"map startup script", m->m_StartupScript); 752 753 // Load the trigger script after we have loaded the simulation and the map. 754 if (GetScriptInterface().HasProperty(m->m_MapSettings.get(), "TriggerScript")) 755 { 756 std::string script_name; 757 GetScriptInterface().GetProperty(m->m_MapSettings.get(), "TriggerScript", script_name); 758 759 script_name = "maps/" + script_name; 760 m->m_ComponentManager.LoadScript(script_name.data()); 761 } 751 762 } 752 763 753 764 int CSimulation2::ProgressiveLoad()