EventResponses
Version 1.0.1
Version 1.0.1
Requirements:
- Jass NewGen
Documentation & Script:
JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~ EventResponses ~~ By kenny! ~~ Version 1.0.1 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is EventResponses?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - EventResponses is a system that allows users to easily access
// the most common event response natives from spell and order events.
// - It was created due to the repetitive nature of writing multiple scripts,
// both for maps and for released spells and systems. The systems aims to
// reduce the amount of time spent developing these spells and systems by
// simplifying the tedious coding needed to write them.
//
// Functions:
// ¯¯¯¯¯¯¯¯¯¯¯
// - EventResp.create(boolean set2D, boolean set3D) --> returns EventResp
//
// This creates a new event response struct that stores the event response
// data for the current spell or order event. The first boolean argument is
// used to decide if you want to set the 2D distance and angle between the
// caster unit's x and y and the target x and y. The second boolean argument
// is used to determine whether or not the 3D distance and angle between the
// coordinates should be set. These are used as many spells do not need them.
//
// - EventResp.replicate(EventResp resp) --> returns EventResp
//
// This creates a new event response struct that stores the event response
// data for the current spell or order event. This function, however, copies
// a current EventResp struct and all of its data. The EventResp argument is
// the EventResp instance that you wish to copy.
//
// - .updateCaster(boolean updateZ) --> returns nothing
//
// Updates the x, y and z locations of the caster. The boolean argument is
// used to determine whether or not the z location should be updated.
//
// - .updateTarget(boolean updateZ) --> returns nothing
//
// Updates the x, y and z locations of the target. The boolean argument is
// used to determine whether or not the z location should be updated.
//
// - .updateDistances(boolean update3D) --> returns nothing
//
// Updates the distance between the caster and target coordinates. The
// boolean argument is used to determine whether or not the 3D distance
// should be updated.
//
// - .updateAngles(boolean update3D) --> returns nothing
//
// Updates the angle between the caster and target coordinates. The
// boolean argument is used to determine whether or not the 3D angle
// should be updated.
//
// - .destroy() --> returns nothing
//
// Destroys the struct holding the variables.
//
// Variables:
// ¯¯¯¯¯¯¯¯¯¯¯
// - .casterUnit --> Returns the unit that casted the spell, or the unit that
// was issued an order.
// - .targetWidg --> Returns the targeted widget of an order or spell. Does not
// return a unit, destructable or item.
// - .targetUnit --> Returns the targeted unit of an order or spell. Will return
// null if the target isn't a unit.
// - .targetDest --> Returns the targeted destructable of an order or spell.
// Will return null if the target isn't a destructable.
// - .targetItem --> Returns the targeted item of an order or spell. Will return
// null if the target isn't an item.
// - .casterX --> Returns the X coordinate of the casting unit or the ordered
// unit.
// - .casterY --> Returns the Y coordinate of the casting unit or the ordered
// unit.
// - .casterZ --> Returns the Z coordinate of the casting unit or the ordered
// unit.
// - .targetX --> Returns the X coordinate of the target of an order or
// spell. Returns .casterX for immediate orders.
// - .targetY --> Returns the Y coordinate of the target of an order or
// spell. Returns .casterY for immediate orders.
// - .targetZ --> Returns the Z coordinate of the target of an order or
// spell. Returns .casterZ for immediate orders.
// - .distXY --> Returns the distance between the .casterUnit and the target
// widget or location of an order or spell.
// - .distXYZ --> Similar to the above variable, however .distXYZ will take
// into account the Z coordinate of the caster and target.
// - .angle --> Returns the angle between the caster and the target in
// radians.
// - .pitch --> Returns the angle between the caster and the target, also
// takes into account the Z location of both.
// - .orderId --> Returns the order id of the spell or order that the caster
// was issued in integer format.
// - .orderStr --> Returns the order string of the spell or order that the
// caster was issued.
//
// Bonus Variables:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - .isTargetWidg --> Returns true if the target is a widget and false if there
// is no target (point or immediate order).
// - .isTargetUnit --> Returns true if the target is a unit and false if it is
// either a destructable or item, or if there is no target.
// - .isTargetDest --> Returns true if the target is a destructable and false if
// it is either a unit or item, or if there is no target.
// - .isTargetItem --> Returns true if the target is an item and false if it is
// either a unit or destructable, or if there is no target.
// - .isTargetLand --> Returns true if there is no target widget but still a
// target location, and false if there is a target.
// - .isTargetNone --> Returns true if there is no target widget or location and
// false if there is (works for immediate orders).
//
// Details:
// ¯¯¯¯¯¯¯¯¯
// - There are no configurables for this system, just import it and use it.
// - Aquiring the Z coordinate of units uses GetLocationZ() which can desync.
// - EventResponses will only work with some player unit and unit events,
// these include:
// - EVENT_PLAYER_UNIT_SPELL_CHANNEL
// - EVENT_PLAYER_UNIT_SPELL_CAST
// - EVENT_PLAYER_UNIT_SPELL_EFFECT
// - EVENT_PLAYER_UNIT_SPELL_FINISH
// - EVENT_PLAYER_UNIT_SPELL_ENDCAST
// - EVENT_PLAYER_UNIT_ISSUED_ORDER
// - EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER
// - EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER
// - EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER
//
// - EVENT_UNIT_SPELL_CHANNEL
// - EVENT_UNIT_SPELL_CAST
// - EVENT_UNIT_SPELL_EFFECT
// - EVENT_UNIT_SPELL_FINISH
// - EVENT_UNIT_SPELL_ENDCAST
// - EVENT_UNIT_ISSUED_ORDER
// - EVENT_UNIT_ISSUED_TARGET_ORDER
// - EVENT_UNIT_ISSUED_POINT_ORDER
// - Only these events will work as they have common native responses. Other
// Events have different responses, which would make this system messy if
// it attempted to work for all of them.
//
// How to import:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - Create a trigger named EventResponses.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// ¯¯¯¯¯¯¯¯
// - Jesus4Lyf for hopefully letting me use his method of determining
// whether or not the spell or order has a target widget or location.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library EventResponses
globals
// Only configurable option available.
private constant integer MAX_RESPONSES = 8190
// Different target types for boolean checks.
private constant integer TARGET_TYPE_NONE = 1
private constant integer TARGET_TYPE_UNIT = 2
private constant integer TARGET_TYPE_DEST = 3
private constant integer TARGET_TYPE_ITEM = 4
private constant integer TARGET_TYPE_LAND = 5
// Required for event initialisation.
private constant integer MAXIMUM_EVENTS = 17
private constant integer MAX_SPELL_EVENTS = 9
endglobals
// Struct that stores preloaded events for the system.
private struct EventId
readonly boolean isSpell = false // If it is a spell or order event.
private eventid eventId = null // The event itself.
private thistype next = 0 // Next event in the list.
private static timer tempTimer = null // Timer needed for initial registration.
private static eventid array eventIds // Array to store the used events.
// I kind of liked this interface, even though it requires a search.
static method operator[] takes eventid id returns thistype
local thistype this = thistype(0).next
// Should still technically be 0(1) complexity as it is a constant search over 17 instances.
loop
exitwhen this == 0
if id == this.eventId then
return this
endif
set this = this.next
endloop
return 0
endmethod
// Set up each event.
private static method setEvent takes eventid id, integer i returns nothing
local thistype this = thistype.allocate()
set this.eventId = id // Set the eventid.
if i <= MAX_SPELL_EVENTS then
set this.isSpell = true // The first 9 events are spell events.
endif
// Lazy.
set this.next=thistype(0).next
set thistype(0).next=this
endmethod
// Register the events at 0.00 seconds of game time (It wouldn't work otherwise).
private static method registerEvents takes nothing returns nothing
local integer i = 0
loop
exitwhen i == MAXIMUM_EVENTS
call thistype.setEvent(thistype.eventIds<i>,i)
set i = i + 1
endloop
// Clean up the temporary timer.
call PauseTimer(thistype.tempTimer)
call DestroyTimer(thistype.tempTimer)
set thistype.tempTimer = null
endmethod
// Initialise all the events used.
private static method onInit takes nothing returns nothing
// First 9 are spell events.
set thistype.eventIds[0] = EVENT_PLAYER_UNIT_SPELL_CHANNEL
set thistype.eventIds[1] = EVENT_PLAYER_UNIT_SPELL_CAST
set thistype.eventIds[2] = EVENT_PLAYER_UNIT_SPELL_EFFECT
set thistype.eventIds[3] = EVENT_PLAYER_UNIT_SPELL_FINISH
set thistype.eventIds[4] = EVENT_PLAYER_UNIT_SPELL_ENDCAST
set thistype.eventIds[5] = EVENT_UNIT_SPELL_CHANNEL
set thistype.eventIds[6] = EVENT_UNIT_SPELL_CAST
set thistype.eventIds[7] = EVENT_UNIT_SPELL_EFFECT
set thistype.eventIds[8] = EVENT_UNIT_SPELL_FINISH
set thistype.eventIds[9] = EVENT_UNIT_SPELL_ENDCAST
// The rest are order events.
set thistype.eventIds[10] = EVENT_PLAYER_UNIT_ISSUED_ORDER
set thistype.eventIds[11] = EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER
set thistype.eventIds[12] = EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER
set thistype.eventIds[13] = EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER
set thistype.eventIds[14] = EVENT_UNIT_ISSUED_ORDER
set thistype.eventIds[15] = EVENT_UNIT_ISSUED_POINT_ORDER
set thistype.eventIds[16] = EVENT_UNIT_ISSUED_TARGET_ORDER
// Start the timer.
set thistype.tempTimer = CreateTimer()
call TimerStart(thistype.tempTimer,0.00,false,function thistype.registerEvents)
endmethod
endstruct
// The exported EventResp struct.
struct EventResp[MAX_RESPONSES]
// All the accessible variables.
readonly unit casterUnit = null
readonly widget targetWidg = null
readonly unit targetUnit = null
readonly destructable targetDest = null
readonly item targetItem = null
readonly string orderStr = ""
readonly real casterX = 0.00
readonly real casterY = 0.00
readonly real casterZ = 0.00
readonly real targetX = 0.00
readonly real targetY = 0.00
readonly real targetZ = 0.00
readonly real distXY = 0.00
readonly real distXYZ = 0.00
readonly real angle = 0.00
readonly real pitch = 0.00
readonly real cosAng = 0.00
readonly real sinAng = 0.00
readonly integer orderId = 0
readonly integer targetType = 0
// Boolean checks.
method operator isTargetWidg takes nothing returns boolean
return this.targetType == TARGET_TYPE_UNIT or this.targetType == TARGET_TYPE_DEST or this.targetType == TARGET_TYPE_ITEM
endmethod
method operator isTargetUnit takes nothing returns boolean
return this.targetType == TARGET_TYPE_UNIT
endmethod
method operator isTargetDest takes nothing returns boolean
return this.targetType == TARGET_TYPE_DEST
endmethod
method operator isTargetItem takes nothing returns boolean
return this.targetType == TARGET_TYPE_ITEM
endmethod
method operator isTargetLand takes nothing returns boolean
return this.targetType == TARGET_TYPE_LAND
endmethod
method operator isTargetNone takes nothing returns boolean
return this.targetType == TARGET_TYPE_NONE
endmethod
// Clean up.
method destroy takes nothing returns nothing
set this.casterUnit = null
set this.targetWidg = null
set this.targetUnit = null
set this.targetDest = null
set this.targetItem = null
call this.deallocate()
endmethod
// Update the coordinates of the caster, with z coordinate if wanted.
method updateCaster takes boolean updateZ returns nothing
set this.casterX = GetUnitX(this.casterUnit)
set this.casterY = GetUnitY(this.casterUnit)
if updateZ then
call MoveLocation(tempLoc,this.casterX,this.casterY)
set this.casterZ = GetLocationZ(tempLoc) + GetUnitFlyHeight(this.casterUnit)
endif
endmethod
// Update the coordinates of the target, with z coordinate if wanted.
method updateTarget takes boolean updateZ returns nothing
set this.targetX = GetUnitX(this.targetUnit)
set this.targetY = GetUnitY(this.targetUnit)
if updateZ then
call MoveLocation(tempLoc,this.targetX,this.targetY)
set this.targetZ = GetLocationZ(tempLoc) + GetUnitFlyHeight(this.targetUnit)
endif
endmethod
// Update the 2D distance, and 3D if wanted.
method updateDistances takes boolean set3D returns nothing
local real x = this.targetX - this.casterX
local real y = this.targetY - this.casterY
local real z = this.targetZ - this.casterZ
set this.distXY = SquareRoot(x * x + y * y)
if set3D then
set this.distXYZ = SquareRoot(x * x + y * y + z * z)
endif
endmethod
// Update the 2D angle, and 3D if wanted.
method updateAngles takes boolean set3D returns nothing
local real x = this.targetX - this.casterX
local real y = this.targetY - this.casterY
local real z = this.targetZ - this.casterZ
set this.angle = Atan2(y,x)
set this.cosAng = Cos(this.angle)
set this.sinAng = Sin(this.angle)
if set3D then
set this.pitch = Atan2(SquareRoot(x * x + y * y),z)
endif
endmethod
// Replicate a current EventResp instance and assign it to a new instance.
static method replicate takes thistype resp returns thistype
local thistype this = thistype.allocate()
set this.casterUnit = resp.casterUnit
set this.targetWidg = resp.targetWidg
set this.targetUnit = resp.targetUnit
set this.targetDest = resp.targetDest
set this.targetItem = resp.targetItem
set this.orderStr = resp.orderStr
set this.casterX = resp.casterX
set this.casterY = resp.casterY
set this.casterZ = resp.casterZ
set this.targetX = resp.targetX
set this.targetY = resp.targetY
set this.targetZ = resp.targetZ
set this.distXY = resp.distXY
set this.distXYZ = resp.distXYZ
set this.angle = resp.angle
set this.pitch = resp.pitch
set this.cosAng = resp.cosAng
set this.sinAng = resp.sinAng
set this.orderId = resp.orderId
set this.targetType = resp.targetType
return this
endmethod
// Scabbed off Jesus4Lyf, hope you don't mind. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite7" alt=":p" title="Stick Out Tongue :p" loading="lazy" data-shortname=":p" />
//! textmacro SetTargetMembers takes TYPE, LAND
set this.targetUnit = Get$TYPE$TargetUnit()
if this.targetUnit == null then
set this.targetDest = Get$TYPE$TargetDestructable()
if this.targetDest == null then
set this.targetItem = Get$TYPE$TargetItem()
if this.targetItem == null then
set this.targetWidg = null
set this.targetX = Get$LAND$X()
set this.targetY = Get$LAND$Y()
call MoveLocation(tempLoc,this.targetX,this.targetY)
set this.targetZ = GetLocationZ(tempLoc)
if this.targetX != 0.00 or this.targetY != 0.00 or this.targetZ != 0.00 then
set this.targetType = TARGET_TYPE_LAND
else
set this.targetType = TARGET_TYPE_NONE
set this.targetX = this.casterX
set this.targetY = this.casterY
set this.targetZ = this.casterZ
endif
else
set this.targetWidg = this.targetItem
set this.targetType = TARGET_TYPE_ITEM
set this.targetX = GetItemX(this.targetItem)
set this.targetY = GetItemY(this.targetItem)
call MoveLocation(tempLoc,this.targetX,this.targetY)
set this.targetZ = GetLocationZ(tempLoc)
endif
else
set this.targetWidg = this.targetDest
set this.targetItem = null
set this.targetType = TARGET_TYPE_DEST
set this.targetX = GetDestructableX(this.targetDest)
set this.targetY = GetDestructableY(this.targetDest)
call MoveLocation(tempLoc,this.targetX,this.targetY)
set this.targetZ = GetLocationZ(tempLoc)
endif
else
set this.targetWidg = this.targetUnit
set this.targetDest = null
set this.targetItem = null
set this.targetType = TARGET_TYPE_UNIT
set this.targetX = GetUnitX(this.targetUnit)
set this.targetY = GetUnitY(this.targetUnit)
call MoveLocation(tempLoc,this.targetX,this.targetY)
set this.targetZ = GetLocationZ(tempLoc) + GetUnitFlyHeight(this.targetUnit)
endif
//! endtextmacro
// Create the event responses.
static method create takes boolean set2D, boolean set3D returns thistype
local EventId id = EventId[GetTriggerEventId()]
local thistype this = 0
local real x = 0.00
local real y = 0.00
local real z = 0.00
// If the event responses were created for an unsupported event, show an error.
if id == 0 then
debug call BJDebugMsg("|cFFFF0000Error using EventResponses:|r Unsupported event type used.")
return 0
endif
// Assign all the members.
set this = thistype.allocate()
set this.casterUnit = GetTriggerUnit()
set this.casterX = GetUnitX(this.casterUnit)
set this.casterY = GetUnitY(this.casterUnit)
call MoveLocation(thistype.tempLoc,this.casterX,this.casterY)
set this.casterZ = GetUnitFlyHeight(this.casterUnit) - GetLocationZ(thistype.tempLoc)
set this.orderId = GetIssuedOrderId()
set this.orderStr = OrderId2String(this.orderId)
if id.isSpell then
//! runtextmacro SetTargetMembers("Spell","SpellTarget")
else
//! runtextmacro SetTargetMembers("Order","OrderPoint")
endif
// Set distances and angles if required.
if set2D then
set x = this.targetX - this.casterX
set y = this.targetY - this.casterY
set z = this.targetZ - this.casterZ
set this.distXY = SquareRoot(x * x + y * y)
set this.angle = Atan2(y,x)
set this.cosAng = Cos(this.angle)
set this.sinAng = Sin(this.angle)
endif
if set3D then
set x = this.targetX - this.casterX
set y = this.targetY - this.casterY
set z = this.targetZ - this.casterZ
set this.distXYZ = SquareRoot(x * x + y * y + z * z)
set this.pitch = Atan2(SquareRoot(x * x + y * y),z)
endif
return this
endmethod
// Initialisation of the temporary loc for Z coords.
private static location tempLoc = Location(0.00,0.00)
endstruct
endlibrary
</i>
Demonstration:
JASS:
private function Actions takes nothing returns boolean
local EventResp er = EventResp.create(true,false) // Create an event response, setting 2D distance and angle, but not 3D.
// Do stuff with the event response.
if er.isTargetUnit then // Can check if the target is a unit.
if er.distXY >= 1000.00 then // No need for distance calculations.
call SetUnitX(er.casterUnit,GetUnitX(er.targetUnit) + 50.00 * er.cosAng) // No need for angle calculations.
call SetUnitY(er.casterUnit,GetUnitY(er.targetUnit) + 50.00 * er.sinAng)
call SetUnitFacing(er.casterUnit,(er.angle * bj_RADTODEG) + 180.00)
call UnitDamageTarget(er.casterUnit,er.targetUnit,100.00,false,false,ATTACK_TYPE_CHAOS,DAMAGE_TYPE_UNIVERSAL,null)
else
// Do something awesome.
endif
else
// Do some more awesome stuff.
endif
// You get the point by now.
call SetWidgetLife(er.casterUnit,GetWidgetLife(er.casterUnit + 100.00))
call DestroyEffect(AddSpecialEffectTarget("SomeCoolEffect.mdl",er.casterUnit,"origin")
call er.destroy() // Destroy the event response struct.
return false
endfunction
Notes:
So this is basically a resource that helps maintain code readability and reduce bloating of scripts. It effectively reduces time spent maintaining, editing and producing spells and systems for personal or public use.
This snippet is aimed at those who, like me, prefer code simplicity and neatness. It has the downside of being quite simple, when compared to something like SpellStruct. However, it has a simple interface and is perfect for those who think systems like the above mentioned may be a bit much for themselves to understand.
Updates:
- Version 1.0.1: Removed the onInit method, initialised the location without it.
- Version 1.0.0: Initial release.