Spell Obelisk of Energy

Ghan

Administrator - Servers are fun
Staff member
Reaction score
889
Well, we're back! Or, at least, I am. :p
With another spell for you! Wow. Two in less than a week. Ghan has gone completely mad. I know. It's sad.

And, the best part is that it's the most complicated JASS spell I've ever made.
Another scope, a library, structs, handlevars, globals, and methods are all included in this free-for-all spell. :D


JASS - vJASS (NewGen Required)
MUI - To infinity, and beyond!
Works - Energy spraying everywhere... check!
Fun - You will want this spell.
Leaks - If you look really hard right there... oh, nope, that was just some dust on the monitor.
Efficiency - See that? One location that is changed into real at the beginning, I think I count two BJ functions. It won't crash your computer, let's put it that way.


You've got questions. I've got implementation!

Obelisk of Energy

This spell creates a temporary defensive tower at the target location that periodically fires off energy bolts at nearby enemies. The tower lasts until it is destroyed or runs out of shots.

Obelisk2.jpg


Obelisk1.jpg


Here is the (much longer than last time) code for your viewing pleasure:

JASS:
library HandleVars
// ===========================
function H2I takes handle h returns integer
    return h
    return 0
endfunction

// ===========================
function LocalVars takes nothing returns gamecache
    // Replace InitGameCache("jasslocalvars.w3v") with a global variable!!
    return InitGameCache("jasslocalvars.w3v")
endfunction

function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction

function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction

endlibrary



scope ObeliskofEnergy
    
    globals
        private constant integer Shots = 20 // This is the number of bolts per level the Obelisk will have.
        private constant real Delay = 4.00 // This is the time between casts of the Obelisk's ability. It decreases by one second per level of the main spell.
        private constant integer UnitCode = 'h000' // This is the code of the Obelisk itself.
        private constant integer AbilityCode = 'A000' // This is the code of the Obelisk of Energy ability.
        private constant integer AttackCode = 'A001' // This is the code of the ability the Obelisk uses to attack.
        private unit SomeUnit
    endglobals
    
    struct Info
        unit Obelisk
        unit Caster
        real X
        real Y
        
        method onDestroy takes nothing returns nothing
            set this.Obelisk = null
            set this.Caster = null
        endmethod
    endstruct
    
    private function UnitFilter takes nothing returns boolean
        local Info data = GetUnitUserData(SomeUnit)
        return IsPlayerEnemy(GetOwningPlayer(GetFilterUnit()), GetOwningPlayer(data.Caster)) and GetWidgetLife(GetFilterUnit()) > .305
    endfunction
    
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == AbilityCode
    endfunction
    
    private function Attack takes nothing returns nothing
        local Info data = GetHandleInt(GetExpiredTimer(), "struct")
        local group tempgroup = CreateGroup()
        local unit tempunit
        call UnitAddAbility(data.Obelisk, AttackCode)
        set SomeUnit = data.Caster
        call SetUnitUserData(SomeUnit, data)
        call GroupEnumUnitsInRange(tempgroup, data.X, data.Y, 800.00, Condition(function UnitFilter))
        set SomeUnit = null
        if(FirstOfGroup(tempgroup) != null) then
            call SetUnitUserData(data.Obelisk, GetUnitUserData(data.Obelisk) - 1)
            call SetUnitState(data.Obelisk, UNIT_STATE_MANA, GetUnitState(data.Obelisk, UNIT_STATE_MANA) - 1)
        endif
        set tempunit = GroupPickRandomUnit(tempgroup)
        call IssueTargetOrder(data.Obelisk, "chainlightning", tempunit)
        call UnitRemoveAbility(data.Obelisk, AttackCode)
        set tempunit = null
        call DestroyGroup(tempgroup)
        set tempgroup = null
        if(GetUnitUserData(data.Obelisk) <= 0 or GetWidgetLife(data.Obelisk) < .305) then
            call PauseTimer(GetExpiredTimer())
            call DestroyTimer(GetExpiredTimer())
            call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl", data.X, data.Y))
            call RemoveUnit(data.Obelisk)
            call data.destroy()
            return
        endif
    endfunction
    
    private function Trig_ObeliskofEnergy_Actions takes nothing returns nothing
        local Info data = Info.create()
        local location target = GetSpellTargetLoc()
        local timer Time = CreateTimer()
        set data.X = GetLocationX(target)
        set data.Y = GetLocationY(target)
        set data.Caster = GetTriggerUnit()
        call RemoveLocation(target)
        set target = null
        call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl", data.X, data.Y))
        set data.Obelisk = CreateUnit(GetOwningPlayer(GetTriggerUnit()), UnitCode, data.X, data.Y, 0.00)
        call SetUnitState(data.Obelisk, UNIT_STATE_MANA, Shots * GetUnitAbilityLevel(GetTriggerUnit(), AbilityCode))
        call SetUnitUserData(data.Obelisk, Shots * GetUnitAbilityLevel(GetTriggerUnit(), AbilityCode))
        call SetHandleInt(Time, "struct", data)
        call TimerStart(Time, Delay - GetUnitAbilityLevel(GetTriggerUnit(), AbilityCode), true, function Attack)
        set Time = null
    endfunction

