Spell Spike Trap

_whelp

New Member
Reaction score
54
GRAVEYARD THIS.

This is actually a remake of some old spell I made, but the thread is old so I made a new one.

MUI/MPI - MUI
GUI/Jass/vJass - vJass

Requirements
Latest Jasshelper/JNGP
Timer32

The caster disappears from sight, dropping a trap on the ground. Anything going to the trap will be struck by the caster.

SpikeTrapPreview.jpg

The picture doesn't really show the spell much, it just looks like the dude is using wind walk or something.

JASS:
//    Spike Trap
//x----------------x
//| How to import:  \
//x-x----------------x----------------------------------------------------------------------------------------x                                                                                             |
//x-|1. Copy the spell and T32 to your map.                                                                   |
//x-|2. Create a new trigger with any name, click Edit-->Convert to Custom Text, then paste the spell's code. |
//x-|3. Do the same as the second step, but with T32.                                                         |
//x-x---------------------------------------------------------------------------------------------------------x
//REQUIRES A VJASS COMPILER OR JNGP
scope SpikeTrap
    globals   
        //Configuration
        
        private constant integer ABILITY_ID = 'A000'
        //The raw code of the spell, use Ctrl+D in the object editor to find it
    
        private constant string FINISH_ANIMATION = "attack walk stand"
        //The animation used after the trap is triggered
    
        private constant real ANIMATION_TIME = 0.5
        //How long the animation lasts
    
        private constant string CASTER_EFFECT = "Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl"
        //The model of the effect created on the caster
    
        private constant string TRAP_EFFECT = "Abilities\\Spells\\Orc\\SpikeBarrier\\SpikeBarrier.mdl"
        //The model of the trap 
    
        private constant string TRIGGER_EFFECT = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl"
        //The effect emitted when the trap is triggered
    
        private constant real RADIUS = 150.
        //How far a unit has to be from the center of a trap to be affected
    
        private constant real SIGHT_RADIUS = 500.
        //How far the player has visibility
    
        private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS
        //The attack type
    
        private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
        //The damage type
    
        private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
        //The weapon type
    
    endglobals
    
    private function MAX_TARGETS takes integer level returns integer
        return level * 5 - 1
    endfunction
    //Maximum targets
    
    private function DAMAGE takes integer level returns real
        return I2R(level) * 50 + 25
    endfunction
    //The damage
    
    private function TRAP_DURATION takes integer level returns real
        return 16 - I2R(level) * 4
    endfunction
    //How long the traps last
    
    //End of Configuration

    native UnitAlive takes unit u returns boolean

    private struct SpikeTrap
        private static player LOCAL_PLAYER
        private static group GROUP = CreateGroup()
        private static thistype TEMP
        private static filterfunc FILTER
        
        unit caster
        player owner
        real x
        real y
        fogmodifier visibility
        integer level
        integer targetsLeft
        integer tick
        integer isAnimationPlaying = 0
        effect sfx
    
        private static method groupActions takes nothing returns boolean
            local unit u = GetFilterUnit()
            if(IsUnitEnemy(u, TEMP.owner) and IsUnitType(u, UNIT_TYPE_GROUND) and TEMP.targetsLeft > 0 and UnitAlive(u)) then
                call DestroyEffect(AddSpecialEffect(TRIGGER_EFFECT, GetUnitX(u), GetUnitY(u)))
                call UnitDamageTarget(TEMP.caster, u, DAMAGE(TEMP.level), false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
                call PauseUnit(TEMP.caster, false)
                call ShowUnit(TEMP.caster, true)
                call SelectUnit(TEMP.caster, LOCAL_PLAYER == TEMP.owner)
                call IssueImmediateOrder(TEMP.caster, "stop")
                call SetUnitAnimation(TEMP.caster, FINISH_ANIMATION)
                call DestroyEffect(TEMP.sfx)
                set TEMP.isAnimationPlaying = 1
                call FogModifierStop(TEMP.visibility)
                call DestroyFogModifier(TEMP.visibility)
                set TEMP.tick = T32_Tick + R2I(ANIMATION_TIME / T32_PERIOD)
            endif
        
            set u = null
            return false
        endmethod
    
        private method periodic takes nothing returns nothing
            local integer i = 0
        
            if(this.tick >= T32_Tick) then
                if(this.isAnimationPlaying == 0) then
                    set TEMP = this
                    call GroupEnumUnitsInRange(GROUP, this.x, this.y, RADIUS, FILTER)
                endif
            else
                call this.stopPeriodic()
            
                if(this.isAnimationPlaying == 0) then
                    call PauseUnit(this.caster, false)
                    call ShowUnit(this.caster, true)
                    call SelectUnit(this.caster, LOCAL_PLAYER == this.owner)
                    call DestroyEffect(this.sfx)
                    call DestroyEffect(AddSpecialEffect(CASTER_EFFECT, this.x, this.y))
                    set this.caster = null
                else
                    call SetUnitAnimation(this.caster, "stand")
                    set this.caster = null
                endif
            
                call FogModifierStop(this.visibility)
                call DestroyFogModifier(this.visibility)
                call this.destroy()
            endif
        endmethod
    
        implement T32x
    
        private static method actions takes nothing returns nothing
            local thistype d = thistype.create()
        
            set d.caster = GetTriggerUnit()
            set d.x = GetUnitX(d.caster)
            set d.y = GetUnitY(d.caster)
            set d.owner = GetOwningPlayer(d.caster)
        
            set d.visibility = CreateFogModifierRadius(d.owner, FOG_OF_WAR_VISIBLE, d.x, d.y, SIGHT_RADIUS, true, false)
            call FogModifierStart(d.visibility)
        
            set d.sfx = AddSpecialEffect(TRAP_EFFECT, d.x, d.y)
        
            set d.level = GetUnitAbilityLevel(d.caster, ABILITY_ID)
            set d.tick = T32_Tick + R2I(TRAP_DURATION(d.level) / T32_PERIOD)
            set d.targetsLeft = MAX_TARGETS
            call IssueImmediateOrder(d.caster, "stop")
            call PauseUnit(d.caster, true)
            call ShowUnit(d.caster, false)
        
            call d.startPeriodic()
        endmethod
    
        private static method condition takes nothing returns boolean
            if(GetSpellAbilityId() == ABILITY_ID) then
                call .actions()
            endif
        
            return false
        endmethod
    
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local integer i = 0
            set FILTER = Filter(function thistype.groupActions)
            set LOCAL_PLAYER = GetLocalPlayer()
        
            loop
                exitwhen i == 15
                call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            
                set i = i + 1
            endloop
        
            call TriggerAddCondition(t, Condition(function thistype.condition))
        endmethod
    endstruct
endscope

V1 - Release
V1.1 - Removed a few useless things from the code
V1.2 - Laziness Update.
V1.3 - Much more laziness.
 

Attachments

  • ~SpikeTrapSpell~.w3x
    27.7 KB · Views: 297

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
JASS:

    private static method groupActions takes nothing returns boolean
        local unit u = GetFilterUnit()
        if(IsUnitEnemy(u, TEMPORARY_INSTANCE.owner) and IsUnitType(u, UNIT_TYPE_GROUND) and TEMPORARY_INSTANCE.targetsLeft > 0 and UnitAlive(u)) then
            set bj_lastCreatedEffect = AddSpecialEffect(TRIGGER_EFFECT, GetUnitX(u), GetUnitY(u))
            call DestroyEffect(bj_lastCreatedEffect)
            call UnitDamageTarget(TEMPORARY_INSTANCE.caster, u, DAMAGE(TEMPORARY_INSTANCE.level), false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
            call PauseUnit(TEMPORARY_INSTANCE.caster, false)
            call ShowUnit(TEMPORARY_INSTANCE.caster, true)
            call SelectUnit(TEMPORARY_INSTANCE.caster, thistype.LOCAL_PLAYER == TEMPORARY_INSTANCE.owner)
            call IssueImmediateOrder(TEMPORARY_INSTANCE.caster, "stop")
            call SetUnitAnimation(TEMPORARY_INSTANCE.caster, thistype.FINISH_ANIMATION)
            call DestroyEffect(TEMPORARY_INSTANCE.sfx)
            set TEMPORARY_INSTANCE.isAnimationPlaying = 1
            call FogModifierStop(TEMPORARY_INSTANCE.visibility)
            call DestroyFogModifier(TEMPORARY_INSTANCE.visibility)
            set TEMPORARY_INSTANCE.tick = T32_Tick + R2I(thistype.ANIMATION_TIME / T32_PERIOD)
        endif
        
        return false
    endmethod

You didn't null u here.

JASS:

    private static method actions takes nothing returns nothing
        local thistype d = thistype.create()
        local unit u = GetTriggerUnit()
        local integer i = 1
        local real r = 0
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        
        set d.x = x
        set d.y = y
        set d.caster = u
        set d.owner = GetOwningPlayer(d.caster)
        
        set d.visibility = CreateFogModifierRadius(d.owner, FOG_OF_WAR_VISIBLE, d.x, d.y, .SIGHT_RADIUS, true, false)
        call FogModifierStart(d.visibility)
        
        set d.sfx = AddSpecialEffect(.TRAP_EFFECT, d.x, d.y)
        
        set d.level = GetUnitAbilityLevel(d.caster, .ABILITY_ID)
        set d.tick = T32_Tick + R2I(TRAP_DURATION(d.level) / T32_PERIOD)
        set d.targetsLeft = MAX_TARGETS
        call IssueImmediateOrder(d.caster, "stop")
        call PauseUnit(d.caster, true)
        call ShowUnit(d.caster, false)
        
        call d.startPeriodic()
    endmethod

Same thing here.
 

Romek

Super Moderator
Reaction score
963
JASS:
        local thistype d = thistype.create()
        local unit u = GetTriggerUnit()
        local integer i = 1
        local real r = 0
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        
        set d.x = x
        set d.y = y
        set d.caster = u
        set d.owner = GetOwningPlayer(d.caster)

Could simply be:
JASS:
        local thistype d = thistype.create()
        // local integer i = 1 // Never Used?
        // local real r = 0 // Never Used?
        
        set d.caster = GetTriggerUnit()
        set d.x = GetUnitX(d.caster)
        set d.y = GetUnitY(d.caster)
        set d.owner = GetOwningPlayer(d.caster)

Shorter, cleaner, and you won't have to null anything. You also had two variables that were never used.

JASS:
            if(this.isAnimationPlaying == 0) then
                call PauseUnit(this.caster, false)
                call ShowUnit(this.caster, true)
                call SelectUnit(this.caster, thistype.LOCAL_PLAYER == this.owner)
                call DestroyEffect(this.sfx)
                set bj_lastCreatedEffect = AddSpecialEffect(thistype.CASTER_EFFECT, this.x, this.y)
                call DestroyEffect(bj_lastCreatedEffect)
                set this.caster = null
            else
                call SetUnitAnimation(this.caster, "stand")
                set this.caster = null
                call this.destroy() // << If this is called, the struct will be freed twice...
            endif
            
            call FogModifierStop(this.visibility)
            call DestroyFogModifier(this.visibility)
            call this.destroy() // << ...Because of this


You don't have to empty 'GROUP' in the periodic function, as [ljass]GroupEnum...[/ljass] functions do this already.
It would also seem that 'isAnimationPlaying' should be a boolean.
 

Chaos_Knight

New Member
Reaction score
39
Just a thing. Dont you use scopes? Or is that neccesary? Cause right now, it looks like the code never starts. :p
 

BlackRose

Forum User
Reaction score
239
Just a thing. Dont you use scopes? Or is that neccesary? Cause right now, it looks like the code never starts.

JASS:
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
        set FILTER = Filter(function thistype.groupActions)
        set LOCAL_PLAYER = GetLocalPlayer()
        
        loop
            exitwhen i == 15
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            
            set i = i + 1
        endloop
        
        call TriggerAddCondition(t, Condition(function thistype.condition))
    endmethod
 

tooltiperror

Super Moderator
Reaction score
231
Why don't people just use normal JASS inits? sigh.
 

Romek

Super Moderator
Reaction score
963
There's nothing wrong with having the entire spell within a struct... :p
 

_whelp

New Member
Reaction score
54
Erm... I added/removed the things that you guys told me to I guess.

What else should I do?
 

BlackRose

Forum User
Reaction score
239


o_O, must I explain?

[ljass]call DestroyEffect( AddSpecialEffect(TRIGGER_EFFECT, GetUnitX(u), GetUnitY(u)) )[/ljass]

Also, probably just me, but I hate how you use TEMPORARY_INSTANCE as the temporary struct, it just looks ugly :p But as long it's still readable I guess.

By the way, upload the correct testmap. Your's has nothing in it. The map name is "Heroic Rush" instead of "Spike Trap", all there is in the map is Grom Hellscream and one creep. No abilities. And only T32, and Restore, and Creeps trigger?
 

BlackRose

Forum User
Reaction score
239
Updated. ~

That, is a lie.

JASS:
    private static method DAMAGE takes integer level returns real
        return I2R(level) * 50 + 25
    endmethod
    //The damage
    
    private static method TRAP_DURATION takes integer level returns real
        return 16 - I2R(level) * 4
    endmethod
    //How long the traps last


Why the conversion?

***
JASS:
        loop
            exitwhen i == 15
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
            
            set i = i + 1
        endloop


The BJ is perfectly fine.

***

I don't know why you really have the entire spell within the struct, to me, it's just strange and I'm more use to having it seperated. The dot prefix before the configurable globals is strange.

***

TEMPORARY_INSTANCE is really ugly :(
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
I don't know why you really have the entire spell within the struct, to me, it's just strange and I'm more use to having it seperated. The dot prefix before the configurable globals is strange.

I happen to prefer it this way. I guess it's a matter of taste. I do think that configuration globals would make more sense outside the struct, though this is all internalized so it makes no difference.

TEMPORARY_INSTANCE is really ugly :(

This is also private and not used outside the library, so whether or not the name is easy to type out is irrelevant.
 

BlackRose

Forum User
Reaction score
239
JASS:
            if(this.tick >= T32_Tick) then
                if(this.isAnimationPlaying == 0) then

                if(this.isAnimationPlaying == 0) then
            if(IsUnitEnemy(u, TEMP.owner) and IsUnitType(u, UNIT_TYPE_GROUND) and TEMP.targetsLeft > 0 and UnitAlive(u)) then


- Just a few more unimportant things but, you don't need brackets on there. Doesn't matter but :p

- But when the condition in the unit filter gets a bit bigger, you may want to use delimited comments to keep them readeable.

JASS:
if IsUnitEnemy( u, TEMP.owner ) and /*
*/ IsUnitType( u, UNIT_TYPE_GROUND ) then


- Why only ground units? Why not just hit units under a certain height, not sure, but what if there was a jumping spell and you leapt over but still got hit?

- Although already in your header, T32 as a commented out requirement next to the scope is nice. [ljass]scope SpikeTrap //requires T32[/ljass], although you can make it a library and do that anyways.

- I just noticed, your entire 'actions' method is your create, so you could just change that to create method and [ljass]thistype.create()[/ljass] to [ljass]thistype.allocate()[/ljass]

- Since you are nulling caster in both if's in the destroy block, why not just use onDestroy method, this will help in easier management for bigger spells.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top