• 🏆 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] Hook. No hooking, but stunning (JASS)

Status
Not open for further replies.
Level 7
Joined
Feb 9, 2021
Messages
301
Hello everyone,

I had been struggling for the last couple of days to create a spell. I just started learning JASS. Description of the spell: The concept of it similar to the hook from Dota. However, the main differences are:
1. The spell is channelled by the caster, and therefore it can be cancelled.
2. Once the target is hit, it gets stunned over 3 seconds, gets DPS and the hook remains in place for this time
3. After the stun is finished, the hook returns to the caster
4. If the caster cancels channelling, the stun stops and the hook returns to the caster (without the target). In case there is no target and the caster cancels channelling, the hook just instantly returns to the caster.

I tried to find a hook made in Jass on the website but found only GUI versions or very complicated vJass. I would really appreciate if someone can point me toward the mistakes I am making. I attached my spell to the thread.

I would post the code as well, but I am not sure how to add it in the right format.
 

Attachments

  • Mammon.w3x
    112 KB · Views: 25
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,850
Ok then:
  1. How To Post Your Trigger
  2. I will tell you some things I noticed
First in this part:
JASS:
call CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
call GroupAddUnitSimple(GetLastCreatedUnit(), TG_Effects_Group[TG_Loop_Int] )
call SaveUnitHandleBJ(GetLastCreatedUnit(), TG_Counter[TG_Loop_Int], TG_Loop_Int, Hash)
You are using GetLastCreatedUnit() after the function CreateUnit but this function doesn't asing the value GetLastCreatedUnit() that just only do the functions CreateUnitAtLocSaveLast, CreateNUnitsAtLoc and CreateNUnitsAtLocFacingLocBJ
Instead do:
JASS:
set u=CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
call GroupAddUnit(TG_Effects_Group[TG_Loop_Int], u)
call SaveUnitHandle(Hash, TG_Loop_Int, TG_Counter[TG_Loop_Int], u)

Second:
Your are using set bj_wantDestroyGroup = true but that only works with this functions: ForGroupBJ, GroupAddGroup, GroupRemoveGroup, IsUnitGroupDeadBJ, IsUnitGroupEmptyBJ and GroupPickRandomUnit, and you don't use any of these functions after doing this and that can do things like erase a group that you don't wanna erase.

Third, some some advices:
  1. Is unnecesary do this thing
    JASS:
    if Condition == true then
    in this case you can simply do
    JASS:
    if Condition then
    , same for this
    JASS:
    if Condition == false then
    just do:
    JASS:
    if not Condition then
  2. Try to avoid some unnecesary BJ functions like GroupAddUnitSimple(GetLastCreatedUnit(), TG_Effects_Group[TG_Loop_Int]) because they are just unnecessary extra steps, use instead GroupAddUnit(TG_Effects_Group[TG_Loop_Int], bj_lastCreatedUnit)
  3. Why you do this
    JASS:
    set TG_Angel[TG_Index] = bj_RADTODEG * Atan2(yTarget - yCaster, xTarget - xCaster)
    if then you will do (Convert the angle from radians to degrees and returning it back to radians)
    JASS:
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 50 * Cos(TG_Angel[TG_Index] * bj_DEGTORAD), yCaster + 50 * Sin(TG_Angel[TG_Index] * bj_DEGTORAD), TG_Angel[TG_Index])
    simply do
    JASS:
    set TG_Angel[TG_Index] = Atan2(yTarget - yCaster, xTarget - xCaster)
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 50 * Cos(TG_Angel[TG_Index] ), yCaster + 50 * Sin(TG_Angel[TG_Index] ), TG_Angel[TG_Index])
  4. If you have TESH editor you can see how works the BJ functions (written in red).
 
Last edited:
Level 7
Joined
Feb 9, 2021
Messages
301
Ok then:
  1. How To Post Your Trigger
  2. I will tell you some things I noticed
First in this part:
JASS:
call CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
call GroupAddUnitSimple(GetLastCreatedUnit(), TG_Effects_Group[TG_Loop_Int] )
call SaveUnitHandleBJ(GetLastCreatedUnit(), TG_Counter[TG_Loop_Int], TG_Loop_Int, Hash)
You are using GetLastCreatedUnit() after the function CreateUnit but this function doesn't asing the value GetLastCreatedUnit() that just only do the functions CreateUnitAtLocSaveLast, CreateNUnitsAtLoc and CreateNUnitsAtLocFacingLocBJ
Instead do:
JASS:
set u=CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
call GroupAddUnit(TG_Effects_Group[TG_Loop_Int], u)
call SaveUnitHandle(Hash, TG_Loop_Int, TG_Counter[TG_Loop_Int], u)

Second:
Your are using set bj_wantDestroyGroup = true but that only works with this functions: ForGroupBJ, GroupAddGroup, GroupRemoveGroup, IsUnitGroupDeadBJ, IsUnitGroupEmptyBJ and GroupPickRandomUnit, and you don't use any of these functions after doing this and that can do things like erase a group that you don't wanna erase.

Third, some some advices:
  1. Is unnecesary do this thing
    JASS:
    if Condition == true then
    in this case you can simply do
    JASS:
    if Condition then
    , same for this
    JASS:
    if Condition == false then
    just do:
    JASS:
    if not Condition then
  2. Try to avoid some unnecesary BJ functions like GroupAddUnitSimple(GetLastCreatedUnit(), TG_Effects_Group[TG_Loop_Int]) because they are just unnecessary extra steps, use instead GroupAddUnit(TG_Effects_Group[TG_Loop_Int], bj_lastCreatedUnit)
  3. Why you do this
    JASS:
    set TG_Angel[TG_Index] = bj_RADTODEG * Atan2(yTarget - yCaster, xTarget - xCaster)
    if then you will do (Convert the angle from radians to degrees and returning it back to radians)
    JASS:
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 50 * Cos(TG_Angel[TG_Index] * bj_DEGTORAD), yCaster + 50 * Sin(TG_Angel[TG_Index] * bj_DEGTORAD), TG_Angel[TG_Index])
    simply do
    JASS:
    set TG_Angel[TG_Index] = Atan2(yTarget - yCaster, xTarget - xCaster)
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 50 * Cos(TG_Angel[TG_Index] ), yCaster + 50 * Sin(TG_Angel[TG_Index] ), TG_Angel[TG_Index])
  4. If you have TESH editor you can see how works the BJ functions (written in red).

Thank you very much. I will take these suggestions into account for my current and future spells. I will update the thread after my implementations.
 
Level 7
Joined
Feb 9, 2021
Messages
301
Ok then:
  1. How To Post Your Trigger
  2. I will tell you some things I noticed
First in this part:
JASS:
call CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
call GroupAddUnitSimple(GetLastCreatedUnit(), TG_Effects_Group[TG_Loop_Int] )
call SaveUnitHandleBJ(GetLastCreatedUnit(), TG_Counter[TG_Loop_Int], TG_Loop_Int, Hash)
You are using GetLastCreatedUnit() after the function CreateUnit but this function doesn't asing the value GetLastCreatedUnit() that just only do the functions CreateUnitAtLocSaveLast, CreateNUnitsAtLoc and CreateNUnitsAtLocFacingLocBJ
Instead do:
JASS:
set u=CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
call GroupAddUnit(TG_Effects_Group[TG_Loop_Int], u)
call SaveUnitHandle(Hash, TG_Loop_Int, TG_Counter[TG_Loop_Int], u)

Second:
Your are using set bj_wantDestroyGroup = true but that only works with this functions: ForGroupBJ, GroupAddGroup, GroupRemoveGroup, IsUnitGroupDeadBJ, IsUnitGroupEmptyBJ and GroupPickRandomUnit, and you don't use any of these functions after doing this and that can do things like erase a group that you don't wanna erase.

Third, some some advices:
  1. Is unnecesary do this thing
    JASS:
    if Condition == true then
    in this case you can simply do
    JASS:
    if Condition then
    , same for this
    JASS:
    if Condition == false then
    just do:
    JASS:
    if not Condition then
  2. Try to avoid some unnecesary BJ functions like GroupAddUnitSimple(GetLastCreatedUnit(), TG_Effects_Group[TG_Loop_Int]) because they are just unnecessary extra steps, use instead GroupAddUnit(TG_Effects_Group[TG_Loop_Int], bj_lastCreatedUnit)
  3. Why you do this
    JASS:
    set TG_Angel[TG_Index] = bj_RADTODEG * Atan2(yTarget - yCaster, xTarget - xCaster)
    if then you will do (Convert the angle from radians to degrees and returning it back to radians)
    JASS:
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 50 * Cos(TG_Angel[TG_Index] * bj_DEGTORAD), yCaster + 50 * Sin(TG_Angel[TG_Index] * bj_DEGTORAD), TG_Angel[TG_Index])
    simply do
    JASS:
    set TG_Angel[TG_Index] = Atan2(yTarget - yCaster, xTarget - xCaster)
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 50 * Cos(TG_Angel[TG_Index] ), yCaster + 50 * Sin(TG_Angel[TG_Index] ), TG_Angel[TG_Index])
  4. If you have TESH editor you can see how works the BJ functions (written in red).


On Third, I get an error if I don't put == true or == false. For example, elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("charm") and TG_Unit_Is_Grappled[TG_Loop_Int] then gives me an error.

On movement, it seems like I don't understand it very well. Is there an article you can recommend? How do I create a dummy at 50 range away from the caster with the caster's angle? I used this from one of the lesson's I watched, and it seemed to work with my previous spells. However, here it creates a unit on the left further away than if it is casted on the right.

Finally, I can't make spell to work for some reason.
 
Level 7
Joined
Feb 9, 2021
Messages
301
Here is my code.

JASS:
globals
    integer TG_Index = 0
    integer array TG_Stun_Index
    unit array TG_Caster
    unit array TG_Caster_Stun
    unit array TG_Dummy
    unit array TG_Dummy_Effect
    unit array TG_Grappled_U
    unit array TG_Target
    unit array TG_Stun_Dummy
    unit TG_Effect
    real array TG_Angel
    real TG_Dmg
    real TG_Dist
    real TG_Speed
    real TG_Aoe
    real TG_Stun_Max_Dur
    real array TG_Process
    real array TG_Stun_Dur
    integer array TG_Counter
    timer TG_Timer = CreateTimer()
    timer TG_Stun_Timer = CreateTimer()
    integer TG_Loop_Int = 0
    group TG_Group
    group array TG_Effects_Group
    boolean array TG_Unit_Is_Grappled
    
    
endglobals

