Spell Homing Missile

Kenny

Back for now.
Reaction score
202
Homing Missile!
By kenny

This is one of my first spells that i have made in vJass, so i am not fully sure if it is completely MUI etc. That is the reason why i have posted it, to get some constructive feedback and criticism from other people, so that i may improve my abilities in jass.

There is a description for implementation in the map, labeled "Implementation" just above the Homing Missile trigger.

Import difficulty: Moderate.
Jass?: Yes.
vJass?: Yes.
Lagless?: Yes.
Leakless?: Yes.
MUI?: Yes.
Requires:
ABC - Struct Attachment System, by Cohadar;
CSSafety by Vexorian; and
NewGen World Editor/vJass Preprocessor

Description: Basically, this is a spell that creates a missile that will follow its target for X amount of seconds. The missile will either reach its target and deal damage to it and smaller damage to nearby enemies, or it will explode before it gets to the target and deal small damage to enemy units near the explosion.

Here are the Screenshots for the skill:
HMImage2.jpg


HMImage1.jpg

Tooltip:
Launches a deadly missile at a target unit. The missile is capable of homing in on the target for up to 5 seconds.

Level 1 - Missile deals 75 damage to the target and 25 damage to nearby enemy units if it reaches the target unit before 5 seconds, otherwise it will deal 25 damage to enemy units when it explodes.

Level 2 - Missile deals 150 damage to the target and 50 damage to nearby enemy units if it reaches the target unit before 5 seconds, otherwise it will deal 50 damage to enemy units when it explodes.

Level 3 - Missile deals 225 damage to the target and 75 to nearby enemy units if it reaches the target unit before 5 seconds, otherwise it will deal 75 damage to enemy units when it explodes.

Explosion has an area of effect of 225.

Here is the code for the spell:
JASS:
scope HomingMissile

// Homing Missile, by kenny!

globals
    private constant integer Abil_id = 'A000'         // Raw Code for Homing Missile Ability
    private constant integer Unit_id = 'h000'         // Raw Code for Homing Missile Dummy Unit
    private constant real Turnrate = 0.55             // Turn Rate; determines how wide the missile will turn if it misses the target, the larger the number the wider the turn
    private constant real Angle = 180.00              // Determines the angle at which the missile leaves the caster; not 100% necessary,but adds to realism and balance
    private constant real Distance = 15.00            // How far the dummy unit will move each period, larger the number the faster the missile; slow = 10-15, fast = 20-25
    private constant real Period = 0.03               // Used in periodic timer, best at 0.03 - 0.035
    private constant real Impact_dist = 90.00         // Determines the distance from the enemy at which the missile will explode, best at 90-100
    private constant real Damage_AoE = 225.00         // Area of Effect for missile explosion
    private constant attacktype A_type = ATTACK_TYPE_CHAOS         // Attack type of damage dealt
    private constant damagetype D_type = DAMAGE_TYPE_UNIVERSAL     // Damage type of damage dealt
    private constant group Damage_group = CreateGroup()            // Group created for Area of effect damage
    private constant string Effect = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl" // Special effect created when the missile explodes
endglobals

//=======================================================================
private function HMDamage takes integer lvl returns real
    return 50.*lvl // Damage dealt to target AND group (damage dealt to group is half of normal damage)
endfunction

//=======================================================================
private function FilterEnemies takes nothing returns boolean
    return GetWidgetLife( GetFilterUnit( ) ) > 0.405 and IsUnitType(GetFilterUnit( ), UNIT_TYPE_STRUCTURE) == false and IsUnitEnemy( GetFilterUnit( ), GetOwningPlayer( GetTriggerUnit( ) ) ) == true and IsUnitType( GetFilterUnit( ), UNIT_TYPE_MAGIC_IMMUNE ) == false
endfunction // So far the Area of Effect damage will deal damage to alive enemy units that are not structures or magic immune

//=======================================================================//
//   DO NOT TOUCH PAST THIS POINT UNLESS YOU KNOW WHAT YOUR ARE DOING!!  //
//=======================================================================//

private function HMDamageArea takes unit Damager, real Area, real x, real y, real Dmg returns nothing
    local unit FoG
    
    set bj_groupEnumOwningPlayer = GetOwningPlayer(Damager)
    call GroupEnumUnitsInRange(Damage_group, x, y, Area, Condition(function FilterEnemies)) 
                                                                                                  // Function that loops through the CreateGroup() 
    loop                                                                                          // and deals damage to each enemy in the area.                                                                                          //
        set FoG = FirstOfGroup(Damage_group)
        exitwhen FoG == null
        call GroupRemoveUnit(Damage_group,FoG)
        call UnitDamageTarget(Damager,FoG,Dmg,false,false,A_type,D_type,null)
    endloop
endfunction

