Spell Mana Bomb

Flare

Stops copies me!
Reaction score
662
My second vJASS spell. [DEL]This version is working, but it isn't VERY customizable (the only thing that would be very customizable would be mana cap).[/DEL]

[DEL](By very customizable, I mean you can't adjust it to suit a particular hero stat, or the ability's level. This will be added in one of the next few releases, if I get around to it)[/DEL]

Technical details:
Made in vJASS
Requires NewGen World Editor
Requires Cohadar's ABC Struct Attachment system
MUI
Lagless
Leakless to my knowledge

Importing instructions
Code:
Copy the Mana Bomb spell, and the Mana Bomb Dummy and Explode Dummy units into your map

Copy the ABC and ManaBomb triggers into your map

In Object Editor, press Ctrl-D to display the rawcodes of the units.

If they are different from the ones displayed at the top of the Mana Bomb code, change them (SPELLID = Mana Bomb rawcode, DUMMYID = Mana Bomb Dummy rawcode, EXPLODEDUMMYID = Explode Dummy rawcode)

Add the Mana Bomb ability to a Hero

Save your map, and test.

Tooltip/Description
The hero creates an orb of mana, which absorbs mana from surrounding enemies. When the caster stops channelling, the orb becomes unstable and detonates, dealing damage based on amount of mana stored.

Size of the orb increases as current mana increases.
Also, mana will NOT be drained from enemies once the orb is maxed out.

NOTE: If you cast this spell in the centre of the group of peasants, the orb will reach maximum size INCREDIBLY fast (2 seconds max)

Screenshots (re-uploaded screenshots with ImageShack)



Spell code
JASS:
//Mana Bomb (by Flare)
//Constructed using vJASS and Cohadar's ABC struct attachment system
//If you use this, give me credits!

//Globals, configurables ETC
//For the time being, all the details here will be relatively constant (functions containing calculation formulas will be added later)
//------------------------------------------------------------------------------------\\

scope ManaBomb
globals

//Ability and dummy ID's
    private constant integer SPELLID = 'A000'
    private constant integer DUMMYID = 'n000'
    
//The character string used by the mana bar
    private constant string MBSTRING = "''''''''''''''''''''''''''''''"
    
//Length of the mana bar
    private constant integer STRINGLENGTH = 30
        
//Mana bar size
    private constant real TEXTSIZE = TextTagSize2Height (8.)
    
//Mana bar z offset
    private constant real ZOFFSET = 150
        
//Colour used for current amount of mana
    private constant string BLUESTRING = "|c000201FF"
    
//Color used for empty portion of mana bar
    private constant string BLACKSTRING = "|c00000000"
    
//String for explosion effect
    private constant string EXPLODEFX = "Objects\\Spawnmodels\\NightElf\\NEDeathMedium\\NEDeath.mdl"
    
//Timer interval
    private constant real TIMERINTERVAL = 0.03
    
//Mana limit (base and skill level multiplier)
//No longer used
    private constant real BASEMANAMAX = 500
    private constant real LEVELMULTIPLIER = 250
    
//Percent of mana dealt in damage (from 0.00 to 1.00)
//No longer used
    private constant real MANA2DAMAGEPERCENT = 0.75
    
//Rate of mana storing per second (and mana stored per timer interval)
//No longer used
    private constant real MANASTORE = 75
    private constant real STORERATE = MANASTORE * TIMERINTERVAL
    
//Rate of mana drain from enemies
//No longer used
    private constant real MANADRAIN = 15
    private constant real DRAINRATE = MANADRAIN*TIMERINTERVAL
    
//Area of effect
    private constant real DRAINAOE = 700
//No longer used
    private constant real BLASTAOE = 400
    
//Explode dummy scale value
//175 is -roughly- the size of the Night Elf death explosion.
    private constant real EXPLODERSCALE = BLASTAOE/175.
    
//Scaling value
//End scale size of the dummy will be BASESCALER + MANA2SCALE*a.currentmana
    private constant real BASESCALER = 1.5
    private constant real MANA2SCALE = 0.003
    
//Explosion speed scaler
    private constant real EXPLODESPEED = .75
    
//End damage SFX string
    private constant string DAMAGEFX = "Abilities\\Spells\\Items\\AIvi\\AIviTarget.mdl"
    
    
