• 🏆 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!

Spell Request: Black Magic

Status
Not open for further replies.
Can someone make for me a non-MUI spell(used only by one)
called Black Magic
There is a real variable called chaos realm(starting 0.000).
The spell takes hit points from there to heal an ally unit, replenishing missing hit points so it becomes FULL If they are not enough it will drain a nearby enemy unit to replenish the missing points in order to heal the unit FULL, if they are also not enough then go to another nearby enemy unit to drain it and so on...
However, if there is an excess of hit points they remain in the chaos realm real for later use.
this is a lot of calculating...

Also, if an enemy is drained create and destroy overhead a special effect

Cannot drain heroes.
 
1. target friendly unit
2. instant cast
3. chaos realm stores hit point for unlimited time
4. full means the max hit points of the unit
5. a friendly unit is healed by drawing from hit points from teh chaos realm and if they are not enough to set the unit's hit points to max then drain nearby enemy non-hero unit to fullfill the remaining and if they are also not enough drain another nearby enemy unit and so on... until your target friendly unit gets max hiit points...
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
You make it sound like it's a tangible thing instead of just a real variable. I was just clarifying.

I don't think you realize how much information you need to make a spell. You never specify if its multi-level, what kind of effects you are looking for, what type of units should be affected, etc. It's best you answer the questions anyone who will be using their own time to make your spell.
 
Level 39
Joined
Feb 27, 2007
Messages
5,008
Been messing around with this and I think I have something that works pretty well right now, though it's certainly not perfect. If you still want this spell, check out the map I made and tell me if it functions how you're wanting it to. It's, uhhh, vJASS though because this spell would be stupid convoluted without it.

You will need the JASS NewGen 2.0.9 from here to open this map in the World Editor. The download contains exact instructions on how to use the JNGP, but my test map below does not have info about how to port the spell into your map. I figured I would wait and make documentation if you care about this.

JASS:
library BlackMagic initializer Init requires GroupUtils, xebasic, xefx, xedamage, xemissile

private keyword SpellData

globals
    public  constant integer    SPELL_ID = 'BkMg'
    public  constant integer    BUFF_ID  = 'BBMg'
    public  constant real       PERIOD   = 0.20    //The spell heals/damages every PERIOD seconds
    public  constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC //see xedamage documentation if you want to change this
    private constant real       PI       = 3.14159  //don't touch this one
endglobals

scope SFX
  scope DRAIN
    globals
    public constant string MODEL  = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl" //Need to use double backslash because the World Editor removes 1 \ when it saves... pretty dumb.
    public constant real   LAUNCH_ANGLE   = PI/180*0.00 //launch angle (up from horizontal), we expect radians here so need to convert by multiplying pi/180
    public constant real   LAUNCH_ZOFFSET = 200.00
    public constant real   IMPACT_ZOFFSET = 0.00
    public constant real   SPEED          = 725.00
    endglobals
  endscope

  scope HEAL
    globals
    public constant string MODEL = "Abilities\\Weapons\\GargoyleMissile\\GargoyleMissile.mdl"
    public constant real   LAUNCH_ANGLE   = PI/180*5.00 //launch angle (up from horizontal), we expect radians here so need to convert by multiplying pi/180
    public constant real   LAUNCH_ZOFFSET = 0.00
    public constant real   IMPACT_ZOFFSET = 200.00
    public constant real   SPEED          = 1100.00
    endglobals
  endscope
endscope

private function DrainTargets takes integer LEVEL returns integer
    return 3*LEVEL
endfunction

private function DrainRange takes integer LEVEL returns real
    return 600.00*LEVEL
endfunction

private function DrainPerSecond takes integer LEVEL returns real
    return 50.00*LEVEL
endfunction

private function HealPerSecond takes integer LEVEL returns real
    return 50.00*LEVEL
endfunction

private function ChaosCap takes integer LEVEL returns real
    return 500.00*LEVEL
endfunction

