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

Holy Conversion v. 1.20

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Enlightens the spirit of a target allied unit, converting the unit into a powerful Divine Knight that will soon ascend to heaven. While alive, the Divine Knight has 1200 hitpoints, Spell Immunity, and Devotion Aura. Upon the Knight's passage into heaven, a burst of pure light will heal up to 16 nearby allied non-Undead units for 350 hitpoints each. The light is so intense, that it also blinds up to 16 nearby enemy Undead units, giving them a chance to miss on attacks.

Divine Knight lasts 45 seconds.
Undead units are blinded for 10 seconds.


This was my first attempt at creating a spell with any sort of JASS in it (first started in 2007). Since then the spell has come a long way. The spell was created with the intentions of being put into a melee-type game, but can be easily modified to fit otherwise.

NOTE: The only problem that is obvious to me is the fact that I have a TriggerSleepAction of 45 seconds in this spell which will not work well at keeping precise time in an online match. I'm in the process of trying to figure out how to use local timers to avoid this problem.

JASS:
//==================================================================================================================================
// Start of constants. These will need to be changed by you. They provide the user with easier implementing//changing of the spell's rawcodes and effects.
//==================================================================================================================================

constant function Divine_ID takes nothing returns integer
    return 'h001' // Divine Knight unit rawcode.
endfunction

constant function Dummy_ID takes nothing returns integer
    return 'h002' // Dummy Unit rawcode.
endfunction

constant function Spirit_ID takes nothing returns integer
    return 'h003' // Spirit Knight unit rawcode.
endfunction

constant function Holy_ID takes nothing returns integer
    return 'A000' // Holy Spirit ability rawcode.
endfunction

constant function Burst_ID takes nothing returns integer
    return 'A001' // Holy Burst ability rawcode.
endfunction

constant function Conversion_ID takes nothing returns integer
    return 'A003' // Holy Conversion ability rawcode.
endfunction

constant function Blind_ID takes nothing returns integer
    return 'A005' // Blinding Light ability rawcode.
endfunction

//==================================================================================================================================
// End of constants. Do not touch anything below this unless you are familiar with JASS.
//==================================================================================================================================

function Trig_Spell_Copy_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == Conversion_ID()
endfunction

function Trig_Spell_Copy_Actions takes nothing returns nothing
    // Declares Local Variables
    local effect HandRight
    local effect HandLeft
    local effect Chest
    local effect Overhead
    local unit t = GetSpellTargetUnit()
    local unit dk
    local real x = GetUnitX(t)
    local real y = GetUnitY(t)
    // Pauses/Replaces the targeted unit
    call PauseUnit( t, true )
    call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\Resurrect\\ResurrectTarget.mdl", x, y))
    call ShowUnit(t, false)
    // Creates Divine Knight
    set dk = CreateUnit(GetOwningPlayer(t), Divine_ID(), x, y, GetUnitFacing(t))
    call SetUnitOwner( dk, GetOwningPlayer(t), true )
    call UnitApplyTimedLife(dk, 'BTLF', 45.00)
    call RemoveUnit(t)
    set t = null
    // Adds Special Effects onto the Divine Knight
    set HandRight = AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\FaerieFire\\FaerieFireTarget.mdl",dk,"hand, right")
    set HandLeft = AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\FaerieFire\\FaerieFireTarget.mdl",dk,"hand, left")
    set Chest = AddSpecialEffectTarget("Abilities\\Spells\\Human\\DivineShield\\DivineShieldTarget.mdl",dk,"origin")
    set Overhead = AddSpecialEffectTarget("Abilities\\Spells\\Human\\InnerFire\\InnerFireTarget.mdl",dk,"overhead")
    set dk = null
    call TriggerSleepAction( 45.00 )
    // Destroys the Special Effects that were attached the Divine Knight
    call DestroyEffect( HandRight )
    set HandRight = null
    call DestroyEffect( HandLeft )
    set HandLeft = null
    call DestroyEffect( Chest )
    set Chest = null
    call DestroyEffect( Overhead )
    set Overhead = null
endfunction

//===========================================================================
function InitTrig_Spell takes nothing returns nothing
    set gg_trg_Spell = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Spell, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Spell, Condition( function Trig_Spell_Copy_Conditions ) )
    call TriggerAddAction( gg_trg_Spell, function Trig_Spell_Copy_Actions )
endfunction

