Spell Omnislash v1.02

emjlr3

Change can be a good thing
Reaction score
395
Omnislash v1.03

A DotA-like Omnislash, made in vJASS. Given the recent increased activity in Tinkis dated Omnislash thread, I figured I would dust off this old favorite, give it an overhall, and release it to the public.

Contains two versions. The Classic Omnislash, and an OnDamage Omnislash, which uses the heroes actual attack to deal the damage, as opposed to triggering it.

Import Difficulty: Low

Units Affected:
Enemy, ground, non-structure, non-magic immune, visible

Target Type: Single unit - Enemy, ground, organic

Spell Info
:

Quickly move around the battlefield, slashing many enemies. You are invulnerable for the duration. Deals between 150 and 250 damage per slash.

Level 1 - Attacks 3 times.
Level 2 - Attacks 5 times.
Level 3 - Attacks 8 times.

The Classic Omnislash requires only Timer Utils to function. The OnDamage Omnislash requires Timer Utils and IDDS (which requires Table 3.0). It also makes use of an attack speed modifying item ability, which can make the hero attack much faster when attacking during the Omnislash.

Classic:
JASS:
scope Omnislash initializer Init

globals
    private constant integer AbilId = 'A000'
    private constant real TimerInterval = .4
    private constant string BlinkFX = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"
    private constant string CasterAttachPoint = "weapon"
    private constant string CasterFX = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl"
    private constant string SlashAnimation = "attack"
endglobals
private constant function MaxSlashes takes integer lvl returns integer
    if lvl<3 then
        return 1+2*lvl
    else
        return 8
    endif
endfunction
private function SlashDamage takes integer lvl returns real
    return GetRandomReal(150.,250.) // This can be changed to a constant function if you update it to use an equation
endfunction
private constant function TargetAoE takes integer lvl returns real
    return 475.
endfunction
private constant function TargetBoolean takes unit caster, unit target returns boolean
    return true
endfunction

//== No touching past this point ==\\
globals
    private group G = CreateGroup()
    private group OmniGroup = CreateGroup()
    private unit Targ
    private boolexpr B
    private integer I
endglobals
private struct data
    unit u
    unit targ
    player p
    effect fx
    timer t
    integer lvl
    integer count = 1
    integer max
    real aoe
    
    method onDestroy takes nothing returns nothing
        call DestroyEffect(.fx)
        call ReleaseTimer(.t)
        call SetUnitInvulnerable(.u,false)
        call SetUnitVertexColor(.u,255,255,255,255)
        call GroupRemoveUnit(OmniGroup,.u)
        call SelectUnit(.u, GetLocalPlayer()==.p)
    endmethod
endstruct

private function Filt takes nothing returns boolean
    local data d = I
    
    set Targ = GetFilterUnit()    
    if TargetBoolean(d.u,Targ) and IsUnitEnemy(Targ,d.p) and GetWidgetLife(Targ)>.405 and IsUnitType(Targ,UNIT_TYPE_STRUCTURE)==false and IsUnitType(Targ,UNIT_TYPE_MAGIC_IMMUNE)==false and not IsUnitHidden(Targ) then
        return true
    endif
    return false
endfunction
private function DoSlash takes data d, real ang, real off returns nothing
    call SetUnitPosition(d.u,GetUnitX(d.targ)+off*Cos(ang),GetUnitY(d.targ)+off*Sin(ang))
    call DestroyEffect(AddSpecialEffectTarget(BlinkFX, d.u, "chest"))
    call UnitDamageTarget(d.u,d.targ,SlashDamage(d.lvl), false, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, null)
    call SetUnitAnimation(d.u,SlashAnimation)
endfunction
private function Effects takes nothing returns nothing
    local data d = GetTimerData(GetExpiredTimer())
    
    set d.count = d.count + 1
    call GroupClear(G)
    set I = d
    call GroupEnumUnitsInRange(G,GetUnitX(d.u),GetUnitY(d.u),d.aoe,B)
    set d.targ = FirstOfGroup(G)
    if d.targ!=null then
        call DoSlash(d,GetRandomReal(1., 360.)*bj_DEGTORAD,GetRandomReal(0., 85.)+100.)
    endif
    
    if d.count>=d.max or d.targ==null then 
        call d.destroy()
    endif
