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

Tentacle Attack v1.10

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: LiOneSS
Tentacle Attack
BTNImpale_0.gif
The target gets impaled by a tentacle - multiple other tentacles spawn and attack the victim until the spell is over.
If the victim dies, the tentacles attack random units nearby

Damage: 3 per hit
Range: 800
Type: Active
Cooldown: 8s

*Spell needs JNGP and TimerUtils
*Spell is mui and coded in vjass
*Note: This spell uses a custom value which is applied to ONE unit - the target.

v1.0
-nothing yet

v1.1
-Put the buffid in a constant
-Removed the init-leak (thx ^^)
-Added some timer to check for the target's status to bring it down if it dies


JASS:
//==Tentacle Attack==
//2oo9 by Squiggy
//Spell needs jngp and TimerUtils
//Credits go to HINDYhat for teaching me jass
//The next spell will include methods, yes yes D:

scope TentacleAttack initializer Init

    globals
        private constant integer SPELLID = 'A000'        //This is the spell's id
        private constant string EFFPATH = "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
        private constant string EFFPATH2 = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
        private constant integer DUMMYID = 'n000'        //Here, we aet the dummy WITHOUT the attack which impales the enemy
        private constant integer DUMMYID2 = 'n001'       //Now we set the dummy WITH attack which rapes the target
        private constant integer BUFFID = 'B000'         //And the integer for the buff is set here
        private constant real DURATION = 7               //Here goes the duration of the spell
        private constant real FLYHEIGHT = 350            //Flying height to which the target is changed
        private constant real RANDOM_MIN_X = 0           //Those are the random values for the tentacles' placement
        private constant real RANDOM_MAX_X = 400
        private constant real RANDOM_MIN_Y = 0
        private constant real RANDOM_MAX_Y = 400
        private constant real RANDOM_MIN_ANG = 0
        private constant real RANDOM_MAX_ANG = 360
        private constant real FALLRATE = 4000            //And here's the speed at which the target falls down
    endglobals

    private struct tentacle //We define all values which we need for the second part of the spell
        unit caster
        unit target
        integer i
        real tx
        real ty
        integer index
        player p
    endstruct
    
    private function Conditions takes nothing returns boolean   //First, we check which ability is being cast
        return GetSpellAbilityId() == SPELLID
    endfunction

    private function Conditions2 takes nothing returns boolean
        return GetUnitTypeId(GetTriggerUnit()) == DUMMYID       //This is the condition for the second trigger - it checks the type of the dying unit
    endfunction

    private function grpenu takes nothing returns boolean       //Group filter to your left checking for the buff 'rape'
        return GetUnitAbilityLevel(GetFilterUnit(), BUFFID)>0
    endfunction
    
    private function Tentacles takes nothing returns nothing    //Here we deal with everything aftert the first tentacle is created
        local real rndx
        local real rndy
        local real rnda
        local unit u2
        local tentacle tc = tentacle(GetTimerData(GetExpiredTimer()))   //we get the data
        set rndx = GetRandomReal(RANDOM_MIN_X, RANDOM_MAX_X)            //Now we create the dummy
        set rndy = GetRandomReal(RANDOM_MIN_Y, RANDOM_MAX_Y)
        set rnda = GetRandomReal(RANDOM_MIN_ANG, RANDOM_MAX_ANG)
        set u2 = CreateUnit(tc.p, DUMMYID2, (tc.tx + rndx*Cos((rnda)*bj_DEGTORAD)), tc.ty + rndy*Sin((rnda)*bj_DEGTORAD), rnda)
        call IssueTargetOrder(u2, "attack", tc.target)                  //We order it to attack the target
        call UnitApplyTimedLife(u2, 'BTLF', DURATION)
        call DestroyEffect(AddSpecialEffect(EFFPATH2, GetUnitX(u2), GetUnitY(u2)))            //And add an effect
        set tc.index = tc.index+1                                       //To limit the number of dummies, we set the maximum to lvl*3
        if tc.i == tc.index then
            call ReleaseTimer(GetExpiredTimer())
            set u2 = null
        endif
    endfunction
    
    private function status takes nothing returns nothing
        local tentacle tc = tentacle(GetTimerData(GetExpiredTimer()))
        if GetUnitState(tc.target, UNIT_STATE_LIFE) < 4 then
            call SetUnitUserData(tc.target, GetUnitUserData(tc.target)-1)               //Now as the instance is over, we substract 1 of the custom value
            if GetUnitUserData(tc.target) == 0 then                                     //And if there are no instances running (cv == 0),
                call SetUnitFlyHeight(tc.target, 0, FALLRATE/2)                         //we bring it down
                call tc.destroy()
            endif
            call ReleaseTimer(GetExpiredTimer())
        endif
    endfunction
    
    private function Actions takes nothing returns nothing               //Here's the first part which creates the impaling tentacle
        local unit caster = GetTriggerUnit()
        local unit u
        local real x = GetUnitX(caster)
        local real y = GetUnitY(caster)
        local timer t = NewTimer()
        local timer t2 = NewTimer()
        local tentacle tc = tentacle.create()
        call SetTimerData(t, integer(tc))
        call SetTimerData(t2, integer(tc))
        set tc.target = GetSpellTargetUnit()
        set tc.tx = GetUnitX(tc.target)
        set tc.ty = GetUnitY(tc.target)
        set tc.i = GetUnitAbilityLevel(caster, SPELLID)*3
        set tc.index = 0
        set tc.p = GetOwningPlayer(caster)
        set u = CreateUnit(tc.p, DUMMYID, tc.tx, tc.ty, 90)
        call UnitApplyTimedLife(u, 'BTLF', DURATION+1.2)
        call UnitAddAbility(tc.target, 'Amrf')                           //We add and remove 'crow form' to the target
        call UnitRemoveAbility(tc.target, 'Amrf')
        call SetUnitFlyHeight(tc.target, FLYHEIGHT, FLYHEIGHT*3)                 //And make it raise
        call DestroyEffect(AddSpecialEffectTarget(EFFPATH, tc.target, "origin"))
        call TimerStart(t, 0.2, true, function Tentacles)               //We start both timers to create the tentacles
        call TimerStart(t2, 0.03, true, function status)                 //And to check for the target's health
        call SetUnitAnimation(u, "birth")                                //Now we make the tentacle appear out of the ground and set it's animation speed to 0
        call SetUnitFlyHeight(u, 0, FLYHEIGHT*3)
        call SetUnitTimeScale(u, 0)
        call SetUnitUserData(tc.target, GetUnitUserData(tc.target)+1)    //Adding a custom value will prevent the target from falling dow too soon
        set caster = null
        set u = null
    endfunction
    
    private function Death takes nothing returns nothing                 //The third and last part is to bring the target back to the ground when the first tentacle dies
        local unit u = GetTriggerUnit()
        local unit gru
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        local group g = CreateGroup()
        call SetUnitTimeScale(u, 1)
        call GroupEnumUnitsInRange(g, x, y, 100, Filter(function grpenu))   //We pick the nearby target which still has the buff 'rape'
        loop
            set gru = FirstOfGroup(g)
            exitwhen gru == null
            call SetUnitUserData(gru, GetUnitUserData(gru)-1)               //Now as the instance is over, we substract 1 of the custom value
            if GetUnitUserData(gru) == 0 then                               //And if there are no instances running (cv == 0),
                call SetUnitFlyHeight(gru, 0, FALLRATE)                     //we bring it down
            endif
            call GroupRemoveUnit(g, gru)
        endloop
        call DestroyGroup(g)
        set u = null
    endfunction
    
    private function AntiLeak takes nothing returns boolean
        return true
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local trigger t2 = CreateTrigger()
        local filterfunc f = Filter(function AntiLeak)
        local integer i = 0
        loop
            exitwhen i >= 16
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, f)
            call TriggerRegisterPlayerUnitEvent(t2, Player(i), EVENT_PLAYER_UNIT_DEATH, f)
            set i = i + 1
        endloop
        call TriggerAddCondition(t, Condition(function Conditions))
        call TriggerAddCondition(t2, Condition(function Conditions2))
        call TriggerAddAction(t, function Actions)
        call TriggerAddAction(t2, function Death)
        call DestroyFilter(f)
        set f = null
    endfunction
