• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Launch Nuclear v1.1

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
The hero launches a nuclear towards a target location, if it has a missile reloaded, increasing its damage every few seconds.

Requires:
GTrigger - Jesus4Lyf
SimError - Vexorian
TimerUtils - Vexorian
DestructableLib - PitzerMike

JASS:
scope LaunchNuclear initializer Init //requires TimerUtils, SimError, DestructableLib, GTrigger
    globals
        private constant integer ABILITY_ID = 'A001' //ability id of the Launch Nuclear ability
        private constant integer NUC_ID = 'A000' //the ability id of the Reload Nulcear ability
        private constant integer UNIT_ID = 'h000' //the unit id of the Nuclear dummy unit
        private constant real INCREMENT_INC = 0.30 //each time the timer hit this value, variables (damage, speed, etc.) will increase
        private constant real INTERVAL = 0.03 //interval of the timers
        private constant real SPEED_ADD = 1 //speed increased by the missile every INCREMENT_INC seconds
        private constant real MAX_SPEED = 13 //maximum speed of the missile
        private constant real TREE_RADIUS = 150 //area where trees will be destroyed
        private constant real DAMAGE_RADIUS = 525 //area where enemies will be damaged
        private constant real DESCEND_DIST = 450 //the distance in between the missile and the target where the missile will start to descend to the ground.
        private constant string SFX = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl" //the effect applied when the nuclear explodes
        private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL //attack type of the final damage 
        private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC //damage type of the final damage
        //==============================================
        private hashtable nuc = InitHashtable()
        private hashtable ht = InitHashtable()
        private integer temp
        private rect rect1 = null
        private group nucGroup
    endglobals
    
    private function GetDamage takes unit u, integer level returns real
        //set the base damage here (smallest amount)
        return I2R(level)*50
    endfunction
    
    private function DamageIncrement takes unit u, integer level returns real
        //incremented damage per INCREMENT_INC second
        return I2R(level)*2
    endfunction
    
    private function MaxMissiles takes unit u, integer level returns integer
        //set the maximum number of missiles in every recharge
        return 1*level
    endfunction
    
    private function Damage_Condition takes unit u, unit f returns boolean
        //conditions to check units to be damaged
        return IsUnitEnemy(f,GetOwningPlayer(u)) and IsUnitType(f,UNIT_TYPE_DEAD) == false and IsUnitType(f,UNIT_TYPE_MAGIC_IMMUNE) == false
    endfunction
    
