• 🏆 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!

Unholy Presence v1.4 [vJass]

  • Like
Reactions: -JonNny
This is my first vJass spell that I am uploading. It's not the most complex of spells but I think it has some use.

This spell requires JassNewgenPack: download it here http://www.wc3c.net/showthread.php?t=90999

Spell: The Dreadlord creates a shield around himself that damages nearby enemy units and heals himself for each unit damaged every second for 15 seconds. Cooldown : 16 seconds

Damages for (level of spell x 5) + 100
Aoe is (level of spell x 5) + 350
Heals for (Strength x 0.05)

I would appreciate comments/suggestions/things to improve/bugs
:O

Here is the code:
JASS:
// i'd like to thank hvo-busterkomo because i learned a lot from his fireball spell
// i'd like to thank Flame_phoenix because i learned a lot from his vjass tutorial
//thanks to Thanathos for helping me out

scope UnholyP initializer Init
//*************************************************************************************************************
//****Configuration. You can set these values to fit your needs.****
globals
    private constant integer    SPELL_ID = 'A000'                                                     //rawcode of the Unholy Presence ability
    private constant integer    BUFF_ID = 'B000'                                                      //rawcode of the Unholy Presence buff
    private constant string     TARGET_FX = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"  //Special effect attached to the origin of enemy units
    private constant string     TARGET_ATTACH_FX = "origin"                                           //attachement point for the effect
    private constant real       INTERVAL = 0.20                                                       //This is the interval of the spell. .20 means units get damaged every .20 seconds, 2: every 2 seconds, etc.
    private constant real       STRENGTH = 0.05                                                       //Set this to a value for how much you will heal (in %) off of Strength each second. 0 means you dont want to heal from Strength
    private constant boolean    STRENGTH_BONUSES = true                                               //true means include bonuses for Strength
    private constant real       INTELLIGENCE = 0.00                                                   //Set this to a value for how much you will heal (in %) off of Intelligence each second. 0 means you dont want to heal from intel
    private constant boolean    INTELLIGENCE_BONUSES = false                                          //true means include bonuses for intelligence
    private constant real       AGILITY = 0.00                                                        //Set this to a value for how much you will heal( in %) off of Agility each second. 0 means you dont want to heal from intel
    private constant boolean    AGILITY_BONUSES = false                                               //true means include bonuses for agility
    private constant attacktype AT = ATTACK_TYPE_NORMAL                                               //attack type
    private constant damagetype DT = DAMAGE_TYPE_FIRE                                                 //damage type
    private constant weapontype WT = WEAPON_TYPE_WHOKNOWS                                             //weapon type
 
endglobals

private constant function Damage takes integer level returns real
    return (level * 5. + 100.) * INTERVAL  //does 100 damage + (level of the ability x 5) divided by the interval
endfunction

private constant function AoE takes integer level returns real
    return level * 5. + 350. // the AOE is 350 + (level of the ability x 5)
endfunction

private constant function Heal takes integer attribute returns real
    return attribute  * INTERVAL  //takes attribute(s) you want of the triggering unit and multiplies it by 0.05 and divides it by the interval. 
endfunction

//****End of configuration*************************************************************************************
//*************************************************************************************************************
//Don't touch this stuff unless you know what you're doing
private struct UnholyP
    unit u
    integer level
    real attribute
endstruct

globals
    private boolexpr filter 
    private constant group Group = CreateGroup()
    private constant timer Tim = CreateTimer()
    private integer total = 0
    private player UnholyPOwner
    private UnholyP array queue
endglobals
private function Timer takes nothing returns nothing

    local integer i = 0
    local UnholyP data
    local unit f
    local real damage
    loop
        exitwhen i >= total 
        set data = queue[i]
        set UnholyPOwner = GetOwningPlayer(data.u)
        call GroupClear(Group)//Clears the global group.
        if (GetUnitAbilityLevel(data.u, BUFF_ID) > 0) then //Checks if the triggering unit has the buff (this means the spell can be dispelled)
           
            call GroupEnumUnitsInRange(Group, GetUnitX(data.u), GetUnitY(data.u), AoE(data.level), filter)//Groups all nearby units.
            set damage = Damage(data.level) 
            loop
            
                set f = FirstOfGroup(Group) //first unit selected in range
                exitwhen f == null 
                call DestroyEffect(AddSpecialEffectTarget(TARGET_FX, f, TARGET_ATTACH_FX)) // creates the effect at the origin of the enemy unit
                call UnitDamageTarget(data.u, f, damage, true, false, AT, DT, WT)//Damages the enemy.
                call SetWidgetLife(data.u, GetWidgetLife(data.u) + Heal(R2I(data.attribute)))  //heals the triggering unit for 5% of its strength
                call GroupRemoveUnit(Group, f)//Removes the first selected unit from the group
            endloop
        else
        call data.destroy()
        set total = total - 1
        set queue[i] = queue[total]
        set i = i - 1
        endif
    set i = i + 1
    endloop
    if total == 0 then
        call PauseTimer(Tim)
    endif
