Devlogs

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

2025-11-16 - Project Fragmentation

  • env_sound now takes a pitch property.
  • Added logic_condition point entity. Behaves basically identically to Source’s logic_branch
  • Added env_sprite point entity. Displays a billboarded additive Sprite3D with a custom shader. Similar to func_godot, will automatically generate a new shader material with the correct texture for a sprite if it hasn’t been used before, otherwise will use the existing sprite material.
  • Updated test_interactables map to use these new entities.
screenshot of test_interactables again, showing more nodes than last time

2025-11-15 - Project Fragmentation

  • AbstractTriggerVolume now has enable, disable and toggle functions. Disabled volumes are marked as not monitoring and not monitorable.
  • AbstractTriggerVolume now has trigger_flags bitflags property. Right now only supports “START_DISABLED”.
  • AbstractTriggerVolume now has handle_continued_contact() function. Has a default nop implementation so not every trigger needs to implement it. PlayerController calls this function every frame for each trigger volume its in contact with.
  • Target strings for entites now support a function method (e.g. mytarget.disable to call disable() on that entity).
    • TBHelper gained get_target_string_group_name, get_target_string_method_name, validate_target_is_callable and validate_targets_are_callable to help with this
    • The previous default method do_trigger() has been replaced with on_default_trigger() and is called when a target string doesn’t specify a specific method.
  • TBHelper now has perform_target_trigger() to generalise the logic for handling triggers on targets. Also has perform_targets_trigger() for the array version
  • Added trigger_push brush entity. Applies a force either each frame or on contact to the player, either in a set direction or towards a target node.
  • TBHelper now has load_asset and load_asset_from_key_if_present for loading assets
  • trigger_repeatable now has a targetname so it can be enabled/disabled
  • Added env_sound point entity. Basically a wrapper around AudioStreamPlayer3D, gets its sound as a path param relative to res://assets/sounds
  • Added logic_relay point entity. Behaves basically like goldsrc’s multi_manager without the delay support (yet). Triggers up to 10 other entities from a single trigger. Can also be enabled/disabled/toggled.
  • Made test_interactables which so far is a small box with the new entities in it.
    Screenshot of trenchbroom showing the new test map

2025-11-07 - Project Fragmentation

  • Added flashlight state info to PlayerStateSnapshot. Flashlight still has the wrong angle when teleporting I’ll need to fix that later…
  • Added Gold times to timetrial routes, between par and gold. Set gold times by eye in the middle of my current author times and the par times and updated all maps. Updated all timetrial related UI to have an extra section for gold times.
  • Refactored the UI related signals that TimeTrialManager emits and how the timetrial ui reacts to them
  • Added the backwards timetrial route to the original map. This marks the final timetrial i need for the next playtest build.
    screenshot of the timetrial list for all maps
  • Did some very minor adjustments to the original map, mainly some clipping stuff or making certain things func_detail_illusory to make the experience of playing the map backwards smoother.
  • Disabled FXAA, enabeld MSAA x2 instead. Will need to add settings for this at some point.

