beyond-all-reason / beyond-all-reason/RecoilEngine
Expose weapon EndBurst events to synced Lua
- Dominant language
- C++
- Stars
- 679
- Forks
- 290
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 40
Description
## Problem
Synced Lua sometimes needs to know when a weapon has completed a full burst.
The motivating case is BAR's ground-attack cycling in beyond-all-reason/Beyond-All-Reason#8773. Ground area attack is implemented in Lua by inserting a generated ground-attack command. After the weapon completes a burst, Lua needs to finish that generated target so the area command can choose another position. The same event can support explicit gameplay policies such as advancing an opted-in queued ground attack after a configured number of salvos.
The engine already knows the exact transition in `CWeapon::UpdateSalvo` and exposes it to COB/Lua unit scripts through `EndBurst`. However, synced LuaRules has no equivalent call-in. This was also identified in [review of the BAR implementation](https://github.com/beyond-all-reason/Beyond-All-Reason/pull/8773#discussion_r3853631694).
## Why projectile counting is only a workaround
The current game-side implementation watches `ProjectileCreated` and reconstructs completed bursts by counting projectiles. This can work for BAR's currently configured artillery because those weapons happen to create one projectile per shot, but a projectile is not the engine's burst-completion event.
Projectile counting requires Lua to duplicate weapon details such as burst size and associate projectile events with the correct unit and weapon. The approximation can diverge for weapons that create multiple projectiles per shot, create no projectile for a shot, use custom projectile-spawning behavior, or have a final shot cancelled. It also makes games watch and process every relevant projectile merely to infer state the weapon simulation already knows directly.
Consequently it is fragile as a general API and can silently become incorrect when weapon behavior or definitions change.
## Proposed API
Expose a synced, weaponDef-watch-gated call-in when the engine completes or terminates a burst, for example:
```lua
function gadget:UnitWeaponBurstEnd(
unitID,
unitDefID,
unitTeam,
weaponNum,
weaponDefID,
commandID,
commandTag
)
end
```
The notification should be queued at the exact point where the engine currently calls the unit-script `EndBurst`, but delivered after the simulation frame has completed. Calling arbitrary Lua inline from `CWeapon::UpdateSalvo` would allow Lua to mutate commands or units while the weapon and command AI are still on the stack.
For command-cycling consumers, `commandID` and `commandTag` should be snapshots from the instant the burst ended. Looking up the front command only during deferred delivery is racy because `WeaponFired` may already have advanced or changed the queue.
A watch API analogous to the existing weapon/projectile watch methods would avoid invoking Lua for unrelated weapons.
## References
- beyond-all-reason/Beyond-All-Reason#8773
- [Suggestion to expose the existing EndBurst event](https://github.com/beyond-all-reason/Beyond-All-Reason/pull/8773#discussion_r3853631694)
## AI usage
OpenAI Codex was used to help investigate the engine/Lua event lifecycle and draft this issue. The submitter requested the issue and is responsible for reviewing and editing it.
Contributor guide
Research direction
Start at CWeapon::UpdateSalvo and trace the existing EndBurst path into COB/Lua unit scripts, then inspect the analogous weapon/projectile watch methods. The work is done when synced LuaRules receives a watch-gated UnitWeaponBurstEnd notification after the simulation frame, with commandID and commandTag captured when the burst ends.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, lua
- Domain
- api, game-dev
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100