function Trig_Tentacle_Grapple_ActionsV3 takes nothing returns nothing
    loop
        exitwhen TG_Stun_Index[2] > TG_Stun_Index[1]
        set TG_Stun_Dur[TG_Stun_Index[2]] = TG_Stun_Dur[TG_Stun_Index[2]] + 0.03
        if TG_Stun_Dur[TG_Stun_Index[2]] < TG_Stun_Max_Dur and UnitHasBuffBJ(TG_Target[TG_Stun_Index[2]], 'B00C') == true then
            call UnitDamageTarget(TG_Caster_Stun[TG_Stun_Index[2]], TG_Target[TG_Stun_Index[2]], TG_Dmg, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
        else
            call KillUnit(TG_Stun_Dummy[TG_Stun_Index[2]])
            
            set TG_Caster_Stun[TG_Stun_Index[2]] = TG_Caster_Stun[TG_Stun_Index[1]]
            set TG_Stun_Dur[TG_Stun_Index[2]] = TG_Stun_Dur[TG_Stun_Index[1]]
            set TG_Target[TG_Stun_Index[2]] = TG_Target[TG_Stun_Index[1]]
            
            set TG_Stun_Index[1] = TG_Stun_Index[1] - 1
            set TG_Stun_Index[2] = TG_Stun_Index[2] - 1
        endif
        set TG_Stun_Index[2] = TG_Stun_Index[2] + 1
        
        if TG_Stun_Index[1] == 0 then
            call PauseTimer(TG_Stun_Timer)
            call DestroyTimer(TG_Stun_Timer)
        endif
        
    endloop
endfunction

function Trig_Tentacle_Grapple_ActionsV2 takes nothing returns nothing
    set TG_Loop_Int = 1
    loop
        exitwhen TG_Loop_Int > TG_Index
          
        set TG_Process[TG_Loop_Int] = TG_Process[TG_Loop_Int] + TG_Speed
            
        if TG_Process[TG_Loop_Int] > TG_Dist or TG_Unit_Is_Grappled[TG_Loop_Int] == true and UnitHasBuffBJ(TG_Target[TG_Loop_Int], 'B00C') == false or GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("charm") and TG_Unit_Is_Grappled[TG_Loop_Int] == false then
            //after the stun finished, return the dummy and delete Effects 
                
            call BJDebugMsg("Grapple Check")
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] - 1
                
            call IssueImmediateOrder(TG_Caster[TG_Loop_Int], "stop")
                
            set TG_Effect = LoadUnitHandleBJ((TG_Counter[TG_Loop_Int] + 1 ), TG_Loop_Int, Hash)
            call KillUnit(TG_Effect)
                
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos(TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD)
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin(TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD)
                
            if TG_Counter[TG_Loop_Int] == 0 then
                call KillUnit(TG_Dummy[TG_Loop_Int])
                    
                call ResetUnitAnimation( TG_Caster[TG_Loop_Int] )
                call DestroyGroup(TG_Effects_Group[TG_Loop_Int])
                    
                set TG_Caster[TG_Loop_Int] = TG_Caster[TG_Index]
                set TG_Dummy[TG_Loop_Int] = TG_Dummy[TG_Index]
                set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
                    
                    
                set TG_Index = TG_Index - 1
                set TG_Loop_Int = TG_Loop_Int - 1
            endif
                
                
        elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("charm") and TG_Unit_Is_Grappled[TG_Loop_Int] == true then
            call UnitRemoveBuffBJ( 'B00C', TG_Target[TG_Loop_Int] )
        else
                //Move Dummy and Create Effects
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] + 1
                
            set TG_Dummy_Effect[TG_Loop_Int] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
            call GroupAddUnit(TG_Effects_Group[TG_Loop_Int], TG_Dummy_Effect[TG_Loop_Int])
            call SaveUnitHandle(Hash, TG_Loop_Int, TG_Counter[TG_Loop_Int], TG_Dummy_Effect[TG_Loop_Int])
                
                //call BJDebugMsg("Grapple Move Check")
                
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos(TG_Angel[TG_Loop_Int]) * bj_DEGTORAD)
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin(TG_Angel[TG_Loop_Int]) * bj_DEGTORAD)
            
                
                //Pick Units Around Dummy
                
            call GroupEnumUnitsInRange(TG_Group, GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Aoe, null)
            loop
                set TG_Target[TG_Loop_Int] = FirstOfGroup(TG_Group)
                exitwhen TG_Target[TG_Loop_Int] == null
                if GetWidgetLife(TG_Target[TG_Loop_Int]) > 0.405 and not IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(TG_Target[TG_Loop_Int], GetOwningPlayer(TG_Caster[TG_Index])) and IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_STRUCTURE) == false then
                    set TG_Unit_Is_Grappled[TG_Loop_Int] = true
                        
                    set TG_Stun_Index[1] = TG_Stun_Index[1] + 1
                    set TG_Caster_Stun[TG_Stun_Index[1]] = TG_Caster[TG_Loop_Int]
                    set TG_Target[TG_Stun_Index[1]] = TG_Target[TG_Loop_Int]
                        
                    set TG_Stun_Dummy[TG_Stun_Index[1]] = CreateUnit(GetOwningPlayer(TG_Caster_Stun[TG_Stun_Index[1]]), 'h001', GetUnitX(TG_Caster_Stun[TG_Stun_Index[1]]), GetUnitY(TG_Caster_Stun[TG_Stun_Index[1]]), 270)
                    call UnitAddAbilityBJ( 'A00K', TG_Stun_Dummy[TG_Stun_Index[1]])
                    call IssueTargetOrder(TG_Stun_Dummy[TG_Stun_Index[1]], "thunderbolt", TG_Target[TG_Stun_Index[1]])
                        
                    if TG_Stun_Index[1] == 1 then
                        call TimerStart(TG_Stun_Timer, 0.03, true, function Trig_Tentacle_Grapple_ActionsV3)
                    endif
                endif
                call GroupRemoveUnit(TG_Group, TG_Target[TG_Loop_Int])
            endloop
        endif
            
        set TG_Loop_Int = TG_Loop_Int + 1
    endloop
    

endfunction


function Trig_Tentacle_Grapple_Actions takes nothing returns nothing
    set TG_Index = (TG_Index + 1)
    set TG_Caster[TG_Index] = GetTriggerUnit()
    
    local real xCaster = GetUnitX(TG_Caster[TG_Index])
    local real yCaster = GetUnitY(TG_Caster[TG_Index])
    local real xTarget = GetSpellTargetX()
    local real yTarget = GetSpellTargetY()
    
    set TG_Angel[TG_Index] =  bj_RADTODEG * Atan2(yTarget - yCaster, xTarget - xCaster)
    
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 50 * Cos(TG_Angel[TG_Index] * bj_DEGTORAD), yCaster + 50 * Sin(TG_Angel[TG_Index] * bj_DEGTORAD), TG_Angel[TG_Index])
    call SetUnitPathing(TG_Dummy[TG_Index], false)
    //Animation, Sounds
    call SetUnitAnimationByIndex(TG_Caster[TG_Index], 5)
    
    //SetUp
    set TG_Stun_Max_Dur = 3.0
    set TG_Dmg = 100 * 0.03 / TG_Stun_Max_Dur
    set TG_Dist = 1000
    set TG_Speed = 200 * 0.3125
    set TG_Aoe = 70

    
    //Addtional Settings
    set TG_Process[TG_Index] = 0
    set TG_Counter[TG_Index] = 0
    set TG_Unit_Is_Grappled[TG_Index] = false
    set TG_Grappled_U[TG_Index] = null
    set TG_Group = CreateGroup()

    
    //Timer
    if TG_Index == 1 then
        call TimerStart(TG_Timer, 0.03125, true, function Trig_Tentacle_Grapple_ActionsV2)
    endif
    
endfunction


function Trig_Tentacle_Grapple_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00J'
endfunction



//===========================================================================
function InitTrig_Tentacle_Grapple takes nothing returns nothing
    set gg_trg_Tentacle_Grapple = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Tentacle_Grapple, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Tentacle_Grapple, Condition( function Trig_Tentacle_Grapple_Conditions ) )
    call TriggerAddAction( gg_trg_Tentacle_Grapple, function Trig_Tentacle_Grapple_Actions )
endfunction
 
Level 24
Joined
Jun 26, 2020
Messages
1,850
On Third, I get an error if I don't put == true or == false. For example, elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("charm") and TG_Unit_Is_Grappled[TG_Loop_Int] then gives me an error.
Can you show the script that causes that error?

And I noticed you did Cos(TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD, is incorrect, * bj_DEGTORAD must be inside the cosine or sine function like this Cos((TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD), in the Atan2 function is outside the conversion because is the inverse function (Edit: I think you know this and that was just a type error.), but I tell you is better that you use radians instead of degrees.

And if you know something of trigonometry you will know in angles "a-180" is equal to "-a" so then cos(a-180)=cos(-a) and sin(a-180)=sin(-a), so you can use that if you have complications using PI.
 
Last edited:
Level 7
Joined
Feb 9, 2021
Messages
301
Can you show the script that causes that error?

And I noticed you did Cos(TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD, is incorrect, * bj_DEGTORAD must be inside the cosine or sine function like this Cos((TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD), in the Atan2 function is outside the conversion because is the inverse function (Edit: I think you know this and that was just a type error.), but I tell you is better that you use radians instead of degrees.

And if you know something of trigonometry you will know in angles "a-180" is equal to "-a" so then cos(a-180)=cos(-a) and sin(a-180)=sin(-a), so you can use that if you have complications using PI.

Conditions do not show an error anymore for some reason.

Okay, I change it to this.
JASS:
call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos((-TG_Angel[TG_Loop_Int]) * bj_DEGTORAD))
call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin((-TG_Angel[TG_Loop_Int]) * bj_DEGTORAD))

The main problem is that the code does not do even close to the spell I described.
 
Last edited:
Level 7
Joined
Feb 9, 2021
Messages
301
Look it again, is still wrong.

edited my last post. Too much time passed from the time I used trigonometry.

Edit: I think I found the same mistake in the movement on the dummy unit. Thank you.

Edit2: The movement works better now. Let me try to work on it again. Thank you very much. I will update the post once I finish fixing it.
 
Last edited:
Level 7
Joined
Feb 9, 2021
Messages
301
JASS:
globals
    integer TG_Index = 0
    integer array TG_Stun_Index
    unit array TG_Caster
    unit array TG_Caster_Stun
    unit array TG_Dummy
    unit array TG_Dummy_Effect
    unit array TG_Grappled_U
    unit array TG_Target
    unit array TG_Stun_Dummy
    unit TG_Effect
    real array TG_Angel
    real TG_Dmg
    real TG_Dist
    real TG_Speed
    real TG_Aoe
    real TG_Stun_Max_Dur
    real array TG_Process
    real array TG_Stun_Dur
    integer array TG_Counter
    timer TG_Timer = CreateTimer()
    timer TG_Stun_Timer = CreateTimer()
    integer TG_Loop_Int = 0
    group TG_Group
    group array TG_Effects_Group
    boolean array TG_Unit_Is_Grappled
   
   
endglobals

function Trig_Tentacle_Grapple_ActionsV3 takes nothing returns nothing
    set TG_Stun_Index[2] = 1
    loop
        exitwhen TG_Stun_Index[2] > TG_Stun_Index[1]
        set TG_Stun_Dur[TG_Stun_Index[2]] = TG_Stun_Dur[TG_Stun_Index[2]] + 0.03
        if TG_Stun_Dur[TG_Stun_Index[2]] < TG_Stun_Max_Dur and UnitHasBuffBJ(TG_Target[TG_Stun_Index[2]], 'B00C') then
            call UnitDamageTarget(TG_Caster_Stun[TG_Stun_Index[2]], TG_Target[TG_Stun_Index[2]], TG_Dmg, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
        else
            call KillUnit(TG_Stun_Dummy[TG_Stun_Index[2]])
           
            set TG_Caster_Stun[TG_Stun_Index[2]] = TG_Caster_Stun[TG_Stun_Index[1]]
            set TG_Stun_Dur[TG_Stun_Index[2]] = TG_Stun_Dur[TG_Stun_Index[1]]
            set TG_Target[TG_Stun_Index[2]] = TG_Target[TG_Stun_Index[1]]
           
            set TG_Stun_Index[1] = TG_Stun_Index[1] - 1
            set TG_Stun_Index[2] = TG_Stun_Index[2] - 1
        endif
        set TG_Stun_Index[2] = TG_Stun_Index[2] + 1
       
        if TG_Stun_Index[1] == 0 then
            call PauseTimer(TG_Stun_Timer)
            call DestroyTimer(TG_Stun_Timer)
        endif
       
    endloop
endfunction

function Trig_Tentacle_Grapple_ActionsV2 takes nothing returns nothing
    set TG_Loop_Int = 1
    loop
        exitwhen TG_Loop_Int > TG_Index
         
        set TG_Process[TG_Loop_Int] = TG_Process[TG_Loop_Int] + TG_Speed
           
        if TG_Process[TG_Loop_Int] > TG_Dist or TG_Unit_Is_Grappled[TG_Loop_Int] and not UnitHasBuffBJ(TG_Target[TG_Stun_Index[1]], 'B00C') or GetUnitCurrentOrder(TG_Target[TG_Stun_Index[1]]) != String2OrderIdBJ("charm") and TG_Unit_Is_Grappled[TG_Loop_Int]  then
            //after the stun finished, return the dummy and delete Effects 
               
            call BJDebugMsg("Grapple Check")
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] - 1
               
            call IssueImmediateOrder(TG_Caster[TG_Loop_Int], "stop")
               
            set TG_Effect = LoadUnitHandleBJ((TG_Counter[TG_Loop_Int] + 1 ), TG_Loop_Int, Hash)
            call KillUnit(TG_Effect)
               
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos((-TG_Angel[TG_Loop_Int]) * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin((-TG_Angel[TG_Loop_Int]) * bj_DEGTORAD))
               
            if TG_Counter[TG_Loop_Int] == 0 then
                call KillUnit(TG_Dummy[TG_Loop_Int])
                   
               
                call DestroyGroup(TG_Effects_Group[TG_Loop_Int])
                   
                set TG_Caster[TG_Loop_Int] = TG_Caster[TG_Index]
                set TG_Dummy[TG_Loop_Int] = TG_Dummy[TG_Index]
                set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
                   
                   
                set TG_Index = TG_Index - 1
                set TG_Loop_Int = TG_Loop_Int - 1
            endif
               
               
        elseif GetUnitCurrentOrder(TG_Target[TG_Stun_Index[1]]) != String2OrderIdBJ("charm") and TG_Unit_Is_Grappled[TG_Loop_Int] then
            call UnitRemoveBuffBJ( 'B00C', TG_Target[TG_Loop_Int] )
        else
                //Move Dummy and Create Effects
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] + 1
               
            set TG_Dummy_Effect[TG_Loop_Int] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
            call GroupAddUnit(TG_Effects_Group[TG_Loop_Int], TG_Dummy_Effect[TG_Loop_Int])
            call SaveUnitHandle(Hash, TG_Loop_Int, TG_Counter[TG_Loop_Int], TG_Dummy_Effect[TG_Loop_Int])
               
            call BJDebugMsg("Grapple Move Check")
               
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
           
               
                //Pick Units Around Dummy
               
            call GroupEnumUnitsInRange(TG_Group, GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Aoe, null)
            loop
                set TG_Target[TG_Loop_Int] = FirstOfGroup(TG_Group)
                exitwhen TG_Target[TG_Loop_Int] == null
                if GetWidgetLife(TG_Target[TG_Loop_Int]) > 0.405 and not IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(TG_Target[TG_Loop_Int], GetOwningPlayer(TG_Caster[TG_Index])) and IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_STRUCTURE) == false then
                    set TG_Unit_Is_Grappled[TG_Loop_Int] = true
                       
                    set TG_Stun_Index[1] = TG_Stun_Index[1] + 1
                    set TG_Caster_Stun[TG_Stun_Index[1]] = TG_Caster[TG_Loop_Int]
                    set TG_Target[TG_Stun_Index[1]] = TG_Target[TG_Loop_Int]
                       
                    set TG_Stun_Dummy[TG_Stun_Index[1]] = CreateUnit(GetOwningPlayer(TG_Caster_Stun[TG_Stun_Index[1]]), 'h001', GetUnitX(TG_Caster_Stun[TG_Stun_Index[1]]), GetUnitY(TG_Caster_Stun[TG_Stun_Index[1]]), 270)
                    call UnitAddAbilityBJ( 'A00K', TG_Stun_Dummy[TG_Stun_Index[1]])
                    call IssueTargetOrder(TG_Stun_Dummy[TG_Stun_Index[1]], "thunderbolt", TG_Target[TG_Stun_Index[1]])
                       
                    if TG_Stun_Index[1] == 1 then
                        call TimerStart(TG_Stun_Timer, 0.03, true, function Trig_Tentacle_Grapple_ActionsV3)
                    endif
                endif
                call GroupRemoveUnit(TG_Group, TG_Target[TG_Loop_Int])
            endloop
        endif
           
        set TG_Loop_Int = TG_Loop_Int + 1
    endloop
   

