• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Looking for good insight

Status
Not open for further replies.
Level 4
Joined
Mar 14, 2009
Messages
98
So I'm making a spell and it's supposed to make every unit in the AoE invulnerable to any unit outside the AoE. (Sort of like the defiler's cloud in starcraft.) But then, units inside should still be able to damage other units inside. For normal attacks, it's really easy, and I've got it all covered.

For spells it's a lot more complicated. Dummy units make this thing a lot more complicated than it already is. Lots of spells made by other spell makers use dummy units. In fact, almost all of them do. And they usually make the dummy units on the target unit itself. So, that's an issue for me. And then there's spells from outside the AoE with debuffs. Stormbolts from the outside still stun, etc.

Edit: I'll just stop units from attacking when they try to attack something inside. That solves orb effects.

So... any ideas on this one?

By the way, I'm doing this for a spell request, so I have no idea what other spells are to be included in the map.
 
Last edited:
Level 6
Joined
Mar 20, 2008
Messages
208
I think posting the code might help us get an idea of where to start in helping you.

Or atleast go into a bit more depth.
 
Level 4
Joined
Mar 14, 2009
Messages
98
Alright, here's the trigger as of this moment. It's pretty long and complicated, so yeah...

JASS:
library Water
    globals
        private unit array units[8192]
        private integer array data[8192]
        private trigger array triggers[8192]
        private unit WaterPreventDamageTarget
        private real WaterPreventDamageAmount
    endglobals
    
    private function H2I takes handle h returns integer
        return h
        return 0
    endfunction
    
    function ReleaseUnitSpecialData takes unit u returns nothing
        local integer i=H2I(u)-0x100000
        set i=i-(i/4096)*4096
        loop
            exitwhen units[i] == u
            set i=i+1
        endloop
        set units[i] = null
        set data[i] = 0
        call DestroyTrigger(triggers[i])
        set triggers[i] = null
    endfunction
    
    private function ReleaseData takes nothing returns nothing
        call ReleaseUnitSpecialData(GetTriggerUnit())
    endfunction

    function SetUnitSpecialData takes unit u, integer value returns nothing
        local integer i=H2I(u)-0x100000
        set i=i-(i/4096)*4096
        loop
            exitwhen triggers[i] == null
            set i=i+1
        endloop
        set units[i] = u
        set data[i]=value
        set triggers[i] = CreateTrigger()
        call TriggerRegisterUnitEvent(triggers[i], u, EVENT_UNIT_DECAY)
        call TriggerAddAction(triggers[i], function ReleaseData)
    endfunction

    function GetUnitSpecialData takes unit u returns integer
        local integer i=H2I(u)-0x100000
        set i=i-(i/4096)*4096
        loop
            exitwhen units[i] == u
            set i=i+1
        endloop
        return data[i]
    endfunction
    
    function ReleaseTriggerSpecialData takes trigger t returns nothing
        local integer i=H2I(t)-0x100000
        set i=i-(i/4096)*4096
        loop
            exitwhen triggers[i] == t
            set i=i+1
        endloop
        set data[i] = 0
        set triggers[i] = null
    endfunction
    
    function SetTriggerSpecialData takes trigger t, integer value returns nothing
       local integer i=H2I(t)-0x100000
        set i=i-(i/4096)*4096
        loop
            exitwhen triggers[i] == null
            set i=i+1
        endloop
        set data[i]=value
        set triggers[i] = t
    endfunction
    
    function GetTriggerSpecialData takes trigger t returns integer
        local integer i=H2I(t)-0x100000
        set i=i-(i/4096)*4096
        loop
            exitwhen triggers[i] == t
            set i=i+1
        endloop
        return data[i]
    endfunction
    function GetDistanceXY takes real FirstX, real FirstY, real SecondX, real SecondY returns real
        return SquareRoot((FirstX-SecondX)*(FirstX-SecondX)+(FirstY-SecondY)*(FirstY-SecondY))
    endfunction
    
    function AngleBetweenPointsXY takes real FirstX, real FirstY, real SecondX, real SecondY returns real
        return Atan2(SecondY-FirstY, SecondX-FirstX)
    endfunction
    
    private function PreventDamage2 takes nothing returns nothing
        call SetUnitState(WaterPreventDamageTarget, UNIT_STATE_LIFE, GetUnitState(WaterPreventDamageTarget, UNIT_STATE_LIFE)+WaterPreventDamageAmount)
        call ReleaseTimer(GetExpiredTimer())
    endfunction

    function PreventDamage takes unit whichUnit, real amountDamage returns nothing
        local real CurrentLife = GetUnitState(whichUnit, UNIT_STATE_LIFE)
        local real MaxLife = GetUnitState(whichUnit, UNIT_STATE_MAX_LIFE)
        if CurrentLife > 0.405 then
            if amountDamage > MaxLife-CurrentLife then
                if amountDamage > CurrentLife then
                    call SetUnitState(whichUnit, UNIT_STATE_LIFE, MaxLife)
                    set WaterPreventDamageAmount = amountDamage+CurrentLife-MaxLife
                else
                    set WaterPreventDamageAmount = amountDamage
                endif
                set WaterPreventDamageTarget = whichUnit
                call TimerStart(NewTimer(), 0.00, false, function PreventDamage2)
            else
                call SetUnitState(whichUnit, UNIT_STATE_LIFE, CurrentLife+amountDamage)
            endif
        endif
    endfunction
endlibrary

JASS:
scope WaterCage
globals
    constant real WATER_CAGE_BASE_RANGE = 250
    constant real WATER_CAGE_LEVEL_RANGE = 25
    constant real WATER_CAGE_BASE_DURATION = 10.00
    constant real WATER_CAGE_LEVEL_DURATION = 2.00
    constant integer WATER_CAGE_ABILITY_ID = 'aw01'
    constant integer PATHING_BLOCKER_BOTH = 'YTfb'
    constant integer WATER_CAGE_CENTER_DUMMY = 'n000'
    constant integer SPELL_IMMUNITY_ABILITY = 'aw02'
    constant string MANA_DRAIN_LIGHTNING = "DRAM"
    integer array GlobalCageStruct[128]
    integer array GlobalPreventDamageStruct[8192]
    unit array InvulnerableUnits[8192]
    real array InvulnerableDamage[8192]
    real CageLocationX
    real CageLocationY
    real CageMaxRange
endglobals

private function H2I takes handle H returns integer
    return H
    return 0
endfunction

struct CageStruct
    unit CagedCaster
    unit CageCenterDummy
    group CagedGroup
    integer CageLevel
    real CenterX
    real CenterY
    real Range
    trigger NoNewUnits
    player array AllowedPlayers[16]
    destructable array CageBlocker[32]
    real array CornerX[32]
    real array CornerY[32]
    lightning array Horizontal1[32]
    lightning array Horizontal2[32]
    lightning array Horizontal3[32]
    lightning array Vertical[32]
    
    static method create takes unit whichUnit, integer whatLevel returns CageStruct
        local CageStruct Cage = CageStruct.allocate()
        set Cage.CagedCaster = whichUnit
        set Cage.CenterX = GetUnitX(whichUnit)
        set Cage.CenterY = GetUnitY(whichUnit)
        set Cage.CageLevel = whatLevel
        set Cage.Range = WATER_CAGE_BASE_RANGE+WATER_CAGE_LEVEL_RANGE*whatLevel
        return Cage
    endmethod
    
    method onDestroy takes nothing returns nothing
        local integer a = 0
        set .CagedCaster = null
        call DestroyGroup(.CagedGroup)
        set .CagedGroup = null
        loop
            if .NoNewUnits != null then
            call ReleaseTriggerSpecialData(.NoNewUnits)
            call DestroyTrigger(.NoNewUnits)
            set .NoNewUnits = null
            endif
            if .CageBlocker[a] != null then
            call RemoveDestructable(.CageBlocker[a])
            set .CageBlocker[a] = null
            endif
            if .Horizontal1[a] != null then
            call DestroyLightning(.Horizontal1[a])
            set .Horizontal1[a] = null
            endif
            if .Horizontal2[a] != null then
            call DestroyLightning(.Horizontal2[a])
            set .Horizontal2[a] = null
            endif
            if .Horizontal3[a] != null then
            call DestroyLightning(.Horizontal3[a])
            set .Horizontal3[a] = null
            endif
            if .Vertical[a] != null then
            call DestroyLightning(.Vertical[a])
            set .Vertical[a] = null
            endif
            set a = a+1
            exitwhen a == 32
        endloop
    endmethod
    
    static method TooFar takes nothing returns boolean
        return GetDistanceXY(GetUnitX(GetFilterUnit()), GetUnitY(GetFilterUnit()), CageLocationX, CageLocationY) > CageMaxRange
    endmethod
    
    static method NotADummy takes nothing returns boolean
        return GetUnitAbilityLevel(GetFilterUnit(), 'aloc') == 0
    endmethod
    
    method PushAndPull takes nothing returns nothing
        local group TemporaryGroup = CreateGroup()
        local unit ToBeMoved
        local real Angle
        set CageLocationX = .CenterX
        set CageLocationY = .CenterY
        set CageMaxRange = .Range
        set .CagedGroup = CreateGroup()
        call GroupEnumUnitsInRange(TemporaryGroup, .CenterX, .CenterY, .Range+200, Filter(function CageStruct.TooFar))
        loop
            set ToBeMoved = FirstOfGroup(TemporaryGroup)
            exitwhen ToBeMoved == null
            set Angle = AngleBetweenPointsXY(.CenterX, .CenterY, GetUnitX(ToBeMoved), GetUnitY(ToBeMoved))
            call SetUnitX(ToBeMoved, .CenterX+Cos(Angle)*200)
            call SetUnitY(ToBeMoved, .CenterY+Sin(Angle)*200)
            call GroupRemoveUnit(TemporaryGroup, ToBeMoved)
            set ToBeMoved = null
        endloop
        call GroupEnumUnitsInRange(TemporaryGroup, .CenterX, .CenterY, .Range, Filter(function CageStruct.NotADummy))
        loop
            set ToBeMoved = FirstOfGroup(TemporaryGroup)
            exitwhen ToBeMoved == null
            call SetUnitX(ToBeMoved, .CenterX)
            call SetUnitY(ToBeMoved, .CenterY)
            call GroupAddUnit(.CagedGroup, ToBeMoved)
            call GroupRemoveUnit(TemporaryGroup, ToBeMoved)
            set ToBeMoved = null
        endloop
        call DestroyGroup(TemporaryGroup)
        set TemporaryGroup = null
    endmethod
    
    static method IsUnitBannedFromCage takes nothing returns boolean
        local integer this = GetTriggerSpecialData(GetTriggeringTrigger())
        local integer i = 0
        local player p = GetOwningPlayer(GetTriggerUnit())
        loop
            if .AllowedPlayers[i] == p then
            return false
            endif
            exitwhen i == 16
            set i = i+1
        endloop
        set p = null
        return true
    endmethod

    static method BanUnitFromCage takes nothing returns nothing
        local integer this = GetTriggerSpecialData(GetTriggeringTrigger())
        local real angle = AngleBetweenPointsXY(.CenterX, .CenterY, GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()))
        call SetUnitX(GetTriggerUnit(), .CenterX+Cos(angle)*(.Range+150))
        call SetUnitY(GetTriggerUnit(), .CenterY+Sin(angle)*(.Range+150))
    endmethod
    
    private static method DestroyCage takes nothing returns nothing
        local integer this = GetTimerData(GetExpiredTimer())
        call ReleaseTimer(GetExpiredTimer())
        call .destroy()
    endmethod
    
    private static method CreateCage4 takes nothing returns nothing
        local integer this = GetTimerData(GetExpiredTimer())
        local integer index = 0
        local real angle = 0
        local real distance = .Range-300
        local real currentX
        local real currentY
        call ReleaseTimer(GetExpiredTimer())
        loop
        set angle = angle+0.195
        set currentX = .CenterX+Cos(angle)*.Range
        set currentY = .CenterY+Sin(angle)*.Range
        set .Vertical[index] = AddLightningEx("DRAM", true, currentX, currentY, 0, currentX, currentY, 450)
        set index = index+1
        exitwhen angle >= 6.20
        endloop
    endmethod
    
    private static method CreateCage3 takes nothing returns nothing
        local integer this = GetTimerData(GetExpiredTimer())
        local integer index = 0
        local real angle = 0
        local real lastX = .CenterX+Cos(angle)*.Range
        local real lastY = .CenterY+Sin(angle)*.Range
        local real currentX
        local real currentY
        local timer Temp
        call ReleaseTimer(GetExpiredTimer())
        loop
        set angle = angle+0.195
        set currentX = .CenterX+Cos(angle)*.Range
        set currentY = .CenterY+Sin(angle)*.Range
        set .Horizontal3[index] = AddLightningEx("DRAM", true, lastX, lastY, 375, currentX, currentY, 375)
        set lastX = currentX
        set lastY = currentY
        set index = index+1
        exitwhen angle >= 6.20
        endloop
        set Temp = NewTimer()
        call SetTimerData(Temp, this)
        call TimerStart(Temp, 0.7, false, function CageStruct.CreateCage4)
        set Temp = null
    endmethod
    
    private static method CreateCage2 takes nothing returns nothing
        local integer this = GetTimerData(GetExpiredTimer())
        local integer index = 0
        local real angle = 0
        local real lastX = .CenterX+Cos(angle)*.Range
        local real lastY = .CenterY+Sin(angle)*.Range
        local real currentX
        local real currentY
        local timer Temp
        call ReleaseTimer(GetExpiredTimer())
        loop
        set angle = angle+0.195
        set currentX = .CenterX+Cos(angle)*.Range
        set currentY = .CenterY+Sin(angle)*.Range
        set .Horizontal2[index] = AddLightningEx("DRAM", true, lastX, lastY, 225, currentX, currentY, 225)
        set lastX = currentX
        set lastY = currentY
        set index = index+1
        exitwhen angle >= 6.20
        endloop
        set Temp = NewTimer()
        call SetTimerData(Temp, this)
        call TimerStart(Temp, 0.7, false, function CageStruct.CreateCage3)
        set Temp = null
    endmethod
    
    method CreateCage takes nothing returns nothing
        local real angle = 0
        local real lastX = .CenterX+Cos(angle)*.Range
        local real lastY = .CenterY+Sin(angle)*.Range
        local integer index = 0
        local real currentX
        local real currentY
        local timer Temp
        loop
        set angle = angle+0.195
        set currentX = .CenterX+Cos(angle)*.Range
        set currentY = .CenterY+Sin(angle)*.Range
        set .CageBlocker[index] = CreateDestructable(PATHING_BLOCKER_BOTH, currentX, currentY, 0, 1, 1)
        set .Horizontal1[index] = AddLightningEx("DRAM", true, lastX, lastY, 75, currentX, currentY, 75)
        set lastX = currentX
        set lastY = currentY
        set index = index+1
        exitwhen angle >= 6.20
        endloop
        set .CageCenterDummy = CreateUnit(GetOwningPlayer(.CagedCaster), WATER_CAGE_CENTER_DUMMY, .CenterX, .CenterY, 0)
        set .NoNewUnits = CreateTrigger()
        call SetTriggerSpecialData(.NoNewUnits, this)
        call TriggerRegisterUnitInRange(.NoNewUnits, .CageCenterDummy, .Range+100, null)
        call TriggerAddCondition(.NoNewUnits, Condition(function CageStruct.IsUnitBannedFromCage))
        call TriggerAddAction(.NoNewUnits, function CageStruct.BanUnitFromCage)
        set Temp = NewTimer()
        call SetTimerData(Temp, this)
        call TimerStart(Temp, 0.7, false, function CageStruct.CreateCage2)
        set Temp = NewTimer()
        call SetTimerData(Temp, this)
        call TimerStart(Temp, WATER_CAGE_BASE_DURATION+WATER_CAGE_LEVEL_DURATION*.CageLevel, false, function CageStruct.DestroyCage)
        set Temp = null
    endmethod
    
    private static method CanUnitAttack takes nothing returns boolean
        local integer this = GetTriggerSpecialData(GetTriggeringTrigger())
        return GetDistanceXY(.CenterX, .CenterY, GetUnitX(GetEventDamageSource()), GetUnitY(GetEventDamageSource())) > .Range
    endmethod
    
    private static method UnitRemoveInvulnerable takes nothing returns nothing
        local integer index = GetTimerData(GetExpiredTimer())
        call ReleaseTimer(GetExpiredTimer())
        call SetUnitState(InvulnerableUnits[index], UNIT_STATE_LIFE, GetUnitState(InvulnerableUnits[index], UNIT_STATE_LIFE)+InvulnerableDamage[index])
        call UnitRemoveAbility(InvulnerableUnits[index], SPELL_IMMUNITY_ABILITY)
        set InvulnerableUnits[index] = null
    endmethod

    private static method UnitMakeInvulnerable takes nothing returns nothing
        local integer index
        local real CurrentLife
        local real MaxLife
        local real EventDamage
        local timer Temp
        local unit Target
        if GetTriggerEventId() == EVENT_UNIT_DAMAGED then
        set Target = GetTriggerUnit()
        set CurrentLife = GetUnitState(Target, UNIT_STATE_LIFE)
        set MaxLife = GetUnitState(Target, UNIT_STATE_MAX_LIFE)
        set EventDamage = GetEventDamage()
        loop 
            set index = GetRandomInt(0, 8190)
            exitwhen InvulnerableUnits[index] == null
        endloop
        if CurrentLife > 0.405 then
            if EventDamage < MaxLife-CurrentLife then
                call SetUnitState(Target, UNIT_STATE_LIFE, CurrentLife+EventDamage)
            else
                if EventDamage < CurrentLife then
                    set InvulnerableDamage[index] = EventDamage
                else
                    call SetUnitState(Target, UNIT_STATE_LIFE, MaxLife)
                    set InvulnerableDamage[index] = EventDamage+CurrentLife-MaxLife
                endif
                set InvulnerableUnits[index] = Target
            endif
        endif
        call UnitAddAbility(Target, SPELL_IMMUNITY_ABILITY)
        set Temp = NewTimer()
        call SetTimerData(Temp, index)
        call TimerStart(Temp, 0.00, false, function CageStruct.UnitRemoveInvulnerable)
        set Temp = null
        else
        call ReleaseTriggerSpecialData(GetTriggeringTrigger())
        call DisableTrigger(GetTriggeringTrigger())
        endif
    endmethod
    
    method StartInvulnerability takes nothing returns nothing
        local trigger t
        local unit u
        local group temporary = CreateGroup()
        loop
            set u = FirstOfGroup(.CagedGroup)
            exitwhen u == null
            set t = CreateTrigger()
            call SetTriggerSpecialData(t, this)
            call TriggerRegisterUnitEvent(t, u, EVENT_UNIT_DAMAGED)
            call TriggerRegisterUnitEvent(t, u, EVENT_UNIT_DECAY)
            call TriggerAddCondition(t, Condition(function CageStruct.CanUnitAttack))
            call TriggerAddAction(t, function CageStruct.UnitMakeInvulnerable)
            call GroupAddUnit(temporary, u)
            call GroupRemoveUnit(.CagedGroup, u)
            set u = null
        endloop
        loop
            set u = FirstOfGroup(temporary)
            exitwhen u == null
            call GroupAddUnit(.CagedGroup, u)
            call GroupRemoveUnit(temporary, u)
            set u = null
        endloop
        call DestroyGroup(temporary)
        set t = null
    endmethod
