May 2024 Devlog: NPCs and Spring Cleaning

Title says it all. This month is all about NPCs, bug fixes, polish, and more. No puns in the title this time.

Uncategorized

Last month was all about improving the performance of the game’s Terminal. This month, we instead focused on polishing the rest of the game’s interface. But that’s not all - as of early this month, Socially Distant’s NPC dialogue system is in a mostly-finished state. So let’s see what’s new in the game this month.

Git Commit History

This month, 81 commits were made by Ritchie to the game’s code. As always, here’s the changelog:

  • Fix issue where whitespace doesn’t get skipped at the start of parsing the next shell instruction in script parser
  • Add support for importing chat scripts into Unity
  • Add dev tools for chat scripts
  • Add support for shrugging in a chat script
  • Allow NPCs to send messages through chat scripts
  • Add support for observing chat branch updates in the UI
  • Add support for clicking chat branch choices
  • Hide chat branch list when the message input isn’t in focus
  • Visually polish the Interact menu
  • Add player typing animation in chat
  • Fix NPC typing delays
  • Add support for starting NPC interactions without dev tools
  • Add support for chat script conditions
  • Add support for tracking completed NPC interactions
  • Add support for MissionComplete, InteractionComplete, WorldFlag, and Lifepath interaction conditions
  • Add support for relationship-based encounter conditions
  • Add support for witness conditions and skill level conditions
  • Add support for generating mention links
  • Add Typer data to channels in World
  • Add support for typing indicators
  • Add support for interaction condition policies
  • Remove script execution time logging
  • Add better support for world tables with Narrative IDs
  • Fix handling of drag selection in the Terminal
  • Optimize desktop layout
  • Add support for loading content from Asset Bundles.
  • Move websites and missions to asset bundles
  • Use AssetBundles to store and load GUI programs
  • Convert GameManager into a singleton
  • Convert MailManager into a singleton
  • Convert MissionManager into a singleton
  • Cobvert WorldManager to a singleton
  • Fix ContentManager erroneously unloading asset bundles
  • Move exploits and payloads to Asset Bundles
  • Move all Command assets to Asset Bundles
  • Optimize various UI issues with Terminal
  • Fix chat auto-scroll not reaching the bottom
  • Add support for typing-based choices in chat
  • Add support for new message animations in chat
  • Cache interaction script syntax trees during game load
  • Cache mission script ASTs on game load
  • Fix chat not unsubscribing from backend observables
  • Add support for loading and saving the world from another thread
  • Fix major performance issues with desktop login
  • Polish style of chat UI
  • Revert support for StaticWidgetList pooling
  • Fix issue where Unity hangs if importing too complex of a chat script
  • Add support for generating NPCs via script
  • Finish NPC generation scripts
  • Use toggle switches in settings instead of checkboxes
  • Retexture the Input Field
  • Add minor tweaks to SectionWidget and window icons
  • Add various audio effect toggles for later use
  • Add various UI sounds
  • Replace typing sound
  • Add support for loading network assets from Asset Bundles
  • Add input field border animation
  • Finish animating Input Fields
  • Properly theme dropdowns
  • Add animations to dropdown interactions
  • Switch from Trixel-Creative/TrixelAudio to our own fork
  • Port audio code to acidic-audio
  • Remove all obsolete TrixwelAudioSource objects
  • Remove all references to TrixelAudioSource in code
  • Remove all missing script references
  • Remove old Settings Widgets and Graphic Picker
  • Change how the game boots
  • Add support for changing the game’s initialization flow via UI
  • Add various settings for controlling game boot
  • Add support for case/esac statements in sdsh
  • Merge ChatImporter with ShellScriptImporter
  • Allow player to disable the glitch bands effect
  • Properly theme buttons
  • Fix glitch bands not applying their settings at game start
  • Replace missing close button texture with shitty programmer art
  • Add support for generated default avatars
  • Improve anti-aliasing of the person in the default avatar
  • Add support for MSAA
  • Use new AvatarWidget in all Chat UI
  • Use new Avatar Widget in main menu

Note: These changelogs are generated by Git during the process of merging a monthly work branch (work/24.05) into the game’s master branch. Some of the commits, such as revert commits, are not included.

New Default Avatar Graphic

Screenshot of the game's Chat interface with the new avatar graphic