endfunction


function Trig_Tentacle_Grapple_Actions takes nothing returns nothing
    set TG_Index = (TG_Index + 1)
    set TG_Caster[TG_Index] = GetTriggerUnit()
   
    local real xCaster = GetUnitX(TG_Caster[TG_Index])
    local real yCaster = GetUnitY(TG_Caster[TG_Index])
    local real xTarget = GetSpellTargetX()
    local real yTarget = GetSpellTargetY()
   
    set TG_Angel[TG_Index] =  bj_RADTODEG * Atan2(yTarget - yCaster, xTarget - xCaster)
   
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 10 * Cos(TG_Angel[TG_Index] * bj_DEGTORAD), yCaster + 10 * Sin(TG_Angel[TG_Index] * bj_DEGTORAD), TG_Angel[TG_Index])
    call SetUnitPathing(TG_Dummy[TG_Index], false)
    //Animation, Sounds
    call SetUnitAnimationByIndex(TG_Caster[TG_Index], 5)
   
    //SetUp
    set TG_Stun_Max_Dur = 3.0
    set TG_Dmg = 500 * 0.03 / TG_Stun_Max_Dur
    set TG_Dist = 1000
    set TG_Speed = 50 * 0.3125
    set TG_Aoe = 70

   
    //Addtional Settings
    set TG_Process[TG_Index] = 0
    set TG_Counter[TG_Index] = 0
    set TG_Unit_Is_Grappled[TG_Index] = false
    set TG_Grappled_U[TG_Index] = null
    set TG_Group = CreateGroup()

   
    //Timer
    if TG_Index == 1 then
        call TimerStart(TG_Timer, 0.03125, true, function Trig_Tentacle_Grapple_ActionsV2)
    endif
   
endfunction


function Trig_Tentacle_Grapple_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00J'
endfunction



//===========================================================================
function InitTrig_Tentacle_Grapple takes nothing returns nothing
    set gg_trg_Tentacle_Grapple = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Tentacle_Grapple, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Tentacle_Grapple, Condition( function Trig_Tentacle_Grapple_Conditions ) )
    call TriggerAddAction( gg_trg_Tentacle_Grapple, function Trig_Tentacle_Grapple_Actions )
endfunction


Edit: It seems like the problem is that TG_Target[TG_Stun_Index[1]] doesn't save the target.
 
Level 7
Joined
Feb 9, 2021
Messages
301
@maxodors Did you edited the error?, I asked you for look the problem, but if you solved it so ok.

I changed
JASS:
call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
 call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))

and
JASS:
call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos((-TG_Angel[TG_Loop_Int]) * bj_DEGTORAD))
 call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin((-TG_Angel[TG_Loop_Int]) * bj_DEGTORAD))

Now the hook goes forward and returns back. However, there are still a couple of problems:
1. The hook does not stay in place while the unit has a buff (stun)
2. There is no DPS
3. For some reason if the hook is casted on the left, the dummy is further away compared to if it is casted on the right
4. On thing I do not know how to do is for the hook to be connected to the face of the character. What I mean by this is if the unit starts moving before the hook returns, the chain follows him. I added a picture to illustrate the point.
5. Another one is if the hook meets a tree, the end of the map or a wall, it comes back.
 

Attachments

  • hook.png
    hook.png
    5.7 KB · Views: 21
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,850
Some things I noticed

1) You didn't even create this group TG_Effects_Group[TG_Loop_Int]
2) If this values doesn't change in every instance, why do you have them inside the actions function? You can setup them in the part of globals.
JASS:
//SetUp
    set TG_Stun_Max_Dur = 3.0
    set TG_Dmg = 500 * 0.03 / TG_Stun_Max_Dur
    set TG_Dist = 1000
    set TG_Speed = 50 * 0.3125
    set TG_Aoe = 70

    set TG_Group = CreateGroup()
3) I don't know if this will affect in something but in this part:
JASS:
call UnitDamageTarget(TG_Caster_Stun[TG_Stun_Index[2]], TG_Target[TG_Stun_Index[2]], TG_Dmg, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
I'm not sure if the weapon-type must have a value, but just in case change that null for WEAPON_TYPE_WHOKNOWS
Edit, maybe is because the boolean that is for "Is attack" (the first) is in false, try change it to true.
 
Level 7
Joined
Feb 9, 2021
Messages
301
Some things I noticed

1) You didn't even create this group TG_Effects_Group[TG_Loop_Int]
2) If this values doesn't change in every instance, why do you have them inside the actions function? You can setup them in the part of globals.
JASS:
//SetUp
    set TG_Stun_Max_Dur = 3.0
    set TG_Dmg = 500 * 0.03 / TG_Stun_Max_Dur
    set TG_Dist = 1000
    set TG_Speed = 50 * 0.3125
    set TG_Aoe = 70

    set TG_Group = CreateGroup()
