Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[System] StatusHandler

Discussion in 'Wurst Resources' started by Menag, Mar 19, 2014.

  1. Menag

    Menag

    Joined:
    Aug 26, 2009
    Messages:
    188
    Resources:
    4
    Spells:
    1
    Wurst:
    3
    Resources:
    4
    [WurstScript] StatusHandler 1.2

    StatusHandler is a library for WurstScript which provides an easy way to apply status effects like stuns and silences.

    There are no requirements except for the obligatory WurstScript StandardLib.

    Code:
    Code (WurstScript):
    /**
     * A package to apply status effects like stun and silence.
     * This package counts the amount of applied and removed status effects,
     * therefore you have to care yourself about how many times you add or
     * remove a status effect from a unit. To use this, you will need a
     * Unit Indexer and have to implement the function in the config file.
     */

    package StatusHandler

    import StatusHandlerConfig
    import DummyCaster

    constant DummyCaster STUN_DUMMY_CASTER = new DummyCaster(STUN_ABILITY_ID, "firebolt", DUMMY_PLAYER, false)
    constant DummyCaster SILENCE_DUMMY_CASTER = new DummyCaster(SILENCE_ABILITY_ID, "soulburn", DUMMY_PLAYER, false)
    constant DummyCaster DISARM_BOTH_DUMMY_CASTER = new DummyCaster(DISARM_BOTH_ABILITY_ID, "drunkenhaze", DUMMY_PLAYER, false)
    constant DummyCaster DISARM_MELEE_DUMMY_CASTER = new DummyCaster(DISARM_MELEE_ABILITY_ID, "drunkenhaze", DUMMY_PLAYER, false)
    constant DummyCaster DISARM_RANGED_DUMMY_CASTER = new DummyCaster(DISARM_RANGED_ABILITY_ID, "drunkenhaze", DUMMY_PLAYER, false)
    constant DummyCaster ENSNARE_DUMMY_CASTER = new DummyCaster(ENSNARE_ABILITY_ID, "ensnare", DUMMY_PLAYER, false)

    /**
     * A class which holds all informations about the status effect of the given unit.
     */

    public class Status
        protected static Status array status
       
        protected timer stunTimer
        protected int stunCounter
       
        protected timer silenceTimer
        protected int silenceCounter
       
        protected timer ensnareTimer
        protected int ensnareCounter
       
        protected timer disarmBothTimer
        protected int disarmBothCounter
       
        protected timer disarmMeleeTimer
        protected int disarmMeleeCounter
       
        protected timer disarmRangedTimer
        protected int disarmRangedCounter
           
        /**
         * Constructs a Status object with the given unit.
         *
         * unit u - the unit whose Status object is being created
         */

        construct(unit u)
            int i = u.getIndex()
            status[i] = this
            stunTimer = null
            stunCounter = 0
            silenceTimer = null
            silenceCounter = 0
            ensnareTimer = null
            ensnareCounter = 0
            disarmBothTimer = null
            disarmBothCounter = 0
            disarmMeleeTimer = null
            disarmMeleeCounter = 0
            disarmRangedTimer = null
            disarmRangedCounter = 0
           
        ondestroy
            if stunTimer != null
                stunTimer.release()
            if silenceTimer != null
                silenceTimer.release()
            if ensnareTimer != null
                ensnareTimer.release()
            if disarmBothTimer != null
                disarmBothTimer.release()
            if disarmMeleeTimer != null
                disarmMeleeTimer.release()
            if disarmRangedTimer != null
                disarmRangedTimer.release()

    /**
     * Returns a boolean indicating if the unit is stunned.
     *
     * returns true if the unit is stunned, false otherwise
     */

    public function unit.isStunned() returns boolean
        return Status.status[this.getIndex()].stunCounter > 0

    /**
     * Applys a stun to the given unit. The duration is infinite. Hence you have to
     * remove it yourself. The function will count the amount of calls and will therefore
     * last till you removed all stuns which have been applied before.
     */

    public function unit.addStun()
        if not this.isStunned()
            STUN_DUMMY_CASTER.castOnTarget(this)
        Status.status[this.getIndex()].stunCounter++

    /**
     * Applies a timed stun to the given unit. If there is already a timed stun on this unit,
     * the stun with the longer duration will be used.
     *
     * real timeout - the stun duration
     */

    public function unit.addStunTimed(real timeout)
        int i = this.getIndex()
        if Status.status[i].stunTimer != null and Status.status[i].stunTimer.getRemaining() >= timeout
            return
           
        if Status.status[i].stunTimer == null
            Status.status[i].stunTimer = getTimer()..setData(i)
            this.addStun()
       
        Status.status[i].stunTimer.start(timeout, () -> begin
            int j = GetExpiredTimer().getData()
            j.getUnit().removeStun()
            Status.status[j].stunTimer.release()
            Status.status[j].stunTimer = null
        end)

    /**
     * Reduces the timed stun duration by the given amount. If the duration is smaller
     * then the given amount the timed stun is getting removed.
     *
     * real timeout - the amount the duration is getting reduced
     */

    public function unit.reduceTimedStun(real timeout)
        int i = this.getIndex()
        if Status.status[i].stunTimer == null
            return
       
        if Status.status[i].stunTimer.getRemaining() - timeout > 0.
            Status.status[i].stunTimer.start(Status.status[i].stunTimer.getRemaining() - timeout, () -> begin
                int j = GetExpiredTimer().getData()
                j.getUnit().removeStun()
                Status.status[j].stunTimer.release()
                Status.status[j].stunTimer = null
            end)
        else
            this.removeStun()
            Status.status[i].stunTimer.release()
            Status.status[i].stunTimer = null

    /**
     * Removes one stun stack from the given unit. It is possible that the unit
     * is still stunned afterwards. Throws a warning if a non-existing stun is being
     * removed.
     */

    public function unit.removeStun()
        Status.status[this.getIndex()].stunCounter--
        if Status.status[this.getIndex()].stunCounter < 0
            printWarning("StatusHandler: tried removing a non-existing stun.")
            Status.status[this.getIndex()].stunCounter = 0
        if not this.isStunned()
            this.removeAbility(STUN_BUFF_ID)

    /**
     * Removes the current timed stun from the given unit. This only removes
     * the timed stun. Stuns applied with addStun() might still exist. Throws
     * a warning if there is no timed stun active.
     */

    public function unit.removeTimedStun()
        int i = this.getIndex()
        if Status.status[i].stunTimer != null
            i.getUnit().removeStun()
            Status.status[i].stunTimer.release()
            Status.status[i].stunTimer = null
        else
            printWarning("StatusHandler: tried removing a non-existing timed stun.")

    /**
     * Removes all stun stacks and the timed stun from the given unit. Afterwards
     * the unit is not stunned anymore.
     */

    public function unit.clearStun()
        int i = this.getIndex()
        if not this.isStunned()
            this.removeAbility(STUN_BUFF_ID)
        Status.status[i].stunCounter = 0
        if Status.status[i].stunTimer != null
            i.getUnit().removeStun()
            Status.status[i].stunTimer.release()
            Status.status[i].stunTimer = null
       
    /**
     * Returns a boolean indicating if the unit is silenced.
     *
     * returns true if the unit is silenced, false otherwise
     */

    public function unit.isSilenced() returns boolean
        return Status.status[this.getIndex()].silenceCounter > 0
       
    /**
     * Applys a silence to the given unit. The duration is infinite. Hence you have to
     * remove it yourself. The function will count the amount of calls and will therefore
     * last till you removed all silences which have been applied before.
     */

    public function unit.addSilence()
        if not this.isSilenced()
            SILENCE_DUMMY_CASTER.castOnTarget(this)
        Status.status[this.getIndex()].silenceCounter++

    /**
     * Applies a timed silence to the given unit. If there is already a timed silence on this unit,
     * the silence with the longer duration will be used.
     *
     * real timeout - the silence duration
     */

    public function unit.addSilenceTimed(real timeout)
        int i = this.getIndex()
        if Status.status[i].silenceTimer != null and Status.status[i].silenceTimer.getRemaining() >= timeout
            return
           
        if Status.status[i].silenceTimer == null
            Status.status[i].silenceTimer = getTimer()..setData(i)
            this.addSilence()
       
        Status.status[i].silenceTimer.start(timeout, () -> begin
            int j = GetExpiredTimer().getData()
            j.getUnit().removeSilence()
            Status.status[j].silenceTimer.release()
            Status.status[j].silenceTimer = null
        end)

    /**
     * Reduces the timed silence duration by the given amount. If the duration is smaller
     * then the given amount the timed silence is getting removed.
     *
     * real timeout - the amount the duration is getting reduced
     */

    public function unit.reduceTimedSilence(real timeout)
        int i = this.getIndex()
        if Status.status[i].silenceTimer == null
            return
       
        if Status.status[i].silenceTimer.getRemaining() - timeout > 0.
            Status.status[i].silenceTimer.start(Status.status[i].silenceTimer.getRemaining() - timeout, () -> begin
                int j = GetExpiredTimer().getData()
                j.getUnit().removeSilence()
                Status.status[j].silenceTimer.release()
                Status.status[j].silenceTimer = null
            end)
        else
            this.removeSilence()
            Status.status[i].silenceTimer.release()
            Status.status[i].silenceTimer = null

    /**
     * Removes one silence stack from the given unit. It is possible that the unit
     * is still silenced afterwards. Throws a warning if a non-existing silence is being
     * removed.
     */

    public function unit.removeSilence()
        Status.status[this.getIndex()].silenceCounter--
        if Status.status[this.getIndex()].silenceCounter < 0
            printWarning("StatusHandler: tried removing a non-existing silence.")
            Status.status[this.getIndex()].silenceCounter = 0
        if not this.isSilenced()
            this.removeAbility(SILENCE_BUFF_ID)

    /**
     * Removes the current timed silence from the given unit. This only removes
     * the timed silence. Silences applied with addSilence() might still exist. Throws
     * a warning if there is no timed silence active.
     */

    public function unit.removeTimedSilence()
        int i = this.getIndex()
        if Status.status[i].silenceTimer != null
            i.getUnit().removeSilence()
            Status.status[i].silenceTimer.release()
            Status.status[i].silenceTimer = null
        else
            printWarning("StatusHandler: tried removing a non-existing timed silence.")

    /**
     * Removes all silence stacks and the timed silence from the given unit. Afterwards
     * the unit is not silenced anymore.
     */

    public function unit.clearSilence()
        int i = this.getIndex()
        if not this.isSilenced()
            this.removeAbility(SILENCE_BUFF_ID)
        Status.status[i].silenceCounter = 0
        if Status.status[i].silenceTimer != null
            i.getUnit().removeSilence()
            Status.status[i].silenceTimer.release()
            Status.status[i].silenceTimer = null
       
    /**
     * Returns a boolean indicating if the unit is ensnared.
     *
     * returns true if the unit is ensnared, false otherwise
     */

    public function unit.isEnsnared() returns boolean
        return Status.status[this.getIndex()].ensnareCounter > 0
       
    /**
     * Ensnares the given unit. The duration is infinite. Hence you have to
     * remove it yourself. The function will count the amount of calls and will therefore
     * last till you removed all ensnares which have been applied before.
     */

    public function unit.addEnsnare()
        if not this.isEnsnared()
            ENSNARE_DUMMY_CASTER.castOnTarget(this)
        Status.status[this.getIndex()].ensnareCounter++

    /**
     * Applies a timed ensnare to the given unit. If there is already a timed ensnare on this unit,
     * the ensnare with the longer duration will be used.
     *
     * real timeout - the ensnare duration
     */

    public function unit.addEnsnareTimed(real timeout)
        int i = this.getIndex()
        if Status.status[i].ensnareTimer != null and Status.status[i].ensnareTimer.getRemaining() >= timeout
            return
           
        if Status.status[i].ensnareTimer == null
            Status.status[i].ensnareTimer = getTimer()..setData(i)
            this.addEnsnare()
           
        Status.status[i].ensnareTimer.start(timeout, () -> begin
            int j = GetExpiredTimer().getData()
            j.getUnit().removeEnsnare()
            Status.status[j].ensnareTimer.release()
            Status.status[j].ensnareTimer = null
        end)
       
    /**
     * Reduces the timed ensnare duration by the given amount. If the duration is smaller
     * then the given amount the timed ensnare is getting removed.
     *
     * real timeout - the amount the duration is getting reduced
     */

    public function unit.reduceTimedEnsnare(real timeout)
        int i = this.getIndex()
        if Status.status[i].ensnareTimer == null
            return
       
        if Status.status[i].ensnareTimer.getRemaining() - timeout > 0.
            Status.status[i].ensnareTimer.start(Status.status[i].ensnareTimer.getRemaining() - timeout, () -> begin
                int j = GetExpiredTimer().getData()
                j.getUnit().removeEnsnare()
                Status.status[j].ensnareTimer.release()
                Status.status[j].ensnareTimer = null
            end)
        else
            this.removeEnsnare()
            Status.status[i].ensnareTimer.release()
            Status.status[i].ensnareTimer = null

    /**
     * Removes the current timed ensnare from the given unit. This only removes
     * the timed ensnare. Ensnares applied with addEnsnare() might still exist. Throws
     * a warning if there is no timed ensnare active.
     */

    public function unit.removeTimedEnsnare()
        int i = this.getIndex()
        if Status.status[i].ensnareTimer != null
            i.getUnit().removeEnsnare()
            Status.status[i].ensnareTimer.release()
            Status.status[i].ensnareTimer = null
        else
            printWarning("StatusHandler: tried removing a non-existing timed ensnare.")

    /**
     * Removes all ensnare stacks and the timed ensnare from the given unit. Afterwards
     * the unit is not ensnared anymore.
     */

    public function unit.clearEnsnare()
        int i = this.getIndex()
        if not this.isEnsnared()
            this.removeAbility(ENSNARE1_BUFF_ID)
            this.removeAbility(ENSNARE2_BUFF_ID)
        Status.status[i].ensnareCounter = 0
        if Status.status[i].ensnareTimer != null
            i.getUnit().removeEnsnare()
            Status.status[i].ensnareTimer.release()
            Status.status[i].ensnareTimer = null

    /**
     * Removes one ensnare stack from the given unit. It is possible that the unit
     * is still ensnared afterwards. Throws a warning if a non-existing ensnare is being
     * removed.
     */

    public function unit.removeEnsnare()
        Status.status[this.getIndex()].ensnareCounter--
        if Status.status[this.getIndex()].ensnareCounter < 0
            printWarning("StatusHandler: tried removing a non-existing ensnare.")
            Status.status[this.getIndex()].ensnareCounter = 0
        if not this.isEnsnared()
            this.removeAbility(ENSNARE1_BUFF_ID)
            this.removeAbility(ENSNARE2_BUFF_ID)
           
    /**
     * Returns a boolean indicating if the unit is disarmed.
     *
     * returns true if the unit is disarmed, false otherwise
     */

    public function unit.isDisarmedBoth() returns boolean
        return Status.status[this.getIndex()].disarmBothCounter > 0

    /**
     * Disarms the given unit. The duration is infinite. Hence you have to
     * remove it yourself. The function will count the amount of calls and will therefore
     * last till you removed all disarmbuffs which have been applied before.
     */

    public function unit.addDisarmBoth()
        if not this.isDisarmedBoth()
            DISARM_BOTH_DUMMY_CASTER.castOnTarget(this)
        Status.status[this.getIndex()].disarmBothCounter++

    /**
     * Applies a timed disarm to the given unit. If there is already a timed disarm on this unit,
     * the disarm with the longer duration will be used.
     *
     * real timeout - the disarm duration
     */

    public function unit.addDisarmBothTimed(real timeout)
        int i = this.getIndex()
        if Status.status[i].disarmBothTimer != null and Status.status[i].disarmBothTimer.getRemaining() >= timeout
            return
           
        if Status.status[i].disarmBothTimer == null
            Status.status[i].disarmBothTimer = getTimer()..setData(i)
            this.addDisarmBoth()
       
        Status.status[i].disarmBothTimer.start(timeout, () -> begin
            int j = GetExpiredTimer().getData()
            j.getUnit().removeDisarmBoth()
            Status.status[j].disarmBothTimer.release()
            Status.status[j].disarmBothTimer = null
        end)
       
    /**
     * Reduces the timed disarm duration by the given amount. If the duration is smaller
     * then the given amount the timed disarm is getting removed.
     *
     * real timeout - the amount the duration is getting reduced
     */

    public function unit.reduceTimedDisarmBoth(real timeout)
        int i = this.getIndex()
        if Status.status[i].disarmBothTimer == null
            return
       
        if Status.status[i].disarmBothTimer.getRemaining() - timeout > 0.
            Status.status[i].disarmBothTimer.start(Status.status[i].disarmBothTimer.getRemaining() - timeout, () -> begin
                int j = GetExpiredTimer().getData()
                j.getUnit().removeDisarmBoth()
                Status.status[j].disarmBothTimer.release()
                Status.status[j].disarmBothTimer = null
            end)
        else
            this.removeDisarmBoth()
            Status.status[i].disarmBothTimer.release()
            Status.status[i].disarmBothTimer = null

    /**
     * Removes one disarm stack from the given unit. It is possible that the unit
     * is still disarmed afterwards. Throws a warning if a non-existing disarmbuff is being
     * removed.
     */

    public function unit.removeDisarmBoth()
        Status.status[this.getIndex()].disarmBothCounter--
        if Status.status[this.getIndex()].disarmBothCounter < 0
            printWarning("StatusHandler: tried removing a non-existing disarm both.")
            Status.status[this.getIndex()].disarmBothCounter = 0
        if not this.isDisarmedBoth()
            this.removeAbility(DISARM_BOTH_BUFF_ID)

    /**
     * Removes the current timed disarm from the given unit. This only removes
     * the timed disarm. Disarms applied with addDisarmBoth() might still exist. Throws
     * a warning if there is no timed disarm active.
     */

    public function unit.removeTimedDisarmBoth()
        int i = this.getIndex()
        if Status.status[i].disarmBothTimer != null
            i.getUnit().removeDisarmBoth()
            Status.status[i].disarmBothTimer.release()
            Status.status[i].disarmBothTimer = null
        else
            printWarning("StatusHandler: tried removing a non-existing timed disarm both.")

    /**
     * Removes all disarm stacks and the timed disarm from the given unit. Afterwards
     * the unit is not disarmed anymore.
     */

    public function unit.clearDisarmBoth()
        int i = this.getIndex()
        if not this.isDisarmedBoth()
            this.removeAbility(DISARM_BOTH_BUFF_ID)
        Status.status[i].disarmBothCounter = 0
        if Status.status[i].disarmBothTimer != null
            i.getUnit().removeDisarmBoth()
            Status.status[i].disarmBothTimer.release()
            Status.status[i].disarmBothTimer = null

    /**
     * Returns a boolean indicating if the unit is melee-disarmed.
     *
     * returns true if the unit is melee-disarmed, false otherwise
     */

    public function unit.isDisarmedMelee() returns boolean
        return Status.status[this.getIndex()].disarmMeleeCounter > 0

    /**
     * Disarms the given melee unit. The duration is infinite. Hence you have to
     * remove it yourself. The function will count the amount of calls and will therefore
     * last till you removed all melee-disarms which have been applied before.
     */

    public function unit.addDisarmMelee()
        if not this.isDisarmedMelee()
            DISARM_MELEE_DUMMY_CASTER.castOnTarget(this)
        Status.status[this.getIndex()].disarmMeleeCounter++

    /**
     * Applies a timed melee-disarm to the given unit. If there is already a timed melee-disarm on this unit,
     * the melee-disarm with the longer duration will be used.
     *
     * real timeout - the melee-disarm duration
     */

    public function unit.addDisarmMeleeTimed(real timeout)
        int i = this.getIndex()
        if Status.status[i].disarmMeleeTimer != null and Status.status[i].disarmMeleeTimer.getRemaining() >= timeout
            return
           
        if Status.status[i].disarmMeleeTimer == null
            Status.status[i].disarmMeleeTimer = getTimer()..setData(i)
            this.addDisarmMelee()
           
        Status.status[i].disarmMeleeTimer.start(timeout, () -> begin
            int j = GetExpiredTimer().getData()
            j.getUnit().removeDisarmMelee()
            Status.status[j].disarmMeleeTimer.release()
            Status.status[j].disarmMeleeTimer = null
        end)
       
    /**
     * Reduces the timed melee-disarm duration by the given amount. If the duration is smaller
     * then the given amount the timed melee-disarm is getting removed.
     *
     * real timeout - the amount the duration is getting reduced
     */

    public function unit.reduceTimedDisarmMelee(real timeout)
        int i = this.getIndex()
        if Status.status[i].disarmMeleeTimer == null
            return
       
        if Status.status[i].disarmMeleeTimer.getRemaining() - timeout > 0.
            Status.status[i].disarmMeleeTimer.start(Status.status[i].disarmMeleeTimer.getRemaining() - timeout, () -> begin
                int j = GetExpiredTimer().getData()
                j.getUnit().removeDisarmMelee()
                Status.status[j].disarmMeleeTimer.release()
                Status.status[j].disarmMeleeTimer = null
            end)
        else
            this.removeDisarmMelee()
            Status.status[i].disarmMeleeTimer.release()
            Status.status[i].disarmMeleeTimer = null

    /**
     * Removes one melee-disarm stack from the given unit. It is possible that the unit
     * is still melee-disarmed afterwards. Throws a warning if a non-existing melee-disarm is being
     * removed.
     */

    public function unit.removeDisarmMelee()
        Status.status[this.getIndex()].disarmMeleeCounter--
        if Status.status[this.getIndex()].disarmMeleeCounter < 0
            printWarning("StatusHandler: tried removing a non-existing disarm melee.")
            Status.status[this.getIndex()].disarmMeleeCounter = 0
        if not this.isDisarmedMelee()
            this.removeAbility(DISARM_MELEE_BUFF_ID)

    /**
     * Removes the current timed melee-disarm from the given unit. This only removes
     * the timed melee-disarm. Melee-disarms applied with addDisarmMelee() might still exist. Throws
     * a warning if there is no timed melee-disarm active.
     */

    public function unit.removeTimedDisarmMelee()
        int i = this.getIndex()
        if Status.status[i].disarmMeleeTimer != null
            i.getUnit().removeDisarmMelee()
            Status.status[i].disarmMeleeTimer.release()
            Status.status[i].disarmMeleeTimer = null
        else
            printWarning("StatusHandler: tried removing a non-existing timed disarm melee.")

    /**
     * Removes all melee-disarm stacks and the timed melee-disarm from the given unit. Afterwards
     * the unit is not melee-disarmed anymore.
     */

    public function unit.clearDisarmMelee()
        int i = this.getIndex()
        if not this.isDisarmedMelee()
            this.removeAbility(DISARM_MELEE_BUFF_ID)
        Status.status[i].disarmMeleeCounter = 0
        if Status.status[i].disarmMeleeTimer != null
            i.getUnit().removeDisarmMelee()
            Status.status[i].disarmMeleeTimer.release()
            Status.status[i].disarmMeleeTimer = null

    /**
     * Returns a boolean indicating if the unit is range-disarmed.
     *
     * returns true if the unit is range-disarmed, false otherwise
     */

    public function unit.isDisarmedRanged() returns boolean
        return Status.status[this.getIndex()].disarmRangedCounter > 0

    /**
     * Disarms the given ranged unit. The duration is infinite. Hence you have to
     * remove it yourself. The function will count the amount of calls and will therefore
     * last till you removed all range-disarms which have been applied before.
     */

    public function unit.addDisarmRanged()
        if not this.isDisarmedRanged()
            DISARM_RANGED_DUMMY_CASTER.castOnTarget(this)
        Status.status[this.getIndex()].disarmRangedCounter++

    /**
     * Applies a timed range-disarm to the given unit. If there is already a timed range-disarm on this unit,
     * the range-disarm with the longer duration will be used.
     *
     * real timeout - the range-disarm duration
     */

    public function unit.addDisarmRangedTimed(real timeout)
        int i = this.getIndex()
        if Status.status[i].disarmRangedTimer != null and Status.status[i].disarmRangedTimer.getRemaining() >= timeout
            return
           
        if Status.status[i].disarmRangedTimer == null
            Status.status[i].disarmRangedTimer = getTimer()..setData(i)
            this.addDisarmRanged()
           
        Status.status[i].disarmRangedTimer.start(timeout, () -> begin
            int j = GetExpiredTimer().getData()
            j.getUnit().removeDisarmRanged()
            Status.status[j].disarmRangedTimer.release()
            Status.status[j].disarmRangedTimer = null
        end)

    /**
     * Reduces the timed range-disarm duration by the given amount. If the duration is smaller
     * then the given amount the timed range-disarm is getting removed.
     *
     * real timeout - the amount the duration is getting reduced
     */

    public function unit.reduceTimedDisarmRanged(real timeout)
        int i = this.getIndex()
        if Status.status[i].disarmRangedTimer == null
            return
       
        if Status.status[i].disarmRangedTimer.getRemaining() - timeout > 0.
            Status.status[i].disarmRangedTimer.start(Status.status[i].disarmRangedTimer.getRemaining() - timeout, () -> begin
                int j = GetExpiredTimer().getData()
                j.getUnit().removeDisarmRanged()
                Status.status[j].disarmRangedTimer.release()
                Status.status[j].disarmRangedTimer = null
            end)
        else
            this.removeDisarmRanged()
            Status.status[i].disarmRangedTimer.release()
            Status.status[i].disarmRangedTimer = null

    /**
     * Removes one range-disarm stack from the given unit. It is possible that the unit
     * is still range-disarmed afterwards. Throws a warning if a non-existing range-disarm is being
     * removed.
     */

    public function unit.removeDisarmRanged()
        Status.status[this.getIndex()].disarmRangedCounter--
        if Status.status[this.getIndex()].disarmRangedCounter < 0
            printWarning("StatusHandler: tried removing a non-existing disarm ranged.")
            Status.status[this.getIndex()].disarmRangedCounter = 0
        if not this.isDisarmedRanged()
            this.removeAbility(DISARM_RANGED_BUFF_ID)
           
    /**
     * Removes the current timed range-disarm from the given unit. This only removes
     * the timed range-disarm. Range-disarms applied with addDisarmRanged() might still exist. Throws
     * a warning if there is no timed range-disarm active.
     */

    public function unit.removeTimedDisarmRanged()
        int i = this.getIndex()
        if Status.status[i].disarmRangedTimer != null
            i.getUnit().removeDisarmRanged()
            Status.status[i].disarmRangedTimer.release()
            Status.status[i].disarmRangedTimer = null
        else
            printWarning("StatusHandler: tried removing a non-existing timed disarm ranged.")

    /**
     * Removes all range-disarm stacks and the timed range-disarm from the given unit. Afterwards
     * the unit is not range-disarmed anymore.
     */

    public function unit.clearDisarmRanged()
        int i = this.getIndex()
        if not this.isDisarmedRanged()
            this.removeAbility(DISARM_RANGED_BUFF_ID)
        Status.status[i].disarmRangedCounter = 0
        if Status.status[i].disarmRangedTimer != null
            i.getUnit().removeDisarmRanged()
            Status.status[i].disarmRangedTimer.release()
            Status.status[i].disarmRangedTimer = null

    /**
     * Removes all status effects. This includes the stacks and the timed effects. Afterwards
     * the unit has no status effect applied anymore.
     */

    public function unit.clearAll()
        this.clearStun()
        this.clearSilence()
        this.clearEnsnare()
        this.clearDisarmBoth()
        this.clearDisarmMelee()
        this.clearDisarmRanged()

    Object Generation:
    Code (WurstScript):
    package StatusHandlerAbilityGen

    import AbilityObjEditing
    import BuffObjEditing
    import ObjectIds
    import StatusHandlerConfig

    /**
     * Sets some basic values for the abilitys used to apply the status effects.
     */

    function AbilityDefinition.setBasicValues()
        this..setLevels(1)
            ..setCooldown(1, 0)
            ..setCastingTime(1, 0)
            ..setManaCost(1, 0)
            ..setDurationNormal(1, 0)
            ..setDurationHero(1, 0)
            ..setCastRange(1, 99999.)
            ..setAnimationNames("")
            ..setMissileArt("")
            ..setMissileSpeed(0)
            ..setHeroAbility(true)
            ..setIconNormal("")
            ..setEditorSuffix("(Status System)")
            ..setTargetsAllowed(1, "invulnerable,vulnerable")
            ..setRequiredLevel(6)
            .setRace("other")

    /**
     * Sets some basic values for the buffs used to apply the status effects.
     */

    function BuffDefinition.setBasicValues()
        this..setRace(1, "other")
            ..setTarget(1, "")
            .setEditorSuffix(0, "(Status System)")

    /**
     * Generates the abilitys and buffs to apply the status effects.
     */

    @compiletime function genAbilities()
        new AbilityDefinitionFireBoltcreep(STUN_ABILITY_ID)
            ..setBasicValues()
            ..setDamage(1, 0)
            ..setName("Stun")
            .setBuffs(1, idInteger2IdString(STUN_BUFF_ID))
        new BuffDefinition(STUN_BUFF_ID, 'BPSE')
            ..setBasicValues()
            ..setTooltipNormal(1, STUN_TOOLTIP_NORMAL)
            ..setTooltipNormalExtended(1, STUN_TOOLTIP_EXTENDED)
            .setIconNormal(1, STUN_ICON_PATH)
       
        new AbilityDefinitionFirelordSoulBurn(SILENCE_ABILITY_ID)
            ..setBasicValues()
            ..setDamageAmount(1, 0)
            ..setDamagePeriod(1, 99999)
            ..setDamagePenalty(1, 0)
            ..setAttackSpeedReduction(1, 0)
            ..setMovementSpeedReduction(1, 0)
            ..setName("Silence")
            .setBuffs(1, idInteger2IdString(SILENCE_BUFF_ID))
        new BuffDefinition(SILENCE_BUFF_ID, 'BNso')
            ..setBasicValues()
            ..setTooltipNormal(1, SILENCE_TOOLTIP_NORMAL)
            ..setTooltipNormalExtended(1, SILENCE_TOOLTIP_EXTENDED)
            .setIconNormal(1, SILENCE_ICON_PATH)
       
        new AbilityDefinitionBrewmasterDrunkenHaze(DISARM_BOTH_ABILITY_ID)
            ..setBasicValues()
            ..setAttackSpeedModifier(1, 0)
            ..setMovementSpeedModifier(1, 0)
            ..setChanceToMiss(1, 0)
            ..setAttacksPrevented(1, "3")
            ..setName("Disarm (Both)")
            .setBuffs(1, idInteger2IdString(DISARM_BOTH_BUFF_ID))
        new BuffDefinition(DISARM_BOTH_BUFF_ID, 'BNdh')
            ..setBasicValues()
            ..setTooltipNormal(1, DISARM_BOTH_TOOLTIP_NORMAL)
            ..setTooltipNormalExtended(1, DISARM_BOTH_TOOLTIP_EXTENDED)
            .setIconNormal(1, DISARM_BOTH_ICON_PATH)

        new AbilityDefinitionBrewmasterDrunkenHaze(DISARM_MELEE_ABILITY_ID)
            ..setBasicValues()
            ..setAttackSpeedModifier(1, 0)
            ..setMovementSpeedModifier(1, 0)
            ..setChanceToMiss(1, 0)
            ..setAttacksPrevented(1, "1")
            ..setName("Disarm (Melee)")
            .setBuffs(1, idInteger2IdString(DISARM_MELEE_BUFF_ID))
        new BuffDefinition(DISARM_MELEE_BUFF_ID, 'BNdh')
            ..setBasicValues()
            ..setTooltipNormal(1, DISARM_MELEE_TOOLTIP_NORMAL)
            ..setTooltipNormalExtended(1, DISARM_MELEE_TOOLTIP_EXTENDED)
            .setIconNormal(1, DISARM_BOTH_ICON_PATH)
       
        new AbilityDefinitionBrewmasterDrunkenHaze(DISARM_RANGED_ABILITY_ID)
            ..setBasicValues()
            ..setAttackSpeedModifier(1, 0)
            ..setMovementSpeedModifier(1, 0)
            ..setChanceToMiss(1, 0)
            ..setAttacksPrevented(1, "2")
            ..setName("Disarm (Range)")
            .setBuffs(1, idInteger2IdString(DISARM_RANGED_BUFF_ID))
        new BuffDefinition(DISARM_RANGED_BUFF_ID, 'BNdh')
            ..setBasicValues()
            ..setTooltipNormal(1, DISARM_RANGED_TOOLTIP_NORMAL)
            ..setTooltipNormalExtended(1, DISARM_RANGED_TOOLTIP_EXTENDED)
            .setIconNormal(1, DISARM_BOTH_ICON_PATH)
       
        new AbilityDefinitionEnsnareCreep(ENSNARE_ABILITY_ID)
            ..setBasicValues()
            ..setAirUnitLowerDuration(1, -1)
            ..setAirUnitHeight(1, -1)
            ..setName("Ensnare")
            .setBuffs(1, idInteger2IdString(ENSNARE1_BUFF_ID) + "," + idInteger2IdString(ENSNARE2_BUFF_ID))
        new BuffDefinition(ENSNARE1_BUFF_ID, 'Beng')
            ..setBasicValues()
            ..setTooltipNormal(1, ENSNARE_TOOLTIP_NORMAL)
            ..setTooltipNormalExtended(1, ENSNARE_TOOLTIP_EXTENDED)
            .setIconNormal(1, ENSNARE_ICON_PATH)
        new BuffDefinition(ENSNARE2_BUFF_ID, 'Bena')
            ..setBasicValues()
            ..setTooltipNormal(1, ENSNARE_TOOLTIP_NORMAL)
            ..setTooltipNormalExtended(1, ENSNARE_TOOLTIP_EXTENDED)
            .setIconNormal(1, ENSNARE_ICON_PATH)

    Configuration:
    To configure the StatusHandler, create a package named "StatusHandlerConfig" and specify the ability and buff ids. You also have to implement the two functions "function unit.getIndex() returns int" and "function int.getUnit() returns unit". Here is an example implementation in case you don't use a unit indexer yet.
    Code (WurstScript):
    package StatusHandlerConfig

    import StatusHandler

    public constant int STUN_ABILITY_ID = 'SAST'
    public constant int STUN_BUFF_ID = 'SBST'
    public constant string STUN_TOOLTIP_NORMAL = "Stunned"
    public constant string STUN_TOOLTIP_EXTENDED = "This unit is stunned; it cannot move, attack or cast spells."
    public constant string STUN_ICON_PATH = "ReplaceableTextures\\CommandButtons\\BTNStun.blp"

    public constant int SILENCE_ABILITY_ID = 'SASI'
    public constant int SILENCE_BUFF_ID = 'SBSI'
    public constant string SILENCE_TOOLTIP_NORMAL = "Silence"
    public constant string SILENCE_TOOLTIP_EXTENDED = "This unit is silenced; it cannot cast spells."
    public constant string SILENCE_ICON_PATH = "ReplaceableTextures\\CommandButtons\\BTNSilence.blp"

    public constant int DISARM_BOTH_ABILITY_ID = 'SADA'
    public constant int DISARM_BOTH_BUFF_ID = 'SBDA'
    public constant string DISARM_BOTH_TOOLTIP_NORMAL = "Disarmed"
    public constant string DISARM_BOTH_TOOLTIP_EXTENDED = "This unit is disarmed; it cannot attack."
    public constant string DISARM_BOTH_ICON_PATH = "ReplaceableTextures\\CommandButtons\\BTNBattleStations.blp"

    public constant int DISARM_MELEE_ABILITY_ID = 'SADM'
    public constant int DISARM_MELEE_BUFF_ID = 'SBDM'
    public constant string DISARM_MELEE_TOOLTIP_NORMAL = "Disarmed (Melee)"
    public constant string DISARM_MELEE_TOOLTIP_EXTENDED = "This unit is disarmed; it cannot use melee attacks."
    public constant string DISARM_MELEE_ICON_PATH = "ReplaceableTextures\\CommandButtons\\BTNBattleStations.blp"

    public constant int DISARM_RANGED_ABILITY_ID = 'SADR'
    public constant int DISARM_RANGED_BUFF_ID = 'SBDR'
    public constant string DISARM_RANGED_TOOLTIP_NORMAL = "Disarmed (Ranged)"
    public constant string DISARM_RANGED_TOOLTIP_EXTENDED = "This unit is disarmed; it cannot use ranged attacks."
    public constant string DISARM_RANGED_ICON_PATH = "ReplaceableTextures\\CommandButtons\\BTNBattleStations.blp"

    public constant int ENSNARE_ABILITY_ID = 'SAEN'
    /** This is for ground units */
    public constant int ENSNARE1_BUFF_ID = 'SBE1'
    /** This is for air units */
    public constant int ENSNARE2_BUFF_ID = 'SBE2'
    public constant string ENSNARE_TOOLTIP_NORMAL = "Ensnared"
    public constant string ENSNARE_TOOLTIP_EXTENDED = "This unit is ensnared, it cannot move or fly."
    public constant string ENSNARE_ICON_PATH = "ReplaceableTextures\\CommandButtons\\BTNWirtsOtherLeg.blp"

    /**
     * Implement this function to return a unique integer for every unit.
     */

    public function unit.getIndex() returns int
        return this.getUserData()
       
    /**
     * Implement this function to return the related unit to this integer.
     */

    public function int.getUnit() returns unit
        return (this castTo UnitWrapper).u

    public class UnitWrapper
        unit u
        Status status
       
        construct(player owner, int raw, real x, real y, real face)
            u = CreateUnit(owner, raw, x, y, face)
            u.setUserData(this castTo int)
            status = new Status(u)
       
        ondestroy
            u.setUserData(0) // Invalid units should have index 0.
            destroy status

     

    GitHub:
    StatusHandler

    Changelog:
    1.0 - Initial release
    1.1 - Made abilities require level 6 to also apply status effects to magic immunte units
    1.2 - Added the function removeTimedStatus(), reduceTimedStatus(real timeout), clearStatus(), clearAll() and changed the function order a bit

    Credits:
    - Pingufex, the bishop of antarctica and the leader of the worldwide Pingu Church
    - muzzel
    - WaterKnight
    - Crigges
    - PurgeandFire
     
    Last edited: Mar 20, 2014
  2. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,793
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Maybe it's a forum error but I get a lot of errors saying use tabs, not spaces.
     
  3. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,429
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    It looks great. Does it work on magic immune units? I remember J4L's Status didn't work on them for a bit, but changing the abilities to ultimates should fix that (set required level to 6). It is something to consider.
     
  4. Menag

    Menag

    Joined:
    Aug 26, 2009
    Messages:
    188
    Resources:
    4
    Spells:
    1
    Wurst:
    3
    Resources:
    4
    Yeah, that's a bug when copying Wurst-Code. It's even an issue ticket on github.

    Okay, I'll test it and see! Thanks!

    Edit: changed it !
     
    Last edited: Mar 19, 2014
  5. muzzel

    muzzel

    Joined:
    Jun 27, 2008
    Messages:
    1,303
    Resources:
    2
    JASS:
    1
    Wurst:
    1
    Resources:
    2
    Nice system, id like to see this getting approved here.

    @TriggerHappy: Just download it from GitHub, then you get tabs instead of spaces.
     
  6. Crigges

    Crigges

    Joined:
    Nov 20, 2011
    Messages:
    201
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    Well written and documented, vote for approval!

    @TriggerHappy if you paste the code inside the we, sometimes tabs gets converted to spaces.
     
  7. Menag

    Menag

    Joined:
    Aug 26, 2009
    Messages:
    188
    Resources:
    4
    Spells:
    1
    Wurst:
    3
    Resources:
    4
    What do you think about
    1. adding a function removeTimedStun(), which removes the current timed stun
    2. adding a function clearStun(), which removes all stun stacks. This includes stopping the timer.
    3. adding a function removeAll(), which removes all status effects completely

    The first two of course for all status effects. Maybe the first one isn't really needed, but I think 2 and 3 could be useful.
     
  8. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,429
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Yes! That'd be awesome for advanced buff control without interfering with everything else (e.g. X ability removes all stuns from the unit). Idk about #1. I suppose you could add a function to reduce a unit's stun duration (could be pretty neat), but it depends on whether you see that as useful.

    Also, I'd recommend having a function list at the top of the script for quick reference (well, unless Wurst already autocompletes in Eclipse, which I think it does–then I suppose it isn't necessary).
     
  9. Menag

    Menag

    Joined:
    Aug 26, 2009
    Messages:
    188
    Resources:
    4
    Spells:
    1
    Wurst:
    3
    Resources:
    4
    Yes, I was thinking about that too.

    There is autocomplete in eclipse and we have awesome hotdoc comments.
     
  10. muzzel

    muzzel

    Joined:
    Jun 27, 2008
    Messages:
    1,303
    Resources:
    2
    JASS:
    1
    Wurst:
    1
    Resources:
    2
    3. is very important
    1. and 2. are optional, 2. might be cool

    About the function list: the wurst conventions are that all public/api functions need to have proper HotDoc documentation. On one hand this works really nice in combination with the eclipse plugin, but there is also the possibility to generate html documentation based on the hotdoc (i think Ruk3 is currently working on the design).
     
  11. Menag

    Menag

    Joined:
    Aug 26, 2009
    Messages:
    188
    Resources:
    4
    Spells:
    1
    Wurst:
    3
    Resources:
    4
    Added functions 1-3, going to add the reduceTimed tomorrow I think!
     
  12. Menag

    Menag

    Joined:
    Aug 26, 2009
    Messages:
    188
    Resources:
    4
    Spells:
    1
    Wurst:
    3
    Resources:
    4
    Is there anything else you want to have changed or added?
     
  13. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,429
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Nope, I'm going to go ahead and approve it.
     
  14. Donach

    Donach

    Joined:
    Jan 12, 2011
    Messages:
    98
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    Any update on this? There is no DummyCaster library as of now in Wurst...
     
  15. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,099
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    There is InstanceDummyCaster instead! Replacing DummyCaster usages with the newer package should fix your problem.