private function ValidTarget takes unit TARGET, unit HOST, player CASTER_OWNER, integer LEVEL returns boolean
    return     IsUnitEnemy(TARGET, CASTER_OWNER)         /*
    */ and     GetWidgetLife(TARGET) > 0.405             /* A unit dies when it has 0.405 health or less
    */ and not IsUnitType(TARGET, UNIT_TYPE_STRUCTURE)   /*
    */ and not IsUnitType(TARGET, UNIT_TYPE_MAGIC_IMMUNE)/*
    */ and not IsUnitType(TARGET, UNIT_TYPE_HERO)
endfunction


private struct HealMissile extends xehomingmissile
    SpellData SD

    private method onHit takes nothing returns nothing
        call .SD.AddChaos(.SD.Heal)
    endmethod
endstruct

private struct DrainMissile extends xehomingmissile
    SpellData SD

    private method onHit takes nothing returns nothing
        local real xTgt = .x
        local real yTgt = .y
        local real zTgt = GetUnitFlyHeight(.targetUnit)
    
        local HealMissile HM = HealMissile.create(xTgt, yTgt, zTgt+SFX_HEAL_LAUNCH_ZOFFSET, .SD.Host, SFX_HEAL_IMPACT_ZOFFSET)
    
        set  HM.SD = .SD
        set  HM.fxpath = SFX_HEAL_MODEL
        call HM.launch(SFX_HEAL_SPEED, SFX_HEAL_LAUNCH_ANGLE)
    
        call SpellData.DamageData.damageTarget(.SD.Dummy, .targetUnit, .SD.Dmg)
    endmethod
endstruct

