1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. The long-awaited results for Concept Art Contest #11 have finally been released!
    Dismiss Notice
  3. Join Texturing Contest #30 now in a legendary battle of mythological creatures!
    Dismiss Notice
  4. The 20th iteration of the Terraining Contest is upon us! Join and create exquisite Water Structures for it.
    Dismiss Notice
  5. Hivers united and created a bunch of 2v2 melee maps. Vote for the best in our Melee Mapping Contest #4 - Poll!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice

Terror Rising v1.5

Submitted by Malhorne
This bundle is marked as approved. It works and satisfies the submission rules.
Hi everybody,

Today I upload a spell that uses my FearSystem.
This spell is Terror Rising (thanks to kakuzu for the name !).

The description of the spell : The caster create a electricity bolt that travels 425/600/775 units and every 175 units the bolt launch a explosion that deal 25/50/75 damages to enemy units and fear those units during 1.2/1.4/1.6 second.

The fear and the damage stacks if the unit take many explosion of the bolt meaning that a unit taking two explosions will be feared during (1.2-0.5)+1.2 = 1.9 second and will take 25+25 = 50 damages for the level 1 of the spell.
Note : There is a -0.5 because there is an explosion every 0.5 second.

This system requires the FearSystem, it is not optional.

Here is the code of the spell :
Code (vJASS):
library TerrorRising requires FearSystem/*By me*/, BoundSentinel //By Vex
/*
FearSystem : http://www.hiveworkshop.com/forums/spells-569/fear-system-v-2-7-a-243755/
BoundSentinel : http://www.wc3c.net/showthread.php?t=102576
*/

//CONFIGURATION
    globals
        private constant real FPS = 0.0312500
        //The distance between two explosions.
        private constant real BETWEEN_UNITS = 175.
        //The time between two explosions.
        private constant real TIME_BETWEEN = 0.5
        //It means that the speed of the orb is BETWEEN_UNITS/TIME_BETWEEN
        private constant integer SPELL_ID = 'U000'
        private constant integer DUMMY_ID = 'd000'
        private constant real AOE = 175.
        private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
        private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
        //The path of the special effect attached to fear status
        private constant string FEAR_PATH = "Abilities\\Spells\\Undead\\Curse\\CurseTarget.mdl"
        private constant string FEAR_ATTACH = "overhead"
        //The path of the effect that will spawn on the units when it takes damage
        private constant string UNIT_PATH = "Abilities\\Spells\\Human\\Feedback\\ArcaneTowerAttack.mdl"
        private constant string UNIT_ATTACH = "origin"
        private constant string EXPLOSION_PATH = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
    endglobals
   
    private constant function Damage takes integer level returns real
        return 25.*level
    endfunction
   
    private constant function Explosion_Number takes integer level returns integer
        return 2+level
    endfunction
   
    private constant function Time_Fear takes integer level returns real
        return 1. + 0.2*level
    endfunction
   
    private function Unit_Filter takes player source, unit targ returns boolean
        return (not IsUnitType(targ, UNIT_TYPE_MAGIC_IMMUNE)) and (UnitAlive(targ)) and IsUnitEnemy(targ, source)
    endfunction
