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

[Wurst] StatusHandler

Level 6
Joined
Aug 26, 2009
Messages
192
[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:
Wurst:
/**
 * 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:
Wurst:
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.
Wurst:
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:
Level 6
Joined
Aug 26, 2009
Messages
192
Maybe it's a forum error but I get a lot of errors saying use tabs, not spaces.

Yeah, that's a bug when copying Wurst-Code. It's even an issue ticket on github.

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.

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

Edit: changed it !
 
Last edited:
Level 6
Joined
Aug 26, 2009
Messages
192
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.
 
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.

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).
 
Level 6
Joined
Aug 26, 2009
Messages
192
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.

Yes, I was thinking about that too.

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).

There is autocomplete in eclipse and we have awesome hotdoc comments.
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
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).
 
Top