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

[Solved] Making a JASS spell into a passive

Status
Not open for further replies.
Level 13
Joined
Oct 12, 2016
Messages
769
I want to take @Tank-Commander 's Massive Cleave V1.01 spell and turn it into a passive ability with a 33% or 50% chance to trigger on an attack for 3 specific unit types. (Asking for permission at the same time in this thread)

I've tried replacing the triggering event with "EVENT_PLAYER_UNIT_ATTACKED", but I'm too inept at JASS to figure out how to detect the attacking unit types, or to figure out how to add a random integer check for the effect.

Would any care to walk me through changing the triggering event and conditions, or help me out?
 
Last edited:
You could do the UnitType check & Random in GUI, but regardless you would need an alternative version of function MC_StartCleave takes nothing returns boolean or change it (the function needs to use the unit & player references for Attacking Event/ DamageSource Event )

and this alternative version you call with custom script


  • Massive Cleave Passive
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • Or - Any (Conditions) are true
        • Conditions
          • (Unit-type of (Attacking unit)) Equal to Cleaver Dragon
          • (Unit-type of (Attacking unit)) Equal to Massive Demon
      • (Random integer number between 1 and 100) Less than or equal to 30
    • Actions
      • Custom script: call MC_StartCleave_Attack()
"MC_StartCleave_Attack" Does not exist yet one would have to write it first.


Massive Cleave is channeled this code stops casters not channeling.
JASS:
            if not(GetUnitCurrentOrder(udg_MC_Unit[Node]) == MC_OrderID()) then
                set udg_MC_Delay[Node] = 0.
                set udg_MC_Angle[Node] = udg_MC_AngleLimit[Node]
            endif


To change the event you need to change that line
JASS:
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
->
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ATTACKED )
 
Level 13
Joined
Oct 12, 2016
Messages
769
Yea, I did change the event to "EVENT_PLAYER_UNIT_ATTACKED,"
then I changed the condition check further up for the unit types.

But that didn't work. So, that node line prevents the action if it's not channeling? OK, didn't know that. I'll try this out.

