Disco Elysium OUT NOW (previously: No Truce With The Furies)

"Wor(l)d creation"

unnamed.jpg

Andreas W

Video game production gives us an opportunity to take a more detailed look at how reality is layered. What experiences of reality can be depicted in game form, that would be un-expressable in, say, a written story? Science has done its best to prove that something’s existence as a story does not mean anything. We could fantasize whatever, and however much we talk about it, it will not acquire more physical existence than it had before the story was made up. Storytelling is not omnipotent, unlike the popular myths would have it.

Let’s think of a woman. Let’s call her Klaasje. We know what she is supposed to look like (blonde hair, silvery jumpsuit, nine-inch heels). Klaasje is a dancer. There’s more to her, but this will have to do for now. And after that… nothing happens. Klaasje has been invented, yes. The literary ingenuity as lauded by authors and readers alike allows us to picture Klaasje exactly how we please. There are a billion Klaasjes and the author is dead, as he should be. (The meme of the author’s death illustrates the impossibility of the author intruding into the reader’s thoughts and dictating how they’ll picture the author’s characters).

So, there’s a potential Klaasje, but nothing happens, because the place where we release her is not a book. No one has told her that her being a dancer means she should be able to manage mere walking with ease and grace. This is why Klaasje just stands there. And when she tries to walk and there’s even a slightest imperfection in her animation, we as viewers *will* notice it for some reason and we will fail to believe she’s a dancer. We will shake with laughter or disgust, depending on the side of the uncanny valley the logic of her movement will throw us. Somebody else will have to do the work usually done by the brain’s motor cortex. It’s an enormous amount of work. The evolution has done it once already, but it won’t be any help to a moving character model or a moving robot. Thus the animator and the programmer find themselves in a world full of problems.

Of course we all know this, it is elementary. Yet naming this elementary does not aid the animator or the programmer either. Inside our heads in a story it is very easy to merge various aspects of a character into a cohesive whole. A few twists and turns of the proverbial quill and the readers have a nice carcass on which they can easily generalize a character. Go on, enjoy the awesome character I just came up with using your heads and imaginations! Suddenly I feel the all-encompassing power of storytelling! All manners of possible worlds are lined up behind the door, patiently waiting for me to give them a shape.

Now let us imagine all the mythopoetic narrators who have utilized their outstanding powers of imagination in assembling the various religions, beliefs, myths and fairy tales we know so well today, let us imagine they had to tell those stories in the form of a video game. All those characters and critters that need to be animated, one by one. What would it be like to animate God? How should he walk? What should the horrible cloud look like under his feet, spitting lightning and coughing up thunder as God addresses his people? What’s its texture like, how large should it be, what level of transparency? What exactly should the damn lightning be like? And oh, did I just say “people”? You have to animate every single one of them now and make them walk properly before they escape the wrath of God (stop fooling around, this is not an epic escape, it’s comical, do it over). And that was one runner, but we have 3500 of them.


"And now please construct all of the prehistoric mythoi in a similar, preferrably even more detailed manner."

Do you think the problem could be solved à la “Thou shalt not make five different variations of your God’s walk cycle!”? No, instead you’ll agree with me here that it is so much more convenient for an author to just tell the story in as much detail as they please and avoid going through the hassle of simplifying so much for the player. It would be so much easier to just trust the reader’s/player’s imagination to do all the heavy lifting for you. Writing won’t make anyone do a reality check. The author will finally see the weak links in the art of storytelling when the story starts manifesting in a new and tangible form – a video game for example. That is when they begin to appreciate the detail level of animating and coding.
 
I want to see a game where there's 35 people walking differently with all complicated backstories never presented to the player.

Make it so.
 
I want to see a game where there's 35 people walking differently with all complicated backstories never presented to the player.

Make it so.

We are actively looking for a new (character) animator (news post pending). As long as we are successful in that - your expectation is pretty much a goal in itself.

This week´s post is code-centered:

"Better Living Through C# Coding, Part 1: Managing Input"


ve.jpeg

Veljo Hagu

Systems Architect

Unity is an awesome development engine. I formed that opinion back in 2012 when I first gave it a try (and during the sleepless night that followed), and I still believe it to be true. It’s not flawless though – sooner or later you discover an aspect or functionality that just confounds you. In contrast to what is normal, things suddenly become… unwieldy, raw, malformed. For me such an aspect is handling player input (keyboard, mouse, and gamepad events).

You see, Unity doesn’t have a nice subsystem here that you’d somehow expect. There’s just a low-level API (Input class with static methods), which appears to have been there since the early Bronze Age, and is about as sophisticated. If you read and follow the documentation, it promptly guides you down the easy road to hell called Bad Design. I mean, multiple component scripts each polling Input status every frame, handling the results independently (and unaware) of each other doesn’t bode well for any non-trivial project.

Almost as if to taunt you, there’s the event-system for working with uGUI (the “new” UI system released with Unity 4.6). Once you break through the initial confusion (as the documentation here suddenly stops being helpful and informative), you’ll find this to be a nice, well designed framework, easy to use for both trivial and advanced input operations alike. Alas, this framework really only covers GUI, leaving you empty-handed with everything else your project needs to handle (WASD/arrows keys for camera movement, mouse clicks to designate target, Escape to bring up game menu, etc.).

So, how does one approach this? One problem at a time of course. Just like eating an elephant.

Let’s start with the lack of awareness between scripts. Say you want a key (Spacebar) to do different things in different situation: if the player character is walking around in the game world, he should stop moving; if he is in a dialogue with an NPC, Spacebar should be the hotkey for Continue button and if you’re typing a text (chat command in a multiplayer game, naming your savegame, etc.), space should not do anything else. Or for example the Escape key: depending on what’s visible on screen, it might close Inventory, bring up game menu, or do something else entirely.

