docs: document game phases, save system, notifications, and events (#65)

This commit is contained in:
demenik
2025-12-10 18:14:15 +01:00
parent 392b93d47b
commit 04a9125a31
13 changed files with 57 additions and 4 deletions

View File

@@ -1,5 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
/// Resource managing active notifications.
#[derive(Resource)] #[derive(Resource)]
pub struct Notifications { pub struct Notifications {
items: Vec<(Notification, NotificationLevel)>, items: Vec<(Notification, NotificationLevel)>,
@@ -44,18 +45,22 @@ impl Notifications {
} }
} }
/// Marker for the UI node containing notifications.
#[derive(Component)] #[derive(Component)]
pub struct NotificationContainer; pub struct NotificationContainer;
/// Component tracking the lifetime of a displayed notification.
#[derive(Component)] #[derive(Component)]
pub struct NotificationLifetime(pub Timer); pub struct NotificationLifetime(pub Timer);
/// Data for a single notification.
#[derive(Component)] #[derive(Component)]
pub struct Notification { pub struct Notification {
pub title: Option<String>, pub title: Option<String>,
pub message: String, pub message: String,
} }
/// Severity level of a notification.
#[derive(Component)] #[derive(Component)]
pub enum NotificationLevel { pub enum NotificationLevel {
Info, Info,

View File

@@ -7,6 +7,7 @@ use components::{NotificationContainer, NotificationLifetime, Notifications};
pub mod components; pub mod components;
pub mod ui; pub mod ui;
/// Plugin for the notification system.
pub struct NotificationPlugin; pub struct NotificationPlugin;
impl Plugin for NotificationPlugin { impl Plugin for NotificationPlugin {
@@ -17,12 +18,12 @@ impl Plugin for NotificationPlugin {
} }
} }
/// Spawns the notification container up /// Spawns the notification container up.
fn setup(mut commands: Commands) { fn setup(mut commands: Commands) {
commands.spawn(notification_container()); commands.spawn(notification_container());
} }
/// Spawns/Despawns UI elements for each item in the `Notifications` Resource /// Spawns/Despawns UI elements for each item in the `Notifications` Resource.
fn handle_notification( fn handle_notification(
mut commands: Commands, mut commands: Commands,
mut notifications: ResMut<Notifications>, mut notifications: ResMut<Notifications>,

View File

@@ -1,6 +1,7 @@
use super::components::*; use super::components::*;
use crate::prelude::*; use crate::prelude::*;
/// Creates the notification container UI bundle.
pub fn notification_container() -> impl Bundle { pub fn notification_container() -> impl Bundle {
( (
NotificationContainer, NotificationContainer,
@@ -16,6 +17,7 @@ pub fn notification_container() -> impl Bundle {
) )
} }
/// Spawns a single notification UI element.
pub fn spawn_notification( pub fn spawn_notification(
commands: &mut Commands, commands: &mut Commands,
content: Notification, content: Notification,

View File

@@ -1,6 +1,7 @@
use super::utils::format_time; use super::utils::format_time;
use crate::prelude::*; use crate::prelude::*;
/// Represents the different states of the Pomodoro timer.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Phase { pub enum Phase {
Break { duration: f32 }, Break { duration: f32 },
@@ -37,9 +38,11 @@ impl Phase {
} }
} }
/// Resource holding the current phase state.
#[derive(Resource, Debug, Serialize, Deserialize, Clone)] #[derive(Resource, Debug, Serialize, Deserialize, Clone)]
pub struct CurrentPhase(pub Phase); pub struct CurrentPhase(pub Phase);
/// Configuration for phase durations.
#[derive(Resource, Debug, Serialize, Deserialize, Clone)] #[derive(Resource, Debug, Serialize, Deserialize, Clone)]
pub struct TimerSettings { pub struct TimerSettings {
pub focus_duration: u32, pub focus_duration: u32,
@@ -59,6 +62,7 @@ impl Default for TimerSettings {
} }
} }
/// Tracks statistics for the current session.
#[derive(Resource, Debug, Default, Serialize, Deserialize, Clone)] #[derive(Resource, Debug, Default, Serialize, Deserialize, Clone)]
pub struct SessionTracker { pub struct SessionTracker {
pub completed_focus_phases: u32, pub completed_focus_phases: u32,

View File

@@ -1,12 +1,15 @@
use crate::prelude::*; use crate::prelude::*;
/// Message sent when a phase timer reaches zero.
#[derive(Message)] #[derive(Message)]
pub struct PhaseTimerFinishedMessage { pub struct PhaseTimerFinishedMessage {
pub phase: Phase, pub phase: Phase,
} }
/// Message to toggle pause state.
#[derive(Message)] #[derive(Message)]
pub struct PhaseTimerPauseMessage; pub struct PhaseTimerPauseMessage;
/// Message to proceed to the next phase.
#[derive(Message)] #[derive(Message)]
pub struct NextPhaseMessage; pub struct NextPhaseMessage;

View File

@@ -7,6 +7,7 @@ pub mod components;
pub mod messages; pub mod messages;
pub mod utils; pub mod utils;
/// Plugin managing the Pomodoro phase timer and state.
pub struct PhasePlugin; pub struct PhasePlugin;
impl Plugin for PhasePlugin { impl Plugin for PhasePlugin {
@@ -36,6 +37,7 @@ impl Plugin for PhasePlugin {
} }
} }
/// Debug system to shorten phase duration for testing.
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
fn debug_short_phase_duration( fn debug_short_phase_duration(
mut phase_res: ResMut<CurrentPhase>, mut phase_res: ResMut<CurrentPhase>,
@@ -55,6 +57,7 @@ fn debug_short_phase_duration(
} }
} }
/// Updates the current phase duration from settings.
fn load_rules(mut phase_res: ResMut<CurrentPhase>, settings: Res<TimerSettings>) { fn load_rules(mut phase_res: ResMut<CurrentPhase>, settings: Res<TimerSettings>) {
let phase = &mut phase_res.0; let phase = &mut phase_res.0;
@@ -71,6 +74,7 @@ fn load_rules(mut phase_res: ResMut<CurrentPhase>, settings: Res<TimerSettings>)
} }
} }
/// Ticks the phase timer and handles completion.
fn tick_timer( fn tick_timer(
mut commands: Commands, mut commands: Commands,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
@@ -107,7 +111,7 @@ fn tick_timer(
} }
} }
/// Rewards the player at the end of a focus phase with `berries_per_focus_minute` * `focus_duration` /// Rewards the player at the end of a focus phase with `berries_per_focus_minute` * `focus_duration`.
fn grant_focus_rewards( fn grant_focus_rewards(
mut messages: MessageReader<PhaseTimerFinishedMessage>, mut messages: MessageReader<PhaseTimerFinishedMessage>,
config: Res<GameConfig>, config: Res<GameConfig>,
@@ -147,6 +151,7 @@ fn grant_focus_rewards(
} }
} }
/// Toggles pause state of the timer.
fn handle_pause( fn handle_pause(
mut messages: MessageReader<PhaseTimerPauseMessage>, mut messages: MessageReader<PhaseTimerPauseMessage>,
mut phase_res: ResMut<CurrentPhase>, mut phase_res: ResMut<CurrentPhase>,
@@ -171,6 +176,7 @@ fn handle_pause(
} }
} }
/// Transitions to the next phase based on current state.
pub fn next_phase( pub fn next_phase(
current_phase: &mut CurrentPhase, current_phase: &mut CurrentPhase,
session_tracker: &mut SessionTracker, session_tracker: &mut SessionTracker,
@@ -204,6 +210,7 @@ pub fn next_phase(
} }
} }
/// Handles transition to the next phase after user confirmation.
pub fn handle_continue( pub fn handle_continue(
mut messages: MessageReader<NextPhaseMessage>, mut messages: MessageReader<NextPhaseMessage>,
mut phase_res: ResMut<CurrentPhase>, mut phase_res: ResMut<CurrentPhase>,

View File

@@ -1,3 +1,4 @@
/// Formats seconds into MM:SS or HH:MM:SS string.
pub fn format_time(seconds: f32) -> String { pub fn format_time(seconds: f32) -> String {
let seconds = seconds.max(0.0) as u32; let seconds = seconds.max(0.0) as u32;

View File

@@ -2,9 +2,11 @@ use crate::prelude::*;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
/// Resource containing the path to the current save file.
#[derive(Resource, Clone, Debug)] #[derive(Resource, Clone, Debug)]
pub struct SavegamePath(pub PathBuf); pub struct SavegamePath(pub PathBuf);
/// Metadata about a savegame.
#[derive(Debug)] #[derive(Debug)]
pub struct SavegameInfo { pub struct SavegameInfo {
pub path: SavegamePath, pub path: SavegamePath,
@@ -13,11 +15,13 @@ pub struct SavegameInfo {
pub completed_focus: u32, pub completed_focus: u32,
} }
/// Helper for partial JSON deserialization.
#[derive(Deserialize)] #[derive(Deserialize)]
struct PartialSaveData { struct PartialSaveData {
session_tracker: PartialSessionTracker, session_tracker: PartialSessionTracker,
} }
/// Helper for partial JSON deserialization of session stats.
#[derive(Deserialize)] #[derive(Deserialize)]
struct PartialSessionTracker { struct PartialSessionTracker {
completed_focus_phases: u32, completed_focus_phases: u32,
@@ -26,6 +30,7 @@ struct PartialSessionTracker {
} }
impl SavegamePath { impl SavegamePath {
/// Constructs a new path for a specific save index.
pub fn new(index: u32) -> Self { pub fn new(index: u32) -> Self {
let base_path = get_internal_path().unwrap_or_else(|| { let base_path = get_internal_path().unwrap_or_else(|| {
println!( println!(
@@ -41,6 +46,7 @@ impl SavegamePath {
Self(base_path.join(format!("savegame-{}.json", index))) Self(base_path.join(format!("savegame-{}.json", index)))
} }
/// Lists all available savegames.
pub fn list() -> Vec<SavegameInfo> { pub fn list() -> Vec<SavegameInfo> {
let mut savegames = Vec::new(); let mut savegames = Vec::new();
@@ -88,6 +94,7 @@ impl SavegamePath {
savegames savegames
} }
/// Returns a path for a new savegame (incremented index).
pub fn next() -> Self { pub fn next() -> Self {
let savegames = Self::list(); let savegames = Self::list();
let next_index = savegames.last().map(|s| s.index + 1).unwrap_or(0); let next_index = savegames.last().map(|s| s.index + 1).unwrap_or(0);
@@ -95,11 +102,13 @@ impl SavegamePath {
} }
} }
/// Markers for savegame UI.
#[derive(Component)] #[derive(Component)]
pub enum RootMarker { pub enum RootMarker {
PopupSavegameLoad, PopupSavegameLoad,
} }
/// Buttons for savegame management.
#[derive(Component)] #[derive(Component)]
pub enum ButtonType { pub enum ButtonType {
SavegameLoad { savegame_path: SavegamePath }, SavegameLoad { savegame_path: SavegamePath },

View File

@@ -1,7 +1,9 @@
use crate::prelude::*; use crate::prelude::*;
/// Trigger to save the current game.
#[derive(Message)] #[derive(Message)]
pub struct SavegameDumpMessage; pub struct SavegameDumpMessage;
/// Trigger to load a game from disk.
#[derive(Message)] #[derive(Message)]
pub struct SavegameLoadMessage; pub struct SavegameLoadMessage;

View File

@@ -10,6 +10,7 @@ pub mod components;
pub mod messages; pub mod messages;
pub mod ui; pub mod ui;
/// Plugin dealing with savegame loading and saving.
pub struct SavegamePlugin; pub struct SavegamePlugin;
impl Plugin for SavegamePlugin { impl Plugin for SavegamePlugin {
@@ -25,6 +26,7 @@ impl Plugin for SavegamePlugin {
} }
} }
/// The structure of a save file.
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct SaveData { struct SaveData {
grid_width: u32, grid_width: u32,
@@ -37,6 +39,7 @@ struct SaveData {
inventory: Vec<ItemStack>, inventory: Vec<ItemStack>,
} }
/// Serializes game state and writes it to a file.
fn dump_savegame( fn dump_savegame(
mut messages: MessageReader<SavegameDumpMessage>, mut messages: MessageReader<SavegameDumpMessage>,
save_path: Res<SavegamePath>, save_path: Res<SavegamePath>,
@@ -106,6 +109,7 @@ fn dump_savegame(
} }
} }
/// Reads a save file and restores game state.
fn load_savegame( fn load_savegame(
mut commands: Commands, mut commands: Commands,
mut messages: MessageReader<SavegameLoadMessage>, mut messages: MessageReader<SavegameLoadMessage>,
@@ -174,7 +178,7 @@ fn load_savegame(
} }
} }
/// Resets all components/resources loaded by `load_savegame` /// Resets all components/resources loaded by `load_savegame`.
fn reset_savegame( fn reset_savegame(
mut commands: Commands, mut commands: Commands,
grid: Res<Grid>, grid: Res<Grid>,

View File

@@ -1,6 +1,7 @@
use super::super::components::{ButtonType, RootMarker}; use super::super::components::{ButtonType, RootMarker};
use crate::{features::savegame::messages::SavegameLoadMessage, prelude::*}; use crate::{features::savegame::messages::SavegameLoadMessage, prelude::*};
/// Spawns the "Load Game" popup.
pub fn spawn_load_popup(commands: &mut Commands) { pub fn spawn_load_popup(commands: &mut Commands) {
spawn_popup( spawn_popup(
commands, commands,
@@ -88,6 +89,7 @@ pub fn spawn_load_popup(commands: &mut Commands) {
); );
} }
/// Handles interactions in the load popup.
pub fn load_popup_handler( pub fn load_popup_handler(
mut commands: Commands, mut commands: Commands,
mut next_state: ResMut<NextState<AppState>>, mut next_state: ResMut<NextState<AppState>>,

View File

@@ -1,5 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
/// Defines the bounds of the grid for the server.
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct MaxFieldSize { pub struct MaxFieldSize {
#[serde(rename = "minX")] #[serde(rename = "minX")]
@@ -12,12 +13,14 @@ pub struct MaxFieldSize {
pub max_y: u32, pub max_y: u32,
} }
/// Coordinates for a wonder event target.
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct Position { pub struct Position {
pub x: u32, pub x: u32,
pub y: u32, pub y: u32,
} }
/// WebSocket messages exchanged with the wonder event server.
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
#[serde(tag = "messageType")] #[serde(tag = "messageType")]
pub enum WonderEventMessage { pub enum WonderEventMessage {

View File

@@ -9,20 +9,25 @@ use url::Url;
pub mod components; pub mod components;
/// Plugin managing random wonder events via WebSocket.
pub struct WonderEventPlugin; pub struct WonderEventPlugin;
/// Message to initiate a wonder event request.
#[derive(Message)] #[derive(Message)]
pub struct RequestWonderEvent { pub struct RequestWonderEvent {
pub url_str: String, pub url_str: String,
pub max_field_size: MaxFieldSize, pub max_field_size: MaxFieldSize,
} }
/// Resource holding the channel sender for wonder events.
#[derive(Resource)] #[derive(Resource)]
pub struct WonderEventSender(pub Sender<WonderEventMessage>); pub struct WonderEventSender(pub Sender<WonderEventMessage>);
/// Resource holding the channel receiver for wonder events.
#[derive(Resource)] #[derive(Resource)]
pub struct WonderEventReceiver(pub Mutex<Receiver<WonderEventMessage>>); pub struct WonderEventReceiver(pub Mutex<Receiver<WonderEventMessage>>);
/// Component for animated floating text effects.
#[derive(Component)] #[derive(Component)]
struct FloatingText { struct FloatingText {
timer: Timer, timer: Timer,
@@ -49,11 +54,13 @@ impl Plugin for WonderEventPlugin {
} }
} }
/// Tracks if an event has already triggered this phase.
#[derive(Resource, Default)] #[derive(Resource, Default)]
struct WonderEventState { struct WonderEventState {
triggered_for_current_phase: bool, triggered_for_current_phase: bool,
} }
/// Checks if the focus phase is halfway through to trigger an event.
fn check_halfway_focus( fn check_halfway_focus(
mut state: ResMut<WonderEventState>, mut state: ResMut<WonderEventState>,
phase_res: Res<CurrentPhase>, phase_res: Res<CurrentPhase>,
@@ -95,6 +102,7 @@ fn check_halfway_focus(
} }
} }
/// Spawns a thread to request a wonder event from the server.
fn handle_wonder_event_trigger( fn handle_wonder_event_trigger(
mut events: MessageReader<RequestWonderEvent>, mut events: MessageReader<RequestWonderEvent>,
sender: Res<WonderEventSender>, sender: Res<WonderEventSender>,
@@ -159,6 +167,7 @@ fn handle_wonder_event_trigger(
} }
} }
/// Processes responses from the wonder event server.
fn handle_wonder_event_response( fn handle_wonder_event_response(
receiver: Res<WonderEventReceiver>, receiver: Res<WonderEventReceiver>,
grid: Res<Grid>, grid: Res<Grid>,
@@ -241,6 +250,7 @@ fn handle_wonder_event_response(
} }
} }
/// Updates floating text positions and lifetimes.
fn animate_floating_text( fn animate_floating_text(
mut commands: Commands, mut commands: Commands,
time: Res<Time>, time: Res<Time>,