• 🏆 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!

Spell Steal v1.1

This is simple Spell Steal ability, which basically steals the last spell target unit has cast and deals minor damage

In this spell you can change:
- time for how long you will have the stolen ability
- effect of the missile that flies to the target
- effect on the target when it arrives
- how much damage it should deal upon hitting the target
- attack type of the damage
- damage type of the damage

cons:
- this spells uses structs(could be faster with hashtable)
- its a little bit harder to change time and damage of the spell
- this spell also uses standard blizzard effects so its not the nicest one but you can change those to any effect you want

tooltip:

[Hero's name] casts a powerful beam of magic towards its target dealing minor damage while templorarilly stealing last active ability the unit has used.

Duration: 60 / 75 / 90
Damage: 20 / 50 / 90

I also provide you a source code of the GetLastCastAbility so you dont need to look for that

Source Code

GetLastCastAbility


JASS:
scope SpellSteal initializer onInit//by edo494 v1.1
/*
*   
*   This spell requires(all of those are provided in the test map):
*
*   TimerUtils - http://www.wc3c.net/showthread.php?t=101322
*
*   GetLastCastAbility - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-getlastcastability-220717/
*
*   RegisterSpellEffectEvent - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193/
*
*
/=============================================================
*
*
*   configurables in the spell: - time which you have the stolen spell(on each level)
*                               - damage when the missile lands(on each level)
*                               - attack type of the damage
*                               - damage type of the damage
*                               - ID of the spell
*                               - effect the missile uses
*                               - effect upon impact
*                               - whether we want 3 effects for missile or just one
*
/==============================================================
*
*   globals
*
*       
*        private real array time
*
*        private real array damage
*
*           both of them are configurable right under the globals block in function Settings
*        
*
*        private constant attacktype ATT_TYPE
*     
*        private constant damagetype DMG_TYPE
*
*           attack and damage types of the spell
*
*        
*        private constant integer SPELLRC
*
*           spells ID
*
*        
*        private constant string MISSILE_EFFECT
*        
*        private constant string EXPLOSION_EFFECT
*        
*           effects used as missile and explosion - happens when missile gets closer then 50 to the target unit
*           at that time also spell steal happens
*
*
*        private constant boolean WANT_3_EFFS
*
*           if you want to use huge missile, basically if we are creating 3(true) effects for missile or 1(false)
*
*   endglobals
*/

    globals
        private real array time

        private real array damage
        
        private constant attacktype ATT_TYPE = ATTACK_TYPE_HERO
        
        private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
        
        private constant integer SPELLRC = 'A000'
        
        private constant string MISSILE_EFFECT = "Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl"
        
        private constant string EXPLOSION_EFFECT = "Units\\NightElf\\Wisp\\WispExplode.mdl"
        
        private constant boolean WANT_3_EFFS = true
    endglobals
    
    private function Settings takes nothing returns nothing
    
            set time[1] = 60
            set time[2] = 75
            set time[3] = 90    /*
                                    use index which is equivalent to the level, so if I had 4th level
                                    I would use time[4]
*/
            set damage[1] = 20
            set damage[2] = 50
            set damage[3] = 90  /*
                                    the same applies here
*/
            call LastAbilityAddFilter(SPELLRC)
                                /*
                                    Why would we want to steal spell steal?
*/
    endfunction
    
    private struct spsteal
        private unit stealer
        private integer stolen
        private real startx
        private real starty
        private unit target
        private effect explosion
        
        static method decay takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData(t)
            call UnitRemoveAbility(stealer, stolen)
            call ReleaseTimer(t)
            set stealer = null
            set target = null
            call DestroyEffect(explosion)       //yes, 60 seconds delay from creation but oh well, at least
                                                //we are not requesting another timer from TimerUtils
            set explosion = null
            set t = null
            call deallocate()
        endmethod
        
        private static method seffect takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData(t)
            local real dx = startx - GetUnitX(target)
            local real dy = starty - GetUnitY(target)
            local real angle = 0
            if (dx*dx + dy*dy) > 2500 then
                set angle = Atan2(GetUnitY(target) - starty, GetUnitX(target) - startx)
                set startx = startx + 35 * Cos(angle)
                set starty = starty + 35 * Sin(angle)
                call DestroyEffect(AddSpecialEffect(MISSILE_EFFECT, startx, starty))
            static if WANT_3_EFFS then      //this is on purpose moved so you can see it clearly
                call DestroyEffect(AddSpecialEffect(MISSILE_EFFECT, startx-10, starty-10))
                call DestroyEffect(AddSpecialEffect(MISSILE_EFFECT, startx+10, starty+10))
            endif
                call TimerStart(t, 0.05, false, function thistype.seffect)
            else
                set explosion = AddSpecialEffect(EXPLOSION_EFFECT, GetUnitX(target), GetUnitY(target))
                call UnitAddAbility(stealer, stolen)
                call SetUnitAbilityLevel(stealer, stolen, GetUnitAbilityLevel(target, stolen))
                call TimerStart(t, time[GetUnitAbilityLevel(target, stolen)], false, function thistype.decay)
                call UnitDamageTarget(stealer, target, damage[GetUnitAbilityLevel(stealer, SPELLRC)], true, false, ATT_TYPE, DMG_TYPE, null)
            endif
            set t = null
        endmethod
        
        static method create takes unit u, integer tosteal, unit utarget returns thistype
            local thistype this = .allocate()
            set stealer = u
            set stolen = tosteal
            set target = utarget
            set startx = GetUnitX(u)
            set starty = GetUnitY(u)
            
            //intial effect
            call DestroyEffect(AddSpecialEffect(MISSILE_EFFECT, startx, starty))
        static if WANT_3_EFFS then
            call DestroyEffect(AddSpecialEffect(MISSILE_EFFECT, startx-10, starty-10))
            call DestroyEffect(AddSpecialEffect(MISSILE_EFFECT, startx+10, starty+10))
        endif
        
            //other effects
            call TimerStart(NewTimerEx(this), 0.05, false, function thistype.seffect)
            return this
        endmethod
        
    endstruct
    
    private function body takes nothing returns nothing
        local spsteal steal = spsteal.create(GetTriggerUnit(),GetUnitLastCastAbility(GetSpellTargetUnit()), GetSpellTargetUnit())
    endfunction
    
    private function onInit takes nothing returns nothing
        call ExecuteFunc(SCOPE_PRIVATE+"Settings")  //this is still onInit and is to ensure that
                                                    //the function will not crash onInit thread with too many
                                                    //values
        call RegisterSpellEffectEvent(SPELLRC, function body)
    endfunction
endscope

by me(edo494)
GetLastCastAbility
JASS:
library GetLastCastAbility uses Table, RegisterPlayerUnitEvent, UnitIndexer
/*   by edo494 version 1.8d
*
*============================================================================
*
* Requires:
*
* Table by Bribe - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
* RegisterPlayerUnitEvent by Magtheridon96 - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
*
* UnitIndexer by Nestharus - http://www.hiveworkshop.com/forums/jass-resources-412/system-unit-indexer-172090/
*
*============================================================================
*   
*   This library includes: 
*
*       - Allows you to retrieve last cast spells ID(returns integer)
*       - Allows you to retrieve last cast spells target
*       - Allows you to retrieve last cast spells caster
*       - Allows you to retrieve last cast spells X and Y coordiantions
*       - Allows you to retrieve last cast spells ID by specific unit
*       - Allows you to retrieve target of cast spell by specific unit
*       - Allows you to retrieve X and Y coordinates of last cast spell by specific unit
*       - Additionally this allows you to pass unit's index and returns the unit's last cast ability
*
*   This also features "BAN/UNBAN" SPELL which you can add spells into it
*   If units cast those spells it will not get registered.
*
*       This feature can be used for things like Spell Steal for either
*       not registering the spell steal(because you dont want to steal that dont you?)
*       or for spells that you dont want to be spell stealed.
*
*   Also if you want to catch only heros spells, there is texted if in MainBody function which
*   you can use.
* 
*============================================================================
*
*   Issues:
*       You cant register passives for instance Bash because they
*       technically never fire the event Unit - a Unit starts the effect of an ability.
*
*       Also if you cast spell with no target the GetLastCastAbilityX, Y and
*       GetLastCastSpellTarget will return 0, 0, null
*
*============================================================================
*   
*   The API is:
*   
*       function GetLastCastAbility takes nothing returns integer
*       function GetLastCastAbilityTarget takes nothing returns unit
*       function GetLastCastAbilityUnit takes nothing returns unit
*       function GetLastCastAbilityX takes nothing returns real
*       function GetLastCastAbilityY takes nothing returns real
*
*   Based on Unit:
*       
*       function GetUnitLastCastAbility takes unit returns integer
*       function GetUnitLastCastAbilityTarget takes unit returns unit
*       function GetUnitLastCastAbilityX takes unit returns real
*       function GetUnitLastCastAbilityX takes unit returns real
*
*   Based on Unit's index:
*
*       function GetUnitLastCastAbilityById takes integer returns integer
*       function GetUnitLastCastAbilityTargetById takes integer returns unit
*       function GetUnitLastCastAbilityXById takes integer returns real
*       function GetUnitLastCastAbilityXById takes integer returns real
*
*   Ban API:
*
*       function LastAbilityAddFilter takes integer returns nothing
*       function LastAbilityRemoveFilter takes integer returns nothing
*
*============================================================================
*
*   Change Log:
*
*       1.0    - Intial Release
*
*       1.1    - Added BanAbility
*              - Added UnbanAbility
*              - Added Get(Unit)LastCastAbilityX and Y
*              - Changed GetLastCast***what***ByUnit to GetUnitLastCast***what***
*
*       1.2    - Added TempBanAbility function
*              - Removed Get(Unit)LastCastAbility and Get(Unit)LastCastAbilityLoc
*
*       1.3    - Changed AbilityId to Ability
*              - Added support for Table
*
*       1.4    - Changed Size of myTab from 0x1000000(lol this wont work anyways) to 0x200
*
*       1.5    - Resized the Table from 0x200 to 5
*              - Removed TempBan function
*
*       1.6    - Changed Ban/Unban Ability to LastAbilityAddFilter/LastAbilityRemoveFilter
*              - Implemented support for RegisterPlayerUnitEvent Library
*              - changed Scope Initializer to Module Initializer
*
*       1.7    - Added "Deinxeding" when unit dies
*
*       1.7a   - Improved speed a very little bit
*
*       1.8    - Remade the idea of GetUnitLastCastAbility
*
*       1.8a   - Removed Useless stuff
*              - Changed the way how the nulling variables works(Special thanks to Bribe)
*
*       1.8b   - TableArray -> Table (special thanks to Yixx)
*
*       1.8b.1 - Changed how GetLastCast***what*** works(thanks to Magtheridon96)
*              - Removed local unit u from Deindex function
*
*       1.8b.2 - unit lastCastAbilityUnit -> integer lastCastAbilityCaster
*
*       1.8c   - Reorganized the structure of the header
*              - Added GetUnitLastCast ability taking unit's index instead of actual unit
*              - Changed names of textmacroes(added GetLastCastAbility_ to their name)
*
*       1.8d   - removed local integer from mainbody function
*
*============================================================================
*/

globals
    private integer lastCastAbilityCaster = 0
    
    /*
    *   table for Filter/UnFilter
    */
    
    private Table myTab
    
    /*
    *   variables for GetUnitLastCast
    */
    
    private integer array lastCastUnitAbility
    private unit array lastCastUnitTarget
    private real array lastCastUnitX
    private real array lastCastUnitY
endglobals


    //! textmacro GetLastCastAbility_GETLAST takes Name, RSName, RName
        function GetLastCast$Name$ takes nothing returns $RSName$
            return $RName$
        endfunction
    //! endtextmacro
    
    //! runtextmacro GetLastCastAbility_GETLAST("Ability", "integer", "lastCastUnitAbility[lastCastAbilityCaster]")
    //! runtextmacro GetLastCastAbility_GETLAST("AbilityUnit", "unit", "GetUnitById(lastCastAbilityCaster)")
    //! runtextmacro GetLastCastAbility_GETLAST("AbilityTarget", "unit", "lastCastUnitTarget[lastCastAbilityCaster]")
    //! runtextmacro GetLastCastAbility_GETLAST("AbilityX", "real", "lastCastUnitY[lastCastAbilityCaster]")
    //! runtextmacro GetLastCastAbility_GETLAST("AbilityY", "real", "lastCastUnitY[lastCastAbilityCaster]")
    
    
    //! textmacro GetLastCastAbility_GETUNITLAST takes name, return, what
        function GetUnitLastCast$name$ takes unit u returns $return$
            return lastCastUnit$what$[GetUnitUserData(u)]
        endfunction
    //! endtextmacro
    
    //! runtextmacro GetLastCastAbility_GETUNITLAST("Ability", "integer", "Ability")
    //! runtextmacro GetLastCastAbility_GETUNITLAST("X", "real", "X")
    //! runtextmacro GetLastCastAbility_GETUNITLAST("Y", "real", "Y")
    //! runtextmacro GetLastCastAbility_GETUNITLAST("Target", "unit", "Target")
    
    
    //! textmacro GetLastCastAbility_GETUNITLASTBYID takes name, return, what
        function GetUnitLastCast$name$ById takes integer index returns $return$
            return lastCastUnit$what$[index]
        endfunction
    //! endtextmacro
    
    //! runtextmacro GetLastCastAbility_GETUNITLASTBYID("Ability", "integer", "Ability")
    //! runtextmacro GetLastCastAbility_GETUNITLASTBYID("X", "real", "X")
    //! runtextmacro GetLastCastAbility_GETUNITLASTBYID("Y", "real", "Y")
    //! runtextmacro GetLastCastAbility_GETUNITLASTBYID("Target", "unit", "Target")


        function LastAbilityAddFilter takes integer abilcode returns nothing
            set myTab[abilcode] = 1
        endfunction
        
        
        function LastAbilityRemoveFilter takes integer abilcode returns nothing
            set myTab[abilcode] = 0
        endfunction

        
        private function MainBody takes nothing returns nothing
            //if IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) then
                if myTab[GetSpellAbilityId()] != 1 then
                    set lastCastAbilityCaster = GetUnitId(GetTriggerUnit())
                    set lastCastUnitAbility[lastCastAbilityCaster] = GetSpellAbilityId()
                    set lastCastUnitTarget[lastCastAbilityCaster] = GetSpellTargetUnit()
                    set lastCastUnitX[lastCastAbilityCaster] = GetSpellTargetX()
                    set lastCastUnitY[lastCastAbilityCaster] = GetSpellTargetY()
                endif
            //endif
        endfunction

    private module GetLastAbility
        private static method onInit takes nothing returns nothing            
            /*
            *   for ban list
            */
            
            set myTab = Table.create()
            
            /*
            *   Registering the starts the effect of an ability to the function
            */
            
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function MainBody)
        endmethod
    endmodule
    
    private struct GetLast
        implement GetLastAbility
    endstruct
    
