Compare commits

...
Author SHA1 Message Date
EIREXE 868df95cd4 wording fixes
CI / build-and-test (pull_request) Successful in 11m31s
2026-09-14 15:20:07 +02:00
EIREXE 8c11a01446 Make debug menu usable with mouse and keyboard
CI / build-and-test (pull_request) Successful in 11m27s
2026-09-14 14:33:04 +02:00
EIREXE 30477254e5 Add build and test CI (#1)
CI / build-and-test (push) Successful in 11m30s
Reviewed-on: #1
Co-authored-by: Álex Román Núñez <eirexe@eirteam.moe>
2026-09-14 12:02:24 +00:00
EIREXE 4861374aef Add test fighter for CI 2026-09-14 11:38:42 +02:00
EIREXE d3a688e768 Fix build and add some assets 2026-09-14 11:25:27 +02:00
EIREXE 376a40d38c Updated game settings and fighter assets 2026-09-13 23:28:40 +02:00
eirexe-test b29085f8a5 Update .gitignore 2026-09-13 23:28:09 +02:00
eirexe-test b0b1420f48 Add mailmap 2026-09-13 23:25:52 +02:00
19 changed files with 82107 additions and 221 deletions
+45
View File
@@ -0,0 +1,45 @@
name: CI
on:
push:
branches:
- master
pull_request:
workflow_dispatch:
jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out source
uses: actions/checkout@v4
- name: Install Rust toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --profile minimal
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
"$HOME/.cargo/bin/rustup" toolchain install nightly --profile minimal
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y \
clang \
libasound2-dev \
libudev-dev \
libusb-1.0-0-dev \
libwayland-dev \
libx11-dev \
libxkbcommon-dev \
libxkbcommon-x11-0 \
mesa-vulkan-drivers \
mold \
pkg-config
- name: Build
run: cargo build --locked -j 4
- name: Run unit tests
run: cargo test --locked --lib -j 4
+10 -1
View File
@@ -1,3 +1,12 @@
/target /target
/.jackdaw /.jackdaw
imported_assets imported_assets
# Local editor configuration
**/.vscode/
# Generated and backup assets
*.blend1
*_old.glb
/replay.json
*.replay.tmp
+1
View File
@@ -0,0 +1 @@
Álex Román (EIREXE) <eirexe123@gmail.com> <eirexe@eirteam.moe>
@@ -1,12 +1,12 @@
frame(3); frame(4);
hitbox(0, hitbox(0,
"Foot.L", "hand_l",
damage("5.0"), damage("5.0"),
vec3("0.0", "0.0", "0.0"), vec3("0.0", "0.0", "0.0"),
fg32("20.0"), fg32("5.0"),
angle_sakurai(), angle_sakurai(),
knockback_normal(), knockback_normal(),
knockback("10.0"), knockback("10.0"),
fg32("1.0") fg32("1.0")
); );
frame(6); frame(8);
@@ -0,0 +1,12 @@
frame(3);
hitbox(0,
"hand_l",
damage("5.0"),
vec3("0.0", "0.0", "0.0"),
fg32("5.0"),
angle(fg32("45.0")),
knockback_normal(),
knockback("18.0"),
fg32("140.0")
);
frame(8);
@@ -0,0 +1,13 @@
frame(9);
hitbox(0,
"hand_l",
damage("5.0"),
vec3("1.0", "0.0", "0.0"),
fg32("7.0"),
angle_sakurai(),
knockback_normal(),
knockback("10.0"),
fg32("1.0")
);
frame(15);
+10 -6
View File
@@ -1,6 +1,6 @@
( (
id: TestFighter, id: TestFighter,
model_path: "fighters/test/test-fighter-2.glb", model_path: "fighters/test/test_fighter.gltf",
model_scale: "10.0", model_scale: "10.0",
baked_animation_path: "fighters/test/test.fighterbake", baked_animation_path: "fighters/test/test.fighterbake",
icon: ( icon: (
@@ -15,10 +15,10 @@
dash_duration: 24, dash_duration: 24,
base_dash_accel: "0.1", base_dash_accel: "0.1",
stick_dash_accel: "0.05", stick_dash_accel: "0.05",
max_dash_vel: "2.3", max_dash_vel: "2.0",
short_hop_vertical_velocity: "1.35", short_hop_vertical_velocity: "1.45",
full_jump_vertical_velocity: "2.1", full_jump_vertical_velocity: "2.0",
jump_horizontal_velocity: "0.7", jump_horizontal_velocity: "0.6",
air_jump_horizontal_velocity: "0.96", air_jump_horizontal_velocity: "0.96",
air_jump_multiplier: "1.0", air_jump_multiplier: "1.0",
dash_initial_velocity: "1.4", dash_initial_velocity: "1.4",
@@ -63,6 +63,10 @@
DoubleJumpBack: "JumpB_Game", DoubleJumpBack: "JumpB_Game",
Fall: "Fall_Loop_Game", Fall: "Fall_Loop_Game",
Turn: "Turn_Game", Turn: "Turn_Game",
FlinchGround: "Air_Hit_Game",
FlinchAir: "Air_Hit_Game",
AttackJab1: "Punch_Jab_Game", AttackJab1: "Punch_Jab_Game",
AttackUpTilt: "Idle_Loop_Game", AttackUpTilt: "Idle_Loop_Game",
AttackForwardTilt: "Idle_Loop_Game", AttackForwardTilt: "Idle_Loop_Game",
@@ -70,7 +74,7 @@
}, },
attack_scripts: { attack_scripts: {
NeutralAir: "fighters/test/scripts/fair.attack.rhai", NeutralAir: "fighters/test/scripts/fair.attack.rhai",
Jab: "fighters/test/scripts/fair.attack.rhai", Jab: "fighters/test/scripts/jab.attack.rhai",
UpTilt: "fighters/test/scripts/uptilt.attack.rhai", UpTilt: "fighters/test/scripts/uptilt.attack.rhai",
ForwardTilt: "fighters/test/scripts/uptilt.attack.rhai", ForwardTilt: "fighters/test/scripts/uptilt.attack.rhai",
DownTilt: "fighters/test/scripts/uptilt.attack.rhai", DownTilt: "fighters/test/scripts/uptilt.attack.rhai",
Binary file not shown.
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -8,12 +8,16 @@
smash_input_frame_threshold: 3, smash_input_frame_threshold: 3,
input_buffer_size: 3, input_buffer_size: 3,
turn_into_dashback_window: 2, turn_into_dashback_window: 2,
), ),
fighter_common: ( fighter_common: (
walk_speed_ease: "0.5", walk_speed_ease: "0.5",
ground_max_horizontal_velocity: "3.0", ground_max_horizontal_velocity: "3.0",
ground_friction_over_walk_speed_multiplier: "2.0" ground_friction_over_walk_speed_multiplier: "2.0",
knockback_stacking_window: 2,
knockback_decay_per_frame: "0.051",
knockback_launch_velocity_multiplier: "0.03",
hitstun_frames_per_unit_knockback: "0.4",
), ),
stage_manifest_paths: [ stage_manifest_paths: [
"stages/battlefield/test.stage.ron" "stages/battlefield/test.stage.ron"
Binary file not shown.
Binary file not shown.
-84
View File
@@ -1,84 +0,0 @@
(
id: TestStage,
model_path: "stages/battlefield.glb#Scene0",
camera: (
left: -75.0,
right: 75.0,
bottom: -30.0,
top: 70.0,
origin: (0.0, 15.0),
vertical_fov_degrees: 30.0,
min_depth: 150.0,
max_depth: 400.0,
subject_scale: 1.0,
tracking_smoothness: 3.0,
vertical_tilt_degrees: 0.0,
horizontal_pan_coefficient: 0.0,
vertical_pan_coefficient: 0.0,
max_horizontal_pan_degrees: 17.5,
max_upward_pan_degrees: 5.0,
max_downward_pan_degrees: 7.0,
max_downward_expansion: 40.0,
),
polygons: [
(
poly_type: Platform,
lines: [
(
(x: "-59.5", y: "23.45"),
Floor
),
(
(x: "-28.0", y: "23.45"),
Floor
)
]
),
(
poly_type: Platform,
lines: [
(
(x: "28.0", y: "23.45"),
Floor
),
(
(x: "59.5", y: "23.45"),
Floor
)
]
),
(
poly_type: Closed,
lines: [
(
(x: "-56", y: "-200.0"),
Wall
),
(
(x: "-56.0", y: "-3.5"),
Floor,
),
(
(x: "-39.2", y: "0.0"),
Floor,
),
(
(x: "0.0", y: "0.0"),
Floor
),
(
(x: "39.2", y: "0.0"),
Floor
),
(
(x: "56.0", y: "-3.5"),
Wall
),
(
(x: "56", y: "-200.0"),
Wall
),
]
)
]
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="128"
height="128"
viewBox="0 0 33.866666 33.866666"
version="1.1"
id="svg1"
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
sodipodi:docname="cursor.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#000000"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="5.6568542"
inkscape:cx="8.6620581"
inkscape:cy="67.086756"
inkscape:window-width="2560"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<linearGradient
id="linearGradient1"
inkscape:collect="always">
<stop
style="stop-color:#949494;stop-opacity:1;"
offset="0"
id="stop1" />
<stop
style="stop-color:#6c6c6c;stop-opacity:1;"
offset="0.77473962"
id="stop3" />
<stop
style="stop-color:#9f9f9f;stop-opacity:1;"
offset="1"
id="stop2" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient1"
id="radialGradient2"
cx="16.933332"
cy="16.933332"
fx="16.933332"
fy="16.933332"
r="12.733072"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2093771,0,0,1.2093771,-3.5454518,-3.5454518)" />
</defs>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1">
<circle
style="fill:url(#radialGradient2);stroke-width:0.319981"
id="path1"
cx="16.933332"
cy="16.933332"
r="15.399086" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+263 -115
View File
@@ -48,7 +48,7 @@ impl PresentationMotionMode {
} }
} }
#[derive(EnumCycle, Reflect)] #[derive(Clone, Copy, Debug, PartialEq, Eq, EnumCycle, Reflect)]
pub enum GameSpeedDebug { pub enum GameSpeedDebug {
Full, Full,
Half, Half,
@@ -387,6 +387,84 @@ fn page_len(page: DebugMenuPage, player_handles: &[usize]) -> usize {
} }
} }
fn queue_settings_save(commands: &mut Commands) {
commands.queue(SaveSettingsDeferred(Duration::from_secs_f32(0.1)));
}
fn activate_selected(
settings: &mut DebugSettings,
page_len: usize,
player_handles: &[usize],
commands: &mut Commands,
) {
if page_len == 0 {
return;
}
match settings.page() {
DebugMenuPage::Static(entries) => match entries[settings.selected()] {
DebugMenuEntry::StopRecording { .. } => {
commands.insert_resource(StopReplayRecording);
}
DebugMenuEntry::Toggle { flag, .. } => {
settings.toggle_flag(flag);
queue_settings_save(commands);
}
DebugMenuEntry::Submenu { entries, .. } => {
settings.push_page(DebugMenuPage::Static(entries));
}
DebugMenuEntry::PlayerSlots { .. } => settings.push_page(DebugMenuPage::PlayerList),
DebugMenuEntry::MotionSampling { .. } => {
settings.cycle_motion_sampling();
queue_settings_save(commands);
}
DebugMenuEntry::GameSpeed { .. } => {
commands.insert_resource(RollbackFrameRate(settings.game_speed.frame_rate()));
queue_settings_save(commands);
}
},
DebugMenuPage::PlayerList => {
let handle = player_handles[settings.selected()];
settings.rows_mut(handle);
settings.push_page(DebugMenuPage::PlayerRows(handle));
}
DebugMenuPage::PlayerRows(_) => {}
}
}
fn cycle_selected_value(settings: &mut DebugSettings, direction: i8, commands: &mut Commands) {
match settings.page() {
DebugMenuPage::Static(entries) => {
if let Some(entry) = entries.get(settings.selected()) {
match entry {
DebugMenuEntry::MotionSampling { .. } => {
settings.cycle_motion_sampling();
queue_settings_save(commands);
}
DebugMenuEntry::GameSpeed { .. } => {
settings.game_speed = if direction > 0 {
settings.game_speed.up()
} else {
settings.game_speed.down()
};
commands
.insert_resource(RollbackFrameRate(settings.game_speed.frame_rate()));
queue_settings_save(commands);
}
_ => {}
}
}
}
DebugMenuPage::PlayerRows(handle) => {
let selected = settings.selected();
let rows = settings.rows_mut(handle);
rows[selected] = rows[selected].cycle(direction);
queue_settings_save(commands);
}
DebugMenuPage::PlayerList => {}
}
}
fn handle_palette_input( fn handle_palette_input(
keyboard: Res<ButtonInput<KeyCode>>, keyboard: Res<ButtonInput<KeyCode>>,
gamepads: Query<(Entity, &Gamepad)>, gamepads: Query<(Entity, &Gamepad)>,
@@ -398,6 +476,15 @@ fn handle_palette_input(
settings.toggle_flag(DebugFlag::DrawThroughGeometry); settings.toggle_flag(DebugFlag::DrawThroughGeometry);
} }
if keyboard.just_pressed(KeyCode::F3) {
settings.menu_open = !settings.menu_open;
settings.controller = None;
if settings.menu_open {
settings.reset_navigation();
}
return;
}
if !settings.menu_open { if !settings.menu_open {
for (entity, gamepad) in &gamepads { for (entity, gamepad) in &gamepads {
if gamepad.just_pressed(GamepadButton::Start) && gamepad.pressed(GamepadButton::DPadUp) if gamepad.just_pressed(GamepadButton::Start) && gamepad.pressed(GamepadButton::DPadUp)
@@ -411,14 +498,15 @@ fn handle_palette_input(
return; return;
} }
let Some(controller) = settings.controller else { let gamepad = if let Some(controller) = settings.controller {
settings.menu_open = false; let Ok((_, gamepad)) = gamepads.get(controller) else {
return; settings.menu_open = false;
}; settings.controller = None;
let Ok((_, gamepad)) = gamepads.get(controller) else { return;
settings.menu_open = false; };
settings.controller = None; Some(gamepad)
return; } else {
None
}; };
let player_handles = player_handles(&players); let player_handles = player_handles(&players);
let page = settings.page(); let page = settings.page();
@@ -428,95 +516,51 @@ fn handle_palette_input(
*settings.selected_mut() = 0; *settings.selected_mut() = 0;
} }
if gamepad.just_pressed(GamepadButton::DPadUp) && page_len != 0 { if (gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::DPadUp))
|| keyboard.just_pressed(KeyCode::ArrowUp)
|| keyboard.just_pressed(KeyCode::KeyW))
&& page_len != 0
{
let selected = settings.selected_mut(); let selected = settings.selected_mut();
*selected = selected.checked_sub(1).unwrap_or(page_len - 1); *selected = selected.checked_sub(1).unwrap_or(page_len - 1);
} }
if gamepad.just_pressed(GamepadButton::DPadDown) && page_len != 0 { if (gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::DPadDown))
|| keyboard.just_pressed(KeyCode::ArrowDown)
|| keyboard.just_pressed(KeyCode::KeyS))
&& page_len != 0
{
let selected = settings.selected_mut(); let selected = settings.selected_mut();
*selected = (*selected + 1) % page_len; *selected = (*selected + 1) % page_len;
} }
let direction = if gamepad.just_pressed(GamepadButton::DPadLeft) { let direction = if gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::DPadLeft))
|| keyboard.just_pressed(KeyCode::ArrowLeft)
|| keyboard.just_pressed(KeyCode::KeyA)
{
Some(-1) Some(-1)
} else if gamepad.just_pressed(GamepadButton::DPadRight) { } else if gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::DPadRight))
|| keyboard.just_pressed(KeyCode::ArrowRight)
|| keyboard.just_pressed(KeyCode::KeyD)
{
Some(1) Some(1)
} else { } else {
None None
}; };
if let Some(direction) = direction { if let Some(direction) = direction {
match page { cycle_selected_value(&mut settings, direction, &mut commands);
DebugMenuPage::Static(entries) => {
if let Some(entry) = entries.get(settings.selected()) {
match entry {
DebugMenuEntry::StopRecording { .. } => {}
DebugMenuEntry::MotionSampling { .. } => settings.cycle_motion_sampling(),
DebugMenuEntry::GameSpeed { .. } => {
if direction > 0 {
settings.game_speed = settings.game_speed.up();
} else {
settings.game_speed = settings.game_speed.down();
}
commands.insert_resource(RollbackFrameRate(
settings.game_speed.frame_rate(),
));
}
_ => {}
}
}
}
DebugMenuPage::PlayerList => todo!(),
DebugMenuPage::PlayerRows(_) => {
let DebugMenuPage::PlayerRows(handle) = page else {
unreachable!("player row navigation must have a player row page")
};
let selected = settings.selected();
let rows = settings.rows_mut(handle);
rows[selected] = rows[selected].cycle(direction);
commands.queue(SaveSettingsDeferred(Duration::from_secs_f32(0.1)));
}
}
} }
if (gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::South))
if matches!(page, DebugMenuPage::PlayerRows(_)) { || keyboard.just_pressed(KeyCode::Enter)
let direction = if gamepad.just_pressed(GamepadButton::DPadLeft) { || keyboard.just_pressed(KeyCode::Space))
Some(-1) && page_len != 0
} else if gamepad.just_pressed(GamepadButton::DPadRight) { {
Some(1) activate_selected(&mut settings, page_len, &player_handles, &mut commands);
} else {
None
};
if let Some(_direction) = direction {}
} }
if gamepad.just_pressed(GamepadButton::South) { if (gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::West))
match page { || keyboard.just_pressed(KeyCode::Escape))
DebugMenuPage::Static(entries) if page_len != 0 => match entries[settings.selected()] { && !settings.pop_page()
DebugMenuEntry::StopRecording { .. } => { {
commands.insert_resource(StopReplayRecording);
}
DebugMenuEntry::Toggle { flag, .. } => {
settings.toggle_flag(flag);
commands.queue(SaveSettingsDeferred(Duration::from_secs_f32(0.1)));
}
DebugMenuEntry::Submenu { entries, .. } => {
settings.push_page(DebugMenuPage::Static(entries));
}
DebugMenuEntry::PlayerSlots { .. } => settings.push_page(DebugMenuPage::PlayerList),
DebugMenuEntry::MotionSampling { .. } => settings.cycle_motion_sampling(),
DebugMenuEntry::GameSpeed { .. } => {}
},
DebugMenuPage::PlayerList if page_len != 0 => {
let handle = player_handles[settings.selected()];
settings.rows_mut(handle);
settings.push_page(DebugMenuPage::PlayerRows(handle));
}
_ => {}
}
}
if gamepad.just_pressed(GamepadButton::West) && !settings.pop_page() {
settings.menu_open = false; settings.menu_open = false;
settings.controller = None; settings.controller = None;
} }
@@ -538,8 +582,9 @@ fn sync_gizmo_depth_bias(settings: Res<DebugSettings>, mut configs: ResMut<Gizmo
fn draw_palette( fn draw_palette(
mut contexts: EguiContexts, mut contexts: EguiContexts,
settings: Res<DebugSettings>, mut settings: ResMut<DebugSettings>,
players: Query<&Player>, players: Query<&Player>,
mut commands: Commands,
) -> Result { ) -> Result {
if !settings.menu_open { if !settings.menu_open {
return Ok(()); return Ok(());
@@ -547,7 +592,7 @@ fn draw_palette(
let player_handles = player_handles(&players); let player_handles = player_handles(&players);
let page = settings.page(); let page = settings.page();
let selected = settings.selected(); let page_len = page_len(page, &player_handles);
egui::Window::new("Debug") egui::Window::new("Debug")
.anchor(egui::Align2::RIGHT_TOP, egui::vec2(-12.0, 12.0)) .anchor(egui::Align2::RIGHT_TOP, egui::vec2(-12.0, 12.0))
.resizable(false) .resizable(false)
@@ -556,38 +601,111 @@ fn draw_palette(
match page { match page {
DebugMenuPage::Static(entries) => { DebugMenuPage::Static(entries) => {
for (index, entry) in entries.iter().enumerate() { for (index, entry) in entries.iter().enumerate() {
let marker = if index == selected { ">" } else { " " }; ui.horizontal(|ui| {
match entry { ui.label(if index == settings.selected() {
DebugMenuEntry::StopRecording { label } => { ">"
ui.label(format!("{marker} {label}")); } else {
" "
});
match entry {
DebugMenuEntry::StopRecording { label } => {
if ui.button(*label).clicked() {
*settings.selected_mut() = index;
activate_selected(
&mut settings,
page_len,
&player_handles,
&mut commands,
);
}
}
DebugMenuEntry::Toggle { label, flag } => {
let mut value = settings.flag(*flag);
if ui.checkbox(&mut value, *label).changed() {
settings.toggle_flag(*flag);
queue_settings_save(&mut commands);
}
}
DebugMenuEntry::Submenu { label, .. }
| DebugMenuEntry::PlayerSlots { label } => {
if ui.button(format!("{label} >")).clicked() {
*settings.selected_mut() = index;
activate_selected(
&mut settings,
page_len,
&player_handles,
&mut commands,
);
}
}
DebugMenuEntry::MotionSampling { label } => {
let old = settings.motion_sampling;
egui::ComboBox::from_id_salt(("debug-motion", index))
.width(180.0)
.selected_text(format!("{label}: {}", old.label()))
.show_ui(ui, |ui| {
ui.selectable_value(
&mut settings.motion_sampling,
PresentationMotionMode::Interpolation,
"Interpolation",
);
ui.selectable_value(
&mut settings.motion_sampling,
PresentationMotionMode::Extrapolation,
"Extrapolation",
);
});
if settings.motion_sampling != old {
queue_settings_save(&mut commands);
}
}
DebugMenuEntry::GameSpeed { label } => {
let old = settings.game_speed;
egui::ComboBox::from_id_salt(("debug-speed", index))
.width(180.0)
.selected_text(format!("{label}: {}", old.label()))
.show_ui(ui, |ui| {
for speed in [
GameSpeedDebug::Full,
GameSpeedDebug::Half,
GameSpeedDebug::Quarter,
] {
ui.selectable_value(
&mut settings.game_speed,
speed,
speed.label(),
);
}
});
if settings.game_speed != old {
commands.insert_resource(RollbackFrameRate(
settings.game_speed.frame_rate(),
));
queue_settings_save(&mut commands);
}
}
} }
DebugMenuEntry::Toggle { label, flag } => { });
let state = if settings.flag(*flag) { "on" } else { "off" };
ui.label(format!("{marker} {label}: {state}"));
}
DebugMenuEntry::Submenu { label, .. }
| DebugMenuEntry::PlayerSlots { label } => {
ui.label(format!("{marker} {label} >"));
}
DebugMenuEntry::MotionSampling { label } => {
ui.label(format!(
"{marker} {label}: {}",
settings.motion_sampling.label()
));
}
DebugMenuEntry::GameSpeed { label } => {
ui.label(format!(
"{marker} {label}: {}",
settings.game_speed.label()
));
}
}
} }
} }
DebugMenuPage::PlayerList => { DebugMenuPage::PlayerList => {
for (index, handle) in player_handles.iter().enumerate() { for (index, handle) in player_handles.iter().enumerate() {
let marker = if index == selected { ">" } else { " " }; ui.horizontal(|ui| {
ui.label(format!("{marker} Player {} >", handle + 1)); ui.label(if index == settings.selected() {
">"
} else {
" "
});
if ui.button(format!("Player {} >", handle + 1)).clicked() {
*settings.selected_mut() = index;
activate_selected(
&mut settings,
page_len,
&player_handles,
&mut commands,
);
}
});
} }
if player_handles.is_empty() { if player_handles.is_empty() {
ui.label("No players in match"); ui.label("No players in match");
@@ -600,16 +718,30 @@ fn draw_palette(
.unwrap_or([FighterDebugRowKind::None; PLAYER_DEBUG_ROW_COUNT]); .unwrap_or([FighterDebugRowKind::None; PLAYER_DEBUG_ROW_COUNT]);
ui.label(format!("Player {}", handle + 1)); ui.label(format!("Player {}", handle + 1));
for (index, row) in rows.iter().enumerate() { for (index, row) in rows.iter().enumerate() {
let marker = if index == selected { ">" } else { " " }; ui.horizontal(|ui| {
ui.label(format!("{marker} Row {}: {}", index + 1, row.label())); ui.label(if index == settings.selected() {
">"
} else {
" "
});
egui::ComboBox::from_id_salt(("debug-row", handle, index))
.width(180.0)
.selected_text(format!("Row {}: {}", index + 1, row.label()))
.show_ui(ui, |ui| {
let rows = settings.rows_mut(handle);
for kind in FighterDebugRowKind::ALL {
ui.selectable_value(&mut rows[index], kind, kind.label());
}
});
});
} }
} }
} }
ui.separator(); ui.separator();
let controls = if matches!(page, DebugMenuPage::PlayerRows(_)) { let controls = if matches!(page, DebugMenuPage::PlayerRows(_)) {
"D-pad: select/change B: back" "Arrows/WASD: select/change Escape/B: back"
} else { } else {
"D-pad: select A: enter/toggle Left/right: change B: back" "Arrows/WASD: select Enter/A: activate Escape/B: back"
}; };
ui.label(controls); ui.label(controls);
}); });
@@ -699,4 +831,20 @@ mod tests {
mode.cycle(); mode.cycle();
assert_eq!(mode, PresentationMotionMode::Interpolation); assert_eq!(mode, PresentationMotionMode::Interpolation);
} }
#[test]
fn game_speed_has_expected_frame_rates() {
assert_eq!(GameSpeedDebug::Full.frame_rate(), 60);
assert_eq!(GameSpeedDebug::Half.frame_rate(), 30);
assert_eq!(GameSpeedDebug::Quarter.frame_rate(), 15);
}
#[test]
fn empty_player_list_does_not_activate() {
let mut settings = DebugSettings::default();
settings.push_page(DebugMenuPage::PlayerList);
assert_eq!(page_len(settings.page(), &[]), 0);
assert_eq!(settings.selected(), 0);
}
} }
+13 -4
View File
@@ -660,6 +660,14 @@ fn bake_gltf(
) -> io::Result<BakedFighterAnimations> { ) -> io::Result<BakedFighterAnimations> {
let (document, buffers, _) = gltf::import_slice(bytes) let (document, buffers, _) = gltf::import_slice(bytes)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
bake_gltf_document(document, buffers, animations)
}
fn bake_gltf_document(
document: gltf::Document,
buffers: Vec<gltf::buffer::Data>,
animations: &HashMap<AnimKind, String>,
) -> io::Result<BakedFighterAnimations> {
let nodes: Vec<_> = document.nodes().collect(); let nodes: Vec<_> = document.nodes().collect();
if nodes.is_empty() { if nodes.is_empty() {
return Err(io::Error::new( return Err(io::Error::new(
@@ -933,11 +941,12 @@ mod tests {
fn configured_fighter_clips_bake_to_fixed_point_frames() { fn configured_fighter_clips_bake_to_fixed_point_frames() {
let manifest: FighterManifest = let manifest: FighterManifest =
ron::from_str(include_str!("../../assets/fighters/test/test.fighter.ron")).unwrap(); ron::from_str(include_str!("../../assets/fighters/test/test.fighter.ron")).unwrap();
let baked = bake_gltf( let (document, buffers, _) = gltf::import(concat!(
include_bytes!("../../assets/fighters/test/test-fighter-2.glb"), env!("CARGO_MANIFEST_DIR"),
&manifest.animations, "/assets/fighters/test/test_fighter.gltf"
) ))
.unwrap(); .unwrap();
let baked = bake_gltf_document(document, buffers, &manifest.animations).unwrap();
assert_eq!(baked.frame_rate, BAKED_ANIMATION_FPS); assert_eq!(baked.frame_rate, BAKED_ANIMATION_FPS);
assert_eq!(baked.frame_count(AnimKind::Wait), Some(121)); assert_eq!(baked.frame_count(AnimKind::Wait), Some(121));
+1 -5
View File
@@ -1,7 +1,3 @@
//! Standalone binary for game-test: `cargo run` plays the game
//! without the editor. The editor's Play button runs the same
//! [`game_test::GamePlugin`] through its own game runner instead.
use bevy::asset::AssetMode::Processed; use bevy::asset::AssetMode::Processed;
use bevy::camera_controller::free_camera::FreeCameraPlugin; use bevy::camera_controller::free_camera::FreeCameraPlugin;
use bevy::image::{ImageAddressMode, ImagePlugin, ImageSamplerDescriptor}; use bevy::image::{ImageAddressMode, ImagePlugin, ImageSamplerDescriptor};
@@ -30,7 +26,7 @@ fn main() -> AppExit {
App::new() App::new()
.add_plugins(default_plugins) .add_plugins(default_plugins)
.add_plugins(avian3d::prelude::PhysicsPlugins::default()) .add_plugins(avian3d::prelude::PhysicsPlugins::default())
.add_plugins(game_test::GamePlugin) .add_plugins(shinespark::GamePlugin)
.add_plugins(FreeCameraPlugin) .add_plugins(FreeCameraPlugin)
.add_plugins(EguiPlugin::default()) .add_plugins(EguiPlugin::default())
.add_plugins(WorldInspectorPlugin::default()) // adds default options and `InspectorEguiImpl`s .add_plugins(WorldInspectorPlugin::default()) // adds default options and `InspectorEguiImpl`s