HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Goblin Bomber

09-09-2008, 06:28 PM#1
Tukki

An explosive spell made out of to much spare time, and perhaps some coding.

Version: 1.3

MUI: Yes.
GUI/JASS/vJass: vJass.
Leakless: Should be.
Laggless: As far as I can tell.
Requirements: xebasic, xedamage and xepreload

Description

Spell Type: Goblin Explosion
Cooldown: 20 - 14 Seconds.

Sends a goblin zeppelin, packed with explosive stuff, towards the targeted point.
Once it reaches it bombs will begin to fall, dealing damage to nearby enemy land units.



Screenshots

Are at the bottom



Collapse Code:
 //===========================================================================================
// Goblin Bomber - v 1.3 by Tukki
//============================================================================================
//
//  Requires:
//--------------------------------------------------------------------------------------------
//      * A point-targetable spell.
//      * The dummy model. (Made by Vexorian)
//      * This trigger.
//      * A vJass preprocessor.
//      * xebasic, xepreload and xedamage ([url]http://www.wc3campaigns.net/showthread.php?t=101150[/url])
//
//  How to import:
//--------------------------------------------------------------------------------------------
//      * Import xe according to the link if you haven't got it.
//      * Copy this trigger into your map.
//      * Assign the appropriate values to the globals further down.
//
//  Description;
//--------------------------------------------------------------------------------------------
//    Spell Type: Goblin Explosion
//    Cooldown: 20 - 14 Seconds.
//
//    Sends a goblin zeppelin, full with explosive stuff, towards the targeted point. 
//    Once it reaches it bombs will begin to fall, dealing damage to nearby enemy land units.
//============================================================================================