endstruct

function Trig_Water_Cage_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == WATER_CAGE_ABILITY_ID
endfunction

function Trig_Water_Cage_Actions takes nothing returns nothing
    local CageStruct Cage = CageStruct.create(GetTriggerUnit(), GetUnitAbilityLevel(GetTriggerUnit(),WATER_CAGE_ABILITY_ID))
    call Cage.PushAndPull()
    call Cage.CreateCage()
    call Cage.StartInvulnerability()
endfunction

//===========================================================================
function InitTrig_Water_Cage takes nothing returns nothing
    set gg_trg_Water_Cage = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Water_Cage, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Water_Cage, Condition( function Trig_Water_Cage_Conditions ) )
    call TriggerAddAction( gg_trg_Water_Cage, function Trig_Water_Cage_Actions )
endfunction
endscope
It's not yet done. I still haven't done the part where the spell ends, so they'll probably stay invulnerable even after the duration or you could get lots of errors or something like that. I don't know. I also know that there are a bunch of unused variables and stuff like that. As I said, I'm not done with the spell yet.

Anyway, what I'm doing right now is when a unit gets damaged, damage prevention is done and the unit is given spell immunity. Spell immunity is taken away 0 seconds later through a timer. I'm not sure how this all works. I haven't tested it yet, I'm just sure of the damage prevention part. I have no idea whether this would prevent things like silence either. So yeah, any ideas or suggestions?