3) I don't know if this will affect in something but in this part:
JASS:
call UnitDamageTarget(TG_Caster_Stun[TG_Stun_Index[2]], TG_Target[TG_Stun_Index[2]], TG_Dmg, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
I'm not sure if the weapon-type must have a value, but just in case change that null for WEAPON_TYPE_WHOKNOWS
Edit, maybe is because the boolean that is for "Is attack" (the first) is in false, try change it to true.

Fixed 1 and 2.

On 3, I set dmg like this in my other spells, and it works fine.
 
Level 7
Joined
Feb 9, 2021
Messages
301
After cracking my head for multiple hours, I was able to fix some problems. However, there are some which I can't solve.

1. The last dummy unit flies away somewhere in other directions.
2. Spell can't be casted multiple times
3. There is no DPS
4. The first dummy is created on different distances relative to the caster, depending on the angle
5. Leaks. I am not sure about leaks with globals. The game crashes after the spell is used multiple times.
6. Optimisation

There are also a couple of features which I don't know how to do:
1. I do not know how to do is for the hook to be connected to the face of the character. What I mean by this is if the unit starts moving before the hook returns, the chain follows him.
2. if the hook meets a tree, the end of the map or a wall, it comes back.

I attached an updated map to the post. My updated code:

JASS:
globals
    integer TG_Index = 0
    integer array TG_Stun_Index
    unit array TG_Caster
    unit array TG_Caster_Stun
    unit array TG_Dummy
    unit array TG_Dummy_Effect
    unit array TG_Grappled_U
    unit array TG_Target
    unit array TG_Stun_Dummy
    unit array TG_Stunned_U
    unit TG_Effect
    real array TG_Angel
    real array TG_Process
    real array TG_Stun_Dur
    integer array TG_Counter
    timer TG_Timer = CreateTimer()
    timer TG_Stun_Timer = CreateTimer()
    integer TG_Loop_Int = 0
    group TG_Group = CreateGroup()
    group TG_Effects_Group = CreateGroup()
    boolean array TG_Unit_Is_Grappled
    boolean array TG_Stun_Removed
 
        //configurations
    real TG_Stun_Max_Dur = 3.0
    real TG_Dmg = 500 * 0.03 / TG_Stun_Max_Dur
    real TG_Dist = 800
    real TG_Speed = 1000 * 0.0312500
    real TG_Aoe = 70
 
endglobals

function Trig_Tentacle_Grapple_ActionsV3 takes nothing returns nothing
    set TG_Stun_Index[2] = 1
    loop
        exitwhen TG_Stun_Index[2] > TG_Stun_Index[1]
        set TG_Stun_Dur[TG_Stun_Index[2]] = TG_Stun_Dur[TG_Stun_Index[2]] + 0.0312500
        if TG_Stun_Dur[TG_Stun_Index[2]] < TG_Stun_Max_Dur and UnitHasBuffBJ(TG_Target[TG_Stun_Index[2]], 'B00C') then
            call UnitDamageTarget(TG_Caster_Stun[TG_Stun_Index[2]], TG_Target[TG_Stun_Index[2]], TG_Dmg, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
            call BJDebugMsg("DPS")
        else
            //call IssueImmediateOrder(TG_Caster_Stun[TG_Stun_Index[2]], "stop")
        
            call KillUnit(TG_Stun_Dummy[TG_Stun_Index[2]])
        
            set TG_Caster_Stun[TG_Stun_Index[2]] = TG_Caster_Stun[TG_Stun_Index[1]]
            set TG_Stun_Dur[TG_Stun_Index[2]] = TG_Stun_Dur[TG_Stun_Index[1]]
            set TG_Target[TG_Stun_Index[2]] = TG_Target[TG_Stun_Index[1]]
        
            set TG_Stun_Index[1] = TG_Stun_Index[1] - 1
            set TG_Stun_Index[2] = TG_Stun_Index[2] - 1
        endif
    
    
        if TG_Stun_Index[1] == 0 then
            call PauseTimer(TG_Stun_Timer)
        endif
    
        set TG_Stun_Index[2] = TG_Stun_Index[2] + 1
    
    
    endloop
endfunction

function Trig_Tentacle_Grapple_ActionsV2 takes nothing returns nothing
    set TG_Loop_Int = 1
    loop
        exitwhen TG_Loop_Int > TG_Index
      
      
        if GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") and TG_Unit_Is_Grappled[TG_Loop_Int] and not TG_Stun_Removed[TG_Loop_Int] then
            call UnitRemoveBuffBJ( 'B00C', TG_Stunned_U[TG_Loop_Int] )
            call BJDebugMsg("RemoveStun")
            set TG_Stun_Removed[TG_Loop_Int] = true
        
        elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") or TG_Process[TG_Loop_Int] > TG_Dist or TG_Unit_Is_Grappled[TG_Loop_Int] and not UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') then
            //after the stun finished, return the dummy and delete Effects
            
            call BJDebugMsg("Grapple Check")
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] - 1
            
            set TG_Effect = LoadUnitHandleBJ((TG_Counter[TG_Loop_Int] + 1 ), TG_Loop_Int, Hash)
            call KillUnit(TG_Effect)
            
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos(( -TG_Angel[TG_Loop_Int]) * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin(( -TG_Angel[TG_Loop_Int]) * bj_DEGTORAD))
        
            if TG_Counter[TG_Loop_Int] == 0 then
                call KillUnit(TG_Dummy[TG_Loop_Int])
                
                set TG_Caster[TG_Loop_Int] = TG_Caster[TG_Index]
                set TG_Dummy[TG_Loop_Int] = TG_Dummy[TG_Index]
                set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
                
                
                set TG_Index = TG_Index - 1
                set TG_Loop_Int = TG_Loop_Int - 1
            endif
        elseif TG_Unit_Is_Grappled[TG_Loop_Int] and UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') and TG_Stun_Dur[TG_Stun_Index[2]] < TG_Stun_Max_Dur then
            call BJDebugMsg("Stunned")
        
        else
                //Move Dummy and Create Effects
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] + 1
            set TG_Process[TG_Loop_Int] = TG_Process[TG_Loop_Int] + TG_Speed
        
            set TG_Dummy_Effect[TG_Loop_Int] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
            call GroupAddUnit(TG_Effects_Group, TG_Dummy_Effect[TG_Loop_Int])
            call SaveUnitHandle(Hash, TG_Loop_Int, TG_Counter[TG_Loop_Int], TG_Dummy_Effect[TG_Loop_Int])
            
            call BJDebugMsg("Grapple Move Check")
            
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
        
            
                //Pick Units Around Dummy
            
            call GroupEnumUnitsInRange(TG_Group, GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Aoe, null)
            loop
                set TG_Target[TG_Loop_Int] = FirstOfGroup(TG_Group)
                exitwhen TG_Target[TG_Loop_Int] == null
                if GetWidgetLife(TG_Target[TG_Loop_Int]) > 0.405 and not IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(TG_Target[TG_Loop_Int], GetOwningPlayer(TG_Caster[TG_Index])) and IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_STRUCTURE) == false then
                    set TG_Unit_Is_Grappled[TG_Loop_Int] = true
                    set TG_Stun_Removed[TG_Loop_Int] = false
                    set TG_Stunned_U[TG_Loop_Int] = TG_Target[TG_Loop_Int]
                
                    set TG_Stun_Index[1] = TG_Stun_Index[1] + 1
                    set TG_Caster_Stun[TG_Stun_Index[1]] = TG_Caster[TG_Loop_Int]
                    set TG_Target[TG_Stun_Index[1]] = TG_Target[TG_Loop_Int]
                    
                    set TG_Stun_Dummy[TG_Stun_Index[1]] = CreateUnit(GetOwningPlayer(TG_Caster_Stun[TG_Stun_Index[1]]), 'h001', GetUnitX(TG_Caster_Stun[TG_Stun_Index[1]]), GetUnitY(TG_Caster_Stun[TG_Stun_Index[1]]), 270)
                    call UnitAddAbilityBJ( 'A00K', TG_Stun_Dummy[TG_Stun_Index[1]])
                    call IssueTargetOrder(TG_Stun_Dummy[TG_Stun_Index[1]], "thunderbolt", TG_Target[TG_Stun_Index[1]])
                    
                    if TG_Stun_Index[1] == 1 then
                        call TimerStart(TG_Stun_Timer, 0.0312500, true, function Trig_Tentacle_Grapple_ActionsV3)
                    endif
                endif
                call GroupRemoveUnit(TG_Group, TG_Target[TG_Loop_Int])
            endloop
        endif
        
        set TG_Loop_Int = TG_Loop_Int + 1
    endloop
 

endfunction


function Trig_Tentacle_Grapple_Actions takes nothing returns nothing
    set TG_Index = (TG_Index + 1)
    set TG_Caster[TG_Index] = GetTriggerUnit()
 
    local real xCaster = GetUnitX(TG_Caster[TG_Index])
    local real yCaster = GetUnitY(TG_Caster[TG_Index])
    local real xTarget = GetSpellTargetX()
    local real yTarget = GetSpellTargetY()
 
    set TG_Angel[TG_Index] = bj_RADTODEG * Atan2(yTarget - yCaster, xTarget - xCaster)
 
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 10 * Cos(TG_Angel[TG_Index] * bj_DEGTORAD), yCaster + 10 * Sin(TG_Angel[TG_Index] * bj_DEGTORAD), TG_Angel[TG_Index])
    call SetUnitPathing(TG_Dummy[TG_Index], false)
    //Animation, Sounds
    call SetUnitAnimationByIndex(TG_Caster[TG_Index], 5)
 
    //Addtional Settings
    set TG_Process[TG_Index] = 0
    set TG_Counter[TG_Index] = 0
    set TG_Unit_Is_Grappled[TG_Index] = false
    set TG_Grappled_U[TG_Index] = null
    set TG_Stun_Index[1] = 0
    //Timer
    if TG_Index == 1 then
        call TimerStart(TG_Timer, 0.0312500, true, function Trig_Tentacle_Grapple_ActionsV2)
    endif
 
endfunction


function Trig_Tentacle_Grapple_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00J'
endfunction



//===========================================================================
function InitTrig_Tentacle_Grapple takes nothing returns nothing
    set gg_trg_Tentacle_Grapple = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Tentacle_Grapple, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Tentacle_Grapple, Condition( function Trig_Tentacle_Grapple_Conditions ) )
    call TriggerAddAction( gg_trg_Tentacle_Grapple, function Trig_Tentacle_Grapple_Actions )
endfunction
 

Attachments

  • Mammon.w3x
    112.1 KB · Views: 20
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,850
2. Spell can't be casted multiple times
That's because you have to reindex all the array variables not only these:
JASS:
set TG_Caster[TG_Loop_Int] = TG_Caster[TG_Index]
set TG_Dummy[TG_Loop_Int] = TG_Dummy[TG_Index]
set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
That include those was in the hashtable
3. There is no DPS
I ask you, Is necessary use 2 timers to do this spell?, you can do it all with the same timer, just for example in this condition block you can do the DPS:
JASS:
elseif TG_Unit_Is_Grappled[TG_Loop_Int] and UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') and TG_Stun_Dur[TG_Stun_Index[2]] < TG_Stun_Max_Dur then
5. Leaks. I am not sure about leaks with globals. The game crashes after the spell is used multiple times.
Well, you don't have to worry about reference leaks with global variables because they always have a value, and for object leak, I don't see you create handles in each instance, so don't worry for that too.
 
Level 7
Joined
Feb 9, 2021
Messages
301
3. I didn't think about it. DPS works now

Should I make TG_Effect an array?

It seems like it all works now, except the problem (4). Thank you, I learnt a lot from these couple of days.

Also, I still need to learn how to do the features.





Here is my code:
JASS:
globals
    integer TG_Index = 0
    unit array TG_Caster
    unit array TG_Dummy
    unit array TG_Dummy_Effect
    unit array TG_Grappled_U
    unit array TG_Target
    unit array TG_Stun_Dummy
    unit array TG_Stunned_U
    unit TG_Effect
    real array TG_Angel
    real array TG_Process
    real array TG_Stun_Dur
    real array TG_Max_Targets
    integer array TG_Counter
    timer TG_Timer = CreateTimer()
    timer TG_Stun_Timer = CreateTimer()
    integer TG_Loop_Int = 0
    group TG_Group = CreateGroup()
    group TG_Effects_Group = CreateGroup()
    boolean array TG_Unit_Is_Grappled
    boolean array TG_Stun_Removed
    boolean array TG_Stun_Finished
   
        //configurations
    real TG_Stun_Max_Dur = 3.0
    real TG_Dmg = 100 * 0.0312500 / TG_Stun_Max_Dur
    real TG_Dist = 800
    real TG_Speed = 1300 * 0.0312500
    real TG_Aoe = 64
   
endglobals