library GoblinBomber initializer InitTrig uses xebasic, xepreload, xedamage

    globals
        // - - - - - - - - - -
        // CHANGABLE THINGS
        
        //===========================================================================
        // Rawcode global.
        private constant integer AID_GOBLIN_BOMBER = 'A000' //  Spell rawcode.
        
        //===========================================================================
        // Distance and Speed globals.
        private constant real ZEPPELIN_VELOCITY    = 600.0  //  The velocity of the zeppelin.
        private constant real DISTANCE             = 1000.0 //  The distance of how far behind the zeppelin is spawned. MUST be positive.
        
        //===========================================================================
        // Different height globals.
        private constant real BOMB_HEIGHT          = 150.0   //  The flying height of the zeppeling when it drops the bombs.
        private constant real FLY_HEIGHT           = 300.0  //  Normal flying height of the zeppelin.
       
        //===========================================================================
        // Unit-right-height duration globals.
        private constant real BOMBING_TIME         = 0.5    //  Time it takes to go into bombing-height. MUST be positive.
        private constant real HEIGHT_RESTORE       = 0.7    //  Time it takes to revert back to normal height. MUST be positive.
        
        //===========================================================================
        // Bomb globals.
        private constant real FALL_TIME            = 0.5    //  Time it takes for the bombs to fall.MUST be positive.
        private constant real BOMB_EXPLODE_HEIGHT  = 0.010  //  The height upon the bombs explodes. Should not be changed.
        private constant real BOMBING_DISTANCE     = 300.0  //  All bombs will be dropped over this distance. 
                                                            //  The distance between the bombs is: BOMBING_DISTANCE/n, where n is number of bombs. Must be grater than 0!
        
        //===========================================================================
        // Timer interval global.
        private constant real INTERVAL             = 0.03125//  The interval between each iteration.
        
        //===========================================================================
        // SFX model globals.
        private constant string ZEPPELIN_MODEL     = "Units\\Creeps\\GoblinZeppelin\\GoblinZeppelin.mdl"
        private constant string BOMB_MODEL         = "Abilities\\Weapons\\Mortar\\MortarMissile.mdl"
        private constant string EXPLOSION_MODEL    = "" // "Abilities\\Weapons\\Mortar\\MortarMissile.mdl"
                                                            //  Effects for "name"_MODEL object.
        
        //===========================================================================
        // Visual globals.
        private constant real ZEPPELIN_SCALE       = 1.50   // Scale of the flying ship.
        private constant real BOMB_SCALE           = 1.30   // Scale of the dropped thing.
        private constant boolean HIDE_ON_DEATH     = false  // Hide units when they die?   
        private constant boolean RESET_LOOK        = true   // Try to reset the facing of the bomb?
        private constant string DAMAGE_MODEL       = ""     // Use an effect on damaged units.
        
        //===========================================================================
        // Attachment globals.
        private constant string MODEL_ATTACHMENT_POS = "origin" // Attachment point for BOMB/ZEPPELIN effects.
        private constant string DAMAGE_POS           = "chest"  // Attachment point for damage model.
    endglobals

    //===========================================================================
    // How many bombs per level. 3 base + 1/level.
    private function GetBombs takes integer lvl returns integer
        return (3 + ((lvl-1) * 1))
    endfunction 

    //===========================================================================
    // Damage per bomb. 33 base + 11/level.
    private function GetDamage takes integer lvl returns real
        return (33.0 + ((lvl-1) * 11.0))
    endfunction 

    //===========================================================================
    // Radius of each explosion. 200 base + 0/level.
    private function GetArea takes integer lvl returns real
        return (200.0 + ((lvl-1) * 0.0))
    endfunction 

    //===========================================================================
    // Function for exploding stuff.
    
    // keywords, do not change.
    private keyword Bomb
    private keyword damageOpt
    
    private function explode takes Bomb b returns nothing 
     local unit u
     
        call DestroyEffect(AddSpecialEffect(EXPLOSION_MODEL, GetUnitX(b.bomb), GetUnitY(b.bomb)))
        
        call damageOpt.damageAOE(b.caster, GetUnitX(b.bomb), GetUnitY(b.bomb), b.aoe, b.damage)
    endfunction 


    //===========================================================================
    // Function for managing damage options.

    private function SetXEDamage takes nothing returns nothing
        // Target options.
        set damageOpt.damageAllies = false
        set damageOpt.damageEnemies = true
        set damageOpt.damageTrees = true
        
        // Damage options.
        set damageOpt.dtype  = DAMAGE_TYPE_UNIVERSAL
        set damageOpt.atype  = ATTACK_TYPE_SIEGE
        set damageOpt.wtype  = WEAPON_TYPE_WHOKNOWS
        
        // Use effect on damaged units?
        call damageOpt.useSpecialEffect(DAMAGE_MODEL, DAMAGE_POS)
    endfunction

    //     - - - - - - - - - - - - - - - - - - 
    //     | NO TOUCHING PAST THIS BLOCK!    | 
    //     | unless you like errors that is  | 
    //==== - - - - - - - - - - - - - - - - - - ==================================

    //===========================================================================
    // Private globals.
    globals
        private timer Timer     = null
        private xedamage damageOpt
    endglobals 

    //===========================================================================
    //  struct for the bombs.
    private struct Bomb 
        unit bomb
        unit caster
        effect model

        real lifeTime
        integer indexPlace
        
        real damage
        real aoe
        
        static Bomb array BOMBS
        static integer COUNTER = 0
        
        //===========================================================================
        // creation method.
        static method create takes unit bomber, unit c, integer lvl returns Bomb
         local Bomb b = Bomb.allocate()
         
            set b.bomb = CreateUnit(GetOwningPlayer(bomber), XE_DUMMY_UNITID, GetUnitX(bomber), GetUnitY(bomber), 0)
            set b.model = AddSpecialEffectTarget(BOMB_MODEL, b.bomb, MODEL_ATTACHMENT_POS)
            
            set b.lifeTime = FALL_TIME
            set b.damage = GetDamage(lvl)
            set b.aoe = GetArea(lvl)
            set b.caster = c
            
            // make the unit flyable and set its flying height. Along with facing.
            call UnitAddAbility(b.bomb, XE_HEIGHT_ENABLER)
            call UnitRemoveAbility(b.bomb, XE_HEIGHT_ENABLER)
            
            call SetUnitFlyHeight(b.bomb, GetUnitFlyHeight(bomber), 0)
            call SetUnitScale(b.bomb, BOMB_SCALE, BOMB_SCALE, BOMB_SCALE)
            call SetUnitAnimationByIndex(b.bomb, 0)
            
            // calculate the time it takes for the bomb to reach BOMB_EXPLODE_HEIGHT.
            call SetUnitFlyHeight(b.bomb, BOMB_EXPLODE_HEIGHT, (GetUnitFlyHeight(b.bomb) - BOMB_EXPLODE_HEIGHT) / FALL_TIME)
            
            // Correct the position of the bomb.
            call SetUnitPathing(b.bomb, false)
            call SetUnitX(b.bomb, GetUnitX(bomber))
            call SetUnitY(b.bomb, GetUnitY(bomber))
            
            // "attach" struct to arrays.
            set b.indexPlace = Bomb.COUNTER
            set Bomb.BOMBS[Bomb.COUNTER] = b
            set Bomb.COUNTER = Bomb.COUNTER + 1

            return b
        endmethod 
        
        //===========================================================================
        // update method.
        method update takes nothing returns nothing
            set this.lifeTime = this.lifeTime - INTERVAL
            if (this.lifeTime <= 0.01 ) then
                call explode(this)
                call this.destroy()
            endif
        endmethod 
        
        //===========================================================================
        // upon destruction method.
        method onDestroy takes nothing returns nothing
            if (HIDE_ON_DEATH) then
                call ShowUnit(this.bomb, false)
            endif
            if (RESET_LOOK) then
                call SetUnitFacing(this.bomb, 0)
                call SetUnitAnimationByIndex(this.bomb, 90)
            endif
            call KillUnit(this.bomb)
            call DestroyEffect(this.model)
                
            // recycle array slots.
            set Bomb.COUNTER = Bomb.COUNTER - 1
            set Bomb.BOMBS[Bomb.COUNTER].indexPlace = this.indexPlace
            set Bomb.BOMBS[this.indexPlace] = Bomb.BOMBS[Bomb.COUNTER]
        endmethod 
    endstruct

    //===========================================================================
    // zeppelin struct.
    private struct Data 
        unit zeppelin
        unit caster
        effect model
        integer level
        integer nbombs
        
        real distToBombing
        real intervalBetween
        real nextBomb
        
        real distanceLeft
        real xTick
        real yTick
        
        boolean bombingAct
        integer indexPlace
        
        static Data array DATA
        static integer COUNTER = 0
        
        //===========================================================================
        // creation method.
        static method create takes nothing returns Data
         local Data d = Data.allocate()
         local location loc = GetSpellTargetLoc()
         local real angle 
         local real x
         local real y
         
            set d.caster = GetTriggerUnit()
            set d.level = GetUnitAbilityLevel(d.caster, AID_GOBLIN_BOMBER)
            set d.nbombs = GetBombs(d.level)
            set angle = Atan2(GetLocationY(loc) - GetUnitY(d.caster), GetLocationX(loc) - GetUnitX(d.caster))
            
            set x = GetUnitX(d.caster) - DISTANCE * Cos(angle)
            set y = GetUnitY(d.caster) - DISTANCE * Sin(angle)
            
            set d.zeppelin = CreateUnit(GetOwningPlayer(d.caster), XE_DUMMY_UNITID, x, y, angle*bj_RADTODEG) 
            set d.model = AddSpecialEffectTarget(ZEPPELIN_MODEL, d.zeppelin, MODEL_ATTACHMENT_POS)
            
            call UnitAddAbility(d.zeppelin, XE_HEIGHT_ENABLER)
            call UnitRemoveAbility(d.zeppelin, XE_HEIGHT_ENABLER)
            
            // position correction.
            call SetUnitPathing(d.zeppelin, false)
            call SetUnitX(d.zeppelin, x)
            call SetUnitY(d.zeppelin, y)
            
            call SetUnitFlyHeight(d.zeppelin, FLY_HEIGHT, 0)
            call SetUnitScale(d.zeppelin, ZEPPELIN_SCALE, ZEPPELIN_SCALE, ZEPPELIN_SCALE)
            
            // here we calcualte distance left, x/y position per iteration, distance to bombing
            // and the interval between the bomb drops.
            set d.distanceLeft = (SquareRoot(Pow(GetLocationX(loc) - GetUnitX(d.caster), 2) + Pow(GetLocationY(loc) - GetUnitY(d.caster), 2)) + DISTANCE) * 2 
            set d.xTick = (ZEPPELIN_VELOCITY * INTERVAL) * Cos(angle)
            set d.yTick = (ZEPPELIN_VELOCITY * INTERVAL) * Sin(angle)
            set d.distToBombing = (d.distanceLeft - BOMBING_DISTANCE) * 0.5
            set d.intervalBetween = BOMBING_DISTANCE/d.nbombs
            
            set d.nextBomb = 0.0
            
            // set array slots and pointers so we may retrieve data later on.
            set d.indexPlace = Data.COUNTER
            set Data.DATA[Data.COUNTER] = d
            set Data.COUNTER = Data.COUNTER + 1
            
            return d
        endmethod 
        
        //===========================================================================
        // update method.
        method update takes nothing returns nothing
         local real x = GetUnitX(this.zeppelin) + this.xTick // prepare for next position.
         local real y = GetUnitY(this.zeppelin) + this.yTick
         
            if (this.distanceLeft > 0) then
                if (this.distToBombing > 0) then
                    set this.distToBombing = this.distToBombing - (ZEPPELIN_VELOCITY * INTERVAL)
                    if (this.distToBombing <= 0) then
                        call SetUnitFlyHeight(this.zeppelin, BOMB_HEIGHT, (FLY_HEIGHT - BOMB_HEIGHT)/BOMBING_TIME) 
                        set this.bombingAct = true
                    endif
                elseif (this.bombingAct) then
                    if (this.nbombs > 0) then
                        set this.nextBomb = this.nextBomb - ZEPPELIN_VELOCITY * INTERVAL
                        if (this.nextBomb <= 0) then
                            set this.nbombs = this.nbombs - 1
                            set this.nextBomb = this.intervalBetween
                            call Bomb.create(this.zeppelin, this.caster, this.level)
                        endif
                    else
                        set this.bombingAct = false
                        call SetUnitFlyHeight(this.zeppelin, FLY_HEIGHT, (FLY_HEIGHT - BOMB_HEIGHT)/HEIGHT_RESTORE)
                    endif
                endif
                set this.distanceLeft = this.distanceLeft - (ZEPPELIN_VELOCITY * INTERVAL)
                call SetUnitX(this.zeppelin, x)
                call SetUnitY(this.zeppelin, y)
            else
                call this.destroy()
            endif
        endmethod 
        
        //===========================================================================
        // upon destruction method.
        method onDestroy takes nothing returns nothing
            if (HIDE_ON_DEATH) then
                call ShowUnit(this.zeppelin, false)
            endif
            call KillUnit(this.zeppelin)
            call DestroyEffect(this.model)
            
            // here we recycle arrays slots.
            set Data.COUNTER = Data.COUNTER - 1
            set Data.DATA[Data.COUNTER].indexPlace = this.indexPlace
            set Data.DATA[this.indexPlace] = Data.DATA[Data.COUNTER]
        endmethod 
    endstruct

    //===========================================================================
    // function for looping through instances.
    private function handlerFunc takes nothing returns nothing
     local integer i = 0
     
        // loop through all Zeppelin structs.
        loop
            exitwhen i >= Data.COUNTER
            call Data.DATA[i].update()
            set i = i + 1
        endloop
        
        // loop through all bomb structs.
        set i = 0
        loop
            exitwhen i >= Bomb.COUNTER
            call Bomb.BOMBS[i].update()
            set i = i + 1
        endloop
        
        if (Data.COUNTER == 0 and Bomb.COUNTER == 0) then
            call PauseTimer(Timer)
        endif
    endfunction 

    //===========================================================================
    // runs on the spell effect event.
    private function act takes nothing returns boolean
        if (GetSpellAbilityId() == AID_GOBLIN_BOMBER) then
            call Data.create()
            if (Data.COUNTER == 1) then
                call TimerStart(Timer, INTERVAL, true, function handlerFunc)
            endif
        endif
        return false
    endfunction 

    //===========================================================================
    // Initialization function.
    private function InitTrig takes nothing returns nothing
     local trigger t = CreateTrigger()
     
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Condition( function act))
        
        // xe functions.
        set damageOpt = xedamage.create()
        call SetXEDamage()
        call XE_PreloadAbility(AID_GOBLIN_BOMBER)
        
        // initialize global variables.
        set Timer = CreateTimer()
    endfunction