//Required by the spell. DO NOT TOUCH!
    private unit gcaster
    private real gdamage
    private real gm1
    private real gm2
    private real gdif
    private real gcurrentmana
    private real gmaxmana
    private real gdrain
endglobals


//Formulas for variable stuff
//------------------------------------------------------------------------------------\\
//946609
//Mana stored per second
private function VariableStore takes unit u returns real
    local real base = 5.
    local real level = I2R (GetUnitAbilityLevel (u, SPELLID))
    local real multiplier = 10.
    return base + level*multiplier
endfunction

//Mana drained per second
private function VariableDrain takes unit u returns real
    local real base = 2.5
    local real level = I2R (GetUnitAbilityLevel (u, SPELLID))
    local real multiplier = 5.
    return base + level*multiplier
endfunction

//Variable mana limit
private function VariableMaxMana takes unit u returns real
    local real base = 250.
    local real level = I2R (GetUnitAbilityLevel (u, SPELLID))
    local real multiplier = 250.
    return base + level*multiplier
endfunction

private function VariableMana2Damage takes unit u returns real
    local real base = 0.25
    local real level = I2R (GetUnitAbilityLevel (u, SPELLID))
    local real multiplier = 0.25
    return base + level*multiplier
endfunction

private function VariableAOE takes real cmana, real mmana returns real
    local real percentscale = (cmana + 1)/(mmana + 1)
    local real maxaoe = 400
    return percentscale * maxaoe
endfunction
//END OF GLOBALS. DO NOT EDIT BELOW HERE
//------------------------------------------------------------------------------------\\


//Struct data
//------------------------------------------------------------------------------------\\
private struct Bombdata
    unit caster
    unit bomb
    real maxmana
    real currentmana
    texttag manabar
    group damagegroup
    real damagepercent
    real damage
    real aoe
    trigger stopcast
    timer mbtimer
    group draingroup
    real bombscale
    integer mbstringcount
    real elapsedtime
    real mana2damage
    real drainrate
    real storerate
    private method onDestroy takes nothing returns nothing
        call ClearTriggerStructA (.stopcast)
        call DestroyTrigger (.stopcast)
        call ClearTimerStructA (.mbtimer)
        call DestroyTimer (.mbtimer)
    endmethod
endstruct

//End group conditions and actions
//------------------------------------------------------------------------------------\\
private function FilterCondition takes nothing returns boolean
    return IsUnitEnemy (GetFilterUnit (), GetOwningPlayer (gcaster)) and GetWidgetLife (GetFilterUnit ()) > 0.405
endfunction