A good solution allows scripts to indicate their interest in a specific Input event, mark the event status (has someone “used up” that event already?) and establish priority order when it comes to choosing who handles the Input event. The natural design pattern for this would include a Singleton input manager class with Observers subscribing to input events. Observers of a specific event can then be ordered by their priority (Chain of Responsibility pattern). In addition to actual Input event data, event parameters can include a flag (boolean) for tracking the “used” status.

GameInputManager.cs
using UnityEngine;
using System.Collections.Generic;

public class GameInputManager : MonoBehaviour {

#region Singleton pattern
protected static GameInputManager singleton;

public static GameInputManager Singleton {
get {
if (singleton==null) singleton = FindObjectOfType<GameInputManager>();
return singleton;
}
}
#endregion

#region Input event parameter
public class EventData {
public string axis = null;
public string button = null;
public KeyCode keyCode = KeyCode.None;
public bool used = false;
public float value = 0f;

public EventData(KeyCode keyCode) { this.keyCode = keyCode; }
public EventData(string axis, float value) { this.axis = axis; this.value = value; }
public EventData(string button) { this.button = button; }
}
#endregion
public const int MAX_PRIORITY = 10000;

#region Public static methods (API)
/// <summary>Register an axis as one of interest.</summary>
public static void ObserveAxis(string axis) {
if (!string.IsNullOrEmpty(axis) && Singleton) Singleton.observedAxes.Add(axis);
}

/// <summary>Register a button as one of interest.</summary>
public static void ObserveButton(string button) {
if (!string.IsNullOrEmpty(button) && Singleton) Singleton.observedButtons.Add(button);
}

/// <summary>Register a keycode as one of interest.</summary>
public static void ObserveKeyCode(KeyCode keyCode) {
if (keyCode!=KeyCode.None && Singleton) Singleton.observedKeycodes.Add(keyCode);
}

/// <summary>Register a handler method for hotkey event with one above currently highest priority.</summary>
/// <param name="Action">Handler method that is called when hotkey event triggers. That method has one EventData parameter.</param>
public static void Register(System.Action<EventData> Action) {
if (Action!=null && Singleton!=null) Singleton.GetBlock(Singleton.highestPriority + 1).Event += Action;
}

/// <summary>Register a handler method for hotkey event with the specified priority.</summary>
/// <param name="Action">Handler method that is called when hotkey event triggers. That method has one EventData parameter.</param>
/// <param name="priority">Callbacks are made in order of priority (from the highest to the lowest).</param>
public static void Register(System.Action<EventData> Action, int priority) {
if (Action!=null && Singleton!=null) Singleton.GetBlock(priority).Event += Action;
}

/// <summary>Unregister a callback method from all Input events.</summary>
public static void Unregister(System.Action<EventData> Action) {
if (Action!=null && Singleton!=null) foreach (EventBlock b in Singleton.eventBlocks) b.Event -= Action;
}
#endregion
#region Unity magic methods
protected void Awake() {
singleton = this;
}

protected void Update() {
foreach (string a in observedAxes) {
SendEvent(new EventData(a, Input.GetAxis(a)));
}
foreach (string b in observedButtons) {
if (Input.GetButtonDown(b)) SendEvent(new EventData(b));
}
foreach (KeyCode k in observedKeycodes) {
if (Input.GetKeyDown(k)) SendEvent(new EventData(k));
}
}
#endregion
#region Internals (under the hood)
protected class EventBlock : System.IComparable<EventBlock> {

public int priority;
public event System.Action<EventData> Event;

public EventBlock(int p) { priority = p; }

public void AppendTo(ref System.Action<EventData> deleg) { if (Event!=null) deleg += Event; }

// Order highest to lowest
public int CompareTo(EventBlock other) { return -priority.CompareTo(other.priority); }

public void Invoke(EventData eventData) { if (Event!=null) Event(eventData); }

public bool IsEmpty { get { return Event==null; } }
}

protected List<EventBlock> eventBlocks = new List<EventBlock>();
protected HashSet<string> observedAxes = new HashSet<string>();
protected HashSet<string> observedButtons = new HashSet<string>();
protected HashSet<KeyCode> observedKeycodes = new HashSet<KeyCode>();

protected EventBlock GetBlock(int priority) {
foreach (EventBlock b in eventBlocks) if (b.priority==priority) return b;
EventBlock newBlock = new EventBlock(priority);
eventBlocks.Add(newBlock);
eventBlocks.Sort();
return newBlock;
}

protected int highestPriority {
get {
// eventBlocks is always sorted in reversed priority order (i.e., highest to lowest), so first non-empty block is the correct result
foreach (EventBlock b in eventBlocks) if (b.priority<MAX_PRIORITY && !b.IsEmpty) return b.priority;
return 0;
}
}

protected void SendEvent(EventData data) {
System.Action<EventData> callStack = null;
foreach (EventBlock block in eventBlocks) block.AppendTo(ref callStack);
if (callStack!=null) callStack(data);
}
#endregion
}


Observer scripts would then look like this:

DemoInputObserver.cs
using UnityEngine;
public class DemoInputObserver : MonoBehaviour {

#region Unity magic methods
protected void OnEnable() {
GameInputManager.ObserveKeyCode(KeyCode.Space);
GameInputManager.ObserveKeyCode(KeyCode.Escape);
GameInputManager.ObserveAxis("Horizontal");

GameInputManager.Register(OnInputEvent);
}

protected void OnDisable() {
GameInputManager.Unregister(OnInputEvent);
}
#endregion

#region Internals (under the hood)
protected void OnInputEvent(GameInputManager.EventData data) {
if (data.used) return;

if (data.keyCode==KeyCode.Space) {
Debug.Log("Spacebar was pressed");
data.used = true;
} else if (data.keyCode==KeyCode.Escape) {
Debug.Log("Escape was pressed");
data.used = true;
} else if (data.axis=="Horizontal") {
if (data.value!=0f) {
Debug.Log("Horizontal axis = " + data.value.ToString());
}
data.used = true;
}
}
#endregion
}

Note that if you attach this script to Game Objects multiple times, the Console will only show a single entry for each event. By default, priority (order of calling) is determined by the order that Unity enables components in scene (LIFO: last one to register receives highest priority). If you want to explicitly determine priority, the Register method has an appropriate override.

Alright, that’s enough for one post. Next time, I’ll show you what Reflection and Attributes can bring to this party.
 
I really like the mixture of game info and general tips and information. It makes for interesting reading!
Thanks! Here´s some more!



"On skill checks"

kinematroopika-150x150.jpg

Robert Kurvitz
Game Designer

In the 15 year build up to No Truce With The Furies, one of our main gripes with RPG-s has always been non-combat skill use. In RPGs – even the story-heavy ones – combat is lavished with tactical tension, skill use produces cool chunky animations, you get pulse-raising rewards and punishments, the logos are colourful. Sound effects go “Tring-trang!” and “Pow!”, there are intricate beautiful systems for you to delve into.

Most of this is missing from non-combat skill use. Talking and exploring gets a simplified, non-competitive version of the combat rules. Usually this comes in the form of passive dialogue options: have this much of that required skill and you’ll be able to say this thing. Even the games we truly admire – Planescape: Torment, Mask of the Betrayer, Fallout – have little going on in the rules department when it comes to dialogue. Ditto for most tabletop pen-and-paper role playing systems. The tactical depth of using arguments, employing logic, original thinking, empathy – the skill use that covers 95% of our actual lives – makes up 5% of the rule system. Yet my experience tells me thinking is the ultimate game. It’s nerve-wrecking, conversations are filled with hidden doubts; we struggle to trust each other, manipulate each other, stay sane. There is great strategic depth and tactical tension that goes into talking that games haven’t really – for me – begun to represent yet.

So that’s the first thing we set out to create: a truly in depth non-combat skill system. We have four stats and under each stat there are 6 skills. That gives us 24 skills – all 24 have critical non-combat use. In fact, No Truce With The Furies (the first implementation of our role playing system) will cover their non-combat use almost exclusively. (In the future we want every skill to be a two-faced Janus with somewhat unsymmetrical and unexpected uses in combat and outside it).

I’ll show off the individual skills in a future post. But first I want to talk about how the skills are used in No Truce With The Furies. That is – about skill checks.


Reference used to create the look of our dialogue system

In role-playing games the check is the moment the system “checks” if a character has enough points in a skill to perform an action. It’s a “you have to be at least this tall to ride the roller coaster” kind of deal. Of course there are exceptions and interesting ideas around, but this is how RPGs usually handle skill checks: your character is talking to someone, that someone lies, if your character has 5 INTELLIGENCE you get a dialogue option that says: “You’re not telling me the truth”. Saying that will make the guy admit he lied. This type of check is called passive because you’re doing nothing. Some hours ago you put two points in “seeing through lies skill” and now the software affirms your choice. There’s not a lot of game in there. And certainly not a lot of literature.

When designing our skill checks in dialogues we had two goals:
  1. Make dialogue more like literature – rethink passive checks
  2. Make dialogue more like a game – add active checks
PASSIVE CHECKS
In literature dialogues are interspersed with thoughts, emotions, ulterior motives and physical phenomenon taking place within the characters while they talk. This comes in the form of parenthesis, streams of consciousness, author interjections etc. A whole plethora of literary devices. We wanted to do that in game form. To depict what’s below the surface: the moment an idea forms, the sense of self delusion, secretly laughing because you came up with a stupid joke. Then trying to figure out if you should say it out or not…

It was surprisingly easy to achieve – your skills talk to you. When we use passive checks they are not dialogue options but extra “characters” who silently interject. Only you, the main character can hear them because they are your thoughts, your sensations. Our passive checks are souffleurs in a play.

Let’s look at a sample situation from the game. And remember: every time the main character speaks they have options to say something else. (I have simplified the choice part of the dialogue for the sake of this example).

You come upon a loitering teenage girl kneeling on the ice with a tape recorder in hand. You approach her, question her, then this happens:
You:
“What’s that device you have there?”
Acele: “This? It’s a portable recording device. It’s for field recording. Low quality, but still.”
You: “And the wires?”
Acele: “Actually just one wire, I picked on it ’til the braiding came loose. The wire leads to a contact microphone.”
You: “What is a “contact microphone”?”
Acele: “A contact mic is a microphone that records sounds from inside things. Like this ice.”
TRIVIA (difficult success): Your mangled brain would like you to know there is a boxer called Contact Mike.
You: What am I supposed to do with this?
TRIVIA: No idea.
You: “Does this have anything to do with Contact Mike?”
Acele: “Uh…” She’s confused. “Yeah, I record stuff with it.”
You: “No, I mean the boxer Contact Mike.”
Acele: “Ah! No. This is a *contact microphone*, it’s for recording *inside* solid objects. Contact Mike just beats people up.”
You: “You know, Contact Mike doesn’t “just beat people up”. Contact Mike is a role model.”
Acele: “Um…”
You: “On second thought, screw Contact Mike. He’s no true champion – you are! Look at you here in front of a saggy tent, picking your nose to drug-addict music. The world of sports is in awe of your faith and dedication!”
Acele: “Man, you are one weird cop.”
You: “This isn’t about me. This is about your lack of respect for one of boxing’s greats – and for *yourself*.”