endlibrary

Log

  • 9/9/08 - v 1.0 released.
  • 2/10/08 - v 1.1 released.
  • 3/10/08 - v 1.2 released. Added xe modules.
  • 10/10/08 - v 1.3 released. Fixed fly-restore formula, and removed usless code. Also updated "readme".



Comments and Suggestions are welcome.
Attached Images
File type: jpgIn-gameScreenie.jpg (297.8 KB)
Attached Files
File type: w3xGoblinBomber 1.3.w3x (84.2 KB)
09-09-2008, 06:41 PM#2
Vestras
Seriusly, I don't get it. Why do people use methods for spells? I think it's well, no offense, but, stupid.

Well, aside from that, beautifully coded.
09-09-2008, 06:52 PM#3
Tukki
Quote:
Why do people use methods for spells?
Why not, Vexorian must have made them for some purpose? Any idea why you dislike methods, as the above statement gives nothing to me ^^

Quote:
Well, aside from that, beautifully coded.
Thanks! Buut.. how do you code beautifully?
09-09-2008, 08:13 PM#4
darkwulfv
Methods are useful for spells because they help clean up the code and stuff. At least, Creation methods that is. I dunno about others.
09-10-2008, 05:41 PM#5
Tukki
Yeah, and other methods makes the code more readable than just chunking everything into one, huge loop function.
09-10-2008, 05:53 PM#6
Vexorian
Quote:
Seriusly, I don't get it. Why do people use methods for spells? I think it's well, no offense, but, stupid.