function Trig_Tentacle_Grapple_ActionsV2 takes nothing returns nothing
    set TG_Loop_Int = 1
    loop
        exitwhen TG_Loop_Int > TG_Index
         
        if TG_Stun_Dur[TG_Loop_Int] >= TG_Stun_Max_Dur and not TG_Stun_Finished[TG_Loop_Int] then
            call IssueImmediateOrder(TG_Caster[TG_Loop_Int], "stop")
            call BJDebugMsg("StunFinished")
            set TG_Stun_Finished[TG_Loop_Int] = true
       
        elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") and TG_Unit_Is_Grappled[TG_Loop_Int] and not TG_Stun_Removed[TG_Loop_Int] and not TG_Stun_Finished[TG_Loop_Int] then
            call UnitRemoveBuffBJ( 'B00C', TG_Stunned_U[TG_Loop_Int] )
            call BJDebugMsg("RemoveStun")
            set TG_Stun_Removed[TG_Loop_Int] = true
           
        elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") or TG_Process[TG_Loop_Int] > TG_Dist or TG_Unit_Is_Grappled[TG_Loop_Int] and not UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') then
            //after the stun finished, return the dummy and delete Effects 
               
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] - 1
            //set TG_Effect = LoadUnitHandleBJ((TG_Counter[TG_Loop_Int] + 1 ), TG_Loop_Int, Hash)
            set TG_Effect = LoadUnitHandle(Hash, TG_Loop_Int, (TG_Counter[TG_Loop_Int] + 1 ))
            call KillUnit(TG_Effect)
            call RemoveUnit(TG_Effect)
            call GroupRemoveUnit(TG_Effects_Group,TG_Effect)
               
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos((TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin((TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD))
           
            if TG_Counter[TG_Loop_Int] == 0 then
                call KillUnit(TG_Dummy[TG_Loop_Int])
                call RemoveUnit(TG_Dummy[TG_Loop_Int])
                   
                set TG_Caster[TG_Loop_Int] = TG_Caster[TG_Index]
                set TG_Dummy[TG_Loop_Int] = TG_Dummy[TG_Index]
                set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
                set TG_Unit_Is_Grappled[TG_Loop_Int] = TG_Unit_Is_Grappled[TG_Index]
                set TG_Stun_Removed[TG_Loop_Int] = TG_Stun_Removed[TG_Index]
                set TG_Stun_Finished[TG_Loop_Int] = TG_Stun_Finished[TG_Index]
                set TG_Dummy_Effect[TG_Loop_Int] = TG_Dummy_Effect[TG_Index]
                set TG_Grappled_U[TG_Loop_Int] = TG_Grappled_U[TG_Index]
                set TG_Target[TG_Loop_Int] = TG_Target[TG_Index]
                set TG_Stun_Dummy[TG_Loop_Int] = TG_Stun_Dummy[TG_Index]
                set TG_Stunned_U[TG_Loop_Int] = TG_Stunned_U[TG_Index]
                set TG_Angel[TG_Loop_Int] = TG_Angel[TG_Index]
                set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
                set TG_Stun_Dur[TG_Loop_Int] = TG_Stun_Dur[TG_Index]
                set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Index]
                set TG_Max_Targets[TG_Loop_Int] = TG_Max_Targets[TG_Index]
               
               
                call FlushChildHashtable(Hash, TG_Loop_Int)
           
                set TG_Index = TG_Index - 1
                set TG_Loop_Int = TG_Loop_Int - 1
               
                if TG_Loop_Int == 0 then
                    call PauseTimer(TG_Timer)
           
                endif
            endif
        elseif TG_Unit_Is_Grappled[TG_Loop_Int] and UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') and TG_Stun_Dur[TG_Loop_Int] < TG_Stun_Max_Dur then
            set TG_Stun_Dur[TG_Loop_Int] = TG_Stun_Dur[TG_Loop_Int] + 0.0312500
            call UnitDamageTarget(TG_Caster[TG_Loop_Int], TG_Stunned_U[TG_Loop_Int], TG_Dmg, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
           
        else
                //Move Dummy and Create Effects
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] + 1
            set TG_Process[TG_Loop_Int] = TG_Process[TG_Loop_Int] + TG_Speed
           
            set TG_Dummy_Effect[TG_Loop_Int] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
            call GroupAddUnit(TG_Effects_Group, TG_Dummy_Effect[TG_Loop_Int])
            call SaveUnitHandle(Hash, TG_Loop_Int, TG_Counter[TG_Loop_Int], TG_Dummy_Effect[TG_Loop_Int])
               
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
           
               
                //Pick Units Around Dummy
               
            call GroupEnumUnitsInRange(TG_Group, GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Aoe, null)
            loop
                set TG_Target[TG_Loop_Int] = FirstOfGroup(TG_Group)
                exitwhen TG_Max_Targets[TG_Loop_Int] == 0 or TG_Target[TG_Loop_Int] == null
                if GetWidgetLife(TG_Target[TG_Loop_Int]) > 0.405 and not IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(TG_Target[TG_Loop_Int], GetOwningPlayer(TG_Caster[TG_Index])) and IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_STRUCTURE) == false then
                    set TG_Stunned_U[TG_Loop_Int] = TG_Target[TG_Loop_Int]
                    set TG_Unit_Is_Grappled[TG_Loop_Int] = true
                   
                    TG_Max_Targets[TG_Loop_Int] = TG_Max_Targets[TG_Loop_Int] - 1
                   
                    set TG_Stun_Dummy[TG_Loop_Int] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h001', GetUnitX(TG_Caster[TG_Loop_Int]), GetUnitY(TG_Caster[TG_Loop_Int]), 270)
                    call UnitAddAbilityBJ( 'A00K', TG_Stun_Dummy[TG_Loop_Int])
                    call IssueTargetOrder(TG_Stun_Dummy[TG_Loop_Int], "thunderbolt", TG_Target[TG_Loop_Int])
                       
                 
                endif
                call GroupRemoveUnit(TG_Group, TG_Target[TG_Loop_Int])
            endloop
        endif
           
        set TG_Loop_Int = TG_Loop_Int + 1
    endloop
   

endfunction


function Trig_Tentacle_Grapple_Actions takes nothing returns nothing
    set TG_Index = (TG_Index + 1)
    set TG_Caster[TG_Index] = GetTriggerUnit()
   
    local real xCaster = GetUnitX(TG_Caster[TG_Index])
    local real yCaster = GetUnitY(TG_Caster[TG_Index])
    local real xTarget = GetSpellTargetX()
    local real yTarget = GetSpellTargetY()
   
    set TG_Angel[TG_Index] = bj_RADTODEG * Atan2(yTarget - yCaster, xTarget - xCaster)
   
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 10 * Cos(TG_Angel[TG_Index] * bj_DEGTORAD), yCaster + 10 * Sin(TG_Angel[TG_Index] * bj_DEGTORAD), TG_Angel[TG_Index])
    call SetUnitPathing(TG_Dummy[TG_Index], false)
    //Animation, Sounds
    call SetUnitAnimationByIndex(TG_Caster[TG_Index], 5)
   
    //Addtional Settings
    set TG_Process[TG_Index] = 0
    set TG_Counter[TG_Index] = 0
    set TG_Unit_Is_Grappled[TG_Index] = false
    set TG_Grappled_U[TG_Index] = null
    set TG_Stun_Removed[TG_Index] = false
    set TG_Stun_Finished[TG_Index] = false
    set TG_Target[TG_Index] = null
    set TG_Dummy_Effect[TG_Index] = null
    set TG_Stun_Dummy[TG_Index] = null
    set TG_Stunned_U[TG_Index] = null
    set TG_Stun_Dur[TG_Index] = 0
    set TG_Max_Targets[TG_Index] = 1
    //Timer
    if TG_Index == 1 then
        call TimerStart(TG_Timer, 0.0312500, true, function Trig_Tentacle_Grapple_ActionsV2)
    endif
   
endfunction


function Trig_Tentacle_Grapple_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00J'
endfunction



//===========================================================================
function InitTrig_Tentacle_Grapple takes nothing returns nothing
    set gg_trg_Tentacle_Grapple = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Tentacle_Grapple, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Tentacle_Grapple, Condition( function Trig_Tentacle_Grapple_Conditions ) )
    call TriggerAddAction( gg_trg_Tentacle_Grapple, function Trig_Tentacle_Grapple_Actions )
endfunction
 
Last edited:
Level 7
Joined
Feb 9, 2021
Messages
301
upload_2021-2-23_10-10-22.png


The locations of the dummies are off relative to the unit.

Also, it is impossible to cast the spell a second time, while the first one is still active.

One more needed feature is if the target dummy is knocked back, the chain should move accordingly.

Another problem I might encounter: in a situation when someone uses a stun on the grappled unit. This would override my stun. There are systems that allow stuck stuns. However, I don't want the stun either to stuck or to override. The grapple timer should be separate from the stun timer. How can I address it?
 
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,850
Here it is
Ok, I for a strange reason, when I edit your map it happens a very strange bug so I can't test it well; in your code, sorry but I can't find the mistakes, but I told you that you have to reindex even the stuff in the hashtables, like this:
JASS:
set i=1
loop
    exitwhen i>TG_Counter[TG_Index]
    call SaveUnitHandle(Hash, TG_Loop_Int, i, LoadUnitHandle(Hash, TG_Index, i))
    set i=i+1
endloop
 
Last edited:
Level 7
Joined
Feb 9, 2021
Messages
301
Ok, I for a strange reason, when I edit your map it happens a very strange bug so I can't test it well; in your code, sorry but I can't find the mistakes, but I told you that you have to reindex even the stuff in the hashtables, like this:
JASS:
set i=1
loop
    exitwhen i>TG_Counter[TG_Index]
    call SaveUnitHandle(Hash, TG_Loop_Int, i, LoadUnitHandle(Hash, TG_Index, i))
    set i=i+1
endloop

Thank you. You helped a lot already. I hope someone else can help me with other points.
 
Level 24
Joined
Jun 26, 2020
Messages
1,850
You're welcome. Well for the multiple instance stun you have an option, use a unit indexer or a hashtable and do
JASS:
//When the unit is stunned
set Stunners_Number[GetUnitUserData(<Unit stunned>)]=Stunners_Number[GetUnitUserData(<Unit stunned>)]+1 //This is if you use a unit indexer
call SaveInteger(Table,1,GetHandleId(<Unit stunned>),LoadInteger(Table,1,GetHandleId(<Unit stunned>))+1) //This is if you use a hashtable
if <Saved integer>==1 then
    //Stun unit
endif

...

//Stop the stun
set <Saved integer>=<Saved integer>-1
if <Saved integer>==0 then
    //Remove stun
endif
And you don't have to reindex it when the instance is done.
 
Level 7
Joined
Feb 9, 2021
Messages
301
Do you reffer the others than the stack the stun?
I refer to others.

Regarding your stun proposition. I do not really understand your solution. Would this allow to stuns run independently of each other? In general, in my map, I want stuns to override each other. However, as this is not a stun, I want it to have its own timer.
 
Level 24
Joined
Jun 26, 2020
Messages
1,850
I refer to others.

Regarding your stun proposition. I do not really understand your solution. Would this allow to stuns run independently of each other? In general, in my map, I want stuns to override each other. However, as this is not a stun, I want it to have its own timer.
Ok, I don't understand you, what do you mean with override each other?
 
Level 24
Joined
Jun 26, 2020
Messages
1,850
I think this a system for stuns which is in wc3 already. Unit A cast a stun on a Target for 3 sec. Then Unit B cast a stun for 2 sec. The target will get 2 sec stun.

I want it to be like explained here:
How does Crowd Control stack with each other?

Unit A cast a stun that has 2 sec. Then Unit B cast a stun that has 3 sec. Every stun has its own timer.
If is that, so you don't have to do nothing.
 
Level 7
Joined
Feb 9, 2021
Messages
301
Ok, I for a strange reason, when I edit your map it happens a very strange bug so I can't test it well; in your code, sorry but I can't find the mistakes, but I told you that you have to reindex even the stuff in the hashtables, like this:
JASS:
set i=1
loop
    exitwhen i>TG_Counter[TG_Index]
    call SaveUnitHandle(Hash, TG_Loop_Int, i, LoadUnitHandle(Hash, TG_Index, i))
    set i=i+1
endloop

I didn't get it. Do I have to create a new cycle to reindex the hashtable?


JASS:
globals
    integer TG_Index = 0
    unit array TG_Caster
    unit array TG_Dummy
    unit array TG_Dummy_Effect
    unit array TG_Grappled_U
    unit array TG_Target
    unit array TG_Stun_Dummy
    unit array TG_Stunned_U
    unit TG_Effect
    real array TG_Angel
    real array TG_Process
    real array TG_Stun_Dur
    real array TG_Max_Targets
    integer array TG_Counter
    timer TG_Timer = CreateTimer()
    timer TG_Stun_Timer = CreateTimer()
    integer TG_Loop_Int = 0
    integer TG_Hash = 0
    group TG_Group = CreateGroup()
    group TG_Effects_Group = CreateGroup()
    boolean array TG_Unit_Is_Grappled
    boolean array TG_Stun_Removed
    boolean array TG_Stun_Finished
   
        //configurations
    real TG_Stun_Max_Dur = 3.0
    real TG_Dmg = 100 * 0.0312500 / TG_Stun_Max_Dur
    real TG_Dist = 800
    real TG_Speed = 1300 * 0.0312500
    real TG_Aoe = 64
   
