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

Cold debuff with variable slow

Status
Not open for further replies.
Level 12
Joined
May 22, 2015
Messages
1,051
The cold abilities (frost nova, ice armour, nerubian tower) that make units bluish with the snow stuff around the victims seem to have a shared slowing effect that can be modified in game constants.

Anyway, I wanted to have variable slowing depending on the ability I am using while still keeping the cold animation working like it does.

I have two ideas that may or may not be good:
1) Trigger the blue colour and snow effects.
2) Make the global constants slow 0% and then apply a separate slowing ability.

Is there a better / easier way to do this? Which of these options is better?

On a side note, this is for a levelled orb effect that slows enemies (with a frost theme to it). I will probably also use this in other spells.

Thanks in advance.
 
Your way should work just fine. I personally always like more control--so I always recommend triggering everything! :D

But if you're triggering the color/effect, then just beware of stacking effects. i.e. let's say you get slowed at time t=0 until t=5. At t=2, you get slowed again until t=7. A lot of people will make the mistake of resetting the unit vertex color at t=5. But if the user is still slowed, then the coloring should last until t=7. I think even the default wc3 engine is a bit buggy when it comes to stacking vertex colors or slows or w/e.

The proper way to handle it depends on whether the vertex color effect for the first slow is the same as the second slow:
(1) If you plan to have all vertex color changes the same, then you can simply have a unit-specific counter to keep track of how many slows the unit has. If a unit is slowed, increment it by 1. If the slow is removed, decrement it by 1. Once that counter reaches 0, you can reset the vertex coloring to default values.
(2) If you want to support different vertex color effects, then you'll probably want to maintain some sort of list per unit instead of just a counter.

So I went ahead and created (2) because I thought it was an interesting problem. This is untested and probably has syntax errors, but this is probably how I'd approach it:
JASS:
/**
	A simple library to handle vertex coloring.

	The most recently applied vertex color effect is the one that shows up.
	When that effect expires, the next one in line appears (if any).

	function RegisterDefaultVertexColor(typeId, red, green, blue, alpha)
		- If a unit has a default vertex color other than (255, 255, 255, 255), then
		  you should use this function to register the appropriate values. You only have
		  to do this once per unit type, preferably on initialization.

	VertexColor.create(unit, red, green, blue, alpha, duration) returns VertexColor
		- Applies a vertex color effect to a unit using the given values for 
		  'duration' seconds. If you want the effect applied indefinitely, input
		  a value of -1 for duration.

	vertexColorInstance.destroy()
		- You can destroy an instance of a vertex color at any time.

*/

library VertexColor

	globals
		private hashtable ht = InitHashtable()
	endglobals

	/**
		If a unit-type has a default vertex color other than (255, 255, 255, 255),
		register those values on initialization through this function.
	*/
	function RegisterDefaultVertexColor takes integer typeId, integer r, integer g, integer b, integer a returns nothing
		call SaveInteger(ht, typeId, 0, r)
		call SaveInteger(ht, typeId, 1, g)
		call SaveInteger(ht, typeId, 2, b)
		call SaveInteger(ht, typeId, 3, a)
	endfunction

	struct VertexColorEffect
		private unit base
		readonly integer red
		readonly integer green
		readonly integer blue
		readonly integer alpha
		readonly thistype next
		readonly thistype prev

		method destroy takes nothing returns nothing
			local integer typeId = GetUnitTypeId(this.base)
			local integer id = GetHandleId(this.base)

			/* If this is the front node, we must alter vertex colors */
			if this == LoadInteger(ht, id, 0) then

				/* If there are no more effects, reset the color */
				if this.next == 0 then
					if HaveSavedInteger(ht, typeId, 0) then
						call SetUnitVertexColor(this.base, LoadInteger(ht, typeId, 0), /*
							*/	LoadInteger(ht, typeId, 1), /*
							*/	LoadInteger(ht, typeId, 2), /*
							*/  LoadInteger(ht, typeId, 3))
					else
						call SetUnitVertexColor(this.base, 255, 255, 255, 255)
					endif
					call RemoveSavedInteger(ht, id, 0)
				else
					call SetUnitVertexColor(this.base, this.next.red, this.next.green, this.next.blue, this.next.alpha)
					call SaveInteger(ht, id, 0, this.next)
				endif

				set this.next = 0
				set this.prev = 0
			else
				set this.next.prev = this.prev
				set this.prev.next = this.next
			endif
			call this.deallocate()
		endmethod

		static method create takes unit u, integer r, integer g, integer b, integer a returns thistype
			local thistype this = thistype.allocate()
			local integer id = GetHandleId(u)
			set this.base  = u
			set this.red   = r
			set this.green = g
			set this.blue  = b
			set this.alpha = a

			/*
				Add this instance to a list.
			*/
			set this.next.prev = this
			set this.next = LoadInteger(ht, id, 0)
			call SaveInteger(ht, id, 0, this.next)
			call SetUnitVertexColor(u, r, g, b, a)
			return this
		endmethod
	endstruct

	struct VertexColor
		private VertexColorEffect fx
		private timer time
		
		method destroy takes nothing returns nothing
			if this.time != null then
				call PauseTimer(this.time)
				call DestroyTimer(this.time)
				set this.time = null
			endif
			call fx.destroy()
		endmethod

		private static method finish takes nothing returns nothing
			local thistype this = LoadInteger(ht, GetHandleId(GetExpiredTimer()), 1)
			call this.destroy()
		endmethod

		static method create takes unit u, integer r, integer g, integer b, integer a, real duration returns thistype
			local thistype this = thistype.allocate()
			set this.fx  = VertexColorEffect.create(u, r, g, b, a)
			set this.dur = duration
			if duration == -1 then
				set this.time = null
			else
				set this.time = CreateTimer()
				call SaveInteger(ht, GetHandleId(this.time), 1, this)
				call TimerStart(this.time, duration, false, function thistype.finish)
			endif
			return this
		endmethod
	endstruct

endlibrary

TL;DR this library keeps track of vertex color effects. They do not stack. The most recently applied effect is the one that is shown. As soon as that expires, it moves on to the next effect in the list (if that effect still has some duration left). You don't really need to know anything about the system beyond what is written in the topmost comment block (unless you plan on implementing it yourself).

Give it a shot and see if this is the behavior you want.
 
Status
Not open for further replies.
Top