• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Epicenter v2.1.0.0

This is a spell based on the Epicenter spell in DotA (lol Magtheridon96 you supernoob)

Since most of you haven't played DotA, this is what the spell does:

- The hero begins channeling for ~2 seconds
- When he's done channeling, the ground around him will be slammed and damaged every 0.xx seconds for a short period of time.

Pretty simple :)

It's written in vJASS (You know me guys :p)
Requirements:
- TimerTools by Nestharus
- SpellEffectEvent by Bribe

Here's the code:

JASS:
/***********************************************
*
*   Epicenter
*   v2.1.0.0
*   By Magtheridon96
*
*   - Requires:
*       - TimerTools By Nestharus
*           - hiveworkshop.com/forums/jass-resources-412/system-timer-tools-201165/
*       - SpellEffectEvent By Bribe
*           - hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193/
*
***********************************************/
library Epicenter requires Tt, SpellEffectEvent
    
    globals
        private constant integer ABIL_CODE = 'A000'
        private constant string EFFECT_PATH = "Epicenter.mdx"
        private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
        private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
        private constant boolean PRELOAD_EFFECT = true
    endglobals
    
    private function GetDamage takes integer level returns real
        return 90. + level * 60.
    endfunction
    
    private function GetTimeout takes integer level returns real
        return 0.4
    endfunction
    
    private function GetRadius takes integer level returns real
        return 450. + level * 50.
    endfunction
    
    private function GetWaves takes integer level returns integer
        return 6 + level * 2
    endfunction
    
    private function TargetFilter takes unit caster, player owner, unit target returns boolean
        return not IsUnitType(target, UNIT_TYPE_STRUCTURE) and not IsUnitType(target, UNIT_TYPE_DEAD) and IsUnitEnemy(target, owner)
    endfunction
    
    private struct Spell extends array
        
        private static unit array caster
        private static player array owner
        private static integer array waves
        private static real array radius
        private static real array damage
        
        implement CTM
            local real x
            local real y
            local unit u
        implement CTMExpire
            // Get the caster's coordinates.
            set x = GetUnitX(caster[this])
            set y = GetUnitY(caster[this])
            // Enumerate units in range of the caster.
            call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, radius[this], null)
            loop
                // Get the first unit in the group, check if he's null, and remove him.
                set u = FirstOfGroup(bj_lastCreatedGroup)
                exitwhen null == u
                call GroupRemoveUnit(bj_lastCreatedGroup, u)
                // Only damage wanted units.
                if TargetFilter(caster[this], owner[this], u) then
                    call UnitDamageTarget(caster[this], u, damage[this], false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
                endif
            endloop
            // Create and destroy special effect.
            call DestroyEffect(AddSpecialEffect(EFFECT_PATH, x, y))
            // Decrease number of waves left.
            set waves[this] = waves[this] - 1
            // If number of waves left is 0, we destroy the current instance.
            if waves[this] == 0 then
                call this.destroy()
                set caster[this] = null
                set owner[this] = null
            endif
        implement CTMEnd
        
        private static method run takes nothing returns nothing
            local integer level = GetUnitAbilityLevel(GetTriggerUnit(), ABIL_CODE)
            local thistype this = create(GetTimeout(level))
            // Get the caster and the triggering player.
            set caster[this] = GetTriggerUnit()
            set owner[this] = GetTriggerPlayer()
            // Cache the radius and damage to avoid repeating calculations.
            set radius[this] = GetRadius(level)
            set damage[this] = GetDamage(level)
            // Get the number of times the periodic function will run.
            set waves[this] = GetWaves(level)
        endmethod
        
        private static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(ABIL_CODE, function thistype.run)
            static if PRELOAD_EFFECT then
                call Preload(EFFECT_PATH)
            endif
        endmethod
    endstruct
    
endlibrary

Oh and sorry for the import ;)
It's just that I can't find any good in-game effects for this :/ (Other than A Command Aura spam suggested by maddeem :p)
The author of the special effect is unknown to me :p