//------------------ END OF CONFIGURABLES ----------------------------
    
    private struct Launch
        unit caster
        unit dummy
        real speed
        real dmg
        real height
        real count 
        real facing
        real increment
        location target
        timer time 
        
        static method DamageGroup takes integer index, unit f returns nothing
            local thistype dat = index
            local boolean b = Damage_Condition(dat.caster,f)
            if b then
                call UnitDamageTarget(dat.caster,f,dat.dmg,false,false,ATTACK_TYPE,DAMAGE_TYPE,null)
            endif
        endmethod
        
        static method DestroyTrees takes nothing returns nothing
            if IsDestructableTree(GetEnumDestructable()) then
                call KillDestructable(GetEnumDestructable())
            endif
        endmethod
        
        static method CheckGroup takes nothing returns nothing
            call thistype.DamageGroup.execute(temp,GetEnumUnit())
        endmethod
        
        static method OnLoop takes nothing returns nothing
            local thistype dat = GetTimerData(GetExpiredTimer())
            local real angle = bj_RADTODEG * Atan2(GetLocationY(dat.target) - GetUnitY(dat.dummy), GetLocationX(dat.target) - GetUnitX(dat.dummy))
            local real dx = GetLocationX(dat.target) - GetUnitX(dat.dummy)
            local real dy = GetLocationY(dat.target) - GetUnitY(dat.dummy)
            local real dist = SquareRoot(dx * dx + dy * dy)
            local real x
            local real y
            set dat.count = dat.count + INTERVAL
            if dat.count >= INCREMENT_INC then
                set dat.dmg = dat.dmg + dat.increment
                if dat.speed < MAX_SPEED then
                    set dat.speed = dat.speed + SPEED_ADD
                endif
            endif
            if dist > DESCEND_DIST then
                call SetUnitFacing(dat.dummy,dat.facing)
                set x = GetUnitX(dat.dummy) + dat.speed * Cos(angle * bj_DEGTORAD)
                set y = GetUnitY(dat.dummy) + dat.speed * Sin(angle * bj_DEGTORAD)
                call SetUnitX(dat.dummy,x)
                call SetUnitY(dat.dummy,y)
            else
                set dat.height = dat.height - 5
                call SetUnitFacing(dat.dummy,angle)
                call SetUnitFlyHeight(dat.dummy,dat.height,1000)
                set x = GetUnitX(dat.dummy) + dat.speed * Cos(angle * bj_DEGTORAD)
                set y = GetUnitY(dat.dummy) + dat.speed * Sin(angle * bj_DEGTORAD)
                call SetUnitX(dat.dummy,x)
                call SetUnitY(dat.dummy,y)
            endif
            if dist <= 20 then
                call RemoveSavedInteger(ht,0,GetHandleId(dat.dummy))
                call DestroyEffect(AddSpecialEffect(SFX,GetUnitX(dat.dummy),GetUnitY(dat.dummy)))
                call SetRect(rect1,GetUnitX(dat.dummy)-TREE_RADIUS,GetUnitY(dat.dummy)-TREE_RADIUS,GetUnitX(dat.dummy)+TREE_RADIUS,GetUnitY(dat.dummy)+TREE_RADIUS)
                call EnumDestructablesInRect(rect1,null,function thistype.DestroyTrees)
                call GroupEnumUnitsInRange(nucGroup,GetUnitX(dat.dummy),GetUnitY(dat.dummy),DAMAGE_RADIUS,null)
                set temp = dat
                call ForGroup(nucGroup,function thistype.CheckGroup)
                call RemoveUnit(dat.dummy)
                call RemoveLocation(dat.target)
                call ReleaseTimer(dat.time)
                call dat.destroy()
            endif
        endmethod
        
        static method start takes nothing returns nothing
            local thistype dat = thistype.allocate()
            local real angle
            local integer int = LoadInteger(nuc,0,GetHandleId(GetTriggerUnit()))
            set dat.caster = GetTriggerUnit()
            set dat.target = GetSpellTargetLoc()
            set dat.dummy = CreateUnit(GetOwningPlayer(dat.caster),UNIT_ID,GetUnitX(dat.caster),GetUnitY(dat.caster),GetUnitFacing(dat.caster))
            set angle = bj_RADTODEG * Atan2(GetLocationY(dat.target) - GetUnitY(dat.dummy), GetLocationX(dat.target) - GetUnitX(dat.dummy))
            call SetUnitFacingTimed(dat.dummy,angle,0.7)
            call PauseUnit(dat.dummy,true)
            set dat.facing = angle
            call SaveInteger(ht,0,GetHandleId(dat.dummy),dat)
            set dat.speed = 1
            set dat.dmg = GetDamage(dat.caster,GetUnitAbilityLevel(dat.caster,ABILITY_ID))
            set dat.increment = DamageIncrement(dat.caster,GetUnitAbilityLevel(dat.caster,ABILITY_ID))
            set int = int - 1
            call SaveInteger(nuc,0,GetHandleId(dat.caster),int)
            set dat.count = 0
            set dat.height = 200
            set dat.time = NewTimer()
            call SetTimerData(dat.time,dat)
            call TimerStart(dat.time,INTERVAL,true,function thistype.OnLoop)
        endmethod
        
        static method OnDeath takes nothing returns nothing
            local thistype dat = LoadInteger(ht,0,GetHandleId(GetTriggerUnit()))
            call RemoveLocation(dat.target)
            call ReleaseTimer(dat.time)
            call dat.destroy()
        endmethod 
        
        static method CheckCast takes nothing returns nothing
            local unit u = GetTriggerUnit()
            if LoadInteger(nuc,0,GetHandleId(u)) == 0 then
                call IssueImmediateOrder(u,"stop")
                call SimError(GetOwningPlayer(u),"You don't have enough nuclears!")
            endif
            set u = null
        endmethod
        
        static method OnLearn takes nothing returns nothing
            local unit u = GetTriggerUnit()
            call SaveInteger(nuc,0,GetHandleId(u),0)
            call UnitAddAbility(u,NUC_ID)
            call UnitMakeAbilityPermanent(u,true,NUC_ID)
            set u = null
        endmethod
        
        static method OnReload takes nothing returns nothing
            local unit u = GetTriggerUnit()
            local integer maxa = MaxMissiles(u,GetUnitAbilityLevel(u,ABILITY_ID))
            local integer int = LoadInteger(nuc,0,GetHandleId(u))
            if int < maxa then
                set int = int + 1
                call SaveInteger(nuc,0,GetHandleId(u),int)
                call DisplayTimedTextToPlayer(GetOwningPlayer(u),0,0,3.,"Nuclear Reloaded: " + I2S(int) + "/" + I2S(maxa))
            else
                call DisplayTimedTextToPlayer(GetOwningPlayer(u),0,0,3.,"You're fully reloaded dude: " + I2S(int) + "/" + I2S(maxa))
            endif
            set u = null
        endmethod
        
        static method conds takes nothing returns boolean
            return GetSpellAbilityId() == ABILITY_ID
        endmethod
        
        static method CheckDeath takes nothing returns boolean
            return LoadInteger(ht,0,GetHandleId(GetTriggerUnit())) != 0
        endmethod
        
        static method CheckLearn takes nothing returns boolean
            return GetLearnedSkill() == ABILITY_ID and GetUnitAbilityLevel(GetTriggerUnit(),NUC_ID) == 0
        endmethod
        
        static method CheckReload takes nothing returns boolean
            return GetSpellAbilityId() == NUC_ID
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerAddAction(t,function thistype.start)
            call GT_RegisterStartsEffectEvent(t,ABILITY_ID)
            set t = CreateTrigger()
            call TriggerAddAction(t,function thistype.CheckCast)
            call GT_RegisterBeginsCastingEvent(t,ABILITY_ID)
            set t = CreateTrigger()
            call TriggerAddAction(t,function thistype.OnDeath)
            call TriggerAddCondition(t,function thistype.CheckDeath)
            call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH)
            set t = CreateTrigger()
            call TriggerAddAction(t,function thistype.OnLearn)
            call TriggerAddCondition(t,function thistype.CheckLearn)
            call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_HERO_SKILL)
            set t = CreateTrigger()
            call TriggerAddAction(t,function thistype.OnReload)
            call GT_RegisterStartsEffectEvent(t,NUC_ID)
            set t = null
        endmethod
    endstruct
    
    private function Init takes nothing returns nothing
        call Preload(SFX)
        set nucGroup = CreateGroup()
    endfunction
