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

[JASS] Making JESP easy to import standart spell, with private globals

Status
Not open for further replies.
Hi guys, well, some of you may already know my spell Apocalypse that is in the spells section. I am sure it will be approved soon or later, as it has no bugs and it very cool.
But purgeandfire gave me a suggestion.

Anywho, the spell is fine and works. It gets the job done. The only thing is, is that you need to add global constants for your spell, otherwise your spell is hideously unconfigurable. ;)

Other than that, I guess it is fine!

But i don't know how to do a spell like he says ... can some1 please teach me how to use global privates in order to make a spell easy to import, and yet MUI, bug free and leak free ?

Here is the open source code:
JASS:
//===========================================================================
//A spell that makes meteors fall randomly in the map, damaging enemy units
//and summoning powerfull infernals
//
//@author Flame_Phoenix 
//@version 1.4
//===========================================================================
struct Mystruct
    unit caster = GetTriggerUnit()
    timer repeator = CreateTimer()
    timer expirer = CreateTimer()
    trigger end = CreateTrigger()
    triggeraction endAction
    triggercondition endCondition
endstruct
//==========================================================================
function endConds takes nothing returns boolean
    local Mystruct data = GetTriggerStructB(GetTriggeringTrigger())
    if GetTriggerEventId() == EVENT_GAME_TIMER_EXPIRED then
        return true
    else
        return GetTriggerUnit() == data.caster
    endif
endfunction
//==========================================================================
function endActs takes nothing returns nothing
    //catches the trigger and runs this function
    local Mystruct data = GetTriggerStructB(GetTriggeringTrigger())

    //removes the created trigger in the Code_acts function
    call TriggerRemoveCondition(data.end,data.endCondition)
    call TriggerRemoveAction(data.end,data.endAction)
    call DestroyTrigger(data.end)
    call ClearTriggerStructB(data.end)

    //stops the repeator timer and ends the spell once and for all
    call PauseTimer(data.repeator)
    call DestroyTimer(data.repeator)
    call ClearTimerStructA(data.repeator)
    call ClearTimerStructB(data.expirer)
endfunction
//==========================================================================
function Apoca takes nothing returns nothing
    //catches the expired timer and runs this function
    local Mystruct data = GetTimerStructA(GetExpiredTimer())
    
    //local variables 
    local integer infernal = GetRandomInt(0, 100)
    local unit array dummy[]
    
    //This uses polar projection. Formula: Center + r * trigfunction(angle teta); 
    //where Center is the center coordinate X or Y
    //r is the distance (in the case rabdom between 300 and 600)
    //trigfunction is Sin if using Y and Cos if using X
    //angle teta is the angle formed between r and the axis (in this case random, can be all circle)
    local real randomX =  GetUnitX(data.caster) + GetRandomReal(300.00, 600.00) * Cos(GetRandomReal(0.0, 360.0) * bj_DEGTORAD)
    local real randomY =  GetUnitY(data.caster) + GetRandomReal(300.00, 600.00) * Sin(GetRandomReal(0.0, 360.0) * bj_DEGTORAD)
    
    
    if (infernal <= 3 * GetUnitAbilityLevel(data.caster, 'A000') + 1) then
        set dummy[0] = CreateUnit(GetOwningPlayer(data.caster), 'h000', GetUnitX(data.caster), GetUnitY(data.caster), 0)
        call UnitAddAbility(dummy[0], 'A002')
        call SetUnitAbilityLevel(dummy[0], 'A002', GetUnitAbilityLevel(data.caster, 'A000'))
        
        call IssuePointOrder(dummy[0], "dreadlordinferno", randomX, randomY)
        
        call UnitApplyTimedLife(dummy[0], 'BTLF', 3)
        set dummy[1] = CreateUnit(GetOwningPlayer(data.caster), 'h000', GetUnitX(data.caster), GetUnitY(data.caster), 0)
        call UnitAddAbility(dummy[1], 'A003')
        call SetUnitAbilityLevel(dummy[1], 'A003', GetUnitAbilityLevel(data.caster, 'A000'))
        
        call IssuePointOrder(dummy[1], "flamestrike", randomX, randomY)
        
        call UnitApplyTimedLife(dummy[1], 'BTLF', 3)
    else 
        set dummy[0] = CreateUnit(GetOwningPlayer(data.caster), 'h000', GetUnitX(data.caster), GetUnitY(data.caster), 0)
        call UnitAddAbility(dummy[0], 'A001')
        call SetUnitAbilityLevel(dummy[0], 'A001', GetUnitAbilityLevel(data.caster, 'A000'))
        
        call IssuePointOrder(dummy[0], "flamestrike", randomX, randomY)
        
        call UnitApplyTimedLife(dummy[0], 'BTLF', 3)
    endif
    
    set dummy[0] = null
    set dummy[1] = null
