Everything is Great! Until it isn’t…
When last we left off, before I was interrupted with 10 Employee Reviews, we had a working system able to pop out goal driven text like this:
Quest Plot 1: Alice Kerslake wants Eliminate - Persuade Other NPC - (Underlying reason: ATTACKING US) Template: Find Person, Kill Generic, Deliver Information
Alice Kerslake, Female, Mid wealth, age 21
Player: Do you have any work for me?
Quest Giver: I believe it's critical that we convince [NPC] about the threat of the Skeleton.
Player: Why?
Quest Giver: There have been clashes between the Skeleton and our people. Not long ago my niece was injured in a [enemy] attack. People are going to die unless we get rid of them. [NPC] has great influence in these matters. If he can be persuaded, others will join the fight. Can you help me?
Player: Yes, I'll help you
Quest Giver: Thank you. I'm afraid [NPC] is notoriously stubborn. And with these recent attacks, I know that we can't do this alone. It would be helpful if you could find somebody named [NPC]. I believe they may prove useful.
The C# code takes game context to query excel files for a speech template. One like this:
Quest Giver: I believe [Would You Kindly:Quest Objective] [Quest Objective:objective] [problem] [Stakes:Time] [Stakes:Subject] [Event:problem] [Fear:goal] [Reason For Task:objective:goal]
Anything in square brackets is replaced by the system.
There are reserved words like objective or goal which are replaced by complex variables in engine.
Then there are straight lookup replacements like [Stakes:Subject], with the colon (:) separating the columns. The data looks like this:
Couple of things we like about this:
Super easy to mix natural language with template values and variables. Nolan only has to know a few key words (objective or goal) but the rest is up to him. He chose to make a grouping known as “Stakes” with sub-groups of Time or Subject. Whenever you can remove a programmer from the workflow, yer doing it right! 😛
The system supports 4 column levels of specificity before returning the line or phrase requested. This seems flexible enough to handle our needs.
Case insensitive search! I can’t stress enough how important it is to put the little bit of extra effort into making string compares case insensitive so designers can just do their work. The rule is to allow any form of bastardized input to make the game work. Try to remove as many barriers as possible. I strip out leading and ending spaces, special characters, blanks, and nulls. No one likes being blocked for forgetting a capital S or accidentally leaving a blank row.
Fast iteration cycle. We can change the excel template or dialogue lines while the program is running and see the results immediately.
Everything is looking great, and then human nature kicks in where if you give an inch they want a mile….
Nolan wants 3 more columns of specificity: relationship, goal, occupation
This brings it from 3 named columns to 7. And we’re still in the early stages! Experience says he’ll probably want at least 3 more as we do more with it. This isn’t sustainable. The excel sheet becomes unweildly with tons of blank columns most of the time, except for the few times he needs it.
This leads to errors.
Time for a new solution.
Hey, You Know That Thing That Works? Rewrite It. Generically.
The core of the system is how it looks up and returns randomized lines. This now needed to be changed.
There is always a point in a project/feature where you better understand the problem and can make a generic solution to really solve it and all it’s ilk.
In this case I had to throw away the concept of named columns. Instead, each : (colon) is simply a separator saying “more specific parameter incoming!” As long as Nolan puts the data in the same order as he calls for it, he can have as many columns as he wants.
It also allowed us to flatten the data. Instead of needing to put data in specifically named columns, with spaces all over the place and the easy ability to stick it into the wrong spot, data is always entered from left to right with increasing specificity. The above data is now:
Nolan got his additional columns. And he can have more if he wants them.
The parameter/variable analyzing and substitution code went from 100 lines to 30. By making it more generic, the code became simpler and clearer. The signs of a good system design!
It took just over a day to rewrite this whole thing.
So what did we achieve, well from an output perspective? Nothing new! In fact that was the test for the new system: did it output the same results as before?
Under the hood it is a lot better and now we can continue moving forward.