• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Wish v1.01

Requires Jass NewGen Pack with the most recent Jasshelper.
Spell should be MUI and leakless.

Wish
The caster makes a wish, healing friendly units over 5 seconds. Once the wish reaches the sky, a number of stars will fall down that are dependent on how many friendly units there were; each star heals friendly units and damages enemy units.
Level 1 - Wish heals for 15 damage every second, stars heal for 20 and deal 10 damage.
Level 2 - Wish heals for 25 damage every second, stars heal for 25 and deal 15 damage.
Level 3 - Wish heals for 35 damage every second, stars heal for 30 and deal 25 damage.

Required Libraries:
- TimerUtils
- xe system (xebasic and xefx)
- [Optional] BoundSentinel
- [Optional] GroupUtils

This was one of my old spells that I created a few months ago while I was fiddling with making things orbit in circles.
It's not that amazing in terms of coding or idea, but I thought it was a nice spell so I decided to upload it.
I would be happy to hear any suggestions to make this spell more interesting.

Problems/Issues:
- Upon creation, the wishes may sometimes snap quickly to the right position.

JASS:
//***************************************************
//Wish v1.01 by watermelon_1234
//***************************************************
//Required Libraries: TimerUtils, xebasic, xefx
//***************************************************
scope Wish
        native UnitAlive takes unit id returns boolean //Remove this line if it's already implemented.
//===================================================
//CONSTANTS
//===================================================
    globals
        private constant integer    SPELL_ID        = 'A000' //The raw id of the Wish ability
        private constant real       HEIGHT          = 1500. //The height the wish should ascend
        private constant string     WISH_SFX        = "Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl" //The sfx used for the missile that will orbit the point
        private constant real       WISH_SPEED      = 225. //Determines the speed for the wish and the timer repitition for the Data.wishEffect. A smaller number means a faster wish missile
        private constant real       TIMER_LOOP      = XE_ANIMATION_PERIOD //How often the timer will loop
        private constant string     STAR_SFX        = "Abilities\\Spells\\NightElf\\Starfall\\StarfallTarget.mdl" //The star sfx that will be used for the falling star
        private constant real       STAR_AREA       = 75. //The area for the stars to affect nearby units upon impact.
        private constant real       STAR_INTERVAL   = .25 //The interval between each star creation
        private constant real       STAR_DELAY      = .7 //The delay the star has after being shown before actually doing anything.
        private constant string     HEAL_SFX        = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl" //The sfx that will play when a unit is healed by the wish
        private constant real       HEAL_INTERVAL   = .4 //The interval between each heal sfx played
        private constant integer    MAX_ARRAY_SIZE  = 6 //Number should be equal to the number of wishes created by the spell
        private constant attacktype ATK_TYPE        = ATTACK_TYPE_NORMAL //Attack type for the star damage
        private constant damagetype DMG_TYPE        = DAMAGE_TYPE_UNIVERSAL //Damage type for the star damage    
        private constant weapontype WPN_TYPE        = null //Weapon type for the star damage
        private constant boolean    PRELOAD         = true //Determines whether or not to preload the spell sfx 
    endglobals    