2025-11-06 - Project Fragmentation

  • TimeTrialManager now plays the “beat par” or “beat author time” sounds whenever you set a faster time, not just the first time.
  • Added a separate AudioListener3D node to the player controller parented to head root. This stops the weird audio panning issue when affected by camera shake.
  • Added a less enthusiastic sound for when the player sets a new time thats slower than par
  • Replaced the existing “beat par” sound with a better one
  • tt_clear_splits is has been renamed tt_erase_route_time to make it more obvious it deletes the saved time
  • Added tt_use_dummy_times command. Controls whether dont_load_times is set on TimeTrialManager. When enabled, TTM will not load from or save to the TimetrialPlayerRecords and will always behave as if no prior saved times exist. Useful for testing.
  • Added tt_force_save_pb command. Overwrites the pb for the active route with the current best attempt regardless of if dont_load_times is enabled or if the current pb is worse than the saved pb.
  • Timetrial UI will now flash a banner when you beat your own PB or one of the two built-in times.
  • Updated the par and author times on test_timetrial to (8.0s, 6.19s) for main and (7.5s, 5.89s) for low_road based on my own times.
  • TimeTrialManager now has current_map_supports_timetrial_mode variable. Set on map load based on whether the map has one or more timetrial routes
  • Pause menu now has a Timetrial button. Its disabled by default but becomes enabled when current_map_supports_timetrial_mode is true.
  • Added the TimetrialRouteWidget ui widget. A reusable button that shows the display name and best time for a given route as well as badges for whether the player has beat the par and author times, as well as a coloured background highlight matching the highest badge the player has achieved for that route.
  • Added the TimetrialTimesPopup ui widget. A reusable sidepanel that shows the player’s best time, the par time, and the author time
  • Added the TimetrialLocalMapSelector ui component. Used to view all timetrial routes for the current map. Clicking a route immediately starts timetrial mode on the current route.
    screenshot of the route list
  • Clicking “Change Map” on the map changer popup hides the map changer.
  • TimeTrialManager now remembers the state the player was in before timetrial mode began. Switching routes or reinitializing timetrial mode doesn’t effect this. When timetrial mode ends, the player is returned to where they were initially. Tutorial button immediately opens loads the tutorial.
  • Added a new main menu scene. Uses main_menu_background.map as the foreground geometry.
    Half life 2 style background with a 3d vista
  • Backstage now has return_to_main_menu() function, which unloads the active scene if it exists and loads the main menu scene instead. By default without launch arguments Backstage will now load the main menu instead of a default map
  • GameManager now has is_on_main_menu. This is set to true by backstage when the main menu scene is loaded and false on any other map change. When true, the UI is always hidden and controls for bringing up the pause menu don’t work.
  • Set the author time on map_tutorial to 34.55
  • Updated ld58_original_map to include an info_timetrial_route Set par time to 40s and author time to 35.27s. I know i can do better for the author time but its good enough for now.
  • TimetrialRouteHelper now has a map name override, which when set allows it to be created outside of a FuncGodotMap. When the override is set, all timetrial volumes will scan for their linked checkpoints again when validating paths. This was necessary because the LD58 map has routes split across two godot maps, but ideally this never has to be used again.
  • TimeTrialManager now has a autostart_route variable. When set to a non-empty string TTM will automatically start timetrial mode with that route after the next map transition.
  • Added TimetrialGlobalMapSelector ui component. The main-menu version of the local component, shows a foldable container for every map that contains a timetrial route, and lists all routes for each map within. Clicking a route will immediately switch to the relevant map and begin timetrial mode on that route.
    screenshot of the new ui for routes

2025-11-05 - Project Fragmentation

  • Created NamedTimeWidget to replace the old best time element on the
  • Redesigned the timetrial related UI with new separate widgets for personal best time, part time and author time.
    part of the new ui
    part of the new ui
    • If the player has no current PB, or the players PB is slower than par, the par time widget is shown.
    • If the player’s PB is faster than par but slower the author time, the author time widget is shown
    • If the player’s PB is faster than author time, nothing is shown
  • Added unique celebration sounds for beating par and beating author times.
  • Added an info_timetrial_route to map_tutorial so that timetrials now again there. The par time is 40s which is a few seconds above what i can manage when taking the slow route. I haven’t set the author time yet but i know i can do at least 37.

