Split Rust game src into modules; update AGENTS.md for dual toolchain
- Refactor apps/game/src/main.rs into modules: state.rs, camera.rs, ui/, gameplay/ - Add gameplay/galaxy_creation.rs scaffold (stubs only, no new logic) - Update root AGENTS.md: separate TS workspace vs Rust game commands - Update apps/AGENTS.md: docs/game/site use two toolchains, not one - Add apps/game/AGENTS.md: build commands, module layout, naming conventions, Plugin pattern
This commit is contained in:
5
apps/game/src/camera.rs
Normal file
5
apps/game/src/camera.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub fn spawn_camera(mut commands: Commands) {
|
||||
commands.spawn(Camera2d);
|
||||
}
|
||||
18
apps/game/src/gameplay/galaxy_creation.rs
Normal file
18
apps/game/src/gameplay/galaxy_creation.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
// ── Markers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct GalaxyCreationUi;
|
||||
|
||||
// ── Galaxy Creation Screen ──────────────────────────────────────────────────
|
||||
|
||||
pub fn setup_galaxy_creation(_commands: Commands) {
|
||||
// TODO: spawn galaxy creation UI
|
||||
}
|
||||
|
||||
pub fn despawn_galaxy_creation(mut commands: Commands, query: Query<Entity, With<GalaxyCreationUi>>) {
|
||||
for entity in &query {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
1
apps/game/src/gameplay/mod.rs
Normal file
1
apps/game/src/gameplay/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod galaxy_creation;
|
||||
@@ -1,176 +1,24 @@
|
||||
mod camera;
|
||||
mod gameplay;
|
||||
mod state;
|
||||
mod ui;
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
use gameplay::galaxy_creation;
|
||||
use state::AppState;
|
||||
use ui::main_menu;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.insert_resource(ClearColor(Color::srgb(0.02, 0.02, 0.06)))
|
||||
.init_state::<AppState>()
|
||||
.add_systems(Startup, spawn_camera)
|
||||
.add_systems(OnEnter(AppState::MainMenu), setup_main_menu)
|
||||
.add_systems(OnExit(AppState::MainMenu), despawn_main_menu)
|
||||
.add_systems(Update, main_menu_buttons)
|
||||
.add_systems(Startup, camera::spawn_camera)
|
||||
.add_systems(OnEnter(AppState::MainMenu), main_menu::setup_main_menu)
|
||||
.add_systems(OnExit(AppState::MainMenu), main_menu::despawn_main_menu)
|
||||
.add_systems(Update, main_menu::main_menu_buttons)
|
||||
.add_systems(OnEnter(AppState::GalaxyCreation), galaxy_creation::setup_galaxy_creation)
|
||||
.add_systems(OnExit(AppState::GalaxyCreation), galaxy_creation::despawn_galaxy_creation)
|
||||
.run();
|
||||
}
|
||||
|
||||
// ── States ──────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Hash, States, Default)]
|
||||
enum AppState {
|
||||
#[default]
|
||||
MainMenu,
|
||||
InGame,
|
||||
Options,
|
||||
}
|
||||
|
||||
// ── Markers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Component)]
|
||||
struct MainMenuUi;
|
||||
|
||||
#[derive(Component)]
|
||||
enum MenuButton {
|
||||
Start,
|
||||
Options,
|
||||
Exit,
|
||||
}
|
||||
|
||||
// ── Camera ──────────────────────────────────────────────────────────────────
|
||||
|
||||
fn spawn_camera(mut commands: Commands) {
|
||||
commands.spawn(Camera2d);
|
||||
}
|
||||
|
||||
// ── Main Menu ───────────────────────────────────────────────────────────────
|
||||
|
||||
fn setup_main_menu(mut commands: Commands) {
|
||||
let button_style = Style {
|
||||
width: Val::Px(280.0),
|
||||
height: Val::Px(64.0),
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
};
|
||||
|
||||
let button_text_font = TextFont {
|
||||
font_size: 28.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)),
|
||||
MainMenuUi,
|
||||
)).with_children(|parent| {
|
||||
// Title
|
||||
parent.spawn((
|
||||
Text::new("VOID::NAV"),
|
||||
TextFont {
|
||||
font_size: 72.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::srgb(0.7, 0.85, 1.0)),
|
||||
Node {
|
||||
margin: UiRect::bottom(Val::Px(48.0)),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
// Subtitle
|
||||
parent.spawn((
|
||||
Text::new("A space exploration RPG"),
|
||||
TextFont {
|
||||
font_size: 18.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::srgb(0.4, 0.5, 0.6)),
|
||||
Node {
|
||||
margin: UiRect::bottom(Val::Px(32.0)),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
// Start Game button
|
||||
spawn_menu_button(
|
||||
&mut parent.spawn_empty(),
|
||||
"Start Game",
|
||||
MenuButton::Start,
|
||||
&button_style,
|
||||
&button_text_font,
|
||||
);
|
||||
|
||||
// Options button
|
||||
spawn_menu_button(
|
||||
&mut parent.spawn_empty(),
|
||||
"Options",
|
||||
MenuButton::Options,
|
||||
&button_style,
|
||||
&button_text_font,
|
||||
);
|
||||
|
||||
// Exit button
|
||||
spawn_menu_button(
|
||||
&mut parent.spawn_empty(),
|
||||
"Exit",
|
||||
MenuButton::Exit,
|
||||
&button_style,
|
||||
&button_text_font,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn_menu_button(
|
||||
cmd: &mut EntityCommands,
|
||||
label: &str,
|
||||
marker: MenuButton,
|
||||
style: &Style,
|
||||
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)),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
fn despawn_main_menu(mut commands: Commands, query: Query<Entity, With<MainMenuUi>>) {
|
||||
for entity in &query {
|
||||
commands.entity(entity).despawn_recursive();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Button Interaction ──────────────────────────────────────────────────────
|
||||
|
||||
fn main_menu_buttons(
|
||||
mut next_state: ResMut<NextState<AppState>>,
|
||||
mut exit: EventWriter<AppExit>,
|
||||
interaction_query: Query<(&Interaction, &MenuButton), Changed<Interaction>>,
|
||||
) {
|
||||
for (interaction, button) in &interaction_query {
|
||||
if *interaction == Interaction::Pressed {
|
||||
match button {
|
||||
MenuButton::Start => next_state.set(AppState::InGame),
|
||||
MenuButton::Options => next_state.set(AppState::Options),
|
||||
MenuButton::Exit => exit.send(AppExit::Success),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
apps/game/src/state.rs
Normal file
11
apps/game/src/state.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Hash, States, Default)]
|
||||
pub enum AppState {
|
||||
#[default]
|
||||
MainMenu,
|
||||
GalaxyCreation,
|
||||
CharacterCreation,
|
||||
InGame,
|
||||
Options,
|
||||
}
|
||||
156
apps/game/src/ui/main_menu.rs
Normal file
156
apps/game/src/ui/main_menu.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
// ── Markers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct MainMenuUi;
|
||||
|
||||
#[derive(Component)]
|
||||
pub enum MenuButton {
|
||||
ContinueGame, //Needed for later once save game functionality is implemented, should have a check to see if a save game exists and only show this button if it does
|
||||
NewGame,
|
||||
Options,
|
||||
Exit,
|
||||
}
|
||||
|
||||
// ── Main Menu ───────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn setup_main_menu(mut commands: Commands) {
|
||||
let button_style = Node {
|
||||
width: Val::Px(280.0),
|
||||
height: Val::Px(64.0),
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
..default()
|
||||
};
|
||||
|
||||
let button_text_font = TextFont {
|
||||
font_size: 28.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)),
|
||||
MainMenuUi,
|
||||
))
|
||||
.with_children(|parent| {
|
||||
// Title
|
||||
parent.spawn((
|
||||
Text::new("VOID::NAV"),
|
||||
TextFont {
|
||||
font_size: 72.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::srgb(0.7, 0.85, 1.0)),
|
||||
Node {
|
||||
margin: UiRect::bottom(Val::Px(48.0)),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
// Subtitle
|
||||
parent.spawn((
|
||||
Text::new("A space exploration RPG"),
|
||||
TextFont {
|
||||
font_size: 18.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::srgb(0.4, 0.5, 0.6)),
|
||||
Node {
|
||||
margin: UiRect::bottom(Val::Px(32.0)),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
// Start Game button
|
||||
spawn_menu_button(
|
||||
&mut parent.spawn_empty(),
|
||||
"Start Game",
|
||||
MenuButton::NewGame,
|
||||
&button_style,
|
||||
&button_text_font,
|
||||
);
|
||||
|
||||
// Options button
|
||||
spawn_menu_button(
|
||||
&mut parent.spawn_empty(),
|
||||
"Options",
|
||||
MenuButton::Options,
|
||||
&button_style,
|
||||
&button_text_font,
|
||||
);
|
||||
|
||||
// Exit button
|
||||
spawn_menu_button(
|
||||
&mut parent.spawn_empty(),
|
||||
"Exit",
|
||||
MenuButton::Exit,
|
||||
&button_style,
|
||||
&button_text_font,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn_menu_button(
|
||||
cmd: &mut EntityCommands,
|
||||
label: &str,
|
||||
marker: MenuButton,
|
||||
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)),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn despawn_main_menu(mut commands: Commands, query: Query<Entity, With<MainMenuUi>>) {
|
||||
for entity in &query {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Button Interaction ──────────────────────────────────────────────────────
|
||||
|
||||
pub fn main_menu_buttons(
|
||||
mut next_state: ResMut<NextState<AppState>>,
|
||||
mut exit: EventWriter<AppExit>,
|
||||
interaction_query: Query<(&Interaction, &MenuButton), Changed<Interaction>>,
|
||||
) {
|
||||
for (interaction, button) in &interaction_query {
|
||||
if *interaction == Interaction::Pressed {
|
||||
match button {
|
||||
MenuButton::ContinueGame => next_state.set(AppState::InGame), // Placeholder for now
|
||||
MenuButton::NewGame => next_state.set(AppState::GalaxyCreation),
|
||||
MenuButton::Options => next_state.set(AppState::Options),
|
||||
MenuButton::Exit => {
|
||||
exit.write(AppExit::Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
apps/game/src/ui/mod.rs
Normal file
1
apps/game/src/ui/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod main_menu;
|
||||
Reference in New Issue
Block a user