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: 271

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.
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1
  • The Helper The Helper:
    New recipe is another summer dessert Berry and Peach Cheesecake - https://www.thehelper.net/threads/recipe-berry-and-peach-cheesecake.194169/

      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