• 🏆 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]Pounce v1.0d [MUI]

A spell requested by Zealon.

Inspired by DOTA's Slark's Pounce spell.

WARNING! USERS THAT USES THIS SPELL BELOW VERSION 1.0c HAVE TO HAVE JNGP TO MAKE IT WORK ELSE YOU WILL GET AN ERROR.

Known issues:
  • Can crash the map if jumping outside the map. Fixed.
  • Jumps over trees, walls, cliffs, buildings.

Remember if you are a vJASS user, I made up the globals to fit in perfectly. Also remember to uncomment the comment in the ini trigger because there the ability is added to the dummy. And a few globals are set there as well.

If you are not a vJASS user, check, double-check and re-check if you got all the variables you need, these are the required variables:

Pounce_ABILID : Ability
Pounce_DummyABILID : Ability
Pounce_AT : AttackType
Pounce_DT : DamageType
PounceHash : Hashtable
TempInt : Integer
TimerN : Integer
PounceLoc : Location
Timers : Timer Array
Pounce_Dummy : Unit
EnumGroup : UnitGroup
Pounce_DummyType : UnitType
Pounce_YMin : Real
Pounce_YMax : Real
Pounce_XMin : Real
Pounce_XMax : Real

The init trigger creates the most of them.

The triggers:


  • Pounce Init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Create some necessary variables --------
      • Set Pounce_XMax = 0.00
      • Set Pounce_XMin = 0.00
      • Set Pounce_YMax = 0.00
      • Set Pounce_YMin = 0.00
      • -------- Just for the creation of the variable, nothing to be afraid of --------
      • Set Pounce_Dummy = No unit
      • -------- Here you set the ability to the one you copy over into your map --------
      • Set Pounce_ABILID = Pounce
      • -------- and the copied dummy ability --------
      • Set Pounce_DummyABILID = Pounce Dummy
      • -------- the dummy type, can actually be any dummy you have --------
      • -------- basic it's needed for casting a spell --------
      • Set Pounce_DummyType = Dummy
      • -------- the attack type --------
      • Set Pounce_AT = Spells
      • -------- and the damage type --------
      • Set Pounce_DT = Magic
      • -------- just for creating a variable --------
      • Set TempInt = 0
      • -------- same --------
      • Set TimerN = 0
      • -------- hashtable creation --------
      • Hashtable - Create a hashtable
      • Set PounceHash = (Last created hashtable)
      • -------- location creation --------
      • -------- DO NOT REMOVE THIS LOCATION AT ANY TIME --------
      • Set PounceLoc = (Point(0.00, 0.00))
      • -------- creating the dummy unit --------
      • -------- the whole spell will only need one dummy, regardless the amount of instances --------
      • Custom script: set udg_Pounce_Dummy = CreateUnit(Player(15), udg_Pounce_DummyType, 0., 0., 0.)
      • -------- add the dummy ability --------
      • Custom script: call UnitAddAbility(udg_Pounce_Dummy, udg_Pounce_DummyABILID)
      • -------- Setting some necessary variables --------
      • Custom script: set udg_Pounce_YMin = GetRectMinY(bj_mapInitialPlayableArea)
      • Custom script: set udg_Pounce_YMax = GetRectMaxY(bj_mapInitialPlayableArea)
      • Custom script: set udg_Pounce_XMin = GetRectMinX(bj_mapInitialPlayableArea)
      • Custom script: set udg_Pounce_XMax = GetRectMaxX(bj_mapInitialPlayableArea)
      • -------- destroy the trigger --------
      • Custom script: call DestroyTrigger(GetTriggeringTrigger())
JASS:
//
//
//
//        *Pounce v1.0d
//                by baassee
//                
//                A request by Zealon.
//
//        *Requirements*
//            None. Just the WE.
//            
//        *How to import*
//        
//            Read the how to import above the pounce init trigger.
//            
//        *Facts about the spell*
//            The DOT is an object editor based spell, acidbomb.
//            It gives us, slow and dps which is good and can if you want to
//            also give us armor reduction.
//            
//            The spell code got it's own timer recycler, use it if you want to but remember to use
//            it properly or it will bug. No this spell does not leak either timers or locations.
//            The location is there to get the height of the location GetLocationZ which is needed
//            for a proper parabola. But I use the native MoveLocation which moves the location
//            so I only need one at all times and it doesnt leak.
//            The timers are recycled so they will not leak either.
//            
//            The enumgroup will not leak either as it refreshes everytime you call the 
//           GroupEnumUnitsInRange native.
//
//            And the unit is ordered to stop at the end of the spell. Why?
//            Because if the unit is moving and you cast this spell, when it finishes
//            the unit will move to its previous set movement point. Stopping the unit
//            removes this.
//            
//            There are a few changeable functions below which I will explain a bit.
//            
//            *Credits*
//                Zealon for the request
 //               baassee for the code
 //               Spec and moyack for the standard Parabola formula
