Spell Phantom Dash

WolfieeifloW

WEHZ Helper
Reaction score
372
Phantom Dash
Created By: WolfieeifloW

Requires NewGen and TimerUtils
Optionally can use GTrigger
Version:
1.0


Credits:
Spell editing, idea, and triggers were created by WolfieeifloW.


If you take the time to download / look at these, please at least leave a comment.
Don't forget to rate also (using the stars
rating_5.gif
) !

phantomdashpicture.jpg

Screenshots serve no justice, try the spell out ingame.


Features:
-Leakless
-Lagless
-Eye-candy
-MUI


Spell Description
Dash to a target point, dealing damage to all enemies hit. Leaves an image at each enemy behind to confuse opponents.
Level 1: 50 damage.
Level 2: 100 damage.
Level 3: 150 damage.
Level 4: 200 damage.
Cooldown: 20/18/16/14 seconds.


Spell Code
JASS:
// +---------------------------------------------------+
// |          -=-=- Phantom Dash [v1.0] -=-=-          |
// |            -=-=- By WolfieeifloW -=-=-            |
// |        Requires JASS NewGen and TimerUtils        |
// |            Optionally can use GTrigger            |
// +---------------------------------------------------+
// |   Dash to a target point, dealing damage and      |
// |    creating an illusion for each enemy hit        |
// +---------------------------------------------------+
// |   -=-=- How To Implement -=-=-                    |
// |      1. Copy TimerUtils into your map             |
// |       b. Copy GTrigger into your map              |
// |      2. Copy this trigger into your map           |
// |      3. Copy the Phantom Dash, and Phantom Dash   |
// |          (Dummy) abilities into your map          |
// |      4. Make sure the 'Rawcodes' in the trigger   |
// |          match your codes in the Object Editor    |
// |      5. Customize the spell                       |
// |      6. Enjoy!                                    |
// +---------------------------------------------------+
// |   -=-=- Credits -=-=-                             |
// |      Credits are not needed, but appreciated      |
// |       Just don't claim this as yours              |
// +---------------------------------------------------+
// |   -=-=- Version History -=-=-                     |
// |      Version 1.0                                  |
// |       - Initial release                           |
// +---------------------------------------------------+

library PhantomDash initializer Init requires TimerUtils optional GTrigger

    globals
// +-----------------------------------+
// |      -=-=- MODIFY HERE -=-=-      |
// |      -=-=- MODIFY HERE -=-=-      |
// |      -=-=- MODIFY HERE -=-=-      |
// +-----------------------------------+
        private constant integer ABILITY_ID = 'PHAN' //Rawcode of the "Phantom Dash" ability
        private constant integer ILLUSION_ID = 'ILLU' //Rawcode of the "Phantom Dash (Dummy)" ability
        private constant integer DUMMY_ID = 'dUMM' //Rawcode of the dummy unit
        private constant integer ORDER_ID = 852274 //Order Id of the "Wand of Illusion" item
        private constant real SPEED = 1500. //Speed the unit moves at during the dash
        private constant real RADIUS = 125. //Radius the unit hits units in - Don't go below 100
        private constant string EFFECT = "Abilities\\Spells\\Items\\AIam\\AIamTarget.mdl"
        private constant string ATTACH = "origin"
        private constant string ANIMATION = "walk" //Animation to play during Phantom Dash
        private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MELEE //Attack Type of Phantom Dash
        private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL //Damage Type of Phantom Dash
        private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS //Weapon Type of Phantom Dash
    endglobals
    
    private function Damage takes integer level returns real
        return level * 50. //Damage that Phantom Dash deals
    endfunction