endfunction
//==========================================================================
function Code_conds takes nothing returns boolean
    return GetSpellAbilityId() == 'A000'
endfunction
//==========================================================================
function Code_acts takes nothing returns nothing
    local Mystruct data = Mystruct.create()

    //starts the effect of the spell, meteors start falling from sky
    call SetTimerStructA(data.repeator, data)
    call TimerStart(data.repeator, 6 / GetUnitAbilityLevel(data.caster, 'A000'), true, function Apoca)

    //creates the timer that ends the spell when expired
    call SetTimerStructB(data.expirer, data)
    call TimerStart(data.expirer, 5 * GetUnitAbilityLevel(data.caster, 'A000') + 15, false, null)

    //creates the trigger that will end the spell when the timer expires or the hero dies
    call SetTriggerStructB(data.end, data)
    call TriggerRegisterTimerExpireEvent( data.end, data.expirer )
    call TriggerRegisterAnyUnitEventBJ(data.end, EVENT_PLAYER_UNIT_DEATH )
    set data.endCondition = TriggerAddCondition( data.end, Condition( function endConds ) )
    set data.endAction = TriggerAddAction( data.end, function endActs )
endfunction
//===========================================================================
function InitTrig_Apocalypse takes nothing returns nothing
    set gg_trg_Apocalypse = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Apocalypse, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Apocalypse, Condition( function Code_conds ) )
    call TriggerAddAction( gg_trg_Apocalypse, function Code_acts )
endfunction

Please i need a teacher!
 
Level 5
Joined
Jan 15, 2007
Messages
199
Example:

JASS:
 //starts the effect of the spell, meteors start falling from sky
    call SetTimerStructA(data.repeator, data)
    call TimerStart(data.repeator, 6 / GetUnitAbilityLevel(data.caster, 'A000'), true, function Apoca)

change that to something like this:

JASS:
constant function Apoc_Inteval takes integer level returns real
    return 6 / level
endfunction
//^^ at the top of the trigger with the other constant funcs
//and

 //starts the effect of the spell, meteors start falling from sky
    call SetTimerStructA(data.repeator, data)
    call TimerStart(data.repeator, Apoc_Interval( GetUnitAbilityLevel(data.caster, 'A000') ), true, function Apoca)
//^^ in the same spot where you had it with the calculated interval there

EDIT: Also, this isn't an ideal trigger to use JESP for, because most of the damage.. etc is assigned in the spell.
You could add into the description at the top of the spell to modify the damage go to the custom infernal/flame strike abilities.
 
