Spell Aerial Strike

Joker(Div)

Always Here..
Reaction score
86
Aerial Strike:
Summons a great bird that fuses with your current energy (mana), which smashes itself into the target location.

The spell is very flexible, MUI, leakless (better be), and easily implemented. It is also pretty close to a JESP.

Credit to Tinki3 for the map. :)

How To Implement:
  • Create a Trigger
  • Name it: AerialStrike
  • Copy the script (Or just copy the entire trigger)
  • Make/Copy a spell based on channel
  • Make/Copy a flying dummy unit

Requires:
  • TT
  • Recycler
  • NewGen
  • Vex's Dummy Model

Screenies:
sc1uk9.jpg

sc2he5.jpg

sc3oj7.jpg

Code:
JASS:
//===========================================================================
//  Aerial Strike:  By Joker(Div)                     Credits: NewGen - PitzerMike/Vexorian
//                                                             TT - Cohadar
//                                                             Recycler - Cohadar, Vexorian (CSSafety)?     
// Requires: TT, Recycler, NewGen                              Dummy Model - Vexorian                                  
//===========================================================================
scope AerialStrike initializer Init

globals
    private constant integer ABILITY_ID = 'A000'        //ID of spell
    private constant integer BIRD_ID    = 'n000'        //ID of bird
    private constant integer DUMMY_ID   = 'n001'        //ID of Dummy (used to attach sfx)
    private constant real    DUMMY_SIZE = 1.0           //Size of sfx in case you increase base AoE
    
    private constant real BIRD_START     = 500.         //# of units behind the caster, where the bird spawns
    private constant real BIRD_SPEED     = 750.         //The bird's Units/Sec
    private constant real BIRD_HEIGHT    = 1500.         //The bird's starting height
    private constant real BIRD_DESCEND   = 1500.         //The bird's Units/Sec for descending
    private constant real BIRD_MINHEIGHT = 5.           //Minimun height of bird
    private constant real BIRD_SIZE_BASE = 3.5          //Make this w/e you have it as in Object Editor 
    private constant real BIRD_MANA2SIZE = 0.003        //The size increment per mana
    private constant real BIRD_ANIMATION = 4.0          //The rate the bird animates
    
    
    private constant real EXPLOSION_RADIUS  = 250.      //Self-Explanatory
    private constant real BASE_DAMAGE       = 50.       //This will be muliplied by the lvl (set it to 0 if you dont want any)
    private constant real MANA_IMPUT        = 0.5       //The % of mana put into dmg (in this case, 50%)
    
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS  //Self-Explanatory
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL //Self-Explanatory
    
    
    //The Explosion models (You can remove and add as you wish)
    private constant string IMPACT_EXPLOSION    = "Objects\\Spawnmodels\\NightElf\\NECancelDeath\\NECancelDeath.mdl"
    private constant string IMPACT_EXPLOSION2    = "Abilities\\Spells\\NightElf\\Taunt\\TauntCaster.mdl"
    private constant string IMPACT_EXPLOSION3   = "Units\\NightElf\\Wisp\\WispExplode.mdl"
   
    //The Teleport model
    private constant string TELEPORT = "Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl"
    
    //The crow ability, incase you have edited yours.
    private constant integer CROW_ABILITY = 'Arav'
    
    //This is here if you want the bird to explode once it collides with any unit.
    //Just set it you true if you want it.
    private boolean CONTACT_EXPLODE = true
endglobals


