Implementing Branches
By Angus Cheng
Try out the latest build.
The development of the engine for Busty Barrister Barbara has been going well. A few days ago I figured out how to deal with branching which was one of the last missing features. I’ve done this before in plenty of other games. What I normally do is:
- Make sure every event has an identifier.
- Chain events together with a nextEventId
- Create Decision events with multiple nextEventIds.
const event1 = { id: "1", nextEventId: "2", message: "hello" }
const event2 = { id: "2", nextEventId: "3", message: "hello to you too" }
const decision = { id: "3", options: [
{ text: 'Say hello again', nextEventId: '4' },
{ text: 'Ask what time it is', nextEventId: '14' },
{ text: 'Stare at them', nextEventId: '24' },
]}
That works pretty well. Then I’d store all the events in a HashMap like this.
const events = new Map<string, IEvent>();
Unfortunately for me, I coded the engine using one mega list. My events had no identifiers. The game just runs through the list of events, only stopping when it hit Delay or WaitForUser events. I could have refactored the whole thing to use a Map and put identifiers on every event but that seemed very annoying and possibly not necessary.
I also thought about how I would like the level data to look for a script writer. What’s the easiest way for them to lay it out?
#START
Angus: Hello
Steve: Hello to you too
DECISION
- Say hello again, HELLO_AGAIN
- Ask what time it is, ASK_WHAT_TIME
- Stare at them, STARE_AT_THEM
#HELLO_AGAIN
Angus: Hello
Steve: You already said hello...
#ASK_WHAT_TIME
Angus: What time is it?
Steve: I don't know
#STARE_AT_THEM
Steve: ...
Steve: What's up?
Something like that is easy to read and debug as a writer. Text elements starting with # are and they track the index point into the list of events.
I haven’t create a script format yet, so all level data is set up in C.
typedef struct Label {
const char *label;
int index;
} Label;
add_label(data, "HELLO_AGAIN");
void add_label(GamePlayData *data, const char* label) {
int i = data->eventData.labelCount++;
int index = data->eventData.eventCount;
data->eventData.labels[i].index = index;
data->eventData.labels[i].label = label;
}
All we do is append create a new Label struct with the current position in the event list. “The events for the label HELLO_AGAIN start at index 17 in the event list”. Looking at this code now, I can see it would be a good idea to add validation to make sure labels in a script are unique. I’ll add that to my TODO.md file.
When a user makes a decision and we want to jump to that label we run this code:
// TODO: Maybe rename this to find_label_index
void jump_to_label(GamePlayData *data, const char *label) {
int jumpToIndex = -1;
for (int i = 0; i < data->eventData.labelCount; i++) {
if (strcmp(data->eventData.labels[i].label, label) == 0) {
jumpToIndex = data->eventData.labels[i].index;
break;
}
}
if (jumpToIndex == -1) {
fprintf(stderr, "Failed to find label with name = %s\n", label);
abort();
}
printf("Jumping to label = %s with eventIndex = %i\n", label, jumpToIndex);
data->eventData.eventIndex = jumpToIndex;
}
Most of the code is finding the index, so I think it makes more sense to rename it and split out the last line. Will do that later though.
The featured image isn’t very branchy but I picked it because it was one hell of a day. I wanted to go hike Pat Sin Leng but I got on the wrong bus and ended up somewhere nearby but not where I wanted to be. I got off the bus and just walked in the direction of the mountain, expecting the trail to eventually join onto where I wanted to go. Mid-way through it started raining very heavily.
I was in the middle of the forrest so I couldn’t avoid the rain. I was going to press on but then realised the area I was looked prone to landslides. I decided not to die in a landslide and turned around.
While doing all this I was listening to an epic episode of On The Metal featuring Jonathan Blow.