endglobals
function Trig_Tentacle_Grapple_ActionsV2 takes nothing returns nothing
    set TG_Loop_Int = 1
    loop
        exitwhen TG_Loop_Int > TG_Index
         
        if TG_Stun_Dur[TG_Loop_Int] >= TG_Stun_Max_Dur and not TG_Stun_Finished[TG_Loop_Int] then
            call IssueImmediateOrder(TG_Caster[TG_Loop_Int], "stop")
            call BJDebugMsg("StunFinished")
            set TG_Stun_Finished[TG_Loop_Int] = true
       
        elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") and TG_Unit_Is_Grappled[TG_Loop_Int] and not TG_Stun_Removed[TG_Loop_Int] and not TG_Stun_Finished[TG_Loop_Int] then
            call UnitRemoveBuffBJ( 'B00C', TG_Stunned_U[TG_Loop_Int] )
            call BJDebugMsg("RemoveStun")
            set TG_Stun_Removed[TG_Loop_Int] = true
           
        elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") or TG_Process[TG_Loop_Int] > TG_Dist or TG_Unit_Is_Grappled[TG_Loop_Int] and not UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') then
            //after the stun finished, return the dummy and delete Effects 
               
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] - 1
            //set TG_Effect = LoadUnitHandleBJ((TG_Counter[TG_Loop_Int] + 1 ), TG_Loop_Int, Hash)
            set TG_Effect = LoadUnitHandle(Hash, TG_Loop_Int, (TG_Counter[TG_Loop_Int] + 1 ))
            call KillUnit(TG_Effect)
            call RemoveUnit(TG_Effect)
            call GroupRemoveUnit(TG_Effects_Group,TG_Effect)
               
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos((TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin((TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD))
           
            if TG_Counter[TG_Loop_Int] == 0 then
                call KillUnit(TG_Dummy[TG_Loop_Int])
                call RemoveUnit(TG_Dummy[TG_Loop_Int])
                   
                set TG_Caster[TG_Loop_Int] = TG_Caster[TG_Index]
                set TG_Dummy[TG_Loop_Int] = TG_Dummy[TG_Index]
                set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
                set TG_Unit_Is_Grappled[TG_Loop_Int] = TG_Unit_Is_Grappled[TG_Index]
                set TG_Stun_Removed[TG_Loop_Int] = TG_Stun_Removed[TG_Index]
                set TG_Stun_Finished[TG_Loop_Int] = TG_Stun_Finished[TG_Index]
                set TG_Dummy_Effect[TG_Loop_Int] = TG_Dummy_Effect[TG_Index]
                set TG_Grappled_U[TG_Loop_Int] = TG_Grappled_U[TG_Index]
                set TG_Target[TG_Loop_Int] = TG_Target[TG_Index]
                set TG_Stun_Dummy[TG_Loop_Int] = TG_Stun_Dummy[TG_Index]
                set TG_Stunned_U[TG_Loop_Int] = TG_Stunned_U[TG_Index]
                set TG_Angel[TG_Loop_Int] = TG_Angel[TG_Index]
                set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
                set TG_Stun_Dur[TG_Loop_Int] = TG_Stun_Dur[TG_Index]
                set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Index]
                set TG_Max_Targets[TG_Loop_Int] = TG_Max_Targets[TG_Index]
               
                set TG_Hash=1
                loop
                    exitwhen TG_Hash>TG_Counter[TG_Index]
                    call SaveUnitHandle(Hash, TG_Loop_Int, TG_Hash, LoadUnitHandle(Hash, TG_Index, TG_Hash))
                    set TG_Hash=TG_Hash+1
                endloop
               
                call FlushChildHashtable(Hash, TG_Loop_Int)
           
                set TG_Index = TG_Index - 1
                set TG_Loop_Int = TG_Loop_Int - 1
               
                if TG_Loop_Int == 0 then
                    call PauseTimer(TG_Timer)
           
                endif
            endif
        elseif TG_Unit_Is_Grappled[TG_Loop_Int] and UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') and TG_Stun_Dur[TG_Loop_Int] < TG_Stun_Max_Dur then
            set TG_Stun_Dur[TG_Loop_Int] = TG_Stun_Dur[TG_Loop_Int] + 0.0312500
            call UnitDamageTarget(TG_Caster[TG_Loop_Int], TG_Stunned_U[TG_Loop_Int], TG_Dmg, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
           
        else
                //Move Dummy and Create Effects
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] + 1
            set TG_Process[TG_Loop_Int] = TG_Process[TG_Loop_Int] + TG_Speed
           
            set TG_Dummy_Effect[TG_Loop_Int] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
            call GroupAddUnit(TG_Effects_Group, TG_Dummy_Effect[TG_Loop_Int])
            call SaveUnitHandle(Hash, TG_Loop_Int, TG_Counter[TG_Loop_Int], TG_Dummy_Effect[TG_Loop_Int])
               
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
           
               
                //Pick Units Around Dummy
               
            call GroupEnumUnitsInRange(TG_Group, GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Aoe, null)
            loop
                set TG_Target[TG_Loop_Int] = FirstOfGroup(TG_Group)
                exitwhen TG_Max_Targets[TG_Loop_Int] == 0 or TG_Target[TG_Loop_Int] == null
                if GetWidgetLife(TG_Target[TG_Loop_Int]) > 0.405 and not IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(TG_Target[TG_Loop_Int], GetOwningPlayer(TG_Caster[TG_Index])) and IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_STRUCTURE) == false then
                    set TG_Stunned_U[TG_Loop_Int] = TG_Target[TG_Loop_Int]
                    set TG_Unit_Is_Grappled[TG_Loop_Int] = true
                   
                    TG_Max_Targets[TG_Loop_Int] = TG_Max_Targets[TG_Loop_Int] - 1
                   
                    set TG_Stun_Dummy[TG_Loop_Int] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h001', GetUnitX(TG_Caster[TG_Loop_Int]), GetUnitY(TG_Caster[TG_Loop_Int]), 270)
                    call UnitAddAbilityBJ( 'A00K', TG_Stun_Dummy[TG_Loop_Int])
                    call IssueTargetOrder(TG_Stun_Dummy[TG_Loop_Int], "thunderbolt", TG_Target[TG_Loop_Int])
                       
                 
                endif
                call GroupRemoveUnit(TG_Group, TG_Target[TG_Loop_Int])
            endloop
        endif
           
        set TG_Loop_Int = TG_Loop_Int + 1
    endloop
   
endfunction
function Trig_Tentacle_Grapple_Actions takes nothing returns nothing
    set TG_Index = (TG_Index + 1)
    set TG_Caster[TG_Index] = GetTriggerUnit()
   
    local real xCaster = GetUnitX(TG_Caster[TG_Index])
    local real yCaster = GetUnitY(TG_Caster[TG_Index])
    local real xTarget = GetSpellTargetX()
    local real yTarget = GetSpellTargetY()
   
    set TG_Angel[TG_Index] = bj_RADTODEG * Atan2(yTarget - yCaster, xTarget - xCaster)
   
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 0 * Cos(TG_Angel[TG_Index] * bj_DEGTORAD), yCaster + 0 * Sin(TG_Angel[TG_Index] * bj_DEGTORAD), TG_Angel[TG_Index])
    call SetUnitPathing(TG_Dummy[TG_Index], false)
    //Animation, Sounds
    call SetUnitAnimationByIndex(TG_Caster[TG_Index], 5)
   
    //Addtional Settings
    set TG_Process[TG_Index] = 0
    set TG_Counter[TG_Index] = 0
    set TG_Unit_Is_Grappled[TG_Index] = false
    set TG_Grappled_U[TG_Index] = null
    set TG_Stun_Removed[TG_Index] = false
    set TG_Stun_Finished[TG_Index] = false
    set TG_Target[TG_Index] = null
    set TG_Dummy_Effect[TG_Index] = null
    set TG_Stun_Dummy[TG_Index] = null
    set TG_Stunned_U[TG_Index] = null
    set TG_Stun_Dur[TG_Index] = 0
    set TG_Max_Targets[TG_Index] = 1
    //Timer
    if TG_Index == 1 then
        call TimerStart(TG_Timer, 0.0312500, true, function Trig_Tentacle_Grapple_ActionsV2)
    endif
   
endfunction
function Trig_Tentacle_Grapple_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00J'
endfunction
//===========================================================================
function InitTrig_Tentacle_Grapple takes nothing returns nothing
    set gg_trg_Tentacle_Grapple = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Tentacle_Grapple, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Tentacle_Grapple, Condition( function Trig_Tentacle_Grapple_Conditions ) )
    call TriggerAddAction( gg_trg_Tentacle_Grapple, function Trig_Tentacle_Grapple_Actions )
endfunction
 
Level 24
Joined
Jun 26, 2020
Messages
1,850
I didn't get it. Do I have to create a new cycle to reindex the hashtable?


JASS:
globals
    integer TG_Index = 0
    unit array TG_Caster
    unit array TG_Dummy
    unit array TG_Dummy_Effect
    unit array TG_Grappled_U
    unit array TG_Target
    unit array TG_Stun_Dummy
    unit array TG_Stunned_U
    unit TG_Effect
    real array TG_Angel
    real array TG_Process
    real array TG_Stun_Dur
    real array TG_Max_Targets
    integer array TG_Counter
    timer TG_Timer = CreateTimer()
    timer TG_Stun_Timer = CreateTimer()
    integer TG_Loop_Int = 0
    integer TG_Hash = 0
    group TG_Group = CreateGroup()
    group TG_Effects_Group = CreateGroup()
    boolean array TG_Unit_Is_Grappled
    boolean array TG_Stun_Removed
    boolean array TG_Stun_Finished
  
        //configurations
    real TG_Stun_Max_Dur = 3.0
    real TG_Dmg = 100 * 0.0312500 / TG_Stun_Max_Dur
    real TG_Dist = 800
    real TG_Speed = 1300 * 0.0312500
    real TG_Aoe = 64
  
endglobals
function Trig_Tentacle_Grapple_ActionsV2 takes nothing returns nothing
    set TG_Loop_Int = 1
    loop
        exitwhen TG_Loop_Int > TG_Index
        
        if TG_Stun_Dur[TG_Loop_Int] >= TG_Stun_Max_Dur and not TG_Stun_Finished[TG_Loop_Int] then
            call IssueImmediateOrder(TG_Caster[TG_Loop_Int], "stop")
            call BJDebugMsg("StunFinished")
            set TG_Stun_Finished[TG_Loop_Int] = true
      
        elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") and TG_Unit_Is_Grappled[TG_Loop_Int] and not TG_Stun_Removed[TG_Loop_Int] and not TG_Stun_Finished[TG_Loop_Int] then
            call UnitRemoveBuffBJ( 'B00C', TG_Stunned_U[TG_Loop_Int] )
            call BJDebugMsg("RemoveStun")
            set TG_Stun_Removed[TG_Loop_Int] = true
          
        elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") or TG_Process[TG_Loop_Int] > TG_Dist or TG_Unit_Is_Grappled[TG_Loop_Int] and not UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') then
            //after the stun finished, return the dummy and delete Effects
              
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] - 1
            //set TG_Effect = LoadUnitHandleBJ((TG_Counter[TG_Loop_Int] + 1 ), TG_Loop_Int, Hash)
            set TG_Effect = LoadUnitHandle(Hash, TG_Loop_Int, (TG_Counter[TG_Loop_Int] + 1 ))
            call KillUnit(TG_Effect)
            call RemoveUnit(TG_Effect)
            call GroupRemoveUnit(TG_Effects_Group,TG_Effect)
              
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos((TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin((TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD))
          
            if TG_Counter[TG_Loop_Int] == 0 then
                call KillUnit(TG_Dummy[TG_Loop_Int])
                call RemoveUnit(TG_Dummy[TG_Loop_Int])
                  
                set TG_Caster[TG_Loop_Int] = TG_Caster[TG_Index]
                set TG_Dummy[TG_Loop_Int] = TG_Dummy[TG_Index]
                set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
                set TG_Unit_Is_Grappled[TG_Loop_Int] = TG_Unit_Is_Grappled[TG_Index]
                set TG_Stun_Removed[TG_Loop_Int] = TG_Stun_Removed[TG_Index]
                set TG_Stun_Finished[TG_Loop_Int] = TG_Stun_Finished[TG_Index]
                set TG_Dummy_Effect[TG_Loop_Int] = TG_Dummy_Effect[TG_Index]
                set TG_Grappled_U[TG_Loop_Int] = TG_Grappled_U[TG_Index]
                set TG_Target[TG_Loop_Int] = TG_Target[TG_Index]
                set TG_Stun_Dummy[TG_Loop_Int] = TG_Stun_Dummy[TG_Index]
                set TG_Stunned_U[TG_Loop_Int] = TG_Stunned_U[TG_Index]
                set TG_Angel[TG_Loop_Int] = TG_Angel[TG_Index]
                set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
                set TG_Stun_Dur[TG_Loop_Int] = TG_Stun_Dur[TG_Index]
                set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Index]
                set TG_Max_Targets[TG_Loop_Int] = TG_Max_Targets[TG_Index]
              
                set TG_Hash=1
                loop
                    exitwhen TG_Hash>TG_Counter[TG_Index]
                    call SaveUnitHandle(Hash, TG_Loop_Int, TG_Hash, LoadUnitHandle(Hash, TG_Index, TG_Hash))
                    set TG_Hash=TG_Hash+1
                endloop
              
                call FlushChildHashtable(Hash, TG_Loop_Int)
          
                set TG_Index = TG_Index - 1
                set TG_Loop_Int = TG_Loop_Int - 1
              
                if TG_Loop_Int == 0 then
                    call PauseTimer(TG_Timer)
          
                endif
            endif
        elseif TG_Unit_Is_Grappled[TG_Loop_Int] and UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') and TG_Stun_Dur[TG_Loop_Int] < TG_Stun_Max_Dur then
            set TG_Stun_Dur[TG_Loop_Int] = TG_Stun_Dur[TG_Loop_Int] + 0.0312500
            call UnitDamageTarget(TG_Caster[TG_Loop_Int], TG_Stunned_U[TG_Loop_Int], TG_Dmg, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
          
        else
                //Move Dummy and Create Effects
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] + 1
            set TG_Process[TG_Loop_Int] = TG_Process[TG_Loop_Int] + TG_Speed
          
            set TG_Dummy_Effect[TG_Loop_Int] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
            call GroupAddUnit(TG_Effects_Group, TG_Dummy_Effect[TG_Loop_Int])
            call SaveUnitHandle(Hash, TG_Loop_Int, TG_Counter[TG_Loop_Int], TG_Dummy_Effect[TG_Loop_Int])
              
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
          
              
                //Pick Units Around Dummy
              
            call GroupEnumUnitsInRange(TG_Group, GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Aoe, null)
            loop
                set TG_Target[TG_Loop_Int] = FirstOfGroup(TG_Group)
                exitwhen TG_Max_Targets[TG_Loop_Int] == 0 or TG_Target[TG_Loop_Int] == null
                if GetWidgetLife(TG_Target[TG_Loop_Int]) > 0.405 and not IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(TG_Target[TG_Loop_Int], GetOwningPlayer(TG_Caster[TG_Index])) and IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_STRUCTURE) == false then
                    set TG_Stunned_U[TG_Loop_Int] = TG_Target[TG_Loop_Int]
                    set TG_Unit_Is_Grappled[TG_Loop_Int] = true
                  
                    TG_Max_Targets[TG_Loop_Int] = TG_Max_Targets[TG_Loop_Int] - 1
                  
                    set TG_Stun_Dummy[TG_Loop_Int] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h001', GetUnitX(TG_Caster[TG_Loop_Int]), GetUnitY(TG_Caster[TG_Loop_Int]), 270)
                    call UnitAddAbilityBJ( 'A00K', TG_Stun_Dummy[TG_Loop_Int])
                    call IssueTargetOrder(TG_Stun_Dummy[TG_Loop_Int], "thunderbolt", TG_Target[TG_Loop_Int])
                      
                
                endif
                call GroupRemoveUnit(TG_Group, TG_Target[TG_Loop_Int])
            endloop
        endif
          
        set TG_Loop_Int = TG_Loop_Int + 1
    endloop
  