endscope

Keywords:
tentacle, attack, blood, jass, vjass, squiggy, epic, lulz
Contents

Tentacle Attack (Map)

Reviews
17:43, 24th Sep 2009 Hanky: You need to update the map since it's not working with 1.24b right now.

Moderator

M

Moderator

17:43, 24th Sep 2009
Hanky:
You need to update the map since it's not working with 1.24b right now.
 
Level 17
Joined
Mar 17, 2009
Messages
1,349
Well, I don't like tentacle spells since they tend to get crowded... the triggering is all well, but since it's well done, I guess this deserves to be done:
JASS:
    private function AntiLeak takes nothing returns boolean
        return true
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local trigger t2 = CreateTrigger()
        local filterfunc f = Filter(function AntiLeak)
        local integer i = 0
        loop
            call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, f)
            call TriggerRegisterPlayerUnitEvent(t2, Player(i), EVENT_PLAYER_UNIT_DEATH, f)
            set i = i + 1
            exitwhen i >= 16
        endloop
        call TriggerAddCondition(t, Condition(function Conditions))
        call TriggerAddCondition(t2, Condition(function Conditions2))
        call TriggerAddAction(t, function Actions)
        call TriggerAddAction(t2, function Death)
        call DestroyFilter(f)
        set f = null
    endfunction