//
// For vJASS users only
//
//globals
//    private hashtable udg_PounceHash = InitHashtable()
//    private timer array Timers
//    private integer TimerN = 0
//    private integer udg_TempInt = 0
//    private attacktype udg_Pounce_AT = ATTACK_TYPE_NORMAL
//    private damagetype udg_Pounce_DT = DAMAGE_TYPE_MAGIC
//    private integer udg_Pounce_DummyType = 'stuf'
//    private unit udg_Pounce_Dummy = CreateUnit(Player(15), udg_Pounce_DummyType, 0., 0., 0.)
//    private integer udg_Pounce_ABILID = 'A000'
//    private integer udg_Pounce_DummyABILID = 'A001'
//    private location udg_PounceLoc = Location(0., 0.,)
//    private real udg_Pounce_YMin
//    private real udg_Pounce_YMax
//    private real udg_Pounce_XMin
//    private real udg_Pounce_XMax
//endglobals


//********CHANGEABLE FUNCTIONS**************

//obviously the total distance of the spell
function PounceDistance takes integer lvl returns real
    return 300. + lvl * 100.
endfunction

//the height of the parabola
function PounceHeight takes integer lvl returns real
    return 150. + lvl * 0
endfunction

//the hit damage of the spell aka when the unit reaches into a unit
// this is not the dps, dps is changeable in the object editor
function PounceHitDamage takes integer lvl returns real
    return 40. * lvl
endfunction

//the speed the unit will move with, high values ~50 is not recommended
//0 speed and the spell will bug and the unit will not move
function PounceSpeed takes integer lvl returns real
    return 18. + 4. * lvl
endfunction

//the aoe the spell has around the caster
function PounceAOE takes integer lvl returns real
    return 85.
endfunction

//the special effect attached to the caster, like a trail or tail
//remember that in GUI the strings have one \ while in JASS it's two \\
function PounceEffect takes nothing returns string
    return "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
endfunction

constant function PounceCollision takes nothing returns real
    return 192.
endfunction

//an enum function
//basic units that cannot be targeted are
//dead
//structures
//magic immumne
//enemies
function PounceFilter takes unit u returns boolean
    local unit c = LoadUnitHandle(udg_PounceHash, udg_TempInt, 0)
    local boolean b = IsUnitEnemy(u, GetOwningPlayer(c)) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(u, UNIT_TYPE_DEAD)
    set c = null
    return b
endfunction

//***************END OF CONFIGURABLES************************
//standard parabola function
function PounceParabolaZ takes real h, real d, real x returns real
  return (4 * h / d) * (d - x) * (x / d)
endfunction

//simple timer recycler
//release the timer
function PounceRTimer takes timer t returns nothing
    if t != null then
        call PauseTimer(t)
        set udg_Timers[udg_TimerN] = t
        set udg_TimerN = udg_TimerN + 1
    else
        call BJDebugMsg("Tried to release a null timer")
    endif
endfunction

//and get a timer
function PounceNTimer takes nothing returns timer
    if udg_TimerN == 0 then
        return CreateTimer()
    endif
    set udg_TimerN = udg_TimerN - 1
    return udg_Timers[udg_TimerN]
endfunction