//===================================================
//Configuration for other parts of the spell
//===================================================     
    private constant function WishNumber takes integer lvl returns integer //The number of wishes created, purely a visual effect. I suggest that this number increases with the spell area.
        return 3+1*lvl
    endfunction
    
    private constant function HeightSpeed takes integer lvl returns real //How much time should it take for the wishes to reach the height
        return 5.+0*lvl
    endfunction
    
    private constant function StarRate takes integer lvl returns integer //The number of stars created depending on how many friendly people there are
        return 0+1*lvl
    endfunction
    
    private constant function HealNumber takes integer lvl returns integer
        return 1+1*lvl
    endfunction
    
    private constant function Area takes integer lvl returns real //Determines the area of effect of the spell
        return 150.+50*lvl
    endfunction   
    
    private constant function AreaHeal takes integer lvl returns real //Amount healed per second in the AoE at the beginning of the spell
        return 5.+10*lvl
    endfunction  
    
    private function StarHeal takes integer lvl returns real //Amount healed when friendly units are hit by the star
        return 15.+5*lvl
    endfunction
    
    private constant function StarDamage takes integer lvl returns real //Damage done to enemy units if hit by the star
        return 5.+5*lvl
    endfunction    
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//CODING OF THE SPELL. Please look at the method filter to determine which units can be healed and damaged.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    globals
         private boolexpr e //Used as a filter.
         private group GROUP_ENUM //This will be used if GroupUtils isn't there.
    endglobals
    
    private struct Data 
        unit        cast //The caster of the spell        
        real        x //Holds the x coordinate of the spell target point
        real        y //Holds the y coordinate of the spell target point
        integer     lvl //The level of the ability when it was casted
        integer     stars = 0 //Used to determine when to stop creating stars by counting how many stars that were created
        integer     which //For the filter method. 0 - acts for units affected by the wishEffect, 1 - acts for units affected by the starEffect
        integer     wnumber //Stores the number of wishes that will be made
        integer     hnumber //Stores the number of heal sfx's that will be played
        integer     snumber = 0 //Stores the number of stars that will be created based on how many friendly units were in that area
        group       sgroup = CreateGroup() //This group will be used to find out how many stars to create from the friendly units
        real        count = 0 //Used to determine the wish's height
        real        hcount = 0  //Used to determine when to play the heal sfx
        real        area //Stores the area of effect for the wish
        real        hspeed //Stores the speed it will take to reach HEIGHT
        timer       t //Timer used by the spell
        xefx array wish[MAX_ARRAY_SIZE] //The missile for the wish effect
        real array ang[MAX_ARRAY_SIZE] //The angle for the wish
        private static thistype temp //Used to pass information from a specific struct to the filter method.        
        
        static method create takes unit c, real x, real y returns thistype //Used for the initial storage and creation of the struct
            local thistype this = thistype.allocate()
            set .cast = c 
            set .x = x
            set .y = y
            set .t = NewTimer()
            return this
        endmethod
        
        static method filter takes nothing returns boolean //Determines which units will be affected by the wish and star. Also applies the effects on them.
            local unit u = GetFilterUnit()            
            if UnitAlive(u) then //Filter only for alive units                
                if IsUnitAlly(u,GetOwningPlayer(temp.cast)) and not IsUnitType(u,UNIT_TYPE_MECHANICAL) then //Healing effect
                    if temp.which == 0 then //Wish part
                        call SetWidgetLife(u,GetWidgetLife(u)+AreaHeal(temp.lvl)*TIMER_LOOP)
                        if not IsUnitInGroup(u,temp.sgroup) then //Adds to a group so it won't be recounted
                            call GroupAddUnit(temp.sgroup,u)
                            set temp.snumber = temp.snumber + StarRate(temp.lvl)
                        endif
                    else //Star part
                        call SetWidgetLife(u,GetWidgetLife(u)+StarHeal(temp.lvl)) 
                    endif   
                elseif temp.which == 1 and IsUnitEnemy(u,GetOwningPlayer(temp.cast)) then //Star damaging effects
                    call UnitDamageTarget(temp.cast,u,StarDamage(temp.lvl),false,true,ATK_TYPE,DMG_TYPE,WPN_TYPE)
                endif
            endif
            set u = null
            return false //Returns false since we don't need to put the units in the group
        endmethod
        
        static method effectStar takes nothing returns nothing //Deals damage and healing after STAR_DELAY seconds
            local thistype this = GetTimerData(GetExpiredTimer())
            set .which = 1 //Filter will be used for star effect
            set temp = this //Sets this so that the struct can be referenced from the static method            
            static if LIBRARY_GroupUtils then
                call GroupEnumUnitsInArea(ENUM_GROUP,.x,.y,STAR_AREA,e)
            else
                call GroupEnumUnitsInRange(GROUP_ENUM,.x,.y,STAR_AREA,e)
            endif
            call ReleaseTimer(.t)
            call .destroy()
        endmethod
       
        static method createStar takes nothing returns nothing //Deals with the creation of the stars
            local thistype this = GetTimerData(GetExpiredTimer())
            local thistype D        
            if .stars < .snumber then //Used to check when to stop creating stars
                set D = Data.create(.cast,.x + GetRandomReal(-.area/2,.area/2),.y + GetRandomReal(-.area/2,.area/2)) //I decided to stick with having one struct so that it could access the same filter method.
                call DestroyEffect(AddSpecialEffect(STAR_SFX,D.x,D.y))
                call SetTimerData(D.t,D)
                call TimerStart(D.t,STAR_DELAY,false,function thistype.effectStar)
                set .stars = .stars + 1
            else                
                call ReleaseTimer(.t)
                call .destroy()
            endif
         endmethod
        
        static method effectWish takes nothing returns nothing //Deals with the wish movements, height, and healing.
            local thistype this = GetTimerData(GetExpiredTimer())
            local integer i = 0
            if .count <= .hspeed then  
                loop
                    set .ang[i] = .ang[i] + WISH_SPEED*TIMER_LOOP*.017453278
                    set .wish[i].x = .x + (.area-.area/.hspeed*.count) * Cos(.ang[i])
                    set .wish[i].y = .y + (.area-.area/.hspeed*.count) * Sin(.ang[i])
                    set .wish[i].z = HEIGHT/.hspeed*.count
                    set i = i + 1
                    exitwhen i == .wnumber
                endloop                
                set .count = .count + TIMER_LOOP                 
                if .hcount >= HEAL_INTERVAL then
                    set .hcount = 0
                endif
                set .which = 0 //Filter will be used for wish effect
                set temp = this //Sets this so that the struct can be referenced from the static method
                static if LIBRARY_GroupUtils then
                    call GroupEnumUnitsInArea(ENUM_GROUP,.x,.y,.area,e)
                else
                    call GroupEnumUnitsInRange(GROUP_ENUM,.x,.y,.area,e)
                endif
                if .hcount == 0 then
                    set i = 0
                    loop
                        call DestroyEffect(AddSpecialEffect(HEAL_SFX,.x+GetRandomReal(-Area(.lvl)/2,Area(.lvl)/2),.y+GetRandomReal(-Area(.lvl)/2,Area(.lvl)/2)))
                        set i = i + 1
                        exitwhen i == .hnumber
                    endloop
                endif
                set .hcount = .hcount + TIMER_LOOP
            else
                loop
                    call .wish[i].destroy() //Destroy the wish missile since it's no longer needed
                    set i = i + 1
                    exitwhen i == .wnumber
                endloop
                call ReleaseTimer(.t)
                call GroupClear(.sgroup) //No longer needed.
                if .snumber > 0 then                
                    set .t = NewTimer()
                    call SetTimerData(.t,this)
                    call TimerStart(.t,STAR_INTERVAL,true,function Data.createStar) 
                else
                    call .destroy()
                endif
            endif
        endmethod
    
        static method spellActions takes nothing returns boolean
            local thistype this
            local integer i = 0
            local real wx //For readability
            local real wy             
            if GetSpellAbilityId() == SPELL_ID then
                set this = Data.create(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
                set .lvl = GetUnitAbilityLevel(.cast,SPELL_ID)                
                set .area = Area(.lvl)
                set .wnumber = WishNumber(.lvl)
                set .hnumber = HealNumber(.lvl)
                set .hspeed = HeightSpeed(.lvl)                
                loop
                    set wx = .x + Area(.lvl)*Cos(6.28285608/WishNumber(.lvl)*i)
                    set wy = .y + Area(.lvl)*Sin(6.28285608/WishNumber(.lvl)*i)
                    set .wish[i] = xefx.create(wx,wy,0)
                    set .wish[i].fxpath = WISH_SFX
                    set .ang[i] = Atan2(.y - wy, .x - wx)        
                    set i = i + 1
                    exitwhen i == WishNumber(.lvl)
                endloop                
                call SetTimerData(.t,this)
                call TimerStart(.t,TIMER_LOOP,true,function thistype.effectWish)
            endif
            return false
        endmethod
    
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t,Condition( function thistype.spellActions)) //Because trigger conditions are faster than actions
            set e = Condition(function thistype.filter) //Sets the filter
            //Preload effects
            static if PRELOAD then
                call Preload(WISH_SFX)
                call Preload(STAR_SFX)
                call Preload(HEAL_SFX)
            endif            
            static if not LIBRARY_GroupUtils then
                set GROUP_ENUM = CreateGroup() //Don't bother creating the group if we'll just use GroupUtils
            endif
            set t = null //Just to null handles.     
        endmethod
    endstruct 
