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

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.
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/

      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