//the loop of the spell
function PounceLoop takes nothing returns nothing
    local integer id = GetHandleId(GetExpiredTimer()) //get the handle's id
    local real distx = LoadReal(udg_PounceHash, id, 6)//load some values
    local real distance = LoadReal(udg_PounceHash, id, 1)
    local real speed //all locals below are necessary if the spell is still on
    local real aoe
    local unit caster
    local real cx
    local real cy
    local real nx
    local real ny
    local real height
    local unit target
    local real hitdmg
    local boolean b = false
    if distx < distance then //checks if we havent reached our goal
        set caster = LoadUnitHandle(udg_PounceHash, id, 0) //loads the caster
        set cx = GetUnitX(caster)
        set cy = GetUnitY(caster)
        set height = LoadReal(udg_PounceHash, id, 2) //loads other values
        set speed = LoadReal(udg_PounceHash, id, 4)
        set aoe = LoadReal(udg_PounceHash, id, 5)
        set hitdmg = LoadReal(udg_PounceHash, id, 3)
        set nx = cx + speed * LoadReal(udg_PounceHash, id, 7)
        set ny = cy + speed * LoadReal(udg_PounceHash, id, 8)
        //check that the next move coordinates are inside the map area
        if udg_Pounce_XMin <= nx and nx <= udg_Pounce_XMax and udg_Pounce_YMin <= ny and ny <= udg_Pounce_YMax then
            call SetUnitX(caster, nx)//sets the new coordinates aka moves the caster
            call SetUnitY(caster, ny)
            set distx = distx + speed
            call SaveReal(udg_PounceHash, id, 6, distx)
            call MoveLocation(udg_PounceLoc, nx, ny) //moves the location
            call SetUnitFlyHeight(caster, PounceParabolaZ(height, distance, distx) + GetLocationZ(udg_PounceLoc), 0.) //just because im awesome
            set udg_TempInt = id //here we need the global variable because locals cannot be transfered
            call GroupEnumUnitsInRange(udg_EnumGroup, nx, ny, aoe + PounceCollision(), null)//enum
            loop
                set target = FirstOfGroup(udg_EnumGroup)
                exitwhen b or target == null
                call GroupRemoveUnit(udg_EnumGroup, target)
                if IsUnitInRangeXY(target, nx, ny, aoe) and PounceFilter(target) then
                    call UnitDamageTarget(caster, target, hitdmg, false, false, udg_Pounce_AT, udg_Pounce_DT, null)
                    call SetUnitOwner(udg_Pounce_Dummy, GetOwningPlayer(caster), false) //changes dummy owner
                    call SetUnitX(udg_Pounce_Dummy, nx)//moves the dummy
                    call SetUnitY(udg_Pounce_Dummy, ny)
                    call SetUnitAbilityLevel(udg_Pounce_Dummy, udg_Pounce_DummyABILID, GetUnitAbilityLevel(caster, udg_Pounce_ABILID))
                    call IssueTargetOrder(udg_Pounce_Dummy, "acidbomb", target)//casts the spell
                    call SetUnitFlyHeight(caster, GetUnitDefaultFlyHeight(caster), 0.)
                    call UnitRemoveAbility(caster, 'Amrf')//removes the crowform
                    call PounceRTimer(GetExpiredTimer())//releases the timer
                    call DestroyEffect(LoadEffectHandle(udg_PounceHash, id, 9))//removes the trail
                    call PauseUnit(caster, false)//unpauses the unit
                    call IssueImmediateOrder(caster, "stop")
                    call FlushChildHashtable(udg_PounceHash, id)//flushes the childkeys
                    set b = true
                endif
            endloop
        else
            call SetUnitFlyHeight(caster, GetUnitDefaultFlyHeight(caster), 0.)
            call UnitRemoveAbility(caster, 'Amrf')//fixes height and removes the crowform
            call PounceRTimer(GetExpiredTimer())//releases timer
            call DestroyEffect(LoadEffectHandle(udg_PounceHash, id, 9))//destroys effect
            call PauseUnit(caster, false)//unpauses
            call IssueImmediateOrder(caster, "stop")//to prevent a bug
            call FlushChildHashtable(udg_PounceHash, id)//clear childkeys
        endif
        set caster = null//and nulls
        set target = null
    else //else if we have reached the total distance we
        set caster = LoadUnitHandle(udg_PounceHash, id, 0)//load the caster
        call SetUnitFlyHeight(caster, GetUnitDefaultFlyHeight(caster), 0.)
        call UnitRemoveAbility(caster, 'Amrf')//fixes height and removes the crowform
        call PounceRTimer(GetExpiredTimer())//releases timer
        call DestroyEffect(LoadEffectHandle(udg_PounceHash, id, 9))//destroys effect
        call PauseUnit(caster, false)//unpauses
        call IssueImmediateOrder(caster, "stop")//to prevent a bug
        call FlushChildHashtable(udg_PounceHash, id)//clear childkeys
        set caster = null//nulls
    endif
endfunction

