I'm trying to create a basic 4x game with bevy in the style of Aurora (or Stellaris). I decided to base the game around handling upcoming events instead of a tick system. Basically:
- There's a priority queue of upcoming events (e.g. a spaceship arriving at a star)
- When the game is updated, (e.g. by 1 day or 5 seconds), a tick event is inserted
- The game processes events until it reaches that event
Handling events requires a big system that takes &mut World because events have different requirements in terms of queries. (could use EventReader but they be out of order).
Ships are issued commands via the UI. There are two events for each command, a start type and a finish type (e.g. the start type of a GoTo command sets a ShipPosition::Travelling component and the finish type sets ShipPosition::At). Start events happen immediately.
Active commands for ships are stored and reference the relevant event so cancelled commands stop the event. Finished events need to remove the active command.
In the UI system, I need to check if it's possible to issue a command so that the button can be disabled. This is tricky because it requires borrowing the &World while still using EguiContexts etc. Not sure how to do this.
Key bits of code:
struct ActiveCommands(Slab<ActiveCommand>);
struct ActiveCommand {
command: Command,
event: u32,
}
struct Command {..}
impl Command {
fn check_conditions(self, world: &World) -> Result<(), String> {..}
// Returns the time the event finishes
fn handle_start(self, world: &mut World, ship: Entity, time: f32) -> Result<f32, String> {..}
fn handle_finish(self, world: &mut World, ship: Entity, time: f32) {..}
}
enum Event {
Tick,
FinishCommand { ship: Entity, command: usize },
StartCommand { ship: Entity, command: Command },
}
struct Events {..}
impl Events {
fn pop(&mut self) -> Option<(f32, Event)> {..}
fn cancel(&mut self, id: u32) {..}
fn push(&mut self, time: f32, event: Event) -> u32 {..}
}