| 11-02-2009, 08:28 PM | #1 |
So, over the summer, Pyrogasm was kind enough to write me a private system so that units would do more damage based on how high they are in comparison to the units they are fighting. This will add huuuuuge strategical advantages to my map. Unfortunately, the code only works for the units that are in the map when the map begins, and if units are added while the game is in progress via any method, the system won't work for them. I've talked to Pyrogasm about it, he said he'd get to it when he can, but, it'd be really helpful for this system to work properly sooner than later. I wanted this to be a private system for my map, but right now idc if others can see the code-- I just need this system to work. If someone could fix the flaws in the system, (why it won't work for units entering the map after the map has began), and if possible make the system work for non-structure units only, that would be extremely helpful, and I'd definitely give credits in the map (Risk Next Gen, if anyone cares) Required Libraries: JASS:library BoolexprUtils initializer init globals boolexpr BOOLEXPR_TRUE=null boolexpr BOOLEXPR_FALSE=null endglobals private function rettrue takes nothing returns boolean return true endfunction private function retfalse takes nothing returns boolean return false endfunction private function init takes nothing returns nothing set BOOLEXPR_TRUE=Condition(function rettrue) set BOOLEXPR_FALSE=Condition(function retfalse) endfunction endlibrary library LightLeaklessDamageDetect initializer Init // Creating threads off of this that last longer than the timeout below will likely cause issues, like everything blowing up (handle stack corruption) // It seems that threads created by timers, rather than executefunc / .evaluate / .execute are not affected. Any threads created from the timer thread are fine. // This being safe with even the usage laid out above isn't guarenteed. Use at own risk. // If you start getting random bugs, see if commenting out the timer line below (see comments) helps // If it does, report it in the thread for this script at [url]www.wc3campaigns.net[/url] globals private constant real SWAP_TIMEOUT = 600. // keep high; 600 should be about the right balance. endglobals globals private conditionfunc array func private integer funcNext = 0 private trigger current = null private trigger toDestroy = null private group swapGroup private rect mapRect endglobals // One of the only accessible functions. Use it to add a condition. Must return boolean type, and then have return false at the end. // Note that it's technically a condition, so if you put a wait in there, it'll die. But waits are lame anyway. function AddOnDamageFunc takes conditionfunc cf returns nothing call TriggerAddCondition(current, cf) set func[funcNext] = cf set funcNext = funcNext + 1 endfunction // These inline. For avoiding feedback loops. Feel free to make your own wrapper function for damage functions using this. function DisableDamageDetect takes nothing returns nothing call DisableTrigger(current) endfunction function EnableDamageDetect takes nothing returns nothing call EnableTrigger(current) endfunction // no more accessible functions, folks. //! textmacro CGLeaklessDamageDetectAddFilter takes UNIT // add here any conditions to add the unit to the trigger, example below, commented out: // if GetUnitTypeId($UNIT$) != 'h000' then // where 'h000' is a dummy unit call TriggerRegisterUnitEvent(current, $UNIT$, EVENT_UNIT_DAMAGED) // endif //! endtextmacro private function AddEx takes nothing returns boolean //! runtextmacro CGLeaklessDamageDetectAddFilter("GetFilterUnit()") return false endfunction private function Enters takes nothing returns boolean //! runtextmacro CGLeaklessDamageDetectAddFilter("GetTriggerUnit()") return false endfunction private function Swap takes nothing returns nothing local integer i = 0 local boolean b = IsTriggerEnabled(current) call DisableTrigger(current) if toDestroy != null then call DestroyTrigger(toDestroy) endif set toDestroy = current set current = CreateTrigger() if not(b) then call DisableTrigger(current) endif call GroupEnumUnitsInRect(swapGroup, mapRect, Filter(function AddEx)) loop exitwhen i >= funcNext call TriggerAddCondition(current, func[i]) set i = i + 1 endloop endfunction private function Init takes nothing returns nothing local trigger t = CreateTrigger() local region r = CreateRegion() local integer i = 0 set mapRect = GetWorldBounds() call RegionAddRect(r, mapRect) call TriggerRegisterEnterRegion(t, r, null) call TriggerAddCondition(t, Condition(function Enters)) set swapGroup = CreateGroup() set current = CreateTrigger() loop exitwhen i >= funcNext call TriggerAddCondition(current, func[i]) set i = i + 1 endloop call GroupEnumUnitsInRect(swapGroup, GetWorldBounds(), Filter(function AddEx)) // Commenting out the next line will make the system leak indexes and events, but should make it safer. call TimerStart(CreateTimer(), SWAP_TIMEOUT, true, function Swap) endfunction endlibrary Actual Code: JASS:library AttackHeightAdvantage initializer Init requires LightLeaklessDamageDetect, BoolexprUtils //Written by Pyrogasm for Psycomarauder // //It's pretty simple: just modify the below calculation however you like. Use teh maths! //Only return the bonus damage you'd like to apply (or subtract, if you give it negative damage) globals private constant integer ORB_ABILITYID = 'Admg' private constant integer ORB_BUFFID = 'Bdmg' private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL private location ZLoc = null endglobals private function DamageCalculation takes unit Source, unit Target, real Damage returns real local real D call MoveLocation(ZLoc, GetUnitX(Source), GetUnitY(Source)) set D = GetLocationZ(ZLoc) call MoveLocation(ZLoc, GetUnitX(Target), GetUnitY(Target)) set D = D-GetLocationZ(ZLoc) //For every 80 height, the attacker gains 7% damage bonus return D/80.00*0.07*Damage endfunction private function OnDamage takes nothing returns boolean local unit T = GetTriggerUnit() local unit S if GetUnitAbilityLevel(T, ORB_BUFFID) > 0 then set S = GetEventDamageSource() call UnitRemoveAbility(T, ORB_BUFFID) call DisableDamageDetect() call UnitDamageTarget(S, T, DamageCalculation(S, T, GetEventDamage()), false, false, ATTACK_TYPE, DAMAGE_TYPE, null) call EnableDamageDetect() set S = null endif set T = null return false endfunction private function EnterAdd takes nothing returns boolean local unit U = GetTriggerUnit() call UnitAddAbility(U, ORB_ABILITYID) call UnitMakeAbilityPermanent(U, true, ORB_ABILITYID) set U = null return false endfunction private function InitAdd takes nothing returns nothing local unit U = GetEnumUnit() call UnitAddAbility(U, ORB_ABILITYID) call UnitMakeAbilityPermanent(U, true, ORB_ABILITYID) set U = null endfunction private function Init takes nothing returns nothing local group G = CreateGroup() local trigger T = CreateTrigger() local region R = CreateRegion() call GroupEnumUnitsInRect(G, bj_mapInitialPlayableArea, BOOLEXPR_TRUE) call ForGroup(G, function InitAdd) call DestroyGroup(G) call RegionAddRect(R, bj_mapInitialPlayableArea) call TriggerRegisterEnterRegion(T, R, BOOLEXPR_TRUE) call TriggerAddCondition(T, Condition(function EnterAdd)) call RemoveRegion(R) set ZLoc = Location(0.00, 0.00) call AddOnDamageFunc(Condition(function OnDamage)) set G = null set R = null endfunction endlibrary |
| 11-02-2009, 08:54 PM | #2 |
Try removing this line in the Init function and see if it works: call RemoveRegion(R) |
| 11-02-2009, 08:57 PM | #3 |
Just from glancing over it, here's what I saw. JASS:
call RegionAddRect(R, bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(T, R, BOOLEXPR_TRUE)
call TriggerAddCondition(T, Condition(function EnterAdd))
call RemoveRegion(R)
Try taking out the RemoveRegion(R) call and see if it works. Right now it's detecting when units enter region R but then deletes region R..so nobody will ever enter it. |
| 11-02-2009, 09:28 PM | #4 |
Oh, the irony. A thousand thanks to both of you, it worked, lol. And thats what I waited 3 months to solve, a single line of code. Is it a quick fix to make this not work for buildings, and make this system optional? (Optional via mode on/off chosen by a host) |
| 11-02-2009, 09:43 PM | #5 |
It should be quite easy to make it not to work for buildings and to turn on/off the system, just put the Init Actions into a if block. For the building part, add these JASS:private function IsNotBuildingFilter takes nothing returns boolean return IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE) endfunction private function IsNotBuildingAction takes nothing returns boolean return IsUnitType(GetTriggerUnit(),UNIT_TYPE_STRUCTURE) endfunction JASS:
//...
call GroupEnumUnitsInRect(G, bj_mapInitialPlayableArea, BOOLEXPR_TRUE)
//...
call TriggerRegisterEnterRegion(T, R, BOOLEXPR_TRUE)
//..
JASS:
//...
call GroupEnumUnitsInRect(G, bj_mapInitialPlayableArea,Condition(function IsNotBuildingFilter))
//...
call TriggerRegisterEnterRegion(T, R,Condition(function IsNotBuildingAction))
//...
JASS:library AttackHeightAdvantage initializer Init requires LightLeaklessDamageDetect //Written by Pyrogasm for Psycomarauder // //It's pretty simple: just modify the below calculation however you like. Use teh maths! //Only return the bonus damage you'd like to apply (or subtract, if you give it negative damage) globals private constant boolean ENABLE_SYSTEM = true private constant integer ORB_ABILITYID = 'Admg' private constant integer ORB_BUFFID = 'Bdmg' private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL private location ZLoc = null endglobals private function IsNotBuildingFilter takes nothing returns boolean return IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE) endfunction private function IsNotBuildingAction takes nothing returns boolean return IsUnitType(GetTriggerUnit(),UNIT_TYPE_STRUCTURE) endfunction private function DamageCalculation takes unit Source, unit Target, real Damage returns real local real D call MoveLocation(ZLoc, GetUnitX(Source), GetUnitY(Source)) set D = GetLocationZ(ZLoc) call MoveLocation(ZLoc, GetUnitX(Target), GetUnitY(Target)) set D = D-GetLocationZ(ZLoc) //For every 80 height, the attacker gains 7% damage bonus return D/80.00*0.07*Damage endfunction private function OnDamage takes nothing returns boolean local unit T = GetTriggerUnit() local unit S if GetUnitAbilityLevel(T, ORB_BUFFID) > 0 then set S = GetEventDamageSource() call UnitRemoveAbility(T, ORB_BUFFID) call DisableDamageDetect() call UnitDamageTarget(S, T, DamageCalculation(S, T, GetEventDamage()), false, false, ATTACK_TYPE, DAMAGE_TYPE, null) call EnableDamageDetect() set S = null endif set T = null return false endfunction private function EnterAdd takes nothing returns boolean local unit U = GetTriggerUnit() call UnitAddAbility(U, ORB_ABILITYID) call UnitMakeAbilityPermanent(U, true, ORB_ABILITYID) set U = null return false endfunction private function InitAdd takes nothing returns nothing local unit U = GetEnumUnit() call UnitAddAbility(U, ORB_ABILITYID) call UnitMakeAbilityPermanent(U, true, ORB_ABILITYID) set U = null endfunction private function Init takes nothing returns nothing local group G = CreateGroup() local trigger T = CreateTrigger() local region R = CreateRegion() if(ENABLE_SYSTEM)then call GroupEnumUnitsInRect(G, bj_mapInitialPlayableArea,Condition(function IsNotBuildingFilter)) call ForGroup(G, function InitAdd) call RegionAddRect(R, bj_mapInitialPlayableArea) call TriggerRegisterEnterRegion(T, R,Condition(function IsNotBuildingAction)) call TriggerAddCondition(T, Condition(function EnterAdd)) set ZLoc = Location(0.00, 0.00) call AddOnDamageFunc(Condition(function OnDamage)) endif call DestroyGroup(G) set G = null set R = null endfunction endlibrary Sorry ... misread the choice with the host, this one is for the map maker. Let me think a bit about it. Alright, here would a fast solution, however you can only choose to activate the system once per game. When it is activated you can not deactivate it, but i would only require some more slightly modifactions to enable that too. To activate it, you have to run the function AttackHeightAdvantage_ToogleSystem(). Gonna edit this post later again. JASS:library AttackHeightAdvantage requires LightLeaklessDamageDetect //Written by Pyrogasm for Psycomarauder // //It's pretty simple: just modify the below calculation however you like. Use teh maths! //Only return the bonus damage you'd like to apply (or subtract, if you give it negative damage) globals private boolean SYSTEM_ENABLED = false private boolean FIRST_START = true private constant integer ORB_ABILITYID = 'Admg' private constant integer ORB_BUFFID = 'Bdmg' private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL private location ZLoc = null endglobals private function IsNotBuildingFilter takes nothing returns boolean return IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE) endfunction private function IsNotBuildingAction takes nothing returns boolean return IsUnitType(GetTriggerUnit(),UNIT_TYPE_STRUCTURE) endfunction private function DamageCalculation takes unit Source, unit Target, real Damage returns real local real D call MoveLocation(ZLoc, GetUnitX(Source), GetUnitY(Source)) set D = GetLocationZ(ZLoc) call MoveLocation(ZLoc, GetUnitX(Target), GetUnitY(Target)) set D = D-GetLocationZ(ZLoc) //For every 80 height, the attacker gains 7% damage bonus return D/80.00*0.07*Damage endfunction private function OnDamage takes nothing returns boolean local unit T = GetTriggerUnit() local unit S if GetUnitAbilityLevel(T, ORB_BUFFID) > 0 then set S = GetEventDamageSource() call UnitRemoveAbility(T, ORB_BUFFID) call DisableDamageDetect() call UnitDamageTarget(S, T, DamageCalculation(S, T, GetEventDamage()), false, false, ATTACK_TYPE, DAMAGE_TYPE, null) call EnableDamageDetect() set S = null endif set T = null return false endfunction private function EnterAdd takes nothing returns boolean local unit U = GetTriggerUnit() call UnitAddAbility(U, ORB_ABILITYID) call UnitMakeAbilityPermanent(U, true, ORB_ABILITYID) set U = null return false endfunction private function InitAdd takes nothing returns nothing local unit U = GetEnumUnit() call UnitAddAbility(U, ORB_ABILITYID) call UnitMakeAbilityPermanent(U, true, ORB_ABILITYID) set U = null endfunction public function ToogleSystem takes nothing returns nothing local group G = null local trigger T = null local region R = null if(not SYSTEM_ENABLED)then set SYSTEM_ENABLED = true if(FIRST_START)then set FIRST_START = false set G = CreateGroup() set T = CreateTrigger() set R = CreateRegion() call GroupEnumUnitsInRect(G, bj_mapInitialPlayableArea,Condition(function IsNotBuildingFilter)) call ForGroup(G, function InitAdd) call RegionAddRect(R, bj_mapInitialPlayableArea) call TriggerRegisterEnterRegion(T, R,Condition(function IsNotBuildingAction)) call TriggerAddCondition(T, Condition(function EnterAdd)) set ZLoc = Location(0.00, 0.00) call AddOnDamageFunc(Condition(function OnDamage)) call DestroyGroup(G) set G = null set R = null endif else set SYSTEM_ENABLED = false endif endfunction endlibrary |
| 11-02-2009, 09:52 PM | #6 |
Well, its not necessarily for the host, but I planned to make it so that if its not being hosted by Ghost++, the player in slot 1 will decide via modes I allow them to configure. I don't pretend to know anything about (v)Jass, but it seems that IF the system can be turned off, I should be able to easily determine whether or not to turn it off via other triggers. Is there a way to turn the system on/off via something simple like a GUI integer variable, and the AHA system will check whether or not the integer is set to x in order for it to work? |
| 11-03-2009, 04:09 PM | #7 |
JASS:library AttackHeightAdvantage initializer Init requires LightLeaklessDamageDetect, BoolexprUtils //Written by Pyrogasm for Psycomarauder // //It's pretty simple: just modify the below calculation however you like. Use teh maths! //Only return the bonus damage you'd like to apply (or subtract, if you give it negative damage) globals private constant integer ORB_ABILITYID = 'Admg' private constant integer ORB_BUFFID = 'Bdmg' private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL private boolean SystemActive = false private location ZLoc = null endglobals private function DamageCalculation takes unit Source, unit Target, real Damage returns real local real D call MoveLocation(ZLoc, GetUnitX(Source), GetUnitY(Source)) set D = GetLocationZ(ZLoc) call MoveLocation(ZLoc, GetUnitX(Target), GetUnitY(Target)) set D = D-GetLocationZ(ZLoc) //For every 80 height, the attacker gains 7% damage bonus return D/80.00*0.07*Damage endfunction function AttackHeightAdvantage_Activate takes boolean activate returns nothing set SystemActive = activate endfunction private function OnDamage takes nothing returns boolean local unit T = GetTriggerUnit() local unit S = GetEventDamageSource() if GetUnitAbilityLevel(T, ORB_BUFFID) > 0 and SystemActive and not IsUnitType(S, UNIT_TYPE_BUILDING) then call UnitRemoveAbility(T, ORB_BUFFID) call DisableDamageDetect() call UnitDamageTarget(S, T, DamageCalculation(S, T, GetEventDamage()), false, false, ATTACK_TYPE, DAMAGE_TYPE, null) call EnableDamageDetect() endif set T = null set S = null return false endfunction private function EnterAdd takes nothing returns boolean local unit U = GetTriggerUnit() if not IsUnitType(U, UNIT_TYPE_BUILDING) then call UnitAddAbility(U, ORB_ABILITYID) call UnitMakeAbilityPermanent(U, true, ORB_ABILITYID) endif set U = null return false endfunction private function InitAdd takes nothing returns nothing local unit U = GetEnumUnit() call UnitAddAbility(U, ORB_ABILITYID) call UnitMakeAbilityPermanent(U, true, ORB_ABILITYID) set U = null endfunction private function Init takes nothing returns nothing local group G = CreateGroup() local trigger T = CreateTrigger() local region R = CreateRegion() call GroupEnumUnitsInRect(G, bj_mapInitialPlayableArea, BOOLEXPR_TRUE) call ForGroup(G, function InitAdd) call DestroyGroup(G) call RegionAddRect(R, bj_mapInitialPlayableArea) call TriggerRegisterEnterRegion(T, R, BOOLEXPR_TRUE) call TriggerAddCondition(T, Condition(function EnterAdd)) set ZLoc = Location(0.00, 0.00) call AddOnDamageFunc(Condition(function OnDamage)) set G = null set R = null endfunction endlibrary Just do Trigger: Custom Script: call AttackHeightAdvantage_Activate(true) |
| 11-03-2009, 08:12 PM | #8 |
Thanks everyone. 3 questions: Is this system pretty efficient? My map is a risk map; there can be up to thousands of units attacking all over. From the tests I've ran it seems to have done pretty well, just wondering what you guys thought. Also, when a unit is dealing damage, is this system giving a bonus based on how high the unit is overall, or how high in comparison to the unit being attacked? Also, are units that are attacking from below dealing less damage, or the normal amount? EDIT: Element, your version of the code doesnt compile properly: Line 3739, 3755- Undeclared Variable UNIT_TYPE_BUILDING |
| 11-03-2009, 10:35 PM | #9 |
2. More damage based on comparison between the units' height, as you can see here: JASS:local real D call MoveLocation(ZLoc, GetUnitX(Source), GetUnitY(Source)) set D = GetLocationZ(ZLoc) call MoveLocation(ZLoc, GetUnitX(Target), GetUnitY(Target)) set D = D-GetLocationZ(ZLoc) //For every 80 height, the attacker gains 7% damage bonus return D/80.00*0.07*Damage 3. Units attacking from a lower height deals the same damage, since there is nothing that is actually reducing the attack itself. The bonus damage is given, if I'm not mistaken, by triggers and is dealt through triggers (the unit does not actually have +X attack). |
| 11-05-2009, 03:16 AM | #10 |
hmnn I think it is a bad habit to use capitals for local variables. |
| 11-05-2009, 03:37 AM | #11 |
Vex, if they are in all lowercase, will that make it compile correctly and work properly? |
| 11-05-2009, 03:41 AM | #12 |
The good karma may help, but not really. It is UNIT_TYPE_STRUCTURE, it was a typo. |
