May your aim be true.
Artemis is an ongoing narrative programming project by myself. After working on the narrative implementation on the game Project Nautilus, I wanted to turn the system I made into something more robust that other developers could use. For games where the order of who you talk to or what you do is variable, Artemis accesses rules and world state data to give the most appropriate and important delivery. It’s not about the means of delivery, like Ink or Yarn Spinner, but instead about deciding what should be delivered.
As discussed in this microtalk @ The Loaf, Project Nautilus took heavy inspiration from Hades's priority queues and Firewatch's Delilah brain. However, not every game has nearly as much written content as Hades; Project Nautilus used a priority stack for there to be recency bias (which Artemis allows you to choose between), and Artemis also takes inspiration from Left 4 Dead 2's Dynamic Dialog.
Check out the repository and read the documentation here!
As discussed in this microtalk @ The Loaf, Project Nautilus took heavy inspiration from Hades's priority queues and Firewatch's Delilah brain. However, not every game has nearly as much written content as Hades; Project Nautilus used a priority stack for there to be recency bias (which Artemis allows you to choose between), and Artemis also takes inspiration from Left 4 Dead 2's Dynamic Dialog.
Check out the repository and read the documentation here!
A more in-depth exploration of Artemis
On the main branch of the public repo is version 0.2.5. The way it all interconnects is as follows:
“The fletcher makes and stockpiles the arrows, and the archer decides which arrow to shoot. The archer can get more arrows from (or throw away some in) a bundle, and she uses her bow to fire them.”
The logic of Artemis is handled by Archers, who decide what "arrow" (narrative data point) to deliver to her bow. Archers will send items with a higher priority closer to the front. It also allows the option for “recency bias,” where instead of putting the newest X-priority item behind the X-priority items that are already there, it can put that item in front. It can also pick the equal priority arrows at random. Depending on the genre of a game, that can be an important distinction, and it can be changed at will by the narrative designers in the custom editors. The priority value of an arrow can also be determined by other means than a flat number, like the number of flags it needs to be met.
Artemis also checks arrows for criteria saved in flags. If the requirements aren’t met, an Archer will skip it. When compiling narrative items, Artemis tracks what flags are supposed to be set values. Enum IDs for these flags are made as they come up, and deleted when they’re not used by any arrows. That means multiple ways to deliver the narrative (i.e. NPC dialogue or letters) can still reference the same pool of flags. Being in the same pool also means developers can also look through everything and make sure no one made two different flags IDs for what are essentially the same thing.
The compilation of these narrative items and their flags use .CSV files.There are template classes for Fletchers (which turn the files into arrow assets) and Bows (which are game objects that take a fletcher's data and execute the in-scene delivery of a fired arrow). This means the developers have control over how to convert the strings into unique structs for a game’s needs, as well as how to deliver the narrative in-scene using these structs.
I’m currently working on an example game utilizing version 0.2.5 in the rituals branch as a proof of concept.
“The fletcher makes and stockpiles the arrows, and the archer decides which arrow to shoot. The archer can get more arrows from (or throw away some in) a bundle, and she uses her bow to fire them.”
The logic of Artemis is handled by Archers, who decide what "arrow" (narrative data point) to deliver to her bow. Archers will send items with a higher priority closer to the front. It also allows the option for “recency bias,” where instead of putting the newest X-priority item behind the X-priority items that are already there, it can put that item in front. It can also pick the equal priority arrows at random. Depending on the genre of a game, that can be an important distinction, and it can be changed at will by the narrative designers in the custom editors. The priority value of an arrow can also be determined by other means than a flat number, like the number of flags it needs to be met.
Artemis also checks arrows for criteria saved in flags. If the requirements aren’t met, an Archer will skip it. When compiling narrative items, Artemis tracks what flags are supposed to be set values. Enum IDs for these flags are made as they come up, and deleted when they’re not used by any arrows. That means multiple ways to deliver the narrative (i.e. NPC dialogue or letters) can still reference the same pool of flags. Being in the same pool also means developers can also look through everything and make sure no one made two different flags IDs for what are essentially the same thing.
The compilation of these narrative items and their flags use .CSV files.There are template classes for Fletchers (which turn the files into arrow assets) and Bows (which are game objects that take a fletcher's data and execute the in-scene delivery of a fired arrow). This means the developers have control over how to convert the strings into unique structs for a game’s needs, as well as how to deliver the narrative in-scene using these structs.
I’m currently working on an example game utilizing version 0.2.5 in the rituals branch as a proof of concept.
Technical information
Example custom Fletcher for debug messages.
Example custom Bow for the same.
The sorted dictionaries in version 0.2 mean we can simply skip anything that's already been looked at so long as we send back the index the next search should start at.
Example custom Bow for the same.
The sorted dictionaries in version 0.2 mean we can simply skip anything that's already been looked at so long as we send back the index the next search should start at.
Linear Search
The reason why some of the structures used (i.e. tuple, sorted list, priority queue, and sorted dictionary) are custom-written is for two reasons:
- Their equivalents aren't serializable. Saving/loading the state of a game's narrative may be deeply important to a game. It's something I'd like to implement down the line, or at least make possible for developers using Artemis.
- Some things need to be able to easily change how it works. Take the "Narrative Priority Queues" from version 0.1 turning into "archers," allowing for more options. It changed what determined the priority value (number of necessary conditions? flat value? the sum of both?) and if it has a sense of recency bias (stack? queue? random of whichever appropriate arrows have the highest value?). A C# priority queue has nearly none of those variations, let alone the ability to re-sort itself when those changes are made.
All criterion checks on flags can be handled all with the same "a >= x && b >= x" function so long as some math is done to convert a value's requirement into a range. The use of float.Epsilon is for changing the >= into a > check.
Criterion Constructor
The flag's IDs are a recompiling enum instead of strings! Internal symbols will save space and process much smoother, and other parts of the system are planned to use internal symbols through similar means. You can see this in the Goddess, which tracks what flags are being used or not. Flags that are created/deleted are added/removed from the newly written FlagID.cs file, and then that enum script is recompiled.
Internal String Compiler