JASS:
function Trig_Dies_Conditions takes nothing returns boolean
    return GetUnitTypeId(GetTriggerUnit()) == Divine_ID()
endfunction

function Holy_Filter_B takes nothing returns boolean
    return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetFilterUnit())) == true and GetUnitRace(GetFilterUnit()) == RACE_UNDEAD and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) != true
endfunction

function Holy_Filter_A takes nothing returns boolean
    return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetFilterUnit())) != true and GetUnitRace(GetFilterUnit()) != RACE_UNDEAD and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) != true
endfunction

function Trig_Dies_Actions takes nothing returns nothing
    // Declares Local Variables
    local effect e
    local unit t = GetTriggerUnit()
    local unit u
    local unit d
    local unit q
    local unit r
    local unit s
    local unit v
    local real x = GetUnitX(t)
    local real y = GetUnitY(t)
    local group g = CreateGroup()
    local group h = CreateGroup()
    local player p = GetOwningPlayer(t)
    local integer i = 0
    // Creates Special Effect
    call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\ReviveHuman\\ReviveHuman.mdl", x, y))
    // Creates a Holy Spirit that ascends to heaven
    set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), Spirit_ID(), x, y, GetUnitFacing(t))
    call UnitApplyTimedLife( u, 'BTLF', 2.5 )
    call SetUnitAnimation( u, "Stand Victory" )
    call UnitAddAbility( u, 'Arav' )
    call UnitRemoveAbility( u, 'Arav' )
    call SetUnitFlyHeight( u, 3000.00, 850.00 )
    set d = CreateUnit(p, Dummy_ID(), x, y, 0.0)
    call UnitApplyTimedLife( d, 'BTLF', 1.50 )
    call UnitApplyTimedLife( t, 'BTLF', 3.00 )
    call UnitAddAbility( d, Holy_ID() )
    call IssueTargetOrder( d, "banish", u )
    // Creates the Unit Groups
    call GroupEnumUnitsInRange(g, x, y, 900.00, Condition(function Holy_Filter_A))
    call GroupEnumUnitsInRange(h, x, y, 900.00, Condition(function Holy_Filter_B))
    loop
        exitwhen i >= 16
        set i = ( i + 1 )
        set q = CreateUnit(p, Dummy_ID(), x, y, 0.0)
        call UnitAddAbility( q, Burst_ID() )
        call UnitApplyTimedLife( q, 'BTLF', 1.00 )
        set s = FirstOfGroup(g)
        call GroupRemoveUnit(g, s)
        call IssueTargetOrder( q, "holybolt", s )
        set r = CreateUnit(p, Dummy_ID(), x, y, 0.0)
        call UnitAddAbility( r, Blind_ID() )
        call UnitApplyTimedLife( r, 'BTLF', 1.00 )
        set v = FirstOfGroup(h)
        call GroupRemoveUnit(h, v)
        call IssueTargetOrder( r, "drunkenhaze", v )
    endloop
    call DestroyGroup(g)
    set g = null
    call DestroyGroup(h)
    set p = null
    set h = null
    set d = null
    set q = null
    set s = null
    set r = null
    set v = null
    set t = null
    set u = null
endfunction

//===========================================================================
function InitTrig_Dies takes nothing returns nothing
    set gg_trg_Dies = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Dies, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddCondition( gg_trg_Dies, Condition( function Trig_Dies_Conditions ) )
    call TriggerAddAction( gg_trg_Dies, function Trig_Dies_Actions )
endfunction

+Multi-Instanceable
+Leakless
+No Imports
+Easily Transferable

v 1.20 (current):

-Recoded the 'Dies' trigger to remove the use of locations and instead use x,y data.
-Added expiration timer to Spirit Knight instead of using a timed wait.
-Created player variable to avoid repeated calling of GetOwningPlayer()

v 1.15:

-Recoded most of the ability.
-Added constants.

v 1.01

-Changed PauseUnitBJ to PauseUnit native
-Changed GetLastReplacedUnitBJ to bj_lastReplacedUnit native

Update August 07, 2009:

The 'Dies' trigger has been converted completely into JASS and optimized.

Update April 21, 2009:

The 'Spell' trigger has been converted completely into JASS and optimized.

Keywords:
heal, undead, human, explode, knight, ultimate, divine, holy, holy light, blind, miss, spirit
Contents

Holy Conversion (Map)

