1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The 18th Icon Contest is ON! Choose any ingame unit and give him/her Hero abilities. Good luck to all.
    Dismiss Notice
  4. The Secrets of Warcraft 3 have revealed interesting works. The RESULTS for Abelhawk's Mini-Mapping Contest #15 have come out!
    Dismiss Notice
  5. Contestants are to create a scene set in the Stone Age. Come and see what you can come up with. We wish you the best of luck!
    Dismiss Notice
  6. Colour outside the lines! Techtree Contest #13 is a go. The contest is optionally paired.
    Dismiss Notice
  7. Night Rider gained several songs for his journey. The poll for the 12th Music Contest has started. Check it out!
    Dismiss Notice
  8. Greetings cerebrates, our Swarm needs new spawners that will have numerous children. Join the HIVE's 31st Modeling Contest - Spawners and Spawned! The contest is optionally paired.
    Dismiss Notice
  9. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  10. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Tornado spell request!

Discussion in 'Requests' started by Dinodin, Jul 21, 2019.

  1. Dinodin

    Dinodin

    Joined:
    Jul 19, 2007
    Messages:
    492
    Resources:
    2
    Maps:
    2
    Resources:
    2
    I need some help to make a tornado spell for my map that works abit like shockwave but also knocks back all damaged enemies and slowing their movement speed. I'm very lost at making triggering spells so I can't do this spell, please is someone up for helping me?

    Better describiton of the tornado-spell:
    The caster summons a powerful wind to attack his enemy targets in the target location. Enemies who get to close to the wind will take damage based on the caster's intelligence and will be knocked back and gets it movement speed slowed for 5 seconds. |n|n|cffffcc00Level 1|r - 1x intelligence damage, slows movement speed by 15%. |n|n|cffffcc00Level 2|r - 2x intelligence damage, slows movement speed by 25%. |n|n|cffffcc00Level 3|r - 3x intelligence damage, slows movement speed by 35%. |n|n|cffffcc00Level 4|r - 4x intelligence damage, slows movement speed by 45%.
     
  2. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,663
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Map is attached. I used Knockback3D, which has a weird bug with the dust fx model I just posted about here, so if you don't like how that looks you can just change the model. The caster units in this map don't have any intelligence and aren't heroes so the spell can't be leveled up but it will do the requested int scaling damage and scale as requested. I didn't write a tooltip, you can do that yourself. If you want sounds you'll have to do that yourself too or give me something to go on and I'll put some sound in. Choose fx as you so desire.

    There is 1 trigger, 3 dependency libraries, 2 abilities, 1 buff, and 1 dummy caster. Copy them all. The map also has a couple other spells I was working on in it, just ignore them unless you like them.
    Code (vJASS):
    library Windblast requires Knockback3D
      globals
        public  integer SPELL_ID   = 'Wbst'
        public  integer DUMMY_ID   = 'dmmy'
        private real    TIMER_TICK = 0.03
        private real MAX_COLLISION = 128.
       
        private real VELOCITY = 900.
        private real DISTANCE = 600.
       
        private real KB_VEL = 500.
        private real KB_ALPHA = 0. //elevation angle to knock back at

        private real    EFFECT_H   = 10.
        private string  EFFECT_1A  = "Doodads\\Cinematic\\GlowingRunes\\GlowingRunes3.mdl"

        private real    E1A_SCALE  = 2.2
        private real    E1A_TSCALE = .1
        private real    E1A_TSKIP  = 1.0 //skip this far ahead in the stand animation

        private integer E1A_COL_R  = 255
        private integer E1A_COL_G  = 255
        private integer E1A_COL_B  = 255

      endglobals
     
      native UnitAlive takes unit u returns boolean
     
      globals
        public  timer   SpellTimer  = CreateTimer()
        public  trigger CastTrigger = CreateTrigger()

        public  keyword SpellData
        private SpellData array GSD
        private integer COUNT = 0
        private integer CURR = 0
      endglobals



      private module spellBehavior
        // these are defined in the struct below for you to use:
        // they do not need to be destroyed or removed, that is handled already
        //
        // static group SEARCH_GROUP  <-- a group you can use to search for targets via GroupEnumUnitsIn...
        // unit DUMMY                 <-- can cast instantly and is moved to (.cx, .cy) every timer step
        //                                add abilities to it with .prepDummy(abilCode) (level is set automatically)
        // group HIT_GROUP            <-- units already hit by this instance of the spell, you are free to clear this as necessary, but you must add units manually
        // integer l                  <-- level of the spell when it was cast
        // unit c                     <-- caster
        // player p                   <-- owner of caster
        // real cx, cy, cz            <-- coordinates of the center of the spell
        // you may end an instance early by calling .endInstance()
        // you may add your own instance or static members here:

        static integer SECONDARY_ID   = 'Wbls'
        static string SECONDARY_ORDER = "slow"
        static real RADIUS = 100.
       
        method dmgIntFactor takes nothing returns real
          return this.l * 1.0
        endmethod

        static method onInitEx takes nothing returns nothing
          //called on map init, so you can set up anything you need to here (most likely arrays or some global dummies)
        endmethod

        method onStart takes nothing returns nothing
          //called when the spell is cast
          call this.prepDummy(SECONDARY_ID)
        endmethod

        method onPeriodic takes nothing returns nothing
          local unit u
          local real x
          local real y
          local real a
          local real dmg = this.dmgIntFactor()*GetHeroInt(this.c, true)

          call GroupEnumUnitsInRange(SEARCH_GROUP, this.cx, this.cy, RADIUS+MAX_COLLISION, null)
          loop
            set u = FirstOfGroup(SEARCH_GROUP)
            exitwhen u == null
            call GroupRemoveUnit(SEARCH_GROUP, u)

            if IsUnitInRangeXY(u, this.cx, this.cy, RADIUS) and /*
            */
    IsUnitEnemy(u, this.p) and UnitAlive(u)      and /*
            */
    not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)    and /*
            */
    not BlzIsUnitInvulnerable(u)                 and /*
            */
    not IsUnitInGroup(u, this.HIT_GROUP)         then
           
              set x = GetUnitX(u)
              set y = GetUnitY(u)
              set a = Atan2(y-this.cx, x-this.cx)
              if a > 0. then
                set a = this.kbaL
              else
                set a = this.kbaR
              endif

              call UnitDamageTarget(this.c, u, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FORCE, WEAPON_TYPE_WHOKNOWS)
              call Knockback3D.add(u, KB_VEL, a, KB_ALPHA)
              call IssueTargetOrder(this.DUMMY, SECONDARY_ORDER, u)
              call GroupAddUnit(this.HIT_GROUP, u)
            endif
          endloop

          set u = null
        endmethod

        method onEnd takes nothing returns nothing
          //called when the spell instance ends
        endmethod
      endmodule



      public struct SpellData
        static group SEARCH_GROUP = CreateGroup()
        group HIT_GROUP = null
        unit DUMMY = null

        unit c    = null
        player p  = null
        integer l = 0
        integer d = 0
        effect e = null
        real cx = 0.
        real cy = 0.
        real dx = 0.
        real dy = 0.
        real kbaL = 0.
        real kbaR = 0.


        method prepDummy takes integer abil returns nothing
          call UnitAddAbility(this.DUMMY, abil)
          call SetUnitAbilityLevel(this.DUMMY, abil, this.l)
        endmethod

        implement spellBehavior

        method endInstance takes nothing returns nothing
          call this.onEnd()

          call RemoveUnit(this.DUMMY)
          call DestroyGroup(this.HIT_GROUP)
          //call DestroyEffect(this.e)
          call RemoveEffect(this.e)

          set GSD[CURR] = GSD[COUNT-1]
          set COUNT = COUNT-1
          set CURR = CURR-1

          if COUNT == 0 then
            call PauseTimer(SpellTimer)
          endif

          call this.destroy()
        endmethod

        static method periodic takes nothing returns nothing
          local SpellData sd

          set CURR = 0
          loop
            exitwhen CURR >= COUNT
            set sd = GSD[CURR]

            set sd.cx = sd.cx + sd.dx
            set sd.cy = sd.cy + sd.dy
            call BlzSetSpecialEffectX(sd.e, sd.cx)
            call BlzSetSpecialEffectY(sd.e, sd.cy)
            call BlzSetSpecialEffectHeight(sd.e, EFFECT_H)
            call SetUnitX(sd.DUMMY, sd.cx)
            call SetUnitY(sd.DUMMY, sd.cy)
            call sd.onPeriodic()

            set sd.d = sd.d-1
            if sd.d <= 0 then
              call sd.endInstance()
            endif
           
            set CURR = CURR+1
          endloop
        endmethod

        static method create takes unit c, real x, real y returns thistype
          local thistype sd = thistype.allocate()
          local real a
         
          set sd.c = c
          set sd.p = GetOwningPlayer(sd.c)
          set sd.l = GetUnitAbilityLevel(sd.c, SPELL_ID)
          set sd.cx = GetUnitX(sd.c)
          set sd.cy = GetUnitY(sd.c)
          set a = Atan2(y-sd.cy, x-sd.cx)
          set sd.kbaL = a + bj_PI/2.
          set sd.kbaR = a - bj_PI/2.
          set sd.dx = VELOCITY*Cos(a)*TIMER_TICK
          set sd.dy = VELOCITY*Sin(a)*TIMER_TICK
          set sd.d = R2I(DISTANCE/VELOCITY/TIMER_TICK)

          set sd.e = AddSpecialEffect(EFFECT_1A, sd.cx, sd.cy)
          call BlzSetSpecialEffectColor(sd.e, E1A_COL_R, E1A_COL_G, E1A_COL_B)
          call BlzSetSpecialEffectHeight(sd.e, EFFECT_H)
          call BlzSetSpecialEffectScale(sd.e, E1A_SCALE)
          call BlzSetSpecialEffectTimeScale(sd.e, E1A_TSCALE)
          call BlzSetSpecialEffectTime(sd.e, E1A_TSKIP)
         
          set sd.DUMMY = CreateUnit(sd.p, DUMMY_ID, sd.cx, sd.cy, 0.)
          set sd.HIT_GROUP = CreateGroup()

          if COUNT == 0 then
            call TimerStart(SpellTimer, TIMER_TICK, true, function thistype.periodic)
          endif
          set GSD[COUNT] = sd
          set COUNT = COUNT+1

          return sd
        endmethod

        static method onCast takes nothing returns boolean
          if GetSpellAbilityId() == SPELL_ID then
            call SpellData.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
            call GSD[COUNT-1].onStart()
          endif
          return false
        endmethod

        static method onInit takes nothing returns nothing
          call TriggerRegisterAnyUnitEventBJ(CastTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT)
          call TriggerAddCondition(CastTrigger, Condition(function thistype.onCast))
          call thistype.onInitEx()
        endmethod
      endstruct

    endlibrary
     

    Attached Files:

  3. Dinodin

    Dinodin

    Joined:
    Jul 19, 2007
    Messages:
    492
    Resources:
    2
    Maps:
    2
    Resources:
    2
    Sry but that's not really how I would like the spell to be like... and I prefer it to NOT be created by JASS-triggers because I'm totally lost at it and almost afraid of JASS-triggerings... You don't know the standard Shockwave ability? I would like it to be like that but with tornado-missile instead and knock all enemies that it hits aside from the tornado with the distance of 250 and damage them and then slowing their movement speed for the short duration.
     
  4. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,663
    Resources:
    1
    Spells:
    1
    Resources:
    1
    The spell literally does everything that you asked. Just change the model of the effect in the configuration. It deals damage, the caster I used just doesn't have any intelligence. It knocks back and it slows them with a dummy slow ability.

    If you have very specific requests then you need to include that information in your request post. You did not specify that you are afraid of JASS. You didn't specify which models should be used where. You didn't specify how far to knock back, how long the slow should last, or the radius of the effect, so I chose values for these things myself. Read the spell. Read the configuration. Mess with some of the config variables. If something doesn't work the way you wanted it to, I can suggest how to change it. If you have questions, ask them, but don't refuse someone's hard work because it doesn't conform to a standard you didn't lay out when you asked for it.
     
  5. Uncle

    Uncle

    Joined:
    Aug 10, 2018
    Messages:
    289
    Resources:
    0
    Resources:
    0
    You don't have to understand JASS to know how to use his spell. Here's what you have to configure:

    Code (Text):

    library Windblast requires Knockback3D
      globals
        public  integer SPELL_ID   = 'Wbst'
        public  integer DUMMY_ID   = 'dmmy'
        private real    TIMER_TICK = 0.03
        private real MAX_COLLISION = 128. //This ISN'T the Area of Effect
     
        private real VELOCITY = 900. //The speed of the tornado missile
        private real DISTANCE = 600. //The distance the tornado travels
     
        private real KB_VEL = 1000. //The speed of the knockback effect
        private real KB_ALPHA = 0. //elevation angle to knock back at

        private real    EFFECT_H   = 10. //I assume this is the height of the effect
        private string  EFFECT_1A  = "Abilities\\Spells\\Other\\Tornado\\TornadoElementalSmall.mdl" //The model of the effect. Make sure to use \\ instead of a single \ between the words like how I have it set

        private real    E1A_SCALE  = 0.8 //The size of the tornado missile
        private real    E1A_TSCALE = .1
        private real    E1A_TSKIP  = 1.0 //skip this far ahead in the stand animation

        private integer E1A_COL_R  = 255 //The colors of the tornado missile. These are the default values
        private integer E1A_COL_G  = 255
        private integer E1A_COL_B  = 255

      endglobals
     
    So when you click on the Trigger "Windblast", you'll see that he put all of the configurable variables at the top. Simply modify these to match the values that you desire. The names are pretty self-explanatory. If you look at the code I posted I already edited the fields to probably match what you want, I also added some comments like "//The speed of the tornado missile" you don't need to copy that it's not part of the code.

    @Pyrogasm
    One thing I did notice was that the knockback seems a little funky. Maybe it's intended this way, but it seems to shove units to the side instead of knocking them back based on their angle between the unit and the tornado.
     
    Last edited: Jul 24, 2019
  6. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,663
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Actually, no, and I should have annotated that specific variable in particular. That's actually the max collision size of a unit in the map, used for range checking with IsUnitInRangeXY. The impact radius is configured in the spellBehavior module below.

    I specifically wrote it this way. Otherwise, units mostly along the line of the spell would be knocked back along the spell direction rather than away from it.