//END CONFIGURATION -> DONUT TOUCH ANYTHING BELOW

    private struct Explosion extends array
        unit u
        unit caster
        player owner
        integer steps
        real temp
        real fear
        real X
        real Y
        real dmg
        thistype prev
        thistype next
        static integer count
        static timer period
        static group g
       
        method destroy takes nothing returns nothing
            if this.next != 0 then
                set this.next.prev = this.prev
            endif
            set this.prev.next = this.next
            set this.prev = thistype(0).prev
            set thistype(0).prev = this
            if thistype(0).next == 0 then
                call PauseTimer(period)
            endif
            call UnitApplyTimedLife(u, 'BTLF', 0.01)
            set this.u = null
            set this.caster = null
            set this.owner = null
        endmethod
       
        static method periodic takes nothing returns nothing
            local Fear F
            local thistype this = thistype(0).next
            local unit v
            local real x
            local real y
            loop
                exitwhen this == 0
                set this.temp = this.temp - FPS
                set x = GetUnitX(this.u) + this.X
                set y = GetUnitY(this.u) + this.Y
                call SetUnitX(this.u, x)
                call SetUnitY(this.u, y)
                if this.temp <= 0 then
                    call DestroyEffect( AddSpecialEffect(EXPLOSION_PATH, x, y) )
                    call GroupEnumUnitsInRange(g, x, y, AOE, null)
                    loop
                        set v = FirstOfGroup(g)
                        exitwhen v==null
                        call GroupRemoveUnit(g, v)
                        if Unit_Filter(this.owner, v) then
                            call DestroyEffect( AddSpecialEffectTarget(UNIT_PATH,v,UNIT_ATTACH) )
                            if Fear.isFeared(v) then
                                set F = Fear.get(v)
                                set F.time = this.fear + F.time
                            else
                                set F = Fear.create()
                                set F.targ = v
                                set F.path = FEAR_PATH
                                set F.attach = FEAR_ATTACH
                                set F.time = this.fear
                                call F.start()
                                call F.destroy()
                            endif
                            call UnitDamageTarget(this.caster, v, this.dmg, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
                        endif
                    endloop
                    set this.steps = this.steps - 1
                    set this.temp = TIME_BETWEEN
                endif
                if this.steps == 0 then
                    call this.destroy()
                endif
                set this = this.next
            endloop
        endmethod
       
        static method cond takes nothing returns boolean
            local thistype this
            local real angle
            local real tx
            local real ty
            local integer i
            if GetSpellAbilityId() == SPELL_ID then
                //Allocate
                if thistype(0).prev == 0 then
                    set count = count + 1
                    set this = count
                else
                    set this = thistype(0).prev
                    set thistype(0).prev = thistype(0).prev.prev
                endif
                if thistype(0).next == 0 then
                    call TimerStart(period, FPS, true, function thistype.periodic)
                else
                    set thistype(0).next.prev = this
                endif
                set this.next = thistype(0).next
                set thistype(0).next = this
                set this.prev = thistype(0)
                //End Allocate
                set this.caster = GetTriggerUnit()
                set i = GetUnitAbilityLevel(this.caster,SPELL_ID)
                set tx = GetSpellTargetX()
                set ty = GetSpellTargetY()
                set angle = Atan2(ty-GetUnitY(this.caster), tx-GetUnitX(this.caster))
                set this.owner = GetTriggerPlayer()
                set this.u = CreateUnit(this.owner, DUMMY_ID, tx, ty, angle*bj_RADTODEG)
                set this.steps = Explosion_Number(i)
                set this.temp = TIME_BETWEEN
                set this.fear = Time_Fear(i)
                set this.X = BETWEEN_UNITS*Cos(angle)*FPS
                set this.Y = BETWEEN_UNITS*Sin(angle)*FPS
                set this.dmg = Damage(i)
            endif
            return false
        endmethod
       
        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.cond))
            call Preload(FEAR_PATH)
            call Preload(EXPLOSION_PATH)
            call Preload(UNIT_PATH)
            set period = CreateTimer()
            set count = 0
            set g = CreateGroup()
            set t = null
        endmethod
    endstruct
endlibrary


How to import :
- You will first need JNGP.
- Copy the three library inside the folder Requirements.
Note : You can use Table by Bribe or no Table at all too (even if I recommend to use one).
Actually I have trouble with Table by Bribe just use Table by Vex and nothing more if you can at the moment.
- Copy the trigger called TerrorRising
- Copy the abilities linked to the FearSystem called 'BEAR FORM FEAR' and 'MORPH FEAR' and 'DISABLE_ATTACK'. Then change the id inside the code of the FearSystem.
- Copy the dummy unit called 'DUMMY_EXPLOSION' and change the id inside the spell.
- Copy the ability FearExplosion. Then change the id inside the code of the spell.
- You're ready to go :)