//========================================================================================
//                  You should not have to touch anything under here. 
//                                (Unless specified)
//========================================================================================
    private struct Aerial
        unit caster
        unit bird
        real dspeed
        real ddescend
        real dx
        real dy
        real x
        real y
        real angle
        real mana
        real endx
        real endy
        integer ticks
        integer lvl 
        boolean have
        group g
        
        static method start takes unit caster, unit bird, real x, real y, real angle, real range returns nothing
            local Aerial dat = Aerial.create()
            
              set dat.caster    = caster
              set dat.bird      = bird
              set dat.x         = x
              set dat.y         = y
              set dat.have      = false
              set dat.mana      = GetUnitState(caster, UNIT_STATE_MANA)
              set dat.lvl       = GetUnitAbilityLevel(caster, ABILITY_ID)
              set dat.g         = NewGroup() //Recycler
    
              set dat.dspeed      = BIRD_SPEED      * TT_PERIOD
              set dat.ddescend    = BIRD_DESCEND    * TT_PERIOD
              set dat.dx          = dat.dspeed      * Cos(angle) 
              set dat.dy          = dat.dspeed      * Sin(angle) 
              set dat.ticks       = R2I( ( (range+BIRD_START) / dat.dspeed ) + 0.5 )
            
            call SetUnitTimeScale( bird, BIRD_ANIMATION)
            call TT_Start(function Aerial.callback, dat ) //TT
        endmethod
        
        static method callback takes nothing returns boolean
            local Aerial dat    = TT_GetData() //TT
            local real birdX    = GetUnitX(dat.bird)
            local real birdY    = GetUnitY(dat.bird)
            local real f        = GetUnitFacing(dat.caster)
            local real x        = dat.x
            local real y        = dat.y
            local real fly      = GetUnitFlyHeight(dat.bird)
            local real scale    = BIRD_SIZE_BASE + dat.mana * BIRD_MANA2SIZE

            if dat.ticks > 0 then 
                call SetUnitPosition(dat.bird, birdX + dat.dx, birdY + dat.dy )
                
                set dat.ticks   = dat.ticks - 1
                
                if fly > BIRD_MINHEIGHT then
                    call SetUnitFlyHeight(dat.bird, fly - dat.ddescend, 0)
                endif
                
                if IsUnitInRange(dat.bird, dat.caster, 100.) and not dat.have then
                    call SetUnitScale( dat.bird, scale, scale, scale )
                    call DestroyEffect( AddSpecialEffect(TELEPORT, GetUnitX(dat.caster), GetUnitY(dat.caster)) )
                    call SetUnitState( dat.caster, UNIT_STATE_MANA, dat.mana * MANA_IMPUT )
                    call ShowUnit(dat.caster, false)
                    call PauseUnit(dat.caster, true)
                    set dat.have = true
                endif
                
                if CONTACT_EXPLODE then
                    if dat.have then
                        call GroupEnumUnitsInRange(dat.g, birdX, birdY, EXPLOSION_RADIUS, Condition(function Aerial.filter) )
                    
                        if FirstOfGroup(dat.g) != null and IsUnitInRange(FirstOfGroup(dat.g), dat.bird, 50.) then
                            set dat.endx    = birdX
                            set dat.endy    = birdY
                            call dat.destroy()
                            return true
                        endif
                    endif
                endif
            else
                set dat.endx    = birdX
                set dat.endy    = birdY
                call dat.destroy()
                return true
            endif
                
            return false
        endmethod
        
        //===============================================================================================================================
        //===============================================================================================================================
        private static method filter takes nothing returns boolean
            //Change the conditions to w/e you want here.
            return IsUnitEnemy( GetTriggerUnit(), GetOwningPlayer(GetFilterUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405
        endmethod
        //===============================================================================================================================
        //===============================================================================================================================
        
        method onDestroy takes nothing returns nothing
            local unit first
            local unit dummy
            
            if .have then
                call GroupEnumUnitsInRange(.g, .endx, .endy, EXPLOSION_RADIUS, Condition(function Aerial.filter) )
                    
                call PauseUnit(.caster, false)
                call SetUnitPosition(.caster, .endx, .endy)
                    
                debug call ClearTextMessages()
                debug call BJDebugMsg("Aerial Strike: max damage: |cFFC0C000" + R2S(.lvl*BASE_DAMAGE + .mana*MANA_IMPUT) )
                    
                loop
                    set first = FirstOfGroup(.g)
                    exitwhen first == null
                    call UnitDamageTarget(.caster, first, .lvl*BASE_DAMAGE + .mana*MANA_IMPUT, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE_WHOKNOWS )
                    call GroupRemoveUnit(.g, first)
                endloop
                            
                if GetLocalPlayer() == GetOwningPlayer(.caster) then
                    call SelectUnit(.caster, true)
                endif
                
                set dummy = CreateUnit(GetOwningPlayer(.caster), DUMMY_ID, .endx, .endy, bj_UNIT_FACING)
                call SetUnitScale(dummy, DUMMY_SIZE, DUMMY_SIZE, DUMMY_SIZE)
                
                //=================================================================================
                //============= Incase you added or removed an effect(s) ==========================
                call DestroyEffect( AddSpecialEffectTarget(IMPACT_EXPLOSION, dummy, "origin" ) )
                call DestroyEffect( AddSpecialEffectTarget(IMPACT_EXPLOSION2, dummy, "origin" ) )
                call DestroyEffect( AddSpecialEffectTarget(IMPACT_EXPLOSION3, dummy, "origin" ) )
                //==================================================================================
                //==================================================================================
                
                call ShowUnit(.caster, true)
            endif
            
            call RemoveUnit(.bird)
            call KillUnit(dummy)
            call ReleaseGroup(.g)
            set first = null
            set dummy = null
        endmethod
    endstruct
  
//===========================================================================
private function Actions takes nothing returns nothing
    local unit caster   = GetTriggerUnit()
    local location loc  = GetSpellTargetLoc()
    local real casterX  = GetUnitX(caster)
    local real casterY  = GetUnitY(caster)
    local real x        = GetLocationX(loc)
    local real y        = GetLocationY(loc)
    local real rangex   = x - casterX
    local real rangey   = y - casterY
    local real range    = SquareRoot(rangex*rangex + rangey*rangey)
    local real angle    = Atan2(rangey, rangex)
    local real X        = casterX- BIRD_START * Cos(angle)
    local real Y        = casterY- BIRD_START * Sin(angle)
    local unit bird
    
    if not RectContainsCoords(bj_mapInitialPlayableArea, X, Y) then
        set bird = CreateUnit( GetOwningPlayer(caster), BIRD_ID, casterX, casterY, bj_RADTODEG*angle )
    else
        set bird = CreateUnit( GetOwningPlayer(caster), BIRD_ID, X, Y, bj_RADTODEG*angle )
    endif
    
    call UnitAddAbility(bird, CROW_ABILITY)
    call UnitRemoveAbility(bird, CROW_ABILITY)
    call SetUnitFlyHeight(bird, BIRD_HEIGHT, 0)
    call Aerial.start(caster, bird, x, y, angle, range)
    call RemoveLocation(loc)
    
    set loc     = null
    set caster  = null
    set bird    = null
endfunction

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == ABILITY_ID
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer i = 0
    loop
        call TriggerRegisterPlayerUnitEvent( t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, BOOLEXPR_TRUE ) //Recycler
        exitwhen i >= 15
        set i = i+1
    endloop
    call TriggerAddCondition( t, Condition( function Conditions ) )
    call TriggerAddAction( t, function Actions )
endfunction

endscope

Code:
//---11/8/08---\\
*Uses Vex's model so that the sfx size is alterable
*Small Clean up

//---10/26/08---\\
*Clean up coding (much easier to follow)

//---3/5/08----\\
* Fixed struct leak
* Fixed a little config error
* Made the "How to Implement" a bit more detailed.
** Got rid of Camera pan
** Added a error msg (Now needs SimError also)
** Does not use GetUnitFacing anymore

//---3/4/08----\\
* Took more of Cohadar's advise and changed more some coding
* Fix the target location, thx to Cohadar
* Little more optimization here and there
* Forgot that the bird takes half your mana, fixed that
* Added a "how to implement" in the map. (dunno how to get more detailed than that...)

//---3/3/08----\\
* Took some of Cohadar's advise and changed some coding
* Changed some other coding myself
* Made it easier for you to turn on Collision explosion
* Made nothing happen if bird does not pick you up.
* Made you level 1 on test map to show better the bird size.
* Test it in debug mode to see the damage dealt.
 

Attachments

  • Joker(Div) - Aerial Strike.w3x
    61.7 KB · Views: 472

Sim

Forum Administrator
Staff member
Reaction score
534
How To Implement:
  • Create a Trigger
  • Name it: AerialStrike
  • Make a spell based on channel
  • Make a flying dummy unit

You mean "Copy the trigger AerialStrike" I hope. :)
Actually, I hope you meant "Copy" on each line too.
 

Arcane

You can change this now in User CP.
Reaction score
87
So THAT'S what the KotG projectile looks like when it's enlarged. Interesting...
 

GoGo-Boy

You can change this now in User CP
Reaction score
40
Ohh my god!! So awesome innovative! ;D
I really love it. You definitely deserve a +Rep for this. It simply looks wonderful. Though one thing makes it look too hasty on the other side, the animation of the unit/projectile is too fast, which probably is a consequence of the fact that this model is used to be a projectile and no bigger model.
But else the model is perfect :) and it's no problem for people, since the model can be changed anyway hehe
 

