// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® module Basic.DebugCommands; import std; import Crafter.Debug; import Basic.World; namespace Debug = Crafter::Debug; namespace Basic { struct SpawnArgs { Debug::Required y; float x = 0; float vy = 0; float radius = 0.1f; }; struct BallArgs { Debug::Required id; }; } void Basic::RegisterDebug(World& world) { // Commands run on the main thread, between ticks, so they touch the // world as the simulation does. Debug::Command("World_Spawn", "Drop a ball from height y (m), moving at vy (m/s, up is positive); returns its id.", [&world](const SpawnArgs& args) -> Debug::Result { if (args.radius <= 0) return Debug::Error{ "BadRadius", "radius must be above 0" }; if (*args.y - args.radius <= world.floor.top) { return Debug::Error{ "BelowFloor", std::format("y must clear the floor's top ({} m) by the radius", world.floor.top) }; } return world.Spawn(args.x, *args.y, args.vy, args.radius); }); Debug::Command("World_Reset", "Remove every ball and drop the scene's five again.", [&world] { world.Reset(); }); Debug::Command("World_Clear", "Remove every ball.", [&world] { world.Clear(); }); Debug::Command("Ball_Get", "One ball's position, velocity and state.", [&world](const BallArgs& args) -> Debug::Result { if (const Ball* ball = world.Find(*args.id)) return *ball; return Debug::Error{ "NoBall", std::format("no ball {}; `get balls` lists them", *args.id) }; }); Debug::Watch("physics.gravity", world.physics.gravity, { .min = -50, .max = 0, .help = "m/s^2" }); Debug::Watch("physics.restitution", world.physics.restitution, { .min = 0, .max = 1, .help = "share of speed kept by a bounce" }); Debug::Watch("physics.substeps", world.physics.substeps, { .min = 1, .max = 16, .help = "collision checks per tick" }); Debug::Watch("floor.top", world.floor.top, { .help = "m" }); Debug::Watch("floor.thickness", world.floor.thickness, { .min = 0.01, .max = 10, .help = "m" }); Debug::Watch("world.time", [&world] { return world.Time(); }, { .help = "simulated seconds since the last reset" }); Debug::Watch("world.count", [&world] { return world.Balls().size(); }, { .help = "balls in the scene" }); Debug::Watch("world.lost", [&world] { return world.Lost(); }, { .help = "balls below the floor" }); Debug::Watch("world.fastest", [&world] { return world.Fastest(); }, { .help = "m/s, last tick" }); Debug::Watch("balls", [&world] { return std::vector(world.Balls().begin(), world.Balls().end()); }); }