Skip to content

Commit

Permalink
Augment event system to allow firing events
Browse files Browse the repository at this point in the history
  • Loading branch information
danhunsaker committed Apr 2, 2024
1 parent d434e54 commit 5156881
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 43 deletions.
3 changes: 3 additions & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- [About](./evac.md)
- [Setup](./evac/setup.md)
- [Usage](./evac/usage.md)
- [Events](./evac/events.md)
- [Functions](./evac/functions.md)
- [Groups](./evac/functions/groups.md)
- [Units](./evac/functions/units.md)
Expand All @@ -29,12 +30,14 @@
- [About](./urgency.md)
- [Setup](./urgency/setup.md)
- [Usage](./urgency/usage.md)
- [Events](./urgency/events.md)

# Gremlin Waves

- [About](./waves.md)
- [Setup](./waves/setup.md)
- [Usage](./waves/usage.md)
- [Events](./waves/events.md)

# Book-Keeping

Expand Down
56 changes: 56 additions & 0 deletions docs/evac/events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!-- markdownlint-disable MD041 -->
## Events

- `Evac:BeaconDead`
- `details` - the beacon details table used internally
- Fired when a beacon dies and gets removed from the map

- `Evac:BeaconSpawn`
- `details` - the beacon details table used internally
- `zone` - The zone the beacon was spawned into
- Fired when a beacon spawns and gets added to the map

- `Evac:Loss`
- `side` - The numeric ID of the coalition that lost
- Fired when any side loses too many evacuees

- `Evac:Spawned`
- `units` - The number of evacuees spawned
- `zone` - The zone in which evacuees were spawned
- Fired when evacuees spawn into the map

- `Evac:UnitLoaded`
- `number` - The number of evacuees loaded
- `unit` - The unit that did the loading
- `zone` - The zone the evacuees were picked up from
- Fired when evacuees are finished loading onto an evacuation unit

- `Evac:UnitUnloaded`
- `number` - The number of evacuees unloaded
- `unit` - The unit that did the unloading
- `zone` - The zone the evacuees were dropped off
- Fired when evacuees are finished unloading from an evacuation unit

- `Evac:Win`
- `side` - The numeric ID of the coalition that won
- Fired when any side rescues enough evacuees

- `Evac:ZoneActive`
- `mode` - The Evac.modes integer dictating how evacuees should be handled in the zone
- `zone` - The affected zone
- Fired when a zone goes active

- `Evac:ZoneAdd`
- `mode` - The Evac.modes integer dictating how evacuees should be handled in the zone
- `zone` - The affected zone
- Fired when a zone is added to Evac

- `Evac:ZoneInactive`
- `mode` - The Evac.modes integer dictating how evacuees should be handled in the zone
- `zone` - The affected zone
- Fired when a zone goes inactive

- `Evac:ZoneRemove`
- `mode` - The Evac.modes integer dictating how evacuees should be handled in the zone
- `zone` - The affected zone
- Fired when a zone is removed from Evac
6 changes: 6 additions & 0 deletions docs/gremlin/functions/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ This is an improved interface that avoids calling functions that don't handle ce
#### `Gremlin.events.off(_eventId, _index)`

Stops calling the `_eventId` handler whose `_index` is given; the only safe way to stop listening for events.

#### `Gremlin.events.fire(_event)`

Another improvement on the DCS event system, this lets you fire off your own events. In fact, the official Gremlin scripts use this to provide support for events in their own code.

> Note: you can only receive these custom-fired events if you register an event handler using `Gremlin.events.on()`!
20 changes: 14 additions & 6 deletions docs/gremlin/functions/utils.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
<!-- markdownlint-disable MD041 -->
### Utils

#### `Gremlin.utils.countTableEntries(_tbl)`

Counts the number of entries in a table, regardless of type

---

#### `Gremlin.utils.displayMessageTo(_name, _text, _time)`

Displays a message to a named Unit, Group, Country, or Coalition, or to everyone with the special name `all`

---

#### `Gremlin.utils.getUnitZones(_unit_)`

Looks up a list of all the zones a unit is currently in

---

#### `Gremlin.utils.parseFuncArgs(_args, _objs)`

Preps arguments for things like scheduled functions, with some limited autocompletion of Units and Groups. Use a string placeholder for these autocompletions that meets one of the following criteria:
Expand All @@ -18,12 +30,8 @@ For `_objs`, simply pass a table with the appropriate structure:

