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:



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

~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.
  • Varine Varine:
    I have a bunch of community service I have to do, and I have a feeling there will be a lot of snow shoveling involved now
  • The Helper The Helper:
    don't know why people choose to live in that kind of weather :)
  • Varine Varine:
    Well
  • Varine Varine:
    My job is here
  • Varine Varine:
    I was born here man I didn't choose shit
  • Varine Varine:
    And also, I keep moving away and it doesn't get any better. I moved to San Antonio and shit froze there AND we had blackouts
  • tom_mai78101 tom_mai78101:
    I'm back, suffering from severe jet lag.
  • The Helper The Helper:
    Cold is following your ass around Varine - I just dont think I could handle a snowy winter.
  • The Helper The Helper:
    Welcome Back Tom!
  • The Helper The Helper:
    I hear that taking a Melatonin around your normal bedtime can really fix jet lag
  • tom_mai78101 tom_mai78101:
    Yeah, I also heard about that as well. I think I'm good. I'm just lucky it's the weekend so I have some time to readjust.
    +1
  • Varine Varine:
    It was a fucking blizzard today, shoveling was an effort in futility.
  • Varine Varine:
    Melatonin gives me nightmares
  • Varine Varine:
    They had me shoveling out the parking lot for the jail. Fucking pointless, they need a blow.
  • Varine Varine:
    Well they gave me a snow blower but it was too wet to do much with with other than compact everything. Oh well, the cop cars can get into there now when they arrest people I guess
    +1
  • The Helper The Helper:
    I shit you not we are Number one on Google for Banana Bread Recipe
  • jonas jonas:
    xD
    +1
  • The Helper The Helper:
    Need Web Design Contractor that knows Word Press, Woocommerce and Elementor to help with the company that sponsors this site
  • The Helper The Helper:
  • The Helper The Helper:
    I am going to be AFK most of this weekend has I have a friend coming into town for his company christmas party and I am going to be busy with that this weekend.
    +1
  • The Helper The Helper:
    541 Active Users and this number has been pretty steady give or take some. The bots are hammering us.
  • The Helper The Helper:
    I have been posting a bunch of recipes and Google is loving them. I mean OK my shit is good but how does google know LOL!

    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