endscope


v1.1
- Updated with [post=2002166]Bribe's Suggestions[/post]
- No longer requires AutoIndex (uses hashtables instead,sorry 'bout that)
- Removed facing timer
- Optimize some config codes
v1.0
- Initial Release


Keywords:
nuclear,launch,jim7777,missile,bomb,boom,increase,damage
Contents

Launch Nuclear Spell v1.1 (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 19:04, 19th Oct 2011 Bribe: You do not need I2R calls unless you are using division. "== false" is a useless comparison. Just use the "not" keyword. You do not need to create a local...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

19:04, 19th Oct 2011
Bribe: You do not need I2R calls unless you are using division.

"== false" is a useless comparison. Just use the "not" keyword.

You do not need to create a local boolean in the DamageGroup method.

Why are you (ab)using the .execute behavior? It is completely useless where you have it compared to a normal function call, and it adds a lot of junk code when compiled. Get rid of it.

FirstOfGroup loops are better than your current setup, because you are not storing any units for the long-term.

TimerUtils is overkill for a timeout of 0.03. Use a lighter timer system for this like T32 for example.

This line is useless, delete it: call SaveInteger(nuc,0,GetHandleId(u),0)

Your conditions and actions should be merged into one.

This could use the new RegisterPlayerUnitEvent library.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,468
Truefilt

Did you know that since at least 2 years filters don't leak?

globalCaseConventionIsLikeThis NotLikeThis.

GetWidgetLife has problems what if the unit's life is set after death?
Better to use IsUnitType, UNIT_TYPE_DEAD.

You can use "null" instead of WEAPON_TYPE_WHOKNOWS

Why are you quickly creating and destroying a group instead of just
using one single global group that never gets destroyed?

Use a single timer that iterates through a stack of units for a timeout
like 0.03 you should not be using dynamic timers for such an easy
timeout.

The timeouts should also be configurable.

Why are you using GetUnitId in parts of the trigger but using a seperate
hashtable in others? Just use one or the other, not both.
 
Just noticed these:

JASS:
private function GetDamage takes unit u, integer level returns real
        //set the base damage here (smallest amount)
        local real dmg = level*50
        return dmg
    endfunction
    
    private function DamageIncrement takes unit u, integer level returns real
        //set the increment damage here (damage added in each distance) every INCREMENT_INC second
        local real dmg = level*2
        return dmg
    endfunction
    
    private function MaxMissiles takes unit u, integer level returns integer
        //set the maximum number of missiles in every recharge
        local integer max = 1*level
        return max
    endfunction

My reaction: :[____]

Those aren't inline friendly at all >.<

edit

Use this instead of GTrigger.
 
Last edited:
Top