This dialogue could have gone differently if you didn’t have a ridiculously detailed (and mostly useless) factual memory. Even then you could have ignored the little connection your mind made, but in this situation the player chose to go off on a tangent.

What happened was
  1. First you had a high enough Trivia skill.
  2. Then your Trivia told you an “interesting” fact.
  3. Then you had a little conversation with that part of your memory.
  4. Then you reached a hub of questions to Acele where in addition to normal, situation-appropriate ones you had “Does this have anything to do with Contact Mike?”.
This line we call a black check. It’s a line of dialogue fed to you by a passive check. It’s the closest we have to a “have this much skill to get dialogue option” type of affair, but 1) it’s covert, often you don’t even understand where an idea came from 2) we always have the conception of an idea first: the skill talks to you and then sometimes you can use this idea on whoever you’re talking to. If you choose to. Keeping the tidbit to yourself produces effects down the line too, since we consider all dialogue options seen by the player to be ideas circulating in the character’s psyche. Some just remain unspoken.

On some occasions the passive check just makes little observations that lead to more things later, but remain one-liners for the time being.

So this is how we’ve re-thought passive checks. The versatility of this simple system – let me just repeat it one more time: YOUR SKILLS TALK TO YOU – is pretty incredible. It is hard for us to imagine writing the game without it already. We can do really weird stuff. Like Half Light – the skill that controls your adrenaline gland and your prey drive – can railroad you into a rage spiral where you hound an innocent suspect on something they clearly didn’t do. And it takes another skill’s intervention for you to get out of it. The next moment a skill can wildly expand the options you have avalable, for example: Drama whispers insane method acting ideas into your ear. Or your Pain Threshold tells you to stab yourself in the hand to make a point. Whatever you do – don’t. Pain Threshold is an unstable masochist. It will only leave you screaming with your hand nailed to the table. And then – while screaming with your hand nailed to the table – Rhetoric to the rescue! Make a political point out of this. Tell them you’re a victim of your own macho mentality. Tell them (with your hand still nailed to the table) that years of chauvinism have led you to this low point in your life.

Now, I just made this situation up because I didn’t want to spoil any more of the game, but you get the point. If “Years of chauvinism have led me to this point!” was just a dialogue option it would come out of the blue. But it’s different to hear the thought form in your head out of great physical discomfort and then be able to converse with it. Should I say that? Do I really mean that? You sometimes let these ideas out, sometimes you carry on. We have a game where you might have to start censoring yourself.

contact-mike-gif7.gif

Current version of our dialogue engine. Notice the sexy yet subtle animation cues!​

Next time I will talk about active skill checks – our gamey, number crunching, min-maxing counterbalance to the literature-wonk of passive checks.

Til then!
 
Wow... just wow. This is really ambitious but also really cool. This better work out well, because I want to see more of this in games.
 
"Better Living Through C# Coding, Part 2: Magic Of Reflection"

ve.jpeg

Veljo Hagu
Systems Architect

(Part 1 can be found here.)

“Mirror, mirror on the wall, what’s the ugliest code of ’em all?”

The answer might very well be the annoying amount of technical code we need to implement in our components. Luckily, it annoyed me enough to start pondering about a better way. Specifically, about using attributes and reflection to hide all that ugliness inside a base class.

In the magical world of unicorns and rainbows, I want my Observer component to look pretty:

DemoInputObserver2.cs
usingUnityEngine;

publicclassDemoInputObserver2:BaseComponent{

[InputKey(KeyCode.Space)]
protectedvoidOnSpacebar(){
Debug.Log("Spacebar was pressed");
}

[InputKey(KeyCode.Escape, priority =10001)]
protectedvoidOnEscape(){
Debug.Log("Escape was pressed");
}

[InputAxis("Horizontal")]
protectedvoidOnHorizontal(float value){
Debug.Log("Horizontal axis = "+ value.ToString());
}
}
The idea is to use custom attributes to establish usage context: what type of input do we want to handle, and values for relevant parameters (KeyCode, name of the axis, handling priority, etc.). We rely on a common base class to discover those attributes and register thus marked methods with input manager.

Implementation-wise, starting with simple things, the custom attributes themselves are quite straight-forward:

InputAttribute.cs
usingUnityEngine;

publicabstractclassInputAttribute:System.Attribute{

protectedint? _priority;

publicbool hasPriority {get{return _priority!=null;}}

publicint priority {
get{return _priority!=null?(int)_priority :0;}
set{ _priority = value;}
}

publicInputAttribute(){ _priority =null;}
}

/// <summary>Attaching this attribute to a method marks it as HotKeyEventData handler. That method should have a single HotKeyEventData type argument.</summary>
[System.AttributeUsage(System.AttributeTargets.Method)]
publicclassInputEventDataAttribute:InputAttribute{

publicInputEventDataAttribute():base(){}
}

/// <summary>Attaching this attribute to a method marks it as Input.GetAxis value handler. That method should have a single float type argument.</summary>
[System.AttributeUsage(System.AttributeTargets.Method)]
publicclassInputAxisAttribute:InputAttribute{

publicstring axis {get;set;}

publicboolIsAxis(string a){return!string.IsNullOrEmpty(a)&&string.Equals(axis, a);}

publicInputAxisAttribute(string axis):base(){this.axis = axis;}
}