//the normal cast trigger, it's awesome
function PounceCast takes nothing returns boolean
    local timer t
    local integer id //few locals needed
    local integer lvl
    local unit c
    local real angle
    if GetSpellAbilityId() == udg_Pounce_ABILID then //the ability check
        set c = GetTriggerUnit()//gets the caster
        set t = PounceNTimer() //gets a new timer
        set id = GetHandleId(t) //gets the id of the timer
        set lvl = GetUnitAbilityLevel(c, udg_Pounce_ABILID)
        set angle = GetUnitFacing(c)
        call SaveUnitHandle(udg_PounceHash, id, 0, c)//saves some values
        call SaveReal(udg_PounceHash, id, 1, PounceDistance(lvl))
        call SaveReal(udg_PounceHash, id, 2, PounceHeight(lvl))
        call SaveReal(udg_PounceHash, id, 3, PounceHitDamage(lvl))
        call SaveReal(udg_PounceHash, id, 4, PounceSpeed(lvl))
        call SaveReal(udg_PounceHash, id, 5, PounceAOE(lvl))
        call SaveReal(udg_PounceHash, id, 6, 0.)
        call SaveReal(udg_PounceHash, id, 7, Cos(angle * bj_DEGTORAD))
        call SaveReal(udg_PounceHash, id, 8, Sin(angle * bj_DEGTORAD))
        call SaveEffectHandle(udg_PounceHash, id, 9, AddSpecialEffectTarget(PounceEffect(), c, "chest"))//adds the trail
        call UnitAddAbility(c, 'Amrf')//adds crow from
        call PauseUnit(c, true)//pauses the unit
        call TimerStart(t, 0.03, true, function PounceLoop)//starts the loop
        set c = null //and null
    endif
    return false
endfunction

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

//standard ini trigger
function InitTrig_Pounce takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function PounceCast))
    //call UnitAddAbility(udg_Pounce_Dummy, udg_Pounce_DummyABILID)
    //set udg_Pounce_YMin = GetRectMinY(bj_mapInitialPlayableArea)
    //set udg_Pounce_YMax = GetRectMaxY(bj_mapInitialPlayableArea)
    //set udg_Pounce_XMin = GetRectMinX(bj_mapInitialPlayableArea)
    //set udg_Pounce_XMax = GetRectMaxX(bj_mapInitialPlayableArea)
endfunction



v1.0d - Fixed Bribe's comments.
2 New configurable functions.
v1.0c - Fixed the delimtered comments that were only for vJASS. Totally forgot about those. Now the spell will function properly for normal WE users and look crap to vJASSers.
v1.0b - Fixed the crash when going outside map. Now it will not go outside map.
- Fixed return boolean stuff as watermelon suggested.
v1.0 - Released.


Got enough documentation. Contains a How To Import Manual inside the map.

Give credits to baassee (me) and Zealon when this spell is used.

Have a nice day.

Keywords:
baassee, spell, pounce, dota, Zealon, parabola, jump,
Contents

Pounce v1.0d (Map)

Reviews
23 Nov 2011 Bribe: Approved (Useful). Please make the changes I requested for a better score. PounceFilter should take unit "c" as a parameter (load the unit handle from within your own code) so that it does not need to be nulled (therefore making...

Moderator

M

Moderator

23 Nov 2011
Bribe: Approved (Useful). Please make the changes I requested for a better score.

PounceFilter should take unit "c" as a parameter (load the unit handle from within your own code) so that it does not need to be nulled (therefore making the function just 1 line long because it just needs "return asdfjlasdf").

The "enumGroup" is not found in your GUI script (you need to include it by using "Remove all units from EnumGroup") so it will throw a syntax error when users try to import your spell becuase that variable will not be generated. But just use bj_lastCreatedGroup, don't create another handle for this, because it's a waste.

Instead of "exitwhen b or ..." just replace the line "set b = true" with "exitwhen true".

You should null the caster at the end of the function and not just within a couple if-blocks, this way your code is shorter and has less duplicate text.

Crow form should be added and removed in the same moment. You do not have to keep the ability on him, it is simply a wc3 bug when the ability is first added it allows the fly height to be changed.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Reserved for future updates, comments.

Yes it contains a basic timer recycler and yes the variables do not have a prefix because it's meant to be used by everybody.

EDIT:

The spell has been updated to version 1.0c.

v1.0c - Fixed the delimtered comments that were only for vJASS. Totally forgot about those. Now the spell will function properly for normal WE users and look crap to vJASSers.
 
Last edited:
Level 37
Joined
Mar 6, 2006
Messages
9,240
One question, why are the variables not nulled when returning true? Does it not leak then and why not?

JASS:
function PounceEnum takes nothing returns boolean
    local unit u = GetFilterUnit()
    local unit c = LoadUnitHandle(udg_PounceHash, udg_TempInt, 0)
    if IsUnitEnemy(u, GetOwningPlayer(c)) and/*
    */ IsUnitType(u, UNIT_TYPE_STRUCTURE) == false and/*
    */ IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) == false and/*
    */ GetWidgetLife(u) > 0.405 and/*
    */ IsUnitType(u, UNIT_TYPE_DEAD) == false then
        return true
    endif
    set u = null
    set c = null
    return false
endfunction
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
To my understanding, returning a value would stop further execution of the function, meaning that the variables won't be nulled.

It's rather easy to fix though; just use a local boolean variable.

