Spell Warp

Reaction score
341
Warp
TriggerHappy187

Spell Info:

Coding: vJASS
Requirements: TimerUtils
Description: Warps the unit to a point, damaging all the units that comes it the casters way.
Screenshot: (link)

attachment.php



Spell Code:

JASS:
library Warp initializer InitTrig requires TimerUtils

    globals
        private constant integer SPELL_ID = 'A000' // The raw ID of the spell
        
        private constant real TIMEOUT = 0.03 // How fast the timer runs
        private constant real FX_TIMEOUT = 0.05 // How fast the effects are created
        
        private constant real SPEED = 30 // How fast the caster is "Warped"
        private constant real DAMAGE_RADIUS = 300 // How close the units need to be to get damaged
        private constant real TARGET_RADIUS = 90 // How close to the destination the caster has to be for it to stop, just a safety global
        
        private constant string TRAIL_EFFECT = "Abilities\\Spells\\Human\\Invisibility\\InvisibilityTarget.mdl" // the warp effect
        
        private constant attacktype ATTACK = ATTACK_TYPE_MAGIC // the damage attack type
        private constant damagetype DAMAGE = DAMAGE_TYPE_UNIVERSAL // the damage damage type
    endglobals
    
    private constant function GetDamage takes integer level returns real
        return 5.*level
    endfunction
    
    private struct data
        unit caster
        
        real x
        real y
        
        real sin
        real cos
        
        real last
        
        static group g = CreateGroup()
        static data temp
        
        static real MapMaxX
        static real MapMinX
        static real MapMaxY
        static real MapMinY
        
        private static method True takes nothing returns boolean
            return true
        endmethod
        
        private static method mapbounds takes real x, real y returns boolean
            return x < data.MapMaxX or x > data.MapMinX or y < data.MapMaxY or y > data.MapMinY
        endmethod

        private static method fx takes nothing returns nothing
            local data d = GetTimerData(GetExpiredTimer())
            local real x1 = GetUnitX(d.caster)
            local real y1 = GetUnitY(d.caster)
            local real x = x1 + d.cos
            local real y = y1 + d.sin
            call GroupEnumUnitsInRange(data.g, d.x, d.y,  TARGET_RADIUS, Filter(function data.True))
            if not IsUnitInGroup(d.caster, data.g) then
                call DestroyEffect(AddSpecialEffect(TRAIL_EFFECT, x, y))
                return
            else
                call ReleaseTimer(GetExpiredTimer())
            endif
        endmethod
        
        private static method callback takes nothing returns nothing
            local data d = GetTimerData(GetExpiredTimer())
            local real x1 = GetUnitX(d.caster)
            local real y1 = GetUnitY(d.caster)
            local real x = x1 + d.cos
            local real y = y1 + d.sin
            local real dmg = GetDamage(GetUnitAbilityLevel(d.caster, SPELL_ID))
            local integer i = 0
            local unit u
            local real dis
            
            if data.mapbounds(x,y) then
                call SetUnitX(d.caster, x)
                call SetUnitY(d.caster, y)
            endif 

            set dis = SquareRoot((d.x - x1) * (d.x - x1) + (d.y - y1) * (d.y - y1))
            
            if dis <= TARGET_RADIUS or dis > (d.last+TARGET_RADIUS) then
                call ReleaseTimer(GetExpiredTimer())
                call d.destroy()
                return
            endif
            set d.last = dis
            
            call GroupEnumUnitsInRange(data.g, x1, y1,  DAMAGE_RADIUS, Filter(function data.True))
            
            loop
                set u = FirstOfGroup(data.g)
                exitwhen u == null
                call UnitDamageTarget(d.caster, u, dmg, true, false, ATTACK, DAMAGE, null)
                call GroupRemoveUnit(data.g, u)
            endloop
        endmethod
        
        static method create takes unit u, real x, real y returns data
            local data d = data.allocate()
            local real angle
            local timer t = NewTimer()
            local timer a = NewTimer()
            local real x1 = GetUnitX(u)
            local real y1 = GetUnitY(u)

            call SetTimerData(t, d)
            call SetTimerData(a, d)
            
            set d.x = x
            set d.y = y
            set d.caster = u
            
            call SetUnitX(d.caster, x1)
            call SetUnitY(d.caster, y1)
            call IssueImmediateOrder(d.caster, "stop")

            set x = x - x1
            set y = y - y1

            set d.last = SquareRoot(x*x+y*y)
            
            set angle = Atan2(y,x)
            set d.cos = SPEED * Cos(angle)
            set d.sin = SPEED * Sin(angle)
            
            call TimerStart(t, TIMEOUT, true, function data.callback)
            call TimerStart(a, FX_TIMEOUT, true, function data.fx)
            
            return d
        endmethod
        
    endstruct
    
    private function Actions takes nothing returns boolean
        local location l = GetSpellTargetLoc()
        if GetSpellAbilityId() == SPELL_ID then
            call data.create(GetTriggerUnit(), GetLocationX(l), GetLocationY(l))
        endif
        call RemoveLocation(l)
        set l = null
        return false
    endfunction

    //===========================================================================
    private function InitTrig takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(t, Condition(function Actions))
        set data.MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)-64.00
        set data.MapMaxY  = GetRectMaxY(bj_mapInitialPlayableArea)-64.00
        set data.MapMinY = GetRectMinX(bj_mapInitialPlayableArea)+64.00
        set data.MapMinX = GetRectMinY(bj_mapInitialPlayableArea)+64.0
    endfunction

