I was sick and tired of writing the same things over and over again for spells that need to store a casting unit, its coordinates, a target unit, its coordinates, the distance between them, the angle between them and all that kind of stuff, so I wrote this:
I actually wrote this a while ago, but I re did it today.
Example:
Limits:
- Only works for a set number of events (the most common events used). This limitation was set in place because other events have different native responses, while spell and order events are quite similar.
So yeah, tell me what you think and why.
JASS:
library EventResponses
globals
// Different target types for boolean checks.
private constant integer TARGET_TYPE_NONE = 0
private constant integer TARGET_TYPE_UNIT = 1
private constant integer TARGET_TYPE_DEST = 2
private constant integer TARGET_TYPE_ITEM = 3
private constant integer TARGET_TYPE_LAND = 4
endglobals
// My attempt at not using GetHandleId().
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.
static method operator[] takes eventid id returns thistype
local thistype this = thistype(0).next
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 <= 9 then
set this.isSpell = true // The first 9 events are spell events.
endif
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 == 17
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
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
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
// 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 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
private integer targetType = 0
private static location tempLoc = null
// Boolean checks.
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
implement optional EventRespOptions
// 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 members if needed.
method update takes boolean updateCaster returns nothing
local real x = 0.00
local real y = 0.00
local real z = 0.00
// If you want to update the initial casting position, you can.
if updateCaster then
set this.casterX = GetUnitX(this.casterUnit)
set this.casterY = GetUnitY(this.casterUnit)
call MoveLocation(thistype.tempLoc,this.casterX,this.casterY)
set this.casterZ = GetLocationZ(thistype.tempLoc) + GetUnitFlyHeight(this.casterUnit)
endif
// Only units can move, so we only update the coordinates for them.
if this.isTargetUnit then
set this.targetX = GetUnitX(this.targetUnit)
set this.targetY = GetUnitY(this.targetUnit)
call MoveLocation(thistype.tempLoc,this.targetX,this.targetY)
set this.targetZ = GetLocationZ(thistype.tempLoc) + GetUnitFlyHeight(this.targetUnit)
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.distXYZ = SquareRoot(x * x + y * y + z * z)
set this.angle = Atan2(y,x)
set this.pitch = Atan2(SquareRoot(x * x + y * y),z)
endif
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(thistype.tempLoc,this.targetX,this.targetY)
set this.targetZ = GetLocationZ(thistype.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(thistype.tempLoc,this.targetX,this.targetY)
set this.targetZ = GetLocationZ(thistype.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(thistype.tempLoc,this.targetX,this.targetY)
set this.targetZ = GetLocationZ(thistype.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(thistype.tempLoc,this.targetX,this.targetY)
set this.targetZ = GetLocationZ(thistype.tempLoc) + GetUnitFlyHeight(this.targetUnit)
endif
//! endtextmacro
// Create the event responses.
static method create takes nothing 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 Invalid event type.")
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 = GetLocationZ(thistype.tempLoc) + GetUnitFlyHeight(this.casterUnit)
if id.isSpell then
//! runtextmacro SetTargetMembers("Spell","SpellTarget")
else
//! runtextmacro SetTargetMembers("Order","OrderPoint")
endif
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.distXYZ = SquareRoot(x * x + y * y + z * z)
set this.angle = Atan2(y,x)
set this.pitch = Atan2(SquareRoot(x * x + y * y),z)
return this
endmethod
private static method onInit takes nothing returns nothing
set thistype.tempLoc = Location(0.00,0.00)
endmethod
endstruct
endlibrary
</i>
I actually wrote this a while ago, but I re did it today.
Example:
JASS:
scope Example initializer Init
globals
private constant integer ABIL_ID = 039;A000039;
endglobals
private function Actions takes nothing returns nothing
local EventResp er = EventResp.create()
if er.isTargetUnit then
call UnitDamageTarget(er.casterUnit,er.targetUnit,200.00,false,false,ATTACK_TYPE_CHAOS,DAMAGE_TYPE_UNIVERSAL,null)
call DestroyEffect(AddSpecialEffect("SomeEffect.mdl",er.targetX,er.targetY))
call SetWidgetLife(er.casterUnit,GetWidgetLife(er.casterUnit) + 200.00)
call DestroyEffect(AddSpecialEffect("SomeOtherEffect.mdl",er.casterX,er.casterY))
elseif er.isTargetLand then
call SetUnitX(er.casterUnit,er.targetX)
call SetUnitY(er.casterUnit,er.targetY)
call DestroyEffect(AddSpecialEffect("SomeOtherOtherEffect.mdl",er.targetX,er.targetY))
endif
// Do more stuff here.
call er.destroy()
endfunction
private function Conditions takes nothing returns boolean
if GetSpellAbilityId() == ABIL_ID then
call Actions()
endif
endfunction
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(trig,Condition(function Conditions)
endfunction
endscope
Limits:
- Only works for a set number of events (the most common events used). This limitation was set in place because other events have different native responses, while spell and order events are quite similar.
So yeah, tell me what you think and why.