//=======================================================================
private struct Data
    timer t
    unit targ
    unit dummy
    unit caster
    integer lvl
    location dumloc
    location targloc                               // onDestroy method damages group using HMDamageArea.
    location newpos                                // Also Clears Struct and releases timer.
    real x
    real y
    
    method onDestroy takes nothing returns nothing
        call HMDamageArea(.caster,Damage_AoE,.x,.y,(HMDamage(.lvl)/2))
        call DestroyEffect(AddSpecialEffectLoc(Effect,.dumloc))
        call ReleaseTimer(.t)
    endmethod
endstruct    

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

//=======================================================================
private function Move takes nothing returns nothing                     
    local Data d = GetTimerStructA(GetExpiredTimer())                   
    
    set d.dumloc = GetUnitLoc(d.dummy)
    set d.targloc = GetUnitLoc(d.targ)
    set d.newpos = PolarProjectionBJ(d.dumloc,Distance,GetUnitFacing(d.dummy))
    set d.x = GetLocationX(d.dumloc)
    set d.y = GetLocationY(d.dumloc)
    call SetUnitPositionLoc(d.dummy,d.newpos)                            
    call SetUnitFacingTimed(d.dummy,AngleBetweenPoints(d.dumloc,d.targloc),Turnrate)
        if(GetWidgetLife(d.dummy)<=.405)then // Deals damage to group if missile does not reach the target
            call ClearTimerStructA(d.t)
            call d.destroy()                 
        endif
                                                                        
        if(DistanceBetweenPoints(d.dumloc,d.targloc)<=Impact_dist)then
            call UnitDamageTarget(d.dummy,d.targ,HMDamage(d.lvl),false,false,A_type,D_type,null)
            call KillUnit(d.dummy)           // Deals damage to group if missile does reach the target
            call ClearTimerStructA(d.t)
            call d.destroy()            
        endif
        
        call RemoveLocation(d.dumloc)
        call RemoveLocation(d.targloc)
        call RemoveLocation(d.newpos)
endfunction                                                              

//=======================================================================
private function Actions takes nothing returns nothing
    local Data d = Data.create()
    local location casterpos
    
    set d.caster = GetTriggerUnit()
    set casterpos = GetUnitLoc(d.caster)
    set d.lvl = GetUnitAbilityLevel(d.caster,Abil_id)
    set d.dummy = CreateUnitAtLoc(GetOwningPlayer(d.caster),Unit_id,casterpos,(GetUnitFacing(d.caster)+GetRandomReal(-(Angle/2.00),(Angle/2.00))))
    set d.targ = GetSpellTargetUnit()
    
    set d.t = NewTimer()
    call SetTimerStructA(d.t,d)
    call TimerStart(d.t,Period,true,function Move) // Starts and continues the'Move' function periodically.
    
    call RemoveLocation(casterpos)    
    set casterpos=null
endfunction

//=======================================================================
public function InitTrig takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig,Condition(function Conditions))
    call TriggerAddAction(trig,function Actions)
endfunction

endscope

Note: I only just realised that there was another skill almost exactly the same to this one about half way through making it and i assure everyone that i had no intention of just copying a skill from someone else, despite how similar they seem.

Thanks to Tinki3 for the cool Test map and to everyone who helped me with the skill.

Hope everyone likes the spell and is able to give me some constructive feedback.

kenny!

*CHANGE LOG*

- 9th of April 08 - Optimized code and changed few things suggested my ~GaLs~, thanks.
-10th of April 08 - Modified trigger a bit, now contains more configurables in the global section
to make configuring easier. Thanks to cr4xzZz, ~GaLs~, Tinki3 and others.

*UPDATED TEST MAP*
 

Attachments

  • Spell - Homing Missile by Kenny!.w3x
    68.1 KB · Views: 606

~GaLs~

† Ғσſ ŧħə ѕαĸε Φƒ ~Ğ䣚~ †
Reaction score
180
Inline your code!

Change this
JASS:
private function FilterEnemies takes nothing returns boolean
    if( GetWidgetLife( GetFilterUnit( ) ) > 0.405 )then
        if( IsUnitType( GetFilterUnit( ), UNIT_TYPE_STRUCTURE ) == false )then
            if( IsUnitEnemy( GetFilterUnit( ), GetOwningPlayer( GetTriggerUnit( ) ) ) == true )then
                if( IsUnitType( GetFilterUnit( ), UNIT_TYPE_MAGIC_IMMUNE ) == false )then
                    return true
                endif                      // So far the Area of Effect damage        //
            endif                          // will deal damage to alive enemy units   //
        endif                              // that are not structures or magic immune //
    endif
    return false
endfunction

To this
JASS:
private function FilterEnemies takes nothing returns boolean
    return GetWidgetLife( GetFilterUnit( ) ) > 0.405 and IsUnitType(GetFilterUnit( ), UNIT_TYPE_STRUCTURE) == false and IsUnitEnemy( GetFilterUnit( ), GetOwningPlayer( GetTriggerUnit( ) ) ) == true and IsUnitType( GetFilterUnit( ), UNIT_TYPE_MAGIC_IMMUNE ) == false
