Saturday, August 15, 2020

Realm of Darkness:

Work continues on the Farm dungeon. By my count, I have around 17 bad ends in the entire game completed, and I'm working on number 18. 

I'm trying to get at least one bad end done per animal variation, both male and female cows, horses, and pigs. I'm about half done with that... because I'm a little heavy on adding female cow bad endings as I enjoy writing them more.

Once the bad ends are done, the trick will be adding in sufficient events and connective tissue to actually get to these bad ends.

To that end, I've been working on an encounter system which will allow me to dynamically select events based on probability weights.

This is a common programming problem when doing such things as randomly selecting brokers to process data or another activity which requires selecting a particular value more often than other values, randomly.

Here's the code I've put together based on research I've done:

window.calculateEncounter = function(possibleEncounters){
	var i = 0;
	var j = 0;
	var totalWeight = 0;
	var keys = Object.keys(possibleEncounters);

	for (; i < keys.length; i++) {
	  totalWeight += possibleEncounters[keys[i]].weight;
	}

	var randomWeightNumber = Math.floor(Math.random() * totalWeight)
	var selectedKey = "";

	for (; j < keys.length; j++) {
	  if(randomWeightNumber < possibleEncounters[keys[j]].weight){
		selectedKey = keys[j];
		break;
	  }

	  randomWeightNumber = randomWeightNumber - possibleEncounters[keys[j]].weight;
	}

	return selectedKey;
}

window.calculateEncounterWithInclude = function(possibleEncounters){
	var selectedKey = calculateEncounter(possibleEncounters);

	return "<<include \"" + selectedKey + "\">>";	
}

window.calculateEncounterWithLink = function(optionNumber, optionName, linkText, possibleEncounters){
	var selectedKey = calculateEncounter(possibleEncounters);
	
	if(linkText){
		return "<span id=\"" + optionName + "Act\"><<link \"(" + optionNumber + ") " + linkText + "\" \"" + selectedKey + "\">><</link>></span>";			
	}else{
		return "<span id=\"" + optionName + "Act\"><<link \"(" + optionNumber + ") " + selectedKey + "\" \"" + selectedKey + "\">><</link>></span>";		
	}
}

The possibleEncounters input looks like the below object, specified in the 'StoryInit' passage so it will be run at the start of the game. A setup variable is used because these are static variables that do not need to be altered or tracked during the game.

<<set setup.testEncounters to {
  "Test Encounter A": {
    weight: 10
  },
  "Test Encounter B": {
    weight: 10
  },
  "Test Encounter C": {
    weight: 100
  },
}>>

The object consists of a passage name in the Twine game to select, and the 'weight' that passage is assigned.

The 'weight' is relative, which means in this case that 'Test Encounter C' would be selected as the passage to use ten times more often than the other passages.

The 'calculateEncounter' method iterates through the provided objects, getting the total weight of all encounters. Then, it randomly chooses a number between zero and the total weight. If the random number chosen is below the weight of the event being iterated, that event is selected. Otherwise, the weight of the event is subtracted from the random number, and the next event is inspected. This occurs until an event is chosen.

'calculateEncounterWithInclude' generates an include statement to be inserted into a Twine story passage. The include statement imports the contents of one passage into another passage.

'calculateEncounterWithLink' allows you to specify a link to the passage that has been chosen randomly. 'optionNumber' and 'optionName' are used for specifying a keyboard shortcut. 'linkText' is optional, but allows you to specify a name for the link different than the name of the destination passage.

Combined together, using this code in a passage would look like this:

This would be your generic location passage that needs a random encounter.

Then, you would have a randomly selected passage, weighted based on what is configured:

<<print calculateEncounterWithInclude(setup.testEncounters)>>

<<print calculateEncounterWithLink(1, "one", "This encounter is truly random.", setup.testEncounters)>>

The first print statement would be replaced with the randomly selected passage contents, while the second print statement would turn into a link. When this passage is visited, the code will execute and randomly choose an encounter based on the specified weight.

The code above would live in the 'Encounter Tester' passage, and when that passage is visited, one of the test encounter passages would be chosen to be included in that passage.

This is a pretty neat way to allow me to write standalone encounters and include them in the adventure pages. I will be using this logic pretty much everywhere in the story where random encounters occur.

In the Farm, there is a day loop, which means at the end of an encounter the game will need to increment the time and put you back in the right spot. I'll talk about my system for doing this another time. One major topic per post, I think!

This illustrates why it takes so much time to make a proper interactive game - there is a lot of logic under the covers like this that needs to be implemented to even make the story work - especially if you're trying to do more than a simple 'choose your own adventure' story!

With any luck, the next time I post, I'll be able to say that I have all of the main bad ends written! (And maybe even some of the encounters, too!)


Monday, August 3, 2020

Realm of Darkness - Starting the Farm Edition

Welcome back to my semi-irregular updates. Today I'd like to discuss the so-called 'dungeon' I'm working on, called 'The Farm.'

For the first pass, I'm planning on allowing you to choose which type of animal you're in danger of being converted into - a humanoid cow, horse, or pig - futa or female.

A large part of the farm will circle around the main gameplay loop of being taken care of by the farmhands and getting milked. The longer you given in to their demands, the more animal-like you'll become. If your intelligence drops too low, you're going to experience a bad end.

There will be other ways to experience a bad end too, of course, some of them more challenging and difficult to find than others. Today I wrote a bad end which has very similar themes to Tabico's 'Herd Instinct,' which fans of that work should enjoy.

There are several NPCs you'll meet at the farm, and whether you help them (or not) will affect their fates and yours. You may even be able to recruit one or two of them to escape and live with you outside the Farm, if you're careful.

Depending on how much the farm staff trust you, you'll be more or less free to roam The Farm. The more you defy them, the more restrained you'll be. For example, if you try to bite them, you'll be muzzled with a bit gag or another suitable implement. These 'debuffs' will make it more difficult for you to perform certain actions at The Farm and to find a way to escape.

Currently, I am working on the introductory area for The Farm, which by itself is somewhat complicated since I need to introduce all the characters and provide choices that allow you to figure out which animal you wish to become (see below picture for a sample of that game flow). Ideally all paths would be just as attractive, but that's probably not realistic, since I'm the biggest fan of the female bovine paths. Still, I'll do my best with the other paths as well.


In addition to what I'm writing, I've also fleshed out various areas you'll be able to visit while trapped on The Farm (subject to change). See a sample below:


There needs to be a way to escape, of course, but it shouldn't be too easy. And I'm planning for there to be more than one way to 'win' your way out of The Farm. Some of them will be more morally gray than others.

My first goal is to get the main gameplay loop working. Then, I will be working on adding sufficient random events to make the game seem fresh as well as unlockable events based on the stats you have. After that, I'll focus on the adventure mechanics in the farm as well as implementing various ways and means to escape.

Big plans, I know. I'm turning into Peter Molyneux here with the promises. We'll see how much is actually reasonable to complete, since I don't even have a way currently of getting your main character from the central town to The Farm without using the debug menu.

I'm thinking tentatively version one of the game should probably include a fleshed out central town as well as a completely playable Farm dungeon. If I get that far, I think that's probably a sufficient vertical slice to show off what I have and get some feedback. It still seems reasonable to get there by the end of the year, but things could always change... I'll keep ya'll up to date here.

That's a wrap for now. Here's the count of bad ends currently in the game: 13. Many more to come - especially at The Farm, which has 15 more already stubbed out! Not to mention the town, which will definitely need quite a few as well!