Edit: TimerUtils is used, but I assume anyone reading this already knows how that works so I didn't paste it. Feel free to point out any flaws. :D

Edit2: Hmm...apparently I had done something very, VERY wrong with this. Anyone know a good damage detection system that doesn't use dynamic triggers?
 
Last edited:
Level 6
Joined
Mar 20, 2008
Messages
208
For the debuffing, you could remove all negative buffs every X seconds

native UnitRemoveBuffs takes unit whichUnit, boolean removePositive, boolean removeNegative returns nothing

For spell negation, I imagine you could check if the unit is within the boundaries when a unit casts a spell (then turn if off when the spell is over) and give it spell immunity, I don't know if it will catch it fast enough.

I think giving spell immunity on damage doesnt stop effects like hex and etc, also relies on enemy units being within the circle or currently attacking.
 
Level 4
Joined
Mar 14, 2009
Messages
98
Yeah, after testing it, I've learned that buffs happen before EVENT_UNIT_DAMAGED happens, but damage happens immediately after, hence it's impossible to catch the buffs. I also don't want it to remove all negative buffs. Finally, setting spell immunity to catch a spell for a second would make the spell abusable. (Using a spell from the outside to make a unit inside spell immune for a second.) I want it to be more like any units trapped in the AoE of the spell will be unable to get out, and any units outside won't be able to get in. I also wanted all units inside to be able to damage each other, but units from the outside can't damage units inside. Personally, I've given up on the spell immunity part since I believe it can't be done.

I'll try that damage detection system. Thanks.
 
Level 6
Joined
Mar 20, 2008
Messages
208
call UnitRemoveBuffs(unit, false,true) will remove all non negative buffs

Apply that to the units within the circle every second or so?
 
Level 4
Joined
Mar 14, 2009
Messages
98
Actually, I was going for "units inside can't be affected by units outside", but then it seems to be impossible unless I can list all possible buffs. I just went with spell immunity to be done with it. Thanks for the help though.
 
Status
Not open for further replies.
Top