endfunction

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == SPELL_ID//Checks if the spell being used is this one
endfunction

private function UnholyP_Filter takes nothing returns boolean
    return GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0. and IsUnitEnemy(GetFilterUnit(), UnholyPOwner) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false)//Chooses which units are affected.
//                 Is the unit alive?                                  Is the unit an enemy to the caster?                Is the unit immune to spells?
endfunction

private function Actions takes nothing returns nothing

    local UnholyP data = UnholyP.create()
    set data.u = GetTriggerUnit() // stores the triggering unit into a struct
    set data.level = GetUnitAbilityLevel(data.u, SPELL_ID)//Stores the level of the ability in a struct.
    //thanks to -JonNny :D
    set data.attribute = GetHeroStr(data.u, STRENGTH_BONUSES) * STRENGTH + GetHeroInt(data.u, INTELLIGENCE_BONUSES) * INTELLIGENCE + GetHeroAgi(data.u, AGILITY_BONUSES) * AGILITY
    
    if total == 0 then
        call TimerStart(Tim, INTERVAL, true, function Timer)
    endif
    
    set queue[total] = data
    set total = total + 1
endfunction

private constant function AntiLeak takes nothing returns boolean
        return true
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()//Creates the trigger.
    local filterfunc f = Filter(function AntiLeak)
    local integer i = 0
    loop
            call TriggerRegisterPlayerUnitEvent(trig, Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, f) // registers the event for each player who casts the spell
            set i = i + 1
            exitwhen i == bj_MAX_PLAYER_SLOTS
        endloop
    call TriggerAddCondition(trig, Condition(function Conditions))//Adds the condition.
    call TriggerAddAction(trig, function Actions)//Adds the action.
    call Preload(TARGET_FX)//Preloads the effect, preventing it from lagging the first time it's cast.
    set  filter = Filter(function UnholyP_Filter)//Stores a global boolexp. Much faster than creating and destroying one all the time.
    call DestroyFilter(f)
    set trig = null
    set f = null
endfunction
endscope

Version 1.1 - Improved code a bit
Version 1.2 - Shifted code
Version 1.3 - Changed code; added more configurations
Version 1.4 - Optimized code - thanks to -JonNny

Keywords:
unholy, aoe, heal, green, shield, dot
Contents

Unholy Presence by Bigapple90 (Map)

Reviews
19:31, 4th Oct 2009 TriggerHappy187: From what I can see it's leakless, though minor adjustments could be made from which I stated in my review.

Moderator

M

Moderator

19:31, 4th Oct 2009
TriggerHappy187:

From what I can see it's leakless, though minor adjustments could be made from which I stated in my review.
 
Level 9
Joined
Aug 2, 2008
Messages
219
Uhh code and spell looks nice so far but there are a few simple things which can be improved
1.Why does the spell start with the sturct ? Usually it´s put after the custom user functions, but doesen´t matter really.
2.You could make your timer and group variable constant.
3.The unit group loop could be evaded since it can cause bugs, but this is your choice too.
4.You leak in the trigger creation, since a null boolexpr will cause a leak, but this thing is minor this is also up to you to fix.
5. The code could be a bit more readable and a bit more more comments would also be good.

Well and the spell is MUI, Leakless has documentation so i would give a [3/5] if everything is fixed it´s a [4/5]. GJ so far.:wink:

~TNT
 
Level 9
Joined
Aug 2, 2008
Messages
219
Well point 4 is easy to fix... just create a constant function that returns true and then declare a filterfunc in your init function which takes that return-true-function as filter
JASS:
    private constant function AntiLeak takes nothing returns boolean
        return true
    endfunction

    private function TriggerRegister takes nothing returns nothing
        //A clear function, you may add stuff like preloading or smth
        local trigger t = CreateTrigger()
        local filterfunc f = Filter(function AntiLeak)
        local integer i = 0
        loop
            call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,f)
            exitwhen i == 15
            set i = i + 1
        endloop
        call TriggerAddCondition(t,Condition(function Conditions))
        call TriggerAddAction(t,function Actions)
        call DestroyFilter(f)
        set t = null
        set f = null
    endfunction
for point 3 just try to use ForGroup.
JASS:
globals
integer TEMPSTRUCT  //Maybe you´ll need a struct inside the callback
endglobals