Credits to IceFrog for the idea (Possibly Eul or Guinsoo (I don't know when Sandking was introduced to DotA))

Feel free to comment and rate.

Oh and the testmap is a must-see :p (I'm serious :D)

Keywords:
DotA, epicenter, Epicenter, Magtheridon96, sandking, hero, spell, wave, shockwave, tremur, earthquake, slam, Nestharus, Spellpack, shock, Sandking.
Contents

Just another Warcraft III map (Map)

Reviews
19th Oct 2011 Bribe: It doesn't even lag on my computer, which is saying a lot. Good work on this. Approved.

Moderator

M

Moderator

19th Oct 2011
Bribe: It doesn't even lag on my computer, which is saying a lot.
Good work on this.
Approved.
 
Level 7
Joined
Sep 2, 2011
Messages
349
Another DotA spell, however, I can't complain because it may be useful to some people. Personally, I like the old epicenter where the terrain dramatically shakes and dissolve.

But yeah.. Great Spell :D Good Job.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
One of my absolute favourite heroes in DOTA. And I know that it's "based" on it because it doesn't fully act like it, like the slow and growing aoe? Actually it's just a bunch of thunder claps in intervals with different aoe :p

-Skip the keyword, 8 lines of init module won't fuck up the readability at all and who cares about that the init stuff should be at the bottom really?

-Why don't you call NewTimer when you declare the timer? :O

-And I wonder when the effect of a channeling ability actually starts as if you start channeling and breaks it, it triggeres cooldown. But effect seems to work quite well.

-Never been a fan adding private prefixes to stuff within a private struct, nothing will reach it anyway :p (will probably save you a few bytes lol so it doesn't matter).

Good job on this one :)
 
Why don't you call NewTimer when you declare the timer? :O

Oh shit D:
will fix :p

Never been a fan adding private prefixes to stuff within a private struct, nothing will reach it anyway :p (will probably save you a few bytes lol so it doesn't matter)

Spamming the private keyword is one of my favorite things to do :ogre_hurrhurr:
I will remove them though ^_^

Skip the keyword, 8 lines of init module won't fuck up the readability at all and who cares about that the init stuff should be at the bottom really?

Ok :p
 
With the 0 == i instead of i == 0, have you actually tested this yourself or
are you following Nestharus blindly on this one. I recall he was testing for
something else and happened to notice that it "appeared" to run faster. We
need to post up the proper benchmarks and run some more appropriate
tests to find out of this is actually true. Currently everyone has just been
quoting him, as for readability it looks stupid.

Also what is wrong with putting a space after a comma or writing "i == 0"
instead of "i==0"? It is more readable to include the spaces, and Vex's
optimizer will remove extra spaces even with "compress names" unticked.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
With the 0 == i instead of i == 0, have you actually tested this yourself or
are you following Nestharus blindly on this one. I recall he was testing for
something else and happened to notice that it "appeared" to run faster. We
need to post up the proper benchmarks and run some more appropriate
tests to find out of this is actually true. Currently everyone has just been
quoting him, as for readability it looks stupid.

Also what is wrong with putting a space after a comma or writing "i == 0"
instead of "i==0"? It is more readable to include the spaces, and Vex's
optimizer will remove extra spaces even with "compress names" unticked.

I lol at this because it's so true and yeah 0==i looks really silly.

May DSG come fourth and lead us on our path! (One of the wisest I know of at THW)
 
He claimed 0==i is faster, and he said he did some benchmarks, so I decided: "Ok, what do I have to lose? Even if it's untrue, I wouldn't be losing any performance."
It's a Win-Win situation ;D

How about we do some benchmarks and see if it's true or not?

Here:

JASS:
library Test
    globals
        private constant boolean TEST_1 = true
        private integer i = 20000
    endglobals

    private function Loop takes nothing returns nothing
        static if TEST_1 then
            loop
                exitwhen 0==i
                set i=i-1
            endloop
        else
            loop
                exitwhen i==0
                set i=i-1
            endloop
        endif
        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"[Test]Thread executed without crashing.")
    endfunction

    private module Init
        private static method onInit takes nothing returns nothing
            call TimerStart(CreateTimer(),3,false,function L)
        endmethod
    endmodule
    private struct Inits extends array
        implement Init
    endstruct
endlibrary

I can't test it though :p
 
Level 10
Joined
May 27, 2009
Messages
495
well i'm looking for the slow part, perhaps the special thing in here is that take those slow parts as fast as possible, cause icefrog manage to do it without even dropping fps higher than .5 (except if there are things happening at the same time, creep spawns, kills, super skills, or you were pwned)
 
Level 10
Joined
Sep 19, 2011
Messages
527
I make some changes to the code:

JASS:
/***********************************************
*
*   Epicenter
*   v1.0.0.1
*   By Magtheridon96
*
*   - Requires TimerUtils
*
***********************************************/
library Epicenter requires TimerUtils
    globals
        private constant integer ABIL_CODE = 'A000'
        private constant string EFFECT_PATH = "Epicenter.mdx"
        private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
        private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
        private group G = CreateGroup()
    endglobals
    
    private function GetDamage takes integer level returns real
        return 90. + level * 60.
    endfunction
    
    private function GetTimeout takes integer level returns real
        return 0.4
    endfunction
    
    private function GetRadius takes integer level returns real
        return 450. + level * 50.
    endfunction
    
    private function GetWaves takes integer level returns integer
        return 6 + level * 2
    endfunction
    
    private module Init
        private 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.run))
            set t = null
        endmethod
    endmodule
    
    private struct Spell extends array
        private static integer ic = 0
        private static integer ir = 0
        private static thistype td = 0
        thistype rn
        unit caster
        player owner
        integer level
        integer counter
        real radius
        real damage
        real animation
        boolean resetAnimation
        real x
        real y
        boolean passTime
        
        private static method filter takes nothing returns boolean
            local unit u = GetFilterUnit()
            if not IsUnitType(u,UNIT_TYPE_STRUCTURE) and not IsUnitType(u,UNIT_TYPE_DEAD) and IsUnitEnemy(u,td.owner) then
                call UnitDamageTarget(td.caster,u,td.damage,false,false,ATTACK_TYPE,DAMAGE_TYPE,null)
            endif
            set u = null
            return false
        endmethod
        private static method periodic takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local real x = GetUnitX(.caster)
            local real y = GetUnitY(.caster)
            set .animation = .animation + GetTimeout(.level)
            set td=this
            
            if (.x == x and .y == y) or passTime then
                if animation > 2. and resetAnimation then
                    set .passTime = true
                    set .resetAnimation = false
                    call SetUnitAnimation(.caster, "stand")
                endif
                
                if animation > 2. then
                    call GroupEnumUnitsInRange(G,x,y,.radius,Condition(function thistype.filter))
                    call DestroyEffect(AddSpecialEffect(EFFECT_PATH,x,y))
                    set counter=counter-1
                    if 0==counter then
                        call ReleaseTimer(GetExpiredTimer())
                        set .passTime = false
                        set .caster = null
                        set .owner = null
                        set .animation = 0.
                        set .resetAnimation = true
                        set .rn=ir
                        set ir=this
                    endif
                endif
            else
                call ReleaseTimer(GetExpiredTimer())
                set .passTime = false
                set .counter = 0
                set .caster = null
                set .owner = null
                set .animation = 0.
                set .resetAnimation = true
                set .rn=ir
                set ir=this
            endif
        endmethod
        private static method create takes nothing returns thistype
            local thistype this
            local timer t = NewTimer()
            if 0==ir then
                set ic=ic+1
                set this=ic
            else
                set this=ir
                set ir=.rn
            endif
            set .caster = GetTriggerUnit()
            set .owner = GetTriggerPlayer()
            set .level = GetUnitAbilityLevel(.caster,ABIL_CODE)
            set .radius = GetRadius(.level)
            set .damage = GetDamage(.level)
            set .counter = GetWaves(.level)
            set .animation = 0.
            set .resetAnimation = true
            set .x = GetUnitX(.caster)
            set .y = GetUnitY(.caster)
            set .passTime = false
            call SetTimerData(t,this)
            call TimerStart(t,GetTimeout(.level),true,function thistype.periodic)
            set t = null
            return this
        endmethod
        private static method run takes nothing returns boolean
            if GetSpellAbilityId()==ABIL_CODE then
                call thistype.create()
            endif
            return false
        endmethod
        implement Init
    endstruct
endlibrary

Great job ^^.

Greetings
 
Level 7
Joined
Sep 2, 2011
Messages
349
Woah. How about making a Sand King spellpack. For me, almost all of his skills are pretty cool and may give a big help and contribution to other people.
 
I don't really do changelogs, but just for you:

v1.0.0.0
Released

v1.0.0.1
Nulled a variable

v2.0.0.0
Made this a lot faster
Now requires SpellEffectEvent by Bribe
Now runs on 2 seperate timers to avoid lots of comparisons and shit.

edit
Holy Shit!
JASS:
integer level
integer counter
These are useless. Magtheridon96, stop being such a noob and uploading shitty resources that no one cares about. Now gtfo!

edit
Take that Magtheridon96! I fixed it.
You are such a loser..

edit
Said the guy who didn't update his Multiboard System yet >_>
HAHAHAHAHAHAHAH Nub.
 
Last edited:
Level 13
Joined
May 11, 2008
Messages
1,198
it looks pretty good and is not very hard to import...however i'm skeptical about the usefulness of those systems you wrote...
i had to edit out the static if near the bottom, and replaced the top line with library Epicenter requires TimerUtils, GT to make it work. not so difficult, as i said.
"SanKakU, If you can convince Nestharus to create a Testmap for his 'Bonus' system, I WILL add the slow effect and any other effect you want :3"
so basically it seems those systems you wrote are apparently incomplete at the moment. that feels a little sad, you looked like you were on a roll, here.
 
GT is just another example of overachieving, trying to add many features "for the sake of completion" when really the only things RegisterPlayerUnitEvent can't do is the "byId" functions. For order events you can use OrderEvent, and for Spell events you could use SpellEffectEvent and/or SpellStruct by Nestharus. For the other "byId" functions -- they are just silly overhead.
 
Level 13
Joined
May 11, 2008
Messages
1,198
It's not meant to use GT now is it? GT may have many functions but it has sadly been made too bad.

So what? Just add the dummy casting thunderclap with changing the dummy ability's level each interval? You just changed event system, shouldn't be difficult for ya.

well, i hadn't thought about that yet. that might be ok.

also i'm not sure what you mean by it's not meant to use GT...afaik the spell is currently incomplete and i don't even understand why it uses the system it does about using effects...but meh....i don't know how the new system works so what do i know... i'll try to modify the spell to see if it can use the intended system and yet also compile properly and work, later, perhaps. right now i'm dealing with other stuff, so i can't be bothered atm.
GT is just another example of overachieving, trying to add many features "for the sake of completion" when really the only things RegisterPlayerUnitEvent can't do is the "byId" functions. For order events you can use OrderEvent, and for Spell events you could use SpellEffectEvent and/or SpellStruct by Nestharus. For the other "byId" functions -- they are just silly overhead.

lol...well anyway i don't know about orderevent, and i don't use stuff by nestharus, as his stuff requires some kind of nestharus mod to world editor that i cannot comprehend....i might take a look at some of those things later, but for now...all is relatively well.

step 1: unregister event
step 2: ???????????????
step 3: no profit????

i could see it being useful in some sort of map that has to kiss certain gameplay stuff goodbye when you reach a certain point...probably great for a rpg.
 
At the end of the day no one is going to stop you from using GTrigger or whatever it is you want to use in its place, the thing is that GTrigger is not the best way to do it and GTrigger is not going to win any speed benchmarks.

Having a feature to turn off a "playerunitevent" trigger is pretty much 99% useless. And for the 1% of the time it's not worth it to create a system just to be able to do that.
 
Top