//***************************************************************
//***************************************************************

    //When unit dies, null the numbers
    
    private function DiesFunc takes nothing returns boolean
        local integer i = GetIndexedUnitId()
        set lastCastUnitAbility[i] = 0
        set lastCastUnitTarget[i] = null
        set lastCastUnitX[i] = 0
        set lastCastUnitY[i] = 0
        return false
    endfunction

//***************************************************************
    
    private module DiesInit
        private static method onInit takes nothing returns nothing
            call RegisterUnitIndexEvent(Condition(function DiesFunc), UnitIndexer.DEINDEX)
        endmethod
    endmodule

    private struct DIES
        implement DiesInit
    endstruct 
endlibrary




- intial release

- removed trigger from onInit
- nulled timer in decay
- made some documentation
- new configurable: if you want 3 effects as missile or 1
- added new function - Settings to the top for people to configure the variables
- cleared the code of some unneeded function calls
- made a support for SpellEffectEvent




Keywords:
Spell Steal, Steal, vJass, GetLastCastAbility
Contents

Just another Warcraft III map (Map)

Reviews
01:21, 31st Dec 2012 Magtheridon96: Approved. This is a useful resource.

Moderator

M

Moderator

01:21, 31st Dec 2012
Magtheridon96: Approved.
This is a useful resource.
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
Pretty simple, but its usefulness is obvious and the coding style isnt bad. I'd recommend moving the init function to a module up top so you dont have to scroll through the body text to get to the array initialization.