endscope

v1.00
Released.

v1.00a
Minor code fixes

v1.00b
More code cleanup
GroupUtils is now optional

v1.01
Effect has been altered a bit, now number of stars spawned depend on how many friendly units went into that area.


Please give me credits if you use this spell in your map. Feedback on the spell is also appreciated.
Credits to:
~ Vexorian for xe system, TimerUtils, and BoundSentinel
~ RisingDusk for GroupUtils


Keywords:
wish,star,heal,holy,sky,light
Contents

Wish (Map)

Reviews
07:42, 14th Apr 2010 TriggerHappy: Coding was fine, nice comments and structure. The spell was also cool.

Moderator

M

Moderator

07:42, 14th Apr 2010
TriggerHappy:

Coding was fine, nice comments and structure.
The spell was also cool.
 
Level 15
Joined
Jul 6, 2009
Messages
889
Coding
  1. You could place the boolexpr e under a second globals block instead of labelling it as not configurable.
  2. HeightSpeed( .lvl), Area( .lvl ), WishNumber( .lvl ) and the like can be stored as struct members to save many calls during the effectWish.
  3. You don't need to set e to the Condition(), you can just place function thistype.data directly in the GroupEnum function.
  4. If your using the initialization method inside the struct, why not have those data's set to thistype?
  5. Unsure, but I hear UnitAlive can mess up when you add HP to a dead unit or something.