private function EndGroupActions takes nothing returns nothing
    local unit u = GetEnumUnit ()
    call UnitDamageTarget (gcaster, u, gdamage, true, true, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
    call DestroyEffect (AddSpecialEffectTarget (DAMAGEFX, u, "origin"))
    set u = null
endfunction

//Drain group actions
//This utilizes the same filter as the end group
//------------------------------------------------------------------------------------\\
private function DrainGroupActions takes nothing returns nothing
    local unit u = GetEnumUnit ()
        if gcurrentmana < gmaxmana then
            set gm1 = GetUnitState (u, UNIT_STATE_MANA)
            call SetUnitState (u, UNIT_STATE_MANA, gm1 - gdrain)
            set gm2 = GetUnitState (u, UNIT_STATE_MANA)
            set gdif = gdif + (gm1 - gm2)
        endif
    set u = null
endfunction

//Stopcast condition and actions
//------------------------------------------------------------------------------------\\
private function StopCondition takes nothing returns boolean
    return GetSpellAbilityId () == SPELLID
endfunction

private function StopActions takes nothing returns nothing
    local Bombdata a = GetTriggerStructA (GetTriggeringTrigger ())
    local real x = GetUnitX (a.bomb)
    local real y = GetUnitY (a.bomb)
    local real aoe = VariableAOE (a.currentmana, a.maxmana)
    set a.damage = a.currentmana * a.mana2damage
    set gcaster = a.caster
    set gdamage = a.damage
    set a.damagegroup = CreateGroup ()
    call GroupEnumUnitsInRange (a.damagegroup, x, y, aoe, Condition (function FilterCondition))
    call ForGroup (a.damagegroup, function EndGroupActions)
    call DestroyTextTag (a.manabar)
    call RemoveUnit (a.bomb)
    call DestroyEffect (AddSpecialEffect (EXPLODEFX, x, y))
    set a.elapsedtime = 0
    set a.currentmana = 0
    set a.maxmana = 0
    call a.destroy ()
endfunction

//Timer actions
//------------------------------------------------------------------------------------\\
private function TimerActions takes nothing returns nothing
    local timer t = GetExpiredTimer ()
    local Bombdata a = GetTimerStructA (t)
    local real x = GetUnitX (a.bomb)
    local real y = GetUnitY (a.bomb)
    local real stringcount = ((a.currentmana + 1)/(a.maxmana + 1)) * I2R (STRINGLENGTH)
    set a.draingroup = CreateGroup ()
    set a.elapsedtime = a.elapsedtime + TIMERINTERVAL
    set gdif = 0
    set gcaster = a.caster
    set gcurrentmana = a.currentmana
    set gmaxmana = a.maxmana
    set gdrain = a.drainrate
    call GroupEnumUnitsInRange (a.draingroup, x, y, DRAINAOE, Condition (function FilterCondition))
    call ForGroup (a.draingroup, function DrainGroupActions)
    call DestroyGroup (a.draingroup)
    set a.currentmana = a.currentmana + gdif
    set a.currentmana = a.currentmana + a.storerate
    set a.mbstringcount = R2I (stringcount)
        if a.currentmana > a.maxmana then
            set a.currentmana = a.maxmana
            set a.mbstringcount = STRINGLENGTH
        endif
    call SetTextTagText (a.manabar, BLUESTRING + SubString (MBSTRING, 1, a.mbstringcount) +"|r" + BLACKSTRING + SubString (MBSTRING, a.mbstringcount, STRINGLENGTH), TEXTSIZE)
    set a.bombscale = BASESCALER + (MANA2SCALE * a.currentmana)
    call SetUnitScale (a.bomb, a.bombscale, a.bombscale, a.bombscale)
    set t = null
endfunction


//Primary trigger condition and actions
//------------------------------------------------------------------------------------\\
private function MBCond takes nothing returns boolean
    return GetSpellAbilityId () == SPELLID
endfunction

private function MBActions takes nothing returns nothing
    local Bombdata a = Bombdata.create ()
    local location loc = GetSpellTargetLoc ()
    local real tx = GetLocationX (loc)
    local real ty = GetLocationY (loc)
    local real level
    set a.caster = GetTriggerUnit ()
    set level = I2R (GetUnitAbilityLevel (a.caster, SPELLID))
    set a.bomb = CreateUnit (GetOwningPlayer (a.caster), DUMMYID, tx, ty, 0)
    call SetUnitPathing (a.bomb, false)
    set a.mbtimer = CreateTimer ()
    set a.stopcast = CreateTrigger ()
    set a.maxmana = VariableMaxMana (a.caster)
    set a.drainrate = VariableDrain (a.caster) * TIMERINTERVAL
    set a.storerate = VariableStore (a.caster) * TIMERINTERVAL
    set a.mana2damage = VariableMana2Damage (a.caster)
    set a.currentmana = 50.
    set a.manabar = CreateTextTag ()
    set a.bombscale = BASESCALER + (MANA2SCALE * a.currentmana)
    set a.mbstringcount = R2I ((a.currentmana + 1)/(a.maxmana + 1) * I2R (STRINGLENGTH))
    call SetTextTagPos (a.manabar, tx, ty, ZOFFSET)
    call SetTextTagText (a.manabar, BLUESTRING + SubString (MBSTRING, 1, a.mbstringcount) +"|r" + BLACKSTRING + SubString (MBSTRING, a.mbstringcount + 1, STRINGLENGTH), TEXTSIZE)
    call SetTextTagVisibility (a.manabar, false)
        if GetLocalPlayer () == GetOwningPlayer (a.caster) then
            call SetTextTagVisibility (a.manabar, true)
        endif
    call RemoveLocation (loc)
    call SetUnitScale (a.bomb, a.bombscale, a.bombscale, a.bombscale)
    set loc = null
    call SetTimerStructA (a.mbtimer, a)
    call TimerStart (a.mbtimer, TIMERINTERVAL, true, function TimerActions)
    call SetTriggerStructA (a.stopcast, a)
    call TriggerRegisterUnitEvent (a.stopcast, a.caster, EVENT_UNIT_SPELL_ENDCAST)
    call TriggerAddCondition (a.stopcast, Condition (function StopCondition))
    call TriggerAddAction (a.stopcast, function StopActions)
endfunction

//Trigger initialization
//------------------------------------------------------------------------------------\\
function InitTrig_ManaBomb takes nothing returns nothing
    local trigger ManaBomb = CreateTrigger ()
    call TriggerRegisterAnyUnitEventBJ (ManaBomb, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition (ManaBomb, Condition (function MBCond))
    call TriggerAddAction (ManaBomb, function MBActions)
endfunction
endscope


Updates
Code:
v1 - First release
v2 - Fixed the Divide by zero error (I think)
v3 - Fixed the slow text tag fill that Purgeanfire reported
v4 - Removed the immediate stop order on 100% charge (was only used for testing)
v5 - Made the text tag visible to the caster's owner only
v6 - Made the struct private. Replaced Wisp Detonate explosion with Night Elf structure death. Added variable formulas for damage, % mana to damage rate, drain/store rates, end explosion scaling size
v7/8/9 - Made the onDestroy method private. Replaced dummy unit effect with normal special effect. Fixed mana bar extending itself.
 

Attachments

  • Mana Bomb submission v9.w3x
    37.4 KB · Views: 268

konerboy

Run piggy Run!
Reaction score
95
very nice, but i do think there should be a way to chancel without detonating incase you change your mind :rolleyes:
 

UndeadDragon

Super Moderator
Reaction score
447
Looks good, sounds like a very useful spell. I like the idea of the Mana absorbed being equal to the damage done.
 

Gwypaas

hook DoNothing MakeGUIUsersCrash
Reaction score
50
I noticed 1 thing when I tested it:
If you spam the spell without draining mana it will try do divide with 0 so add a if so it doesn't try to do that.
 

Flare

Stops copies me!
Reaction score
662
Where does it say the Divide by zero error occurs? The only division in the spellcode is (/a.maxmana), and thats AT LEAST 500 at all times. Did you spam with the same unit? Spam with multiple casters never gave me that error

And when you say draining mana, do you mean draining mana from enemies, or default mana storing?

I suppose I could just add a +1 to the division (even though that would bug the mana bar since I would never get 20 as an integer :S. I guess I could add 0.5 to stringcount or something


EDIT:
Gwypaas, can you please tell me the exact circumstances under which you got the error? I tested with spam casting with the same unit, and no error was received. Each spell instance ended correctly, and new one was created straight after. I tried casting with all heroes at the same time, then rapidly detonating all of them, and no error was received.
 

Larcenist

REP: Respect, Envy, Prosperity?
Reaction score
211
Looks nice and preforms very well. Is this the mana bomb I think it is btw? :O

anyways I guess this is what Gwypaas was talking about:



Occured when I casted it next to them wisps.
 

Gwypaas

hook DoNothing MakeGUIUsersCrash
Reaction score
50
I actully got it again now when I spammed it far away from any units.
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
Nice coding, but there is one problem I occurred. It is kind of major.

The really odd part is that you start to spam the spell which each unit, I did that. Then it exploded etc.. I did this a few times, and you'll gradually notice that it takes longer for the mana bar to fill up. :( It takes forever eventually. :\

It doesn't lag, it is just that the texttag takes forever.
 

GoGo-Boy

You can change this now in User CP
Reaction score
40
Nice spell. Like that floating bars... gotta steal some information of your code o..O
 

Larcenist

REP: Respect, Envy, Prosperity?
Reaction score
211
Cool stuff, and it works perfectly now. Can't +rep you since I do it way too much, so you'll have to live without some more rep even though TH rep is serious business.
 

PurgeandFire

zxcvmkgdfg
Reaction score
509
Actually, that reminds me. Is there a simple way to hide text tags from all players except one? I'd like to hide the bar from enemies so they don't know exactly how much mana is stored (even though the orb's size would be a give-away xD)

JASS:


Freehand... Something like that. Sorry if the functions are misspelled or something.
 

GoGo-Boy

You can change this now in User CP
Reaction score
40
You only need the first one I think. And why don't you use != instead of "not". Dunno if there's a real difference though^^
 
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