endlibrary
 

Attachments

  • warp.jpg
    warp.jpg
    48 KB · Views: 737
  • Warp.w3x
    28.4 KB · Views: 302

trb92

Throwing science at the wall to see what sticks
Reaction score
142
In the "callback" method, you have
JASS:
call GroupEnumUnitsInRange(data.g, x1, y1,  90, Filter(function data.True))
call ForGroup(data.g, function data.damage)

Why don't you use this global there?
JASS:
private constant real DAMAGE_RADIUS = 300 // How close the units need to be to get damaged
 

Kenny

Back for now.
Reaction score
202
JASS:
library Warp initializer InitTrig requires TimerUtils


You do not need a library. I don't know why people think that they need to do that. Scopes are automatically put below libraries. I see little need in it to be honest.

JASS:
private constant function GetDamage takes real modifier, real level returns real
    return level*modifier
endfunction


Thats a bit messed up. Users can't currently change the damage as easily as they should be able to. You should make a the "modifier" a real value that can be changed and not have the 100 in the spell script below. Something like:

JASS:
private constant function GetDamage takes real level returns real
    return 100.00 * level
endfunction


JASS:
private static method damage takes nothing returns nothing
    local data d = data.temp
    local unit u = GetEnumUnit()
    local real dmg = GetDamage(100, GetUnitAbilityLevel(d.caster, SPELL_ID))
    call UnitDamageTarget(d.caster, u, dmg, true, false, ATTACK, DAMAGE, null)
endmethod


First off, You don't null u. Secondly, you should just do those functions in a FirstOfGroup() loop. Due to the (lack of) complexity of what is being done with the group, it would be easier and more efficient.

As its already been stated, you need to fix the radius of the GroupEnum functions to actually use a global value.

Your way of checking when to finish the spell is odd. In the (incredibly unlikely) situation that someone uses a speed of like 300 instead of a smaller number, the caster could just "skip" the target point and never stop.

And seeing as this spell has no SafeX/Y functions for border safety then that could become a problem. I guess that already is a little bit of a problem with this type of spell.

JASS:
private static method callback takes nothing returns nothing
    local data d = GetTimerData(GetExpiredTimer())
    local real x1 = GetUnitX(d.caster)
    local real y1 = GetUnitY(d.caster)
    local real x = x1 + d.cos
    local real y = y1 + d.sin
            
    call DestroyEffect(AddSpecialEffect(TRAIL_EFFECT, x, y))
    call GroupEnumUnitsInRange(data.g, d.x, d.y,  150, Filter(function data.True))
    call SetUnitX(d.caster, x)
    call SetUnitY(d.caster, y)

    if IsUnitInGroup(d.caster, data.g) then
        call ReleaseTimer(GetExpiredTimer())
        call d.destroy()
    endif
            
    // These things:
    set data.temp = d
    call GroupEnumUnitsInRange(data.g, x1, y1,  90, Filter(function data.True))
    call ForGroup(data.g, function data.damage)
endmethod


If d.destroy() is called, the stuff below my commented line are still called as well, and seeing as d does not exist anymore, that is a problem. Maybe just add a return or something under d.destroy().

JASS:
set angle = bj_RADTODEG * Atan2(y - y1, x - x1)
set d.cos = SPEED * Cos(angle * bj_DEGTORAD)
set d.sin = SPEED * Sin(angle * bj_DEGTORAD)


Take away the bj_DEGTORAD and bj_RADTODEG's...

You should also make weapontype configurable just for kicks :).

This spell would benefit from something like GTrigger (well all spells would). Maybe you should try implementing it. It would turn your init function to something like:

JASS:
private function InitTrig takes nothing returns nothing
    call GT_AddSpellEffectAction(function Actions,SPELL_ID)
endfunction


This spell is pretty basic, but if a few changes are made to it, it would be a nice resource.
 

BlackRose

Forum User
Reaction score
239
Strange, first time I tested it, it bugged and didn't stop moving at all. But now I cannot recreate this bug at all.

Anyways, hotkey doesn't work. It is B not W......?
The effect is ok, appealing XD
 
Reaction score
341
You do not need a library. I don't know why people think that they need to do that. Scopes are automatically put below libraries. I see little need in it to be honest.

It's just so if the user doesn't have the library a "missing requirement" error is easier to find the problem then "undefined function".

Everything else added, except for GTrigger. I don't want to add any more system requirements.
 

simonake

New Member
Reaction score
72
Maybe make the caster play an animation.

When it casted, she just stand here....
Maybe attack, slam?
 

Viikuna

No Marlo no game.
Reaction score
265
Yea, animation is must.

Also, you need to make it possible for users to have different effect creation timeout than dashing timeout. ( I always use .025 for moving units, and I dont wanna create effects every .025 seconds because it looks ugly as hell )

Aslo, you might check if unit is already damaged by the spell, so it wont get damaged many times: example

Unles you want it to damage units many times, which is kinda cool, because you need to do some aiming to deal as much damage as you can.
 
Reaction score
341
Also, you need to make it possible for users to have different effect creation timeout than dashing timeout.

Change the SPEED constant :p

Aslo, you might check if unit is already damaged by the spell, so it wont get damaged many times

I had that before the spell was released, just complicated the spell more. An easy way around this is just to make the damage lower.
 

Viikuna

No Marlo no game.
Reaction score
265
What I meant was that spell should maybe use different timer for creating effects, do we can have it to move unit every .025 sec and create effects every .045 or something like that.

Period for movement should be .03 or .025, otherwise it will look bad. Speed depends on gameplay stuff like balance and spell theme and everything. It still needs some way to change effect period without changing these.
 

Romek

Super Moderator
Reaction score
963
> GetDamage takes real level
I haven't seen a level 2.5 spell before.
(Level shouldn't be a real)

Also, instead of pointlessly using a library, how about making it clear that the spell requires TU, in the actual code?
 
Reaction score
341
I don't see the big deal in not using a library, it cleary states what it needs, and if you don't have that library it pJASS will tell you so.

Instead of a 'Undefined function' error.

I haven't seen a level 2.5 spell before.

Fixed.

Code updated, added FX_TIMEOUT constant.
 

Azlier

Old World Ghost
Reaction score
461
You should really calculate the distance upon casting, and slide the caster a certain direction until s/he(it) slides that distance exactly. Checking if the caster has reached the target spot is simple, yet totally breaks if you throw other slides and teleports into the mix.
 
Reaction score
341
Hotkey is still B, not W?

fixed.

You should really calculate the distance upon casting, and slide the caster a certain direction until s/he(it) slides that distance exactly. Checking if the caster has reached the target spot is simple, yet totally breaks if you throw other slides and teleports into the mix.

Added something similar to that.
 

Jesus4Lyf

Good Idea™
Reaction score
397
JASS:
            set d.last = SquareRoot((x - x1) * (x - x1) + (y - y1) * (y - y1))
            
            call SetUnitX(d.caster, x1)
            call SetUnitY(d.caster, y1)
            call IssueImmediateOrder(d.caster, "stop")
            
            set angle = bj_RADTODEG * Atan2(y - y1, x - x1)
            set d.cos = SPEED * Cos(angle * bj_DEGTORAD)
            set d.sin = SPEED * Sin(angle * bj_DEGTORAD)

Change to...
JASS:
            call SetUnitX(d.caster, x1)
            call SetUnitY(d.caster, y1)
            call IssueImmediateOrder(d.caster, "stop")

            set x=x-x1
            set y=y-y1

            set d.last = SquareRoot(x*x+y*y)
            
            set angle = Atan2(y,x)
            set d.cos = SPEED * Cos(angle)
            set d.sin = SPEED * Sin(angle)

In fact...
JASS:
            local real x1 = GetUnitX(u)
            local real y1 = GetUnitY(u)

Get rid of these and inline them into the
JASS:
            set x=x-x1
            set y=y-y1

lines.

Edit: Oh, and your constants are not stand-alone. They're interdependant. Changing the speed will change the damage, changing the timeout will change the speed, etc... =/
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top