Reviews
12.12 IcemanBo: For long time as NeedsFix. Rejected. Bribe: You don't need this: call SetUnitOwner( dk, GetOwningPlayer(t), true ) There are of course the things I've listed in my reply. I also suggest writing local variables with...

Moderator

M

Moderator

12.12
IcemanBo: For long time as NeedsFix. Rejected.

Bribe:

You don't need this: call SetUnitOwner( dk, GetOwningPlayer(t), true )

There are of course the things I've listed in my reply.

I also suggest writing local variables with camelCasing (first letter is lowercase). This helps to identify those variables as local. If you notice, every non-constant variable in Blizzard.j and Common.j begins with a lowercase. It's JASS convention and convention is a good thing to have in a crazy language like JASS.

Instead of GetOwningPlayer(GetTriggerUnit()) it's faster to write GetTriggerPlayer() and also executes faster in game.

You also don't need the "true" comparison except for with IsUnitType, and that's only from within boolexpr returns, so you can gain some more efficiency this way.

This can be done more efficiently:

JASS:
    call GroupEnumUnitsInRange(g, x, y, 900.00, Condition(function Holy_Filter_A))
    call GroupEnumUnitsInRange(h, x, y, 900.00, Condition(function Holy_Filter_B))

Instead of splitting the filters from the "FirstOfGroup" loop that you do, just use one filter and do all actions from within the filter. And use bj_lastCreatedGroup as your static enum group so you never have to create/destroy your groups.
 
Level 10
Joined
Nov 10, 2004
Messages
351
[APPROVED]

Pain in the eyes reading through JASS + GUI>_<

I see that it was your first attempt to jass, even though GUI-JASS is harder to configure(especially if you are not familiar with jass)

On the minus side:
-Some BJs could have been replaced with natives.
-Alot of waits(but that's unavoidable)
-Hard to configure.

On the plus side:

-No leaks found
-MUI
-Alot of documentation on how to import/edit (without that most people would be lost trying to edit it)
-Nice amount of effects
--------------------------------------
btw:
Starts the effect of an ability > Begins casting an ability
 
Level 2
Joined
Jun 9, 2010
Messages
14
Hello guys how to make like -save and then the code will comes out and if u will type
-load <code> and your code just like RPG how to do that?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
1. TriggerSleepAction is not reliable in online games as it will keep running even if the game is lagging with a dialog box, making it potentially desync when you destroy the handle. The solution is to use something like TimerUtils or TimerQueue in order to determine precisely when the effects should be destroyed.

As well, removing the spell target unit... this is not a good idea. I imagine your spell filters out heroes by default but you should add a disclaimer to make that clear.

The JASS code is also quite bizarre, looks like relics of an old age of coding, locations as you may be aware have become nigh obsolete for JASSers and their use should be avoided except for the rare need for GetLocationZ or GetUnitRallyPoint.
 
Level 18
Joined
Nov 1, 2006
Messages
1,612
Hey Bribe, thanks. I'm aware that TriggerSleepAction is not reliable in an online match (even put a disclaimer up there). I've just recently been trying to understand how hashtables work but admittedly I still don't know how to use them. I haven't coded since last summer because of how busy I was with school and work so I missed a lot of the development of their use. I could just remove the effects to get rid of the TriggerSleepAction, but it's whatever.

You're also right about this being relics of an older style of coding. I hadn't touched this particular spell since 2009 when I decided to go back and recode it yesterday. Apparently I missed a few things, for example I'm still using a location in the Dies trigger. I'm going to go back and fix a few things and update the spell.

Lastly, what's wrong with removing the SpellTargetUnit? The spell cannot be cast on heroes and is not giving you the targeted unit back. In effect this spell replaces the unit.

EDIT* Wow there is a lot that I missed in the "Dies" trigger. I'm fixing everything that is obvious to me. I didn't realize I used a wait there too instead of adding an expiration timer to the spirit knight. I'll be posting the update in a min.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
Comments:
- RemoveUnit(t) >>> leaks, better to UnitApplyTimedLife >>> ShowUnit
- IMO, you dont need PauseUnit since the unit is afterall removed/destroyed
- You may use bj_lastCreatedGroup instead of creating a group
JASS:
 GroupEnumUnitsInRange(bj_lastCreatedGroup , x, y, 900.00, Condition(function Holy_Filter_A))
- Make the AoE and SFX configurables
 
Top