function GroupStuff takes nothing returns nothing
   local <SomeStruct> this = TEMP_STRUCT
   local unit u = GetEnumUnit()
   call UnitDamageTarget(this.caster,u,<Some more params>
   set u = null
endfunction

function MainFunc takes nothiing returns nothing

   local <SomeStruct> this = <.....>
   
   <Your actions>
   set TEMPSTRUCT = this
   call ForGroup(<YourGroup>,function GroupStuff)
   <...>
new to vjass as u can tell
Far better than my first submissions:xxd:
 
Level 12
Joined
Jul 27, 2008
Messages
1,181
Nice spell, but
JASS:
private function UnholyP_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == //Checks if the spell being used is this one
endfunction

You don't have a condition. May just be the posted code, saw it in FF bcause I was to bored to get JASScraft.
 
Level 15
Joined
Jul 6, 2009
Messages
889
JASS:
loop
            call TriggerRegisterPlayerUnitEvent(trig, Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, f) // registers the event for each player who casts the spell
            set i = i + 1
            exitwhen i == bj_MAX_PLAYER_SLOTS
        endloop
What is wrong with the BJ... the BJ doesn't leak....

-Most global constants are named in full capitals, for some reason, people do it and its easier to read anyway.
-I dislike your struct name, rather then data, why not just do "d"... eg... d.level, instead of data.level.
JASS:
call DestroyEffect(AddSpecialEffectTarget(TargetFX, f, "origin")) // creates the effect at the origin of the enemy unit
"origin" should also be made configurable.
-UnholyP_Conditions, Actions, Timer.... you do not need UnholyP..... you already have it private (Which renames it to scope name + "___" + function name (I THINK).
JASS:
private function UnholyP_Filter takes nothing returns boolean
    return GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0. and IsUnitEnemy(GetFilterUnit(), UnholyPOwner) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false)//Chooses which units are affected.
//                 Is the unit alive?                                  Is the unit an enemy to the caster?                Is the unit immune to spells?
endfunction
I'd have GetFilterUnit() a variable, but oh well. Global variable would be used.
JASS:
    set data.u = GetTriggerUnit() // stores the triggering unit into a struct
    set data.strength = GetHeroStr(GetTriggerUnit(), true) //gets triggering units strength. true for include bonuses
    set data.level = GetUnitAbilityLevel(GetTriggerUnit(), ID)//Stores the level of the ability in a struct.
Then use the stored "data.u" instead of GetTriggerUnit() <.<
Also, make the hero attribute configurable... I don't want STR. I WANTZ INT :D

- The spell itself, is kinda lame how it damages at 1 second per interval, if it was a single target or a buff, fine. But because it requires the caster to be near.... I would make the interval maybe 0.25 and devide all damage stuff by 4 so you'd get the same result except its smoother. 100 damage per 1.00 = 25 damage per 0.25.
 
Level 15
Joined
Jan 31, 2007
Messages
502
I didnt test it ingame, but the code looks quite ok so far

what ive recognised is that you setting of attruibutes for the heal is a but complicated with all those elseifs, could be done simpler also without the HEAL_PRCENT variable

take a look

JASS:
    private constant boolean    STRENGTH = true                                                       //true means you want to heal based on strength
    private constant boolean    STRENGTH_BONUSES = true                                               //true means include bonuses for strength
    private constant boolean    INTELLIGENCE = false                                                   //true means you want to heal based on intelligence
    private constant boolean    INTELLIGENCE_BONUSES = false                                          //true means include bonuses for intelligence
    private constant boolean    AGILITY = false                                                        //true means you want to heal based on agility
    private constant boolean    AGILITY_BONUSES = false 
    private constant real       HEAL_PRCENT = 0.05 

private constant function Heal takes integer attribute returns real
    return (attribute * HEAL_PRCENT) * INTERVAL  //takes attribute(s) you want of the triggering unit and multiplies it by 0.05 and divides it by the interval.
endfunction
....

    if ( STRENGTH and INTELLIGENCE and AGILITY ) then //checks to see if these 3 are true

        set data.attribute = GetHeroStr(data.u, STRENGTH_BONUSES) + GetHeroInt(data.u, INTELLIGENCE_BONUSES) + GetHeroAgi(data.u, AGILITY_BONUSES) //adds all 3 attributes to be used for healing

    elseif ( STRENGTH and INTELLIGENCE ) then         //checks to see if these 2 are true

        set data.attribute = GetHeroStr(data.u, STRENGTH_BONUSES) + GetHeroInt(data.u, INTELLIGENCE_BONUSES)   // adds these 2 to be used for healing

    elseif ( STRENGTH and AGILITY ) then              //checks to see if these 2 are true

        set data.attribute = GetHeroStr(data.u, STRENGTH_BONUSES) + GetHeroAgi(data.u, AGILITY_BONUSES)       //adds these 2 to be used for healing

    elseif ( STRENGTH ) then                         //checks to see if this is true

        set data.attribute = GetHeroStr(data.u, STRENGTH_BONUSES)                                            //sets strength to be healed from

    elseif ( INTELLIGENCE and AGILITY ) then         //checks to see if these 2 are true

        set data.attribute = GetHeroInt(data.u, INTELLIGENCE_BONUSES) + GetHeroAgi(data.u, AGILITY_BONUSES)  //adds these 2 to be used for healing

    elseif ( INTELLIGENCE ) then                     //checks to see if this is true

        set data.attribute = GetHeroInt(data.u, INTELLIGENCE_BONUSES)                                       //sets intelligence to be healed from

    elseif ( AGILITY ) then                          //checks to see if this is true

        set data.attribute = GetHeroAgi(data.u, INTELLIGENCE_BONUSES)                                       //sets agility to be healed from

    endif
btw, theres also a typo in the last line of this (kehe, c&p ? ;D )
set data.attribute = GetHeroAgi(data.u, INTELLIGENCE_BONUSES)





JASS:
    private constant real    STRENGTH = 0.05                                                      //true means you want to heal based on strength
    private constant  boolean     STRENGTH_BONUSES = true                                               //true means include bonuses for strength
    private constant real    INTELLIGENCE = 0.00                                                   //true means you want to heal based on intelligence
    private constant boolean     INTELLIGENCE_BONUSES = false                                          //true means include bonuses for intelligence
    private constant real    AGILITY = 0.00    
    private constant  boolean     AGILITY_BONUSES = false

private constant function Heal takes integer attribute returns real
    return attribute * INTERVAL  //takes attribute(s) you want of the triggering unit and multiplies it by 0.05 and divides it by the interval.
endfunction
...

   set data.attribute = GetHeroStr(data.u, STRENGTH_BONUSES) * STRENGTH + GetHeroInt(data.u, INTELLIGENCE_BONUSES) * INTELLIGENCE + GetHeroAgi(data.u, AGILITY_BONUSES) * AGILITY
 
Level 17
Joined
Mar 17, 2009
Messages
1,349
xBlackRose said:
JASS:
loop
            call TriggerRegisterPlayerUnitEvent(trig, Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, f) // registers the event for each player who casts the spell
            set i = i + 1
            exitwhen i == bj_MAX_PLAYER_SLOTS
        endloop
What is wrong with the BJ... the BJ doesn't leak....
Well xBlackRose we don't want to initiate another argument on the issue, and there was a thread considering this issue.
The BJ does leak, but it's usage is allowed due to the very minor leak.
So basically, it's a matter of taste and efficiency-obsession :p

What I'm saying, give advice that using the BJ is not wrong, but don't say it doesn't leak.
 
Level 15
Joined
Jul 6, 2009
Messages
889
Well xBlackRose we don't want to initiate another argument on the issue, and there was a thread considering this issue.
The BJ does leak, but it's usage is allowed due to the very minor leak.
So basically, it's a matter of taste and efficiency-obsession :p

What I'm saying, give advice that using the BJ is not wrong, but don't say it doesn't leak.
Ok. I just saw, oh well.
ok thanks for the reviews :) ill work on it.
the interval is changeable :S

