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

Fan of Toxic Knives v1.2

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Fan of Toxic Knives
Made by KhaosMachine

Tooltip

qHIYQ.png
(Q)Fan of Toxic Knives - [Level: 1/2/3/4]

The hero flings a flurry of toxic knives at multiple targets around of he.

Level 1 - Deals 120 damage and slows attack speedy by 20% and movement speed by 14% last for 4 seconds.
Level 2 - Deals 150 damage and slows attack speedy by 28% and movement speed by 28% last for 4 seconds.
Level 3 - Deals 180 damage and slows attack speedy by 36% and movement speed by 42% last for 4 seconds.
Level 4 - Deals 210 damage and slows attack speedy by 44% and movement speed by 56%last for 4 seconds.

Cooldown: 30/24/18/12 seconds
Area of effect: 300/380/460/540
Mana cost: 70/80/90/100

Screenshots

fnA8q.png

Vx2wM.png

sLNe9.png


vJASS code

JASS:
scope fanOfToxicKnives initializer init

//****************************************************
//START OF THE CONFIGURATION
//****************************************************

globals
    constant integer abilityId = 'A000'//Hero ability rawcode
    constant integer abilityId2 = 'A001'//Slow ability rawcode
    constant string slowOrder = "slow"//Slow ability order
    constant string missileModel = "Abilities\\Weapons\\WardenMissile\\WardenMissile.mdl"//Missile model
    constant integer unitType = 'u001'//Unit type of the missile and caster
    constant real interval = 0.03125//Interval of the spell (0.03125 = 32FPS)
    constant damagetype damageType = DAMAGE_TYPE_NORMAL//Damage type of the deal
    constant attacktype attackType = ATTACK_TYPE_NORMAL//Attack type of the deal
    constant weapontype weaponType = WEAPON_TYPE_WHOKNOWS//Weapon type of the deal
    real missileScale = 1.9//Scale of the missile
    integer colorRed = 120//Red color of the missile
    integer colorGreen = 255//Green color of the missile
    integer colorBlue = 0//Blue color of the missile
    real speed = 1200//Movement speed of the missile
endglobals

private function damageDealed takes integer level returns real
    return 90. + 30. * level//Damage dealed each level
endfunction

private function areaOfEffect takes integer level returns real
    return 220. + 80. * level//Area of effect each level
endfunction

//****************************************************
//END OF THE CONFIGURATION
//****************************************************

struct knive
    unit dealer
    unit target
    unit dummy
    boolean quit
    integer level
    effect eff
endstruct

globals
    timer Tim = CreateTimer()
    knive array Ar
    integer Total = 0
endglobals

private function action takes nothing returns nothing
    local knive kn
    local integer i = 0
    local real x
    local real y
    local real x1
    local real y1
    local real angle
    local unit caster
    loop
    exitwhen i >= Total
        set kn = Ar[i]
        set x = GetUnitX(kn.dummy)
        set y = GetUnitY(kn.dummy)
        set x1 = GetUnitX(kn.target)
        set y1 = GetUnitY(kn.target)
        set angle = bj_RADTODEG * Atan2(y1 - y, x1 - x)
        call SetUnitPosition(kn.dummy, x + speed * interval * Cos(bj_DEGTORAD * angle), y + speed * interval * Sin(bj_DEGTORAD * angle))
        call SetUnitFacing(kn.dummy, angle)
        set kn.quit = IsUnitType(kn.target, UNIT_TYPE_DEAD) or IsUnit(kn.target, null)
        if SquareRoot((x1 - x) * (x1 - x) + (y1 - y) * (y1 - y)) < 50 then
            call UnitDamageTarget(kn.dealer, kn.target, damageDealed(kn.level), true, false, attackType, damageType, weaponType)
            set kn.quit = true
        endif
        if kn.quit then
            call SetUnitExploded(kn.dummy, true)
            call KillUnit(kn.dummy)
            call DestroyEffect(kn.eff)
            set caster = CreateUnit(GetOwningPlayer(kn.dealer), unitType, x1, y1, 0)
            call UnitAddAbility(caster, abilityId2)
            call SetUnitAbilityLevel(caster, abilityId2, kn.level)
            call IssueTargetOrder(caster, slowOrder, kn.target)
            call UnitApplyTimedLife(caster, 'BTLF', 0.5)
            set caster = null
            set Ar[i] = Ar[Total - 1]
            set Total = Total - 1
            call kn.destroy()
            set caster = null
        endif
        set i = i + 1
    endloop
    if Total == 0 then
        call PauseTimer(Tim)
    endif