I know 32 leaks are nothing, but they're easy to remove :p

Umm, I saw you say you will include methods in next versions, no need to do that if everything functions correctly.

Keep it up Squig...
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
Tested the spell and it looks nice, the idea is also nice and the impale effect is great.
Just dunno why or how the fire hapens in this spell :p
Still you got a strange aproach to spells(i personaly don't like too much effects) which is unique to you.
All in all a good spell which deserves a recommeneded rating.
Possible improvements:
  • replace the looping through the group with ForGroup() function
  • make the buff ID a constant
  • prevent the stacking of ability effect(buff check in the condition maybe?) as i don't really see this spell being able to stack
  • maybe make the tentacles smaller? currently their size is rather big
  • set the conditionfunc you use for GroupEnumUnitsInRange() into a variable so you can destroy it afterwards

These are rather minor improvements/suggestions that i hope helps you.
Gj and keep it up!
 
Level 28
Joined
Mar 25, 2008
Messages
2,955
prevent the stacking of ability effect(buff check in the condition maybe?) as i don't really see this spell being able to stack

That is actually wanted - if it wouldn't stack plus the ability having a cooldown of three seconds, the target wouldn't get impaled by the second instance if casted multiple times on the same unit.

maybe make the tentacles smaller? currently their size is rather big

I tried with scale 1.0.. what happened?
http://www.hiveworkshop.com/forums/...height-how-the-effff-is-this-possible-134858/

So I just enlarged them and left the height as-is.

Might be true that I forgot to set the buffid to a constant - I'll make an update soon, anyway ^.^
 
Level 10
Joined
Aug 19, 2008
Messages
491
A unit dies first when it reaches 0.405 hit points. Don't ask me how Blizzard designed that, but that's how it works. This is very useful because dead units can have more than 0.0 hit points.
And you should be careful too, if a dead unit is healed with triggers it will still gain hit points, which can exceed 0.405 (yes, dead units can have 1000 hit points, but it will still be dead and decay).

I also suggest you replace GetUnitState() with GetWidgetLife() since it's more reliable and faster (you skip that state argument too)
 
Top