Your code to me is hard to read, but then again, to me, everyone's code is hard to read. Well, not really. :con:

Spell
It's basically a Heal Spray with different visuals?
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
I updated the coding for 1 and 4.

HeightSpeed( .lvl), Area( .lvl ), WishNumber( .lvl ) and the like can be stored as struct members to save many calls during the effectWish.
I rather not make the struct have more members that are dependent on just one variable, and I don't think it should affect speed too much...?

You don't need to set e to the Condition(), you can just place function thistype.data
I just put Condition() to remind myself.

Unsure, but I hear UnitAlive can mess up when you add HP to a dead unit or something.
Since you're unsure, I'm not going to change this unless I see evidence or test this by myself.
Edit: I tested it by myself and see no problems with UnitAlive.

It's basically a Heal Spray with different visuals?
Did you test the spell itself or is that comment based on the screenshot? The only similarity I can see between the two spells is that they both heal and use the same sfx for the healing.
 
Level 15
Joined
Jul 6, 2009
Messages
889
Did you test the spell itself or is that comment based on the screenshot? The only similarity I can see between the two spells is that they both heal and use the same sfx for the healing.

I did test. It basically makes a wispwheel that go upwards, creating random sparks during so, after it sends down stars that damage and heal. Somewhat simple.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
I did test. It basically makes a wispwheel that go upwards, creating random sparks during so, after it sends down stars that damage and heal. Somewhat simple.
True, it's a simple spell.
I haven't been getting good or original ideas for spells anymore. (Actually, did I ever have a good idea for a spell?)
I also seem to have a knack for submitting spells at the wrong time.
I should probably retire from submitting spells here and just review them instead. =P
 
I should probably retire from submitting spells here and just review them instead. =P

Don't! D: It is fun seeing spells. :sad:

Anyway, awesome job on the coding, but perhaps you should make GroupUtils optional since you only use one of its functions. (which can be inlined for simple group recycling) But that is ultimately up to you, it will just ease implementation a bit. And saving Area(.lvl) [and possibly Area(.lvl)/2] as Blackrose mentioned, just for the reason that it will save you some hundred or so calculation repeats. It won't be a noticeable difference but it is just better in terms of optimization.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
Don't! D: It is fun seeing spells. :sad:
That's probably one of the few nice things anyone said about my spells. Thanks! =D (I also agree with your sentiment about looking at the spells.)
But I shouldn't submit crappy spells that just clog up the database.

Anyway, awesome job on the coding, but perhaps you should make GroupUtils optional since you only use one of its functions. (which can be inlined for simple group recycling) But that is ultimately up to you, it will just ease implementation a bit. And saving Area(.lvl) [and possibly Area(.lvl)/2] as Blackrose mentioned, just for the reason that it will save you some hundred or so calculation repeats. It won't be a noticeable difference but it is just better in terms of optimization.
I'll try to update with that.
Edit: Updated.
 
Top