Log 1: Edit the world

Welcome to the first proper devlog of Horticular! I’m absolutely excited to finally be writing one of these, as it allows me to show the development progress in more detail than possible on Twitter.

My current development goal is to overhaul the first 90 minutes of playtime, supporting both future demos and improving playtest efficacy. As such, in this inaugural installation, I’ll be going over the brand-new editor, as well as some other changes to the player journey.

As I’ll be covering the editor, some descriptions will gravitate in a technical direction. But I have saved the more technical text for the optional sections (you have to expand them) that I call ???? Technical tidbits.

I hope you enjoy the read!

Editor v2

As advertised, Horticular will ship with the editor that I use to make the game. You will easily be able to make changes to metadata that govern the game and its storyline. Currently, that means changing or adding new items, surface types, animals, missions, and letters. With more added as needed during development.

So why spend time on replacing a working editor? Well, there were a few outcomes that I wanted to achieve:

  • Multiplatform support (Linux and Windows as of now).
  • Better performance.
  • More control over the UI components.
  • Resilience to user mistakes.
  • Convenience features for general development speed.

Truth be told, the old editor was a pretty dumb development tool. As soon as I planned to revamp the first 90 minutes playtime, I realized I would have to modify existing quests. The thing is, I didn’t trust myself to not make mistakes in those changes without realizing, and I definitely didn’t trust the editor to help me out.

???? Technical tidbits

In addition to the above-mentioned reasons, one key direction with the new editor was to abandon the previous hacky solution and embrace dependency injection, reactive MVVM, and an auto-generated UI based on the data model.

For that I’m using .NET 6, ReactiveUI, and proprietary data-model attributes for the editor to act upon at runtime.

Once the editor boots up, it configures itself to edit the current data-model with a reactive architecture. There’s no additional development when making changes to the game, other than marking foreign IDs with an attribute.

Here is an example of what the data-model could look like:

public class DataModel
{
    public List<GroundData> GroundData { get; set; }
    public List<QuestData> QuestData { get; set; }
    public List<DecorationData> DecorationData { get; set; }
}

public class GroundData : IGameData
{
    public int Id { get; set; }
    [ForeignId(typeof(QuestData))]
    public int QuestId { get; set; }
    public List<DecorationRef> Decorations { get; set; }
    /* Domain properties */
}

public class DecorationRef
{
    [ForeignId(typeof(DecorationData))]
    public int DecorationId { get; set; }
    /* Relationship properties */
}

public class QuestData : IGameData
{
    public int Id { get; set; }
    /* Domain properties */
}

public class DecorationData : IGameData
{
    public int Id { get; set; }
    /* Domain properties */
}

Visual changes

As you can see below, the first thing you would notice with the new version is the overall visuals.

The old (bright) and the new (dark) editor. Each displaying a list of data entries in a tab-view per data type.

While both versions are basically glorified spreadsheets, the underlying technology is completely different in order to support Linux and give me better control over the UI. That is why the new editor looks quite different visually; it’s just what comes out of the box (albeit with some minor tweaks).

One detail to notice is the color-coding that was added to quickly communicate what you are looking at. Unique IDs for each entry are blue, names are green, and references to unique IDs of other data types are orange.

???? Technical tidbits

The old editor was implemented in .NET using regular WinForms controls and, in addition to performance limitations, therefore was limited to Windows and macOS.

As of this writing, there is no Microsoft-backed tech that runs out-of-the-box on Linux. Hence, the switch to Avalonia UI. It is basically a multi-platform WPF that is deployed like any other .NET 6 application. Very easy to work with!

If you think the editor resembles a relational database administration tool, well, it basically is one.

Sorting & Filtering

If you have ever used a table interface (rows and columns), you are probably accustomed to sorting its content on any column. Ascending or descending. You might also be used to filtering out or in entries to make the data relevant to your work.

Well, I expect both in a table, and I have severely been wanting those features while using the old editor. Therefore, I took some extra time to make sure both use cases are covered.

