Split character creation into mod/params/ui submodules
Promote the monolithic character_creation/mod.rs into a folder per apps/game/AGENTS.md: lays out the picker UI in ui.rs, holds presets and the editable CharacterDraft resource in params.rs, and keeps mod.rs as the plugin + lifecycle shell. Re-exports CharacterDraft / Origin / StartingShip at the module root for the (yet-to-be-wired) persistence layer.
This commit is contained in:
@@ -1,13 +1,22 @@
|
||||
//! Character creation scene.
|
||||
//!
|
||||
//! Barebones skeleton: spawns a placeholder UI on enter, despawns on exit,
|
||||
//! and wires a `Confirm` button that transitions to [`AppState::InGame`] and
|
||||
//! a `Back` button (or Escape) that returns to [`AppState::Galaxy`].
|
||||
//! Layout / picker UI lives in [`ui`]; presets and the editable draft
|
||||
//! resource live in [`params`]. This module wires the Bevy plugin, owns the
|
||||
//! spawn / despawn lifecycle, and handles the Escape → Galaxy shortcut.
|
||||
//!
|
||||
//! Intended to grow into the real character creator — name, portrait, stats,
|
||||
//! starting ship, background / origin, etc. Each major aspect should split
|
||||
//! into its own submodule (e.g. `ui.rs`, `params.rs`, `presets.rs`) once it
|
||||
//! outgrows this file (see the module-layout guidelines in `apps/game/AGENTS.md`).
|
||||
//! Persistence is not yet wired up — on Confirm, the current draft is logged
|
||||
//! and the app transitions to [`AppState::InGame`]. The TODO is to persist it
|
||||
//! to SpacetimeDB once the Rust SDK is integrated.
|
||||
|
||||
mod params;
|
||||
mod ui;
|
||||
|
||||
// Public API surface for the (yet-to-be-wired) persistence layer / InGame
|
||||
// plugin. Currently unused outside this module — `#[allow]` keeps the names
|
||||
// exported so consumers can `use crate::gameplay::character_creation::Origin`
|
||||
// without us remembering to re-add the re-export the day they're needed.
|
||||
#[allow(unused_imports)]
|
||||
pub use params::{CharacterDraft, Origin, StartingShip};
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
@@ -17,7 +26,11 @@ pub struct CharacterCreationPlugin;
|
||||
|
||||
impl Plugin for CharacterCreationPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(OnEnter(AppState::CharacterCreation), setup_character_creation)
|
||||
app.init_resource::<CharacterDraft>()
|
||||
.add_systems(
|
||||
OnEnter(AppState::CharacterCreation),
|
||||
ui::setup_character_creation_ui,
|
||||
)
|
||||
.add_systems(
|
||||
OnExit(AppState::CharacterCreation),
|
||||
despawn_character_creation,
|
||||
@@ -26,9 +39,11 @@ impl Plugin for CharacterCreationPlugin {
|
||||
Update,
|
||||
(
|
||||
escape_to_galaxy,
|
||||
confirm_button_handler,
|
||||
back_button_handler,
|
||||
ui::picker_button_handler,
|
||||
ui::refresh_picker_values,
|
||||
ui::action_button_handler,
|
||||
)
|
||||
.chain()
|
||||
.run_if(in_state(AppState::CharacterCreation)),
|
||||
);
|
||||
}
|
||||
@@ -49,104 +64,6 @@ pub enum CharacterCreationButton {
|
||||
Back,
|
||||
}
|
||||
|
||||
// ── Setup ───────────────────────────────────────────────────────────────────
|
||||
|
||||
fn setup_character_creation(mut commands: Commands) {
|
||||
let button_style = Node {
|
||||
width: Val::Px(220.0),
|
||||
height: Val::Px(56.0),
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
};
|
||||
let button_font = TextFont {
|
||||
font_size: 24.0,
|
||||
..default()
|
||||
};
|
||||
|
||||
commands
|
||||
.spawn((
|
||||
Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
display: Display::Flex,
|
||||
flex_direction: FlexDirection::Column,
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
row_gap: Val::Px(24.0),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::srgb(0.02, 0.02, 0.06)),
|
||||
CharacterCreationSpawned,
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn((
|
||||
Text::new("Character Creation"),
|
||||
TextFont {
|
||||
font_size: 56.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::srgb(0.7, 0.85, 1.0)),
|
||||
Node {
|
||||
margin: UiRect::bottom(Val::Px(16.0)),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
parent.spawn((
|
||||
Text::new("TODO: name, portrait, stats, background, starting ship"),
|
||||
TextFont {
|
||||
font_size: 18.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::srgb(0.4, 0.5, 0.6)),
|
||||
Node {
|
||||
margin: UiRect::bottom(Val::Px(48.0)),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
spawn_button(
|
||||
&mut parent.spawn_empty(),
|
||||
"Confirm",
|
||||
CharacterCreationButton::Confirm,
|
||||
&button_style,
|
||||
&button_font,
|
||||
);
|
||||
spawn_button(
|
||||
&mut parent.spawn_empty(),
|
||||
"Back",
|
||||
CharacterCreationButton::Back,
|
||||
&button_style,
|
||||
&button_font,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn_button(
|
||||
cmd: &mut EntityCommands,
|
||||
label: &str,
|
||||
marker: CharacterCreationButton,
|
||||
style: &Node,
|
||||
text_font: &TextFont,
|
||||
) {
|
||||
cmd.insert((
|
||||
Button,
|
||||
style.clone(),
|
||||
BackgroundColor(Color::srgb(0.08, 0.1, 0.18)),
|
||||
BorderColor(Color::srgb(0.3, 0.45, 0.7)),
|
||||
BorderRadius::all(Val::Px(8.0)),
|
||||
marker,
|
||||
))
|
||||
.with_children(|btn| {
|
||||
btn.spawn((
|
||||
Text::new(label),
|
||||
text_font.clone(),
|
||||
TextColor(Color::srgb(0.75, 0.85, 1.0)),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
// ── Lifecycle ───────────────────────────────────────────────────────────────
|
||||
|
||||
fn despawn_character_creation(
|
||||
@@ -169,26 +86,3 @@ fn escape_to_galaxy(
|
||||
next_state.set(AppState::Galaxy);
|
||||
}
|
||||
}
|
||||
|
||||
fn confirm_button_handler(
|
||||
mut next_state: ResMut<NextState<AppState>>,
|
||||
query: Query<(&Interaction, &CharacterCreationButton), Changed<Interaction>>,
|
||||
) {
|
||||
for (interaction, button) in &query {
|
||||
if *interaction == Interaction::Pressed && matches!(button, CharacterCreationButton::Confirm) {
|
||||
// TODO: persist the configured character before entering the game.
|
||||
next_state.set(AppState::InGame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn back_button_handler(
|
||||
mut next_state: ResMut<NextState<AppState>>,
|
||||
query: Query<(&Interaction, &CharacterCreationButton), Changed<Interaction>>,
|
||||
) {
|
||||
for (interaction, button) in &query {
|
||||
if *interaction == Interaction::Pressed && matches!(button, CharacterCreationButton::Back) {
|
||||
next_state.set(AppState::Galaxy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user