Devlogs

Short updates for what I've done on a given day

2025-10-30 - Project Fragmentation

  • Backstage now caches a list of maps by their prefix (‘map_’, ’test_’, etc)
  • Added Change Map button to the pause menu. Shows a dropdown list of all maps, separated by prefix. Allows swapping between maps without using the console
    Screenshot showing the change map popup and the Change Map option in the pause menu
    A screenshot of the dropdown showing the list of maps
  • Added UiAudioCollection resource. Serves as a grouping of several common sound effects (button hover, button click, slider click, etc) combined with their desired gain offsets and pitches. Like an audio version of a Theme. Pause menu and options menu widgets have now been moved to refer to these instead of referencing their sounds directly to improve consistency. There’s a main audio collection and one specific to the pause menu button (where the hover and interact sounds are different)
  • Switched to Jolt physics. This makes the ledge grab bug easier to reproduce but fixes the weird slide jitter for free, so its worth it.
  • Fixed a bug where calling DebugLogStream.setup() twice with the same name in different locations wouldn’t return the same log both times as expected.
  • Added r_show_debug_info_group <name> <enabled> command. Used with register_debug_visualisation(name, callable). Currently used to control whether the ledge grab detection will show raytraces with DebugDraw3D using “ledgegrab_detection”
  • Replaced previous LedgeGrabRaycast Shapecast3D with new LedgeGrabDetectionCluster which, similar to EdgeDetectionCluster contains raycasts and logic specific to detecting ledge grab conditions on its own.
    • Grabbable surfaces are no longer detected with a vertical sphere shapecast. Instead we’re now using a slanted Raycast3D that starts slightly behind the player’s radius and extends down 1m and forward 0.2m. This ensures that a raycast can never start within a solid face.
    • This fixed the bug where you could grab the seam between two flush concave collision shapes.
    • Ledge grabs now require that the surface struck by the raycast have a normal that faces at least 80% vertical.
  • Added “ledgegrab_vectors” debug info group to show the nudge vector and collision vectors considered during a ledgegrab
  • Fixed the bug where ledge grabbing the corner between a ledge and a wall would sometimes get you stuck. get_wall_normal() only returns the normal of one of the touching surfaces and thus the nudge direction would push into the wall instead of the ledge. PlayerLedgeGrabState now iterates through all collisions and picks the normal that closest matches the camera direction.
  • Added “interior” property to env_lightmapgi. Maps 1:1 onto the interior option of godot’s builtin LightmapGI but also sets Environment Mode do DISABLED when turned on.
  • Fixed a bug in MapLightmapHelper where light blockers would be hidden prior to a “bake all” instead of shown
  • Fixed a bug where the HeadCollisionShapecast on the player controller extended slightly above the StandingCollisionShape, leading to being unable to stand up in places you could fit after crouching.
  • Added a bit more to map_tutorial
    alt text

2025-10-29 - Project Fragmentation

  • Added a basic pause menu. Pause menu no longer displays the options menu by default
    alt text
  • Added pause_menu_button UI widget to handle sfx
  • Removed the Quit to desktop button from the options menu now that there’s a button for it on the pause menu
  • Move the “save changes” button to the other side of the options menu
  • Reduced the sound of mouse-over sfx from -5db to -15db
  • GameManager now tracks the desired pause state requested by the player separately from the pause state of the scene tree and will automatically repause the game if a different node unpauses the tree.