/// <summary>Attaching this attribute to a method marks it as Input.GetButtonDown event handler. That method should have no arguments.</summary>
[System.AttributeUsage(System.AttributeTargets.Method)]
publicclassInputButtonAttribute:InputAttribute{

publicstring button {get;set;}

publicboolIsButton(string b){return!string.IsNullOrEmpty(b)&&string.Equals(button, b);}

publicInputButtonAttribute(string button):base(){this.button = button;}
}

/// <summary>Attaching this attribute to a method marks it as Input.GetKeyDown event handler. That method should have no arguments.</summary>
[System.AttributeUsage(System.AttributeTargets.Method)]
publicclassInputKeyAttribute:InputAttribute{

publicvirtualKeyCode keyCode {get;set;}

publicInputKeyAttribute(KeyCode keyCode):base(){this.keyCode = keyCode;}
}
Reflection also turns out to be easy to use. The base class that you inherit from can be as simple as:
usingUnityEngine;
usingSystem.Reflection;

publicclassBaseComponent:MonoBehaviour{

protectedvoidInitManagerAgents(){
MemberInfo[] members =this.GetType().GetMembers(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance);
foreach(MemberInfo m in members){
System.Attribute[] attribs =System.Attribute.GetCustomAttributes(m,false);
foreach(System.Attribute a in attribs){
//if (a is UpdateAttribute) UpdateManager.Register(this, m, a as UpdateAttribute);
if((a isInputAttribute)&&(m isMethodInfo))InputManager.Register(this, m asMethodInfo, a asInputAttribute);
}
}
//if (this is InputManager.IKeyHandler) InputManager.Register(this as InputManager.IKeyHandler);
}

protectedvirtualvoidAwake(){
InitManagerAgents();
}
}
Virtually all of the complexity is left for InputManager to handle. All we’re providing is reference to self, MethodInfo (which has Invoke method for callback), and InputAttribute providing context.

Lo and behold, the magic module.

