• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Sliding Spell doesn't work properly

Status
Not open for further replies.
Level 10
Joined
Sep 29, 2006
Messages
447
Okay so I'm creating a spell called rage that causes a unit to run at a target location and knockback and damage all units it comes into contact with.

Current Problems:
Hero goes through doodads

Also, bear in mind that I'm working off of a Mac and do not have ANY third party programs. Everything must be done with the vanilla editor.

Here's the spell:

JASS:
function RageConditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A001'
endfunction

function RageGroupSlide takes nothing returns nothing
    local unit target = GetEnumUnit()
    local integer targetID = GetHandleId(target)
    local real angle = LoadReal(udg_hash, targetID, 0)
    local real kbCount = LoadReal(udg_hash, targetID, 1)
    local timer kbTimer = LoadTimerHandle(udg_hash, targetID, 2)
    local real dist = 20.00
    local real kbTotalDist = 350.00

    if (kbCount < kbTotalDist) then
        call SaveReal(udg_hash, targetID, 1, kbCount + dist)
        call Slide(target, dist, angle, "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl")
    else
        call PauseTimer(kbTimer)
        call PauseUnit(target, false)
        call DestroyTimer(kbTimer)
        call GroupRemoveUnit(udg_rageDamaged, target)
        call FlushChildHashtable(udg_hash, targetID)
    endif

    set kbTimer = null
    set target = null
endfunction

function RageKnockback takes nothing returns nothing
     call ForGroup(udg_rageDamaged, function RageGroupSlide)
endfunction

function RageGroupFilter takes nothing returns boolean
    local unit caster = udg_globalCaster
    local player owner = GetOwningPlayer(caster)
    local unit target = GetFilterUnit()
    local integer targetID
    local real damageAmount = 100 + ((I2R(GetUnitAbilityLevel(caster, 'A001')) - 1.00) * 50)
    local real angle
    local real angle2
    local real kbCount = 0
    local timer targetTimer = CreateTimer()

    if (GetUnitState(target, UNIT_STATE_LIFE) > 0  and IsUnitType(target, UNIT_TYPE_GROUND) == true and IsUnitEnemy(target, owner) == true and IsUnitInGroup(target, udg_rageDamaged) == false) then
        call GroupAddUnit(udg_rageDamaged, target)
        call UnitDamageTarget(caster, target, damageAmount, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)

        set targetID = GetHandleId(target)
        set angle = GetAngle(GetUnitX(caster), GetUnitY(caster), GetUnitX(target), GetUnitY(target))

        if angle < 0 then
           set angle = angle + 360.
        endif

        set angle2 = GetUnitFacing(caster)

        if angle2 > angle then
             set angle = angle2 - 45.
        elseif angle2 < angle then
             set angle = angle2 + 45.
        endif

        call SaveReal(udg_hash, targetID, 0, angle)
        call SaveReal(udg_hash, targetID, 1, kbCount)
        call SaveTimerHandle(udg_hash, targetID, 2, targetTimer)
        call PauseUnit(target, true)
        call TimerStart(targetTimer, .05, true, function RageKnockback)
    endif

    set caster = null
    set target = null
    return false
endfunction

function RageCallback takes nothing returns nothing
    local timer rageTimer = GetExpiredTimer()
    local integer timerID = GetHandleId(rageTimer)
    local unit caster = LoadUnitHandle(udg_hash, timerID, 0)
    local real angle = LoadReal(udg_hash, timerID, 1)
    local real count = LoadReal(udg_hash, timerID, 2)
    local real totalDist = LoadReal(udg_hash, timerID, 3)
    local real x = GetUnitX(caster)
    local real y = GetUnitY(caster)
    local real dist = 45.00
    local group targetGroup = CreateGroup()
    set udg_globalCaster = caster

    if (count < totalDist) then
        call SaveReal(udg_hash, timerID, 2, count + dist)
        call GroupEnumUnitsInRange(targetGroup, x, y, 175.00, Filter(function RageGroupFilter))
        call SlideKeepOrder(caster, dist, angle, "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl")
    else
        call PauseTimer(rageTimer)
        call DestroyTimer(rageTimer)
        call PauseUnit(caster, false)
        call FlushChildHashtable(udg_hash, timerID)
    endif

    set rageTimer = null
    set caster = null
    call DestroyGroup(targetGroup)
    set targetGroup = null
endfunction

function RageActions takes nothing returns nothing
    local unit caster = GetTriggerUnit()
    local real x1 = GetUnitX(caster)
    local real y1 = GetUnitY(caster)
    local real x2 = GetSpellTargetX()
    local real y2 = GetSpellTargetY()
    local real angle = GetAngle(x1, y1, x2, y2)
    local real totalDist = Distance(x1, y1, x2, y2)
    local real count = 0.00
    local timer rageTimer = CreateTimer()
    local integer timerID = GetHandleId(rageTimer)

    call SaveUnitHandle(udg_hash, timerID, 0, caster)
    call SaveReal(udg_hash, timerID, 1, angle)
    call SaveReal(udg_hash, timerID, 2, count)
    call SaveReal(udg_hash, timerID, 3, totalDist)

    call PauseUnit(caster, true)
    call TimerStart(rageTimer, 0.03, true, function RageCallback)
    
    set caster = null
    set rageTimer = null