2025-10-28 - Project Fragmentation

  • Added info_target entity. Nothing but a named Node3D.
  • Added info_teleport_destination entity. Same as info_target but with visualised with a player spawn location and orientable
    alt text
  • Added AbstractTriggerVolume class as a subclass of Area3D. Requires handle_contact(player: PlayerController) be implemented
  • Added trigger_teleport entity as an AbstractTriggerVolume. Teleports the player on contact to either an info_target or an info_teleport_destination. If the target is a teleport destination also rotates the players vision to match the new location.
  • Migrated trigger_respawn, trigger_win, trigger_timetrial_start, trigger_timetrial_checkpoint and trigger_timetrial_finish to be descendants of AbstractTriggerVolume.
  • PlayerController now calls handle_contact and handle_leave on contact with an Area3D AbstractTriggerVolume. All existing _on_enter_trigger_volume and _on_exit_trigger_volume has been reduced to these calls.
  • Backstage now emits map_change_started before beginning a map change or hot reload.
  • GameManager now emits backstage_ready once backstage is available.
  • TimeTrialManager now supports ending timetrial mode. All timetrial state is reset back to null or zero. This currently purges the set PB and split. Timetrial mode will automatically end if a map change is triggered.
  • TimeTrialManager emits a warning in the console if the current map doesn’t support time trial mode.
  • Added trigger_repeatable brush entity. On contact with the player, calls do_trigger() on every node in its “target” group that has a “do_trigger” method.
  • Added info_command entity. The first entity to support do_trigger(). Takes a command string an a property. When triggered, executes the given command in the console.
  • Added the ledge grab monolith to test_movement. Each pillar is constructed a different way. Allowed me to determine that the phantom ledge grab bug is specific to gaps between ConcavePolygonShape3Ds.
    alt text

2025-10-27 - Project Fragmentation

  • Adjusted monitor override logic to always move the window to the Primary monitor instead of just monitor 0.
  • Added func_geo_convex brush. Like func_geo but uses Convex collision polygon type instead of Concave.
  • Added a very minor vignette to the UI layer
  • Tweaked geometry in map_tutorial slightly. Just to try and fix various bits of light bleed or replace certain func_geos with func_geo_convex where i was having physics issues
  • Added DebugLogStream as a class of separate loggers that can be selectively silenced or enabled. Duplicate messages sent to the same LogStream collect together with an indicator of how many times they’ve occured.
  • Up to four enabled LogStreams can be displayed on screen at a time.
    alt text
  • Added debug_log_list_streams, debug_log_show <name>, debug_log_silence <name>, debug_log_show_all and debug_log_silence_all commands.
  • Player state changes are now logged to the PLAYERSTATE log stream.
  • A few of the ledgegrab print statements are now logged to the LEDGEGRAB log stream.
  • None of this extra logging is useful to me right now, but having the ability to optionally log quite verbosely into a filtered stream will be useful

2025-10-26 - Project Fragmentation

  • Added point_dev_warppoint entity, provides a named marker that’s only visible when developer_mode is enabled
  • Added warp_to <name> cheat. Immediately teleports the player to the named warpoint, if it exists. Autocompletes with all warppoints in the current map.
  • Added warp_to_next cheat. Gets the closest warp point to the player and teleports the player to the warp point that comes next in the dev_warppoint group after it. Order is arbitrary based on how FuncGodot builds the map.
  • Added warp_to_last cheat. Returns the player to the last warp point they warped to via warp_to or warp_to_next
  • Added teleport_to_location(position) to PlayerController, bundles together setting global_position, zeroing out velocity and transitioning to the IN_AIR state
  • PHYSICS CHANGE: Increased walljump force from 13 to 14. Adjusted walljump direction to push 5% more out than up. Didn’t account for increased air control from yesterday allowing to chain walljumps against the same wall so strongly, this seems to help a bit.
  • Did more work on the new tutorial map (reverted to grayboxing for now)
    alt text