endfunction
private function Actions takes nothing returns nothing
    local data d = data.create()
    local real ang = GetRandomReal(1., 360.)*bj_DEGTORAD
    local real off = GetRandomReal(0., 85.)+100.
    
    set d.u = GetTriggerUnit()
    set d.targ = GetSpellTargetUnit()
    set d.p = GetOwningPlayer(d.u)
    set d.fx = AddSpecialEffectTarget(CasterFX, d.u, CasterAttachPoint)
    set d.lvl = GetUnitAbilityLevel(d.u,AbilId)
    set d.max = MaxSlashes(d.lvl)
    set d.aoe = TargetAoE(d.lvl)
    set d.t = NewTimer()

    call SetUnitInvulnerable(d.u, true)
    call SetUnitVertexColor(d.u, 255, 255, 255, 100)
    if GetLocalPlayer()==d.p then
        call SelectUnit(d.u, false)
    endif
    call GroupAddUnit(OmniGroup,d.u)
    call DoSlash(d,ang,off)

    call SetTimerData(d.t,d)
    call TimerStart(d.t, TimerInterval, true, function Effects)
endfunction

private function SpellConditions takes nothing returns boolean
    if GetSpellAbilityId() == AbilId then
        call Actions()
    endif
    return false
endfunction
private function SelectConditions takes nothing returns boolean
    if IsUnitInGroup(GetTriggerUnit(),OmniGroup) and GetLocalPlayer()==GetOwningPlayer(GetTriggerUnit()) then
        call SelectUnit(GetTriggerUnit(),false)
    endif
    return false