private struct SpellData
    unit    Host
    unit    Target   = null
    integer Owner
    real    Chaos    = 0.00
    real    ChaosMax = 200.00
    integer Level
    integer index
    unit    Dummy
    real    Dmg
    real    Heal
    real    HealTTL

    static integer        LoopTotal = 0
    static thistype array LoopData
    static timer          LoopTimer = CreateTimer()
    static boolexpr       TargetFilter
    static xedamage       DamageData
    static thistype         TmpSD

    private static unit Uclose
    private static real Dclose
    private static real xHost
    private static real yHost




    static method ForgetUnit takes unit u returns nothing
        local integer i = 0

        loop
            exitwhen i >= LoopTotal
        
            set TmpSD = LoopData[i]
            if TmpSD.Host == u then
                call TmpSD.destroy()
                exitwhen true
            endif
        endloop
    endmethod
  
    method AddChaos takes real Amt returns nothing
        set .Chaos = Chaos+Amt
        if .Chaos > .ChaosMax then
            set .Chaos = .ChaosMax
        endif
    endmethod
  
    method HealHost takes nothing returns nothing
        local real HP   = GetWidgetLife(.Host)
        local real MxHP = GetUnitState(.Host, UNIT_STATE_MAX_LIFE)
        local real Amt  = .Heal
      
        if .Chaos > 0.00 then
            if Amt > .Chaos then
                set Amt = .Chaos
            endif

            if HP+Amt > MxHP then
                set Amt = MxHP-HP
            endif
                      
            call SetWidgetLife(.Host, HP+Amt)
            set .Chaos = .Chaos-Amt
        endif
    endmethod
  
    static method ValidTargetFilterFunc takes nothing returns boolean
        local unit u = GetFilterUnit()
        local real x
        local real y
        local real d2
        local boolean b = ValidTarget(u, TmpSD.Host, Player(TmpSD.Owner), TmpSD.Level)
        if b then
            set x  = GetUnitX(u)
            set y  = GetUnitY(u)
            set d2 = (x-xHost)*(x-xHost)+(y-yHost)*(y-yHost)
          
            if (d2 < Dclose) or (Uclose == null) then
                set Uclose = u
                set Dclose = d2
            endif
        endif
      
        set u = null
        return false
    endmethod
  
    static method Periodic takes nothing returns nothing
        local integer i = 0
        local DrainMissile DM
  
        loop
            exitwhen i >= LoopTotal  
          
            set TmpSD = LoopData[i]          
            if (TmpSD.Host == null) or (GetWidgetLife(TmpSD.Host) <= 0.405) or (GetUnitAbilityLevel(TmpSD.Host, BUFF_ID) == 0) then
                call TmpSD.destroy()
              
                if i == LoopTotal then
                    set i = i+1
                endif
            else
                set Uclose = null
                set Dclose = 0.00
                set xHost  = GetUnitX(TmpSD.Host)
                set yHost  = GetUnitY(TmpSD.Host)
              
                call GroupEnumUnitsInArea(ENUM_GROUP, xHost, yHost, DrainRange(TmpSD.Level), TargetFilter)

                if Uclose != null then
                    set  DM = DrainMissile.create(xHost, yHost, GetUnitFlyHeight(TmpSD.Host)+SFX_DRAIN_LAUNCH_ZOFFSET, Uclose, SFX_DRAIN_IMPACT_ZOFFSET)
                    set  DM.SD  = TmpSD
                    set  DM.fxpath = SFX_DRAIN_MODEL
                    call DM.launch(SFX_DRAIN_SPEED, SFX_DRAIN_LAUNCH_ANGLE)
                endif
              
                call TmpSD.HealHost()
                call ClearTextMessages()
                call BJDebugMsg("Chaos: "+R2S(TmpSD.Chaos)+"/"+R2S(TmpSD.ChaosMax))
              
                set i = i+1
            endif

        endloop
      
    endmethod
  
    static method create takes unit H, player P, integer Level returns thistype
        local thistype this = allocate()
      
        set this.Host  = H
        set this.Owner = GetPlayerId(P)
        set this.Level = Level
        set this.index = LoopTotal
        set this.Dummy = XE_NewDummyUnit(P, 0.00, 0.00, 0.00)
        set this.Dmg   = DrainPerSecond(Level)*PERIOD
        set this.Heal  =  HealPerSecond(Level)*PERIOD
        set this.ChaosMax = ChaosCap(Level)
      
        set LoopData[LoopTotal] = this
        set LoopTotal = LoopTotal + 1
      
        if LoopTotal == 1 then
            call TimerStart(LoopTimer, PERIOD, true, function thistype.Periodic)
        endif
      
        return this
    endmethod
  
    method onDestroy takes nothing returns nothing
        call XE_ReleaseDummyUnit(.Dummy)
        set .Dummy  = null
        set .Host   = null
        set .Target = null
              
        set LoopTotal = LoopTotal-1
        set LoopData[index] = LoopData[LoopTotal]
        set LoopData[index].index = .index
      
        if LoopTotal == 0 then
            call PauseTimer(LoopTimer)
        endif
    endmethod
  
    static method onInit takes nothing returns nothing
        set  TargetFilter = Filter(function thistype.ValidTargetFilterFunc)
      
        set  DamageData = xedamage.create()
        set  DamageData.dtype = DAMAGE_TYPE_MAGIC
    endmethod
endstruct

function OnCast takes nothing returns nothing
    local unit c    = GetTriggerUnit()
    local unit h    = GetSpellTargetUnit()
    local player  p = GetOwningPlayer(c)
    local integer l = GetUnitAbilityLevel(c, SPELL_ID)
      
    if GetUnitAbilityLevel(h, BUFF_ID) > 0 then
        call SpellData.ForgetUnit(h)
    endif
    call SpellData.create(h, p, l)
  
    set c = null
    set h = null
    set p = null
endfunction

function CorrectSpell takes nothing returns boolean
    return GetSpellAbilityId() == SPELL_ID
endfunction

function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function CorrectSpell))
    call TriggerAddAction(t, function OnCast)
  
    debug call BJDebugMsg("Debug mode enabled!")
endfunction

endlibrary
 

Attachments

  • Test Map - Black Magic.w3x
    183.1 KB · Views: 35
Level 39
Joined
Feb 27, 2007
Messages
5,008
Oh also the DrainTargets function does nothing at the moment. I added it because I was gonna make it target multiple units but I didn't actually write any code to do that. You can safely delete that function because nothing calls it.
 
Status
Not open for further replies.
Top