2025-10-25 - Project Fragmentation

  • Redesigned the Speedometer to match the current theme better. Speedometer now shows an extra digit
  • Moved the “Segmented run” warning in timetrial mode to the top left corner to avoid it colliding with the speedometer.
  • Added interface_always_show_speedometer option. Starts disabled.
  • Added interface_show_speed_in_units option. Starts disabled. Makes the speedometer show the speed in quake units instead of meters.
  • When changing between fullscreen and windowed, the game will now force itself back into the center of Monitor 0. Fixes the issue I’m having where when the game starts from x11 instead of wayland it appears on the wrong monitor. This should be a setting later but for now its just hardcoded.
  • PHYSICS CHANGE: Halved air resistance, Increased air acceleration by 25%.
    • Air acceleration speed gives nicer air control overall, should address fedback
    • Knock-on effect of air resistance vs air acceleration is that above 20m/s it becomes faster to let go of the movement keys and coast, as the way the player lerps towards the desired speed is no good.
      • I will need to swap out the current lerp system for something more akin to how quake does it to get around this.
  • Added a couple new textures.
  • Started grayboxing a new tutorial level to replace the tutorial section of spire1 (textures added for screenshot)
    alt text

2025-10-24 - Project Fragmentation

  • Cache of map names is now managed by Backstage, not DebugHelper
  • Minorly redesigned the loading screen
  • Added new InterfaceFX audio bus and audio_interfacefx_volume option to control it. Seperated all UI sounds (including limboconsole) out onto this bus so the sounds can be controlled separately
  • Moved timetrial_mode command to TimeTrialManager.gd
  • Added developer_mode var on GameManager and matching command to set it. Defaults to true on debug builds, otherwise false. Currently doesn’t do anything on its own
  • Added point_devtext point entity. Shows some UI text mapped to a 3d position at one of two fixed sizes. Disappears at a fixed range to avoid getting too large and supports occlusion culling. Has an toggle for developer_only which makes the text only appear in game when developer_mode is enabled.
    alt text
  • Gave light_omni an icon
    alt text
  • Added custom SceneTracker EditorPlugin to the project. Keeps track of the current scene open in the editor and updates current_scene.txt to be the file path for that scene whenever it changes
  • Added --mapfile=<filepath> command line argument. Functions like --map but reads the content of a file and uses that as the map name instead. If the file doesn’t exist, is empty, or does not list a scene that exists in the maps directory, loads the default map like normal.
  • Configured “Main Run Args” setting in the project to always include ++ --mapfile=res://current_scene.txt. This ensures running the game with F5 will always load the map i’m currently editing.
  • Added point_worldtext point entity. Shows a Label3D at the set position and orientation. Has the same properties as point_devtext as well as an option for colour.
    alt text

2025-10-23 - Project Fragmentation

  • Reskinned the limbo console to not be HL1 themed anymore
    alt text
  • Changed the limbo console noises to be my own sounds, removing the only remaining hl1 sounds from the project
  • Limbo console now plays its UI sounds through the SoundFX bus
  • Limbo console will now pitch the close sound lower if its the same as the open sound
  • Added display_render_scale Option. Starts at 100% and allows the range 0.5 to 1.5. Controls the 3d scale factor on the root viewport. Useful for running the game at a lower resolution (necesssary for developing on my laptop)
  • Added four look_left/up/right/down keybinds that have no default binding. When set allows the player to look around using the keyboard (necessary for developing on my laptop) (i tested and i could get from the start of the current map to the end using only keyboard, which is nice)
  • Added Backstage, replaced world.tscn as the initial scene to load on start. Backstage is now responsible for changing scenes instead of GameManager and does so via async threaded loading. Displays a new nice loading animation during the transition between scenes.
  • map now actually suggets maps from the map cache
  • Added maps command, lists all maps in the map cache
  • Added restart which hot-reloads the current map. Does this by calling hot_reload_active_scene on backstage. Backstage will queue_free and reinstantiate the last loaded packed scene making another call to ResourceLoader, Flashes the loading sceen for a fraction of a second but i believe should be as fast as the old method.
  • Game now accepts --map=<mapname> as a command line argument. Will automatically load into this map if possible when provided instead of defaulting to world.tscn. Primary usecase is i can trigger launching the game from trenchbroom.