darkRae

Ueki Fan (Ueki is watching you)
Reaction score
173
> So THAT'S what the KotG projectile looks like when it's enlarged. Interesting...

Actually, it's Human Priest's.
 

GoGo-Boy

You can change this now in User CP
Reaction score
40
As far as I know they are simply differently colored...

However to the spell, though awesome, I found something that has to be fixed. It's a channel ability and therefore should NOT mess up when canceled, but it does. The bird flies weird curves and you still get blinked and even (only when canceled) damaged.
And I'm not sure whether this problem rarely occurred as well when I didn't cancel the spell.

However: You should post this spell on the dota allstars forum, because spells that transport you to an other position in a unique way are beloved, and this one is so f##king nice :)
 

Cohadar

master of fugue
Reaction score
209
This is bad.
JASS:

    private constant real BIRD_SPEED = 25.       //X this by 32, or however fast you made TT to get Units/Sec
    private constant real BIRD_DESCEND = 15.     //X this by 32, or however fast you made TT to get Units/Sec


First of all those are not speeds but delta speeds.
Delta speed is a distance an object will move during one quantum of time (during one tick)

The proper way to do this is to use real speeds and TT_PERIOD constant.
JASS:

    // Those are the real game speeds, like the move speed of units
    // heroes usually move in range 300 - 350  << Movement - Speed Base >>
    // 522 is the maximum speed units can move (without triggers)
    // projectiles usually move in range 900 - 1600 << Combat - Attack 1 - Proojectile Speed >>
    // 
    private constant real BIRD_SPEED = 800.
    private constant real BIRD_DESCEND = 480.