EDIT:
I tried disabling those lines with "//" but that didn't get it to work. I think it's prudent to post the code with current changes:
Code:
////////////////////////////////////////////////////////////////////
//  Target filter function used to differentiate between units    //
//  that can be hit by the spell and those that cannot            //
////////////////////////////////////////////////////////////////////
function MC_TargetFilter takes unit u, player pl returns boolean
    return (not IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (GetUnitFlyHeight(u) <= MC_FilterMaxZ()) and (not IsUnitType(u, UNIT_TYPE_ANCIENT)) and (IsUnitEnemy(u, pl)) and (GetUnitTypeId(u) != MC_DummyID()) and not(IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0)
endfunction

////////////////////////////////////////////////////////////////////
//  Check cleave function used ot prevent two channels being done //
//  by the same unit at the same time                             //
////////////////////////////////////////////////////////////////////
function MC_CheckCleave takes unit u returns nothing
    local integer Node = udg_MC_NextNode[0]
 
    loop
        exitwhen (Node == 0) or (udg_MC_Unit[Node] == u)
        set Node = udg_MC_NextNode[Node]
    endloop
 
   set udg_MC_Delay[Node] = 0.
    set udg_MC_Angle[Node] = udg_MC_AngleLimit[Node]
endfunction

////////////////////////////////////////////////////////////////////
//  AbsAngle function used to convert negative angles (rads) into //
//  positive angles (rads), standardises angles so formulas can   //
//  be used effectively                                           //
////////////////////////////////////////////////////////////////////
function MC_AbsAngle takes real angle returns real
    if angle < 0. then
        return angle + MC_AngleCorrection()
    else
        return angle
    endif
endfunction
////////////////////////////////////////////////////////////////////
//  Validate Angle function used to check if a given unit is      //
//  within the boundaries of the current cleave segment           //
////////////////////////////////////////////////////////////////////
function MC_ValidateAngle takes real boundary, real boundary2, real angle returns boolean

    //Check what direction the cleave is going in
    //And then check if the angle is within the two boundaries
    if MC_Clockwise() then
 
        if boundary <= 0 and angle < boundary2 then
            set angle = angle - MC_AngleCorrection()
        endif
        return (boundary - angle >= -MC_CleaveAngleLet() and boundary2 - angle <= MC_CleaveAngleLet())
    else
 
        if boundary2 >=  MC_AngleCorrection() and angle < boundary then
            set angle = angle + MC_AngleCorrection()
        endif
        return (boundary2 - angle >= -MC_CleaveAngleLet() and boundary - angle <= MC_CleaveAngleLet())
    endif
 
    return false
endfunction

////////////////////////////////////////////////////////////////////
//  Loop function used to control the cleave, damaging units      //
//  knocking them in the air and creating effects                 //
////////////////////////////////////////////////////////////////////
function MC_MassiveCleaveLoop takes nothing returns nothing
    //Set up Locals
    local integer Node = udg_MC_NextNode[0]
    local real angle
    local real angle2
    local real tempReal
    local real x
    local real y
    local real x2
    local real y2
    local real dy
    local real dx
    local boolean bool
    local unit u
 
    //Cycle through each node
    loop
        exitwhen Node == 0
    
        //Check if it's time to create another cleave point
        if udg_MC_Delay[Node] < MC_TimerSpeed() then
    
            //Check if the cleave is finished
            if (udg_MC_Angle[Node] >= (udg_MC_AngleLimit[Node] - MC_CleaveAngleLet()) and not(MC_Clockwise())) or ((udg_MC_Angle[Node] <= (udg_MC_AngleLimit[Node] + MC_CleaveAngleLet())) and MC_Clockwise()) then
                set udg_MC_RecycleNodes[udg_MC_RecyclableNodes] = Node
                set udg_MC_RecyclableNodes = udg_MC_RecyclableNodes + 1
                set udg_MC_NextNode[udg_MC_PrevNode[Node]] = udg_MC_NextNode[Node]
                set udg_MC_PrevNode[udg_MC_NextNode[Node]] = udg_MC_PrevNode[Node]
                    
                //Pause timer if this was the last instance
                if (udg_MC_PrevNode[0] == 0) then
                    call PauseTimer(udg_MC_Timer)
                endif
            else
        
                set x = GetUnitX(udg_MC_Unit[Node])
                set y = GetUnitY(udg_MC_Unit[Node])
                set bool = false
                set udg_MC_Delay[Node] = udg_MC_DelayReset[Node]
                set angle = udg_MC_Angle[Node] + udg_MC_Division[Node]

                //Find units to cleave
                call GroupEnumUnitsInRange(udg_MC_TempGroup, x, y, udg_MC_AOE[Node], null)
                loop
                    set u = FirstOfGroup(udg_MC_TempGroup)
                    exitwhen u == null
            
                    //Valid target type
                    if  MC_TargetFilter(u, udg_MC_Player[Node]) then
                        set x2 = GetUnitX(u)
                        set y2 = GetUnitY(u)
                        set dy = y2 - y
                        set dx = x2 - x
                        set angle2 = Atan2(dy, dx)

                        if angle2 < 0. then
                            set angle2 = angle2 + MC_AngleCorrection()
                        endif
                
                        //Within the correct arc
                        if (MC_ValidateAngle(udg_MC_Angle[Node], angle, angle2)) then
                            set bool = true
                            //Damage unit and setup knockback data
                            call UnitDamageTarget(udg_MC_Unit[Node], u, udg_MC_HealthDamage[Node], true, false,  MC_AttackType(), MC_DamageType(), null)

                            //*********************************************
                            //Knockback (delete if not using BUS_Knockback)
                            if MC_Clockwise() then
                                set angle2 = angle2 - MC_KnockbackCleaveAngle()
                            else
                                set angle2 = angle2 + MC_KnockbackCleaveAngle()
                            endif

                            set tempReal = SquareRoot(dx * dx + dy * dy)

                            call BUS_StartKnockback(u, udg_MC_KnockbackForce[Node], x2 + tempReal * Cos(angle2), x2, y2 + tempReal * Sin(angle2), y2, 0., MC_PitchCalcVal())
                            //*********************************************
                        endif
                
                    endif
                    call GroupRemoveUnit(udg_MC_TempGroup, u)
                endloop
        
                //Create blood effect if a unit was hit
                if bool then
                    //Find position
                    set angle2 = udg_MC_Angle[Node] + udg_MC_AngleAdjust[Node]
            
                    //Create effect
                    set u = CreateUnit(MC_DummyPlayer(), MC_DummyID(), x + udg_MC_EffectDistance[Node] * Cos(angle2), y + udg_MC_EffectDistance[Node] * Sin(angle2), angle2 * bj_RADTODEG)
                    call SetUnitScale(u, udg_MC_CleaveScale[Node], 0., 0.)
                    call DestroyEffect(AddSpecialEffectTarget(MC_CleaveHitEffect(), u, MC_AttachmentPoint()))
                    call UnitApplyTimedLife(u, 'BHwe', MC_Timer())
                    set u = null
                endif
        
                //Move Position
                set udg_MC_Angle[Node] = angle
            endif

        else
            set udg_MC_Delay[Node] = udg_MC_Delay[Node] - MC_TimerSpeed()
        
            if not(GetUnitCurrentOrder(udg_MC_Unit[Node]) == MC_OrderID()) then
                set udg_MC_Delay[Node] = 0.
                set udg_MC_Angle[Node] = udg_MC_AngleLimit[Node]
            endif
        
        endif
    
        set Node = udg_MC_NextNode[Node]
    endloop
 
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to start the channel process when the abiliyt   //
//  has been cast                                                 //
////////////////////////////////////////////////////////////////////
function MC_StartCleave takes nothing returns boolean
    //Set up locals
    local integer Node
    local real level
    local real offset
    local real facing
    local unit u
 
    //Start the channel (WARK: Here I changed the condition check)
    if ( not (( GetRandomInt(1, 5) <= 2 )) and (( GetUnitTypeId(GetAttacker()) == 'noga' ) or ( GetUnitTypeId(GetAttacker()) == 'okod' ) or ( GetUnitTypeId(GetAttacker()) == 'nba2' )) ) then
        //Get unit and check for multiple channels
        set u = GetAttacker()
        call MC_CheckCleave(u)

        //Set up Node
        if (udg_MC_RecyclableNodes == 0) then
            set udg_MC_NodeNumber = udg_MC_NodeNumber + 1
            set Node = udg_MC_NodeNumber
        else
            set udg_MC_RecyclableNodes = udg_MC_RecyclableNodes - 1
            set Node = udg_MC_RecycleNodes[udg_MC_RecyclableNodes]
        endif

        set udg_MC_NextNode[Node] = 0
        set udg_MC_NextNode[udg_MC_PrevNode[0]] = Node
        set udg_MC_PrevNode[Node] = udg_MC_PrevNode[0]
        set udg_MC_PrevNode[0] = Node
    
        //Start timer if this is the only instance
        if (udg_MC_PrevNode[Node] == 0) then
            call TimerStart(udg_MC_Timer, MC_TimerSpeed(), true, function MC_MassiveCleaveLoop)
        endif
    
        //Set up cleave data
        set udg_MC_Unit[Node] = u
        set udg_MC_Player[Node] = GetTriggerPlayer()
        set level = GetUnitAbilityLevel(udg_MC_Unit[Node], MC_Ability())
        set facing = GetUnitFacing(udg_MC_Unit[Node]) * bj_DEGTORAD
        set offset = MC_Angle(level)

        if MC_Clockwise() then
            set udg_MC_Division[Node] = offset / -MC_Divisions(level)
            set offset = offset / 2
            set udg_MC_AngleLimit[Node] = MC_AbsAngle(facing + offset)
            set udg_MC_Angle[Node] = udg_MC_AngleLimit[Node] - offset * 2
        else
            set udg_MC_Division[Node] = offset / MC_Divisions(level)
            set offset = offset / -2
            set udg_MC_Angle[Node] = MC_AbsAngle(facing + offset)
            set udg_MC_AngleLimit[Node] = udg_MC_Angle[Node] - offset * 2
        endif
    
        set udg_MC_AOE[Node] = MC_AOE(level)
        set udg_MC_EffectDistance[Node] = MC_EffectDistance(level)
        set udg_MC_AngleAdjust[Node] = (udg_MC_Division[Node] / 2)
        set udg_MC_HealthDamage[Node] = MC_HealthDamage(level)
        set udg_MC_Delay[Node] = MC_InitialDelay(level)
        set udg_MC_DelayReset[Node] = MC_DivisionDelay(level)
        set udg_MC_KnockbackForce[Node] = MC_KnockbackForce(level)
        set udg_MC_CleaveScale[Node] = MC_CleaveHitScale(level)
    endif

    return false
endfunction

////////////////////////////////////////////////////////////////////
//  Function used to register the trigger of the ability          //
////////////////////////////////////////////////////////////////////
function InitTrig_Massive_Cleave takes nothing returns nothing
    //Set up local variables
    local trigger t = CreateTrigger()

    //Set up trigger
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_ATTACKED)
    call TriggerAddCondition(t, Condition(function MC_StartCleave))
 
