• 🏆 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] Special effect isn't removed when spell is level 2 or 3...

Status
Not open for further replies.
Level 14
Joined
Jul 19, 2007
Messages
772
I have imported a spell from a spellpack that has 3 levels and it seems to work like it should in level 1 but when using this spell in level 2 and 3, the special effect created on affected units doesn't seems to be gone, it only removes the special effect from the caster and that's very strange. Anyone knows what's the cause of this? Pls try help. I love this spell and I don't want to remove it from my map just because of the bug :(
JASS:
//TESH.scrollpos=30
//TESH.alwaysfold=0
/*
    **********************************************************************************************************
    
                                        Installation:
        
        1) Copy this trigger and 'DD Library' to your map
        2) Copy Nature Blessing abilitie to your map
        3) Export and Import 'GaiaShield.mdx', 'GaiaMissile.mdx', 'NatureExplosion.mdx' sound from this map to yours
        4) Modify constants to your liking below
        5) Made on Warcraft III v1.30
    
    **********************************************************************************************************
*/

//! zinc
library NatureBlessing requires DDLib
{

        // This is the main raw code of ability Nature Blessing
        // Default: 'Nbls'
        constant    integer            NATURE_BLESSING                    = 'A0FX';
        
        // Natures shield effect model path
        // Default: "Spells\\NatureBlessing\\GaiaShield.mdx"
        constant    string            NATURE_SHIELD_EFFECT            = "Spells\\NatureBlessing\\GaiaShield.mdx";
        
        // Natures missile effect model path
        // Default: "Spells\\NatureBlessing\\GaiaMissle.mdx"
        constant    string            NATURE_MISSILE_EFFECT            = "Spells\\NatureBlessing\\GaiaMissle.mdx";
        
        // Natures explosion model effect path
        // Default: "Spells\\NatureBlessing\\NatureExplosion.mdx"
        constant    string            NATURE_EXPLOSION_EFFECT            = "Spells\\NatureBlessing\\NatureExplosion.mdl";
        
        // Natures missile fire sound path (this sound is played when missiles start to move)
        // Default: "Abilities\\Spells\\NightElf\\Tranquility\\TranquilityTarget1.wav"
        constant    string            NATURE_SHOOT_SOUND                = "Abilities\\Spells\\NightElf\\Tranquility\\TranquilityTarget1.wav";
        constant    real            NATURE_SHOOT_SOUND_VOL            = 100.;
        constant    real            NATURE_SHOOT_SOUND_PITCH        = 1.5;
        constant    real            NATURE_SHOOT_SOUND_MAX_DISTANCE = 3500.;
        
        // Nature explosion sound file path (is played when nature explosion is caused)
        // Default: "Abilities\\Spells\\NightElf\\Tranquility\\Tranquility.wav"
        constant    string            NATURE_EXPLOSION_SOUND            = "Abilities\\Spells\\NightElf\\Tranquility\\Tranquility.wav";
        constant    real            NATURE_EXPLOSION_SOUND_VOL        = 90.;
        
        // This is number of nature missiles created
        // Default: 12
        constant    integer            MISSILES_COUNT                    = 12;
        // Default: 1.2
        constant    real            MISSILE_SIZE                    = 1.2;
        
        // This is min and max distance from caster to missiles start point
        // Default: 400 / 600
        constant    real            MISSILE_MIN_DISTANCE            = 400.;
        constant    real            MISSILE_MAX_DISTANCE            = 600.;
        
        // This is min and max finish height / caster position of missiles
        // Default: 75 / 400
        constant    real            MISSILE_MIN_HEIGHT                = 40.;
        constant    real            MISSILE_MAX_HEIGHT                = 400.;
        
        // This is missiles value which tells how much time they need to reach caster and how long it takse for them to manifest
        // Default: 0.75 / 0.5
        constant    real            MISSILE_TRAVEL_TIME                = .9;
        constant    real            MISSILE_BIRTH_TIME                = 0.5;
        
        // Default: 50
        constant    real            MISSILE_INITIAL_HEIGHT            = 10.;
        
        // Default: 0.2 seconds
        constant    real            HEAL_INTERVAL                    = .2;
        // --------------------------------------------------------------
        //    *** Do not edit level variables here edit them below ***
        real            EXPLOSION_AOE[];
        real            SHIELD_PROTECT[];
        real            HEAL_PERC_AMOUNT[];
        real            SPELL_DURATION[];
    
    // -----------------------------------------------------------------
    //                 *** Edit level data here ***
    function NatureBlessingLevelSetup() {
        // This is AOE of explosion "effect"
        // Default: 410 / 530 / 650
        EXPLOSION_AOE[01] = 550.;
        EXPLOSION_AOE[02] = 550.;
        EXPLOSION_AOE[03] = 550.;
        
        // Shield protection is amount of damage reduced in percentage
        // Default: 15 / 25 / 35
        SHIELD_PROTECT[01] = 20.;
        SHIELD_PROTECT[02] = 30.;
        SHIELD_PROTECT[03] = 40.;
        
        // This is heal amount in percentage per point of life missing
        // Default: 5 / 7 / 9
        HEAL_PERC_AMOUNT[01] = 5.;
        HEAL_PERC_AMOUNT[02] = 7.;
        HEAL_PERC_AMOUNT[03] = 9.;
        
        // This is how long spell lasts in seconds
        // Default: 10 / 10 / 10
        SPELL_DURATION[01] = 10.;
        SPELL_DURATION[02] = 10.;
        SPELL_DURATION[03] = 10.;
    }
    
    function PickUnitsFilter(unit f, player owner) -> boolean {
        return IsUnitAlly(f, owner)                                 &&
               !IsUnitType(f, UNIT_TYPE_DEAD)                         &&
               !IsUnitType(f, UNIT_TYPE_STRUCTURE)                     &&
               !BlzIsUnitInvulnerable(f)                            &&
               !DDIsUnitWard(f)                                        &&
               !IsUnitType(f, UNIT_TYPE_MAGIC_IMMUNE)                &&
               !IsUnitType(f, UNIT_TYPE_MECHANICAL);
    }
    
    // #####################################################################################
    // #####################################################################################
    
    timer            Tim = null;
    group            Grp = null;
    
    struct unitdata {
        real dur, heal, shield;
        effect e;
        
        static thistype Att[];
    }
    
    struct missiles {
        ddeffect    m[MISSILES_COUNT];
        p_real         pos[MISSILES_COUNT];
        p_real        spd[MISSILES_COUNT];
        integer     cnt;
        
        real        X, Y;
        player        owner;
        integer     lvl;
        
        
        static method create(unit c) -> thistype {
            thistype this = allocate();
            integer i;
            real x = GetWidgetX(c), y = GetWidgetY(c);
            real rad = 0., d;
            
            X = x; Y = y;
            owner = GetOwningPlayer(c);
            lvl = GetUnitAbilityLevel(c, NATURE_BLESSING);
            
            cnt = R2I((MISSILE_TRAVEL_TIME-.1) / DD_INTERVAL);
            for(i=00; i < MISSILES_COUNT; i+=01) {
                rad += bj_PI/6.;
                d = GetRandomReal(MISSILE_MIN_DISTANCE, MISSILE_MAX_DISTANCE);
                pos[i] = pVector(x + d * Cos(rad), y + d * Sin(rad), MISSILE_INITIAL_HEIGHT);
                spd[i] = pVector(-Cos(rad) * DD_INTERVAL * d / MISSILE_TRAVEL_TIME,
                                 -Sin(rad) * DD_INTERVAL * d / MISSILE_TRAVEL_TIME,
                                 DD_INTERVAL * (GetRandomReal(MISSILE_MIN_HEIGHT, MISSILE_MAX_HEIGHT)-MISSILE_INITIAL_HEIGHT) / MISSILE_TRAVEL_TIME);
                m[i] = ddeffect.createZ(NATURE_MISSILE_EFFECT, pos[i][00], pos[i][01], pos[i][02] + DDTerrZ(pos[i][00], pos[i][01]), rad, MISSILE_SIZE);
            }
            
            return this;
        }
        
        method destroy() {
            integer i;
            
            for(i=00; i < MISSILES_COUNT; i+=01) {
                m[i].destroy();
                pos[i].destroy();
                spd[i].destroy();
            }
            deallocate();
        }
        
        method Step() -> boolean {
            integer i;
            
            for(i=00; i < MISSILES_COUNT; i+=01) {
                pos[i][00] += spd[i][00];
                pos[i][01] += spd[i][01];
                pos[i][02] += spd[i][02];
                
                m[i].PositionZ(pos[i][00], pos[i][01], pos[i][02]);
                //m[i].Y = pos[i][01];
                //m[i].Z = pos[i][02];
            }
            
            cnt -= 01;
            return (cnt == 00);
        }
        
    }
    
    
    // ==========================================================================================
    function onInit() {
        trigger t = CreateTrigger();
        
        Grp = CreateGroup();
        NatureBlessingLevelSetup();
        
        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        
        TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
        TriggerAddCondition(t, Condition(function() -> boolean {
            missiles ms;
            unit u;
            
            if (GetSpellAbilityId() != NATURE_BLESSING)
                return false;
            
            u = GetTriggerUnit();
            ms = missiles.create(u);
            
            // Play sound and shake camera for players within spell cast range
            DDGenericSound(NATURE_SHOOT_SOUND, NATURE_SHOOT_SOUND_VOL, GetWidgetX(u), GetWidgetY(u), NATURE_SHOOT_SOUND_MAX_DISTANCE, NATURE_SHOOT_SOUND_PITCH);
            
            DDStartTim(MISSILE_BIRTH_TIME, false, ms, function() {
                DDStartTim(DD_INTERVAL, true, DDTimData(), function() {
                    missiles ms = DDTimData();
                    trigger t;
                    
                    if (ms.Step()) {
                        DDGenericSound(NATURE_EXPLOSION_SOUND, NATURE_EXPLOSION_SOUND_VOL, ms.X, ms.Y, NATURE_SHOOT_SOUND_MAX_DISTANCE, 1.8);
                        ddeffect.create(NATURE_EXPLOSION_EFFECT, ms.X, ms.Y, GetRandomReal(0., bj_PI*2.), EXPLOSION_AOE[ms.lvl]/250.).destroyx(3.);
                        
                        DDGroupFilterArea(ms.X, ms.Y, EXPLOSION_AOE[ms.lvl], ms, function() -> boolean {
                            unitdata ud;
                            missiles ms = DDGFilterData();
                            unit f = GetFilterUnit();
                            
                            if (PickUnitsFilter(f, ms.owner)) {
                                if (ud.Att[GetHandleId(f)-0x100000] == p_null) {
                                    ud = unitdata.create();
                                    ud.Att[GetHandleId(f)-0x100000] = ud;
                                    ud.e = AddSpecialEffectTarget(NATURE_SHIELD_EFFECT, f, "origin");
                                    GroupAddUnit(Grp, f);
                                } else
                                    ud = ud.Att[GetHandleId(f)-0x100000];
                                
                                ud.heal         = HEAL_PERC_AMOUNT[ms.lvl] / 100.;
                                ud.shield         = SHIELD_PROTECT[ms.lvl] / 100.;
                                ud.dur             = SPELL_DURATION[ms.lvl];
                            }
                            
                            f = null;
                            return false;
                        });
                        
                        if (Tim == null) {
                            Tim = DDLoadTim();
                        
                        TimerStart(Tim, HEAL_INTERVAL, true, function() {
                            ForGroup(Grp, function() {
                                unit e = GetEnumUnit();
                                unitdata ud = unitdata.Att[GetHandleId(e)-0x100000];
                                
                                SetWidgetLife( e, GetWidgetLife(e) + (HEAL_INTERVAL * ud.heal * (GetUnitState(e, UNIT_STATE_MAX_LIFE)-GetWidgetLife(e))) );
                                
                                ud.dur -= .2;
                                if (ud.dur < 0. || IsUnitType(e, UNIT_TYPE_DEAD)) {
                                    GroupRemoveUnit(Grp, e);
                                    ud.Att[GetHandleId(e)-0x100000] = p_null;
                                    DestroyEffect(ud.e);
                                    ud.e = null;
                                    ud.destroy();
                                    if (FirstOfGroup(Grp) == null) {
                                        DDRecycleTim(Tim);
                                        Tim = null;
                                    }
                                }
                                
                                e = null;
                            });
                        });
                        
                        }
                        
                        ms.destroy();
                        DDQuitTim();
                    }
                });
                DDQuitTim();
            });
            
            u = null;
            return false;
        }));
        
        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        
        t = CreateTrigger();
        DDTriggerRegisterAnyUnitDamaged(t);
        TriggerAddCondition(t, Condition(function() -> boolean {
            unit u = GetTriggerUnit();
            unitdata ud = unitdata.Att[GetHandleId(u)-0x100000];
                
            if (ud == p_null) {
                u = null;
                return false;
            }
            
            SetWidgetLife(u, GetWidgetLife(u) + (GetEventDamage() * ud.shield));
            
            u = null;
            return false;
        }));
        
        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
        
    }
    
}
//! endzinc
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
vJASS:
if (ud.dur < 0. || IsUnitType(e, UNIT_TYPE_DEAD)) {
    GroupRemoveUnit(Grp, e);
    ud.Att[GetHandleId(e)-0x100000] = p_null
    DestroyEffect(ud.e);
    ud.e = null;
    ud.destroy();
    if (FirstOfGroup(Grp) == null) {
       DDRecycleTim(Tim)
       Tim = null;
    }
}
^Here's the block of code that contains the destruction of the special effect -> DestroyEffect(ud.e);

It says if the duration is less than 0 |OR| the unit is dead then destroy the effect (and also remove the unit from the spell system).

I have no idea why it would stop working at different levels but can you try changing this line:
vJASS:
ud.dur < 0.
To this:
vJASS:
ud.dur <= 0.

It would also help if you linked the spell pack.
 
Last edited:
Level 14
Joined
Jul 19, 2007
Messages
772
vJASS:
if (ud.dur < 0. || IsUnitType(e, UNIT_TYPE_DEAD)) {
    GroupRemoveUnit(Grp, e);
    ud.Att[GetHandleId(e)-0x100000] = p_null
    DestroyEffect(ud.e);
    ud.e = null;
    ud.destroy();
    if (FirstOfGroup(Grp) == null) {
       DDRecycleTim(Tim)
       Tim = null;
    }
}
^Here's the block of code that contains the destruction of the special effect -> DestroyEffect(ud.e);

It says if the duration is less than 0 |OR| the unit is dead then destroy the effect (and also remove the unit from the spell system).

I have no idea why it would stop working at different levels but can you try changing this line:
vJASS:
ud.dur < 0.
To this:
vJASS:
ud.dur <= 0.

It would also help if you linked the spell pack.
Sry I forgot. Here it is DD Universal Pack v5.1c
 
Level 14
Joined
Jul 19, 2007
Messages
772
Well, the spell works fine at all levels in the demo map, either you messed something up while importing it or you're experiencing some kind of graphical glitch. Have you tried restarting your computer?
Yes I have but no difference. It's wierd because it removes the special effect from the caster but not the other affected units.
 

Attachments

  • bless.png
    bless.png
    1 MB · Views: 20

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
Are you sure it's not because the units died? Have you tested if it works properly when doing the full duration on living units?

By morphing he means Transforming like Metamorphosis or Bear Form.
 
Last edited:
Level 14
Joined
Jul 19, 2007
Messages
772
Are you sure it's not because the units died? Have you tested if it works the full duration on living units?

By morphing he means Transforming like Metamorphosis or Bear Form.
No it's not because they died. The effect isn't removed if they are alive either and it's wierd because the spell works perfect in level 1 so I can't understand why it wont work for lvl 2 and 3...
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,539
No it's not because they died. The effect isn't removed if they are alive either and it's wierd because the spell works perfect in level 1 so I can't understand why it wont work for lvl 2 and 3...
Not sure, that's a weird issue. That's why I think it's just a graphical glitch on your part or you made some mistake while importing it. It doesn't happen in the demo map.
 
Level 14
Joined
Jul 19, 2007
Messages
772
Not sure, that's a weird issue. That's why I think it's just a graphical glitch on your part or you made some mistake while importing it. It doesn't happen in the demo map.
Now I have played my map with this spell some times and notice it even is happening in level 1 but it seems like the effect glitch is happening on the units that gets a attack and armor upgrade because it doesn't happening on heroes or summoned units so it has to be something wrong with the units that gets the upgrade on attack and armor... Very strange!
 
Level 19
Joined
Feb 27, 2019
Messages
590
Have you pin pointed that the upgrades are the issue here?
Meaning:
1. Have you tried removing the upgrades and the bug does not occur?
2. Have you given the upgrade to the units immediately and confirmed if the unit has the upgrade the bug occurs 100% of the time?
For 2. You could create two triggers, one that adds and one that removes the upgrade for the player and see if there is a difference when upgrade is researched and when its not.
 
Level 14
Joined
Jul 19, 2007
Messages
772
Have you pin pointed that the upgrades are the issue here?
Meaning:
1. Have you tried removing the upgrades and the bug does not occur?
2. Have you given the upgrade to the units immediately and confirmed if the unit has the upgrade the bug occurs 100% of the time?
For 2. You could create two triggers, one that adds and one that removes the upgrade for the player and see if there is a difference when upgrade is researched and when its not.
Well it isn't caused because of the upgrades. I tested the map without any upgrades and the bug still happening and it happened after about 17 minutes of gameplay.
 
Status
Not open for further replies.
Top