Then you calculate the delta speeds in the trigger actions
JASS:

    set data.dSpeed = BIRD_SPEED * TT_PERIOD
    set data.dDescend = BIRD_DESCEND * TT_PERIOD


This way it is much easier to configure the speeds because you can directly compare then to standard game speeds.
It is also independent of what TT period you use.
Spell will behave the same for two people using same spell with differently set TT periods. (witch was the whole idea of making TT_PERIOD public)

I believe this was well demonstrated in TT demo map.

Also when you move unit in one direction it is pointless to calculate Cos and Sin every time. You can use deltaX and deltaY directly.
(again demonstrated in demo map)

I know it is easier to simply write 25. and 15. than to do extra brainwork but trust me - it is worth it.

For the end a couple of useful formulas:
JASS:

    ds == speed*TT_PERIOD
    dx == ds*Cos(angle)
    dy == ds*Sin(angle)
    ticks == R2I(distance/ds)
 
T

Tubba

Guest
It seemed to bug when close to boundaries, too. Otherwise, pretty nice.
 

Joker(Div)

Always Here..
Reaction score
86
I love you Cohadar <3

@Flare, That's why I got the 3rd screenshot, the effects are quick so its hard to capture it. You can change it anyway. :p

@Tubba, I can't control that, map boundaries are evil.

@GoGo-Boy, Sorry I missed your posts. Thx for the nice comments. :) Anyway, the spell is not really a channeling spell. I just put a casting time so you won't move from the point you casted (The bird would miss you). The wierd curves are proly caused by the map boundaries. I can't control that. Also, I am not gonna go bother the dota community. 80% of the people there have an IQ of 50. You are welcome to suggest the spell yourself, post the test map if you want to also.

NEW UPDATE 3/3/08
 

GoGo-Boy

You can change this now in User CP
Reaction score
40
Edit: Ohh... I see that you updated and removed that? I'll test it right now^^
Edit2: Now this way it's really nice, that the bird flies even when channeling is canceled and IF he comes close to you still moves you but else simple disappear... really, really nice way you erased the problems ;)
To the dota community, well I dunno, they always seemed fine to me (not the gamer for sure lol >_<) and the map is well made with nice coding, therefore I though it's nice to know a spell of oneself in such a nice map (please, nobody should start to talk about dota, I know there are different opinions about it :D)

