• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Custom Closure Events

Status
Not open for further replies.
Level 8
Joined
Nov 20, 2011
Messages
202
Here is a new kind of an event system which uses the new wurst feature closures.

Here is the package:

http://peeeq.de/code.php?id=7220

Wurst:
package CustomClosureEvents
	/**
	********	Info	********
	* A litte package which allows you to create custom events which closures (a new wurst feature)
	********* Usage ********
	* To create a new eventtype working event follow this steps:
	*	-Create a new class which inherits from the class Event and fill in the type parameter.
	* 		The Type paramter specifies the datatype of the event response data.
	* 	-Create a function fire which creates a new object of the desired event response data. Set the attributes of the object and
	* 		call the function of the superclass: Event.callActions(Your event response object here) 
	* Use the function Event.addAction(...) to add new actions to the event, and destroy the actions to remove them
	* If you destroy the event all actions which refers to the event get destroyed 
	*/
	public class Event<T>
		protected Action<T> first = null
		protected Action<T> last = null
		protected boolean checkData = false
		
		/** Adds a new action to the closure */
		function addAction(Action<T> a) returns Action<T>
			a.ev = this
			if first == null
				first = a
				last = a
				a.next = null
				a.prev = null
			else
				first.prev = a
				a.next = first
				first = a
			return a
				
		/** If this function returns true the event will be interupted. 
			Override this function in your subclass if you wish to inspect the event data after each action*/
		protected function stop(T d) returns boolean
			return false
			
		/** Call this function in your underclass to run all actions which refer to this event */
		protected function callActions(T d)
			Action<T> buffer = first
			if checkData 
				while buffer != null
					if stop(d)
						return
					buffer.fire(d)
					buffer = buffer.next
			else
				while buffer != null
					buffer.fire(d)
					buffer = buffer.next
				
				
		ondestroy
			Action<T> buffer1 = first
			while buffer1 != null
				Action<T> buffer2 = buffer1.next
				destroy buffer1
				buffer1 = buffer2
				
				
	public abstract class Action<T>
		protected Action<T> next
		protected Action<T> prev
		protected Event<T> ev
		
		abstract function fire(T d)
		
		ondestroy
			if ev.first != this
				prev.next = next
			else
				ev.first = next
			if ev.last != this
				next.prev = prev
			else
				ev.last = prev
	
endpackage

Here is an example Event:

http://peeeq.de/code.php?id=7221

Wurst:
package SampleEvent

	import CustomClosureEvents

	//The event response data
	class UnitDamageData
		unit source // The damage source
		unit target // The damage target
		real damage // The damage dealed
		boolean stop = false // interrupt the event
		
	class UnitDamageEvent extends Event<UnitDamageData>
		unit target
				
		construct(unit target)
			this.target = target
			//We want to check the response data after each action, so we this this true
			checkData = true
			
		//An easy fire function which returns the final amount of damage
		function fire(unit source, real dmg) returns real
			//Create the response data object
			let data = new UnitDamageData()
			//Set the data
			data.source = source
			data.target = target
			data.damage = dmg
			//Call the refering actions
			callActions(data)
			//Read out the final damage
			real finalDmg = data.damage
			destroy data
			//Return the final damage
			return finalDmg
			
		//Interrupt the event if necessary
		override function stop(UnitDamageData data) returns boolean
			return data.stop
			
		
	init
		//Create a new UnitDamageEvent
		let evnt = new UnitDamageEvent(null)
		//10% dmg buff if the target has under 400 hp
		let a1 = evnt.addAction((UnitDamageData data) -> begin
			if data.target.getHP() < 400
				data.damage *= 1.1
		end)
		//50% chance to block an attack if the source is more than 1000 units away
		let a2 = evnt.addAction((UnitDamageData data) -> begin
			if data.target.getPos().distToVec(data.source.getPos()) > 1000 and GetRandomReal(0, 100) > 50
				data.damage = 0
				//interrupt the event
				data.stop = true
		end)
		//destroy both actions to remove the effects
		destroy a1
		destroy a2
		//destroy the event
		destroy evnt
		
endpackage
 
Last edited:
Level 14
Joined
Jun 27, 2008
Messages
1,325
Some thoughts:

System
- checkData() is kinda useless as you can just use "stop(DATATYPE d)" instead, no need to have both
- Style: DATATYPE should imo be called Datatype or just something like T (its not mentioned in the Wurst Coding Conventions)
- Im not sure if "stop(DATATYPE d)" really makes sense. If one action is stopping the event some of the other actions will be evaluated and some others will not, depending on how the list is sorted. However, implementing something like a sorted list with priorities seems too complicated, im not sure how to solve this :>

Sample
- line 29: super.callActions(data), no need to use "super" keyword here
- //50% chance to block an attack if the source is more than 1000 units away, its 100% chance :p
 
Level 8
Joined
Nov 20, 2011
Messages
202
- checkData() is kinda useless as you can just use "stop(DATATYPE d)" instead, no need to have both
Well iv changed it this way:

JASS:
protected function callActions(T d)
			Action<T> buffer = first
			if checkData 
				while buffer != null
					if stop(d)
						return
					buffer.fire(d)
					buffer = buffer.next
			else
				while buffer != null
					buffer.fire(d)
					buffer = buffer.next

- Style: DATATYPE should imo be called Datatype or just something like T (its not mentioned in the Wurst Coding Conventions)
Fixed
- Im not sure if "stop(DATATYPE d)" really makes sense. If one action is stopping the event some of the other actions will be evaluated and some others will not, depending on how the list is sorted. However, implementing something like a sorted list with priorities seems too complicated, im not sure how to solve this :>

Well you are right. But i think it is a good compromise between control and performance.


- line 29: super.callActions(data), no need to use "super" keyword here
- //50% chance to block an attack if the source is more than 1000 units away, its 100% chance :p

Both fixed
 
Status
Not open for further replies.
Top