endfunction
private function Init takes nothing returns nothing
    local trigger t1 = CreateTrigger()
    local trigger t2 = CreateTrigger()
    local integer i = 0
    
    loop
        exitwhen i>bj_MAX_PLAYERS
        call TriggerRegisterPlayerUnitEvent(t1,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
        call TriggerRegisterPlayerUnitEvent(t2,Player(i),EVENT_PLAYER_UNIT_SELECTED,null)
        set i = i + 1
    endloop
    call TriggerAddCondition(t1, Condition( function SpellConditions ))
    call TriggerAddCondition(t2, Condition( function SelectConditions ))
    set B = Condition(function Filt)
    call Preload(BlinkFX)
    call Preload(CasterFX)
endfunction

endscope

OnDamage:
JASS:
scope OnDamageOmnislash initializer Init

globals
    private constant integer AbilId = 'A000'
    private constant integer AttackSpeedId = 'A001'
    private constant real TimerInterval = 1.5
    private constant string BlinkFX = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"
    private constant string CasterAttachPoint = "weapon"
    private constant string CasterFX = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl"
endglobals
private constant function MaxSlashes takes integer lvl returns integer
    if lvl<3 then
        return 1+2*lvl
    else
        return 8
    endif
endfunction
private constant function TargetAoE takes integer lvl returns real
    return 475.
endfunction
private constant function TargetBoolean takes unit caster, unit target returns boolean
    return true
endfunction

//== No touching past this point ==\\
private keyword data
globals
    private group G = CreateGroup()
    private group OmniGroup = CreateGroup()
    private unit Targ
    private boolexpr B
    private integer I
    private data array D
endglobals
private struct data
    unit u
    unit targ
    player p
    effect fx
    timer t
    integer lvl
    integer count = 1
    integer max
    real aoe
    
    method onDestroy takes nothing returns nothing
        call UnitRemoveAbility(.u,AttackSpeedId)
        call DestroyEffect(.fx)
        call ReleaseTimer(.t)
        call SetUnitInvulnerable(.u,false)
        call SetUnitVertexColor(.u,255,255,255,255)
        call GroupRemoveUnit(OmniGroup,.u)
        call SelectUnit(.u, GetLocalPlayer()==.p)
    endmethod
endstruct

private function Filt takes nothing returns boolean
    local data d = I
    
    set Targ = GetFilterUnit()    
    if TargetBoolean(d.u,Targ) and IsUnitEnemy(Targ,d.p) and GetWidgetLife(Targ)>.405 and IsUnitType(Targ,UNIT_TYPE_STRUCTURE)==false and IsUnitType(Targ,UNIT_TYPE_MAGIC_IMMUNE)==false and not IsUnitHidden(Targ) then
        return true
    endif
    return false
endfunction
private function DoSlash takes data d, real ang, real off returns nothing
    call SetUnitPosition(d.u,GetUnitX(d.targ)+off*Cos(ang),GetUnitY(d.targ)+off*Sin(ang))
    call DestroyEffect(AddSpecialEffectTarget(BlinkFX, d.u, "chest"))
    call IssueTargetOrder(d.u,"attack",d.targ)
endfunction
private function Effects takes nothing returns nothing
    local data d = GetTimerData(GetExpiredTimer())
    
    set d.count = d.count + 1
    call GroupClear(G)
    set I = d
    call GroupEnumUnitsInRange(G,GetUnitX(d.u),GetUnitY(d.u),d.aoe,B)
    set d.targ = FirstOfGroup(G)
    if d.targ!=null then
        call DoSlash(d,GetRandomReal(1., 360.)*bj_DEGTORAD,GetRandomReal(0., 85.)+100.)
    endif
    
    if d.count>=d.max or d.targ==null then 
        call d.destroy()
    endif
endfunction
private function Actions takes nothing returns nothing
    local data d = data.create()
    local real ang = GetRandomReal(1., 360.)*bj_DEGTORAD
    local real off = GetRandomReal(0., 85.)+100.
    
    set d.u = GetTriggerUnit()
    set d.targ = GetSpellTargetUnit()
    set d.p = GetOwningPlayer(d.u)
    set d.fx = AddSpecialEffectTarget(CasterFX, d.u, CasterAttachPoint)
    set d.lvl = GetUnitAbilityLevel(d.u,AbilId)
    set d.max = MaxSlashes(d.lvl)
    set d.aoe = TargetAoE(d.lvl)
    set d.t = NewTimer()

    set D[GetHandleId(d.u)-0x100000] = d
    call UnitAddAbility(d.u,AttackSpeedId)
    call SetUnitInvulnerable(d.u, true)
    call SetUnitVertexColor(d.u, 255, 255, 255, 100)
    if GetLocalPlayer()==d.p then
        call SelectUnit(d.u, false)
    endif
    call GroupAddUnit(OmniGroup,d.u)
    call DoSlash(d,ang,off)

    call SetTimerData(d.t,d)
    call TimerStart(d.t, TimerInterval, false, function Effects)
endfunction

private function DamageDone takes nothing returns nothing
    local data d = D[GetHandleId(GetTriggerDamageSource())-0x100000]
    
    call PauseTimer(d.t)
    call TimerStart(d.t,0.,false,function Effects)
endfunction

private function SpellConditions takes nothing returns boolean
    if GetSpellAbilityId() == AbilId then
        call Actions()
    endif
    return false
endfunction
private function SelectConditions takes nothing returns boolean
    if IsUnitInGroup(GetTriggerUnit(),OmniGroup) and GetLocalPlayer()==GetOwningPlayer(GetTriggerUnit()) then
        call SelectUnit(GetTriggerUnit(),false)
    endif
    return false
endfunction
private function DamageConditions takes nothing returns boolean
    if IsUnitInGroup(GetTriggerDamageSource(),OmniGroup) then
        call DamageDone()
    endif
    return false
endfunction
private function Init takes nothing returns nothing
    local trigger t1 = CreateTrigger()
    local trigger t2 = CreateTrigger()
    local trigger t3 = CreateTrigger()
    local integer i = 0
    
    loop
        exitwhen i>bj_MAX_PLAYERS
        call TriggerRegisterPlayerUnitEvent(t1,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
        call TriggerRegisterPlayerUnitEvent(t2,Player(i),EVENT_PLAYER_UNIT_SELECTED,null)
        set i = i + 1
    endloop
    call TriggerRegisterDamageEvent(t3,1)
    call TriggerAddCondition(t1, Condition( function SpellConditions ))
    call TriggerAddCondition(t2, Condition( function SelectConditions ))
    call TriggerAddCondition(t3, Condition( function DamageConditions ))
    set B = Condition(function Filt)
    call Preload(BlinkFX)
    call Preload(CasterFX)
endfunction

endscope


Updates:
1.03
  • Ported to patch 1.24+
1.02
  • Minor code updates
1.01
  • Changed configurable functions to constants where applicable
 

Attachments

  • omni.jpg
    omni.jpg
    69.3 KB · Views: 465
  • Omnislash v1.03.w3x
    162.9 KB · Views: 273

Gtam

Lerning how to write and read!! Yeah.
Reaction score
164
Nice i like:D does the ondamage omnislash also take damage bonus as well:confused:
 

emjlr3

Change can be a good thing
Reaction score
395
the hero attacks its targets - any and all bonuses are carried over along with the attack
 

Viikuna

No Marlo no game.
Reaction score
265
That onDamage omnislash looks pretty neat.

Just one thing:

JASS:
set d.targ = FirstOfGroup(G)


I think you should use somekind of get random unit from group function, because FirstOfGroup is not really so random, if I remember correctly.

( There was some thread in wc3c about this, if my memory serves me right. Grouping order is affected by units handle id or something and preplaced units tend to get picked before other and stuff like that )

It would kinda suck, if your hero gets slashed more than some other guys hero, because of some silly stuff like that.
 

emjlr3

Change can be a good thing
Reaction score
395
I think the results were inconclusive - unless you can link to something I haven't read

I clearly stated why I released this - read the damn thread topic post
 

Romek

Super Moderator
Reaction score
963

Jesus4Lyf

Good Idea™
Reaction score
397
JASS:
private function TargetBoolean takes unit caster, unit target returns boolean

I think this one needs a more descriptive name. I don't know what it does.

Also, all the config functions should be constant functions. Constant functions are functions which don't change anything - they state of the game before calling it is the same as the state after. :)

Haven't actually seen the spell, but I do remember where the Omnislash spells are in case I ever want to see what makes them tick. So thanks. Looks good. :)
 

emjlr3

Change can be a good thing
Reaction score
395
I dont think they can be

return 50. can be constant
return lvl*50/3. can't, I think, maybe it can
 

emjlr3

Change can be a good thing
Reaction score
395
added links to required systems

also changed configurable functions to constants where applicable
 

emjlr3

Change can be a good thing
Reaction score
395
BUMP, if no objections, I will approve it
 

Romek

Super Moderator
Reaction score
963
JASS:
 //
        if GetLocalPlayer()==.p then
            call SelectUnit(.u, true)
        endif

Could be:
JASS:
            call SelectUnit(.u, GetLocalPlayer()==.p)


In 'Filt', how about using a global, temporary variable instead of a local? That simple filter function looks horrible because of the nulling. :p

JASS:
private function SelectConditions takes nothing returns boolean
    if IsUnitInGroup(GetTriggerUnit(),OmniGroup) then
        if GetLocalPlayer()==GetOwningPlayer(GetTriggerUnit()) then
            call SelectUnit(GetTriggerUnit(),false)
        endif
    endif
    return false
endfunction

Could be:
JASS:
private function SelectConditions takes nothing returns boolean
    if IsUnitInGroup(GetTriggerUnit(),OmniGroup) and GetLocalPlayer()==GetOwningPlayer(GetTriggerUnit()) then
        call SelectUnit(GetTriggerUnit(),false)
    endif
    return false
endfunction

I don't think inlining the 'if' would work well here. It'd be long, and it'd select the unit if the conditions aren't matched.
 

emjlr3

Change can be a good thing
Reaction score
395
updated and approved
 

emjlr3

Change can be a good thing
Reaction score
395
its been reviewed twice, and Romeks latest was menial, i made the requested changes and approved, whats the problem with removing some of the burden from other mods?
 

DubDub

New Member
Reaction score
0
I can't play the demo-map. Testing it just sends me to the menu screen. Is the problem with TimeUtils? I read that there's a new table (3.0), and I'm wondering if swapping out the old one with the new one will help solve the problem. :) I'm hoping it's that simple.
 

Viikuna

No Marlo no game.
Reaction score
265
Yep. Updating Table and TimerUtils should do the job.

The spell code itself should work in new patch, Its just these data storage utilies that have to be updated.
 

Sim

Forum Administrator
Staff member
Reaction score
534
Thanks!

Naturally, if the author is to fix these spells, they will most probably be approved once again!
 
General chit-chat
Help Users
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top