Spell Spike Trap

_whelp

New Member
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.


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

Darthfett

Super Mod
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
Staff member
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.
 

BlackRose

Forum User
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
Staff member
Why don't people just use normal JASS inits? sigh.
 

Romek

Super Moderator
Staff member
There's nothing wrong with having the entire spell within a struct... :p
 

BlackRose

Forum User


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
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

Super Mod
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
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 The Helper:
    I am not complaining though Hurricanes are not fun I have been through alot of them
  • Blackveiled Blackveiled:
    I evacuated for it just incase since I live in La Porte pretty much right on the water (in Houston area). Didn't see anything. It's a good thing for me anyways.
  • Ghan Ghan:
    It's too bad that Blizzard messed up so terribly with Reforged.
  • Ghan Ghan:
    Still has a 0.6 user metacritic score.
  • Ghan Ghan:
    (Out of 10)
  • Varine Varine:
    I haven't even tried it yet because everyone said it sucked. How do you get .6 out of ten?
  • The Helper The Helper:
    I am waiting for Dwarf Fortress to come out on Steam then I am on it.
  • Accname Accname:
    I play Dwarf Fortress from time to time.
  • Accname Accname:
    Its Okay.
  • Accname Accname:
    But the performance is sub-par. Does not seem to be well optimized.
  • Varine Varine:
    Is the Steam version coming with a graphical overlay or is it using the ASCII graphics still?
  • tom_mai78101 tom_mai78101:
    Steam version has the graphical overlay.
  • The Helper The Helper:
    Steam Version has graphics, a new interface and will be integrated in the steam mod stuff
  • Blackveiled Blackveiled:
    I didn't think WC3 Reforged was that bad. It pretty much did what it stated (to me at least), but then again I can care less about reforged campaigns and all that. I just care about gameplay.
  • Varine Varine:
    I think people were hoping for a resurgence in mod support. I have little interest in the actual game tbh
  • Varine Varine:
    Plus I know they added that Dota precedent clause where Blizzard can steal your shit which isn't cool.
  • Ghan Ghan:
    They sold Reforged to the community as primarily a huge graphical uplift with high res textures and redone cinematics.
  • Ghan Ghan:
    They even showed one of the redone cinematics at Blizzcon and that very cinematic wasn't even in the released game.
  • Ghan Ghan:
    So I think folks were angry about that.
  • Varine Varine:
    Didn't the backwards compatability have issues too?
  • Accname Accname:
    I never had any hopes for that. Blizzard is not the same company it was 10 years ago.
  • The Helper The Helper:
    It happens in corporations. They just absorb the companies they buy and it is not about the love of making awesome games it is how much money can we make with the least amount of cost.
  • The Helper The Helper:
    Blizzard is watered down now hopefully they can pull it together
  • The Helper The Helper:
    they got a server engineer job opening :)

    Members online

    No members online now.

    Affiliates

    Hive Workshop
    Top