endfunction


Since you are using vJass, why use location instead of real?

Edit - You need a screenshot.
 

Kenny

Back for now.
Reaction score
202
Thanks for your imput, i changed the FilterEnemies function straight away.

I have already stated in my opening post, that it is difficult right now for me to get the screenshots up and running, and that i'll do it when i get the time.

A few questions for anyone out there:
- Is this skill leak free and MUI?
- Does it Follow the JESP standard?, if it doesn't what else do i need, cause I'm pretty sure i have almost everything, except for screenies for obvious reasons.

*EDIT* To GoGo-Boy:
I Agree that it looks a bit stupid, but if i remember correctly, i think i saw a thread that stated that ands are faster then if-thens, only by the smallest amount, but i could be mistaken.
 

GoGo-Boy

You can change this now in User CP
Reaction score
40
Why should he put this into one line? I dunno whether "and's" are faster then "if - thens", but still it is, at least for 4 conditions a lot cleaner. Having one endless line is just stupid to look at.
 

~GaLs~

† Ғσſ ŧħə ѕαĸε Φƒ ~Ğ䣚~ †
Reaction score
180
>>Having one endless line is just stupid to look at.
Don't having 10 storey high of If-s aren't stupid to look at?

>>that it is difficult right now for me to get the screenshots up and running
It doesn't matter it is taken to the important part or not, but it really needs a screenshot.

>>Does it Follow the JESP standard?
Actually, it doesn't really follow JESP, or atleast you didn't pack in the document.

Edit:
These line can be done in a create static method.
JASS:
    local Data d = Data.create()

    set d.caster = GetTriggerUnit()
    set d.lvl = GetUnitAbilityLevel(d.caster,Abil_id)
    set d.dummy = CreateUnitAtLoc(GetOwningPlayer(d.caster),Unit_id,casterpos,(GetUnitFacing(d.caster)+GetRandomReal(-(Angle/2.00),(Angle/2.00))))
    set d.targ = GetSpellTargetUnit()
 

cr4xzZz

Also known as azwraith_ftL.
Reaction score
51
> Stop using locations! Coordinates are so much faster/better.

Nah. Locations are faster but you have to remove them in the end and that's why they aren't so popular. People are lazy and prefer reals. :}

> private function HMDamageArea takes unit Damager, real Area, real x, real y, real Dmg, attacktype Attacktype, damagetype Damagetype returns nothing

Why the hell do you add attacktype and damagetype as arguments? You already have them as globals, don't you? :) That's just unnecessary.

> unit targ = null
You could remove the =null part. Without a value it's still null.

> call ClearTimerStructA(d.t)
Put this in the onDestroy method.

> set gg_trg_HomingMissile = CreateTrigger()

If you want it to be really vJASS, make it:
local trigger trig = CreateTrigger()
gg_trg_ is just annoying ;P

Anyway, very good for a first vJASS spell. GJ :)
 

~GaLs~

† Ғσſ ŧħə ѕαĸε Φƒ ~Ğ䣚~ †
Reaction score
180
>>Nah. Locations are faster but you have to remove them in the end and that's why they aren't so popular. People are lazy and prefer reals. :}
False? Location do have alot of limitation as real doesn't.
In fact, location needs to be removed but real don't, you need to call a BJ to do offsets or angle or even distance.
 

Kenny

Back for now.
Reaction score
202
Ok so i have updated the skill according to the comments i have gotten. Only things i havent done are:
- created a static method create function, as ~GaLs~ suggested. This is because i aint that smart, lol.
- Changed the Locations to reals, i have nulled the locations so everything should be cool now, but at the moment im too lazy to change all of em to reals.

Updates include:
- More Configurables, making things easier for everyone.
- Nulling and removing useless code.
- More notes to help people understand.
- Completely vJass now (i think)
- I also think this version follows the JESP standard, if anyone can see why it doesn't please tell me so i can change it.
- And more.... so how about people download the map to check it out. Its a fun and versitile skill.

kenny!

*EDIT*

SCREENSHOTS ARE NOW UP! Take a look, they aren't that exciting but yeah thats them. Check out the complete skill by downloading the map.
 

~GaLs~

† Ғσſ ŧħə ѕαĸε Φƒ ~Ğ䣚~ †
Reaction score
180
Actually, after the spell if approved, it is nearly perfect, no more feedback is actually require.

Just to mention, if you do really, really not lazy, use REAL.
 

Weep

Godspeed to the sound of the pounding
Reaction score
401
I try to run the map; map does not run. At least the test map is broken.

"Broken due to outdated X" of course means it's the fault of a system that can be updated to work correctly. :rolleyes:
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      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