Merge branch '53-refactor-project-structure' into 'dev'

Refactor project structure

See merge request softwaregrundprojekt/2025-2026/einzelprojekt/tutorium-moritz/bernroider-dominik/bernroider-dominik!11
This commit is contained in:
Dominik Bernroider
2025-11-26 19:15:33 +00:00
35 changed files with 179 additions and 202 deletions

View File

@@ -1,5 +0,0 @@
pub mod phase;
pub mod pom;
pub mod savegame;
pub mod tile;
pub mod ui;

View File

@@ -1,5 +1,4 @@
use bevy::prelude::*; use crate::prelude::*;
use serde::Deserialize;
use std::fs::File; use std::fs::File;
use std::io::BufReader; use std::io::BufReader;
@@ -20,8 +19,10 @@ impl Default for GameConfig {
} }
} }
pub fn read_config() -> Option<GameConfig> { impl GameConfig {
pub fn read_config() -> Option<Self> {
let file = File::open("assets/config.json").ok()?; let file = File::open("assets/config.json").ok()?;
let reader = BufReader::new(file); let reader = BufReader::new(file);
serde_json::from_reader(reader).ok() serde_json::from_reader(reader).ok()
}
} }

View File

@@ -0,0 +1 @@
pub mod components;

View File

@@ -1,5 +1,6 @@
use crate::states::*; use crate::prelude::*;
use bevy::prelude::*;
pub mod states;
pub struct CorePlugin; pub struct CorePlugin;

View File

@@ -0,0 +1,8 @@
use crate::prelude::*;
#[derive(States, Clone, PartialEq, Eq, Debug, Hash, Default)]
pub enum AppState {
#[default]
StartScreen,
GameScreen,
}

View File

@@ -1,5 +1,4 @@
use crate::states::*; use crate::prelude::*;
use bevy::prelude::*;
pub struct GameScreenPlugin; pub struct GameScreenPlugin;

View File

@@ -1,7 +1,5 @@
use bevy::prelude::*; use super::errors::GridError;
use serde::{Deserialize, Serialize}; use crate::prelude::*;
use crate::errors::GridError;
#[derive(Component)] #[derive(Component)]
pub struct Tile { pub struct Tile {

View File

@@ -0,0 +1 @@
pub const TILE_SIZE: f32 = 32.0;

View File

@@ -1,19 +1,9 @@
use crate::{ use crate::prelude::*;
components::tile::{Grid, Tile, TileState},
config::GameConfig,
states::AppState,
};
use bevy::prelude::*;
use bevy_aseprite_ultra::prelude::AseSlice;
pub const TILE_SIZE: f32 = 32.0; pub mod components;
pub mod consts;
pub fn grid_start_x(grid_width: u32) -> f32 { pub mod errors;
-(grid_width as f32 * TILE_SIZE) / 2.0 + TILE_SIZE / 2.0 pub mod utils;
}
pub fn grid_start_y(grid_height: u32) -> f32 {
-(grid_height as f32 * TILE_SIZE) / 2.0 + TILE_SIZE / 2.0
}
pub struct GridPlugin; pub struct GridPlugin;
@@ -94,37 +84,3 @@ fn update_tile_colors(
}; };
} }
} }
pub fn world_to_grid_coords(world_pos: Vec3, grid_width: u32, grid_height: u32) -> (u32, u32) {
let start_x = grid_start_x(grid_width);
let start_y = grid_start_y(grid_height);
let x = ((world_pos.x - start_x + TILE_SIZE / 2.0) / TILE_SIZE).floor();
let y = ((world_pos.y - start_y + TILE_SIZE / 2.0) / TILE_SIZE).floor();
let mut x_u32 = x as u32;
let mut y_u32 = y as u32;
if x_u32 >= grid_width {
x_u32 = grid_width - 1;
}
if y_u32 >= grid_height {
y_u32 = grid_height - 1;
}
(x_u32, y_u32)
}
pub fn grid_to_world_coords(
grid_x: u32,
grid_y: u32,
z: Option<f32>,
grid_width: u32,
grid_height: u32,
) -> Vec3 {
Vec3::new(
grid_start_x(grid_width) + grid_x as f32 * TILE_SIZE,
grid_start_y(grid_height) + grid_y as f32 * TILE_SIZE,
z.unwrap_or(0.0),
)
}

View File

