Ticket #999: #999-2012-03-23.patch
File #999-2012-03-23.patch, 54.4 KB (added by , 12 years ago) |
---|
-
binaries/data/mods/public/art/actors/units/carthaginians/healer.xml
8 8 <animation file="female/f_walk_01.dae" name="Walk" speed="30"/> 9 9 <animation file="female/f_walk_01.dae" name="Run" speed="45"/> 10 10 <animation file="female/f_death_01.dae" name="Death" speed="100"/> 11 <animation file="female/f_salute_01.dae" name="Heal" speed="30"/> 11 12 </animations> 12 13 <props> 13 14 <prop actor="props/units/heads/head_kart_priestess.xml" attachpoint="head"/> -
binaries/data/mods/public/art/actors/units/celts/healer.xml
8 8 <animation file="biped/inf_staff_walk_a.dae" name="Walk" speed="20"/> 9 9 <animation file="infantry/general/death/inf_01.psa" name="Death" speed="400"/> 10 10 <animation file="biped/inf_staff_walk_a.dae" name="Run" speed="40"/> 11 <animation file="female/f_salute_01.dae" name="Heal" speed="30"/> 11 12 </animations> 12 13 <mesh>skeletal/m_dress_cuffs.dae</mesh> 13 14 <props> -
binaries/data/mods/public/art/actors/units/hellenes/healer.xml
7 7 <animation file="biped/inf_staff_idle_a.dae" name="Idle" speed="200"/> 8 8 <animation file="biped/inf_staff_walk_a.dae" name="Walk" speed="20"/> 9 9 <animation file="infantry/general/death/inf_01.psa" name="Death" speed="400"/> 10 <animation file="female/f_salute_01.dae" name="Heal" speed="30"/> 10 11 </animations> 11 12 <mesh>skeletal/m_dress_a.pmd</mesh> 12 13 <props> -
binaries/data/mods/public/art/actors/units/iberians/healer.xml
8 8 <animation file="female/f_walk_01.dae" name="Walk" speed="30"/> 9 9 <animation file="female/f_walk_01.dae" name="Run" speed="45"/> 10 10 <animation file="female/f_death_01.dae" name="Death" speed="120"/> 11 <animation file="female/f_salute_01.dae" name="Heal" speed="30"/> 11 12 </animations> 12 13 <props> 13 14 <prop actor="props/units/heads/head_iber_healer.xml" attachpoint="head"/> -
binaries/data/mods/public/art/actors/units/persians/healer.xml
7 7 <animation file="biped/inf_staff_idle_a.dae" name="Idle" speed="200"/> 8 8 <animation file="biped/inf_staff_walk_a.dae" name="Walk" speed="20"/> 9 9 <animation file="infantry/general/death/inf_01.psa" name="Death" speed="400"/> 10 <animation file="female/f_salute_01.dae" name="Heal" speed="30"/> 10 11 </animations> 11 12 <mesh>skeletal/m_dress_cuffs.dae</mesh> 12 13 <props> -
binaries/data/mods/public/art/actors/units/romans/healer.xml
8 8 <animation file="biped/inf_staff_walk_a.dae" name="Walk" speed="20"/> 9 9 <animation file="biped/inf_staff_walk_a.dae" name="Run" speed="20"/> 10 10 <animation file="infantry/general/death/inf_01.psa" name="Death" speed="400"/> 11 <animation file="female/f_salute_01.dae" name="Heal" speed="30"/> 11 12 </animations> 12 13 <mesh>skeletal/m_dress_a.pmd</mesh> 13 14 <props> -
binaries/data/mods/public/art/textures/cursors/action-heal.txt
1 1 1 -
binaries/data/mods/public/gui/session/input.js
297 297 return {"possible": true, "tooltip": tooltip}; 298 298 } 299 299 break; 300 case "heal": 301 // The check if the target is unhealable is done by targetState.needsHeal 302 if (entState.Healer && hasClass(targetState, "Unit") && targetState.needsHeal && (playerOwned || allyOwned)) 303 { 304 var unhealableClasses = entState.Healer.unhealableClasses; 305 for each (var unitClass in targetState.identity.classes) 306 { 307 if (unhealableClasses.indexOf(unitClass) != -1) 308 { 309 return {"possible": false}; 310 } 311 } 312 313 var healableClasses = entState.Healer.healableClasses; 314 for each (var unitClass in targetState.identity.classes) 315 { 316 if (healableClasses.indexOf(unitClass) != -1) 317 { 318 return {"possible": true}; 319 } 320 } 321 } 322 break; 300 323 case "gather": 301 324 if (targetState.resourceSupply && (playerOwned || gaiaOwned)) 302 325 { … … 417 440 return {"type": "build", "cursor": "action-repair", "target": target}; 418 441 else if ((actionInfo = getActionInfo("set-rallypoint", target)).possible) 419 442 return {"type": "set-rallypoint", "cursor": actionInfo.cursor, "data": actionInfo.data, "position": actionInfo.position}; 443 else if (getActionInfo("heal", target).possible) 444 return {"type": "heal", "cursor": "action-heal", "target": target}; 420 445 else if (getActionInfo("attack", target).possible) 421 446 return {"type": "attack", "cursor": "action-attack", "target": target}; 422 447 else if (getActionInfo("unset-rallypoint", target).possible) … … 1037 1062 Engine.GuiInterfaceCall("PlaySound", { "name": "order_attack", "entity": selection[0] }); 1038 1063 return true; 1039 1064 1065 case "heal": 1066 Engine.PostNetworkCommand({"type": "heal", "entities": selection, "target": action.target, "queued": queued}); 1067 // TODO: Play a sound? 1068 // Engine.GuiInterfaceCall("PlaySound", { "name": "order_heal", "entity": selection[0] }); 1069 return true; 1070 1040 1071 case "build": // (same command as repair) 1041 1072 case "repair": 1042 1073 Engine.PostNetworkCommand({"type": "repair", "entities": selection, "target": action.target, "autocontinue": true, "queued": queued}); -
binaries/data/mods/public/gui/session/selection_details.js
74 74 experienceSize.rtop = 100 - 100 * Math.max(0, Math.min(1, 1.0 * entState.promotion.curr / entState.promotion.req)); 75 75 experienceBar.size = experienceSize; 76 76 77 var experience = "[font=\"serif-bold-13\"]Experience [/font]" + entState.promotion.curr;77 var experience = "[font=\"serif-bold-13\"]Experience [/font]" + Math.floor(entState.promotion.curr); 78 78 if (entState.promotion.curr < entState.promotion.req) 79 79 experience += "/" + entState.promotion.req; 80 80 getGUIObjectByName("experience").tooltip = experience; -
binaries/data/mods/public/simulation/components/GarrisonHolder.js
320 320 var cmpHealth = Engine.QueryInterface(entity, IID_Health); 321 321 if (cmpHealth) 322 322 { 323 if (cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints()) 323 // We do not want to heal unhealable units 324 if (!cmpHealth.IsUnhealable()) 324 325 cmpHealth.Increase(this.healRate); 325 326 } 326 327 } -
binaries/data/mods/public/simulation/components/GuiInterface.js
155 155 ret.hitpoints = cmpHealth.GetHitpoints(); 156 156 ret.maxHitpoints = cmpHealth.GetMaxHitpoints(); 157 157 ret.needsRepair = cmpHealth.IsRepairable() && (cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints()); 158 ret.needsHeal = !cmpHealth.IsUnhealable(); 158 159 } 159 160 160 161 var cmpAttack = Engine.QueryInterface(ent, IID_Attack); … … 273 274 ret.barterMarket = { "prices": cmpBarter.GetPrices() }; 274 275 } 275 276 277 var cmpHeal = Engine.QueryInterface(ent, IID_Heal); 278 if (cmpHeal) 279 { 280 ret.Healer = { 281 "unhealableClasses": cmpHeal.GetUnhealableClasses(), 282 "healableClasses": cmpHeal.GetHealableClasses(), 283 }; 284 } 285 276 286 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 277 287 ret.visibility = cmpRangeManager.GetLosVisibility(ent, player, false); 278 288 -
binaries/data/mods/public/simulation/components/Heal.js
1 function Heal() {} 2 3 Heal.prototype.Schema = 4 "<a:help>Controls the healing abilities of the unit.</a:help>" + 5 "<a:example>" + 6 "<Range>20</Range>" + 7 "<HP>5</HP>" + 8 "<Rate>2000</Rate>" + 9 "<UnhealableClasses datatype=\"tokens\">Cavalry</UnhealableClasses>" + 10 "<HealableClasses datatype=\"tokens\">Support Infantry</HealableClasses>" + 11 "</a:example>" + 12 "<element name='Range' a:help='Range (in metres) where healing is possible'>" + 13 "<ref name='nonNegativeDecimal'/>" + 14 "</element>" + 15 "<element name='HP' a:help='Hitpoints healed per Rate'>" + 16 "<ref name='nonNegativeDecimal'/>" + 17 "</element>" + 18 "<element name='Rate' a:help='A heal is performed every Rate ms'>" + 19 "<ref name='nonNegativeDecimal'/>" + 20 "</element>" + 21 "<element name='UnhealableClasses' a:help='If the target has any of these classes it can not be healed (even if it has a class from HealableClasses)'>" + 22 "<attribute name='datatype'>" + 23 "<value>tokens</value>" + 24 "</attribute>" + 25 "<text/>" + 26 "</element>" + 27 "<element name='HealableClasses' a:help='The target must have one of these classes to be healable'>" + 28 "<attribute name='datatype'>" + 29 "<value>tokens</value>" + 30 "</attribute>" + 31 "<text/>" + 32 "</element>"; 33 34 Heal.prototype.Init = function() 35 { 36 }; 37 38 Heal.prototype.Serialize = null; // we have no dynamic state to save 39 40 Heal.prototype.GetTimers = function() 41 { 42 var prepare = 1000; 43 var repeat = +this.template.Rate; 44 return { "prepare": prepare, "repeat": repeat }; 45 }; 46 47 Heal.prototype.GetRange = function() 48 { 49 var max = +this.template.Range; 50 var min = 0; 51 return { "max": max, "min": min }; 52 }; 53 54 Heal.prototype.GetUnhealableClasses = function() 55 { 56 var classes = this.template.UnhealableClasses._string; 57 // If we have no unhealable classes defined classes is undefined 58 return classes ? classes.split(/\s+/) : []; 59 }; 60 61 Heal.prototype.GetHealableClasses = function() 62 { 63 var classes = this.template.HealableClasses._string; 64 return classes.split(/\s+/); 65 }; 66 67 /** 68 * Heal the target entity. This should only be called after a successful range 69 * check, and should only be called after GetTimers().repeat msec has passed 70 * since the last call to PerformHeal. 71 */ 72 Heal.prototype.PerformHeal = function(target) 73 { 74 var cmpHealth = Engine.QueryInterface(target, IID_Health); 75 if (!cmpHealth) 76 return; 77 var targetState = cmpHealth.Increase(Math.max(0,this.template.HP)); 78 79 // Add XP 80 var cmpLoot = Engine.QueryInterface(target, IID_Loot); 81 var cmpPromotion = Engine.QueryInterface(this.entity, IID_Promotion); 82 if (targetState!==undefined && cmpLoot && cmpPromotion) 83 { 84 // HP healed * XP per HP 85 cmpPromotion.IncreaseXp((targetState.new-targetState.old)*(cmpLoot.GetXp()/cmpHealth.GetMaxHitpoints())); 86 } 87 //TODO we need a sound file 88 // PlaySound("heal_impact", this.entity); 89 }; 90 91 Engine.RegisterComponentType(IID_Heal, "Heal", Heal); -
binaries/data/mods/public/simulation/components/Health.js
25 25 "<value a:help='Remain in the world with 0 health'>remain</value>" + 26 26 "</choice>" + 27 27 "</element>" + 28 "<element name=' Healable' a:help='Indicates that the entity canbe healed by healer units'>" +28 "<element name='Unhealable' a:help='Indicates that the entity can not be healed by healer units'>" + 29 29 "<data type='boolean'/>" + 30 30 "</element>" + 31 31 "<element name='Repairable' a:help='Indicates that the entity can be repaired by builder units'>" + … … 72 72 return (this.template.Repairable == "true"); 73 73 }; 74 74 75 Health.prototype.IsUnhealable = function() 76 { 77 return (this.template.Unhealable == "true" 78 || this.GetHitpoints() <= 0 79 || this.GetHitpoints() >= this.GetMaxHitpoints()); 80 }; 81 75 82 Health.prototype.Kill = function() 76 83 { 77 84 this.Reduce(this.hitpoints); … … 80 87 Health.prototype.Reduce = function(amount) 81 88 { 82 89 var state = { "killed": false }; 90 if (this.hitpoints == this.GetMaxHitpoints()) 91 { 92 Engine.PostMessage(this.entity, MT_ShowAsModified, { "entity": this.entity, "showAsModified": true }); 93 // TODO send a second message after this turn to set showAsModified to false to make this show up only once 94 // and not in every query. As a workaround we can set showAsModified false if we get max hitpoints in increase 95 // or if we are in the else block of this if statement. 96 // We could use Timer.SetTimeout with a timeout of the length of a turn to solve this. 97 //Engine.PostMessage(this.entity, MT_ShowAsModified, { "entity": this.entity, "showAsModified": false }); 98 } 83 99 if (amount >= this.hitpoints) 84 100 { 85 101 // If this is the first time we reached 0, then die. … … 131 147 { 132 148 // If we're already dead, don't allow resurrection 133 149 if (this.hitpoints == 0) 134 return ;150 return undefined; 135 151 136 152 var old = this.hitpoints; 137 153 this.hitpoints = Math.min(this.hitpoints + amount, this.GetMaxHitpoints()); 138 154 139 155 Engine.PostMessage(this.entity, MT_HealthChanged, { "from": old, "to": this.hitpoints }); 156 // We return the old and the actual hp 157 return { "old": old, "new": this.hitpoints}; 140 158 }; 141 159 142 160 //// Private functions //// -
binaries/data/mods/public/simulation/components/Loot.js
21 21 22 22 Loot.prototype.GetXp = function() 23 23 { 24 return this.template.xp;24 return +(this.template.xp || 0); 25 25 }; 26 26 27 27 Loot.prototype.GetResources = function() -
binaries/data/mods/public/simulation/components/UnitAI.js
283 283 this.FinishOrder(); 284 284 }, 285 285 286 "Order.Heal": function(msg) { 287 // Check the target is alive 288 if (!this.TargetIsAlive(this.order.data.target)) 289 { 290 this.FinishOrder(); 291 return; 292 } 293 294 // Check if the target is in range 295 if (this.CheckTargetRange(this.order.data.target, IID_Heal)) 296 { 297 this.StopMoving(); 298 this.SetNextState("INDIVIDUAL.HEAL.HEALING"); 299 return; 300 } 301 302 // If we can't reach the target, but are standing ground, 303 // then abandon this heal order 304 if (this.GetStance().respondStandGround && !this.order.data.force) 305 { 306 this.FinishOrder(); 307 return; 308 } 309 310 // Try to move within heal range 311 if (this.MoveToTargetRange(this.order.data.target, IID_Heal)) 312 { 313 // We've started walking to the given point 314 this.SetNextState("INDIVIDUAL.HEAL.APPROACHING"); 315 return; 316 } 317 318 // We can't reach the target, and can't move towards it, 319 // so abandon this heal order 320 this.FinishOrder(); 321 }, 322 286 323 "Order.Gather": function(msg) { 287 324 288 325 // If the target is still alive, we need to kill it first … … 408 445 cmpFormation.Disband(); 409 446 }, 410 447 448 "Order.Heal": function(msg) { 449 // TODO: see notes in Order.Attack 450 var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); 451 cmpFormation.CallMemberFunction("Heal", [msg.data.target, false]); 452 cmpFormation.Disband(); 453 }, 454 411 455 "Order.Repair": function(msg) { 412 456 // TODO: see notes in Order.Attack 413 457 var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); … … 547 591 // remain idle 548 592 this.StartTimer(1000); 549 593 594 // If a unit can heal and attack we first want to heal wounded units, 595 // so check if we are a healer and find whether there's anybody nearby to heal. 596 // If anyone approaches later it'll be handled via LosRangeUpdate.) 597 // If anyone in sight gets hurt that will be handled via LosRangeUpdate. 598 if (this.IsHealer() && this.FindNewHealTargets()) 599 return true; // (abort the FSM transition since we may have already switched state) 600 550 601 // If we entered the idle state we must have nothing better to do, 551 602 // so immediately check whether there's anybody nearby to attack. 552 603 // (If anyone approaches later, it'll be handled via LosRangeUpdate.) … … 560 611 "leave": function() { 561 612 var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 562 613 rangeMan.DisableActiveQuery(this.losRangeQuery); 614 if (this.losHealRangeQuery) 615 rangeMan.DisableActiveQuery(this.losHealRangeQuery); 563 616 564 617 this.StopTimer(); 565 618 … … 571 624 }, 572 625 573 626 "LosRangeUpdate": function(msg) { 627 if (this.IsHealer()) 628 { 629 // Start healing one of the newly-seen own or ally units (if any) 630 this.FindNewHealTargets(); 631 } 574 632 if (this.GetStance().targetVisibleEnemies) 575 633 { 576 634 // Start attacking one of the newly-seen enemy (if any) … … 1000 1058 }, 1001 1059 }, 1002 1060 1061 "HEAL": { 1062 "EntityRenamed": function(msg) { 1063 if (this.order.data.target == msg.entity) 1064 this.order.data.target = msg.newentity; 1065 }, 1066 1067 "Attacked": function(msg) { 1068 // If we stand ground we will rather die than flee 1069 if (!this.GetStance().respondStandGround) 1070 this.Flee(msg.data.attacker, false); 1071 }, 1072 1073 "APPROACHING": { 1074 "enter": function () { 1075 this.SelectAnimation("move"); 1076 this.StartTimer(1000, 1000); 1077 }, 1078 1079 "leave": function() { 1080 this.StopTimer(); 1081 }, 1082 1083 "Timer": function(msg) { 1084 if (this.ShouldAbandonChase(this.order.data.target, this.order.data.force)) 1085 { 1086 this.StopMoving(); 1087 this.FinishOrder(); 1088 1089 // Return to our original position 1090 if (this.GetStance().respondHoldGround) 1091 this.WalkToHeldPosition(); 1092 } 1093 }, 1094 1095 "MoveCompleted": function() { 1096 this.SetNextState("HEALING"); 1097 }, 1098 1099 "Attacked": function(msg) { 1100 // If we stand ground we will rather die than flee 1101 if (!this.GetStance().respondStandGround) 1102 this.Flee(msg.data.attacker, false); 1103 }, 1104 }, 1105 1106 "HEALING": { 1107 "enter": function() { 1108 var cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); 1109 this.healTimers = cmpHeal.GetTimers(); 1110 this.SelectAnimation("heal", false, 1.0, "heal"); 1111 this.SetAnimationSync(this.healTimers.prepare, this.healTimers.repeat); 1112 this.StartTimer(this.healTimers.prepare, this.healTimers.repeat); 1113 // TODO if .prepare is short, players can cheat by cycling heal/stop/heal 1114 // to beat the .repeat time; should enforce a minimum time 1115 // see comment in ATTACKING.enter 1116 this.FaceTowardsTarget(this.order.data.target); 1117 }, 1118 1119 "leave": function() { 1120 this.StopTimer(); 1121 }, 1122 1123 "Timer": function(msg) { 1124 var target = this.order.data.target; 1125 // Check the target is still alive and healable 1126 if (this.TargetIsAlive(target) && this.CanHeal(target)) 1127 { 1128 // Check if we can still reach the target 1129 if (this.CheckTargetRange(target, IID_Heal)) 1130 { 1131 this.FaceTowardsTarget(target); 1132 var cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); 1133 cmpHeal.PerformHeal(target); 1134 return; 1135 } 1136 // Can't reach it - try to chase after it 1137 if (this.ShouldChaseTargetedEntity(target, this.order.data.force)) 1138 { 1139 if (this.MoveToTargetRange(target, IID_Heal)) 1140 { 1141 this.SetNextState("HEAL.CHASING"); 1142 return; 1143 } 1144 } 1145 } 1146 // Can't reach it, healed to max hp or doesn't exist any more - give up 1147 if (this.FinishOrder()) 1148 return; 1149 1150 // Heal another one 1151 if (this.FindNewHealTargets()) 1152 return; 1153 1154 // Return to our original position 1155 if (this.GetStance().respondHoldGround) 1156 this.WalkToHeldPosition(); 1157 }, 1158 "Attacked": function(msg) { 1159 // If we stand ground we will rather die than flee 1160 if (!this.GetStance().respondStandGround) 1161 this.Flee(msg.data.attacker, false); 1162 }, 1163 }, 1164 "CHASING": { 1165 "enter": function () { 1166 this.SelectAnimation("move"); 1167 this.StartTimer(1000, 1000); 1168 }, 1169 1170 "leave": function () { 1171 this.StopTimer(); 1172 }, 1173 "Timer": function(msg) { 1174 if (this.ShouldAbandonChase(this.order.data.target, this.order.data.force)) 1175 { 1176 this.StopMoving(); 1177 this.FinishOrder(); 1178 1179 // Return to our original position 1180 if (this.GetStance().respondHoldGround) 1181 this.WalkToHeldPosition(); 1182 } 1183 }, 1184 "MoveCompleted": function () { 1185 this.SetNextState("HEALING"); 1186 }, 1187 }, 1188 }, 1189 1003 1190 // Returning to dropsite 1004 1191 "RETURNRESOURCE": { 1005 1192 "APPROACHING": { … … 1473 1660 return (this.template.NaturalBehaviour ? true : false); 1474 1661 }; 1475 1662 1663 UnitAI.prototype.IsHealer = function() 1664 { 1665 return Engine.QueryInterface(this.entity, IID_Heal); 1666 }; 1667 1476 1668 UnitAI.prototype.IsIdle = function() 1477 1669 { 1478 1670 return this.isIdle; … … 1496 1688 UnitAI.prototype.OnOwnershipChanged = function(msg) 1497 1689 { 1498 1690 this.SetupRangeQuery(); 1691 if (this.IsHealer()) 1692 this.SetupHealRangeQuery(); 1499 1693 }; 1500 1694 1501 1695 UnitAI.prototype.OnDestroy = function() … … 1507 1701 var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 1508 1702 if (this.losRangeQuery) 1509 1703 rangeMan.DestroyActiveQuery(this.losRangeQuery); 1704 if (this.losHealRangeQuery) 1705 rangeMan.DestroyActiveQuery(this.losHealRangeQuery); 1510 1706 }; 1511 1707 1512 1708 // Set up a range query for all enemy units within LOS range … … 1539 1735 } 1540 1736 } 1541 1737 1542 var range = this.GetQueryRange( );1738 var range = this.GetQueryRange(IID_Attack); 1543 1739 1544 this.losRangeQuery = rangeMan.CreateActiveQuery(this.entity, range.min, range.max, players, IID_DamageReceiver );1740 this.losRangeQuery = rangeMan.CreateActiveQuery(this.entity, range.min, range.max, players, IID_DamageReceiver, false); 1545 1741 rangeMan.EnableActiveQuery(this.losRangeQuery); 1546 1742 }; 1547 1743 1744 // Set up a range query for all own or ally units within LOS range 1745 // which can be healed. 1746 // This should be called whenever our ownership changes. 1747 UnitAI.prototype.SetupHealRangeQuery = function() 1748 { 1749 var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); 1750 var owner = cmpOwnership.GetOwner(); 1751 1752 var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 1753 var playerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager); 1754 1755 if (this.losHealRangeQuery) 1756 rangeMan.DestroyActiveQuery(this.losHealRangeQuery); 1757 1758 var players = [owner]; 1759 1760 if (owner != -1) 1761 { 1762 // If unit not just killed, get ally players via diplomacy 1763 var cmpPlayer = Engine.QueryInterface(playerMan.GetPlayerByID(owner), IID_Player); 1764 var numPlayers = playerMan.GetNumPlayers(); 1765 for (var i = 1; i < numPlayers; ++i) 1766 { 1767 // Exclude gaia and enemies 1768 if (cmpPlayer.IsAlly(i)) 1769 players.push(i); 1770 } 1771 } 1772 1773 var range = this.GetQueryRange(IID_Heal); 1774 1775 this.losHealRangeQuery = rangeMan.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Health, true); 1776 rangeMan.EnableActiveQuery(this.losHealRangeQuery); 1777 }; 1778 1548 1779 //// FSM linkage functions //// 1549 1780 1550 1781 UnitAI.prototype.SetNextState = function(state) … … 1766 1997 1767 1998 UnitAI.prototype.OnRangeUpdate = function(msg) 1768 1999 { 1769 if (msg.tag == this.losRangeQuery )2000 if (msg.tag == this.losRangeQuery || msg.tag == this.losHealRangeQuery) 1770 2001 UnitFsm.ProcessMessage(this, {"type": "LosRangeUpdate", "data": msg}); 1771 2002 }; 1772 2003 … … 2296 2527 case "Flee": 2297 2528 case "LeaveFoundation": 2298 2529 case "Attack": 2530 case "Heal": 2299 2531 case "Gather": 2300 2532 case "ReturnResource": 2301 2533 case "Repair": … … 2358 2590 { 2359 2591 if (!this.CanAttack(target)) 2360 2592 { 2361 this.WalkToTarget(target, queued); 2593 // We don't want to let healers walk to the target unit so they can be easily killed. 2594 // Instead we just let them get into healing range. 2595 if (this.IsHealer()) 2596 this.MoveToTargetRange(target, IID_Heal); 2597 else 2598 this.WalkToTarget(target, queued); 2362 2599 return; 2363 2600 } 2364 2601 … … 2419 2656 this.AddOrder("GatherNearPosition", { "type": type, "x": x, "z": z }, queued); 2420 2657 } 2421 2658 2659 UnitAI.prototype.Heal = function(target, queued) 2660 { 2661 if (!this.CanHeal(target)) 2662 { 2663 this.WalkToTarget(target, queued); 2664 return; 2665 } 2666 2667 this.AddOrder("Heal", { "target": target, "force": true }, queued); 2668 }; 2669 2422 2670 UnitAI.prototype.ReturnResource = function(target, queued) 2423 2671 { 2424 2672 if (!this.CanReturnResource(target, true)) … … 2530 2778 this.stance = stance; 2531 2779 else 2532 2780 error("UnitAI: Setting to invalid stance '"+stance+"'"); 2533 } 2781 }; 2534 2782 2535 2783 UnitAI.prototype.SwitchToStance = function(stance) 2536 2784 { … … 2548 2796 2549 2797 // Reset the range query, since the range depends on stance 2550 2798 this.SetupRangeQuery(); 2551 } 2799 // Just if we are a healer 2800 // TODO maybe move those two to a SetupRangeQuerys() 2801 if (this.IsHealer()) 2802 this.SetupHealRangeQuery(); 2803 }; 2552 2804 2553 2805 /** 2554 2806 * Resets losRangeQuery, and if there are some targets in range that we can … … 2569 2821 return this.RespondToTargetedEntities(ents); 2570 2822 }; 2571 2823 2572 UnitAI.prototype.GetQueryRange = function() 2824 /** 2825 * Resets losHealRangeQuery, and if there are some targets in range that we can heal 2826 * then we start healing and this returns true; otherwise, returns false. 2827 */ 2828 UnitAI.prototype.FindNewHealTargets = function() 2829 { 2830 if (!this.losHealRangeQuery) 2831 return false; 2832 2833 var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 2834 var ents = rangeMan.ResetActiveQuery(this.losHealRangeQuery); 2835 2836 for each (var ent in ents) 2837 { 2838 if (this.CanHeal(ent)) 2839 { 2840 this.PushOrderFront("Heal", { "target": ent, "force": false }); 2841 return true; 2842 } 2843 } 2844 // We haven't found any target to heal 2845 return false; 2846 }; 2847 2848 UnitAI.prototype.GetQueryRange = function(iid) 2573 2849 { 2574 2850 var ret = { "min": 0, "max": 0 }; 2575 2851 if (this.GetStance().respondStandGround) 2576 2852 { 2577 var cmpRanged = Engine.QueryInterface(this.entity, IID_Attack);2853 var cmpRanged = Engine.QueryInterface(this.entity, iid); 2578 2854 if (!cmpRanged) 2579 2855 return ret; 2580 var range = cmpRanged.GetRange(cmpRanged.GetBestAttack());2856 var range = iid !== IID_Attack ? cmpRanged.GetRange() : cmpRanged.GetBestAttack(); 2581 2857 ret.min = range.min; 2582 2858 ret.max = range.max; 2583 2859 } … … 2591 2867 } 2592 2868 else if (this.GetStance().respondHoldGround) 2593 2869 { 2594 var cmpRanged = Engine.QueryInterface(this.entity, IID_Attack);2870 var cmpRanged = Engine.QueryInterface(this.entity, iid); 2595 2871 if (!cmpRanged) 2596 2872 return ret; 2597 var range = cmpRanged.GetRange(cmpRanged.GetBestAttack());2873 var range = iid !== IID_Attack ? cmpRanged.GetRange() : cmpRanged.GetBestAttack(); 2598 2874 var cmpVision = Engine.QueryInterface(this.entity, IID_Vision); 2599 2875 if (!cmpVision) 2600 2876 return ret; 2601 2877 var halfvision = cmpVision.GetRange() / 2; 2602 2878 ret.max = range.max + halfvision; 2603 2879 } 2880 // We probably have stance 'passive' and we wouldn't have a range, 2881 // but as it is the default for healers we need to set it to something sane. 2882 else if (iid === IID_Heal) 2883 { 2884 var cmpRanged = Engine.QueryInterface(this.entity, iid); 2885 if (!cmpRanged) 2886 return ret; 2887 var range = cmpRanged.GetRange(); 2888 ret.min = range.min; 2889 ret.max = range.max; 2890 } 2604 2891 return ret; 2605 2892 }; 2606 2893 … … 2690 2977 return true; 2691 2978 }; 2692 2979 2980 UnitAI.prototype.CanHeal = function(target) 2981 { 2982 // Formation controllers should always respond to commands 2983 // (then the individual units can make up their own minds) 2984 if (this.IsFormationController()) 2985 return true; 2986 2987 // Verify that we're able to respond to Heal commands 2988 var cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); 2989 if (!cmpHeal) 2990 return false; 2991 2992 // Verify that the target is alive 2993 if (!this.TargetIsAlive(target)) 2994 return false; 2995 2996 // Verify that the target is owned by the same player as the entity or of an ally 2997 var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); 2998 if (!cmpOwnership || !(IsOwnedByPlayer(cmpOwnership.GetOwner(), target) || IsOwnedByAllyOfPlayer(cmpOwnership.GetOwner(), target))) 2999 return false; 3000 3001 // Verify that the target is not unhealable (or at max health) 3002 var cmpHealth = Engine.QueryInterface(target, IID_Health); 3003 if (!cmpHealth || cmpHealth.IsUnhealable()) 3004 return false; 3005 3006 // Verify that the target has no unhealable class 3007 var cmpIdentity = Engine.QueryInterface(target, IID_Identity); 3008 if (!cmpIdentity) 3009 return false; 3010 for each (var unhealableClass in cmpHeal.GetUnhealableClasses()) 3011 { 3012 if (cmpIdentity.HasClass(unhealableClass) != -1) 3013 { 3014 return false; 3015 } 3016 } 3017 3018 // Verify that the target is a healable class 3019 var healable = false; 3020 for each (var healableClass in cmpHeal.GetHealableClasses()) 3021 { 3022 if (cmpIdentity.HasClass(healableClass) != -1) 3023 { 3024 healable = true; 3025 } 3026 } 3027 if (!healable) 3028 return false; 3029 3030 return true; 3031 }; 3032 2693 3033 UnitAI.prototype.CanReturnResource = function(target, checkCarriedResource) 2694 3034 { 2695 3035 // Formation controllers should always respond to commands -
binaries/data/mods/public/simulation/components/interfaces/Heal.js
1 Engine.RegisterInterface("Heal"); -
binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
5 5 Engine.LoadComponentScript("interfaces/DamageReceiver.js"); 6 6 Engine.LoadComponentScript("interfaces/Foundation.js"); 7 7 Engine.LoadComponentScript("interfaces/GarrisonHolder.js"); 8 Engine.LoadComponentScript("interfaces/Heal.js"); 8 9 Engine.LoadComponentScript("interfaces/Health.js"); 9 10 Engine.LoadComponentScript("interfaces/Promotion.js"); 10 11 Engine.LoadComponentScript("interfaces/RallyPoint.js"); … … 271 272 GetHitpoints: function() { return 50; }, 272 273 GetMaxHitpoints: function() { return 60; }, 273 274 IsRepairable: function() { return false; }, 275 IsUnhealable: function() { return false; }, 274 276 }); 275 277 276 278 AddMock(10, IID_Identity, { … … 304 306 hitpoints: 50, 305 307 maxHitpoints: 60, 306 308 needsRepair: false, 309 needsHeal: true, 307 310 buildEntities: ["test1", "test2"], 308 311 barterMarket: { 309 312 prices: { "buy": {"food":150}, "sell": {"food":25} }, -
binaries/data/mods/public/simulation/components/tests/test_UnitAI.js
4 4 Engine.LoadComponentScript("interfaces/Attack.js"); 5 5 Engine.LoadComponentScript("interfaces/DamageReceiver.js"); 6 6 Engine.LoadComponentScript("interfaces/Formation.js"); 7 Engine.LoadComponentScript("interfaces/Heal.js"); 7 8 Engine.LoadComponentScript("interfaces/Health.js"); 8 9 Engine.LoadComponentScript("interfaces/ResourceSupply.js"); 9 10 Engine.LoadComponentScript("interfaces/Timer.js"); -
binaries/data/mods/public/simulation/helpers/Commands.js
66 66 }); 67 67 break; 68 68 69 case "heal": 70 if (g_DebugCommands && !(IsOwnedByPlayer(player, cmd.target) || IsOwnedByAllyOfPlayer(player, cmd.target))) 71 { 72 // This check is for debugging only! 73 warn("Invalid command: heal target is not owned by player "+player+" or their ally: "+uneval(cmd)); 74 } 75 76 // See UnitAI.CanHeal for target checks 77 var entities = FilterEntityList(cmd.entities, player, controlAllUnits); 78 GetFormationUnitAIs(entities).forEach(function(cmpUnitAI) { 79 cmpUnitAI.Heal(cmd.target, cmd.queued); 80 }); 81 break; 82 69 83 case "repair": 70 84 // This covers both repairing damaged buildings, and constructing unfinished foundations 71 85 if (g_DebugCommands && !IsOwnedByAllyOfPlayer(player, cmd.target)) -
binaries/data/mods/public/simulation/templates/template_structure.xml
33 33 <Health> 34 34 <DeathType>corpse</DeathType> 35 35 <RegenRate>0</RegenRate> 36 < Healable>false</Healable>36 <Unhealable>true</Unhealable> 37 37 <Repairable>true</Repairable> 38 38 </Health> 39 39 <Identity> -
binaries/data/mods/public/simulation/templates/template_unit.xml
30 30 <DeathType>corpse</DeathType> 31 31 <Max>100</Max> 32 32 <RegenRate>0</RegenRate> 33 < Healable>true</Healable>33 <Unhealable>false</Unhealable> 34 34 <Repairable>false</Repairable> 35 35 </Health> 36 36 <Identity> -
binaries/data/mods/public/simulation/templates/template_unit_fauna_hunt_whale.xml
4 4 <Max>100</Max> 5 5 <DeathType>remain</DeathType> 6 6 <RegenRate>1</RegenRate> 7 < Healable>false</Healable>7 <Unhealable>true</Unhealable> 8 8 <Repairable>false</Repairable> 9 9 </Health> 10 10 <Identity> -
binaries/data/mods/public/simulation/templates/template_unit_mechanical.xml
7 7 <SinkAccel>2.0</SinkAccel> 8 8 </Decay> 9 9 <Health> 10 < Healable>false</Healable>10 <Unhealable>true</Unhealable> 11 11 <Repairable>true</Repairable> 12 12 </Health> 13 13 <Identity> -
binaries/data/mods/public/simulation/templates/template_unit_support_healer.xml
5 5 <Pierce>2.0</Pierce> 6 6 <Crush>2.0</Crush> 7 7 </Armour> 8 <Auras> 9 <Heal> 10 <Radius>20</Radius> 11 <Speed>2000</Speed> 12 </Heal> 13 </Auras> 8 <Heal> 9 <Range>30</Range> 10 <HP>5</HP> 11 <Rate>2000</Rate> 12 <UnhealableClasses datatype="tokens"/> 13 <HealableClasses datatype="tokens">Support Infantry Cavalry</HealableClasses> 14 </Heal> 14 15 <Cost> 15 16 <Resources> 16 17 <metal>120</metal> … … 23 24 <Identity> 24 25 <Classes datatype="tokens">Healer</Classes> 25 26 <GenericName>Healer</GenericName> 26 <Tooltip>Heal units within his Aura. (Not implemented yet)</Tooltip>27 <Tooltip>Heal units.</Tooltip> 27 28 </Identity> 29 <Promotion> 30 <RequiredXp>100</RequiredXp> 31 </Promotion> 28 32 <Sound> 29 33 <SoundGroups> 30 34 <select>voice/hellenes/civ/civ_male_select.xml</select> -
binaries/data/mods/public/simulation/templates/template_unit_support_slave.xml
36 36 </Resources> 37 37 </Cost> 38 38 <Health> 39 < Healable>false</Healable>39 <Unhealable>true</Unhealable> 40 40 </Health> 41 41 <Identity> 42 42 <Classes datatype="tokens">Slave</Classes> -
binaries/data/mods/public/simulation/templates/units/cart_support_healer_b.xml
6 6 <History>Tanit (also spelled TINITH, TINNIT, or TINT), chief goddess of Carthage, equivalent of Astarte. Although she seems to have had some connection with the heavens, she was also a mother goddess, and fertility symbols often accompany representations of her. She was probably the consort of Baal Hammon (or Amon), the chief god of Carthage, and was often given the attribute "face of Baal." Although Tanit did not appear at Carthage before the 5th century BC, she soon eclipsed the more established cult of Baal Hammon and, in the Carthaginian area at least, was frequently listed before him on the monuments. In the worship of Tanit and Baal Hammon, children, probably firstborn, were sacrificed. Ample evidence of the practice has been found west of Carthage in the precinct of Tanit, where a tofet (a sanctuary for the sacrifice of children) was discovered. Tanit was also worshipped on Malta, Sardinia, and in Spain. There is no other reason for giving the Carthaginians a priestess instead of a priest in 0 A.D., although Tanit was the most popular of their two main gods with the people. </History> 7 7 <Tooltip>Heal units within her aura. (Not implemented yet)</Tooltip> 8 8 <Icon>units/cart_support_healer.png</Icon> 9 <Rank>Basic</Rank> 9 10 </Identity> 11 <Promotion> 12 <Entity>units/cart_support_healer_a</Entity> 13 </Promotion> 10 14 <VisualActor> 11 15 <Actor>units/carthaginians/healer.xml</Actor> 12 16 </VisualActor> -
binaries/data/mods/public/simulation/templates/units/celt_support_healer_b.xml
5 5 <SpecificName>Druides </SpecificName> 6 6 <History>A druid may be one of many different professions; priest, historian, lawyer, judges, teachers, philosophers, poets, composers, musicians, astronomers, prophets, councillors, high craftsmen like a blacksmith, the classes of the 'men of art', and sometimes kings, chieftains, or other politicians. Druids were very hierarchal, with classes and ranks based on the length of their education and what fields they practiced. They learned their trades through mnemonics by way of poetry and songs, as writing was rarely used by Celts outside of prayers on votive objects, or lists of names for migratory records.</History> 7 7 <Icon>units/celt_support_healer.png</Icon> 8 <Rank>Basic</Rank> 8 9 </Identity> 10 <Promotion> 11 <Entity>units/celt_support_healer_a</Entity> 12 </Promotion> 9 13 <VisualActor> 10 14 <Actor>units/celts/healer.xml</Actor> 11 15 </VisualActor> -
binaries/data/mods/public/simulation/templates/units/hele_support_healer_b.xml
5 5 <SpecificName>Hiereús</SpecificName> 6 6 <History>The art of medicine was widely practised in Classical Greece. Hippocrates was the first physician to separate religion and superstition from actual medicine, and many others followed his lead.</History> 7 7 <Icon>units/hele_support_healer.png</Icon> 8 <Rank>Basic</Rank> 8 9 </Identity> 10 <Promotion> 11 <Entity>units/hele_support_healer_a</Entity> 12 </Promotion> 9 13 <VisualActor> 10 14 <Actor>units/hellenes/healer.xml</Actor> 11 15 </VisualActor> -
binaries/data/mods/public/simulation/templates/units/iber_support_healer_b.xml
5 5 <SpecificName>Sacerdotisa de Ataekina</SpecificName> 6 6 <History> To the best of our knowledge, only one 'temple'-like structure has been found on the Iberian Peninsula dating from the times and the Iberians worshiped their pantheon of gods at small home altars; however, a very special sculptured head and torso was found in a farmer's field around the turn of the 20th century of a personage who was obviously someone of great substance. As the two principal gods, of the many worshiped, were male Endovellikos and female Ataekina, we thought it would be nice to adopt The Lady of Elche as our priestess-healer representing Ataekina. We know from archelogy and the Romans that Ataekina was associated with spring, the changing of seasons, and nature in general. Ataekina also seems to have been associated with the cycle of birth-death-rebirth.</History> 7 7 <Icon>units/iber_support_healer.png</Icon> 8 <Rank>Basic</Rank> 8 9 </Identity> 10 <Promotion> 11 <Entity>units/iber_support_healer_a</Entity> 12 </Promotion> 9 13 <VisualActor> 10 14 <Actor>units/iberians/healer.xml</Actor> 11 15 </VisualActor> -
binaries/data/mods/public/simulation/templates/units/pers_support_healer_b.xml
6 6 <SpecificName>Maguš Mada</SpecificName> 7 7 <History>Under both the Medes and later the Persian the tribe of the Magi or the Magians were the masters of religious and oral tradition, comparable to the Levites of the Bible. They were connected to Zoroastrianism, but likely tended to other Iranian cults as well. Aside from religious duties the Magians also functioned as the Great King's bureaucrats and kept his administration running.</History> 8 8 <Icon>units/pers_support_healer.png</Icon> 9 <Rank>Basic</Rank> 9 10 </Identity> 11 <Promotion> 12 <Entity>units/pers_support_healer_a</Entity> 13 </Promotion> 10 14 <VisualActor> 11 15 <Actor>units/persians/healer.xml</Actor> 12 16 </VisualActor> -
binaries/data/mods/public/simulation/templates/units/rome_support_healer_b.xml
6 6 <SpecificName>Pontifex Minoris</SpecificName> 7 7 <History>During the Republic, the position of priest was elevated and required a lot of responsibilities, which is why priests were by no means chosen randomly. The position of Pontifex Maximus, the high priest of the Roman religion, was occupied by such prominent figures as Julius Caesar, Marcus Aemilius Lepidus and Augustus.</History> 8 8 <Icon>units/rome_support_healer.png</Icon> 9 <Rank>Basic</Rank> 9 10 </Identity> 11 <Promotion> 12 <Entity>units/rome_support_healer_a</Entity> 13 </Promotion> 10 14 <VisualActor> 11 15 <Actor>units/romans/healer.xml</Actor> 12 16 </VisualActor> -
source/scriptinterface/ScriptInterface.h
38 38 // Set the maximum number of function arguments that can be handled 39 39 // (This should be as small as possible (for compiler efficiency), 40 40 // but as large as necessary for all wrapped functions) 41 #define SCRIPT_INTERFACE_MAX_ARGS 641 #define SCRIPT_INTERFACE_MAX_ARGS 7 42 42 43 43 // TODO: what's a good default? 44 44 #define DEFAULT_RUNTIME_SIZE 16 * 1024 * 1024 -
source/simulation2/MessageTypes.h
230 230 int32_t to; 231 231 }; 232 232 233 class CMessageShowAsModified : public CMessage 234 { 235 public: 236 DEFAULT_MESSAGE_IMPL(ShowAsModified) 237 238 CMessageShowAsModified(entity_id_t entity, bool showAsModified) : 239 entity(entity), showAsModified(showAsModified) 240 { 241 } 242 243 entity_id_t entity; 244 bool showAsModified; 245 }; 246 233 247 /** 234 248 * Sent during TurnStart. 235 249 * -
source/simulation2/TypeList.h
41 41 MESSAGE(Create) 42 42 MESSAGE(Destroy) 43 43 MESSAGE(OwnershipChanged) 44 MESSAGE(ShowAsModified) 44 45 MESSAGE(PositionChanged) 45 46 MESSAGE(MotionChanged) 46 47 MESSAGE(RangeUpdate) -
source/simulation2/components/CCmpRangeManager.cpp
50 50 u32 ownersMask; 51 51 i32 interface; 52 52 std::vector<entity_id_t> lastMatch; 53 bool showModified; 53 54 }; 54 55 55 56 /** … … 69 70 */ 70 71 struct EntityData 71 72 { 72 EntityData() : retainInFog(0), owner(-1), inWorld(0) { }73 EntityData() : retainInFog(0), owner(-1), inWorld(0), showAsModified(0) { } 73 74 entity_pos_t x, z; 74 75 entity_pos_t visionRange; 75 76 u8 retainInFog; // boolean 76 77 i8 owner; 77 78 u8 inWorld; // boolean 79 u8 showAsModified; // boolean 78 80 }; 79 81 80 82 cassert(sizeof(EntityData) == 16); … … 95 97 serialize.NumberU32_Unbounded("owners mask", value.ownersMask); 96 98 serialize.NumberI32_Unbounded("interface", value.interface); 97 99 SerializeVector<SerializeU32_Unbounded>()(serialize, "last match", value.lastMatch); 100 serialize.Bool("show modified", value.showModified); 98 101 } 99 102 }; 100 103 … … 112 115 serialize.NumberU8("retain in fog", value.retainInFog, 0, 1); 113 116 serialize.NumberI8_Unbounded("owner", value.owner); 114 117 serialize.NumberU8("in world", value.inWorld, 0, 1); 118 serialize.NumberU8("show as modified", value.showAsModified, 0, 1); 115 119 } 116 120 }; 117 121 … … 161 165 componentManager.SubscribeGloballyToMessageType(MT_Create); 162 166 componentManager.SubscribeGloballyToMessageType(MT_PositionChanged); 163 167 componentManager.SubscribeGloballyToMessageType(MT_OwnershipChanged); 168 componentManager.SubscribeGloballyToMessageType(MT_ShowAsModified); 164 169 componentManager.SubscribeGloballyToMessageType(MT_Destroy); 165 170 166 171 componentManager.SubscribeToMessageType(MT_Update); … … 384 389 385 390 break; 386 391 } 392 case MT_ShowAsModified: 393 { 394 const CMessageShowAsModified& msgData = static_cast<const CMessageShowAsModified&> (msg); 395 entity_id_t ent = msgData.entity; 396 397 std::map<u32, EntityData>::iterator it = m_EntityData.find(ent); 398 399 // Ignore if we're not already tracking this entity 400 if (it == m_EntityData.end()) 401 break; 402 403 it->second.showAsModified = msgData.showAsModified ? 1 : 0; 404 405 break; 406 } 387 407 case MT_Destroy: 388 408 { 389 409 const CMessageDestroy& msgData = static_cast<const CMessageDestroy&> (msg); … … 514 534 515 535 virtual tag_t CreateActiveQuery(entity_id_t source, 516 536 entity_pos_t minRange, entity_pos_t maxRange, 517 std::vector<int> owners, int requiredInterface )537 std::vector<int> owners, int requiredInterface, bool showModified) 518 538 { 519 539 tag_t id = m_QueryNext++; 520 m_Queries[id] = ConstructQuery(source, minRange, maxRange, owners, requiredInterface );540 m_Queries[id] = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, showModified); 521 541 522 542 return id; 523 543 } … … 565 585 { 566 586 PROFILE("ExecuteQuery"); 567 587 568 Query q = ConstructQuery(source, minRange, maxRange, owners, requiredInterface );588 Query q = ConstructQuery(source, minRange, maxRange, owners, requiredInterface, false); 569 589 570 590 std::vector<entity_id_t> r; 571 591 … … 677 697 std::set_difference(r.begin(), r.end(), q.lastMatch.begin(), q.lastMatch.end(), std::back_inserter(added)); 678 698 std::set_difference(q.lastMatch.begin(), q.lastMatch.end(), r.begin(), r.end(), std::back_inserter(removed)); 679 699 700 if (q.showModified) 701 { 702 for (std::vector<entity_id_t>::iterator iter = r.begin(); iter != r.end(); ++iter) 703 { 704 std::map<u32, EntityData>::iterator bla = m_EntityData.find(*iter); 705 if (bla == m_EntityData.end()) 706 continue; 707 if (bla->second.showAsModified) 708 { 709 // We just add the entity to removed and added if it is not there yet 710 if (find(removed.begin(), removed.end(), *iter) == removed.end() 711 && find(added.begin(), added.end(), *iter) == added.end()) 712 { 713 removed.push_back(*iter); 714 added.push_back(*iter); 715 } 716 } 717 } 718 } 680 719 if (added.empty() && removed.empty()) 681 720 continue; 682 721 … … 773 812 774 813 Query ConstructQuery(entity_id_t source, 775 814 entity_pos_t minRange, entity_pos_t maxRange, 776 const std::vector<int>& owners, int requiredInterface )815 const std::vector<int>& owners, int requiredInterface, bool showModified) 777 816 { 778 817 // Min range must be non-negative 779 818 if (minRange < entity_pos_t::Zero()) … … 794 833 q.ownersMask |= CalcOwnerMask(owners[i]); 795 834 796 835 q.interface = requiredInterface; 836 q.showModified = showModified; 797 837 798 838 return q; 799 839 } -
source/simulation2/components/ICmpRangeManager.cpp
35 35 36 36 BEGIN_INTERFACE_WRAPPER(RangeManager) 37 37 DEFINE_INTERFACE_METHOD_5("ExecuteQuery", std::vector<entity_id_t>, ICmpRangeManager, ExecuteQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int) 38 DEFINE_INTERFACE_METHOD_ 5("CreateActiveQuery", ICmpRangeManager::tag_t, ICmpRangeManager, CreateActiveQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int)38 DEFINE_INTERFACE_METHOD_6("CreateActiveQuery", ICmpRangeManager::tag_t, ICmpRangeManager, CreateActiveQuery, entity_id_t, entity_pos_t, entity_pos_t, std::vector<int>, int, bool) 39 39 DEFINE_INTERFACE_METHOD_1("DestroyActiveQuery", void, ICmpRangeManager, DestroyActiveQuery, ICmpRangeManager::tag_t) 40 40 DEFINE_INTERFACE_METHOD_1("EnableActiveQuery", void, ICmpRangeManager, EnableActiveQuery, ICmpRangeManager::tag_t) 41 41 DEFINE_INTERFACE_METHOD_1("DisableActiveQuery", void, ICmpRangeManager, DisableActiveQuery, ICmpRangeManager::tag_t) -
source/simulation2/components/ICmpRangeManager.h
96 96 * @param maxRange non-negative maximum distance in metres (inclusive); or -1.0 to ignore distance. 97 97 * @param owners list of player IDs that matching entities may have; -1 matches entities with no owner. 98 98 * @param requiredInterface if non-zero, an interface ID that matching entities must implement. 99 * @param showModified show modified entitys that are within range. 99 100 * @return unique non-zero identifier of query. 100 101 */ 101 102 virtual tag_t CreateActiveQuery(entity_id_t source, 102 entity_pos_t minRange, entity_pos_t maxRange, std::vector<int> owners, int requiredInterface ) = 0;103 entity_pos_t minRange, entity_pos_t maxRange, std::vector<int> owners, int requiredInterface, bool showModified) = 0; 103 104 104 105 /** 105 106 * Destroy a query and clean up resources. This must be called when an entity no longer needs its -
source/simulation2/scripting/MessageTypeConversions.cpp
190 190 191 191 //////////////////////////////// 192 192 193 jsval CMessageShowAsModified::ToJSVal(ScriptInterface& scriptInterface) const 194 { 195 TOJSVAL_SETUP(); 196 SET_MSG_PROPERTY(entity); 197 SET_MSG_PROPERTY(showAsModified); 198 return OBJECT_TO_JSVAL(obj); 199 } 200 201 CMessage* CMessageShowAsModified::FromJSVal(ScriptInterface& scriptInterface, jsval val) 202 { 203 FROMJSVAL_SETUP(); 204 GET_MSG_PROPERTY(entity_id_t, entity); 205 GET_MSG_PROPERTY(bool, showAsModified); 206 return new CMessageShowAsModified(entity, showAsModified); 207 } 208 209 //////////////////////////////// 210 193 211 jsval CMessagePositionChanged::ToJSVal(ScriptInterface& scriptInterface) const 194 212 { 195 213 TOJSVAL_SETUP(); -
source/simulation2/system/InterfaceScripted.h
79 79 5, \ 80 80 JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT }, 81 81 82 #define DEFINE_INTERFACE_METHOD_6(scriptname, rettype, classname, methodname, arg1, arg2, arg3, arg4, arg5, arg6) \ 83 { scriptname, \ 84 ScriptInterface::callMethod<rettype, arg1, arg2, arg3, arg4, arg5, arg6, &class_##classname, classname, &classname::methodname>, \ 85 6, \ 86 JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT }, 87 82 88 #endif // INCLUDED_INTERFACE_SCRIPTED