Rename galaxy_creation to galaxy; add character creation skeleton

- Rename GalaxyCreationPlugin -> GalaxyPlugin, AppState::GalaxyCreation
  -> AppState::Galaxy, module path galaxy_creation -> galaxy (folder
  rename preserves git history via git mv).
- Rename GalaxyCreationSpawned -> GalaxySpawned, despawn_galaxy_creation
  -> despawn_galaxy.
- Update AGENTS.md module layout, plugin pattern example, and naming
  table for the new names.
- Add apps/game/src/gameplay/character_creation/ skeleton: placeholder
  UI with Confirm (-> InGame) and Back (-> Galaxy) buttons plus Escape
  shortcut. Wired into main.rs via CharacterCreationPlugin.
This commit is contained in:
2026-06-07 17:11:56 -04:00
parent 031a674bd0
commit 75f58bcd54
15 changed files with 247 additions and 43 deletions

View File

@@ -0,0 +1,194 @@
//! 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`].
//!
//! 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`).
use bevy::prelude::*;
use crate::state::AppState;
pub struct CharacterCreationPlugin;
impl Plugin for CharacterCreationPlugin {
fn build(&self, app: &mut App) {
app.add_systems(OnEnter(AppState::CharacterCreation), setup_character_creation)
.add_systems(
OnExit(AppState::CharacterCreation),
despawn_character_creation,
)
.add_systems(
Update,
(
escape_to_galaxy,
confirm_button_handler,
back_button_handler,
)
.run_if(in_state(AppState::CharacterCreation)),
);
}
}
// ── Markers ─────────────────────────────────────────────────────────────────
/// Tag for everything spawned during character creation so it can be cleanly
/// despawned on state exit.
#[derive(Component)]
pub struct CharacterCreationSpawned;
#[derive(Component)]
pub enum CharacterCreationButton {
/// Finalize the character and begin the game.
Confirm,
/// Discard and return to the galaxy selection screen.
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(
mut commands: Commands,
query: Query<Entity, With<CharacterCreationSpawned>>,
) {
for entity in &query {
// Bevy 0.16: despawn() is recursive by default.
commands.entity(entity).despawn();
}
}
// ── Input ───────────────────────────────────────────────────────────────────
fn escape_to_galaxy(
keys: Res<ButtonInput<KeyCode>>,
mut next_state: ResMut<NextState<AppState>>,
) {
if keys.just_pressed(KeyCode::Escape) {
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);
}
}
}