endfunction
function Trig_Tentacle_Grapple_Actions takes nothing returns nothing
    set TG_Index = (TG_Index + 1)
    set TG_Caster[TG_Index] = GetTriggerUnit()
  
    local real xCaster = GetUnitX(TG_Caster[TG_Index])
    local real yCaster = GetUnitY(TG_Caster[TG_Index])
    local real xTarget = GetSpellTargetX()
    local real yTarget = GetSpellTargetY()
  
    set TG_Angel[TG_Index] = bj_RADTODEG * Atan2(yTarget - yCaster, xTarget - xCaster)
  
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 0 * Cos(TG_Angel[TG_Index] * bj_DEGTORAD), yCaster + 0 * Sin(TG_Angel[TG_Index] * bj_DEGTORAD), TG_Angel[TG_Index])
    call SetUnitPathing(TG_Dummy[TG_Index], false)
    //Animation, Sounds
    call SetUnitAnimationByIndex(TG_Caster[TG_Index], 5)
  
    //Addtional Settings
    set TG_Process[TG_Index] = 0
    set TG_Counter[TG_Index] = 0
    set TG_Unit_Is_Grappled[TG_Index] = false
    set TG_Grappled_U[TG_Index] = null
    set TG_Stun_Removed[TG_Index] = false
    set TG_Stun_Finished[TG_Index] = false
    set TG_Target[TG_Index] = null
    set TG_Dummy_Effect[TG_Index] = null
    set TG_Stun_Dummy[TG_Index] = null
    set TG_Stunned_U[TG_Index] = null
    set TG_Stun_Dur[TG_Index] = 0
    set TG_Max_Targets[TG_Index] = 1
    //Timer
    if TG_Index == 1 then
        call TimerStart(TG_Timer, 0.0312500, true, function Trig_Tentacle_Grapple_ActionsV2)
    endif
  
endfunction
function Trig_Tentacle_Grapple_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A00J'
endfunction
//===========================================================================
function InitTrig_Tentacle_Grapple takes nothing returns nothing
    set gg_trg_Tentacle_Grapple = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Tentacle_Grapple, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Tentacle_Grapple, Condition( function Trig_Tentacle_Grapple_Conditions ) )
    call TriggerAddAction( gg_trg_Tentacle_Grapple, function Trig_Tentacle_Grapple_Actions )
endfunction
Yes but you flush that child hashtable when it must be the TG_index child hashtable.
 
Level 7
Joined
Feb 9, 2021
Messages
301
Ok, let's try again, what's wrong?
Problems:

1. If I cast the spell while the first cast is not finished, dummies stay
2. The dummies are not created in the centre in front of the hero
3. I also get a fatal error if I cast the spell too many times.
4. If I cast the spell and instantly move, the dummy flies away somewhere randomly.

Features:
1. If the target unit is moved during the grapple, I need to create more effects for the "hook" to follow and change the direction of the previous effects accordingly
2. If the caster is moved, similar to the above
3. I want the hook to be connected to the caster. If the caster moves in the opposite direction from the hook, the hook should be connected accordingly ( I sent a picture before)
4. if the hook meets a tree, the end of the map or a wall, it comes back.
 

Attachments

  • Mammon.w3x
    118.4 KB · Views: 14
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,850
Question, what is this?
JASS:
#include "cj_types_priv.j"

globals

group g = CreateGroup()
hashtable Hash = InitHashtable()
unit E = null
boolexpr Base

endglobals

function Condition_Base takes player p,unit e returns boolean
   return IsUnitEnemy(e,p) && !IsUnitType(e,UNIT_TYPE_DEAD) && !IsUnitType(e,UNIT_TYPE_STRUCTURE)
endfunction

function BaseBool takes nothing returns boolean
   return GetUnitAbilityLevel(GetFilterUnit(),'Aloc') == 0 and not IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) and not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)
endfunction
 

// Получить угол между точками
function GetAngleBetweenPoints takes real x1, real x2, real y1, real y2 returns real
    real result = bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
    return result
endfunction

//Получить координату X с оффсетом
function GetXWithOffset takes real x, real distance, real angle returns real
    local real xWithOffset = x + distance * Cos(angle * bj_DEGTORAD)
    return xWithOffset
endfunction

//Получить координату Y с оффсетом
function GetYWithOffset takes real y, real distance, real angle returns real
    local real yWithOffset = y + distance * Sin(angle * bj_DEGTORAD)
    return yWithOffset
endfunction

//Получить расстояние между координатами
function GetDistanceBetweenCoordinates takes real x1, real x2, real y1, real y2 returns real
    local real dx = x2 - x1
    local real dy = y2 - y1
    return SquareRoot(dx * dx + dy * dy)
endfunction

//Установить позицию юнита
function setUnitPosition takes unit u, real x, real y returns nothing
    SetUnitX(u, x)
    SetUnitY(u, y)
    u = null
endfunction
[/Jass]
 
Level 7
Joined
Feb 9, 2021
Messages
301
Question, what is this?
JASS:
#include "cj_types_priv.j"

globals

group g = CreateGroup()
hashtable Hash = InitHashtable()
unit E = null
boolexpr Base

endglobals

function Condition_Base takes player p,unit e returns boolean
   return IsUnitEnemy(e,p) && !IsUnitType(e,UNIT_TYPE_DEAD) && !IsUnitType(e,UNIT_TYPE_STRUCTURE)
endfunction

function BaseBool takes nothing returns boolean
   return GetUnitAbilityLevel(GetFilterUnit(),'Aloc') == 0 and not IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) and not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)
endfunction


// Получить угол между точками
function GetAngleBetweenPoints takes real x1, real x2, real y1, real y2 returns real
    real result = bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
    return result
endfunction

//Получить координату X с оффсетом
function GetXWithOffset takes real x, real distance, real angle returns real
    local real xWithOffset = x + distance * Cos(angle * bj_DEGTORAD)
    return xWithOffset
endfunction

//Получить координату Y с оффсетом
function GetYWithOffset takes real y, real distance, real angle returns real
    local real yWithOffset = y + distance * Sin(angle * bj_DEGTORAD)
    return yWithOffset
endfunction

//Получить расстояние между координатами
function GetDistanceBetweenCoordinates takes real x1, real x2, real y1, real y2 returns real
    local real dx = x2 - x1
    local real dy = y2 - y1
    return SquareRoot(dx * dx + dy * dy)
endfunction

//Установить позицию юнита
function setUnitPosition takes unit u, real x, real y returns nothing
    SetUnitX(u, x)
    SetUnitY(u, y)
    u = null
endfunction
[/Jass]
This part is not related to the spell in question, yet. I started to pre-load some functions to make the code simpler.
 
Level 24
Joined
Jun 26, 2020
Messages
1,850
Make it comments. However, I see no reason why you should do it. This code works perfectly fine.
To you, but well.
1615860538343.png

And you script is full of stuff that is not in normal Jass like not initialize the local variables in the top or miss words like "set" or "local".
And you can't say me there is nothing related to the Tentacle graple, because there initialize a Hashtable that you use in Tentacle Graple.
 
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,850
@HerlySQR , you just have to make some changes to his code to make it work, such as changing the && to and and adding keywords. That piece of code also imports a package that might need to be addressed as well.
Nah, what laziness, also I wanna @maxodors explains me what is that but well,

@maxodors, also I made my best to fix the code and I think is done, because now it can be multicasted without problem
The main things I changed are:
1) I added this function
vJASS:
call UnitRemoveBuffBJ( 'B00C', TG_Stunned_U[TG_Loop_Int] )
to the first if block, just in case, because the stun is not removed automatically if the unit is paused.
2) In the part of
vJASS:
if GetWidgetLife(TG_Target[TG_Loop_Int]) > 0.405 and not IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(TG_Target[TG_Loop_Int], GetOwningPlayer(TG_Caster[TG_Index])) and IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_STRUCTURE) == false then
is a mistake, you wrote "TG_Index" instead of "TG_Loop_Int" in some part.
3) I add to
vJASS:
elseif TG_Unit_Is_Grappled[TG_Loop_Int] and UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') and TG_Stun_Dur[TG_Loop_Int] < TG_Stun_Max_Dur then
this part
vJASS:
TG_Stunned_U[TG_Loop_Int] != null
because you can't check if has a buff a no unit
4) I changed
vJASS:
elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") or TG_Process[TG_Loop_Int] > TG_Dist or TG_Unit_Is_Grappled[TG_Loop_Int] and not UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') then
for
vJASS:
elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") or TG_Process[TG_Loop_Int] > TG_Dist or TG_Stun_Finished[TG_Loop_Int] or TG_Stun_Removed[TG_Loop_Int] then
because is better end the spell if some of the unique two variable conditions to end you have are true.
5) I changed
vJASS:
if TG_Loop_Int == 0 then
    call PauseTimer(TG_Timer)
            
endif
for
vJASS:
if TG_Index == 0 then
    call PauseTimer(TG_Timer)
            
