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

[JASS] Back and need help

Status
Not open for further replies.
Level 8
Joined
Jul 28, 2008
Messages
211
Well as the title suggests, I'm back and I need your help yet again. I decided to start making spells and, as usuall, I got stuck. I've beed looking at the code for about 15 minutes now and I fixed a few things, but it still won't work as it should. Here's the code:

JASS:
//Made by Destroyer95

scope Spell initializer Initt

globals

    //***********************************************\\
    //********************SPELL**********************\\
    //***********************************************\\
    private timer T = CreateTimer()
    private group G
    private constant integer DUMMY_ID = 'h000'
    private constant integer SPELL_ID = 'A000'
    private constant real DURRATION = 16.00
    private constant real PERIOD = 0.04
    private constant real RADIUS = 400
    private constant string SFX = "Abilities\\Spells\\Orc\\Bloodlust\\BloodlustTarget.mdl"
    private constant string ATTACH = "overhead"
    private constant string EXPLODE = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
    private constant string EATTACH = "overhead"
    //***********************************************\\
    
    //***********************************************\\
    //*********************BURN**********************\\
    //***********************************************\\
    private timer BT = CreateTimer()
    private group BG
    private constant real BDURRATION = 6.00
    private constant real BPERIOD = 0.25
    private constant real BDMG_MIN = 3.00
    private constant real BDMG_MAX = 6.00
    private constant real BEXPLODE_DMG = 20.00
    private constant real BRADIUS = 200
    private constant string BSFX = "Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl"
    private constant string BATTACH = "origin"
    private constant string BEXPLODE_SFX = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
    //***********************************************\\

    private boolexpr bool
    
endglobals

private function Filt takes nothing returns boolean
    return (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE ) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE ) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL ) == false) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction

private struct burn

    unit caster
    unit target
    effect eff
    real durr

    static integer Total = 0
    static burn array arr
    
    static method Loop takes nothing returns nothing
        local integer i = 0
        local burn b
        local unit temp
        loop
            exitwhen i >= burn.Total
            set b = burn.arr[i]
            if b.durr >= 0 and GetWidgetLife(b.target) > 0.405 then
                call UnitDamageTarget(b.caster, b.target, GetRandomReal(BDMG_MIN, BDMG_MAX), false, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
                if b.durr == BDURRATION / 2 then
                    call GroupEnumUnitsInRange(BG, GetUnitX(b.target), GetUnitY(b.target), BRADIUS, bool)
                    call DestroyEffect(AddSpecialEffect(BEXPLODE_SFX, GetUnitX(b.target), GetUnitY(b.target)))
                    loop
                        set temp = FirstOfGroup(BG)
                        exitwhen temp == null
                        if IsUnitEnemy(temp, GetOwningPlayer(b.caster)) then
                            call UnitDamageTarget(b.caster, temp, BEXPLODE_DMG, false, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
                        endif
                        call GroupRemoveUnit(BG, temp)
                    endloop
                endif
                set b.durr = b.durr - BPERIOD
            else
                call GroupEnumUnitsInRange(BG, GetUnitX(b.target), GetUnitY(b.target), BRADIUS, bool)
                call DestroyEffect(AddSpecialEffect(BEXPLODE_SFX, GetUnitX(b.target), GetUnitY(b.target)))
                loop
                    set temp = FirstOfGroup(BG)
                    exitwhen temp == null
                    if IsUnitEnemy(temp, GetOwningPlayer(b.caster)) then
                        call UnitDamageTarget(b.caster, temp, BEXPLODE_DMG, false, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
                    endif
                    call GroupRemoveUnit(BG, temp)
                endloop
                call DestroyEffect(b.eff)
                set b.eff = null
                set b.caster = null
                set b.target = null
                call b.destroy()
                set burn.Total = burn.Total - 1
                set burn.arr[i] = burn.arr[burn.Total]
                set i = i - 1
            endif
            set i = i + 1
        endloop
        if burn.Total == 0 then
            call PauseTimer(BT)
        endif
    endmethod
    
    static method Start takes unit caster, unit target returns nothing
        local burn b = burn.allocate()
        set b.target = target
        set b.caster = caster
        set b.eff = AddSpecialEffectTarget(BSFX, b.target, BATTACH)
        if burn.Total == 0 then
            call TimerStart(BT, BPERIOD, true, function burn.Loop)
        endif
        set burn.arr[burn.Total] = b
        set burn.Total = burn.Total + 1
    endmethod
    
endstruct

private struct data

    unit target
    unit caster
    unit dummy
    real durr = DURRATION
    effect eff
    
    static integer Total = 0
    static data array arr
    
    static method onLoop takes nothing returns nothing
        local integer i = 0
        local data d
        local real x
        local real y
        local unit temp
        loop
            exitwhen i >= data.Total
            set d = data.arr[i]
            if d.durr >= 0 and GetWidgetLife(d.target) > 0.405 then
                set x = GetUnitX(d.target)
                set y = GetUnitY(d.target)
                call SetUnitX(d.dummy, x)
                call SetUnitY(d.dummy, y)
                if d.durr == DURRATION / 4 or d.durr == DURRATION * 3 / 4 then
                    call DestroyEffect(AddSpecialEffectTarget(EXPLODE, d.target, EATTACH))
                    call GroupEnumUnitsInRange(G, x, y, RADIUS, bool)
                    loop
                        set temp = FirstOfGroup(G)
                        exitwhen temp == null
                        if IsUnitEnemy(temp, GetOwningPlayer(d.caster)) then
                            call burn.Start(d.caster, d.target)
                        endif
                        call GroupRemoveUnit(G, temp)
                    endloop
                endif
                set d.durr = d.durr - PERIOD
            else
                call DestroyEffect(d.eff)
                call RemoveUnit(d.dummy)
                set d.dummy = null
                set d.target = null
                set d.caster = null
                set d.eff = null
                call d.destroy()
                set data.Total = data.Total - 1
                set data.arr[i] = data.arr[data.Total]
                set i = i - 1
            endif 
            set i = i + 1
        endloop
        if data.Total == 0 then
            call PauseTimer(T)
        endif
    endmethod
    
    static method Start takes unit cast, unit targ returns nothing
        local data d = data.allocate()
        set d.target = targ
        set d.caster = cast
        set d.dummy = CreateUnit(GetOwningPlayer(cast), DUMMY_ID, GetUnitX(targ), GetUnitY(targ), 0.00)
        set d.eff = AddSpecialEffectTarget(SFX, targ, ATTACH)
        if data.Total == 0 then
            call TimerStart(T, PERIOD, true, function data.onLoop)
        endif
        set data.arr[data.Total] = d
        set data.Total = data.Total + 1
    endmethod

endstruct

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

private function Act takes nothing returns nothing
    call data.Start(GetTriggerUnit(), GetSpellTargetUnit())
endfunction

private function Initt takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function Cond))
    call TriggerAddAction(t, function Act)
    
    set G = CreateGroup()
    set BG = CreateGroup()
    set bool = Condition(function Filt)
endfunction

endscope

//Made by Destroyer95

Now, for the spell. It should create a dummy at a unit (works). The dummy makes explosions every on 1/4 and 3/4 of its durration (works). Units in the radius of the explosion start to burn, taking damage over time (doesn't work), emmiting an explosion of their own at 1/2 of the burn durration (works). I don't understand why won't they burn. It's like the burn effect doesn't appear and they don't take damage over time, but they still explode. Oh and I think that it deals more damage than it should (just isn't logical that 3 or 4 explosions of 20 damage get a 300 hp unit to red) so maybe the burning damage happens all at once.

If you find anything let me know, please. Thanks in advance.
 
Level 9
Joined
Dec 12, 2007
Messages
489
this is inside struct data, on method onLoop
JASS:
                    call GroupEnumUnitsInRange(G, x, y, RADIUS, bool)
                    loop
                        set temp = FirstOfGroup(G)
                        exitwhen temp == null
                        if IsUnitEnemy(temp, GetOwningPlayer(d.caster)) then
                            call burn.Start(d.caster, d.target)
                        endif
                        call GroupRemoveUnit(G, temp)
                    endloop
maybe that should be
JASS:
call burn.start(d.caster,temp)
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,468
Some things to point out:

You never initialized the burn instance's duration, that will screw things up.

Using filters instead of an embedded "loop enumeration" is much faster, it just requires you to create a couple global variables, no biggie.

You only need one group as you are never adding units to it, and you can initialize groups in the globals block, you don't have to put them in the initializer function.

Nulling struct members at the end of a struct just for the sake of "clearing handles" is pointless as there is no way an array with the size of 8190 is ever going to cause a handle issue, especially if that array will likely never go higher than a few dozen like your spell.

You're going to have a hard time reading this because I'm switching from vJass to Zinc, but I've made a lot of optimizations, it's tested and as near as I can tell flawless:

JASS:
native UnitAlive takes unit id returns boolean
/*
 *  I'm replacing those slow GetWidgetLife calls with the awesome
 *  UnitAlive native from common.ai; it is efficient, less of an
 *  eyesore and less flawed.
 *
 */
//! zinc
library BurnSpell /* Made by Destroyer95 */
{
    
    constant integer DUMMY_ID = 'h000'  ;
    constant integer SPELL_ID = 'A000'  ;
    
    
    group  Filt_G = CreateGroup();  /* You only need 1 group. */
    unit   Filt_T;
    unit   Filt_C;
    player Filt_P;  /* Player and unit are set before calling "Check" */
    
    
    function Check()-> boolean
    { 
        Filt_T=GetFilterUnit();
        return
        (
            IsUnitEnemy(Filt_T,Filt_P)
            && UnitAlive(Filt_T)
            &&! IsUnitType(Filt_T,UNIT_TYPE_STRUCTURE)
            &&! IsUnitType(Filt_T,UNIT_TYPE_MECHANICAL)
            &&! IsUnitType(Filt_T,UNIT_TYPE_MAGIC_IMMUNE)
        );
    }
    
    
    struct burn
    {
/*  >>  Configuration
        ¯¯¯¯¯¯¯¯¯¯¯¯¯
*/      static constant real
            DURATION = 6.0, PERIOD = 0.25, RADIUS = 200.,
            DMG_MIN  = 3.0, DMG_MAX = 6.0, EXPLODE_DMG = 20.;
            
        static constant string
            SFX = "Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl",
            ATTACH = "origin",
            EXPLODE_SFX = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl";
        
        
//*********************************************************************************************************
//*     Static Components
//*     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        static timer T = CreateTimer();
        static thistype Total = 0, arr[];
        
        
//*     Dynamic Components
//*     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        real dur = DURATION;
        unit caster, target;
        effect attach;
        
        
//      "Explosion" Effect
//      ===================================================================================================
        method ExplodingEffect()
        {
            real x=GetWidgetX(target), y=GetWidgetY(target);
            
            Filt_C = caster; Filt_P = GetOwningPlayer(caster);
            GroupEnumUnitsInRange(Filt_G, x,y, RADIUS, Filter(static method()-> boolean
            {
                if (Check()) UnitDamageTarget(Filt_C,Filt_T, EXPLODE_DMG, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
                return false;
            }));
            DestroyEffect(AddSpecialEffect(EXPLODE_SFX, x,y));
        }
        
//      Looping Behavior
//      ===================================================================================================
        static method onLoop()
        {
            unit temp;
            thistype i=0, b;
            while (i < Total)
            {
                b = arr[i];
                if (b.dur >= 0 && UnitAlive(b.target))
                {
                    UnitDamageTarget(b.caster, b.target, GetRandomReal(DMG_MIN, DMG_MAX), true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
                    if (b.dur == DURATION / 2) b.ExplodingEffect();
                    i+=1; b.dur = b.dur - PERIOD;
                }
                else
                {
                    b.ExplodingEffect(); DestroyEffect(b.attach); 
                    Total-=1; arr[i] = arr[Total]; if (Total == 0) PauseTimer(T);
                    b.deallocate();
                }
            }
        }
        
//      Instanciation
//      ===================================================================================================
        static method Start()
        {
            thistype b = allocate();
            
            b.caster = Filt_C; b.target = Filt_T;
            b.attach = AddSpecialEffectTarget(SFX, Filt_T, ATTACH);
            
            arr[Total] = b; Total+=1; if (Total == 1) TimerStart(T, PERIOD, true,static method thistype.onLoop);
        }
    }
 
 
//*********************************************************************************************************
 
 
    struct Data
    {
/*  >>  Configuration 
        ¯¯¯¯¯¯¯¯¯¯¯¯¯
*/      static constant real
            DURATION = 16.  ,
            PERIOD   = 0.04 ,
            RADIUS   = 400. ;
            
        static constant string
            SFX = "Abilities\\Spells\\Orc\\Bloodlust\\BloodlustTarget.mdl",
            ATTACH = "overhead",
            EXPLODE = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl",
            EATTACH = "overhead";
            
            
//*********************************************************************************************************
//*     Static Components
//*     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        static timer T = CreateTimer();
        static thistype Total = 0, arr[];
//*     Dynamic Components
//*     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
        real dur = DURATION;
        unit target, caster, dummy;
        effect skin;
        
    
//      Looping Behavior
//      ===================================================================================================
        static method onLoop()
        {
            real x, y;
            thistype i=0, d;
            
            while (i < Total)
            {
                d = arr[i];
                
                if (d.dur >= 0 && UnitAlive(d.target))
                {
                    if (d.dur == DURATION / 4 || d.dur == DURATION * 3 / 4)
                    {
                        x = GetWidgetX(d.target); SetUnitX(d.dummy, x);
                        y = GetWidgetY(d.target); SetUnitY(d.dummy, y);
                        Filt_C = d.caster; Filt_P = GetOwningPlayer(d.caster);
                        
                        GroupEnumUnitsInRange(Filt_G, x, y, RADIUS, Filter(static method()-> boolean
                        {
                            if (Check()) burn.Start();
                            return false;
                        }));
                        DestroyEffect(AddSpecialEffectTarget(EXPLODE, d.target, EATTACH));
                    }
                    i+=1; d.dur = d.dur - PERIOD;
                }
                else
                {
                    DestroyEffect(d.skin); RemoveUnit(d.dummy);
                    Total-=1; arr[i] = arr[Total]; if (Total == 0) PauseTimer(T);
                    d.deallocate();
                }
            }
        }
        
//      Instanciation
//      ===================================================================================================
        static method Start()-> boolean
        {
            thistype d;
            if (GetSpellAbilityId() == SPELL_ID)
            {
                d = allocate();
                d.caster = GetTriggerUnit(); d.target = GetSpellTargetUnit();
                
                d.dummy = CreateUnit(GetTriggerPlayer(), DUMMY_ID, GetWidgetX(d.target), GetWidgetY(d.target), 0.);
                d.skin = AddSpecialEffectTarget(SFX, d.target, ATTACH);
                
                arr[Total] = d; Total+=1; if (Total == 1) TimerStart(T, PERIOD, true,static method thistype.onLoop);
            }
            return false;
        }
    
        
//      Initialization
//      ===================================================================================================
        static method onInit()
        {
            trigger t = CreateTrigger();
            TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
            TriggerAddCondition(t, Condition(static method thistype.Start));
        }
    }
}
//! endzinc
 
Level 8
Joined
Jul 28, 2008
Messages
211
Thx guys :) I'll try it now
Sorry for the bumping, just saw several threads above it and was going offline so I wanted it to be seen :grin:

Using filters instead of an embedded "loop enumeration" is much faster, it just requires you to create a couple global variables, no biggie.

Is that something like
JASS:
   GroupEnumUnitsInRange(G, x, y, 500, b)
    if filter then
        call ForGroup(G, burn.start(.....))
    endif

EDIT: It works now! Thanks alot guys :)
Oh by the way, UnitAlive won't work for some reason and it's really weird :S (I used it before and it worked but now if I put it there the map won't open)
 
Level 8
Joined
Jul 28, 2008
Messages
211
Yea I know that people help here (every time I get stuck I post it here and there are already about 10 of my posts here :grin:)
Anyway, what's zinc? First time I hear about it... well I am a little bit rusty.

because everybody knows that bribe is a
JASS:
 tag addict :P[/QUOTE]
Well I always look for [code=jass] tags too so does that make me an addict too? :eek:
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,468
First, filter is just a function filter.

JASS:
function GroupFilter takes nothing returns boolean
    return UnitAlive(GetFilterUnit())
endfunction

function GroupEnum takes nothing returns nothing
    call GroupEnumUnitsInRect(g,r,Filter(function GroupFilter))
endfunction

Second, you copied everything perfectly and UnitAlive is causing you errors?
 
Level 8
Joined
Jul 28, 2008
Messages
211
First, filter is just a function filter.

Jass:
function GroupFilter takes nothing returns boolean
return UnitAlive(GetFilterUnit())
endfunction

function GroupEnum takes nothing returns nothing
call GroupEnumUnitsInRect(g,r,Filter(function GroupFilter))
endfunction
Second, you copied everything perfectly and UnitAlive is causing you errors?

I just added
JASS:
native UnitAlive takes unit id returns boolean
at the start and used it instead of GetWidgetLife() and the map wouldn't open. But when I deleted it and put GetWidgetLife() it worked.
 
Status
Not open for further replies.
Top