1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. The 30th edition of the Modeling Contest is finally up! The Portable Buildings need your attention, so come along and have a blast!
    Dismiss Notice
  3. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice
  4. Melee Mapping Contest #3 - Results are out! Congratulate the winners and check plenty of new 4v4 melee maps designed for this competition!
    Dismiss Notice
  5. The winners of our cinematic soundtrack competition have been decided! Step by the Music Contest #11 - Results to check the entries and congratulate the winners!
    Dismiss Notice

Call of Nature v1.1.6

Submitted by 3yeballz
This bundle is marked as approved. It works and satisfies the submission rules.
Description
Select a target area to summon wisps. For every blooming tree will be summoned one wisp. If a wisp touches an enemy, it will explode and deal 50/75/100 damage in a small AoE.


Credits and Systems
GroupUtils by RisingDusk
GTrigger by Jesus4Lyf (optional)
T32 by Jesus4Lyf
model 'Navi' by Elenai



code
Code (vJASS):

library CallOfNature requires GroupUtils, T32, optional GT
//  v 1.1.6
//////////////////////////////////////////////
// Credits:
//      Spell made by 3yeballz
//          please give credits if you use :-)
//
//      GroupUtils by Rising Dusk
//      T32 by Jesus4Lyf
//      GTrigger by Jesus4Lyf
//      model: Navi by Elenai
///////////////////////////////////////////////


        /////////////////////////////////////////
        /////////// User Configuration //////////
        /////////////////////////////////////////
    globals
        private constant integer SPELLID   = 'A000'     // rawcode of spell
        private constant integer UNITID    = 'h000'     // rawcode of wisp
        private constant integer TREE1     = 'ATtr'     // rawcode for trees where wisps can spawn
        private constant integer TREE2     = 'ATtc'
            // if you require more different trees go to line 218 and change it

       
        private constant real    RADIUS    = 400.       // radius spell cast
        private constant real    RADIUSC   = 80.        // radius detonation init
        private constant real    RADIUSDMG = 150.       // radius detonation damage
        private constant integer ARRAYSIZE = 200        // -> 40 instances allowed
        private constant integer ENDCOUNT  = 650        // wisps will move outside the map after x callbacks
        private constant integer MAXCOUNT  = 1500       // should be much bigger than ENDCOUNT. after x callbacks struct will be destroyed (if it's not destroyed yet)
        private constant real    HEIGHT    = 60         // fly height of wisps
            ////////////////////////////////////
            // constants for flight route //////
            ////////////////////////////////////
        private constant real ACCELERATION = 0.5        //default: 0.5
        private constant real ACCURACY     = 0.002      //default: 0.002
        private constant real INERTIA      = 0.06       //default: 0.06, the lower the stronger is inertia, affects acceleration as well
        private constant real TOLERANCE    = 1.27       //default: 1.27
        private constant real MAXSPEED     = 30         //default: 30
        private constant real STARTSPEED   = 25         //default: 25
       
       
        private attacktype atktype = ATTACK_TYPE_NORMAL
        private damagetype dmgtype = DAMAGE_TYPE_MAGIC
        private weapontype wpntype = null
    endglobals
   
    private function GetDamageAmount takes integer level returns real
        return 25. + 25. * level
    endfunction
        /////////////////////////////////////////
        /////// End of User Configuration ///////
        /////////////////////////////////////////

    private module WispInit
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            static if LIBRARY_GT then
                call TriggerAddCondition(GT_RegisterStartsEffectEvent(t, SPELLID), Filter(function thistype.SpellCast))
            else
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerAddCondition(t, Filter(function thistype.SpellCast))
            endif
            set t = null
            set check = Filter(function thistype.CheckForEnemy)
            set filter = Filter(function thistype.DealDamage)
            set filt = Filter(function thistype.Init)
            set mapmaxX = GetRectMaxX(bj_mapInitialPlayableArea)
            set mapmaxY = GetRectMaxY(bj_mapInitialPlayableArea)
            set mapminX = GetRectMinX(bj_mapInitialPlayableArea)
            set mapminY = GetRectMinY(bj_mapInitialPlayableArea)
            set rct = Rect(-RADIUS,-RADIUS,RADIUS,RADIUS)
        endmethod
    endmodule
   
    private struct Wisp
        private static thistype temp
        private static boolean  explode = false
        private static boolexpr filter
        private static boolexpr filt
        private static boolexpr check
        private static real mapmaxX
        private static real mapmaxY
        private static real mapminX
        private static real mapminY
        private static real px
        private static real py
        private static rect rct
       
        private unit caster
        private player owner
        private real x
        private real y
        private integer i
        private integer count
        private integer timecount
        private integer level
        private real damage
        private unit array wisp [ARRAYSIZE]
        private real array facing [ARRAYSIZE]
        private real array speed [ARRAYSIZE]
        private real array locx [ARRAYSIZE]
        private real array locy [ARRAYSIZE]
       
        static method DealDamage takes nothing returns boolean
            local unit u = GetFilterUnit()
            if IsUnitEnemy(u, temp.owner) and not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0 and GetUnitAbilityLevel(u, 'Avul')==0 then
                call UnitDamageTarget(temp.caster, u, temp.damage, false, false, atktype, dmgtype, wpntype)
            endif
            set u = null
            return false
        endmethod

        static method CheckForEnemy takes nothing returns boolean
            local unit u
            if explode then
                return false
            endif
            set u = GetFilterUnit()
            if IsUnitEnemy(u, temp.owner) and not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0 and GetUnitAbilityLevel(u, 'Avul')==0 then
                set explode = true
            endif
            set u = null
            return false
        endmethod
       
        private method ClearData takes nothing returns nothing
            set .wisp[i] = .wisp[.count]
            set .locx[i] = .locx[.count]
            set .locy[i] = .locy[.count]
            set .facing[i] = .facing[.count]
            set .speed[i] = .speed[.count]
            set .wisp[.count] = null
            set .locx[.count] = 0.
            set .locy[.count] = 0.
            set .facing[.count] = 0.
            set .speed[.count] = 0.
            set .count = .count - 1
        endmethod
       
        private method periodic takes nothing returns nothing
            local real gravitation
            set .i = 1
            loop
                exitwhen i > .count
                if this.timecount <= ENDCOUNT then
                    if .facing[i] < 0 then
                        set .facing[i] = facing[i]+360.0
                    endif
                    set gravitation = Atan2(GetUnitY(.caster) - .locy[i], GetUnitX(.caster) - .locx[i]) * bj_RADTODEG
                    if gravitation < 0 then
                        if .facing[i] > 180.0 then
                            set gravitation = gravitation + 360.0
                        endif
                    endif
                    set .facing[i] = .facing[i] + (gravitation-.facing[i]) * ACCURACY * (MAXSPEED-.speed[i])
                    set .speed[i] = .speed[i] + ACCELERATION * (TOLERANCE - Pow(RAbsBJ(gravitation-.facing[i]), INERTIA))
                    if .speed[i] > MAXSPEED then
                        set .speed[i] = MAXSPEED
                    endif
                    if .speed[i] < 0 then
                        set .speed[i] = 0
                    endif
                    call SetUnitFacing(.wisp[i], .facing[i])
                else
                    if .speed[i] < MAXSPEED then
                        set .speed[i] = .speed[i] + 0.1
                    endif
                endif
                set .locx[i] = .locx[i] + Cos(.facing[i] * bj_DEGTORAD) * .speed[i]
                set .locy[i] = .locy[i] + Sin(.facing[i] * bj_DEGTORAD) * .speed[i]
                if .locx[i] > mapmaxX or .locy[i] > mapmaxY or .locx[i] < mapminX or .locy[i] < mapminY then
                    call RemoveUnit(.wisp[i])
                    call .ClearData()
                else
                    call SetUnitX(.wisp[i], .locx[i])
                    call SetUnitY(.wisp[i], .locy[i])
                    set temp = this
                    call GroupEnumUnitsInArea(ENUM_GROUP, .locx[i], .locy[i], RADIUSC, check)
                    if explode then
                        call GroupEnumUnitsInArea(ENUM_GROUP, .locx[i], .locy[i], RADIUSDMG, filter)
                        call UnitApplyTimedLife(.wisp[i], 'BTLF', 0.01)
                        call .ClearData()
                        set explode = false
                    else
                        set i = i + 1
                    endif
                endif
            endloop
            set .timecount = .timecount + 1
            if .count == 0 or .timecount >= MAXCOUNT then
                call .stopPeriodic()
                call .destroy()
            endif
        endmethod

        implement T32x
       
        private static method Init takes nothing returns boolean
            local thistype this = temp
            local destructable d = GetFilterDestructable()
            local real x = GetDestructableX(d)
            local real y = GetDestructableY(d)
            local real dx = x - .x
            local real dy = y - .y
            local integer destrID = GetDestructableTypeId(d)
            if SquareRoot(dx*dx+dy*dy) > RADIUS then
                return false
            endif
            if (destrID == TREE1 or destrID == TREE2) then     // change this if you require more trees
                set .count = .count + 1
                set .facing[.count] = GetRandomReal(0., 360.)
                set .wisp[.count] = CreateUnit(.owner, UNITID, x, y, .facing[.count])
                set .locx[.count] = x
                set .locy[.count] = y
                set .speed[.count] = STARTSPEED
                call UnitAddAbility(.wisp[.count], 'Amrf')
                call UnitRemoveAbility(.wisp[.count], 'Amrf')
                call SetUnitFlyHeight(.wisp[.count], HEIGHT, 0)
            endif
            set d = null
            return false
        endmethod  
       
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            set .caster = GetTriggerUnit()
            set .owner = GetTriggerPlayer()
            set .x = GetSpellTargetX()
            set .y = GetSpellTargetY()
            set .count = 0
            set .timecount = 0
            set .level = GetUnitAbilityLevel(.caster, SPELLID)
            set .damage = GetDamageAmount(.level)
            set temp = this
            call MoveRectTo(rct, .x, .y)
            call EnumDestructablesInRect(rct, filt, null)
            return this
        endmethod
   
        private static method SpellCast takes nothing returns boolean
            static if LIBRARY_GT then
                call thistype.create().startPeriodic()
            else
                if GetSpellAbilityId() == SPELLID then
                    call thistype.create().startPeriodic()
                endif
            endif
            return false
        endmethod
   
        implement WispInit
   
    endstruct