InputManager.cs
[Persistent,SelfSpawning(name="InputManager")]
publicclassInputManager:SingletonComponent<InputManager>{

publicinterfaceIKeyHandler{
KeyCode inputKey {get;}
voidOnInputKey();
}

publicinterfaceIPriority{
int inputPriority {get;}
}

publicclassEventData{
publicstring axis =null;
publicstring button =null;
publicKeyCode keyCode =KeyCode.None;
publicbool used =false;
publicfloat value =0f;

publicEventData(KeyCode keyCode){this.keyCode = keyCode;}
publicEventData(string axis,float value){this.axis = axis;this.value = value;}
publicEventData(string button){this.button = button;}
}

/// <summary>Register an axis as one of interest.</summary>
publicstaticvoidObserveAxis(string axis){
if(!string.IsNullOrEmpty(axis)&&Singleton)Singleton.observedAxes.Add(axis);
}

/// <summary>Register a button as one of interest.</summary>
publicstaticvoidObserveButton(string button){
if(!string.IsNullOrEmpty(button)&&Singleton)Singleton.observedButtons.Add(button);
}

/// <summary>Register a keycode as one of interest.</summary>
publicstaticvoidObserveKeyCode(KeyCode keyCode){
if(keyCode!=KeyCode.None&&Singleton)Singleton.observedKeycodes.Add(keyCode);
}

/// <summary>Register a handler method for hotkey event with one above currently highest priority.</summary>
/// <param name="Action">Handler method that is called when hotkey event triggers. That method has one HotKeyEventData parameter.</param>
publicstaticvoidRegister(System.Action<EventData>Action){
if(Action!=null&&Singleton!=null)Singleton.GetBlock(Singleton.highestPriority +1).Event+=Action;
}

/// <summary>Register a handler method for hotkey event with the specified priority.</summary>
/// <param name="Action">Handler method that is called when hotkey event triggers. That method has one HotKeyEventData parameter.</param>
/// <param name="priority">Callbacks are made in order of priority (from the highest to the lowest).</param>
publicstaticvoidRegister(System.Action<EventData>Action,int priority){
if(Action!=null&&Singleton!=null)Singleton.GetBlock(priority).Event+=Action;
}

publicstaticvoidRegister(IKeyHandler handler){
if(handler!=null&&Singleton!=null)Singleton.AddAgent(newKeyHandlerAgent(handler));
}

publicstaticvoidRegister(Behaviour parent,System.Reflection.MethodInfo methodInfo,InputAttribute attribute){
if(methodInfo==null||!parent ||!Singleton)return;
if(attribute isInputEventDataAttribute){
Singleton.AddAgent(newEventDataAgent(parent, methodInfo, attribute asInputEventDataAttribute));
}elseif(attribute isInputAxisAttribute){
Singleton.AddAgent(newAxisAgent(parent, methodInfo, attribute asInputAxisAttribute));
}elseif(attribute isInputButtonAttribute){
Singleton.AddAgent(newButtonAgent(parent, methodInfo, attribute asInputButtonAttribute));
}elseif(attribute isInputKeyAttribute){
Singleton.AddAgent(newKeyCodeAgent(parent, methodInfo, attribute asInputKeyAttribute));
}
}

/// <summary>Unregister a callback method from all timer events.</summary>
publicstaticvoidUnregister(System.Action<EventData>Action){
if(Action!=null&&Singleton!=null)foreach(EventBlock b inSingleton.eventBlocks) b.Event-=Action;
}

protectedabstractclassEventHandlerAgent:Agent{

protectedSystem.Action<EventData>Action;

publicEventHandlerAgent(Behaviour parent):base(parent){}

publicoverridevoidDispose(){
if(Action!=null)Unregister(Action);
Action=null;
base.Dispose();
}
}

protectedclassEventDataAgent:EventHandlerAgent{

publicEventDataAgent(Behaviour parent,System.Reflection.MethodInfo methodInfo,InputEventDataAttribute attribute):base(parent){
if(IsFinished)return;
Action=(x =>{if(!IsFinished&& parent.isActiveAndEnabled) methodInfo.Invoke(parent,newobject[]{ x });});
if(attribute!=null&& attribute.hasPriority)Register(Action, attribute.priority);
elseRegister(Action);
}
}

protectedclassAxisAgent:EventHandlerAgent{

publicAxisAgent(Behaviour parent,System.Reflection.MethodInfo methodInfo,InputAxisAttribute attribute):base(parent){
if(IsFinished)return;
Action=(x =>{
if(!IsFinished&&!x.used && attribute.IsAxis(x.axis)&& parent.isActiveAndEnabled){
object res = methodInfo.Invoke(parent,newobject[]{ x.value });
if(res isbool) x.used =(bool)res;
else x.used =true;
}
});
ObserveAxis(attribute.axis);
if(attribute.hasPriority)Register(Action, attribute.priority);
elseRegister(Action);
}
}

protectedclassButtonAgent:EventHandlerAgent{

publicButtonAgent(Behaviour parent,System.Reflection.MethodInfo methodInfo,InputButtonAttribute attribute):base(parent){
if(IsFinished)return;
Action=(x =>{
if(!IsFinished&&!x.used && attribute.IsButton(x.button)&& parent.isActiveAndEnabled){
object res = methodInfo.Invoke(parent,null);
if(res isbool) x.used =(bool)res;
else x.used =true;
}
});
ObserveButton(attribute.button);
if(attribute.hasPriority)Register(Action, attribute.priority);
elseRegister(Action);
}
}

protectedclassKeyCodeAgent:EventHandlerAgent{

publicKeyCodeAgent(Behaviour parent,System.Reflection.MethodInfo methodInfo,InputKeyAttribute attribute):base(parent){
if(IsFinished)return;
Action=(x =>{
if(!IsFinished&&!x.used && x.keyCode!=KeyCode.None&& x.keyCode==attribute.keyCode && parent.isActiveAndEnabled){
object res = methodInfo.Invoke(parent,null);
if(res isbool) x.used =(bool)res;
else x.used =true;
}
});
ObserveKeyCode(attribute.keyCode);
if(attribute.hasPriority)Register(Action, attribute.priority);
elseRegister(Action);
}
}

protectedclassKeyHandlerAgent:EventHandlerAgent{

publicKeyHandlerAgent(IKeyHandler handler):base(handler asBehaviour){
if(IsFinished)return;
Action=(x =>{
if(!IsFinished&&!x.used && x.keyCode!=KeyCode.None&& x.keyCode==handler.inputKey && parent.isActiveAndEnabled){
handler.OnInputKey();
x.used =true;
}
});
ObserveKeyCode(handler.inputKey);
if(parent isIPriority)Register(Action,(parent asIPriority).inputPriority);
elseRegister(Action);
}
}

protectedclassEventBlock:System.IComparable<EventBlock>{

publicint priority;
publiceventSystem.Action<EventData>Event;

publicEventBlock(int p){ priority = p;}

publicvoidAppendTo(refSystem.Action<EventData> deleg){if(Event!=null) deleg +=Event;}

// Order highest to lowest
publicintCompareTo(EventBlock other){return-priority.CompareTo(other.priority);}

publicvoidInvoke(EventData eventData){if(Event!=null)Event(eventData);}

publicboolIsEmpty{get{returnEvent==null;}}
}

protectedAgentCollection agents =null;
protectedList<EventBlock> eventBlocks =newList<EventBlock>();
protectedHashSet<string> observedAxes =newHashSet<string>();
protectedHashSet<string> observedButtons =newHashSet<string>();
protectedHashSet<KeyCode> observedKeycodes =newHashSet<KeyCode>();


protectedboolAddAgent(Agent item){
if(agents==null) agents =newAgentCollection();
return agents.Add(item);
}

protectedEventBlockGetBlock(int priority){
foreach(EventBlock b in eventBlocks)if(b.priority==priority)return b;
EventBlock newBlock =newEventBlock(priority);
eventBlocks.Add(newBlock);
eventBlocks.Sort();
return newBlock;
}

protectedint highestPriority {
get{
// eventBlocks is always sorted in reversed priority order (i.e., highest to lowest), so first non-empty block is the correct result
foreach(EventBlock b in eventBlocks)if(b.priority<10000&&!b.IsEmpty)return b.priority;
return0;
}
}

protectedvoidSendEvent(EventData data){
System.Action<EventData> callStack =null;
foreach(EventBlock block in eventBlocks) block.AppendTo(ref callStack);
if(callStack!=null) callStack(data);
}

protectedvoidUpdate(){
foreach(KeyCode k in observedKeycodes){
if(Input.GetKeyDown(k))SendEvent(newEventData(k));
}
foreach(string a in observedAxes){
SendEvent(newEventData(a,Input.GetAxis(a)));
}
foreach(string b in observedButtons){
if(Input.GetButtonDown(b))SendEvent(newEventData(b));
}
agents.Update(Time.deltaTime);
}

protectedoverridevoidOnDestroy(){if(agents!=null) agents.Clear();base.OnDestroy();}
}
As you can see, there’s a bit more going on here than I have revealed the code for. All that has to wait for Part 3, as time is up for today. Sorry!
 
"THE DESIGN ETHOS OF OUR ROLE PLAYING SYSTEM"



kinematroopika-150x150.jpg

