extends Node ## Central registry for all runtime-tunable parameters. ## Add a new param with one _reg_f() call in _register_all() — the menu picks it up automatically. ## Access: DP.f("key") • Persist: DP.save() / DP.load_saved() • Reset: DP.reset_all() signal any_changed(key: String, value: Variant) const SAVE_PATH := "user://debug_params.cfg" var _params: Dictionary = {} func _ready() -> void: _register_all() load_saved() func _register_all() -> void: # ── Movement ────────────────────────────────────────────────────────────── _reg_f("Movement", "thrust_impulse", 18.0, 1.0, 200.0) _reg_f("Movement", "thrust_vertical", 2.0, 0.0, 20.0) _reg_f("Movement", "thrust_min_angle", 15.0, 0.0, 90.0, 1.0) _reg_f("Movement", "thrust_max_angle", 90.0, 0.0, 180.0, 1.0) _reg_f("Movement", "thrust_charge_time", 0.5, 0.05, 2.0, 0.05) _reg_f("Movement", "max_speed", 50.0, 2.0, 1000.0) _reg_f("Movement", "roll_damping", 0.15, 0.01, 0.99, 0.01) _reg_f("Movement", "jump_velocity", 4.5, 1.0, 200.0) _reg_f("Movement", "visual_turn_speed", 12.0, 1.0, 200.0) _reg_f("Movement", "bounce_restitution", 0.55, 0.0, 2.0, 0.05) _reg_f("Movement", "bounce_min_impact", 1.0, 0.0, 20.0) _reg_f("Movement", "wall_bounce_restitution", 0.6, 0.0, 1.5, 0.05) _reg_f("Movement", "wall_min_bounce_speed", 6.0, 0.0, 30.0) _reg_f("Movement", "kick_impulse", 8.0, 1.0, 50.0) _reg_f("Movement", "kick_interval", 0.25, 0.05, 1.0, 0.01) _reg_f("Movement", "kick_vertical", 0.0, 0.0, 15.0) _reg_f("Movement", "kick_force_var", 0.2, 0.0, 1.0, 0.01) _reg_f("Movement", "kick_spread", 10.0, 0.0, 45.0, 1.0) _reg_f("Movement", "charge_kick_mult", 2.0, 1.0, 5.0) # ── Dust speed thresholds ───────────────────────────────────────────────── _reg_f("Dust", "dust_walk_spd", 1.5, 0.1, 10.0) _reg_f("Dust", "dust_charge_spd", 7.0, 1.0, 20.0) # ── Dust ────────────────────────────────────────────────────────────────── _reg_f("Dust", "dust_lifetime", 0.9, 0.1, 4.0) _reg_f("Dust", "dust_explosiveness", 0.35, 0.0, 1.0) _reg_f("Dust", "dust_spread", 45.0, 5.0, 90.0) _reg_f("Dust", "dust_gravity_y", -1.5,-15.0, 0.0) _reg_f("Dust", "walk_scale_min", 0.35, 0.05, 3.0) _reg_f("Dust", "walk_scale_max", 0.65, 0.05, 3.0) _reg_f("Dust", "walk_vel_min", 0.8, 0.1, 12.0) _reg_f("Dust", "walk_vel_max", 1.8, 0.1, 12.0) _reg_f("Dust", "charge_scale_min", 1.0, 0.05, 6.0) _reg_f("Dust", "charge_scale_max", 2.2, 0.05, 6.0) _reg_f("Dust", "charge_vel_min", 2.8, 0.1, 20.0) _reg_f("Dust", "charge_vel_max", 5.5, 0.1, 20.0) # ── Tail ────────────────────────────────────────────────────────────────── _reg_f("Tail", "tail_gravity", 9.8, 0.0, 30.0) _reg_f("Tail", "tail_damping", 0.08, 0.0, 0.5, 0.01) _reg_f("Tail", "tail_body_radius", 0.35, 0.05, 2.0, 0.05) _reg_f("Tail", "tail_wag_freq", 4.0, 0.5, 15.0) _reg_f("Tail", "tail_wag_idle", 0.3, 0.0, 2.0, 0.05) # ── Charge (both mouse buttons) ────────────────────────────────────────── _reg_f("Charge", "bull_charge_accel", 80.0, 1.0, 400.0) _reg_f("Charge", "bull_charge_max_speed", 76.0, 5.0, 200.0) _reg_f("Charge", "charge_ramp_time", 0.75, 0.1, 5.0, 0.05) _reg_f("Charge", "charge_head_pitch", 0.3, 0.0, 1.2, 0.01) _reg_f("Charge", "charge_head_speed", 5.0, 0.5, 20.0) # ── Body ────────────────────────────────────────────────────────────────── _reg_f("Body", "body_tilt_fwd", 0.015, 0.0, 0.1, 0.001) _reg_f("Body", "body_tilt_side", 0.01, 0.0, 0.1, 0.001) _reg_f("Body", "aerial_pitch_scale", 0.6, 0.0, 2.0) _reg_f("Body", "aerial_pitch_speed", 5.0, 0.5, 20.0) # ── Legs ────────────────────────────────────────────────────────────────── _reg_f("Legs", "leg_step_threshold", 0.15, 0.01, 1.0, 0.01) _reg_f("Legs", "leg_step_duration", 0.12, 0.02, 0.5, 0.01) _reg_f("Legs", "leg_step_height", 0.15, 0.0, 0.5) _reg_f("Legs", "leg_step_overshoot", 0.3, 0.0, 1.0) _reg_f("Legs", "gait_freq", 1.5, 0.1, 5.0) _reg_f("Legs", "launch_hold_time", 0.15, 0.0, 1.0, 0.01) _reg_f("Legs", "ragdoll_wobble_amp", 0.15, 0.0, 1.0) _reg_f("Legs", "tuck_apex_threshold", 0.5, -5.0, 5.0) _reg_f("Legs", "jump_tuck_speed", 8.0, 1.0, 30.0) _reg_f("Legs", "jump_tuck_up", 0.2, 0.0, 1.0) _reg_f("Legs", "jump_tuck_fwd", -0.1, -0.5, 0.5) _reg_f("Legs", "jump_land_threshold", 1.5, 0.5, 5.0) # ── Camera ──────────────────────────────────────────────────────────────── _reg_f("Camera", "cam_lerp_power", 6.0, 0.5, 30.0) _reg_f("Camera", "cam_sensitivity", 0.005, 0.001, 0.05, 0.001) _reg_f("Camera", "cam_min_zoom", 3.5, 0.5, 20.0) _reg_f("Camera", "cam_max_zoom", 40.0, 2.0, 80.0) _reg_f("Camera", "cam_spring_length", 7.0, 1.0, 30.0) _reg_f("Camera", "cam_min_vert_angle", -60.0, -90.0, 0.0, 1.0) _reg_f("Camera", "cam_max_vert_angle", 45.0, 0.0, 90.0, 1.0) # ── Matador ─────────────────────────────────────────────────────────────── _reg_f("Matador", "mat_spawn_count", 1.0, 1.0, 30.0, 1.0) _reg_f("Matador", "mat_walk_speed", 4.0, 0.5, 10.0) _reg_f("Matador", "mat_wander_radius", 22.0, 5.0, 50.0) _reg_f("Matador", "mat_idle_min", 0.5, 0.0, 5.0, 0.1) _reg_f("Matador", "mat_idle_max", 2.5, 0.5, 10.0, 0.1) _reg_f("Matador", "mat_hit_threshold", 4.0, 1.0, 30.0) _reg_f("Matador", "mat_ragdoll_impulse", 6.0, 0.5, 20.0) _reg_f("Matador", "mat_flee_range", 3.5, 2.0, 40.0) _reg_f("Matador", "mat_flee_speed", 5.5, 0.5, 12.0) _reg_f("Matador", "mat_charge_speed", 10.0, 1.0, 30.0) _reg_f("Matador", "mat_brace_range", 5.0, 2.0, 20.0) _reg_f("Matador", "mat_brace_duration", 0.7, 0.05, 1.5, 0.05) _reg_f("Matador", "mat_commit_dist", 2.5, 0.5, 6.0, 0.1) _reg_f("Matador", "mat_step_speed", 3.5, 2.0, 20.0) _reg_f("Matador", "mat_step_duration", 0.35, 0.1, 1.0, 0.05) _reg_f("Matador", "mat_dodge_cooldown", 5.0, 0.5, 12.0, 0.25) _reg_f("Matador", "mat_dodge_chance", 0.25, 0.0, 1.0, 0.05) # ── Debug ───────────────────────────────────────────────────────────────── _reg_b("Debug", "show_collisions", false) func _reg_f(section: String, key: String, default: float, min_val: float, max_val: float, step: float = 0.05) -> void: _params[key] = { "value": default, "default": default, "section": section, "type": TYPE_FLOAT, "min": min_val, "max": max_val, "step": step, } func _reg_b(section: String, key: String, default: bool) -> void: _params[key] = { "value": default, "default": default, "section": section, "type": TYPE_BOOL, } ## Read a float param. Panics on unknown key — intentional: typos should be loud. func f(key: String) -> float: return _params[key]["value"] as float ## Read a bool param. func b(key: String) -> bool: return _params[key]["value"] as bool func set_value(key: String, value: Variant) -> void: if not _params.has(key): return _params[key]["value"] = value any_changed.emit(key, value) ## Returns the full metadata dict — used by DebugMenu to build UI. func get_all() -> Dictionary: return _params func save() -> void: var cfg := ConfigFile.new() for key: String in _params: var p: Dictionary = _params[key] cfg.set_value(p["section"], key, p["value"]) cfg.save(SAVE_PATH) func load_saved() -> void: var cfg := ConfigFile.new() if cfg.load(SAVE_PATH) != OK: return for section: String in cfg.get_sections(): for key: String in cfg.get_section_keys(section): if not _params.has(key): continue var p: Dictionary = _params[key] var val: Variant = cfg.get_value(section, key) if p["type"] == TYPE_FLOAT: val = clampf(val as float, p["min"] as float, p["max"] as float) _params[key]["value"] = val func reset_all() -> void: for key: String in _params: _params[key]["value"] = _params[key]["default"] any_changed.emit("__all__", null)