Well, aside from that, beautifully coded.
The code looks more beautiful than average because of methods. Notice that it is better structured than usual...
09-10-2008, 06:32 PM#7
Tukki
Quote:
Notice that it is better structured than usual...
You mean that structs are better structured then functions?

EDIT: Just some random question;
When you are using the SetUnitFlyHeight( someunit, someheight, higher than 0) its fly height (via GetUnitFlyingHeight) automatically returns the specified height. Is there some way around this?
09-11-2008, 03:01 PM#8
Vestras
Quote:
Originally Posted by Tukki
You mean that structs are better structured then functions?

EDIT: Just some random question;
When you are using the SetUnitFlyHeight( someunit, someheight, higher than 0) its fly height (via GetUnitFlyingHeight) automatically returns the specified height. Is there some way around this?

Set the height with a timer.
09-11-2008, 05:47 PM#9
Tukki
Yes, but then I would need to manipulate around with Z heights of terrain and units -- which is too much for me.
09-18-2008, 06:20 PM#10
Tukki
[bump]B.O.M.B[/bump]
10-01-2008, 05:15 PM#11
Flame_Phoenix
I seriously think you stole one of my maps and converted it to vJASS ...
I hope I am mistaken, because if I am not (I don't think I am) than we have a problem here...
10-01-2008, 10:48 PM#12
Kam
Ok well this is a simple issue.

Attach the map you are saying he stole, unprotected, so we can all see. If it is the case then I'm sure a mod will take appropriate actions.
10-02-2008, 10:35 AM#13
Flame_Phoenix
I believe this map is way too similar to my map and that he only remade the code and changed some small stuff like the bombs and the goblin.... however, see it by yourselves, but I don't think I am mistaken:
http://www.hiveworkshop.com/resources_new/spells/1324/
10-02-2008, 11:59 AM#14
Captain Griffen
FP, don't throw random accusations around. You don't have an exclusive right to bomber-type spells. Let me quote you:

Quote:
he only remade the code

The code in your map is bad and totally different to this. So, the code is totally different, and yet you think he nicked your spell...?

Right...I suggest you drop this.
10-02-2008, 12:41 PM#15
Flame_Phoenix
Quote:
The code in your map is bad and totally different to this. So, the code is totally different, and yet you think he nicked your spell...?
I did that thing ages ago even before coming here. Everything in that spell look like my spell:
- drops 3 bombs
- plane goes down and up
- plane appears from behind the caster
- plane dies after

So, although the code is different, ya I believe he saw something ...