Spell Soul Pounder

Discussion in 'Spells' started by Hatebreeder, Aug 30, 2009.

  1. Hatebreeder

    Hatebreeder So many apples

    Ratings:
    +383 / 0 / -0
    Description:
    Summons a Sphere of leeching mana and unleashes it against enemy units in a 275 AOE of a target location. It aims for units with a mana source, pounding on it's very soul, damaging, stealing mana and growing with every mana point it feast on, though it also attacks units with no mana source (with the penalty of not growing at all) and it moves from unit to unit, if there are any other in a 300 AOE around the unit it is leeching on - else it will vanish.
    Requirements:
    - Jass New Gen Editor
    - Warcraft 1.24 Patch
    -Cohadar's Timer Ticker (Also found in Demo Map)

    [​IMG]
    [​IMG]
    [​IMG]
    Code:
    JASS:
    scope SoulPounder initializer Init
        globals
            private constant integer ID    = 'A000' //Spell Raw Code
            private constant integer DUMMY = 'u001' //Dummy Raw Code
            
            private constant attacktype ATTACK_TYPE = ATTACK_TYPE_HERO
            private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
            private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
            
            private constant real BAIL     = 75. //Distance in front of Caster - required for the creation of the dummy
            private constant real ARC      = 1.4 // Dummy Arc movement
            
            private constant string ERRORMSG = "There are no targets in this area." //Displays message if no unit is detected
            private constant string SFX    = "Abilities\\Spells\\NightElf\\ManaBurn\\ManaBurnTarget.mdl" //Impact SFX
            private constant string ATTACH = "origin" //SFX attachmentpoint
            
            private group CHECK   = CreateGroup()
        endglobals
    
        private function Parabola takes real DistCur, real DistMax returns real //parabola function by shadow1500
        local real N = (DistCur * 2) / DistMax - 1
            return (-N * N + 1) * (DistMax / ARC)
        endfunction
        
        private function SetUnitXY takes unit u, real x, real y returns nothing //boarder safty
            if x<GetRectMaxX(bj_mapInitialPlayableArea) and x>GetRectMinX(bj_mapInitialPlayableArea) and y<GetRectMaxY(bj_mapInitialPlayableArea) and y>GetRectMinY(bj_mapInitialPlayableArea) then
                call SetUnitX(u,x)
                call SetUnitY(u,y)
            endif
        endfunction
        
        private constant function UnitFilter takes nothing returns boolean //basic unit Filter
            return IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) == false and IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE) == false and IsUnitEnemy(GetFilterUnit(),bj_groupEnumOwningPlayer) == true
        endfunction
            
        private function Radius takes integer Lvl returns real //initial Area of Effect
            return 275. + (0. * Lvl)
        endfunction
            
    private struct Data
        
        private method Radius_Move takes nothing returns real //Area of effect after each bounce
            return 300. + (0. * .Lvl)
        endmethod
        
        private method Speed takes nothing returns real //Speed of the projectile
            return 17. + (0. * .Lvl)
        endmethod
        
        private method DamageAmount takes nothing returns real // Base Damage
            return 45. * .Lvl
        endmethod
        
        private method Increase takes nothing returns real // stolen mana (in %)
            return 0.05 + ( 0.02 * .Lvl )
        endmethod
        
        private method Jumps takes nothing returns integer // max jumps
            return 3 + ( 2 * .Lvl)
        endmethod    
        
            unit Caster
            unit Dummy
            unit Target
            real MaxX
            real MaxY
            real Damage
            group Group
            integer Max = 0
            integer Lvl
            static method Timer takes nothing returns boolean
                local Data this = TT_GetData()
                local real DX = GetUnitX(.Dummy)
                local real DY = GetUnitY(.Dummy)
                local real TX = GetUnitX(.Target)
                local real TY = GetUnitY(.Target)
                local real Angle = Atan2(TY - DY,TX - DX)
                local real X = DX + .Speed() * Cos(Angle)
                local real Y = DY + .Speed() * Sin(Angle)
                local real DistX
                local real DistY
                local real MaxDist
                local real CurDist
                local real Height
                local real Mana
                local integer Count = 0
                local integer Check = 0
                local unit Picked
                local unit array Choose
                
                set DistX = TX - DX
                set DistY = TY - DY
                set CurDist = SquareRoot(DistX * DistX + DistY * DistY)
                set DistX = TX - .MaxX
                set DistY = TY - .MaxY
                set MaxDist = SquareRoot(DistX * DistX + DistY * DistY)
                set Height = Parabola(CurDist,MaxDist)
                
                call SetUnitXY(.Dummy,X,Y)
                call SetUnitFlyHeight(.Dummy,Height,0)
                call SetUnitFacing(.Dummy,Angle * bj_RADTODEG)
                
                if .Target == null then
                    call .destroy()
                        return true
                endif
                
                if CurDist <= .Speed() then
                
                    if IsUnitType(.Target,UNIT_TYPE_DEAD) == false then
                        if GetUnitState(.Target,UNIT_STATE_MANA) >= 1 then
                            set Mana = GetUnitState(.Target,UNIT_STATE_MANA) * .Increase()
                            set .Damage = .Damage + Mana
                        
                            call SetUnitState(.Target,UNIT_STATE_MANA,GetUnitState(.Target,UNIT_STATE_MANA) - Mana)
                            call UnitDamageTarget(.Caster,.Target,.Damage,true,true,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
                            call SetUnitScale(.Dummy,1. + (Mana/.Damage),1. + (Mana/.Damage),1. + (Mana/.Damage))
                        else
                            call UnitDamageTarget(.Caster,.Target,.Damage,true,true,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
                        endif
            
                        call DestroyEffect(AddSpecialEffectTarget(SFX,.Target,ATTACH))
                    endif
                    
                    set bj_groupEnumOwningPlayer = GetOwningPlayer(.Caster)
                    call GroupEnumUnitsInRange(.Group,TX,TY,.Radius_Move(),Condition(function UnitFilter))
                        loop
                            set Picked = FirstOfGroup(.Group)
                                exitwhen Picked == null
                                    if IsUnit(Picked,.Target) == false then
                                        set Count = Count + 1
                                        set Choose[Count] = Picked
                                    endif
                                call GroupRemoveUnit(.Group,Picked)
                        endloop
                        
                    set Picked = null
                        
                    if Count > 0 and .Max < .Jumps() then
                        set .MaxX   = GetUnitX(.Target)
                        set .MaxY   = GetUnitY(.Target)
                        set .Target = Choose[GetRandomInt(1,Count)]
                        
                            loop
                                exitwhen Check >= Count
                                    set Check = Check + 1
                                        set Choose[Check] = null
                            endloop
                            
                        set .Max = .Max + 1
                        
                    else
                        call .destroy()
                            return true
                    endif
                endif
            return false
            endmethod
            
            static method create takes unit c,real x, real y returns Data
                local Data this = Data.allocate()
                local real X
                local real Y
                local real TX = x
                local real TY = y
                local real Angle
                local integer Count = 0
                local integer Check = 0
                local unit array Choose
                local unit Picked
    
                    set .Caster = c
                    set .Lvl    = GetUnitAbilityLevel(.Caster,ID)
                    set X       = GetUnitX(.Caster)
                    set Y       = GetUnitY(.Caster)
                    set Angle   = Atan2(TY - Y,TX - X)
                    set .MaxX   = X + BAIL * Cos(Angle)
                    set .MaxY   = Y + BAIL * Sin(Angle)
                    set .Damage = .DamageAmount()
                    
                    if .Group == null then
                        set .Group = CreateGroup()
                    else
                        call GroupClear(.Group)
                    endif
                    
                    set bj_groupEnumOwningPlayer = GetOwningPlayer(.Caster)
                    call GroupEnumUnitsInRange(.Group,TX,TY,Radius(.Lvl),Condition(function UnitFilter))
                        loop
                            set Picked = FirstOfGroup(.Group)
                                exitwhen Picked == null
                                        set Count = Count + 1
                                        set Choose[Count] = Picked
                                call GroupRemoveUnit(.Group,Picked)
                        endloop
                        
                    set Picked = null
                        
                        set .Target = Choose[GetRandomInt(1,Count)]
                        set .Dummy  = CreateUnit(GetOwningPlayer(.Caster),DUMMY,.MaxX,.MaxY,Angle * bj_RADTODEG)
                        
                        loop
                            exitwhen Check >= Count
                                set Check = Check + 1
                                    set Choose[Check] = null
                        endloop
                        
                        call TT_Start(function Data.Timer,this)
                return this
            endmethod
            
            private method onDestroy takes nothing returns nothing
                call KillUnit(.Dummy)
            endmethod
        endstruct
        
    private function CheckCast takes nothing returns boolean
    local unit Caster
        if GetSpellAbilityId() == ID then
            set Caster = GetTriggerUnit()
            set bj_groupEnumOwningPlayer = GetOwningPlayer(Caster)
            call GroupEnumUnitsInRange(CHECK,GetSpellTargetX(),GetSpellTargetY(),Radius(GetUnitAbilityLevel(Caster,ID)),Condition(function UnitFilter))
                if FirstOfGroup(CHECK) == null then
                    call SimError(GetOwningPlayer(Caster),ERRORMSG)
                    call IssueImmediateOrder(Caster,"stop")
                        return false
                endif
            call GroupClear(CHECK)
        endif
    set Caster = null
        return true
    endfunction
        
    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == ID then
            call Data.create(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
        endif
    return false
    endfunction
    
    //===========================================================================
    private function Init takes nothing returns nothing
        local trigger Trig = CreateTrigger(  )
        local trigger Trg  = CreateTrigger(  )
            call TriggerRegisterAnyUnitEventBJ( Trg , EVENT_PLAYER_UNIT_SPELL_CAST )
            call TriggerRegisterAnyUnitEventBJ( Trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
            call TriggerAddCondition( Trg , Condition( function CheckCast ) )
            call TriggerAddCondition( Trig, Condition( function Conditions ) )
        set Trg  = null
        set Trig = null
    endfunction
    endscope
    I don't care about credits as long as you don't claim this your own.
    Comments? =P
     

    Attached Files:

    • Like Like x 1
  2. kingkingyyk3

    kingkingyyk3 Visitor (Welcome to the Jungle, Baby!)

    Ratings:
    +216 / 0 / -0
    Typo?
     
  3. Kenny

    Kenny Back for now.

    Ratings:
    +202 / 0 / -0
    Cool spell. Pretty basic in function, but it would suit many different types of maps.

    Few things I picked up on:

    - Your RADIUS, RADIUS_MOVE and SPEED constant globals. I think they would be better off as constant functions. Also I would like to see speed in units per second, not units per interval.

    - You should get rid of the bj_DEGTORAD and bj_RADTODEG's. They are pretty useless and easy to fix.

    - Attacktype, damagetype and weapontype should be configurable.

    - From the looks of it, you can probably just use a global group instead of one per instance. Right now it looks like your not even destroying the dynamic groups, and creating one more every time the struct instance is reused.

    - I'm pretty sure you shouldn't be using null as the boolexpr in your GroupEnum functions. Make a basic filter (like filtering for alive units), and then make a configurable function for users, instead of having it done internally.

    - You can change the use of Data to thistype, so it is easier for people to change the struct name (if they want). However, this isn't really necessary.

    - I'd also like to see this spell use KT2 and GTrigger. You have the best of the best available for you to use, so you may as well use them. Again, not necessary, but would be nice.
     
  4. Larcenist

    Larcenist REP: Respect, Envy, Prosperity?

    Ratings:
    +211 / 0 / -0
    Great feedback, really.

    As for the spell:

    You might want to add WC3 patch 1.24b as requirements in case someone (god forbid) uses a cracked version, or if they (like me) for some reason can't connect to battlenet and patch unless they reinstall WC3 each time they feel like connecting.

    One thing you might feel like mentioning is whether or not this can hit the same target twice, and perhaps add a configurable boolean to allow/disallow that?

    > You can change the use of Data to thistype, so it is easier for people to change the struct name (if they want). However, this isn't really necessary.

    Why is it so popular to abuse thistype for this kind of thing nowadays?
     
  5. quraji

    quraji zap

    Ratings:
    +143 / 0 / -0
    Soul Pounder, eh? Sounds kinky.

    Can you unbold and smallerize the description? It destroyed my eyes :p

    Remove the bj_RADTODEG and bj_DEGTORADs (you convert to deg then back to rad):
    JASS:
    
                local real Angle = bj_RADTODEG * Atan2(TY - DY,TX - DX)
                local real X = DX + SPEED * Cos(Angle * bj_DEGTORAD)
                local real Y = DY + SPEED * Sin(Angle * bj_DEGTORAD)


    Other than that I don't see anything wrong with this as long as it works, although there's some optimization to be done (that I'm too lazy to point out).

    Cool, anyways.
     
  6. Hatebreeder

    Hatebreeder So many apples

    Ratings:
    +383 / 0 / -0
    Changed the script for most of the things you mentioned.

    Didn't do this, since I never realy had to use this.
    Though, i might implement this, when I can use this extra.

    Since TT and KT2 are similar systems, it wouldn't be much of a pain to search and replace this by yourself.
    Same goes for GTrigger (Although it is good, i don't want to increase the list of requirements).

    I tried this. But meh, sometimes weird things happen when spam casting.
    Did I "dynamically destroy" the group in the struct in the new script?

    Speed per second? you mean something like:
    JASS:
    Speed() / TT_PERIOD

    this?

    EDIT: forgot to mention that i have updated Demo map and Code and:
    I also had this in Mind. Though, hitting a unit twice would happen in an instant - I would have to think of a whole new function of some sort to create a nice smooth jump over the same unit.
    And to be honest, I don't feel like thinking about that right now.

    Should I have called it "Soul F*cker"?
    Also, i think Pound means to beat something/somebody up - at least this was the first thing i had in mind when i was creating the spell :3
     
  7. Kenny

    Kenny Back for now.

    Ratings:
    +202 / 0 / -0
    As I said, it isn't necessary, but it is neater (in my opinion).

    You sure you were doing it right? You are currently going through the unit groups instantly, so there should be no problem.

    And you should clear a group before you destroy it. Or even better, do some group recycling within the script, something like this:

    JASS:
    private struct Data
            unit Caster
            unit Dummy
            unit Target
            real MaxX
            real MaxY
            real Damage
            group Group // Get rid of the = CreateGroup() here.
            integer Max = 0
    
            static method Timer takes nothing returns boolean
                local Data this = TT_GetData()
                local real DX = GetUnitX(.Dummy)
                local real DY = GetUnitY(.Dummy)
                local real TX = GetUnitX(.Target)
                local real TY = GetUnitY(.Target)
                local real Angle = Atan2(TY - DY,TX - DX)
                local real X = DX + Speed() * Cos(Angle)
                local real Y = DY + Speed() * Sin(Angle)
                local real DistX
                local real DistY
                local real MaxDist
                local real CurDist
                local real Height
                local real Mana
                local integer Count = 0
                local integer Check = 0
                local unit Picked
                local unit array Choose
                
                set DistX = TX - DX
                set DistY = TY - DY
                set CurDist = SquareRoot(DistX * DistX + DistY * DistY)
                set DistX = TX - .MaxX
                set DistY = TY - .MaxY
                set MaxDist = SquareRoot(DistX * DistX + DistY * DistY)
                set Height = Parabola(CurDist,MaxDist)
                
                call SetUnitXY(.Dummy,X,Y)
                call SetUnitFlyHeight(.Dummy,Height,0)
                call SetUnitFacing(.Dummy,Angle)
                
                if CurDist <= Speed() then
                    if GetUnitState(.Target,UNIT_STATE_MANA) >= 1 then
                        set Mana = GetUnitState(.Target,UNIT_STATE_MANA) * Increase(GetUnitAbilityLevel(.Caster,ID))
                        set .Damage = .Damage + Mana
                        
                        call SetUnitState(.Target,UNIT_STATE_MANA,GetUnitState(.Target,UNIT_STATE_MANA) - Mana)
                        call UnitDamageTarget(.Caster,.Target,.Damage,true,true,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
                        call SetUnitScale(.Dummy,1 + (Mana/.Damage),1 + (Mana/.Damage),1 + (Mana/.Damage))
                    else
                        call UnitDamageTarget(.Caster,.Target,.Damage,true,true,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
                    endif
            
                    call DestroyEffect(AddSpecialEffectTarget(SFX,.Target,ATTACH))
                    
                    call GroupEnumUnitsInRange(.Group,TX,TY,Radius_Move(),Condition(function UnitFilter))
                        loop
                            set Picked = FirstOfGroup(.Group)
                                exitwhen Picked == null
                                    if IsUnitEnemy(Picked,GetOwningPlayer(.Caster)) == true and IsUnit(Picked,.Target) == false then
                                        set Count = Count + 1
                                        set Choose[Count] = Picked
                                    endif
                                call GroupRemoveUnit(.Group,Picked)
                        endloop
                        
                    set Picked = null
                        
                    if Count > 0 and .Max < Jumps(GetUnitAbilityLevel(.Caster,ID)) then
                        set .MaxX   = GetUnitX(.Target)
                        set .MaxY   = GetUnitY(.Target)
                        set .Target = Choose[GetRandomInt(1,Count)]
                        
                            loop
                                exitwhen Check >= Count
                                    set Check = Check + 1
                                        set Choose[Check] = null
                            endloop
                            
                        set .Max = .Max + 1
                        
                    else
                        call .destroy()
                            return true
                    endif
                endif
            return false
            endmethod
            
            static method create takes unit c,real x, real y returns Data
                local Data this = Data.allocate()
                local real X
                local real Y
                local real TX = x
                local real TY = y
                local real Angle
                local integer Count = 0
                local integer Check = 0
                local unit array Choose
                local unit Picked
                
                    set .Caster = c
                    set X       = GetUnitX(.Caster)
                    set Y       = GetUnitY(.Caster)
                    set Angle   = Atan2(TY - Y,TX - X)
                    set .MaxX   = X + BAIL * Cos(Angle)
                    set .MaxY   = Y + BAIL * Sin(Angle)
                    set .Damage = DamageAmount(GetUnitAbilityLevel(.Caster,ID))
    
                    if .Group == null then
                        set .Group = CreateGroup() // Create a new group if this struct instance has not been reached before.
                    else
                        call GroupClear(.Group) // Otherwise just reuse the one you already have.
                    endif
                    
                    call GroupEnumUnitsInRange(.Group,TX,TY,Radius(),Condition(function UnitFilter))
                        loop
                            set Picked = FirstOfGroup(.Group)
                                exitwhen Picked == null
                                    if IsUnitEnemy(Picked,GetOwningPlayer(.Caster)) == true then
                                        set Count = Count + 1
                                        set Choose[Count] = Picked
                                    endif
                                call GroupRemoveUnit(.Group,Picked)
                        endloop
                        
                    set Picked = null
                        
                    if Count > 0 then
                        set .Target = Choose[GetRandomInt(1,Count)]
                        set .Dummy  = CreateUnit(GetOwningPlayer(.Caster),DUMMY,.MaxX,.MaxY,Angle)
                        loop
                            exitwhen Check >= Count
                                set Check = Check + 1
                                    set Choose[Check] = null
                        endloop
                        call TT_Start(function Data.Timer,this)
                    else
                        call .destroy()
                    endif
                return this
            endmethod
            
            private method onDestroy takes nothing returns nothing
                // You don't need to destroy the group anymore :).
                call KillUnit(.Dummy)
            endmethod
        endstruct


    As for the speed per second, yes I meant something like that. So Speed() would return something like 800.00 units per second, then multiply it by TT_Period.

    What I think he meant was: does this hit the same unit twice throughout the duration of the spell. So if there are two units, will it jump between the two until there are no more jumps left, or is it like chain lightning and just stop after it hits each one once? I'm pretty sure it is the the first option.

    And since you got rid of the bj_DEGTORAD and stuff, you need to fix up for SetUnitFacing() function and your CreateUnit() function. Currently you are still using a radians angle for the units facing.

    Oh and you can probably just inline your actions function and do it all in the conditions function, like so:

    JASS:
    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == ID then
            call Data.create(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
        endif
    
        return false
    endfunction
     
  8. Hatebreeder

    Hatebreeder So many apples

    Ratings:
    +383 / 0 / -0
    Oh damn, i forgot to change to degrees >_<

    Btw, do the natives "GetSpellTargetX and GetSpellTargetY" respond to the event in it's condition?
    Else i would be glad to take over that idea !

    EDIT: Updated Code and Map.
    There shouldn't be a problem with the SetUnitFacing and the Create Function.
    Also, implemented Kenny!'s Group recycle Code.

    There is still a problem with the Speed in seconds...
    I just can't make it work the way i want it to (Meaning, that the Spell malfuncs when i try to convert Speed per instance to speed per second).
     
  9. Kenny

    Kenny Back for now.

    Ratings:
    +202 / 0 / -0
    I'm pretty sure GetSpellTargetLoc() works, so those two should as well. You can always test it. It would only take a few minutes.

    As for the speed in units per second, do something like this:

    JASS:
    private constant function Speed takes integer lvl returns real //Speed of the projectile
        return 800.00 + (0.00 * lvl)
    endfunction
    
    // and when you use it:
    
    local real X = DX + (Speed() * TT_Period) * Cos(Angle)
    local real Y = DY + (Speed() * TT_Period) * Sin(Angle) // Should be using * not /


    Also, I just realised that all your constant functions should be taking 'integer lvl' or something along that line, to make the spell easily configurable for X amount of levels.
     
  10. Hatebreeder

    Hatebreeder So many apples

    Ratings:
    +383 / 0 / -0
    I've done this.
    But the projectile moves strangly, if not even at all.
    >_<
     
  11. Hatebreeder

    Hatebreeder So many apples

    Ratings:
    +383 / 0 / -0
    Changed the Code for it to be more customizable, and got rid of the "actions" block and instead done all the actions in the "conditions" block.

    Updated Code and Re-uploaded the Demo Map.
     
  12. Kenny

    Kenny Back for now.

    Ratings:
    +202 / 0 / -0
    JASS:
    private function Conditions takes nothing returns boolean
    local Data Soul
        if GetSpellAbilityId() == ID then
            set Soul = Data.create(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
        else
            call Soul.destroy()
        endif
    return false
    endfunction


    That doesn't really work. Just do it this way:

    JASS:
    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == ID then
            call Data.create(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
        endif
    
        return false
    endfunction


    Also, you can just use x and y (from the parameters) in the create method, you don't need to set them to a new variable and use them.

    You should also have a struct member for the level of the ability for the caster, if will save a couple of function calls.
     
  13. Hatebreeder

    Hatebreeder So many apples

    Ratings:
    +383 / 0 / -0
    Hmmmz...
    I changed the Conditions thingy.
    Will be approved in the state it is right now?
     
  14. Darthfett

    Darthfett Super Mod

    Ratings:
    +614 / 0 / -0
    "Discribtion" is spelled "Description".

    In the Data.create method:

    JASS:
    local integer Lvl = GetUnitAbilityLevel(.Caster,ID)


    .Caster does not yet exist, you should change it to "c".

    You should probably combine the FirstOfGroup loop into the UnitFilter, or the UnitFilter into the FirstOfGroup loop, so you can avoid going through every unit twice.

    I saw you stop the spell on spell effect if there are no units in the targeted area, you could probably add a spell-cast event, and have that order the unit to stop if there are no valid units, that way your spell can save the unit the casting time and mana from an invalid cast. (You could check out the SimError library to display a warning message if you like).

    Since you are getting the caster's ability level on every callback, you could try using the level as a struct member. The only difference would be that if your hero/unit happens to increase the skill's level while it's casting, it wouldn't increase in intensity halfway through the spell (a minor con).
    This would also allow you to make your Radius, Radius_Move, Speed, DamageAmount, Increase, and Jumps functions all methods. They wouldn't need to take the Level argument, since they could use the .level struct member.

    It's very well coded, I like the simple Condition-Action-func. ;)

    I'll approve it once you fix the .Caster problem. :)
     
  15. Hatebreeder

    Hatebreeder So many apples

    Ratings:
    +383 / 0 / -0
    Fixed.
     
  16. Darthfett

    Darthfett Super Mod

    Ratings:
    +614 / 0 / -0
    Functions that do not return the same value every time they are called can not be constant functions.

    Since you've already added the Lvl as a struct member, a simple change would be to simply move it into the struct, change:

    JASS:
    constant function NAME takes integer Level returns....


    to

    JASS:
    method NAME takes nothing returns....


    and to call the function, simply switch

    JASS:
    NAME(.Lvl)


    with

    JASS:
    .NAME()


    You could also improve your enum filter even further by passing the data instance as a global variable. This would allow you to compare whether the picked unit is the target or not, etc. You would only have to transfer the Count integer and the Choose unit array (which I missed on the first look-through, it will leak as a local variable) to a struct member.

    JASS:
                        call IssueImmediateOrder(.Caster,"stop")


    This would only work on EVENT_PLAYER_UNIT_SPELL_CAST events, not EVENT_PLAYER_UNIT_SPELL_EFFECT events. You'd have to add a second trigger and condition in order to save the unit some mana/casting time.
     
  17. Darthfett

    Darthfett Super Mod

    Ratings:
    +614 / 0 / -0
    Nice work on the changes. A few more things I caught though. (Sorry to continuously point out the errors!! :p)

    JASS:
            private constant group CHECK   = CreateGroup()


    Constant for globals means it will be inlined. You don't want it to use a new group every time you use CHECK, do you? (Just make it private group CHECK).

    function UnitFilter and function Parabola can't be constant either, as they take arguments (UnitFilter does stuff with a not-constant function, something constant functions can't do) and use them.

    You could move Radius into the methods area too, since it only takes the struct-member Lvl argument. Seeing how you've done it for all the other similar functions, it makes sense to do them all the same way.

    Your Timer method could be private (not that you need to, the struct is private).

    JASS:
    local unit Picked
    ...
            set Picked = FirstOfGroup(CHECK)
                if Picked == null then
    ...
    set Picked = null


    You could convert this all into if FirstOfGroup(CHECK) == null then

    JASS:
    local real X
    local real Y
    local integer Level
    ...
            set X      = GetSpellTargetX()
            set Y      = GetSpellTargetY()
            set Level  = GetUnitAbilityLevel(Caster,ID)
            call GroupEnumUnitsInRange(CHECK,X,Y,Radius(Level),Condition(function UnitFilter))


    could all be converted into
    JASS:
    call GroupEnumUnitsInRange(CHECK,GetSpellTargetX(),GetSpellTargetY(),Radius(GetUnitAbilityLevel(Caster,ID)),Condition(function UnitFilter))


    There's no sense creating variables when you only use the value once. ;)
     
  18. Hatebreeder

    Hatebreeder So many apples

    Ratings:
    +383 / 0 / -0
    kay I'll change the constant stuff.

    the Radius() is not a method since a struct is not created in the CAST event.
    Which doesn't do much of a difference, or?

    Will also change the variable stuff in the CAST Conditions Block.

    Wait for the next Update ;)

    Will it be ready for approval when I'm done?
     
  19. Darthfett

    Darthfett Super Mod

    Ratings:
    +614 / 0 / -0
    You are right. I didn't look where it was used, sorry.

    I can't say, I'll have to look through the code a little more, and then test the map, to make sure everything's working right. ;)
     
  20. Azlier

    Azlier Old World Ghost

    Ratings:
    +461 / 0 / -0
    I have two great suggestions. Get rid of TT, and use T32 (or if you're lazy, KT2, it has the exact same syntax as TT with two words changed). Also, use GTrigger.
     

Share This Page