//===========================================================================
    public function InitTrig takes nothing returns nothing
        local trigger ObeliskofEnergy = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(ObeliskofEnergy, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(ObeliskofEnergy, Condition(function Conditions))
        call TriggerAddAction(ObeliskofEnergy, function Trig_ObeliskofEnergy_Actions )
    endfunction

endscope
 

Attachments

  • Spell - Obelisk of Energy.w3x
    47.9 KB · Views: 294

Joker(Div)

Always Here..
Reaction score
86
Couldn't you make this without any triggers? It's just a summon that attacks after all. Also, HandleVars is so outdated and bugged. You should try ABC/TT/ABCT. They do just about the same thing, just better.
 

Ghan

Administrator - Servers are fun
Staff member
Reaction score
889
> It's just a summon that attacks after all.

I'm not sure, but I doubt it. It uses a spell and attacks at regular intervals determined by a timer. Also, it doesn't have an expiration timer, the point at which it disappears is determined by the number of shots it makes.

> You should try ABC/TT/ABCT.

I really haven't looked at those systems before. I don't know how they work. Maybe in the future, but for the purposes of this, HandleVars seems to work fine. It hasn't given me any problems.
 

Joker(Div)

Always Here..
Reaction score
86
HandleVars will fail at times in maps. Have you heard of AotZ? It's a nice AoS map that is filled with HandleVars. About 1 of 6 games, there are many spells/triggers that fail because of it.

You should really look into the systems that I have listed in my earlier post. They are seriously life savers. :)
 

Cohadar

master of fugue
Reaction score
209
HandleVars will fail at times in maps. Have you heard of AotZ? It's a nice AoS map that is filled with HandleVars. About 1 of 6 games, there are many spells/triggers that fail because of it.

Where did you hear this?
AotZ was made by Rising_Dusk, one of greatest wc3 mappers of all times,
I really find it hard to believe that it has bugs. (handlevars or not)
 

PurgeandFire

zxcvmkgdfg
Reaction score
508
Looks nice. Here is my comment:

Well, first off, the coding is nicely organized and is neat. :)

I suggest removing all unused handle var functions, just to save space. Apparently, you only use Set and GetHandleInt, so:
JASS:
library HandleVars
// ===========================
function H2I takes handle h returns integer
    return h
    return 0
endfunction

// ===========================
function LocalVars takes nothing returns gamecache
    // Replace InitGameCache("jasslocalvars.w3v") with a global variable!!
    return InitGameCache("jasslocalvars.w3v")
endfunction

function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction

function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction

endlibrary


So that should be your library.

To unconfuse this line:
JASS:
if FirstOfGroup(tempgroup) != null then


:p Using not isn't bad, but it is just more confusing. xD