Avatars in the game used to just be solid white circles due to the fact we just didn’t have anything else to put there. That was fine earlier on in development when you’d rarely see them. But when we fleshed out the chat system, we felt it was finally necessary to at least get something to show.

There are still some missing textures throughout the UI, such as the “Direct Messages” button in the top left of chat. But the new avatar graphic definitely helps out. It can also be dynamically recoloured using a pixel shader, as seen in the chat messages in the above screenshot.

Interface polish

Several interface elements have been re-textured and animated, more-closely resembling the game’s UI concepts.

Screenshot of a dropdown. The dropdown is open, three items are visible. The selected item is "Adaptive."

Switches and Sliders

Screenshot of several settings fields. Two audio sliders and three toggle switches are visible. The first slider is partially filled, the second is at maximum. Two out of three of the toggle switches are turned on.

Info Boxes

There are now three color variants of the Info Box. Blue for general dialogs, yellow for warnings, and red for errors.

Screenshot of a blue question dialog asking the user if they want to save changes made in a file

Screenshot of the warning dialog that appears when the game asks if you want to quit

Screenshot of an error message saying that a file cannot be opened

Currently, the window’s titlebar doesn’t get recolored. This is because tabs still use a static texture set that can’t be easily recoloured, this will change in a future game build.

Interface performance fixes

Although we cannot convey this in a screenshot, many parts of the game’s interface have been optimized for CPU usage. Unity’s UI system has various automatic layout components that we used to use heavily for convenience reasons. However, with complex UIs like Socially Distant’s desktop, these layout components can quickly become a performance nightmare by causing massive spikes in CPU usage when the layout needs to update.

To counter-act this, we have switched to proportional and manual layout where possible. Many interface elements, such as windows and the status bar, now also use nested canvases. This means that they retain their own isolated layout trees and render geometry. If a window needs a layout update, it no longer forces the entire desktop to update its layout as well.

The Move to Asset Bundles

Traditionally, many of Socially Distant’s content resources were loaded using Unity’s ancient Resource.Load() mechanism. While it worked initially and got us as far as it did, it is very limiting.

  1. Resources must be placed in a dedicated Resources folder in the project
  2. They can only be loaded synchronously on the main thread
  3. They can only be loaded by absolute path or by object type
  4. They are included in the game’s static asset bundle, meaning it is very difficult (if not impossible) to use this for DLC or mods.

This resulted in a noticeable stutter when first loading a save file, as all the data assets needed for the game’s Career mode needed to be loaded in on the main thread. By moving to Asset Bundles, we were able to address all four of the above limitations and fix the performance issue.

  1. Asset Bundles are loaded on a dedicated Loading thread by Unity, and thus can be loaded asynchronously.
  2. You are not limited in where you can place bundled assets (now “Resources” folder), meaning organizing the game’s project is easier
  3. Assets in an asset bundle are addressable by more than just their absolute path and type
  4. Asset bundles are loaded by the game dynamically, when needed. They can also be loaded as part of DLC and mods.

This move is a massive win for performance and for productivity during development.

Future Plans for Open-Source

The move to Asset Bundles allows the game’s core to be separated from Career content. In the future this will allow us to open-source the game’s core codebase. This means the community can help contribute to the game’s core functionality, and for mod developers to be able to use the same tools used to develop the game’s Career mode.

Note: If the game is to be open-sourced, Career Mode will still remain private with its source repository being inaccessible to the community. This is because Career Mode is what you pay for when you buy the game on Steam.

Sound Effects

Thanks to the lovely support from the community on Ritchie’s Patreon, we were able to license a sound effect pack for the game. You’ll be able to hear it in-game when sound is further-polished. For now, just know it adds a lot to the game feeling alive.

Glitch Bands effect

A new post-process effect was added to the game for when your system is under load or under attack. It causes horizontal flashes to occasionally appear at random spots on the screen. It is inspired by an actual glitch on Ritchie’s system when using Xorg. A video of it was posted to Ritchie’s YouTube channel.

Finished Chat System

We decided to save the best for last.

One of Socially Distant’s most important mechanics is the dialogue system. It allows you to interact with NPCs in the game world using the chat application. You’ve already seen a screenshot of the interface, which is in a mostly-complete state now. The underlying mechanic is also almost fully implemented.