PS: Can someone tell me how I can cross out text? So that I don't have to remove text I wrote because I noticed it's useless anymore but people might wonder if I simply remove it :d
 

Sim

Forum Administrator
Staff member
Reaction score
534
Implementation Instructions need to be inside the map.

Please make them more detailed too...

As for the spell: Great idea!

Love how it works out!

You got a few problems though...

  • I actually died at some point when casting this. Without any reason. If it can help you, I died when reaching the destination point.
  • Reselect the hero once the spell is over. It is annoying to select it again.
  • Your spell is acting weird, often. First of all, the blink should transport you to the destination point, not a set distance away, as blink does. Second, sometimes my hero wouldn't disappear "inside" the bird. I just got teleported a couple seconds later. Third, It doesn't always act the same way. Sometimes it teleports you to the target point, sometimes not, sometimes it lags a bit...

Nice spell overall, but it needs a bit of fixing!
 

Joker(Div)

Always Here..
Reaction score
86
@Daxtreme

"I actually died at some point when casting this. Without any reason. If it can help you, I died when reaching the destination point."
- Uh...your hero died while he was hidden?

"Reselect the hero once the spell is over. It is annoying to select it again."
- I'm pretty sure it does, unless you tested the older version.

"Your spell is acting weird, often. First of all, the blink should transport you to the destination point, not a set distance away, as blink does. Second, sometimes my hero wouldn't disappear "inside" the bird. I just got teleported a couple seconds later..."
- You must be testing the older version. I'll still try optimize this those, just to make sure.

"...Third, It doesn't always act the same way. Sometimes it teleports you to the target point, sometimes not, sometimes it lags a bit..."
- It lags?! :( Can you find any leaks?

Thx for those comments :D
 

Sim

Forum Administrator
Staff member
Reaction score
534
> - You must be testing the older version. I'll still try optimize this those, just to make sure.

Indeed.

Alright, new report. :D

It seems the bird is bugging hardcore when either his spawn point or his (not yours) destination point is outside the map's bounds. Simply check if the point's location is pathable, and if it isn't, find a pathable point nearby.

Because right now, if the bird's spawning and landing positions are unpathable, it just doesn't work at all.:rolleyes:
 

Joker(Div)

Always Here..
Reaction score
86
aw, you posted too early...Just got a new update. :(

Do I have to make a pathing checker? :( You can get rid of/lower the bird starting position to help with that. I just added it for the eyecandy. Most maps should be big enough, boundaries shouldn't be a prob.

NEW UPDATE 3/4/08
 

Sim

Forum Administrator
Staff member
Reaction score
534
Well, either cancel the spell or something. Warning message?

Oh and, please get rid of that camera lock. It's a spell, not a cinematic! :)
 

Joker(Div)

Always Here..
Reaction score
86
You could easily delete the camera lock yourself, had a text in there saying so, but w/e. I got rid of it anyway.

Added a error msg using SimError.

I sense an approval. :D
 

Sim

Forum Administrator
Staff member
Reaction score
534
> I sense an approval.
> I don't sense an approval?...

Ya gotta let me some time. :D

Approved. Great job!
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    I ordered like five blocks for 15 dollars. They're just little aluminum blocks with holes drilled into them
  • Varine Varine:
    They are pretty much disposable. I have shitty nozzles though, and I don't think these were designed for how hot I've run them
  • Varine Varine:
    I tried to extract it but the thing is pretty stuck. Idk what else I can use this for
  • Varine Varine:
    I'll throw it into my scrap stuff box, I'm sure can be used for something
  • Varine Varine:
    I have spare parts for like, everything BUT that block lol. Oh well, I'll print this shit next week I guess. Hopefully it fits
  • Varine Varine:
    I see that, despite your insistence to the contrary, we are becoming a recipe website
  • Varine Varine:
    Which is unique I guess.
  • The Helper The Helper:
    Actually I was just playing with having some kind of mention of the food forum and recipes on the main page to test and see if it would engage some of those people to post something. It is just weird to get so much traffic and no engagement
  • The Helper The Helper:
    So what it really is me trying to implement some kind of better site navigation not change the whole theme of the site
  • 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 Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top