endfunction

//===========================================================================
function InitTrig_Rage takes nothing returns nothing
    local trigger trigRage = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ(trigRage, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trigRage, Condition(function RageConditions))
    call TriggerAddAction(trigRage, function RageActions)
endfunction

And here are some function declared in the header that I used in this spell:

JASS:
//================================================================

function PolarProjectX takes real x, real dist, real angle returns real
    return x + dist * Cos(angle * bj_DEGTORAD)
endfunction

function PolarProjectY takes real y, real dist, real angle returns real
    return y + dist * Sin(angle * bj_DEGTORAD)
endfunction

function GetAngle takes real x1, real y1, real x2, real y2 returns real
    return bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
endfunction

function Distance takes real x1, real y1, real x2, real y2 returns real
    local real dx = x2 - x1
    local real dy = y2 - y1
    return SquareRoot(dx * dx + dy * dy)
endfunction

//================================================================

function SafeX takes real x returns real
    if (x > udg_maxX) then
        return udg_maxX
    elseif (x < udg_minX) then
        return udg_minX
    else
        return x
    endif
endfunction

function SafeY takes real y returns real
    if (y > udg_maxY) then
        return udg_maxY
    elseif (y < udg_minY) then
        return udg_minY
    else
        return y
    endif
endfunction

//================================================================

function SlideKeepOrder takes unit target, real dist, real angle, string fx returns nothing
    local real x = GetUnitX(target)
    local real y = GetUnitY(target)
    local real x2 = SafeX(PolarProjectX(x, dist, angle))
    local real y2 = SafeY(PolarProjectY(y, dist, angle))
    call DestroyEffect(AddSpecialEffect(fx, x, y))
    call SetUnitX(target, x2)
    call SetUnitY(target, y2)
endfunction

//================================================================

function Slide takes unit target, real dist, real angle, string fx returns nothing
    local real x = GetUnitX(target)
    local real y = GetUnitY(target)
    local real x2 = SafeX(PolarProjectX(x, dist, angle))
    local real y2 = SafeY(PolarProjectY(y, dist, angle))
    call DestroyEffect(AddSpecialEffect(fx, x, y))
    call SetUnitPosition(target, x2, y2)
endfunction

//================================================================

Extra Info, from the comments section of the Trigger:

Code:
Hashtable info: Hashtable = udg_hash
  Index (timerID,0) reserved for unit "caster"
  Index (timerID,1) reserved for real "angle"
  Index (timerID,2) reserved for real "count"
  Index (timerID,3) reserved for real "totalDist"

Hashtable info: Hashtable = udg_hash
  Index (targetID,0) reserved for real "angle"
  Index (targetID,1) reserved for real "kbcount"
  Index (targetID,2) reserved for timer "kbTimer"

Thanks for any help.
 
Last edited:
Use
JASS:
GetWidgetLife
or
JASS:
UnitAlive
or
JASS:
UNIT_STATE_LIFE
to check if a unit is dead, not UNIT_TYPE_DEAD. They return boolean, but I don't really suggest the last one.

The damage part, it needs to have the I2R conversion for the ability part, else, you need two seperate functions to define seperately the level and the damage, one of which will return an integer (the level) and the other a real (the damage).

I'll look at the trigger further.
 
Level 10
Joined
Sep 29, 2006
Messages
447
The damage part, it needs to have the I2R conversion for the ability part, else, you need two seperate functions to define seperately the level and the damage, one of which will return an integer (the level) and the other a real (the damage).

I've been working with java too long, I figured if I did an operation with both an integer and a real, it would return a real.

I updated that function with your suggestions, but nothing's changed. No damage is done, and the movement of both the hero and the target is very sporadic and glitchy.

Edit:

I have a theory that the caster unit is being passed into the RageGroupSlide function, which is why his movement is glitchy. This is why I think this.

When I change the equality comparison in the if statement, which is:
JASS:
if (kbCount < kbTotalDist) then
to this instead,
JASS:
if (kbCount > kbTotalDist) then
, it effectively disables that function. Once this is done the caster moves through objects and units and slides smoothly, but the enemy units do not get knocked back. So, I need to find a way to fix this. If someone thinks the cause may be different then please let me know.

Edit2:

I got the spell working and updated the code in the first post. The only remaining problem is that the hero slides through doodads, how do I prevent this?
 
Last edited:
Level 18
Joined
Jan 21, 2006
Messages
2,552
So what is going wrong with the function? The function actually returns the opposite boolean to what you would think, that is, IsTerrainPathable(..., PATHING_TYPE_WALKABILITY) returns false if the terrain is walkable. If that isn't the source of your problem, could you please describe what isn't working?

PrisonLove said:
The only remaining problem is that the hero slides through doodads, how do I prevent this?

Just read this now. I think the terrain pathability check still covers doodads, but I'm not 100% sure.

This will return true when a unit slides over a rock (tested with a knock-back lib I recently submitted : P):

JASS:
if IsTerrainPathable(GetUnitX(kb.subject), GetUnitY(kb.subject), PATHING_TYPE_WALKABILITY) then

In other words, this is the proper check to see whether or not terrain at a given x/y coordinate is not walkable.
 
Status
Not open for further replies.
Top