about the condition: its just the code. no clue why that happened

updated the spell
Oops, my bad about interval.
JASS:
        if (GetUnitAbilityLevel(data.u, BUFF_ID) > 0) then //Checks if the triggering unit has the buff (this means the spell can be dispelled)
           
            call GroupEnumUnitsInRange(Group, GetUnitX(data.u), GetUnitY(data.u), AoE(data.level), filter)//Groups all nearby units.
            set damage = Damage(data.level)
Why not set damage to a struct member to avoid calling it every interval.
set UnholyPOwner = GetOwningPlayer(data.u)
Maybe put that underneath the 'if', it will save 1 call when the spell ends. Not much, but yeah XD

Well, imo, this spell is not very original >.>, it is basically a "Ion Shell" that is dependent on an attribute. Good work anyway.
 
Level 7
Joined
Dec 19, 2008
Messages
276
Yep, not bad spell.

And me TOO want say: " i'd like to thank hvo-busterkomo because i learned a lot from his fireball spell"

His spell most easy way to understand structs, if you know normal jass. :p

Huh i was think special effect is a import, but it is standart... huh... nice looking :p
 
Some improvements.

  • Why does everyone inline the 'any unit event', the BJ is perfectly fine.
  • Using the antileak filter inside events is useless, as null boolexpr's don't leak in events.
  • GetWidgetLife is faster than GetUnitState.
  • Maybe use a timer attachment system to increase efficiency, though keeping your spell system independant may have been what you were working for.
 
Last edited:
Top