Credits to :
- Vexorian -> Table, JassHelper, BoundSentinels
- Bribe -> Table
- Maker -> Help in FearSystem
- Chobibo -> Help in FearSystem
- Kakuzu -> Help for the name :3

Give me credits and to the other if you use ;)

Changelog
v1.0 :
- Initial Release

v1.1 :
- Implemented BoundSentinels
- Struct members optimized.

v1.2 :
- Trigger optimization.

v1.3 :
- Added the Unit_Filter function
- Trigger optimization.

v1.4 :
- I don't remember what I've done sorry ;)

v1.5 :
- A good name finally !


Keywords:
Explosion, vJASS, JESP, Fear, Terror, Rising, Terror Rising, Malhorne
Contents

Just another Warcraft III map (Map)

Reviews
Moderator
20:49, 3rd Jan 2014 BPower: Changes made, approved. One minor thing: The dummy unit still uses upgrades. (old)review: http://www.hiveworkshop.com/forums/spells-569/fearexplosion-v1-3-a-244427/index4.html#post2466426
  1. 20:49, 3rd Jan 2014
    BPower: Changes made, approved.

    One minor thing: The dummy unit still uses upgrades.

    (old)review: https://www.hiveworkshop.com/posts/2466426/
     
  2. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,336
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    Reserved~
     
  3. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,745
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    set angle = Atan2(ty-GetUnitY(this.caster), tx-GetUnitX(this.caster))
    instead of
    set x = GetUnitX(this.caster)
    and
    set y = GetUnitY(this.caster)
    since x and y are only used once.

    Add some explanation in the global settings block, to make it more user friendly.
    Especially for those two: private constant real BETWEEN_UNITS and private constant real TIME_BETWEEN

    call SetUnitPosition(this.u, x + this.X, y + this.Y)
    could be SetUnitX/Y, but then you have to use something like WorldBounds

    set p = GetOwningPlayer(this.caster)
    could be done in your cond method. -->
    this.owner = GetTriggerPlayer()

    Which also allows you to
    set this.u = CreateUnit(GetOwningPlayer(this.caster),...
    -->
    set this.u = CreateUnit(this.owner, ...


    GetWidgetLife(v)>0.405
    -->
    not (IsUnitType(v, UNIT_TYPE_DEAD))
    or native UnitAlive

    null
    this.caster
    in the end

    not IsUnitAlly(v, p)
    -->
    IsUnitEnemy(v, p)
    :) I don't know if here is a difference, it just looks better.

    Optional:
    • You could use xe, Dummy or Missile for Dummy recycling. (Probably you won't have to call CreateUnit more than 10 times for this spell with one of those)
    • SpellEffectEvent
    • WorldBounds
    • CTL (You did a good job with your timer so w/e)

    After all these 3 are pretty standart resources and having them does hurt noone.
     
    Last edited: Nov 18, 2013
  4. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    Code (vJASS):

                call Preload(FEAR_PATH)
                call Preload(EXPLOSION_PATH)
                call Preload(UNIT_PATH)
     


    lol wut? :D

    Code (vJASS):

        private struct Explosion extends array
            unit u
            unit caster
            integer steps
            real temp
            real fear
            real X
            real Y
            real dmg
            thistype prev
            thistype next
            static integer count
            static timer period
            static group g
     


    into

    Code (vJASS):

        private struct Explosion extends array
            private unit u
            private unit caster
            private integer steps
            private real temp
            private real fear
            private real X
            private real Y
            private real dmg
            private thistype prev
            private thistype next
            private static integer count
            private static timer period
            private static group g
     


    Code (vJASS):

                    call SetUnitPosition(this.u, x + this.X, y + this.Y)
     


    into

    Code (vJASS):

                    call SetUnitX(this.u, x + this.X)
                    call SetUnitY(this.u, y + this.Y)
     


    you are creating
    TriggerExecute
    , place destroy method above create
     
  5. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,745
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    Edo the struct is private. There is no need for a private keyword. It makes absolutely no difference.
     
  6. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    well, that is correct, I somehow forgot that fact
     
  7. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,336
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    It is a missile so SetUnitX/Y instead of SetUnitPosition is negligible.

    For the this.owner I'll do this too.

    The preload are for effects.
    I might add some features since the spell is a bit simple :)
     
  8. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    SetUnitX is always superior, and moreso for missiles, you dont need to stop orders or check pathing, you just need to move the missile
    also calling SetUnitPosition has potential to lagg after several casts because of how it works
     
  9. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,336
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    Yes but it needs WorldBounds and I like to put resources with not many things needed :/
    I'll see this after...
     
  10. chobibo

    chobibo

    Joined:
    Sep 24, 2005
    Messages:
    2,692
    Resources:
    0
    Resources:
    0
    Above the periodic method.

    @Malhorne
    Try queuing the death animation instead of creating multiple dummies to play the animation.
     
  11. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,578
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Code (vJASS):

    static if LIBRARY.WorldBounds then
     


    This is contradicting. The fact it's a missile is exactly why you should use SetUnitX/Y, SetUnitPosition is SO SLOW (compared to setunitx).
     
  12. chobibo

    chobibo

    Joined:
    Sep 24, 2005
    Messages:
    2,692
    Resources:
    0
    Resources:
    0
    I think he meant dependencies on external libraries, but either way, Bounds Sentinel ain't so bad to import right?
     
  13. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,578
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Exactly?
    optional Libname
    with static if's can get around that, if he really cared.
     
  14. chobibo

    chobibo

    Joined:
    Sep 24, 2005
    Messages:
    2,692
    Resources:
    0
    Resources:
    0
    Right, but I'd rather use Bounds Sentinel to save a few lines haha :D
     
  15. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,336
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    I create only one dummy.

    It really create a triggerexecute if I put the destroy method after ?


    Anyway I'll add the WorldBounds.
    Someone has a link ?
     
  16. chobibo

    chobibo

    Joined:
    Sep 24, 2005
    Messages:
    2,692
    Resources:
    0
    Resources:
    0
  17. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,578
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Yeah.

    Anyway here are some things I noticed.
    • GetOwningPlayer(this.caster)
      could be stored in the struct.
    • GetWidgetLife(v)>0.405
      could be replaced with UnitAlive. Make sure to declare
      native UnitAlive takes unit id returns boolean
      in your script somewhere or else you will get errors. The reasons are minor (small performance, readability) to implement this, but I think it's worth it.
    • I really think you should be using a timer system, like TimerUtils if you plan on using dynamic timers instead of one. If you insist on using one timer, I know there are some single timer loops around. The reason being that people generally want one of two things in their maps when it comes to utilizing timers. One, timer recycling (dynamic timers), or two a single timer for their map. Your spell currently fits into neither :( Not a big deal, but something to consider.
     
  18. Malhorne

    Malhorne

    Joined:
    Sep 14, 2012
    Messages:
    2,336
    Resources:
    6
    Spells:
    4
    Tutorials:
    1
    JASS:
    1
    Resources:
    6
    Updated
    Changelog
    V1.0 :
    - Initial Release

    V1.1 :
    - Implemented BoundSentinels
    - Struct members optimized.
     
  19. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,745
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    BoundsSentinel different to WorldBounds. While WorldBounds only gives information about map bounds, BoundsSentinel moves units back in if they leave those.
    If you plan to use one timer per instance, I wouldn't use TimerUtils since your timer timeout is 0.03125000, therefore I would go with CTL.
    It's not recommended to change this timeout to higher values anyways for instance 0.1, because it would look stupid.

    Edited
     
    Last edited: Nov 18, 2013