JASS:
function PounceEnum takes nothing returns boolean
    local unit u = GetFilterUnit()
    local unit c = LoadUnitHandle(udg_PounceHash, udg_TempInt, 0)
    local boolean b = IsUnitEnemy(u, GetOwningPlayer(c)) and/*
    */ IsUnitType(u, UNIT_TYPE_STRUCTURE) == false and/*
    */ IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) == false and/*
    */ GetWidgetLife(u) > 0.405 and/*
    */ IsUnitType(u, UNIT_TYPE_DEAD) == false
    set u = null
    set c = null
    return b
endfunction
Nothing else seems wrong. You could declare the functions at the top as constant but I don't think it really matters.
You also could change "boolean == false" to "not boolean".
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
It's rather easy to fix though; just use a local boolean variable.

JASS:
function PounceEnum takes nothing returns boolean
    local unit u = GetFilterUnit()
    local unit c = LoadUnitHandle(udg_PounceHash, udg_TempInt, 0)
    local boolean b = IsUnitEnemy(u, GetOwningPlayer(c)) and/*
    */ IsUnitType(u, UNIT_TYPE_STRUCTURE) == false and/*
    */ IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) == false and/*
    */ GetWidgetLife(u) > 0.405 and/*
    */ IsUnitType(u, UNIT_TYPE_DEAD) == false
    set u = null
    set c = null
    return b
endfunction
Nothing else seems wrong. You could declare the functions at the top as constant but I don't think it really matters.
You also could change "boolean == false" to "not boolean".

Fixed.

Dunno if it matters with constant functions in this case.

Yeah I do not know why but I keep switching between not and == false :p


I've been waiting for the murloc's skills to be uploaded here in the hive for a really long time, thanks for sharing this man! keep up the good work for more skills

You're welcome! Although it's not exactly like the DOTA one.


Suggestion: Can you make it target point?

Just ripped the concept from DOTA, sorry.
 
Level 11
Joined
May 31, 2008
Messages
698
In the loop, make a location "loc" 70 units in front of the unit, do EnumDestructablesInCircleBJ with radius of 85 of loc, and in the function that it calls, set a global "destructablecount" = destructiblecount + 1
Then do a condition back in the loop,
if pathing is off at loc or destructiblecount > 0 then
stop the pounce
endif
you could also add to that if a unit of type structure is near loc then stop the pounce
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
I think the instant thing is fine since IT IS FROM DOTA... but I guess it wouldn't hurt to make it targetable...

4/5, good job on this... ^_^

I'll think I'll go with the non-targetable. Thanks :D


In the loop, make a location "loc" 70 units in front of the unit, do EnumDestructablesInCircleBJ with radius of 85 of loc, and in the function that it calls, set a global "destructablecount" = destructiblecount + 1
Then do a condition back in the loop,
if pathing is off at loc or destructiblecount > 0 then
stop the pounce
endif
you could also add to that if a unit of type structure is near loc then stop the pounce

So you want this to end at a destructable? You know it's not only trees that are included in the category destructables. And BJs suck btw.

The structure thing is a bit meh since the unit will pop next to the building when moving.
 
Level 11
Joined
May 31, 2008
Messages
698
Well you would probably have to develop some kind of system that detects if the destructible is a tree/something you cant walk on or jump over or if it is something that you can walk on, like a bridge or ramp.

And as for the targetable, why not make the option of having it be targetable? it wouldnt be too hard to do. And that would make everyone happy :D lol
 
Level 12
Joined
May 21, 2009
Messages
994
I'm pretty sure PitzerMike made a function called IsDestructableTree(destructable d) then you can do: if not IsDestructableTree(yourdest) then do stuff that would happen if it is not a tree, or reverse. I'm sorry if I'm misunderstanding something.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
You set your custom camerabounds to something like "set camera bounds to region ..." in GUI. So you see my region I've used to get coordinates bj_initialMapPlayableArea (or something like that), just change that into your region which if it is pre-defined will be something like gg_rct_nameofregion. And when you want to switch the bounds used for the spells you can just set the variables again, they're globals and it will work just fine.
 
Level 8
Joined
Feb 17, 2007
Messages
368
Ah ok, thanks for that. It works but you see there is 1 problem... I have some arenas which are circular in shape and a rectangular region in which the units are confined to extends outwards beyond the boundary of the arena. So units are still able to jump into the boundary that the region covers. I can't shape the region to perfectly fit the arena, so that's my problem...lol. Here is a picture to show you what I mean:
 

Attachments

  • worldedit121 2011-11-26 11-02-24-47.jpg
    worldedit121 2011-11-26 11-02-24-47.jpg
    158 KB · Views: 106
Top