endif
because that was a mistake
6) I added
vJASS:
call GroupClear(TG_Group)
to the part of the group loop, just in case.
JASS:
globals
    integer TG_Index = 0
    unit array TG_Caster
    unit array TG_Dummy
    unit array TG_Dummy_Effect
    unit array TG_Grappled_U
    unit array TG_Target
    unit array TG_Stun_Dummy
    unit array TG_Stunned_U
    unit TG_Effect
    real array TG_Angel
    real array TG_Process
    real array TG_Stun_Dur
    real array TG_Max_Targets
    integer array TG_Counter
    timer TG_Timer = CreateTimer()
    timer TG_Stun_Timer = CreateTimer()
    integer TG_Loop_Int = 0
    integer TG_Hash = 0
    group TG_Group = CreateGroup()
    group TG_Effects_Group = CreateGroup()
    boolean array TG_Unit_Is_Grappled
    boolean array TG_Stun_Removed
    boolean array TG_Stun_Finished
    
        //configurations
    real TG_Stun_Max_Dur = 3.0
    real TG_Dmg = 100 * 0.0312500 / TG_Stun_Max_Dur
    real TG_Dist = 800
    real TG_Speed = 1300 * 0.0312500
    real TG_Aoe = 64
    
endglobals

function Trig_Tentacle_Grapple_ActionsV2 takes nothing returns nothing
    set TG_Loop_Int = 1
    loop
        exitwhen TG_Loop_Int > TG_Index
         
        if TG_Stun_Dur[TG_Loop_Int] >= TG_Stun_Max_Dur and not TG_Stun_Finished[TG_Loop_Int] then
            call UnitRemoveBuffBJ( 'B00C', TG_Stunned_U[TG_Loop_Int] )
            call IssueImmediateOrder(TG_Caster[TG_Loop_Int], "stop")
            call BJDebugMsg("StunFinished")
            set TG_Stun_Finished[TG_Loop_Int] = true
        
        elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") and TG_Unit_Is_Grappled[TG_Loop_Int] and not TG_Stun_Removed[TG_Loop_Int] and not TG_Stun_Finished[TG_Loop_Int] then
            call UnitRemoveBuffBJ( 'B00C', TG_Stunned_U[TG_Loop_Int] )
            call BJDebugMsg("RemoveStun")
            set TG_Stun_Removed[TG_Loop_Int] = true
            
        elseif GetUnitCurrentOrder(TG_Caster[TG_Loop_Int]) != String2OrderIdBJ("absorb") or TG_Process[TG_Loop_Int] > TG_Dist or TG_Stun_Finished[TG_Loop_Int] or TG_Stun_Removed[TG_Loop_Int] then
            //after the stun finished, return the dummy and delete Effects 
                
            set TG_Effect = LoadUnitHandle(Hash, TG_Loop_Int, TG_Counter[TG_Loop_Int])
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] - 1
            //set TG_Effect = LoadUnitHandleBJ((TG_Counter[TG_Loop_Int] + 1 ), TG_Loop_Int, Hash)
            call RemoveUnit(TG_Effect)
            call GroupRemoveUnit(TG_Effects_Group,TG_Effect)
                
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos((TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin((TG_Angel[TG_Loop_Int] - 180) * bj_DEGTORAD))
            
            if TG_Counter[TG_Loop_Int] == 0 then
                call RemoveUnit(TG_Dummy[TG_Loop_Int])
                    
                set TG_Caster[TG_Loop_Int] = TG_Caster[TG_Index]
                set TG_Dummy[TG_Loop_Int] = TG_Dummy[TG_Index]
                set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
                set TG_Unit_Is_Grappled[TG_Loop_Int] = TG_Unit_Is_Grappled[TG_Index]
                set TG_Stun_Removed[TG_Loop_Int] = TG_Stun_Removed[TG_Index]
                set TG_Stun_Finished[TG_Loop_Int] = TG_Stun_Finished[TG_Index]
                set TG_Dummy_Effect[TG_Loop_Int] = TG_Dummy_Effect[TG_Index]
                set TG_Grappled_U[TG_Loop_Int] = TG_Grappled_U[TG_Index]
                set TG_Target[TG_Loop_Int] = TG_Target[TG_Index]
                set TG_Stun_Dummy[TG_Loop_Int] = TG_Stun_Dummy[TG_Index]
                set TG_Stunned_U[TG_Loop_Int] = TG_Stunned_U[TG_Index]
                set TG_Angel[TG_Loop_Int] = TG_Angel[TG_Index]
                set TG_Process[TG_Loop_Int] = TG_Process[TG_Index]
                set TG_Stun_Dur[TG_Loop_Int] = TG_Stun_Dur[TG_Index]
                set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Index]
                set TG_Max_Targets[TG_Loop_Int] = TG_Max_Targets[TG_Index]
                
                set TG_Hash=1
                loop
                    exitwhen TG_Hash>TG_Counter[TG_Index]
                    call SaveUnitHandle(Hash, TG_Loop_Int, TG_Hash, LoadUnitHandle(Hash, TG_Index, TG_Hash))
                    set TG_Hash=TG_Hash+1
                endloop
            
                set TG_Index = TG_Index - 1
                set TG_Loop_Int = TG_Loop_Int - 1
                
                if TG_Index == 0 then
                    call PauseTimer(TG_Timer)
            
                endif
            endif
        elseif TG_Unit_Is_Grappled[TG_Loop_Int] and TG_Stunned_U[TG_Loop_Int] != null and UnitHasBuffBJ(TG_Stunned_U[TG_Loop_Int], 'B00C') and TG_Stun_Dur[TG_Loop_Int] < TG_Stun_Max_Dur then
            set TG_Stun_Dur[TG_Loop_Int] = TG_Stun_Dur[TG_Loop_Int] + 0.0312500
            call UnitDamageTarget(TG_Caster[TG_Loop_Int], TG_Stunned_U[TG_Loop_Int], TG_Dmg, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, null)
            
        else
                //Move Dummy and Create Effects
            set TG_Counter[TG_Loop_Int] = TG_Counter[TG_Loop_Int] + 1
            set TG_Process[TG_Loop_Int] = TG_Process[TG_Loop_Int] + TG_Speed
            
            set TG_Dummy_Effect[TG_Loop_Int] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h003', GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Angel[TG_Loop_Int])
            call GroupAddUnit(TG_Effects_Group, TG_Dummy_Effect[TG_Loop_Int])
            call SaveUnitHandle(Hash, TG_Loop_Int, TG_Counter[TG_Loop_Int], TG_Dummy_Effect[TG_Loop_Int])
                
            call SetUnitX(TG_Dummy[TG_Loop_Int], GetUnitX(TG_Dummy[TG_Loop_Int]) + TG_Speed * Cos(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
            call SetUnitY(TG_Dummy[TG_Loop_Int], GetUnitY(TG_Dummy[TG_Loop_Int]) + TG_Speed * Sin(TG_Angel[TG_Loop_Int] * bj_DEGTORAD))
            
                
                //Pick Units Around Dummy
                
            call GroupEnumUnitsInRange(TG_Group, GetUnitX(TG_Dummy[TG_Loop_Int]), GetUnitY(TG_Dummy[TG_Loop_Int]), TG_Aoe, null)
            loop
                set TG_Target[TG_Loop_Int] = FirstOfGroup(TG_Group)
                exitwhen TG_Max_Targets[TG_Loop_Int] == 0 or TG_Target[TG_Loop_Int] == null
                if GetWidgetLife(TG_Target[TG_Loop_Int]) > 0.405 and not IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_MAGIC_IMMUNE) and IsUnitEnemy(TG_Target[TG_Loop_Int], GetOwningPlayer(TG_Caster[TG_Loop_Int])) and not IsUnitType(TG_Target[TG_Loop_Int], UNIT_TYPE_STRUCTURE) then
                    set TG_Stunned_U[TG_Loop_Int] = TG_Target[TG_Loop_Int]
                    set TG_Unit_Is_Grappled[TG_Loop_Int] = true
                    
                    set TG_Max_Targets[TG_Loop_Int] = TG_Max_Targets[TG_Loop_Int] - 1
                    
                    set TG_Stun_Dummy[TG_Loop_Int] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Loop_Int]), 'h001', GetUnitX(TG_Caster[TG_Loop_Int]), GetUnitY(TG_Caster[TG_Loop_Int]), 270)
                    call UnitAddAbilityBJ( 'A00K', TG_Stun_Dummy[TG_Loop_Int])
                    call IssueTargetOrder(TG_Stun_Dummy[TG_Loop_Int], "thunderbolt", TG_Target[TG_Loop_Int])
                        
                    call GroupClear(TG_Group)
                endif
                call GroupRemoveUnit(TG_Group, TG_Target[TG_Loop_Int])
            endloop
        endif
            
        set TG_Loop_Int = TG_Loop_Int + 1
    endloop
    

endfunction


function Trig_Tentacle_Grapple_Actions takes nothing returns nothing
    local real xCaster = GetUnitX(TG_Caster[TG_Index])
    local real yCaster = GetUnitY(TG_Caster[TG_Index])
    local real xTarget = GetSpellTargetX()
    local real yTarget = GetSpellTargetY()
    
    set TG_Angel[TG_Index] = bj_RADTODEG * Atan2(yTarget - yCaster, xTarget - xCaster)
    
    set TG_Dummy[TG_Index] = CreateUnit(GetOwningPlayer(TG_Caster[TG_Index]), 'h003', xCaster + 0 * Cos(TG_Angel[TG_Index] * bj_DEGTORAD), yCaster + 0 * Sin(TG_Angel[TG_Index] * bj_DEGTORAD), TG_Angel[TG_Index])
    call SetUnitPathing(TG_Dummy[TG_Index], false)
    //Animation, Sounds
    call SetUnitAnimationByIndex(TG_Caster[TG_Index], 5)
    
    //Addtional Settings
    set TG_Process[TG_Index] = 0
    set TG_Counter[TG_Index] = 0
    set TG_Unit_Is_Grappled[TG_Index] = false
    set TG_Grappled_U[TG_Index] = null
    set TG_Stun_Removed[TG_Index] = false
    set TG_Stun_Finished[TG_Index] = false
    set TG_Target[TG_Index] = null
    set TG_Dummy_Effect[TG_Index] = null
    set TG_Stun_Dummy[TG_Index] = null
    set TG_Stunned_U[TG_Index] = null
    set TG_Stun_Dur[TG_Index] = 0
    set TG_Max_Targets[TG_Index] = 1
    //Timer
    if TG_Index == 1 then
        call TimerStart(TG_Timer, 0.0312500, true, function Trig_Tentacle_Grapple_ActionsV2)
    endif
    
endfunction


function Trig_Tentacle_Grapple_Conditions takes nothing returns boolean
    if GetSpellAbilityId() == 'A00J' then
        set TG_Index = (TG_Index + 1)
        set TG_Caster[TG_Index] = GetTriggerUnit()
        return true
    endif
    return false
endfunction



//===========================================================================
function InitTrig_Tentacle_Grapple takes nothing returns nothing
    set gg_trg_Tentacle_Grapple = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Tentacle_Grapple, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Tentacle_Grapple, Condition( function Trig_Tentacle_Grapple_Conditions ) )
    call TriggerAddAction( gg_trg_Tentacle_Grapple, function Trig_Tentacle_Grapple_Actions )
endfunction

One problem is if the target dies while is stunned the chains continues the process like anything happens but it can't grab another unit, but I think you can solve it easily.

Also:
1. If the target unit is moved during the grapple, I need to create more effects for the "hook" to follow and change the direction of the previous effects accordingly
2. If the caster is moved, similar to the above
3. I want the hook to be connected to the caster. If the caster moves in the opposite direction from the hook, the hook should be connected accordingly ( I sent a picture before)
For this you have to do A LOT of extra work, I don't know if you wanna, but lets see
4. if the hook meets a tree, the end of the map or a wall, it comes back.
For this you just have to comparte the path of the terrain your dummy is gonna move
 
Status
Not open for further replies.
Top