@@ -0,0 +1,43 @@
use crate::prelude::*;
pub fn grid_start_x(grid_width: u32) -> f32 {
-(grid_width as f32 * TILE_SIZE) / 2.0 + TILE_SIZE / 2.0
}
pub fn grid_start_y(grid_height: u32) -> f32 {
-(grid_height as f32 * TILE_SIZE) / 2.0 + TILE_SIZE / 2.0
}
pub fn world_to_grid_coords(world_pos: Vec3, grid_width: u32, grid_height: u32) -> (u32, u32) {
let start_x = grid_start_x(grid_width);
let start_y = grid_start_y(grid_height);
let x = ((world_pos.x - start_x + TILE_SIZE / 2.0) / TILE_SIZE).floor();
let y = ((world_pos.y - start_y + TILE_SIZE / 2.0) / TILE_SIZE).floor();
let mut x_u32 = x as u32;
let mut y_u32 = y as u32;
if x_u32 >= grid_width {
x_u32 = grid_width - 1;
}
if y_u32 >= grid_height {
y_u32 = grid_height - 1;
}
(x_u32, y_u32)
}
pub fn grid_to_world_coords(
grid_x: u32,
grid_y: u32,
z: Option<f32>,
grid_width: u32,
grid_height: u32,
) -> Vec3 {
Vec3::new(
grid_start_x(grid_width) + grid_x as f32 * TILE_SIZE,
grid_start_y(grid_height) + grid_y as f32 * TILE_SIZE,
z.unwrap_or(0.0),
)
}

View File

@@ -1,17 +1,10 @@
use bevy::input::mouse::MouseButton; use crate::features::{
use bevy::prelude::*; phase::messages::{NextPhaseMessage, PhaseTimerPauseMessage},
use bevy::window::PrimaryWindow; pom::messages::InvalidMoveMessage,
use crate::components::phase::{CurrentPhase, Phase};
use crate::components::tile::{Grid, TileState};
use crate::config::GameConfig;
use crate::messages::phase::{NextPhaseMessage, PhaseTimerPauseMessage};
use crate::messages::{
interact::InteractStartMessage,
r#move::{InvalidMoveMessage, MoveMessage},
}; };
use crate::plugins::grid::world_to_grid_coords; use crate::prelude::*;
use crate::states::AppState; use bevy::input::mouse::MouseButton;
use bevy::window::PrimaryWindow;
pub struct InputPlugin; pub struct InputPlugin;

View File

@@ -1,3 +1,4 @@
pub mod config;
pub mod core; pub mod core;
pub mod game_screen; pub mod game_screen;
pub mod grid; pub mod grid;
@@ -7,6 +8,7 @@ pub mod pom;
pub mod savegame; pub mod savegame;
pub mod start_screen; pub mod start_screen;
pub mod status; pub mod status;
pub mod ui;
pub use core::CorePlugin; pub use core::CorePlugin;
pub use game_screen::GameScreenPlugin; pub use game_screen::GameScreenPlugin;

View File