I just reviewed your code and couldn't find anything wrong, great job! :D

HandleVars will fail at times in maps. Have you heard of AotZ? It's a nice AoS map that is filled with HandleVars. About 1 of 6 games, there are many spells/triggers that fail because of it.

HandleVars may be outdated, but are completely usable. They are actually just rusty and unpreferred because they are slow due to the gamecache.

Oddly, handleVars are rusty for handles, yet are fine to use for integers, reals, boolean, and strings. Well, at least faster than all others because the other ones have to call a BJ which calls a BJ + the gamecache. The non-handles just practically save one BJ. :)

As far as I know and what I remember...
 

Joker(Div)

Always Here..
Reaction score
86
Where did you hear this?
AotZ was made by Rising_Dusk, one of greatest wc3 mappers of all times,
I really find it hard to believe that it has bugs. (handlevars or not)

Sry for going off topic so much Ghan...

AotZ was his first? (not sure) or one of his first maps. He started it a really long time ago when Handlevars was popular and probably, he's got many triggers created from his early days. I think that was one of the reasons why Dusk started a new map (DoE) which seems to me, an improved version of AotZ. If you've played enough AotZ, you'll find, at times, ridiculous bugs (perma stun, perma invuln, perma pause, etc.).
 

Ghan

Administrator - Servers are fun
Staff member
Reaction score
889
> I suggest removing all unused handle var functions, just to save space.

Ah. Ok. I thought it did seem like there was a lot there. :D

> To unconfuse this line:

For some reason, I thought you couldn't have != there....

> I just reviewed your code and couldn't find anything wrong, great job!

You do realize this is ME we're talking about? :p

I implemented your suggestions. Thanks!
 

Sim

Forum Administrator
Staff member
Reaction score
534
> HandleVars will fail at times in maps.

There is some kind of gradation that started about 2 years ago concerning handle vars. Unsurprisingly though.

"Handle Vars rock and the return bug is teh Sh*t!!!!"
(time passes)
"Handle Vars work good, but only because of the return bug hehe."
(time passes)
"Handle Vars are quite slow... but hey, they do the job!"
(time pases)
"structs > handle vars. Slow, extra function calls..."
(time passes)
"all > handle vars"
(time passes)
"Handle Vars blow."

Though it's true that there are many things Handle Vars don't do right, it's still not that bad actually... my old spells using them work perfectly. They're just less efficient, as we all know.

> I really find it hard to believe that it has bugs. (handlevars or not)

Well, a non-melee map with no bug is hardly a map. It is something "superior" that is also perfect, and, sadly, impossible. :p

--------------------------------------------------------
// Back on topic!
--------------------------------------------------------

You should add a constant function or something that enables modifications of the timer's interval. Maybe one would not want it to be always "4 - level"! Without mentioning the fact that if the spell's level go above 4, something nasty could happen...

Surprisingly enough, my obelisk got hexed by some creep.
You shouldn't have unchecked "Is a building" inside the Object Editor. :p

> local timer Time = CreateTimer()

You never null it.

The spell should have a hotkey assigned to it.

Excellent spell!
 

Ghan

Administrator - Servers are fun
Staff member
Reaction score
889
> You should add a constant function or something that enables modifications of the timer's interval. Maybe one would not want it to be always "4 - level"! Without mentioning the fact that if the spell's level go above 4, something nasty could happen...

The thought had occurred to me.... :p Done.

> Surprisingly enough, my obelisk got hexed by some creep.
You shouldn't have unchecked "Is a building" inside the Object Editor.

Believe me, I didn't want to. The problem was that if I left it a structure, every creep in the world woke up and ran to attack the thing. I didn't like that.
Anyway, it now has Spell Immunity.

> You never null it.

Thanks for the catch! Fixed.

> The spell should have a hotkey assigned to it.

Done.
 

Romek

Super Moderator
Reaction score
964
Thanks, GY'd.

I'd like to note that these spells can, and will be approved again once fixed. :)
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      • Ghan
        Administrator - Servers are fun

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top