// +----------------------------------------------+
// |      -=-=- DO NOT TOUCH PAST HERE -=-=-      |
// |      -=-=- DO NOT TOUCH PAST HERE -=-=-      |
// |      -=-=- DO NOT TOUCH PAST HERE -=-=-      |
// +----------------------------------------------+    
    struct data
        unit caster
        real casterX
        real casterY
        real targetX
        real targetY
        real facing
        effect sfx
        group hit = CreateGroup()
        timer t
        
        method destroy takes nothing returns nothing
            call ReleaseTimer(.t)
            call GroupClear(.hit)
            call .deallocate()
        endmethod
    endstruct
    
    globals
        private constant integer HIT_RANGE = 50
        private constant real PERIOD = 0.03125
        private constant real DSPEED = SPEED * PERIOD
        private group GROUP = CreateGroup()
        private location loc
        private real x
        private real y
        private integer CurrentInstance
    endglobals
    
    private function Conditions takes nothing returns boolean
        local unit u = GetFilterUnit()
        local unit dummy
        local data d = CurrentInstance
        
        if IsUnitType(u, UNIT_TYPE_STRUCTURE) == false and GetWidgetLife(u) >= 0.405 and IsUnitEnemy(u, GetOwningPlayer(d.caster)) and IsUnitInGroup(u, d.hit) == false then
            call UnitDamageTarget(d.caster, u, Damage(GetUnitAbilityLevel(d.caster, ABILITY_ID)), true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
            set dummy = CreateUnit(GetOwningPlayer(d.caster), DUMMY_ID, GetUnitX(u), GetUnitY(u), bj_UNIT_FACING)
            call UnitAddAbility(dummy, ILLUSION_ID)
            call SetUnitAbilityLevel(dummy, ILLUSION_ID, GetUnitAbilityLevel(d.caster, ABILITY_ID))
            call IssueTargetOrderById(dummy, ORDER_ID, d.caster)
            call UnitApplyTimedLife(dummy, 'BTLF', 1.)
            call GroupAddUnit(d.hit, u)
        endif
        set dummy = null
        set u = null
        return false
    endfunction
    
    private function Callback takes nothing returns nothing
        local data d = GetTimerData(GetExpiredTimer())
        
        set d.casterX = GetUnitX(d.caster)
        set d.casterY = GetUnitY(d.caster)
        set x = d.targetX - d.casterX
        set y = d.targetY - d.casterY
        if SquareRoot(x * x + y * y) >= HIT_RANGE then
            set d.facing = Atan2(d.targetY - d.casterY, d.targetX - d.casterX)
            set d.casterX = d.casterX + DSPEED * Cos(d.facing)
            set d.casterY = d.casterY + DSPEED * Sin(d.facing)
            call SetUnitX(d.caster, d.casterX)
            call SetUnitY(d.caster, d.casterY)
            set CurrentInstance = d
            call GroupEnumUnitsInRange(GROUP, GetUnitX(d.caster), GetUnitY(d.caster), RADIUS, Condition(function Conditions))
        else
            call SetUnitAnimation(d.caster, "stand")
            call PauseUnit(d.caster, false)
            call DestroyEffect(d.sfx)
            call d.destroy()
        endif
    endfunction
    
    private function Actions takes nothing returns boolean
        local data d
        
        if GetSpellAbilityId() == ABILITY_ID then
            set d = data.create()
            set d.caster = GetTriggerUnit()
            set loc = GetSpellTargetLoc()
            set d.casterX = GetUnitX(d.caster)
            set d.casterY = GetUnitY(d.caster)
            set d.targetX = GetLocationX(loc)
            set d.targetY = GetLocationY(loc)
            set d.sfx = AddSpecialEffectTarget(EFFECT, d.caster, ATTACH)
            set d.t = NewTimer()
            call SetTimerData(d.t, d)
            call SetUnitAnimation(d.caster, ANIMATION)
            call PauseUnit(d.caster, true)
            call TimerStart(d.t, PERIOD, true, function Callback)
            set loc = null
        endif
        return false
    endfunction
        
    private function Init takes nothing returns nothing
        local trigger trig = CreateTrigger()
        
        static if(LIBRARY_GTrigger) then
            call TriggerAddCondition(GT_RegisterStartsEffectEvent(trig, ABILITY_ID), Condition(function Actions))
        else
            call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(trig, Condition(function Actions))
        endif
        set trig = null
    endfunction
    
endlibrary

Test map included.
Constructive criticism is welcomed, flaming is not.
This spell will only be submitted to TH.net.
Do not redistribute or modify without permission.
Credits are not needed but are appreciated.
Just don't claim the spell as yours.

Hope you enjoy it!​

Changelog
v1.0
- Initial release
 

Attachments

  • opt-Phantom_Dash_v10.w3x
    49.6 KB · Views: 464

BlackRose

Forum User
Reaction score
239
[LJASS]private constant real RADIUS = 125. //Radius the unit hits units in - Don't go below 100[/LJASS]

Some explanation would be nice. I've seen your other thread, but a simple statement explaining why users shouldn't go below the 100 radius value is nice.

• You don't comment the EFFECT and ATTACH globals? I have no idea what effect it is for.
• Function Damage should be a constant function so it is inlineable.
• You are leaking groups. You are creating them on every spell cast, but you aren't destroying them - only clearing them. May I suggest you use GroupUtils or recycle them properly?
• You should make use of the create method that structs provide. That way, you can avoid the use of having to a local integer. Also, it's better encapsulation practice.
• In some patch, I can't remember, natives [LJASS]GetSpellTargetX[/LJASS] and [LJASS]GetSpellTargetY[/LJASS] were added. You should make use of them instead of using locations.
• Why are variables x and y not local to the Callback function? Any speed difference should be neglible. In addition, you're only using them within that function.
• In function Callback, that distance comparison line should square both sides of the equation, so you don't have to call a [LJASS]SquareRoot[/LJASS]. That is, it should be [LJASS](x * x + y * y) >= HIT_RANGE * HIT_RANGE[/LJASS] - However, [LJASS]HIT_RANGE * HIT_RANGE[/LJASS] should be a new constant variable.
• You should perform the GroupEnum call before you offset d.casterX. That way, you won't have to call GetUnitX and GetUnitY twice in that function. Alternatively, if you want the GroupEnum to be centered directly on the caster, store the caster's x into a variable.
• Pausing units for spells isn't recommended. What if another script unpauses it during operation?
• You have x and y as globals, but you don't have u or dummy in Conditions as a global? What is this. Pick one.
• You don't need to declare data d, just use CurrentInstance directly, saves a local integer declaration.
• The spell will adjust to the casting unit's ability level for stats (like damage). It should take the level at the time of casting as it's argument. Store level as a struct member, or store damage as a struct member.
• Speed and radius should also be configurable functions. Users may want leveling Phantom Dash to increase the movement speed.
• HIT_RANGE should be configurable. Users may make the Phantom Dash have a ranged effect once the dash is nearby. Consider all scenarios.
• Consider using Jesus4Lyf's DummyCaster. Don't create a dummy caster each time.

[DEL]I haven't actually tested your spell out, so I can't say anything on that.[/DEL]

EDIT: I have now tested the spell out. My comments are as follows:
• As a suggestion, you should trigger automatic selection of the hero on map initialization. This is for convenience
• The visual effects are not very well executed. I have considered that things are configurable - but I still wish to comment on what you have presented. The hero doesn't dash towards the targeted point as the spell tooltip says. What happens instead is the hero is simply being moved across in a straight line. It doesn't look like a dash at all. I mean, the hero even plays it's random stand animations during it's so called "dash". In addition, the attached effect does not last on the caster; the visual effect ends before the caster is done moving for slower speeds. Also, the "spell" animation the hero throws doesn't particularly look good too. If it's a Phantom Dash, why does the hero lift his arms up?
• Going back on a previous point, the pause mechanic of the spell is horrible! What if I wanted to cast something like Thunder Clap during that dash? I wouldn't be able to because of the pause from the code and the pause from Channel. In your ability in the Object Editor, "Data - Disable Other Abilities" is true, how come?
• Why are the illusions being summoned really far with lots of variation from the targets hit? I'm getting illusions spawned from all angles on the targets when the hero dashes through them. This is due to the "Stats - Area of Effect" field in the Wand of Illusion ability in the Object Editor. It's offsetting the illusions by that amount.
• The "Stats - Target Allowed" field in the Phantom Dash (Dummy) ability should be changed so that it can target all types of units. I broke Phantom Dash by making the caster invulnerable; no illusions were being made when I used Phantom Dash.
• My hero died when I cast Phantom Dash, but it kept moving. This looks weird, I suggest you stop the dash if the caster dies/is removed by other code. I don't know about if the hero is to be hidden with [LJASS]ShowUnit[/LJASS], it would look weird if upon showing it again, it was suddenly displaced from the point it was hidden.
• The ability tooltip could do with a bit more - the numerical stat values could be added, but I don't think it's too important.
 

NoobImbaPro

You can change this now in User CP.
Reaction score
60
Where did you get this "•" ?? It's very nice for pointing things.

• Spell Effect is lost during movement, I meant you should kill it when your target reaches final point, the trails left from effect will only cover 500 wc3 meter units and not the whole distance. Find another more eye candy effect.
• Give more time to illusions as you can predict easily who is the real deal.
• On demo map give less hp to units, users like to kill creeps :D
JASS:

• Don't initialize the kind of non-static struct members, you create about 8000 empty and useless and leakful groups now on map initialization.
Use "set local.hit = CreateGroup" on struct create method and use "call DestroyGroup(this.hit)" on destroy method
• Use onDestroy method omg to avoid any struct instance problem.
 

inevit4ble

Well-Known Member
Reaction score
38
I like it! :D
I think its quite cool :thup:

But I did notice that it doesn't cast if you try to "shift+cast" (action queue).

But nice gj :)
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • 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

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top