```lua
{
unit = {
[unitName] = Unit.getByName(unitName),
},
group = {
[groupName] = Group.getByName(groupName),
},
unit = Unit.getByName(unitName),
group = Group.getByName(groupName),
}
```

Expand Down
16 changes: 16 additions & 0 deletions docs/urgency/events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!-- markdownlint-disable MD041 -->
## Events

- `Urgency:CountdownEnd`
- `name` - The name of the countdown that just ended
- Fired when a countdown ends

- `Urgency:CountdownStart`
- `name` - The name of the countdown that just started
- Fired when a countdown begins

- `Urgency:CountdownsReset`
- Fired when an admin pilot resets all active countdowns to pending

- `Urgency:CountdownsRestored`
- Fired when an admin pilot restores all countdowns so they will start over
17 changes: 17 additions & 0 deletions docs/waves/events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!-- markdownlint-disable MD041 -->
## Events

- `Waves:GroupSpawn`
- `wave` - The name of the wave that is spawning groups
- `group` - The name of the group that spawned in this wave
- Fired when a group is spawned as part of a wave

- `Waves:Paused`
- Fired when an admin pilot pauses all pending waves

- `Waves:Resumed`
- Fired when an admin pilot resumes all pending waves

- `Waves:WaveSpawn`
- `wave` - The name of the wave that spawned
- Fired when a wave is fully spawned
17 changes: 15 additions & 2 deletions src/evac.lua
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,6 @@ Evac.groups = {
}

mist.dynAdd(_group)
-- trigger.action.activateGroup(_group.name)
-- mist.teleportInZone(_group.name, _zone, true, _scatterRadius)