endfunction

private function run takes nothing returns boolean
    local knive kn
    local unit caster
    local unit picked
    local real x
    local real y
    local real x1
    local real y1
    local real angle
    local player pla
    local integer level
    if GetSpellAbilityId() == abilityId then
        set caster = GetTriggerUnit()
        set pla = GetOwningPlayer(caster)
        set level = GetUnitAbilityLevel(caster, abilityId)
        set x = GetUnitX(caster)
        set y = GetUnitY(caster)
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, areaOfEffect(level), null)
        loop
            set picked = FirstOfGroup(bj_lastCreatedGroup)
        exitwhen picked == null
            call GroupRemoveUnit(bj_lastCreatedGroup, picked)
            if IsUnitEnemy(picked, pla) and not IsUnitType(picked, UNIT_TYPE_DEAD) and not IsUnitType(picked, UNIT_TYPE_STRUCTURE) and not IsUnitType(picked, UNIT_TYPE_MAGIC_IMMUNE) then
                set kn = knive.create()
                set x1 = GetUnitX(picked)
                set y1 = GetUnitY(picked)
                set angle = bj_RADTODEG * Atan2(y1 - y, x1 - x)
                set kn.dealer = caster
                set kn.target = picked
                set kn.level = level
                set kn.dummy = CreateUnit(pla, unitType, x + 50 * Cos(bj_DEGTORAD * angle), y + 50 * Sin(bj_DEGTORAD * angle), angle)
                set kn.eff = AddSpecialEffectTarget(missileModel, kn.dummy, "origin")
                set kn.quit = false
                call SetUnitScale(kn.dummy, missileScale, missileScale, missileScale)
                call SetUnitVertexColor(kn.dummy, colorRed, colorGreen, colorBlue, 255)
                if Total == 0 then
                    call TimerStart(Tim, interval, true, function action)
                endif
                set Total = Total + 1
                set Ar[Total - 1] = kn
            endif
        endloop
        set caster = null
    endif
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer index = 0
    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(index), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set index = index + 1
        exitwhen index == bj_MAX_PLAYER_SLOTS
    endloop
    call TriggerAddCondition(t, Condition(function run))
    set bj_lastCreatedUnit = CreateUnit(Player(0), unitType, 0, 0, 0)
    call UnitAddAbility(bj_lastCreatedUnit, abilityId)
    call UnitAddAbility(bj_lastCreatedUnit, abilityId2)
    call RemoveUnit(bj_lastCreatedUnit)
    set bj_lastCreatedUnit = null
    if missileScale > 3 then
        set missileScale = 3
    elseif missileScale < 0.4 then
        set missileScale = 0.4
    endif
    if colorRed > 255 then
        set colorRed = 255
    elseif colorRed < 0 then
        set colorRed = 0
    endif
    if colorGreen > 255 then
        set colorGreen = 255
    elseif colorGreen < 0 then
        set colorGreen = 0
    endif
    if colorBlue > 255 then
        set colorBlue = 255
    elseif colorBlue < 0 then
        set colorBlue = 0
    endif
    if speed > 1800 then
        set speed = 1800
    elseif speed < 50 then
        set speed = 50
    endif
    set t = null
endfunction

endscope

Credits and some info

Credits:

I made this spell because I love make spells, haha :p. This is my first vJASS spell, sorry for the errors or bugs if it have any!



Keywords:
fan of knives, fan of toxic knives, of knives, of toxic, toxic knives, fan of, glaives, tower, hiveworkshop, spell, vjass spell, simple buy lovely, i
Contents