Last edited:
Level 12
Joined
Apr 27, 2008
Messages
1,228
Well not easier to import but easier to configure.
Putting everything(like damage, duration, AoE, Summoned units' IDs, ability IDs etc) in some constant(variable/function) so that they can easily be change by the average user without him having to find them in the script.
 
Well not easier to import but easier to configure.
Putting everything(like damage, duration, AoE, Summoned units' IDs, ability IDs etc) in some constant(variable/function) so that they can easily be change by the average user without him having to find them in the script.

Well, that is what Rising_Dusk does in the map i posted right ??
If so, than that is what i wanna learn =S
Help please ?
Can some1 also explain what private globals are for ??? Are they like locals ?? are they MUI ?
 
Level 9
Joined
Mar 25, 2005
Messages
252
Can some1 also explain what private globals are for ??? Are they like locals ?? are they MUI ?

read the manual perhaps?

Private globals are almost like normal globals, they just get a partly random prefix when jasshelper compiles them. It will also change all occurrences of them inside the scope they are in with those randomed ones. This means that in practice you cant use them outside the scope they are in, but inside it you use them exactly like normal globals.

for example:
JASS:
scope X
globals
    integer A = 1
    private integer B = 1
endglobals
function DoSomething takes nothing returns nothing
    set A = A + 1 // both of these work, since even though B is private it can
    set B = B + 1 // be used just like any other global inside the scope it is in
endfunction
endscope
function TrySomething takes nothing returns nothing
    set A = A - 1 // works

    set B = B - 1 // doesn't work, since we aren't inside the scope B was declared in
endfunction





You can make your spell private (so that it's function and variable names wont conflict with other scripts' identifiers for example when someone decides to name a function "endActs" somewhere else) by doing the following:
  • replace occurrences of "function" at the beginning of any line with "private function"
  • replace the line "struct Mystruct" with "private struct Mystruct"
  • at the beginning of the spell before anything else add a line "scope Apocalypse
  • add a line "endscope" at the end of all code

To make the spell's main rawcode ('A000') to be easily configurable add the following somewhere at the top of the script:
JASS:
globals
    private constant integer AID = 'A000' // rawcode of the main ability
endglobals
After that replace all occurrences of 'A000' in your script with AID, except for that globals block. Use Ctrl+H in a text editor to do this easily.
Now one can more easily to import your spell and correct the rawcode to whatever the ability has in his map. If you optimize your map with Vexorians optimizer it will inline all those uses of AID directly with 'A000' making it just as efficient as it was.

You can repeat that for all the other rawcodes, orderstrings, ranges, damages and other stuff that people will most likely want to change.
 
OMG, i did everything you told me to do Disciple, but now my code doesn't even work !!!
Whyyy !! Please help me out ! I am new at this vJASS scope thing !

JASS:
//===========================================================================
//A spell that makes meteors fall randomly in the map, damaging enemy units
//and summoning powerfull infernals
//
//@author Flame_Phoenix 
//@version 1.6
//===========================================================================
scope Apocalypse
    globals
        private constant integer AID = 'A000'
        private constant integer INFERNAL = 'A002'
        private constant integer SPAWN = 'A003'
        private constant integer NO_SPAWN = 'A001' 
        private constant integer DUMMY_RAW = 'h000'
        private constant real MIN_RANGE = 300.00
        private constant real MAX_RANGE = 600.00
    endglobals

    private struct Mystruct
        unit caster = GetTriggerUnit()
        timer repeator = CreateTimer()
        timer expirer = CreateTimer()
        trigger end = CreateTrigger()
        triggeraction endAction
        triggercondition endCondition
    endstruct
//==========================================================================
    private function endConds takes nothing returns boolean
        local Mystruct data = GetTriggerStructB(GetTriggeringTrigger())
        if GetTriggerEventId() == EVENT_GAME_TIMER_EXPIRED then
            return true
        else
            return GetTriggerUnit() == data.caster
        endif
    endfunction
//==========================================================================
    private function endActs takes nothing returns nothing
        //catches the trigger and runs this function
        local Mystruct data = GetTriggerStructB(GetTriggeringTrigger())
    
        //removes the created trigger in the Actions function
        call TriggerRemoveCondition(data.end,data.endCondition)
        call TriggerRemoveAction(data.end,data.endAction)
        call DestroyTrigger(data.end)
        call ClearTriggerStructB(data.end)
    
        //stops the repeator timer and ends the spell once and for all
        call PauseTimer(data.repeator)
        call DestroyTimer(data.repeator)
        call PauseTimer(data.expirer)
        call DestroyTimer(data.expirer)
        call ClearTimerStructA(data.repeator)
        call ClearTimerStructB(data.expirer)
    endfunction
//==========================================================================
    private function Effect takes nothing returns nothing
        //catches the expired timer and runs this function
        local Mystruct data = GetTimerStructA(GetExpiredTimer())
        
        //local variables 
        local integer infernal = GetRandomInt(0, 100)
        local unit array dummy[]
        
        //This uses polar projection. Formula: Center + r * trigfunction(angle teta); 
        //where Center is the center coordinate X or Y
        //r is the distance (in the case rabdom between 300 and 600)
        //trigfunction is Sin if using Y and Cos if using X
        //angle teta is the angle formed between r and the axis (in this case random, can be all circle)
        local real randomX =  GetUnitX(data.caster) + GetRandomReal(MIN_RANGE, MAX_RANGE) * Cos(GetRandomReal(0.0, 360.0) * bj_DEGTORAD)
        local real randomY =  GetUnitY(data.caster) + GetRandomReal(MIN_RANGE, MAX_RANGE) * Sin(GetRandomReal(0.0, 360.0) * bj_DEGTORAD)
        
        
        if (infernal <= 3 * GetUnitAbilityLevel(data.caster, AID) + 1) then
            set dummy[0] = CreateUnit(GetOwningPlayer(data.caster), DUMMY_RAW, GetUnitX(data.caster), GetUnitY(data.caster), 0)
            call UnitAddAbility(dummy[0], INFERNAL)
            call SetUnitAbilityLevel(dummy[0], INFERNAL, GetUnitAbilityLevel(data.caster, AID))
            
            call IssuePointOrder(dummy[0], "dreadlordinferno", randomX, randomY)
            
            call UnitApplyTimedLife(dummy[0], 'BTLF', 3)
            set dummy[1] = CreateUnit(GetOwningPlayer(data.caster), DUMMY_RAW, GetUnitX(data.caster), GetUnitY(data.caster), 0)
            call UnitAddAbility(dummy[1], SPAWN)
            call SetUnitAbilityLevel(dummy[1], SPAWN, GetUnitAbilityLevel(data.caster, AID))
            
            call IssuePointOrder(dummy[1], "flamestrike", randomX, randomY)
            
            call UnitApplyTimedLife(dummy[1], 'BTLF', 3)
        else 
            set dummy[0] = CreateUnit(GetOwningPlayer(data.caster), DUMMY_RAW, GetUnitX(data.caster), GetUnitY(data.caster), 0)
            call UnitAddAbility(dummy[0], NO_SPAWN)
            call SetUnitAbilityLevel(dummy[0], NO_SPAWN, GetUnitAbilityLevel(data.caster, AID))
            
            call IssuePointOrder(dummy[0], "flamestrike", randomX, randomY)
            
            call UnitApplyTimedLife(dummy[0], 'BTLF', 3)
        endif
        
        set dummy[0] = null
        set dummy[1] = null
    endfunction
//==========================================================================
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == AID
    endfunction
//==========================================================================
    private function Actions takes nothing returns nothing
        local Mystruct data = Mystruct.create()

        //starts the effect of the spell, meteors start falling from sky
        call SetTimerStructA(data.repeator, data)
        call TimerStart(data.repeator, 6 / GetUnitAbilityLevel(data.caster, AID), true, function Effect)
    
        //creates the timer that ends the spell when expired
        call SetTimerStructB(data.expirer, data)
        call TimerStart(data.expirer, 5 * GetUnitAbilityLevel(data.caster, AID) + 15, false, null)
    
        //creates the trigger that will end the spell when the timer expires or the hero dies
        call SetTriggerStructB(data.end, data)
        call TriggerRegisterTimerExpireEvent( data.end, data.expirer )
        call TriggerRegisterAnyUnitEventBJ(data.end, EVENT_PLAYER_UNIT_DEATH )
        set data.endCondition = TriggerAddCondition( data.end, Condition( function endConds ) )
        set data.endAction = TriggerAddAction( data.end, function endActs )
    endfunction
//===========================================================================
    private function InitTrig_Apocalypse takes nothing returns nothing
        set gg_trg_Apocalypse = CreateTrigger( )
        call TriggerRegisterAnyUnitEventBJ( gg_trg_Apocalypse, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( gg_trg_Apocalypse, Condition( function Conditions ) )
        call TriggerAddAction( gg_trg_Apocalypse, function Actions )
    endfunction
endscope
 
It still doesn't work =S

EDIT EDIT EDIT

It works after all, i just messed up in the name.
Thx BoneBreaker.
Btw, can you please explain me why that function has to be different ??
Btw, I tried to use
JASS:
public function InitTrig_Apocalypse takes nothing returns nothing
but it didn't work.. why ??

If I use
JASS:
public function InitTrig takes nothing returns nothing
in all my triggers, even with a scope, won't they conflict ??

Btw, rep+ for all added
 
Level 11
Joined
Feb 18, 2004
Messages
394
Flame_Phoenix, Vexorian's optimizer inlines constant globals and functions, as well as most one-line functions nowadays, to my knowledge. Thus, it does not make it any slower.
Actually, JASS Helper does the advanced inlining.

Because of the scope, InitTrig will actually become InitTrig_scopeName.
Don't use InitTrig functions. scopes can use initializers now, so use initializers instead.
 
Level 10
Joined
Jun 26, 2005
Messages
236
JASS:
scope Apocalypse initializer Init
    private function Init takes nothing returns nothing
    //Do normal InitTrig stuff here
    endfunction
endscope
 
Level 5
Joined
Jan 15, 2007
Messages
199
Flame... the constant function method is the JESP standard

Also, the way you do it with private globals makes it so you cant change it based on the level of the ability
 
Flame... the constant function method is the JESP standard

Also, the way you do it with private globals makes it so you cant change it based on the level of the ability

Not quite truth. I studied a code from Rising_Dusk, an now they have similar structure. If his code is said to be JESP, than mine is as well.

About changing things (timer) well, I actually can't find a better way, to do it like i did, I fear people will have to look at the function i say. Anyway, the code is commented and i tell them what to do, so that shouldn't really be a problem.
 
Status
Not open for further replies.
Top