You should also make things like this configurable

JASS:
call DestroyEffect(AddSpecialEffect(missileeffect, startx, starty))
call DestroyEffect(AddSpecialEffect(missileeffect, startx-10, starty-10))
call DestroyEffect(AddSpecialEffect(missileeffect, startx+10, starty+10))

you also have some indetation problems in onInit
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
thanks for the comment
I'll move it to top, thats a great idea(I didnt thought of using module)
its just to make the effect bigger so I made it to 3 effects :D and the effect itself is already configurable

you also have some indetation problems in onInit
I didnt really undestand this, could you expand on that a little bit?

Thanks for the feedback
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
thanks for the comment
I'll move it to top, thats a great idea(I didnt thought of using module)
its just to make the effect bigger so I made it to 3 effects :D and the effect itself is already configurable


I didnt really undestand this, could you expand on that a little bit?

Thanks for the feedback

your array initializations and the function right after them are an extra indent in
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
You don't need to declare a local trigger in the onInit function because you aren't using it.
I think it would be better to have the configuration code at the top of the spell along with some header for documentation. (Docs are always important because some users need importing instructions, others need version numbers and author names, and sometimes, requirements. In this case, a requirement list is a must because you're using a scope instead of a library, so the requirements are not explicit.
I'll add to the previous point by saying that you can have a function called "Settings" used to configure the time and damage arrays.
Constant variables should be NAMED_LIKE_THIS.
Since you have RegisterPlayerUnitEvent as a requirement, I don't think asking for another requirement should be a problem. For spells, I'd recommend Bribe's SpellEffectEvent. All you do is pass in a function along with a raw code and you're done. No need to check if the right spell was casted or not.
You forgot to null the timer t in the decay static method.
You don't need to set the integer/real struct members to 0 upon the ending of a spell instance.
SquareRoot(dx*dx + dy*dy) > 50 -> dx*dx + dy*dy > 2500
You don't need to get the level of the ability inside the 'body' function. You aren't using it.
I facepalmed 2 times

Since you have RegisterPlayerUnitEvent as a requirement, I don't think asking for another requirement should be a problem. For spells, I'd recommend Bribe's SpellEffectEvent. All you do is pass in a function along with a raw code and you're done. No need to check if the right spell was casted or not.
I understood from learning C and C++ that if you try to make your code without libraries you will not get far :D so that is no longer problem for me

so obvious mistakes...
Updated
 
Top