Spell PowerBlitz

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
Power Blitz

BTNSacrificialSkull.gif


Created by Darthfett, Concept by ReVolver
As seen in Undead Outbreak by ReVolver

Death Spell
AoE

Screenshot:
powerblitzsmallmy4.jpg


Description:
While channeled, the caster will draw the souls of all nearby corpses to him. Then, once their energy has been channeled to him, he releases them upon his enemies in explosive fury.
Deals 10*(Number of Corpses) to all enemy units in the targeted area. Steals the souls of all units in a 300, 400, 500 etc range, and damages all units in a 300, 400, 500, etc range of the target point.

-MPI
-MUI
-Leak-less
-Lag-less

-Created in vJASS


Requires:
PUI
KT2
Dummy unit - with model "Abilities\Weapons\ZigguratMissile\ZigguratMissile.mdl" (Included in Demo Map)
AoE or Point Targeting ability (Included in Demo Map)

JASS:
scope PowerBlitz initializer Init

//==================================================||
//                                                  ||
//           Spell Created by Darthfett             ||
//               Concept by ReVolver                ||
//                                                  ||
//                 Uses PUI and KT2                 ||
//                                                  ||
//             Easy to end spell, use:              ||
//                                                  ||
//    call PowerBlitz_Data[UNIT].release()          ||
//      - to finish a spell abruptly                ||
//                                                  ||
//==================================================||
//You can take out the above if you like. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" />

globals
    private constant integer AID_PowerBlitz         = &#039;A001&#039;
    //The ability which starts the spell
    
    private constant string CHANNEL                 = &quot;shockwave&quot;
    //The order string, so that it can stop the corpses if the hero stops channeling.
    
    private constant integer MAX_CORPSES            = 10
    //The maximum number of corpses that can have their souls stolen.
    
    private constant real TIMEOUT                   = 0.03125 
    //The timer timeout.  (FPS is ~32)
    
    private constant integer UID_DUMMY              = &#039;visi&#039; 
    //The &quot;souls/ghosts&quot; that are being slid as dummy units
    
    private constant real OFFSET                    = 10
    //How far to slide each tick
    
    private constant integer GHOST_TICK_COUNT_DELAY = 10
    //Number of timer ticks in between each corpse spirit spaw
    
    private constant sound SND_Start                = CreateSound(&quot;Abilities\\Spells\\Demon\\SoulPreservation\\SoulPreservation.wav&quot;,false,true,false,10,10,&quot;&quot;)
    //Sound that plays during at start.  Change the string to whatever you need it to be.
    
    private constant sound SND_Damage               = CreateSound(&quot;Abilities\\Spells\\Other\\Drain\\LifeDrain.wav&quot;,false,true,false,10,10,&quot;&quot;)
    //Sound that plays when damage starts.  Change the string to whatever you need it to be.

    private constant attacktype ATK_TYPE            = ATTACK_TYPE_CHAOS
    //The attack type the unit uses to damage enemies
    
    private constant weapontype WPN_TYPE            = WEAPON_TYPE_WHOKNOWS
    //The weapon type the unit uses to damage enemies
    
    private constant damagetype DMG_TYPE            = DAMAGE_TYPE_UNIVERSAL
    //The damage type the unit uses to damage enemies
endglobals

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_PowerBlitz
endfunction

private constant function DamageAmount takes real lvl, real count returns real
    return 10 * count //Count is the number of corpses.  
    //This is the damage to all units in the area over all the damage time.
endfunction

private constant function CorpseRange takes integer lvl returns integer
    return 200 + (100 * lvl) //Range from which corpses can be picked.
endfunction

private constant function DamageRange takes integer lvl returns integer
    return 200 + (100 * lvl) //Range from which / damage is dealt / units slide to from target point
endfunction

//===================DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING====================\\

globals
    private player tempown
    private integer gbl_groupCount
    private group tempgrp = CreateGroup()
endglobals

//What happens:

//The Caster picks a corpse every GHOST_TICK_COUNT_DELAY * TIMEOUT seconds and starts sliding it toward the caster.
//One there are either:
//   1. MAX_CORPSES corpses slid to the caster
//       OR
//   2. No Corpses left in range
//It will wait 2 seconds and then the dummies will start sliding outward from the targetted point.
//The DAMAGE_PER_CORPSES is dealt over the time that the units slide.

//Features:
//    Corpses will stop being picked when unit ends spell or dies, but spell will continue.
//    Spirits from corpses are created at intervals, and slid to the caster
//    Leakless, Lagless, MUI, MPI, etc
//    Sound! <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite7" alt=":p" title="Stick Out Tongue    :p" loading="lazy" data-shortname=":p" />

private function Remove takes nothing returns nothing
    call KillUnit(GetEnumUnit())
endfunction

private function FiltFunc takes nothing returns boolean
    if GetWidgetLife(GetFilterUnit()) &lt; .405 and IsUnitType(GetFilterUnit(),UNIT_TYPE_MECHANICAL) == false then
        set bj_groupCountUnits = bj_groupCountUnits + 1
        return true
    endif
    return false
endfunction

private function FiltFuncDamage takes nothing returns boolean
    return GetWidgetLife(GetFilterUnit()) &gt;= .405 and IsUnitType(GetFilterUnit(),UNIT_TYPE_MECHANICAL) == false and IsUnitEnemy(GetFilterUnit(),tempown)
endfunction

private function GroupCountEnum takes nothing returns nothing
    set gbl_groupCount = gbl_groupCount + 1
endfunction

private function GroupCount takes group g returns integer //Non-BJ version of CountUnitsInGroup.
    set gbl_groupCount = 0
    call ForGroup(g,function GroupCountEnum)
    return gbl_groupCount
endfunction

public struct Data
    //! runtextmacro PUI()
    unit c
    player p
    group sliders
    group slid2
    integer i
    real cx
    real cy
    real tx
    real ty
    real angle
    integer lvl
    integer ticks
    integer units
    
    static method create takes nothing returns Data
        local Data d = Data.allocate()
        set d.ticks = 0
        set d.i = 0
        set d.units = 0
        set d.sliders = CreateGroup()
        set d.slid2 = CreateGroup()
        return d
    endmethod
        
    method onDestroy takes nothing returns nothing
//All the actual cleanup is done in .release/.destroy methods, 
//so that it is possible to stop a spell whenever.
//This makes the spell very flexible
        call ForGroup(this.slid2,function Remove)
        call DestroyGroup(this.sliders)
        call DestroyGroup(this.slid2)
        set this.sliders = null
        set this.slid2 = null
        set this.c = null
    endmethod
endstruct


private function Callback_Damage takes nothing returns boolean
    local Data d = KT_GetData()
    local unit fog
    local real x
    local real y
    local real a
    local real newx
    local real newy
    local integer i = 0
    set tempown = d.p
    call GroupEnumUnitsInRange(tempgrp,d.tx,d.ty,DamageRange(d.lvl),Filter(function FiltFuncDamage))
    
    set bj_groupCountUnits = 0
    call ForGroup(d.slid2,function CountUnitsInGroupEnum)
    
    loop //Damage targets
        set fog = FirstOfGroup(tempgrp)
        exitwhen fog == null
        call GroupRemoveUnit(tempgrp,fog)
        call UnitDamageTarget(d.c,fog,(DamageAmount(d.lvl,bj_groupCountUnits) / (DamageRange(d.lvl) / OFFSET)) ,false,false,ATK_TYPE,DMG_TYPE,WPN_TYPE)
    endloop
    if d.ticks * OFFSET &lt; DamageRange(d.lvl) then
        set bj_groupAddGroupDest = tempgrp
        call ForGroup(d.slid2,function GroupAddGroupEnum)
        loop
            set fog = FirstOfGroup(tempgrp)
            exitwhen fog == null
            call GroupRemoveUnit(tempgrp,fog)
            set a = bj_DEGTORAD * ((360 / bj_groupCountUnits) * i)
            set newx = d.tx + (OFFSET * d.ticks ) * Cos(a)
            set newy = d.ty + (OFFSET * d.ticks ) * Sin(a)
            call SetUnitX(fog,newx)
            call SetUnitY(fog,newy)
            set i = i + 1
        endloop
    else
        call d.release()
        return true
    endif
    set d.ticks = d.ticks + 1
    return false