In particular, the filtering box was added, and it filters on all columns, whether that be a number or a name. If you know that your item has a particular name or is unlocked by a certain quest, just enter the name or quest ID in the filter box.

Filtered search result, showing only quest entries with the text “Upgrading” in it.

Preventing duplicate IDs

We all make mistakes, so I wanted to make sure certain mistakes were impossible to make in the editor. In other words, protecting the data from the user.

For now, the editor not only makes it impossible to let two entries have the same unique ID (a crash-worthy offense when loading the game), but it also automatically cycles to the next valid one instead of merely complaining.

A small change, but this will help me a lot when I want to change the ID of an existing entry. Which is usually when I make changes and want to keep related quests in a natural ID order (for ease of finding them).

Propagating ID changes

Similar to the previous point, many data types contain references to others by their unique ID. For instance, a specific item (say a Yarrow) may be unlocked by a particular quest (the tutorial, for example). And it therefore references which quest that unlocks it in its data (for instance, the quest with a unique ID of 5).

Should you change the ID of the quest (say from 5 to 17), it is crucial that the item also points to the new ID. This is why I made sure that any changes to unique IDs now also propagate to every referencing entry.

This will help immensely when restructuring the game, as the user doesn’t have to manually track locations they would have to change. The editor does it for you automatically!

???? Technical tidbits

Continuing the relational-database theme, this is basically a cascading change to a primary key, referenced by other tables as a foreign key.

The reactive nature of the application makes it easy to add logic to data changes, so this feature only required the addition of propagation logic, accomplished with a separate dependency service.

The dependency service recursively traverses the data-model on a per-property basis. It saves a record of the path to each found foreign ID.

Given the data-model code above, the results of the dependency service would look something like this:

  • QuestData: Collection GroundData ⇾ Reference QuestId
  • DecorationData: Collection GroundData ⇾ Reference Decorations ⇾ Collection Decoration ⇾ Reference DecorationId

With the above entries, it is known that should the ID of a quest or decoration change, we have a referencing property to change. Then it is just a matter of traversing all game data instances of the correct type and update its reference.

The whole process basically looks like the following:

  1. Detect a change made to a property of an entry.
  2. If the property is a Unique ID property.
  3. Retrieve the previous ID value (done through change tracking).
  4. Get all data-model entries relying on the property.
  5. If the entry was pointing to the previous unique ID (and therefore affected by the change).
  6. Point property of entry to the new ID.

I realize this was harder for me to explain than I thought, but I hope this was understandable!

What’s next?

While I’m sure some minor things will pop up during development, there are no new features planned for the editor. However, the aim is to keep all tooling in the same place. And I do have some ideas for how to manage and extract other metadata from the game into its configuration.

When code is moved out to the configuration file, the editor will be updated to support the data. The idea is to then make the data-model one bespoke section and add additional sections for each purpose.

Other than that, the editor is already ready to ship in the upcoming release of Horticular!

Intro & Saves

Before wrapping up, I just want to give a smaller update on the game itself. Prior to replacing the editor, work was started on the player journey from startup to being immersed in gameplay.

Firstly, I have added an intro-cinematic when starting a new game. Its purpose is to set the stage for why you are in the world of Horticular and to briefly foreshadow game-mechanics. I’ll keep this under wraps a while longer, but its style is hinted at in a previous Tweet.

Similarly, I also added a startup intro to set the theme of the game. Its style is very similar to the ending of the reveal trailer. Below, I have included the first iteration of the intro sans music.

Finally, I also changed the slot-based save system for one that more resembles the conventional new/load/continue format. It was hard to motivate spending time on something else, as the previous setup showed player confusion in playtests and the conventional one is well established.


And that is that for this devlog! I hope you enjoyed the read. All feedback is welcome, so don’t hesitate to get in touch anywhere, such as on Twitter or via email.

I wish you a great day and I hope to see you next time! ????

Don't miss the next post.

Join the inDirection Games newsletter to receive a notification straight to your inbox!