Pages

Friday, February 27, 2015

Clearing up Some Rumors (in Thebes)

I’ve had some exciting tasks this week, including tracking down a particularly tricky save file bug as well as providing support for a mystery feature we'll hopefully be revealing soon.

But for this blog post, I’m gonna be talking about adding the appropriate rumors to sh5 Thebes. As I mentioned in this blog post, rumors are conversations that can seem like just flavor or world building, but also provide the necessary foreshadowing and/or context for events that might occur later in the game (depending on the player's choices).

When originally writing sh5_thebes, we didn't know exactly all the paths the story could take from there, so we just made a note to go back and add rumors. Since we now have a pretty good grasp of how the story’s going to shake out, it was time to get those rumors in.

The first thing I did was look at all possible events the paths leading out of Thebes could go to and examine the corresponding rumors. Then, I removed the rumors that would give redundant information. Sure, the rumor expounding on the state of the Spartan civil war would be relevant if the player chooses the associated path, but the conversations surrounding that choice already touch on a lot of that information. Instead, I chose to feature two rumors which would help introduce a character and a secret society that the party might unexpectedly stumble on in the future, hopefully making those appearances seem more foreshadowed and less Deus Ex Machina.

Even though both of these rumors were already written, they hadn't been implemented in the game yet, so there was some cleanup there too. A couple of flaws in conversation pacing or character voice weren't really obvious until we saw them in game. We additionally caught the characters casually referencing specific events in the 6th century BCE, so we added a glossary entry to make sure players could understand the context without needing to be a Greek history expert.


Sunday, February 22, 2015

Code Dive: Custom iterators in Hellenica (Part 1)

Recently I've been digging around in some code I wrote a long while back, and it reminded me of one handy feature of C# that I thought worth explaining here. It's been too long since we've had a hearty code post! There's a bit of background info I'll need to go over to make sure everyone's on the same page, so I've broken things up into two separate posts. This week I'll explain the C# feature, and next week I'll go over an example of its use in Hellenica.

When you write game code, or any kind of code really, you often spend a good deal of time iterating through collections of things. Usually this is something simple like a list of values, and it's easy enough to write code to step through them.


If you wanted to print out each of these values in order, you could write code like this:

foreach (int value in superImportantValues)
{
    print(value);
}

This would output:

12345678

Sometimes your data isn't so simply organized, however. Usually the code we write needs to perform some task under a specific set of constraints; perhaps we need it to run a specific operation very quickly for a large set of data, or maybe we need it to be able to do many things but only within a very small amount of memory.

Programmers have devised a number of special structures and techniques to store and interact with collections of data in ways that address these constraints. Consider this tree structure:

These are still the same super important values from our list earlier, but now the conceptual model used to represent the collection is very different. This specific structure is handy for quickly searching for a specific value, but it's not as clear how we might step through and add up the total of each of the values in this... thing.

It would be great if there were a simple way to iterate over these values just like I did in the previous list example without having to add complexity to my code every time I wanted to do so. In C#, there is a built-in type called IEnumerator<T> that allows you to define an iterator over any sort of complex collection of items, so long as you can tell the compiler how to do it. Here's what that might look like for this type of data structure:

IEnumerator<int> in_order_tree_traversal(Node root)
{
    Node current_node = root;
    Stack<Node> node_stack = new Stack<Node>();

    while (node_stack.Count > 0 || current_node != null)
    {
        if (current_node == null)
        {
            current_node = node_stack.Pop();

            yield return current_node.value;

            current_node = current_node.right;
        }
        else if (current_node.left != null)
        {
            node_stack.Push(current_node);
            current_node = current_node.left;
        }
        else
        {
            yield return current_node.value;

            current_node = current_node.right;
        }
    }
}

I claim that this function will climb around that tree from above and return each of our super important values in order. The specifics of the algorithm aren't vital to this discussion, but feel free to dig through it if you're interested.

I do want to draw your attention to the bolded statements in the code, though.

The first is IEnumerator<int> in the function definition at the top. This tells us that calling this function in_order_traversal will return to us an object that will enumerate a collection of integer values. Sounds promising so far!

The second are the yield return statements, of which there are two in the example. This is the C# secret sauce. These are the guys that tell the program, "Hey! We found another value that you might care about!" When execution hits one of these statements, it returns the value just like a normal function, but importantly, the state of the function is preserved -- local variables, the next instruction, everything. So, when we come back and ask for another value, the process can start up right where it left off.

It might help to see how to use this IEnumerator<int> thing. Here's an example similar to our previous one:

IEnumerator<int> superImportantValues = in_order_traversal(superImportantTree);
while (superImportantValues.MoveNext())
{
    print(superImportantValues.Current);
}

Pretty cool! Even though the user code looks fairly similar, internally we're now climbing all over a complex tree structure instead of just walking through a linear list. Let's look at what's going on here:

IEnumerator<int> superImportantValues = in_order_traversal(superImportantTree);

First, we're calling our new function and getting an object of type IEnumerator<int>. This is just a thing we can use to iterate over a collection of integer values.

while (superImportantValues.MoveNext())
{

Next, we use MoveNext() to find the next value in our tree by running our code until it hits another yield return statement. (If we reach the end of our iterator function without finding a yield return statement, MoveNext() will return false, signaling to us that we've seen all of the values.)

    print(superImportantValues.Current);

Lastly, we use .Current to look at the last value that our iterator found. We can keep referencing .Current to access the same value until we call MoveNext() again.

Still with me? That's one example of how to use yield return statements and the IEnumerator<T> type. All in all, a pretty convenient way to write a custom iterator for a collection of data.

Perhaps more interestingly, there's no limitation to the kind of collection you can iterate over. That collection could even be something you generate on the fly instead of something stored in memory. Next week, I'll go over one way we use this idea in Hellenica to do some of our combat calculations!

Saturday, February 14, 2015

The Dialogue Tool

(If you’re not familiar with how our dialogue system works, this post should fill in some details about what we mean when talking about “criteria”.)

This past week, I’ve been doing a variety of dialogue fixes, which aren’t particularly interesting on their own. However, the (self-built) tool I’m working in is pretty cool, so I thought I’d talk about that.

There's a bunch of extra stuff in it now, but the basic functionality is being able to add a dialogue line (green) and modify the dialogue line and the details it sets (red) and criteria (blue) that control when it plays.



By itself, these pieces are enough to implement our dialogue system, but in practice, manually setting all the fields to create conversations ended up being too labor intensive and error prone.

To that end, most of the other buttons don't actually add new functionality, they're just ways of automatically filling in fields in ways that were useful. A key example is the "Add Reply" button, which creates a new dialogue line and automatically sets criteria and details to have it continue from a previous line.

Another good one is "Clone Line", which exactly copies a previous line. A user then just needs to modify the text and add an extra criterion and whala!—she just added a custom line in the middle of a conversation.

My favorite feature, though, is the conversation converter. To take a step back, we do a lot of our writing in Google docs first, because it gives Siobhan and me a good place to share ideas and comment on each other's work (as well as sharing it with others to proofread). The conversation converter gives us a way to automatically convert this “normal” writing into lines in the dialogue tool.



Every line of dialogue in the conversation is inserted, with speakers and conversation details automatically set up for us. This only works for branchless conversations, but using the functionality above, it's usually not that much work to go back and add branches. (At least mechanically. The actual writing a coherent story filled with all these branches is still really hard.)

Hope you've enjoyed this look into how the Dialogue Tool works. If you’re curious for a more in-depth explanation on anything, let me know in comments and I’ll either explain there or in a future blog post.

Saturday, February 7, 2015

Hellenica Environments: Rhodes

It's now week two of post-PAX South life, and we're back into the swing of things. I'm currently in the middle of tweaking combat and building new levels, so it seemed like a good opportunity for an art update!

This week's travel destination is Rhodes, an elevated island city surrounded by a great wall on one side and staggering elevated docks on the other. Led by the so-called Mechanist-King Evagoras, the island nation is known for its theomechanical marvels as well as its generosity towards its allies and those in need.

We actually began work on Rhodes before Daniel, our environment artist, started working with us. At the time, we were still searching for the right partner, and we decided to give Collateral Damage Studios a trial run with Rhodes.

To start off, we wanted to work out the important elements unique to Rhodes. This meant figuring out how the steam-powered ship lift worked and what the Colossus of Rhodes may have looked like. We also knew we'd need to include some other elements for story reasons, so we tackled those as well. This exploratory part of the process is always my favorite.

 


A couple of the lifts relied on some questionable physics, so we decided on the rightmost implementation. In general, we were pleased with most variations of the other elements, though we really liked the mechanical solar system installation on the observatory.

With some more concrete details in mind for these elements, we started thinking about the composition of the scene.


We knew we wanted a dramatic view of the harbor that showcased the Colossus as well as the elevated docks. The first option actually worked the best for us, as it provided a nice vantage point from which to view the cityscape in the background. After a little more playing around with the finer points, we settled on option B from the second image. The airy, open feel of this composition provided a nice contrast to some of our other city locations that feel more cramped and urban.

Once that was settled, the artist took off towards the final image. CDS was great about keeping us posted on the various stages involved along the way.




This is where things stood at the end of our trial run with CDS. They exhibited an incredible attentiveness when it came to the fine details, and their transparency throughout the process was fantastic.  We were quite happy with how the final image turned out, but unfortunately the scheduling just didn't work out when it came to a continued partnership.

Now, jump ahead several months with me. Daniel had been on board for a while and was turning out great work on our other locations. We decided that we wanted him to go back over Rhodes and ensure it matched the style of his other pieces. This mostly entailed intensifying the lighting and shadows to match his preference for more dynamic lighting. Flip back and forth between the two images to see the differences.


Notice anything else that's different?

The Colossus and the boats are huge now! Unfortunately, we lost the staggering height that was critical to the island's identity during the transformation, as Daniel had dragged in the Colossus and resized the boats in the harbor due to a miscommunication on our part.

Luckily, this worked to our favor in the end. With a quick change to the horizon, a few additional ship elevators, and some architectural duct tape to help hold up the docks, Daniel was able to make the sense of scale even more pronounced than before.


So there you have it: Rhodes. And if you happen to be in town, don't forget to ask Evagoras about the elevators' auto-load balancers!