In a previous devlog we demonstrated how we turned the in-game command shell into a full-fledged scripting language for the game. This is part of what it was for. NPC dialogue interactions can now be fully scripted out in a bash-like syntax. If you know bash well, that might sound terrifying. But let’s look at what an example interaction looks like in code. Below is a Truth-Machine (a common example program used in the esolang community) written as an NPC interaction. This is about the most complicated that an interaction script can be, using loops and conditionals. Most scripts will not need that.

#!chat chat_truth_machine

metadata() {
  type dm
  start_type auto
  actor computer
  start_message "Play the Truth-Machine test script"
  repeatable true
}

# By default, the first actor defined in metadata() is the marked as the current actor when the script starts.
main_branch() {
  say "I am a computer."
  say "Please pick a number."

  branch 0 'Choose Zero'
  branch 1 'Choose One'

  wait_for_choices

  if branch_picked 0;
  then
    actor player
    say "I choose zero."
    
    actor computer
    say "You picked zero!"
    exit
  fi

  branch stop "Stop the script!"

  if branch_picked 1;
  then
    actor player
    say "I choose one."
    actor computer
  fi

  while branch_picked 1;
  do
    if branch_picked stop;
    then
      actor player
      say "Stop yelling at me!"
      actor computer
      say "You told me to stop computering!"
      exit
    fi
    say "You picked one!"
  done
}

This next example script shows the use of a case statement for handling the player’s dialogue choices. This is what most actual scripts will look like.

#!chat chat_test_casestatements

metadata() {
    type dm
    start_type auto
    actor ritchie
    start_message "Case Statements Test"
    repeatable true
}

main_branch() {
    say "Please enter a number."
    
    branch 1 One
    branch 2 Two
    branch 3 Three
    
    case "$(wait_for_choices)" in
        1)
            actor player
            say "I choose one"
            ;;
        2)
            actor player
            say "I choose two"
            ;;
        3)
            actor player
            say "I choose three"
            ;;
        *)
            actor player
            say "I choose none of them"
            ;;
    esac
    
    actor ritchie
    say "Okay."
}

Lastly, this example script shows how simple it can get to write an interaction.

#!chat chat_guilds_test

metadata() {
    type guild
    start_type auto
    actor brodie
    actor vaxry
    actor skelly
    start_message "Say hi"
    guild gld_hyper_land
    channel home
    repeatable true
}

main_branch() {
    actor player
    say "Hi!"
    
    actor brodie
    say "$(mention player) Hello!"
    
    actor vaxry
    say "hi $(mention player)"
    
    actor skelly
    say "I\'m a skeleton"
}

Special thanks to: Brodie Robertson, Vaxry, and TheEvilSkeleton for being unwilling participants in the above example conversation.

The idea behind all of this is that it is really simple yet really powerful. A writer can very easily put together an interaction script with minimal programming knowledge, and the script can even be automatically generated with a tool (theoretically). A programmer can then take advantage of the language’s turing-completeness and integration with the rest of the game’s systems, to further flesh out the interaction script if needed. Modders can also add their own scripts in a Content Mod. Nobody has to worry about what the game does behind the scenes to run the script.

How it works for the player

Obviously, although it is important, the development experience of chat scripts isn’t what really matters. It’s how the system works for the player.

Currently, chat interactions in Socially Distant work the same way as they do in most RPG games. It’s a simple mechanic and works well. When it’s time for you to say something in the chat, you get presented with a list of possible options. You can either click one or type a related message in to choose an interaction. The complexity comes in with what you say to an NPC and how that influences the narrative, but that’s a story for a future devlog.

Other minor notes

Here are some other minor changes that were made this month.

  • You can now change how the game boots up in Interface Preferences
  • Native Linux support has been validated
  • Many old UI elements and their associated code have been removed
  • Lots of missing script references have been fixed
  • Under Linux, the game now prefers Vulkan over OpenGL
  • Saving the game is now done asynchronously
  • TrixelAudio has now been forked as acidic audio, which is now the game’s audio system
  • The ban on singletons in the game’s code has been laxed, to make development easier where it makes sense
  • Some accessibility features, such as higher-contrast chat bubbles, are now implemented
  • Script syntax trees are now cached in-memory while the game loads

So that’s basically it.

This month tied a lot of foundational work together into a finished gameplay mechanic. We think that’s awesome and we hope you do too. Next devlog is all about Career Mode and the mission system, so stay tuned for that.