Robert Kurvitz
Game Designer


I’ve spent most of my adult life tinkering on this system. (Most sounded better than all.) It used to be huge and unwieldy, meant to span several books. Originally we wanted to build a set of tabletop rulebooks inseparable from it’s setting. An end-all, include-all sourcebook / setting for tabletop role playing. Over the years we’ve cut it down to fit on a napkin.

For “No Truce With The Furies” we adapted those same pen and paper rules for an isometric RPG. I firmly believe it was the right choice.

The years have seen a lot of critique of our own system and even more learning from others. We’ve followed the debates around J. Sawyer’s design for Pillars of Eternity, rated editions of D&D and even played Temple of Elemental Evil for it’s turn based engine. We’ve done our time, so to say. Today I want to share some of our main beliefs as designers of role playing systems. These are not the “pillars of our design”, just some reasoning behind our rule system. The list is meant to show where we’re coming from to fellow designers and RPG enthusiasts.


Concept art for our skill icons​

1) Only one
We make one system, one world – and that’s it. Everything goes into this one structure. All our ability for systematic thinking, all our knowledge of history. We iterate upon these rules until they are just right, the best numerical foundation for experiencing our world. And we make the world as complete and total as we can. And then we’re done. Wrap it up and send it into the future, New Testament style. We will never make a steampunk cyberpunk spyworld, Owls versus Foxes, Yarn Boy rule system.

2) Tyranny of cool
If a skill has a great name, if a talent is poetic, if a mechanic is haute tension – it’s in. We’ll make it work. Beautiful stuff does not get taken out because “someone somewhere” didn’t understand what it does. If it’s clunky or extraneous we iterate and redesign until it works. We will always have talent names longer than “Small Black Flowers That Grow In The Sky” and “The Intense Humming of Evil” combined.

3) Unsymmetrical is symmetrical
It’s good to have almost useless things and seemingly overpowered things. A good composition is not all equal parts. A good composition is equal experiences. There is great symmetrical tension and effect in a seemingly useless abilities that you try to use for the sake of cool. Pull off Spook, Shocking Grasp and Spell Thrust in Baldur’s Gate and you’re a wizard. All builds should not be viable, but all builds should be interesting. Some skills only pop up one or two times — they will be all the more special for it. While other’s buzz around as often as possible. (Empathy always tells you what people are feeling but when Shivers comes in, it’s a special moment.)

4) Fit on a napkin or fit in the trash bin
After a while, we want you to be able to draw the entire system on a napkin from your head. That’s how elegant and self contained we want it to be. There are four stats and everything folds back into their value. We only use six sided dice. We prefer the Babylonian system of sixes to the Roman system of tens. (Six is a more comprehensible number, ten is too vague and philosophical and includes a zero). If we have a number in the rules – 4, 3 or 6 – we will reuse it as often as possible. All numbers fold back into themselves, everything is it’s own cap, never multiply, never produce long formulas.

5) Small numbers
Congratulations, you just got +1 of something. It’s a big deal. Six is the maximum. You don’t get 28 experience, you get ONE POINT to put into a skill. That one point gives you the aforementioned +1 bonus. You don’t suffer 76 damage, you lose TWO LIVES. The smaller a number, the less you have of it, the more dramatic it will feel. We large mammals have two to three offspring. We have one home. We have two eyes. Our numerical values are large and chunky, losing one is tragic and gaining one is a triumph. Our system reflects that.

6) Innovate like a fool
Innovate for innovation’s sake. This isn’t a medical procedure, it’s a rule system for a game. If we see a way to ditch experience then let’s do it. Sure, we could divide a point into 100 experience and it would let us balance the game better, but let’s not. Let’s not do levels either, carrying around points has been done less. And how about GAME OVER if you run out of money? Let’s do a clock too. A real time of day system will let us build great systems around it, imagine the great names we can give to talents for evening people! Above all – introduce hugely ambitious superstructures. A great failure is ten times better than a small success.


Gosh! Even more concept art for our skill icons​

+1 Tabletop is god
We believe in great D&D. Not in high fantasy or cyberpunk but in the potential of the underlying tabletop experience. If the Game Master has a great story and the players are competent writers too… tabletop wipes the floor with any other medium. (Literature and video games included.) The Zola, Gombrowicz and Bulgakov of our time are already playing D&D, possibly around one table. The trouble is – the experience cannot be recorded and relayed to others. Tabletop is written on water.

Therefore we believe in video game adaptations of the tabletop experience. Games have had great success adapting tactical combat oriented D&D into video games. (Baldur’s Gate 2, etc). We want to do the same for heavy duty story oriented D&D.


#Drizzt
 
Thanks, dudes.

Newspost time! Robert, the design lead on NO TRUCE! talked about passive skill checks previously, but presents the active checks this time around:

"ON ACTIVE SKILL CHECKS"

kinematroopika-150x150.jpg

Robert Kurvitz
Game Designer

Last time I talked about our passive skills checks – ideas involuntarily forming in your head, sensations creeping up your spine. An active skill check on the other hand, is the moment where you force your mind and body to react in a certain way. You direct a skill to go off.

Every dialogue has at least one active skill check moment. Think of these as important shots in a combat sequence, mini showdowns that form a knot in the scene. This is what the story has been building towards. Have they been lying to you all along? Can you dance, or will you grab the mic and sing karaoke? We want every appearance of an active skill check to feel weighty. It’s a dramatic juncture: either a closed door or a fork in the road.

In No Truce With The Furies an active check appears in the form of a special dialogue option. This usually happens deep in the conversation. It looks like a regular dialogue option, but highlighted:



The phrasing of this special dialogue option tells you what you are trying to do, not what you will do. A tooltip menu tells you what your chances of succeeding are:



The task and your ability to perform it face each other like armies on a battlefield. You vs the world. On your side are your stats (character creation), your learned skill (leveling up) plus the items in your inventory and the thoughts you’re thinking. On the opposing side – the difficulty of the task.

An element of chance determines the outcome of this attempt, the game roll two six sided die. But before you do you should prepare, nudge the odds in your favour a bit.

PREPARING FOR BATTLE
You can prop up your side by rummaging through your Thought Cabinet and changing stuff around: maybe it would pay to be a radical feminist at this juncture? Or wait, no! Better to think really, really hardcore racist thoughts. That’ll do the trick, dazzle them with your advanced race theory! But would your character do that? Do you want to take your character in that direction?

Maybe you should just use drugs and face the consequences later – or put points in the appropriate skill if you have any saved up. We want to bring min-maxing (upgrading your character on the fly), potion use (drug use in our case) and inventory management (changing thoughts in your Thought Cabinet) to dialogues. We want you to buff yourself up mid-dialogue and play it like a turn-based combat encounter.

ON THE OPPOSING SIDE
It’s not only you who can change. The task at hand becomes harder or easier depending on the changes you’ve made to the world. Wanted to “get” what “the kids nowadays” are listening to? Maybe the music happens to be a bit more “basic” today because you waited? Wanted to convince your partner to get drunk on the job? Something you said has him reaching for the bottle. Wanted to come up with an exciting mystery? A shadow on the wall in the evening light has inspired you. All these things can make checks harder or easier. These modifiers give us a nifty little tool to show the player the game is taking note of their actions in the world.

So okay, you’ve seen the odds and you’ve seen the modifiers. Now it’s time to either click on the check or not. If you do, what – if any – are the risks in failure? This is where my favourite thing about our active checks comes in. Notice how we use two colours of highlighting?



That’s because there are two types: white checks and red checks. What happens once you roll depends on the type of check. The first is safer.

WHITE CHECKS
If you fail a white check you suffer damage (mental or physical), but nothing happens in the world, no negative consequences – just the lack of positive ones. You don’t “get” that wacky music, your partner tells you to stop trying to get him fired. The proverbial door in the dungeon remains closed, the treasure out of reach. The white check becomes greyed out. You can try again later once you’re better at it (put more points into the skill), or you can make the task easier by changing the environment.

But if you do succeed you get access to a special nook in the content: a bunch of clever things to say that build your character, or inadvertedly solving a problem somewhere else. You may even find an entirely new side-case to take on, or new project for your Thought Cabinet to process. And yes, even a bigger gun, we’re not above those. White checks are locked gates for content and rewards. (Which we think of as one and the same). Even finding the gates counts as progress. Part of the game is mapping out these “closed doors” and then returning later once you think you need what is behind them – or if you’re just curious.

Our Art Director compares white checks to using dynamite to mine more content out of the game. It’s relatively safe if you plan your rolls carefully.

RED CHECKS
… on the other hand are dangerously unsafe.





If you roll this bad boy – THE NEGATIVE RESULT IS PLAYED OUT TOO. Say you were trying to come up with an idea and you fail a red check. You don’t just stand there clueless, you come up with a bad idea. A really bad one. An utterly idiotic one. Tell your friends to fuck off. Tell them all to fuck off because they’re “cramping your style.” Your character will not be able to tell the difference, they will think it’s a good idea. You the player are stuck saying something truly idiotic with a winning grin on your chartacter’s face – or doing something very dangerous.

You can always come back to white checks but red checks can only be rolled once: here and now. If you don’t they are lost forever. Think of red checks as forks in the path – if you don’t try it now you will never get the chance to. The suspect will leave the hallway, the train will leave the station. But you might fail if you do.

FAILURE IS FUN
Players almost always try red checks. The negative content is fuel for role playing, and it’s also – dare I say – fun. Failure puts you in the skin of your character. You can be embarrassing. Weak. Ridiculous. Full of yourself. Just plain wrong. Paranoid. Idiotic. Every director knows that actors build characters out of failures and fears, not heroics. We’ve noticed players instinctively feel the same way. They begin to search for red checks to fail at. Especially the right ones – the ones that fit their character. They do this for character building, but also because they’re curious of the outcome. It feels like playing with fire.

This adds an interesting effect to Skills and spending experience to improve them. It can be useful not to. It’s rewarding to be bad at some things because it produces interesting red check failures.

You’re right to raise an eyebrow. “In our game you are defined equally by your strengths and weaknesses” is the type of wonk a lot of designers would like to utter. But I think we’re slowly earning our right to. Week by week, month by month of development.

You’ll be the judge of it of course. Either way, I bet you’ll pull that red trigger 90% of the time.

Next week I’ll introduce our four main stats and how they shape your character. 'Til then!
 
Last edited:
Nice, hey could you show an example of a single minor quest and the outcomes it has? Kind of like an unplayable demo.
 
I´d love to give you something more specific, but that´s all I´ve got right now. When the time comes, you can be sure I´ll get you all the info. Whether it is public or closed, paid or free etc etc - you´ll just have to hang in there for a little while :)
 
Double post for the sake of news

"ICE BEAR SARCOPHAGUS"

batuavatur-150x150.png

Kasparov
Concept artist

Concept art for the Ice Bear Sarcophagus, a novelty fridge made by the inexplicably unsuccessful ice cream manufacturers Revachol Ice City. Someone in Revachol Ice City had a friend who was a taxidermist. The formidable Ice Bear Sarcophagus is made of real bear.

 
Back
Top