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

[vJASS] Timer Stopping With No Indication

Status
Not open for further replies.
Level 4
Joined
Mar 20, 2014
Messages
67
Hey All,

I have this ability that creates 1-4 orbs based on level that circle around the caster, throwing healing missiles towards the caster and allies that heal. For some reason the spell fails. Like, there's no error, nothing's going wrong, it just stops. The orbs stop moving and it's as if the timer just gets destroyed. Even when it does this though it makes sure to loop through at least 20 times and shoots the missile out so. I can't figure it out.

There's nothing to indicate why, and I can't figure it out.

It's possible it's a Blizzard bug, but if it's my fault maybe one of you can spot it, I can't find it

JASS:
//! zinc
library BreezeriousHeal requires GameTimer, xemissile, xedamage, UnitAlive, UnitMaxState {
    private struct HealMissile extends xehomingmissile {
        private xedamage heal;
        real amount;
        unit caster;
        unit target;
        private method onHit() {
            this.heal = xedamage.create();
            this.heal.damageSelf = true;
            this.heal.damageAllies = true;
            this.heal.damageEnemies = false;
            this.heal.damageNeutral = false;
            this.heal.allyfactor = -1.0;
         
            this.heal.damageTarget(this.caster, this.target, this.amount);
            this.heal.destroy();
        }
    }

    private struct BreezeriousHeal {
        private static integer abilityId = 'A0CT';
        private static string missileEffect = "war3mapImported\\TBAEMissile.mdl";
        private static integer dummyId = 'n02B';
        private static integer uniqueAbilityId = 'A0D4';
        private unit caster;
        private unit dummyUnit[4];
        private real angle[4];
        private GameTimer periodicTimer;
        private real timerSpeed = .03125;
        private real timerCount = 0;
        private static integer timerTicks = 20;
        private real HPS = 60 * (timerSpeed*thistype.timerTicks);
        private real duration = 10;
        private real AOE = 600;
        private real orbOffset = 400;
        //Rotation speed in degrees per second
        private real rotationSpeed = 15 * timerSpeed;
        private integer numOrbs;
        //Orbs HP Regen
        private real orbHPR;
        //Orbs HP
        private real orbHP;
        //Does it target the lowest HP ally in range?
        private boolean lowestHP = true;
     
        private method setup(integer level) {
            this.orbHP = 400 + (level * 200);
            this.orbHPR = 4 * level * this.timerSpeed;
            this.numOrbs = level;
            if(GetUnitAbilityLevel(this.caster, this.uniqueAbilityId) > 0) {
                this.orbHP = 400 + (level * 200) + I2R(GetUnitAbilityLevel(this.caster, this.uniqueAbilityId) * 200);
            }
        }
     
        private method CheckTarget(unit u) -> boolean {
            return IsUnitAlly(u, GetOwningPlayer(this.caster)) &&
            !IsUnitType(u, UNIT_TYPE_MECHANICAL) &&
            GetWidgetLife(u) > .405 &&
            GetUnitTypeId(u) != this.dummyId;
        }
     
        private method CheckOrbsHP() -> boolean {
            integer i;
            boolean dummyAlive = true;
            for(0<i<=this.numOrbs) {
                if(GetWidgetLife(this.dummyUnit[i]) > .405) {
                    return true;
                }
            }
            return false;
        }
     
        private method tick() {
            integer i;
            real casterx = GetUnitX(this.caster);
            real castery = GetUnitY(this.caster);
            real offsetx;
            real offsety;
            HealMissile h;
            group g;
            unit u=null;
            unit lowUnit=null;
            this.timerCount += 1;
            for(0<=i<=this.numOrbs) {
                if(!this.CheckOrbsHP()) {
                    this.periodicTimer.deleteLater();
                    this.destroy();
                    break;
                } else if(GetWidgetLife(this.dummyUnit[i]) > .405) {
                    this.angle[i] += this.rotationSpeed;
                    offsetx = casterx + this.orbOffset * Cos(this.angle[i] * bj_DEGTORAD);
                    offsety = castery + this.orbOffset * Sin(this.angle[i] * bj_DEGTORAD);
                    SetUnitX(this.dummyUnit[i], offsetx);
                    SetUnitY(this.dummyUnit[i], offsety);
                    SetWidgetLife(this.dummyUnit[i], GetWidgetLife(this.dummyUnit[i]) + this.orbHPR);
                    if(this.timerCount >= this.timerTicks) {
                        g=CreateGroup();
                        GroupEnumUnitsInRange(g, offsetx, offsety, this.AOE, null);
                        u=FirstOfGroup(g);
                        while(u!=null) {
                            if(this.CheckTarget(u)) {
                                if(this.lowestHP) {
                                    if(lowUnit == null) {
                                        lowUnit = u;
                                    } else if((GetUnitState(u, UNIT_STATE_LIFE)/GetUnitState(u, UNIT_STATE_MAX_LIFE)) < (GetUnitState(lowUnit, UNIT_STATE_LIFE)/GetUnitState(lowUnit, UNIT_STATE_MAX_LIFE))) {
                                        lowUnit = u;
                                    } else if(!this.lowestHP) {
                                        h = HealMissile.create(offsetx, offsety, 100, u, GetUnitFlyHeight(u));
                                        h.fxpath = this.missileEffect;
                                        h.amount = this.HPS;
                                        h.caster = this.caster;
                                        h.launch(800, .15);
                                        break;
                                    }
                                }
                            }
                            GroupRemoveUnit(g, u);
                            u=null;
                            u=FirstOfGroup(g);
                        }
                        h = HealMissile.create(offsetx, offsety, 100, lowUnit, GetUnitFlyHeight(lowUnit));
                        h.fxpath = this.missileEffect;
                        h.amount = this.HPS;
                        h.target = lowUnit;
                        h.caster = this.caster;
                        h.launch(800, .15);
                        DestroyGroup(g);
                        u=null;
                        lowUnit = null;
                    }
                }
            }
            if(this.timerCount >= this.timerTicks) {
                this.timerCount = 0;
            }
        }
     
        private static method Begin() -> thistype {
            thistype this = thistype.allocate();
            real casterx;
            real castery;
            real offsetx;
            real offsety;
            integer i;
            this.caster = GetTriggerUnit();
            this.setup(GetUnitAbilityLevel(this.caster, this.abilityId));
            casterx = GetUnitX(this.caster);
            castery = GetUnitY(this.caster);
            for(0<i<=this.numOrbs) {
                this.angle[i] = (360/this.numOrbs)*i;
                offsetx = casterx + this.orbOffset * Cos(this.angle[i] * bj_DEGTORAD);
                offsety = castery + this.orbOffset * Sin(this.angle[i] * bj_DEGTORAD);
                this.dummyUnit[i] = CreateUnit(GetOwningPlayer(this.caster), this.dummyId, offsetx, offsety, bj_UNIT_FACING);
                SetUnitMaxState(this.dummyUnit[i], UNIT_STATE_MAX_LIFE, this.orbHP);
                UnitApplyTimedLife(this.dummyUnit[i], 'BTLF', this.duration);
            }
            this.periodicTimer = GameTimer.newPeriodic(function (GameTimer t) {
                thistype this = t.data();
                this.tick();
            }).start(this.timerSpeed);
            this.periodicTimer.setData(this);
            this.periodicTimer.printList();
            return this;
        }
 
        private static method onInit() {
            trigger t = CreateTrigger();
            TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
            TriggerAddCondition(t, function() -> boolean {
                if(GetSpellAbilityId() == thistype.abilityId) {
                    thistype.Begin();
                }
                return false;
            });
        }
    }
}
//! endzinc

Thank you all!

- Zach
 
Last edited by a moderator:
Correct, you are.

The first point still stands and extends towards Begin. To clarify it,

JASS:
function ...
    integer i = 0;

Just in case, try adding debug messages in your code and see which parts work and which do not. I'd suggest attaching a debug message at the lambda expression first, before the begin method is called. From there, assume logical behavior of the game and proceed the way the game would proceed, step by step.


 
Status
Not open for further replies.
Top