nightshade 0.8.0

A cross-platform data-oriented game engine.
Documentation
use super::{ComponentInspector, InspectorContext, impl_simple_inspector};
use crate::ecs::physics::components::{CharacterControllerComponent, ColliderShape};
use crate::prelude::*;

fn add_character_controller(world: &mut World, entity: Entity) {
    world.add_components(entity, crate::ecs::world::CHARACTER_CONTROLLER);
    world.set_character_controller(entity, CharacterControllerComponent::default());
}

fn character_controller_ui(
    world: &mut World,
    entity: Entity,
    ui: &mut egui::Ui,
    _context: &mut InspectorContext,
) {
    if let Some(controller) = world.get_character_controller_mut(entity) {
        ui.label(egui::RichText::new("Shape").strong());

        let shape_label = match controller.shape {
            ColliderShape::Capsule { .. } => "Capsule",
            ColliderShape::Ball { .. } => "Ball",
            ColliderShape::Cuboid { .. } => "Cuboid",
            _ => "Other",
        };
        let mut shape_index = match controller.shape {
            ColliderShape::Capsule { .. } => 0,
            ColliderShape::Ball { .. } => 1,
            ColliderShape::Cuboid { .. } => 2,
            _ => 0,
        };
        let old_shape_index = shape_index;
        egui::ComboBox::from_id_salt("cc_shape")
            .selected_text(shape_label)
            .show_ui(ui, |ui| {
                ui.selectable_value(&mut shape_index, 0, "Capsule");
                ui.selectable_value(&mut shape_index, 1, "Ball");
                ui.selectable_value(&mut shape_index, 2, "Cuboid");
            });
        if shape_index != old_shape_index {
            controller.shape = match shape_index {
                1 => ColliderShape::Ball { radius: 0.5 },
                2 => ColliderShape::Cuboid {
                    hx: 0.3,
                    hy: 0.9,
                    hz: 0.3,
                },
                _ => ColliderShape::Capsule {
                    half_height: 0.9,
                    radius: 0.3,
                },
            };
        }

        match &mut controller.shape {
            ColliderShape::Capsule {
                half_height,
                radius,
            } => {
                ui.horizontal(|ui| {
                    ui.label("Half Height:");
                    ui.add(
                        egui::DragValue::new(half_height)
                            .speed(0.01)
                            .range(0.01..=10.0),
                    );
                });
                ui.horizontal(|ui| {
                    ui.label("Radius:");
                    ui.add(egui::DragValue::new(radius).speed(0.01).range(0.01..=5.0));
                });
            }
            ColliderShape::Ball { radius } => {
                ui.horizontal(|ui| {
                    ui.label("Radius:");
                    ui.add(egui::DragValue::new(radius).speed(0.01).range(0.01..=5.0));
                });
            }
            ColliderShape::Cuboid { hx, hy, hz } => {
                ui.horizontal(|ui| {
                    ui.add(
                        egui::DragValue::new(hx)
                            .speed(0.01)
                            .range(0.01..=10.0)
                            .prefix("X: "),
                    );
                    ui.add(
                        egui::DragValue::new(hy)
                            .speed(0.01)
                            .range(0.01..=10.0)
                            .prefix("Y: "),
                    );
                    ui.add(
                        egui::DragValue::new(hz)
                            .speed(0.01)
                            .range(0.01..=10.0)
                            .prefix("Z: "),
                    );
                });
            }
            _ => {}
        }

        ui.separator();
        ui.label(egui::RichText::new("Movement").strong());

        ui.horizontal(|ui| {
            ui.label("Max Speed:");
            ui.add(
                egui::DragValue::new(&mut controller.max_speed)
                    .speed(0.1)
                    .range(0.0..=100.0),
            );
        });

        ui.horizontal(|ui| {
            ui.label("Acceleration:");
            ui.add(
                egui::DragValue::new(&mut controller.acceleration)
                    .speed(1.0)
                    .range(0.0..=500.0),
            );
        });

        ui.horizontal(|ui| {
            ui.label("Jump Impulse:");
            ui.add(
                egui::DragValue::new(&mut controller.jump_impulse)
                    .speed(0.1)
                    .range(0.0..=50.0),
            );
        });

        ui.horizontal(|ui| {
            ui.label("Scale:");
            ui.add(
                egui::DragValue::new(&mut controller.scale)
                    .speed(0.1)
                    .range(0.01..=10.0),
            );
        });

        ui.separator();
        ui.label(egui::RichText::new("Crouch").strong());

        ui.horizontal(|ui| {
            ui.label("Enabled:");
            ui.checkbox(&mut controller.crouch_enabled, "");
        });

        ui.horizontal(|ui| {
            ui.label("Speed Multiplier:");
            ui.add(egui::Slider::new(
                &mut controller.crouch_speed_multiplier,
                0.0..=1.0,
            ));
        });

        ui.horizontal(|ui| {
            ui.label("Standing Height:");
            ui.add(
                egui::DragValue::new(&mut controller.standing_half_height)
                    .speed(0.01)
                    .range(0.1..=5.0),
            );
        });

        ui.horizontal(|ui| {
            ui.label("Crouching Height:");
            ui.add(
                egui::DragValue::new(&mut controller.crouching_half_height)
                    .speed(0.01)
                    .range(0.1..=5.0),
            );
        });

        ui.separator();
        ui.label(egui::RichText::new("Sprint").strong());

        ui.horizontal(|ui| {
            ui.label("Speed Multiplier:");
            ui.add(egui::Slider::new(
                &mut controller.sprint_speed_multiplier,
                1.0..=5.0,
            ));
        });

        ui.separator();
        ui.label(egui::RichText::new("Controller Config").strong());

        ui.horizontal(|ui| {
            ui.label("Offset:");
            ui.add(egui::DragValue::new(&mut controller.config.offset).speed(0.001));
        });

        ui.horizontal(|ui| {
            ui.label("Max Slope Climb:");
            let mut degrees = controller.config.max_slope_climb_angle.to_degrees();
            if ui
                .add(egui::Slider::new(&mut degrees, 0.0..=89.0).suffix("°"))
                .changed()
            {
                controller.config.max_slope_climb_angle = degrees.to_radians();
            }
        });

        ui.horizontal(|ui| {
            ui.label("Min Slope Slide:");
            let mut degrees = controller.config.min_slope_slide_angle.to_degrees();
            if ui
                .add(egui::Slider::new(&mut degrees, 0.0..=89.0).suffix("°"))
                .changed()
            {
                controller.config.min_slope_slide_angle = degrees.to_radians();
            }
        });

        ui.separator();
        ui.label(egui::RichText::new("Status (read-only)").strong());

        ui.horizontal(|ui| {
            ui.label("Grounded:");
            ui.label(if controller.grounded { "Yes" } else { "No" });
        });

        ui.horizontal(|ui| {
            ui.label("Crouching:");
            ui.label(if controller.is_crouching { "Yes" } else { "No" });
        });

        ui.horizontal(|ui| {
            ui.label("Sprinting:");
            ui.label(if controller.is_sprinting { "Yes" } else { "No" });
        });
    }
}

impl_simple_inspector!(
    CharacterControllerInspector,
    "Character Controller",
    entity_has_character_controller,
    remove_character_controller,
    character_controller_ui,
    custom_add => add_character_controller
);