endfunction

private function DamageWait takes nothing returns boolean
    local Data d = KT_GetData()
    call AttachSoundToUnit(SND_Damage,d.c)
    call SetSoundVolume(SND_Damage,127)
    call StartSound(SND_Damage)
    call KT_Add(function Callback_Damage,d,TIMEOUT)
    return true
endfunction

private function Slide2Caster_Callback takes nothing returns boolean
    local Data d = KT_GetData()
    local unit fog
    local unit newdummy
    local real x
    local real y
    local real a
    local real newx
    local real newy
    set bj_groupCountUnits = 0
    call GroupEnumUnitsInRange(tempgrp,d.cx,d.cy,CorpseRange(d.lvl),Filter(function FiltFunc))
    
    if GetWidgetLife(d.c) &lt; .405 or GetUnitCurrentOrder(d.c) != OrderId(CHANNEL) then
        set bj_groupCountUnits = 0
    endif
    
    set d.ticks = d.ticks + 1
    if d.units * GHOST_TICK_COUNT_DELAY &lt;= d.ticks and bj_groupCountUnits &gt; 0 and d.units &lt; MAX_CORPSES then //Every GHOST_TICK_COUNT_DELAY ticks it will start a new slider
        set fog = FirstOfGroup(tempgrp)
        set x = GetUnitX(fog)
        set y = GetUnitY(fog)
        set a = bj_RADTODEG * Atan2((d.cy - y), (d.cx - x)) //Angle between dummy and caster
        set newdummy = CreateUnit(d.p,UID_DUMMY,x,y,a)
        call GroupAddUnit(d.sliders,newdummy)
        call SetUnitPathing(newdummy,false)
        call RemoveUnit(fog)
        set d.units = d.units + 1
    endif
    set bj_groupAddGroupDest = tempgrp
    call ForGroup(d.sliders,function GroupAddGroupEnum)
    loop
        set fog = FirstOfGroup(tempgrp)
        exitwhen fog == null
        call GroupRemoveUnit(tempgrp,fog)
        set x = GetUnitX(fog)
        set y = GetUnitY(fog)
        set a = Atan2((d.cy - y), (d.cx - x))
        set newx = x + OFFSET * Cos(a)
        set newy = y + OFFSET * Sin(a)
        call SetUnitX(fog,newx)
        call SetUnitY(fog,newy)
        if SquareRoot((newx - d.cx)*(newx - d.cx) + (newy - d.cy)*(newy - d.cy)) &lt;= 15 then //Distance Formula
            call GroupRemoveUnit(d.sliders,fog)
            call GroupAddUnit(d.slid2,fog)
        endif
    endloop
    if (d.units &gt;= MAX_CORPSES or bj_groupCountUnits == 0) and GroupCount(d.sliders) == 0 then
        call KT_Add(function DamageWait,d,2)
        set d.ticks = 0
        return true
    endif
    set newdummy = null
    return false
endfunction

private function Actions takes nothing returns nothing
    local Data d = Data[GetTriggerUnit()]
    local location temp = GetSpellTargetLoc()
    if d == 0 then
        set d = Data.create()
        set Data[GetTriggerUnit()] = d
    endif
    
    //Set data
    set d.c = GetTriggerUnit()
    set d.p = GetOwningPlayer(d.c)
    set d.cx = GetUnitX(d.c)
    set d.cy = GetUnitY(d.c)
    set d.tx = GetLocationX(temp)
    set d.ty = GetLocationY(temp)
    call RemoveLocation(temp)
    set temp = null
    set d.lvl = GetUnitAbilityLevel(d.c,AID_PowerBlitz)
    call AttachSoundToUnit(SND_Start,d.c)
    call SetSoundVolume(SND_Start,127)
    call StartSound(SND_Start)
    
    call KT_Add(function Slide2Caster_Callback,d,TIMEOUT)
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t,Condition(function Conditions))
    call TriggerAddAction(t,function Actions)