for _, _unit in pairs(_groupData.units) do
Evac._state.extractableNow[_zone][_unit.unitName] = _unit
Expand Down Expand Up @@ -578,6 +576,7 @@ Evac._internal.aircraft = {

Gremlin.log.debug(Evac.Id, string.format('Loading Evacuees : Sending %s to %s', _message, tostring(_unit)))

Gremlin.events.fire({ id = 'Evac:UnitLoaded', zone = _zone, unit = _unit, number = _number })
Gremlin.utils.displayMessageTo(_unit, _message, _displayFor)
end, {_timeNow + 0.01}, _timeNow + 0.01, 1, _timeNow + _timeout + 0.02)
end,
Expand Down Expand Up @@ -697,6 +696,7 @@ Evac._internal.aircraft = {

Gremlin.log.debug(Evac.Id, string.format('Unloading Evacuees : Sending %s to %s', _message, tostring(_unit)))

Gremlin.events.fire({ id = 'Evac:UnitUnloaded', zone = _zone, unit = _unit, number = _number })
Gremlin.utils.displayMessageTo(_unit, _message, _displayFor)
Evac._internal.utils.endIfEnoughGotOut()
end, { _timeNow + 0.01 }, _timeNow + 0.01, 1, _timeNow + _timeout + 0.02)
Expand Down Expand Up @@ -775,6 +775,8 @@ Evac._internal.beacons = {
Evac._internal.beacons.update(_beaconDetails)
table.insert(Evac._state.beacons, _beaconDetails)

Gremlin.events.fire({ id = 'Evac:BeaconSpawn', zone = _zone, details = _beaconDetails })

return _beaconDetails
end,
getFreeADFFrequencies = function()
Expand Down Expand Up @@ -854,6 +856,7 @@ Evac._internal.beacons = {
trigger.action.stopRadioTransmission(_beaconDetails.text .. ' | FM')
_radioGroup:destroy()

Gremlin.events.fire({ id = 'Evac:BeaconDead', details = _beaconDetails })
return false
end

Expand Down Expand Up @@ -899,6 +902,7 @@ Evac._internal.beacons = {
end
end

Gremlin.events.fire({ id = 'Evac:BeaconDead', details = _beaconDetails })
table.remove(Evac._state.beacons, _index)
end
end
Expand Down Expand Up @@ -1044,6 +1048,7 @@ Evac._internal.zones = {
mode = _evacMode
}
Evac._state.extractableNow[_zone] = {}
Gremlin.events.fire({ id = 'Evac:ZoneAdd', zone = _zone, mode = _evacMode })
end,
generateEvacuees = function(_side, _numberOrComposition, _country)
if type(_numberOrComposition) == 'table' then
Expand Down Expand Up @@ -1106,6 +1111,7 @@ Evac._internal.zones = {
Evac._state.zones[Evac.modeToText[_evacMode]] ~= nil and
Evac._state.zones[Evac.modeToText[_evacMode]][_zone] ~= nil
then
Gremlin.events.fire({ id = 'Evac:ZoneActive', zone = _zone, mode = _evacMode })
Evac._state.zones[Evac.modeToText[_evacMode]][_zone].active = true
end
end,
Expand Down Expand Up @@ -1179,6 +1185,7 @@ Evac._internal.zones = {
deactivate = function(_zone, _evacMode)
Gremlin.log.trace(Evac.Id, string.format('Deactivating Zone Internally : %s, %i', _zone, _evacMode))

Gremlin.events.fire({ id = 'Evac:ZoneInactive', zone = _zone, mode = _evacMode })
Evac._state.zones[Evac.modeToText[_evacMode]][_zone].active = false
end,
unregister = function(_zone, _evacMode)
Expand All @@ -1191,6 +1198,7 @@ Evac._internal.zones = {
end
end

Gremlin.events.fire({ id = 'Evac:ZoneRemove', zone = _zone, mode = _evacMode })
Evac._state.extractableNow[_zone] = nil
Evac._state.zones[Evac.modeToText[_evacMode]][_zone] = nil
end
Expand Down Expand Up @@ -1281,6 +1289,7 @@ Evac._internal.utils = {

if (_combined > 0 and _combined >= (Evac.winThresholds[_side] / 100)) or
(_pilot > 0 and _pilot >= (Evac.winThresholds[_side] / 100)) then
Gremlin.events.fire({ id = 'Evac:Win', side = _side })
trigger.action.setUserFlag(Evac.winFlags[_side], true)
end
end
Expand All @@ -1302,6 +1311,7 @@ Evac._internal.utils = {

if (_combined > 0 and _combined >= (Evac.lossThresholds[_side] / 100)) or
(_pilot > 0 and _pilot >= (Evac.lossThresholds[_side] / 100)) then
Gremlin.events.fire({ id = 'Evac:Loss', side = _side })
trigger.action.setUserFlag(Evac.lossFlags[_side], true)
end
end
Expand Down Expand Up @@ -1520,14 +1530,17 @@ Evac._internal.doSpawns = function()
_addedUnits = true
end

Gremlin.events.fire({ id = 'Evac:Spawned', units = -_removed, zone = _zone })
Gremlin.log.debug(Evac.Id, string.format('Removed %i evacuees from %s', _removed, _zone))
else
Evac.groups.spawn(_side, _units, _side, _zone, 5)
_addedUnits = true

if type(_units) == 'table' then
Gremlin.events.fire({ id = 'Evac:Spawned', units = #_units, zone = _zone })
Gremlin.log.debug(Evac.Id, string.format('Spawned %i evacuees in %s', #_units, _zone))
else
Gremlin.events.fire({ id = 'Evac:Spawned', units = _units, zone = _zone })
Gremlin.log.debug(Evac.Id, string.format('Spawned %i evacuees in %s', _units, _zone))
end
end
Expand Down
10 changes: 6 additions & 4 deletions src/gremlin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Gremlin = {
enabled = false,
event = -1,
fn = function(_event)
Gremlin.log.debug(Gremlin.Id, string.format('%s: %s\n', Gremlin.events.idToName[_event.id], mist.utils.tableShowSorted(_event)))
Gremlin.log.debug(Gremlin.Id, string.format('%s: %s\n', Gremlin.events.idToName[_event.id] or _event.id, mist.utils.tableShowSorted(_event)))
end
},
},
Expand All @@ -55,12 +55,14 @@ Gremlin = {
Gremlin.events._handlers[_eventId][_index] = nil
end
end,
fire = function(_event)
Gremlin.events._handler(_event)
end,
_handler = function(_event)
for _, _handler in pairs(Gremlin.utils.mergeTables(Gremlin.events._handlers[_event.id] or {},
Gremlin.events._handlers[-1] or {})) do
for _, _handler in pairs(Gremlin.utils.mergeTables(Gremlin.events._handlers[_event.id] or {}, Gremlin.events._handlers[-1] or {})) do
_handler(_event)
end
end
end,
},
log = {
error = function(toolId, message)
Expand Down
7 changes: 5 additions & 2 deletions src/urgency.lua
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ Urgency._internal.startCountdown = function(_name)
_countdown.startedAt = _now
Urgency._state.countdowns.active[_name] = _countdown
Urgency._state.countdowns.pending[_name] = nil
Gremlin.events.fire({ id = 'Urgency:CountdownStart', name = _name })
trigger.action.setUserFlag(_countdown.startFlag, true)
trigger.action.setUserFlag(_countdown.endFlag, false)
end
Expand All @@ -164,6 +165,7 @@ Urgency._internal.endCountdown = function(_name)
Urgency._state.countdowns.done[_name] = _countdown
end
Urgency._state.countdowns.active[_name] = nil
Gremlin.events.fire({ id = 'Urgency:CountdownEnd', name = _name })
trigger.action.setUserFlag(_countdown.startFlag, false)
trigger.action.setUserFlag(_countdown.endFlag, true)
end
Expand All @@ -183,6 +185,7 @@ Urgency._internal.resetCountdowns = function()
trigger.action.setUserFlag(_countdown.endFlag, false)
end

Gremlin.events.fire({ id = 'Urgency:CountdownsReset' })
Gremlin.log.trace(Urgency.Id, string.format('Active Countdowns Reset'))
end

Expand All @@ -199,6 +202,7 @@ Urgency._internal.restoreCountdowns = function()
trigger.action.setUserFlag(_countdown.endFlag, false)
end

Gremlin.events.fire({ id = 'Urgency:CountdownsRestored' })
Gremlin.log.trace(Urgency.Id, string.format('Restored Configured Countdowns'))
end

Expand All @@ -221,8 +225,7 @@ Urgency._internal.handlers = {
eventTriggers = {
event = -1,
fn = function(_event)
Gremlin.log.trace(Urgency.Id,
string.format('Checking Event Against Countdowns : %s', Gremlin.events.idToName[_event.id]))
Gremlin.log.trace(Urgency.Id, string.format('Checking Event Against Countdowns : %s', Gremlin.events.idToName[_event.id] or _event.id))

for _name, _countdown in pairs(Urgency._state.countdowns.pending) do
if _countdown.startTrigger.type == 'event'
Expand Down
7 changes: 5 additions & 2 deletions src/waves.lua
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Waves._internal.spawnWave = function(_name, _wave)
x = _pos3.x,
y = _pos3.z,
})
Gremlin.events.fire({ id = 'Waves:GroupSpawn', wave = _name, group = _groupName })

-- Apparently, ships in particular don't like having their AI messed with.
-- We'll leave them be just following their routes.
Expand Down Expand Up @@ -114,6 +115,7 @@ Waves._internal.spawnWave = function(_name, _wave)
end
end

Gremlin.events.fire({ id = 'Waves:WaveSpawn', wave = _name })
Gremlin.log.trace(Waves.Id, string.format('Finished Spawning Wave : %s', _name))
end

Expand Down Expand Up @@ -206,13 +208,15 @@ end
Waves._internal.pause = function()
Gremlin.log.trace(Waves.Id, string.format('Pausing Reinforcement Waves'))

Gremlin.events.fire({ id = 'Waves:Paused' })
Waves._state.paused = true
Gremlin.menu.updateF10(Waves.Id, Waves._internal.menu, Waves._internal.getAdminUnits())
end

Waves._internal.unpause = function()
Gremlin.log.trace(Waves.Id, string.format('Releasing Pending Reinforcement Waves'))

Gremlin.events.fire({ id = 'Waves:Resumed' })
Waves._state.paused = false
Gremlin.menu.updateF10(Waves.Id, Waves._internal.menu, Waves._internal.getAdminUnits())
end
Expand Down Expand Up @@ -247,8 +251,7 @@ Waves._internal.handlers = {
event = -1,
fn = function(_event)
if not Waves._state.paused then
Gremlin.log.trace(Waves.Id,
string.format('Checking Event Against Waves : %s', Gremlin.events.idToName[_event.id]))
Gremlin.log.trace(Waves.Id, string.format('Checking Event Against Waves : %s', Gremlin.events.idToName[_event.id] or _event.id))

for _name, _wave in pairs(Waves.config.waves) do
if _wave.trigger.type == 'event'
Expand Down
Loading

0 comments on commit 5156881

Please sign in to comment.