endfunction
////////////////////////////////////////////////////////////////////
//END OF THE SPELL                                                //
////////////////////////////////////////////////////////////////////
Near the bottom, the event and condition is changed. However, it still doesn't do the effect for that unit type on attack.
If it helps, the "channel" ability was used previously for the spell. I don't want it to be ability based anymore.
 
Last edited:
Level 8
Joined
Mar 19, 2017
Messages
248
Remove this line of code:
if not(GetUnitCurrentOrder(udg_MC_Unit[Node]) == MC_OrderID()) then
set udg_MC_Delay[Node] = 0.
set udg_MC_Angle[Node] = udg_MC_AngleLimit[Node]
endif

You can even just set the Delay to 0. I've just tested this, and a delay of 0.5 looks smooth in the dragonscale dude.
To be fair, a delay on attack is pointless, since you can still cancel it, and the trigger will no destroy the instance if you ie. use stop or hold position to cancel the attack animation, it will still apply the damage and angle of that instance. You might be able to track if the unit stops (the unit will still try to autoattack everytime), so you reset all the instance fields (specially angle).

If i were to trigger such spell, i would simply use a damage detection system that can differenciate between normal auto attacks and spells.

Also in the StartCleave trigger the MC_Player should be GetOwningPlayer(GetAttacker()). Also the "level" field now becomes pointless since this ability now just depends on the unit that is attacking instead of an actual ability, but this will be tedious.

EDIT: here is the edited map.
 

Attachments

  • Massive Cleave V1.01.w3x
    98.3 KB · Views: 20
Level 13
Joined
Oct 12, 2016
Messages
769
Alright, that helps a lot. I've only got a basic understanding of JASS at the moment, and your explanation makes sense.
Thank you Disruptive and Tasyen! +rep

The whole attack instance thing, I know, is buggy with the "stop" command, and I've got a different way around that for how this'll be used.
 
Status
Not open for further replies.
Top