endfunction

endscope


Attached map has the spell, dummy unit, and ability.

===========
The screenshot is not really all that great. You have to see it in action to actually see the spell.
 

Attachments

  • PowerBlitz.w3x
    56.6 KB · Views: 423

Flare

Stops copies me!
Reaction score
662

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
JASS:
    call KillUnit(GetEnumUnit())
    call UnitApplyTimedLife(GetEnumUnit(),&#039;BTLF&#039;,1)

:confused: Ahm... I fail to see the point in that - care to enlighten me? :p

Aha, sorry. :eek: Fix'd. I think I was originally thinking to play the death animation and then the unit would be removed after one second.


Configurable attack/damage/weapon types would be nice (someone may not want to do Chaos damage)

Added them in. I originally made the spell to make it easy for ReVolver to customize, so I left a few things out. ;)

EDIT:

Updated the post with a better screenshot.
 

Romek

Super Moderator
Reaction score
964
JASS:
private function GroupCountEnum takes nothing returns nothing
    set gbl_groupCount = gbl_groupCount + 1
endfunction

private function GroupCount takes group g returns integer //Non-BJ version of CountUnitsInGroup.
    set gbl_groupCount = 0
    call ForGroup(g,function GroupCountEnum)
    return gbl_groupCount
endfunction

There really is no point in recreating the BJ like that. You might as well use the BJ and have the same effect, but less code. :D

Also, maybe you could make 'valid' a global group.
It's only used for an instance, and this could make the code slightly more efficient.

Just set it to CreateGroup() once, and the use GroupClear(). And never destroy it :D
 

Tom_Kazansky

--- wraith it ! ---
Reaction score
157
cool ! +rep :D
If I use this spell in my map, I should credit for Darthfett or ReVolver ? or both ( I think both)

a little off topic: Is a "Grave Keeper" hero suitable for this spell ? :)
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
JASS:
private function GroupCountEnum takes nothing returns nothing
    set gbl_groupCount = gbl_groupCount + 1
endfunction

private function GroupCount takes group g returns integer //Non-BJ version of CountUnitsInGroup.
    set gbl_groupCount = 0
    call ForGroup(g,function GroupCountEnum)
    return gbl_groupCount
endfunction

There really is no point in recreating the BJ like that. You might as well use the BJ and have the same effect, but less code. :D

The BJ has a little bit of fluff:

JASS:
function CountUnitsInGroup takes group g returns integer
    // If the user wants the group destroyed, remember that fact and clear
    // the flag, in case it is used again in the callback.
    local boolean wantDestroy = bj_wantDestroyGroup
    set bj_wantDestroyGroup = false

    set bj_groupCountUnits = 0
    call ForGroup(g, function CountUnitsInGroupEnum)

    // If the user wants the group destroyed, do so now.
    if (wantDestroy) then
        call DestroyGroup(g)
    endif
    return bj_groupCountUnits
endfunction


No need to create the local boolean, no need to check if the group needs to be destroyed.

I did use the original in another section, just so I could inline a function.

Also, maybe you could make 'valid' a global group.
It's only used for an instance, and this could make the code slightly more efficient.

Just set it to CreateGroup() once, and the use GroupClear(). And never destroy it :D

Good idea. I'll update it when I get a chance.

cool ! +rep :D
If I use this spell in my map, I should credit for Darthfett or ReVolver ? or both ( I think both)

a little off topic: Is a "Grave Keeper" hero suitable for this spell ? :)

Well he asked me not to credit him, but I decided to give him credit for the concept anyways. I basically coded what he asked me to. I say credit us both, but he might say just to credit me. Credit us both. :cool:

Grave Keeper sounds like the theme matches (Death, souls, corpses).
 

Romek

Super Moderator
Reaction score
964
There's no need to do this: (In 2 places)
JASS:
call GroupClear(tempgrp)


The group is being cleared every time it's used anyway (FOG Loops).
 

Romek

Super Moderator
Reaction score
964
I don't see any other problems from a quick glance.
Approved

A well made, original spell. :thup:
 
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

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top