endlibrary


Please give Credits if you use this Spell in your map

Keywords:
call, nature, wisp, wisps, tree, trees, explode, explosion, mui, vjass, navi
Contents

Noch eine WARCRAFT-III-Karte (Map)

Reviews
Moderator
12:10GMT, 17th Jun 2011 Bribe: This is an overall good spell. The only change to be made is to base the spell off of a different ability (instead of silence) because of the weird audio/visual effect it makes when enemies are close to the target...
  1. 12:10GMT, 17th Jun 2011
    Bribe:

    This is an overall good spell. The only change to be made is to base the spell off of a different ability (instead of silence) because of the weird audio/visual effect it makes when enemies are close to the target point. But users can do that on their own.

    Approved
     
  2. hell gate

    hell gate

    Joined:
    Nov 23, 2008
    Messages:
    477
    Resources:
    10
    Models:
    1
    Spells:
    9
    Resources:
    10
    looks good for me but you can optimize it a bit
    Code (vJASS):
    if not IsUnitEnemy(u, temp.owner) then
                return false
            endif
            if GetWidgetLife(u)<0.405 then
                return false
            endif
            if GetUnitAbilityLevel(u, 'Avul') >= 1 then
                return false
            endif
     

    better
    Code (vJASS):
    if not IsUnitEnemy(u, temp.owner) or GetWidgetLife(u)<0.405 or GetUnitAbilityLevel(u, 'Avul') >= 1 then
                return false
            endif
     
     
  3. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,005
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Nature Calls! :D
     
  4. 3yeballz

    3yeballz

    Joined:
    Feb 28, 2011
    Messages:
    33
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Yeah,
    I just didn't want to put everything in one line. :grin:
    Any other suggestions?

    /e: updated. removed location and nulling locals
     
    Last edited: Mar 1, 2011
  5. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,746
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    KeyTimers2 - not the best choice. Timer32 is a lot more efficient (also it doesn't need to be a static if then).

    call EnumDestructablesInCircleBJ(RADIUS, loc, function Wisp.Init)

    This uses a location and a BJ where coordinates and natives could be used instead.

    set this.owner = GetOwningPlayer(this.caster)

    Could be set this.owner = GetTriggerPlayer()

    Running a textmacro for cleardata is a waste, that should be its own function so you're not re-generating text over and over (generating huge KB file size)

    That is all for now.
     
  6. 3yeballz

    3yeballz

    Joined:
    Feb 28, 2011
    Messages:
    33
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Reworked the whole code.
    Now it should be... much better.

    Now using coordinates, but anyway I need the location for
    filterEnumDestructablesInCircleBJ
    . Otherwise it would be a square :eek:
     
  7. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,418
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    1) In
    .create()
    , you forgot to do
    set rct = null
    .
    2)
    this.owner = GetOwningPlayer(this.caster)
    can just be
    this.owner = GetTriggerPlayer()

    3) x and y in the create method can be initially set to
    GetSpellTargetX()
    /
    GetSpellTargetY()
    .
    4) Instead of
    call SetUnitFacing(wisp[this.count], this.facing[this.count])
    , you may want to just set the unit's facing in the create unit function. Like so:
    Code (vJASS):
    set this.wisp[this.count] = CreateUnit(this.owner, UNITID, x, y, this.facing[this.count])

    Otherwise, it is not instant.
    5) GroupUtils? If you are going to use the single-group method, then you might as well remove the requirement and use the normal natives. xD (GroupUtils == recycling, boolexpr safeness, + null leak. The null leak has been fixed in 1.24, boolexpr safeness is only needed if you handle your boolexprs in an odd manner, and if you are using a single group, then you don't need the recycling)

    Otherwise, nice job. :ogre_haosis:
     
  8. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,746
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    He's using GroupUtils for EnumUnitsInArea, which is really a good use for it actually. But since he's using GroupUtils, instead of using his global group "g" he could take advantage of the existing ENUM_GROUP.
     
  9. 3yeballz

    3yeballz

    Joined:
    Feb 28, 2011
    Messages:
    33
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Updated again.

    Thanks for your suggestions :grin:
     
  10. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,005
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    You know, this is the first time i've seen a JASS/vJASS spell get rejected :p
     
  11. 3yeballz

    3yeballz

    Joined:
    Feb 28, 2011
    Messages:
    33
    Resources:
    1
    Spells:
    1
    Resources:
    1
    It should be approved soon ;-)
    There was just one thing that leaked, but now the code is much better.
     
  12. Pharaoh_

    Pharaoh_

    Joined:
    Nov 6, 2008
    Messages:
    8,127
    Resources:
    11
    Icons:
    3
    Skins:
    1
    Spells:
    6
    Tutorials:
    1
    Resources:
    11
    You mean "and" (?).
     
  13. Marcos DAB

    Marcos DAB

    Joined:
    Mar 26, 2011
    Messages:
    1,033
    Resources:
    215
    Models:
    1
    Icons:
    211
    Spells:
    2
    Tutorials:
    1
    Resources:
    215
    Nice eyecandy.
     
  14. Lambdadelta

    Lambdadelta

    Joined:
    Jul 6, 2009
    Messages:
    730
    Resources:
    1
    Maps:
    1
    Resources:
    1
    I saw this somewhere before.... spell contest?
     
  15. 3yeballz

    3yeballz

    Joined:
    Feb 28, 2011
    Messages:
    33
    Resources:
    1
    Spells:
    1
    Resources:
    1
    @Pharaoh_:
    No, it's right what he said.

    @xBlackRose:
    It's uploaded since 02-28, but it wasn't part of any spell contest.
    It's a spell from my map, but I didn't release it yet at hive.
     
  16. Lambdadelta

    Lambdadelta

    Joined:
    Jul 6, 2009
    Messages:
    730
    Resources:
    1
    Maps:
    1
    Resources:
    1
    The Navi model must be used a lot then :)
     
  17. baassee

    baassee

    Joined:
    Nov 14, 2008
    Messages:
    3,220
    Resources:
    17
    Spells:
    14
    Tutorials:
    3
    Resources:
    17
    Shouldn't you use pitzermike's destlib so it supports all kind of trees? (Using IsDestTree)

    All similar functions to this RectMaxX(bj_mapInitialPlayableArea) should be stored into globals non-constant and set at ini. (cannot be set at the globals section and made as constant because globals runs before the bj is set).
     
  18. 3yeballz

    3yeballz

    Joined:
    Feb 28, 2011
    Messages:
    33
    Resources:
    1
    Spells:
    1
    Resources:
    1
    I didn't want to use that and I wrote in the spell description that wisps are only summoned for blooming trees, but there are some trees like the ones from northrend that doesn't fit to that description.

    Good idea ;-)
     
  19. 3yeballz

    3yeballz

    Joined:
    Feb 28, 2011
    Messages:
    33
    Resources:
    1
    Spells:
    1
    Resources:
    1

    What do you mean with that?

    Ok, but why avoid using the destructable BJ?
    Do you mean
    Code (vJASS):
                set bj_enumDestructableCenter = loc
                set bj_enumDestructableRadius = RADIUS
                call EnumDestructablesInRect(rct, filterEnumDestructablesInCircleBJ, function thistype.Init)
    ?
    If I don't use the BJ, I would have to check the distance for every filtered destructable to the center to get a circle.

    Where? I can't see any.