2025-10-22 - Project Fragmentation

  • Options now precalculates all valid bindstrings (both mouse and key) at start. This is arguably only useful for providing autocomplete to the bind command but its nice to have.
  • Added reset_all_keybinds() as a utility function on Options to bulk reset only keybind related options
  • Added KeybindSettingsWidget which is responsible for displaying and updating both binds for a given key.
  • Added a second tab to the options menu for all currently implemented keybinds.
  • GameManager now handles the pause event in _input() instead of _process(), this allows the OptionsMenu to completely hijack all inputs for any source (necessary to capture the new key for a keybind without anything else reacting)
  • Thelimbo_console_toggle action has now been renamed to dev_console. input_keybind_dev_console is now an exposed keybind that can be rebound. This did require me to change the hardcoded input name in limbo console directly.
  • Added the gameplay_tap_to_respawn option. Starts disabled. Switches from “tap to place checkpoint, hold to respawn” to “tap to respawn, hold to place checkpoint”

2025-10-21 - Project Fragmentation

  • Settings Float Widget will now show a rounded version of the value in Raw mode if the step value is a whole number
  • Fixed audio bus volume’s not being updated initially and not applying their loaded setting until the value had been adjusted at least once.
  • Created DisplayManager autoload. Moved the fullscreen logic out of GameManager to it.
  • Added a bunch of low-hanging-fruit options:
    • Added the gameplay_fov option. UI slider covers range 60 to 100 in increments of 1
    • Added the gameplay_camera_shake option. Starts enabled. Currently disables wind shake at high speeds if turned off.
    • Added the gameplay_walk_by_default option. Starts disabled. Inverts walk behaviour.
    • Added the gameplay_auto_bhop option. Stards enabled. When disabled, jumping only ever triggers when a jump is buffered and pressing jump on the ground does buffer a jump.
    • Added the display_fullscreen option and added it to UI. Previous fullscreen toggle bind has been moved from T to Alt+Enter as would be standard
    • Added the display_vsync option and added it to the UI. Starts enabled.
    • Added the display_max_fps option. UI Slider covers the range 30 to 240 in increments of 10. Starts at 120
  • Removed the “release_mouse” binding now that pausing always frees the mouse
  • Added a “quit to desktop” button to the options menu so the game can be closed from fullscreen
  • Settings heading section spacing is no longer hardcoded at 25
  • Added custom icons for settings widgets and audio players
  • OptionEntry now supports can_be_not_set. Options set this way support being saved as null. Null values are saved to the ini file as the “UNSET”
    • Caveat: optional settings must be typed as Variant instead of a static type, or setting to null doesn’t work
  • Options will now log and reset values from the config which have an incompatible value (e.g from manual tampering)
  • Options supports adding new OptionEntries programatically. Dynamically named options have their values stored in RUNTIME_ADDED_OPTION_VALUES as new properties can’t be set if they’re not defined in the code at compile time
  • Options now has get_option, functions similar to get but will check if the property exists in RUNTIME_ADDED_OPTION_VALUES first.
  • Options now has input_event_to_bindstring() and bindstring_to_input_event() to convert InputKeyEvent and InputMouseButtonEvents to and from a simple string form (e.g. “key_w”, “key_space”, “mouse_left_button”, “mouse_wheel_up”, etc)
  • Options now automatically generates primary and alternative input_keybind_<action> options for all actions in the InputMap (except for those in HIDDEN_BINDS) on start . Default primary and secondary bindings are based on what’s configured in the InputMap
    Screenshot of options.cfg
  • Whenever an input_keybind_<action> option is updated, the InputMap is updated to match
  • Added bind action primary_button [secondary_button] to update keybinds conveniently.
  • Added unbind action to clear bindings for an action
  • Fixed an bug where pressing jump for a single frame didn’t trigger the jump (is_action_pressed triggers the frame after is_action_just_pressed if its still held down). This has the knock-on effect of allowing jump on mouse wheel actions, which has interesting results

what if a bug had a website

emily/chloe
it/its/she/her
occasional maker of things and poster of thoughts