-
Notifications
You must be signed in to change notification settings - Fork 0
Immediate Game API
rmx edited this page May 30, 2016
·
1 revision
This pages discusses the change from the current scheduleEvent
based global game API to an new, immediate API.
- All actions are scheduled as new events in the event queue
- Scripts (auras, behaviors) can filter and modify these events
Example:
class Fireball
finishSpellcast: (target) ->
eventSchedule 0, unitAttributeChangeBy target, 'health', -100
class Fireward
filterEvent: (e) ->
if e.origin.name is 'Fire Blast'
# Revert the damage done by fire blast
eventAppend e, unitAttributeChangeBy e.target, -e.args[2]
- Major change: all actions are immediately applied
- Minor change: Use named parameters in API calls
- Minor change: Built-in attributes like hp or mana have their own API
Example:
class Fireball
onFinishSpellcast: (target) ->
# Note how actions can have return values now
damageDealt = damageUnit {unit:target, amount:100, type:'fire'}
class Fireward
onDamageUnit: (e) ->
# Convenience function that subtracts from e.data.amount and adds to e.data.absorbed
damageAbsorbed = absorbDamage e, {amount:1000, type:'fire'}
A very rough sketch for how the damageUnit call is implemented:
damageUnit: (data) ->
event = {name:'damageUnit', data:data}
# Get a list of scripts that may filter this event, sorted by their priority
scripts = getScriptsThatMayFilterThisEvent()
scripts.sort (a, b) -> a.priority < b.priority
# Allow the scripts to modify the damage amount
for script in scripts
script.onDamageUnit? event
# Notify the client
_addCombatLogEvent event
# Apply the damage
_changeUnitHp data.unit, -data.amount
-
How should event filters look like?
- As before (i.e.,
filterEvent
)- You most likely have to check the event name
- Named after the event (i.e.,
onDamageUnit
)- Looks nice, easier to understand
- More consistent with the behavior scripts
- More difficult to implement a function that reacts to multiple events
- As before (i.e.,
-
Stack overflow
- If an event filter generates another event, the immediate evaluation can lead to a stack overflow
- This corresponds to a queue with a never-ending stream of events with delay 0 in the old system
- Possible solution: check the call stack depth in each global api call
-
Reactions and the order of combat log events
- If a direct damage triggers an immediate heal of the damaged unit (prayer of mending), how should those two events be reported and applied?
- If all actions are immediately applied, this would result in the heal being applied before the damage
- It is probably ok to immediately modify and apply the immediate effects of an action (i.e., immediately modify the unit hp in a
damageUnit
call) - It is probably not ok to immediately execute reactions to an action.
- Possible solution: use something similar to the current
eventAppend
- Possible solution: actions like
damageUnit
cannot be executed from within event filters. Instead, event filters can register reaction callbacks, that will be executed after the event is finished. Note that this would also prevent the stack overflow problem. Example:
class PrayerOfMendingAura onDamageUnit: (e) -> triggerReaction @triggerHeal triggerHeal: (e) -> healUnit {unit:e.data.unit, amount:100} removeAura {unit:e.data.unit, name:'PoM'} addAura {unit:getNearbyUnit(), name:'PoM'}
Tom: We can avoid these problems if we separate the two, absorbing incoming damage and reacting to incoming damage. Eg:
class PrayerOfMending # Called by the engine after the unit takes damage. The actual amount of damage, # how much was absorbed, resisted etc is known at this point. # We can define either specific callbacks (onDamage, onHeal, onXyz) or have a more # generic handleEvent() which will be given the same Event instance as behaviors. onDamage: (...) -> healUnit ...
class FireWard # Called by the engine when the unit is about to take damage, to allow # the script to absorb, resist etc. modifyIncomingDamage: (x) -> absorbDamage x, 20
-
Delayed actions
- Currently, none of our encounter need delayed actions (apart from stuff like the removal of expired auras or ticking auras)
- Possible solution: we just do not support it
- Use short-duration auras that do something when they expire
- Use projectiles with a flight duration that do something on impact
-
Backward compatibility
- Should we keep the two systems in parallel until we rewrite all encounters?