@@ -1,5 +1,5 @@
use bevy::prelude::*; use super::utils::format_time;
use serde::{Deserialize, Serialize}; use crate::prelude::*;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Phase { pub enum Phase {
@@ -9,21 +9,6 @@ pub enum Phase {
Finished { completed_phase: Box<Phase> }, Finished { completed_phase: Box<Phase> },
} }
fn format_time(seconds: f32) -> String {
let seconds = seconds.max(0.0) as u32;
if seconds >= 3600 {
let hours = seconds / 3600;
let minutes = (seconds % 3600) / 60;
let secs = seconds % 60;
format!("{:02}:{:02}:{:02}", hours, minutes, secs)
} else {
let minutes = seconds / 60;
let secs = seconds % 60;
format!("{:02}:{:02}", minutes, secs)
}
}
impl Phase { impl Phase {
pub fn ui_color(&self) -> Color { pub fn ui_color(&self) -> Color {
match self { match self {

View File

@@ -1,5 +1,4 @@
use crate::components::phase::Phase; use crate::prelude::*;
use bevy::prelude::*;
#[derive(Message)] #[derive(Message)]
pub struct PhaseTimerFinishedMessage { pub struct PhaseTimerFinishedMessage {

View File

@@ -1,9 +1,11 @@
use crate::{ use crate::features::savegame::messages::SavegameDumpMessage;
components::phase::*, use crate::prelude::*;
messages::{phase::*, savegame::SavegameDumpMessage}, use components::{SessionTracker, TimerSettings};
states::AppState, use messages::*;
};
use bevy::prelude::*; pub mod components;
pub mod messages;
pub mod utils;
pub struct PhasePlugin; pub struct PhasePlugin;

View File

@@ -0,0 +1,14 @@
pub fn format_time(seconds: f32) -> String {
let seconds = seconds.max(0.0) as u32;
if seconds >= 3600 {
let hours = seconds / 3600;
let minutes = (seconds % 3600) / 60;
let secs = seconds % 60;
format!("{:02}:{:02}:{:02}", hours, minutes, secs)
} else {
let minutes = seconds / 60;
let secs = seconds % 60;
format!("{:02}:{:02}", minutes, secs)
}
}

View File

@@ -1,8 +1,6 @@
use crate::prelude::*;
use std::collections::VecDeque; use std::collections::VecDeque;
use bevy::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Component)] #[derive(Component)]
pub struct Pom; pub struct Pom;

View File

@@ -1,4 +1,4 @@
use bevy::prelude::*; use crate::prelude::*;
#[derive(Message)] #[derive(Message)]
pub struct MoveMessage { pub struct MoveMessage {
@@ -10,3 +10,9 @@ pub struct MoveMessage {
pub struct InvalidMoveMessage { pub struct InvalidMoveMessage {
pub message: String, pub message: String,
} }
#[derive(Message)]
pub struct InteractStartMessage {
pub x: u32,
pub y: u32,
}

View File

@@ -1,12 +1,11 @@
use crate::components::pom::{GridPosition, MovingState, PathQueue, Pom}; use crate::prelude::*;
use crate::components::tile::{Grid, TileState}; use components::*;
use crate::config::GameConfig; use messages::InvalidMoveMessage;
use crate::messages::r#move::{InvalidMoveMessage, MoveMessage}; use utils::find_path;
use crate::plugins::grid::{TILE_SIZE, grid_to_world_coords};
use crate::states::*; pub mod components;
use crate::utils::pathfinding::find_path; pub mod messages;
use bevy::prelude::*; pub mod utils;
use bevy_aseprite_ultra::prelude::*;
pub struct PomPlugin; pub struct PomPlugin;

View File

@@ -1,5 +1,4 @@
use crate::components::tile::{Grid, TileState}; use crate::prelude::*;
use bevy::prelude::*;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{BinaryHeap, HashMap, VecDeque}; use std::collections::{BinaryHeap, HashMap, VecDeque};

View File

@@ -1,4 +1,4 @@
use bevy::prelude::*; use crate::prelude::*;
use directories::ProjectDirs; use directories::ProjectDirs;
use std::path::PathBuf; use std::path::PathBuf;

View File

@@ -1,4 +1,4 @@
use bevy::prelude::*; use crate::prelude::*;
#[derive(Message)] #[derive(Message)]
pub struct SavegameDumpMessage; pub struct SavegameDumpMessage;

View File

@@ -1,18 +1,12 @@
use crate::{ use crate::features::phase::components::{SessionTracker, TimerSettings};
components::{ use crate::prelude::*;
phase::{CurrentPhase, SessionTracker, TimerSettings}, use messages::*;
pom::{GridPosition, Pom},
savegame::SavegamePath,
tile::{Grid, TileState},
},
messages::savegame::*,
states::AppState,
};
use bevy::prelude::*;
use serde::{Deserialize, Serialize};
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
pub mod components;
pub mod messages;
pub struct SavegamePlugin; pub struct SavegamePlugin;
impl Plugin for SavegamePlugin { impl Plugin for SavegamePlugin {

View File

@@ -1,5 +1,4 @@
use crate::{components::savegame::SavegamePath, states::*}; use crate::prelude::*;
use bevy::prelude::*;
pub struct StartScreenPlugin; pub struct StartScreenPlugin;

View File

@@ -1,9 +1,5 @@
use crate::{ use crate::features::savegame::messages::SavegameDumpMessage;
components::{phase::CurrentPhase, ui::*}, use crate::prelude::*;
messages::savegame::SavegameDumpMessage,
states::AppState,
};
use bevy::prelude::*;
pub struct StatusPlugin; pub struct StatusPlugin;
@@ -38,13 +34,13 @@ fn setup(mut commands: Commands) {
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)), BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
children![ children![
( (
UiPhaseText, UiStatusText::Phase,
Text::new("..."), Text::new("..."),
TextFont::from_font_size(16.0), TextFont::from_font_size(16.0),
TextColor(Color::WHITE) TextColor(Color::WHITE)
), ),
( (
UiTimerText, UiStatusText::Timer,
Text::new("--:--"), Text::new("--:--"),
TextFont::from_font_size(16.0), TextFont::from_font_size(16.0),
TextColor(Color::WHITE) TextColor(Color::WHITE)
@@ -63,22 +59,17 @@ fn setup(mut commands: Commands) {
)); ));
} }
fn update_status( fn update_status(phase_res: Res<CurrentPhase>, mut text_query: Query<(&mut Text, &UiStatusText)>) {
phase_res: Res<CurrentPhase>,
mut phase_query: Query<&mut Text, (With<UiPhaseText>, Without<UiTimerText>)>,
mut timer_query: Query<&mut Text, (With<UiTimerText>, Without<UiPhaseText>)>,
) {
if !phase_res.is_changed() { if !phase_res.is_changed() {
return; return;
} }
let current_phase = &phase_res.0; let current_phase = &phase_res.0;
if let Ok(mut phase_text) = phase_query.single_mut() { for (mut text, status_type) in text_query.iter_mut() {
phase_text.0 = current_phase.display_name().to_string(); text.0 = match status_type {
} UiStatusText::Phase => current_phase.display_name().into(),
UiStatusText::Timer => current_phase.format_duration(),
if let Ok(mut timer_text) = timer_query.single_mut() { };
timer_text.0 = current_phase.format_duration();
} }
} }

View File

@@ -1,13 +1,13 @@
use bevy::prelude::*; use crate::prelude::*;
#[derive(Component)] #[derive(Component)]
pub struct UiStatusRootContainer; pub struct UiStatusRootContainer;
#[derive(Component)] #[derive(Component)]
pub struct UiPhaseText; pub enum UiStatusText {
Phase,
#[derive(Component)] Timer,
pub struct UiTimerText; }
#[derive(Component)] #[derive(Component)]
pub enum UiStatusButton { pub enum UiStatusButton {

1
src/features/ui/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod components;

View File

@@ -1,7 +1,2 @@
pub mod components; pub mod features;
pub mod config; pub mod prelude;
pub mod errors;
pub mod messages;
pub mod plugins;
pub mod states;
pub mod utils;

View File

@@ -1,11 +1,8 @@
use bevy::prelude::*;
use bevy_aseprite_ultra::prelude::*;
use bevy_dev_tools::fps_overlay::*; use bevy_dev_tools::fps_overlay::*;
use pomomon_garden::config::{GameConfig, read_config}; use pomomon_garden::prelude::*;
use pomomon_garden::plugins;
fn main() { fn main() {
let config = read_config().unwrap_or(GameConfig::default()); let config = GameConfig::read_config().unwrap_or(GameConfig::default());
App::new() App::new()
.add_plugins(( .add_plugins((
@@ -25,15 +22,15 @@ fn main() {
}, },
},)) },))
.add_plugins(( .add_plugins((
plugins::CorePlugin, features::CorePlugin,
plugins::StartScreenPlugin, features::StartScreenPlugin,
plugins::GameScreenPlugin, features::GameScreenPlugin,
plugins::GridPlugin, features::GridPlugin,
plugins::PomPlugin, features::PomPlugin,
plugins::InputPlugin, features::InputPlugin,
plugins::PhasePlugin, features::PhasePlugin,
plugins::StatusPlugin, features::StatusPlugin,
plugins::SavegamePlugin, features::SavegamePlugin,
)) ))
.insert_resource(config) .insert_resource(config)
.run(); .run();

View File

@@ -1,7 +0,0 @@
use bevy::prelude::*;
#[derive(Message)]
pub struct InteractStartMessage {
pub x: u32,
pub y: u32,
}

View File

@@ -1,4 +0,0 @@
pub mod interact;
pub mod r#move;
pub mod phase;
pub mod savegame;

20
src/prelude.rs Normal file
View File

@@ -0,0 +1,20 @@
pub use crate::features;
pub use crate::features::{
config::components::GameConfig,
core::states::AppState,
grid::{
components::{Grid, Tile, TileState},
consts::TILE_SIZE,
utils::{grid_to_world_coords, world_to_grid_coords},
},
phase::components::{CurrentPhase, Phase},
pom::{
components::{GridPosition, MovingState, Pom},
messages::{InteractStartMessage, MoveMessage},
},
savegame::components::SavegamePath,
ui::components::*,
};
pub use bevy::prelude::*;
pub use bevy_aseprite_ultra::prelude::*;
pub use serde::{Deserialize, Serialize};

View File

@@ -1,8 +0,0 @@
use bevy::prelude::*;
#[derive(States, Clone, PartialEq, Eq, Debug, Hash, Default, Reflect)]
pub enum AppState {
#[default]
StartScreen,
GameScreen,
}

View File

@@ -1 +0,0 @@
pub mod pathfinding;