2025-11-04 - Project Fragmentation

  • PlayerController now updates smoothed root immediately on teleport.
  • Backstage now keeps track of current_map_name based on the maps path regardless of whether the change_scene() call provides a name or not
  • All timetrial volumes (start, checkpoint, end) now have a routes property, which can be one or more route_names separated by commas.
  • Timetrial volumes now have followup_by_route, a lookup table for which followup volume comes after given a route_name. Part of post-build validation ensures that routes are not ambiguous and print an error if one node has two followups which share a route_name
  • TimetrialPlayerRecords now stores three keys for each route, one ending in _time, one ending in _splits and one ending in _revision. Time is the same as before (seconds to finish), Splits is a string of floats separated by commas, to represent the times a player eached each checkpoint, and revision is an int.
  • info_timetrial_route now keeps a list of all the volumes that form it’s route, in order. This is built as a postbuild step via travese_and_collect_checkpoints()
  • TimetrialRouteHelper now automaticall calls travese_and_collect_checkpoints on all routes immediately after map build.
  • TimeTrialManager now initializes the best_time SplitHistory based on whats in the TimetrialPlayerRecords object, assuming the revisions match. It will also save the current best_time split history back into the record when the player achieves a new best time, or their first time a run is set if no prior time exists.
  • tt_clear_splits command now erases the split and record time for the active route both from memory AND from times.dat
  • Added no_best_time signal to TimeTrialManager. Emits when the best time widget needs to be hidden, like after tt_clear_splits or switching routes from a route with a previous time to one without.
  • TimeTrialManager will now track and print messages in the console when you beat the built-in par time or author time.
  • This marks the new timetrial route system being mechanically complete, but there’s still a bunch of UI work that still needs to be done.

2025-11-03 - Project Fragmentation

  • Added add_entity_icon to TBHelper. Adds an internal billboarded Sprite3D with a given texture to a node that isn’t visible ingame.
  • UI now resets state on level transition. Fixes bug where the win screen would stay visible between levels.
  • Collectable counter UI only appears on levels which have at least 1 collectable.
  • Fixed a bug where the GameManager wasn’t being reset on hot reload.
  • Fixed a bug where speedrun_mode would persist to a different map.
  • Added AssetHelper autoload. Contains one method, load_asset, which is a replacement for ResourceLoader.load() that handles .remap files
  • Added info_timetrial_route point entity. Includes the name of a route, the route version (to invaldate old times when the map updates), and the par and author times. Adds itself to the group timetrial_route
  • Added TimetrialBuiltInTimes resource. Stores the par and author times for routes for any map.
  • Added TimetrialRouteHelper class, like lightmap helper. Any map with at least one info_timetrial_route in it automatically gets a timetrial route helper. Currently has a button to update the par and author times in the TimetrialBuiltInTimes resource.
  • Added TimetrialPlayerRecords class. Like the built in variety but reads and saves to user://times.dat.
  • TimetrialManager now loads both the built in times and the player records on start and on map changes reads whether a map has existing author/par times and whether the player has a PB. No saving happens yet though.

2025-11-02 - Project Fragmentation

  • The options menu now supports an optional “blurb” for each setting. The blurb is displayed at the bottom of the menu when the mouse is hovering over a setting with a blurb.
    alt text
  • Added interface_double_scale option. When enabled if the player’s window is larger than 1100px tall the interface will be rendered at 2x scale.
  • Added the flashlight and some sounds and minor animation for it. By default bound to F
    alt text
  • Timetrial mode restart is now bound to T by default, but peoople who have played before will end up with a collision
  • Finished the geo for the tutorial map, just need to texture it now
    alt text

2025-11-01 - Project Fragmentation

  • MapLightmapHelper now shows a bake button for each individual lightmap in a scene.
    alt text
  • Player now has freeze_movement variable. When true, certain motion states wont call _drive_motion, locking the player in place while still other doing state and effect logic.
  • Added trigger_freeze_movement brush entity. Sets freeze_movement to true on contact and back to false when the player leaves. Useful for preventing the player from hitting the bottom of a respawn pit.
  • Did a bunch more work on map_tutorial, The main design is basically done now
    a screenshot of the new area

2025-10-31 - Project Fragmentation

  • Completely remade the fog plane shader. Now exposes most properties on a per-instance basis and uses a triplanar texture parameter using vertex world coordinates instead of object coordinates.
  • Added FogPitPreset resource. Defines a collection of all the properties the new fog plane shader needs (colour transition info, alpha transition info, etc)
  • Added env_fog_pit brush entity. Replaces the old WorldFog setup that needed to be manually talored for each map. Fog pits automatically generate a plane mesh that fits their confines and generates the required number of planes. Takes the name of a FogPitPreset as a property to determine what values to use for the shader.
    Screenshot of a platform that opens into wide open spaces on two sides and two pits, every opening has a different coloured fog

what if a bug had a website

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