Fan of Toxic Knives (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 17th Jul 2012 Bribe: Overall could use a few important changes, then I feel it is ready. "struct knive" must be private. The following globals must also be private: globals...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

17th Jul 2012
Bribe: Overall could use a few important changes, then I feel it is ready.

"struct knive" must be private.

The following globals must also be private:

JASS:
globals
    timer Tim = CreateTimer()
    knive array Ar
    integer Total = 0
endglobals

Please don't inline TriggerRegisterAnyUnitEventBJ, it is quite useless to do it. Also, IsUnit is useless compared to unit == other_unit
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
- add prefixes to your globals, it may conflict global names in other scripts
- put this 255 and 0 and alike into global constants
- you may want to consider to use SpellEffectEvent by Bribe in the jass resource section
to replace the first 8 lines in the 'init' function
- GetOwningPlayer(caster) >>> GetTriggerPlayer() is better
- why are you converting the angle to degrees then convert it back again to radians in the in the CreateUnit part?
- SquareRoot is really slow, just remove that and make the 50 into 50*50 or 2500
- you dont need kn.quit, just put a condition directly in the action function
 
Nice job, I'll review the code a bit.

1. "Run" function:
  • bj_lastCreatedGroup - Some GUIers will use this and it may end up returning null. I recommend you use one global group for the spell rather than bj_lastCreatedGroup to avoid any possible bugs.
  • JASS:
    set x1 = GetUnitX(picked)
    set y1 = GetUnitY(picked)
    set angle = bj_RADTODEG * Atan2(y1 - y, x1 - x)
    You don't really need x1 and y1 since you only use it once within that loop. You can remove the local declarations and change that portion to become:
    JASS:
    set angle = bj_RADTODEG * Atan2(GetUnitY(picked) - y, GetUnitX(picked) - x)
  • JASS:
    set angle = bj_RADTODEG *...
    //... 
    set kn.dummy = CreateUnit(pla, unitType, x + 50 * Cos(bj_DEGTORAD * angle)...
    On those lines, you are converting the angle to degrees and then back to radians for the input. Instead, you can leave it as radians (remove the bj_RADTODEG *) and then remove the conversion to radians again (remove the bj_DEGTORAD *). When you use it for the facing angle, just make it angle*bj_RADTODEG directly.

2. "Action" function:
  • bj_RADTODEG... same thing as mentioned for the other function.
  • SquareRoot((x1 - x) * (x1 - x) + (y1 - y) * (y1 - y)) < 50
    You can square both sides for it to become:
    (x1-x)*(x1-x) + (y1-y)*(y1-y) < 2500 //50 * 50 = 2500
    The SquareRoot function is somewhat slow.
  • kn.quit can be a local variable in this function. It doesn't have to be a struct member.
  • set caster = null You only need to do this once at the end of the function.

3. Additional/Misc:
  • Make all your globals and the struct private.

Good job overall. :)
 
Level 8
Joined
Dec 30, 2011
Messages
134
1. "Run" function:
  • bj_lastCreatedGroup - Some GUIers will use this and it may end up returning null. I recommend you use one global group for the spell rather than bj_lastCreatedGroup to avoid any possible bugs.
  • JASS:
    set x1 = GetUnitX(picked)
    set y1 = GetUnitY(picked)
    set angle = bj_RADTODEG * Atan2(y1 - y, x1 - x)
    You don't really need x1 and y1 since you only use it once within that loop. You can remove the local declarations and change that portion to become:
    JASS:
    set angle = bj_RADTODEG * Atan2(GetUnitY(picked) - y, GetUnitX(picked) - x)
  • JASS:
    set angle = bj_RADTODEG *...
    //... 
    set kn.dummy = CreateUnit(pla, unitType, x + 50 * Cos(bj_DEGTORAD * angle)...
    On those lines, you are converting the angle to degrees and then back to radians for the input. Instead, you can leave it as radians (remove the bj_RADTODEG *) and then remove the conversion to radians again (remove the bj_DEGTORAD *). When you use it for the facing angle, just make it angle*bj_RADTODEG directly.


Thanks for your suggestions man!, but I have a problem the angle works only with this:

JASS:
        set angle = bj_RADTODEG * Atan2(y1 - y, x1 - x)
        call SetUnitPosition(kn.dummy, x + speed * interval * Cos(bj_DEGTORAD * angle), y + speed * interval * Sin(bj_DEGTORAD * angle))

Can you make the change that you said me? and give me it? :D please

OHH AND I see your LUA tutorials, can I make a custom dummy into the trigger to use it without the object editor?!? :vw_wtf:

THANKS
 
No prob. :)

For the function "run":
JASS:
set angle = Atan2(GetUnitY(picked) - y, GetUnitX(picked) - x)
set kn.dealer = caster
set kn.target = picked
set kn.level = level
set kn.dummy = CreateUnit(pla, unitType, x + 50 * Cos(angle), y + 50 * Sin(angle), angle * bj_RADTODEG)

For the function "action":
JASS:
set angle = Atan2(y1 - y, x1 - x)
call SetUnitPosition(kn.dummy, x + speed * interval * Cos(angle), y + speed * interval * Sin(angle))
call SetUnitFacing(kn.dummy, angle * bj_RADTODEG)

As for the question about lua, yes you can do that for the dummy unit. But some people don't like it because you have to close the map and reopen it or it may be confusing for GUI people if they are used to just copying from the object editor. However, it doesn't harm to add the lua version as an option. :)
 
Top