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:
194
apps/game/src/gameplay/character_creation/mod.rs
Normal file
194
apps/game/src/gameplay/character_creation/mod.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user