Difference of kinds? (also, illegal object number in a relation)

Hello! I’m working on a heavily simulationist story set in the Belgian resistance to Nazi occupation in WW2. It includes a lot of procedurally generated NPCs who can speak different languages and belong to one faction or another. Some kinds of clothing are identified with factions; for example, a gendarme’s tunic represents the Belgian police. Also, non-civilians can order one another around; because this is an occupied country, this means that German NCOs can order the police to do things.

Currently I have this implemented as follows:

A language is a kind of value. The languages are French, Dutch, German.
Fluency relates various people to various languages. The verb to be fluent in means the fluency relation.
A faction is a kind of value. The factions are ... [spoilers!]
A uniform is a kind of thing. A uniform is usually wearable. A gendarme's tunic is a kind of uniform. A Feldbluse is a kind of uniform. 
Representation relates a uniform to a faction. The verb to represent means the representation relation.
Every gendarme's tunic represents Gendarmerie. Every Feldbluse represents Gestapo.
A soldier is a kind of man. The faction of a soldier is usually Gestapo. A soldier has a number called rank. The rank of a soldier is usually 0. 
A gendarme is a kind of soldier. The faction of a gendarme is Gendarmerie. Every gendarme is fluent in French and Dutch. Every gendarme wears a gendarme's tunic.
There are 5 soldiers. There are 2 gendarmes.

And this all works fine. (There are higher ranks of soldier, but I’ve left them out for conciseness.) The problem is, how do I express things about the class of soldiers who aren’t gendarmes? The compiler won’t let me say “Every Gestapo soldier is fluent in German.” If I say “Every non-gendarme soldier is fluent in German,” the compiler accepts it, but when I look at RELATIONS in a debug build, this happens:

>relations
Fluency relates various people to various languages:
  You  >=>  French
  Every non-gendarme soldier  >=>  German
  The gendarme  >=>  French
  The gendarme  >=>  Dutch
  The gendarme  >=>  French
  The gendarme  >=>  Dutch

Which doesn’t look right. If instead I set the fluency relation in the “when play begins” rule that initializes various behind-the-scenes NPC properties, like so:

When play begins:
	let L be the list of Gestapo soldiers;
	repeat with occupier running through L:
		now the occupier is fluent in German;
        [other occupier-initialization things].

then I get a much more reasonable-looking list:

Fluency relates various people to various languages:
  You  >=>  French
  The soldier  >=>  German
  The soldier  >=>  German
  The soldier  >=>  German
  The soldier  >=>  German
  The soldier  >=>  German
  The gendarme  >=>  French
  The gendarme  >=>  Dutch
  The gendarme  >=>  French
  The gendarme  >=>  Dutch

It compiles if I create 5 Feldbluses and add “now the occupier wears a random Feldbluse;” after bestowing fluency, but the Representation part of the relations table looks very weird:

Representation relates a uniform to a faction:
  The Feldbluse  >=>  <illegal object number 14>
  The Feldbluse  >=>  <illegal object number 14>
  The Feldbluse  >=>  <illegal object number 14>
  The Feldbluse  >=>  <illegal object number 14>
  The Feldbluse  >=>  <illegal object number 14>
  The gendarme's tunic  >=>  <illegal object number 3>
  The gendarme's tunic  >=>  <illegal object number 3>

It’s not clear to me why I can iterate over “the list of Gestapo soldiers”, but not create an assembly that applies to Gestapo soldiers. Is there a way to refer to a difference of kinds, i.e., all soldiers who are not gendarmes, in this context? If assigning random generic uniforms to random generic grunts one by one is the way to do it, then that’s fine, but either way, what’s going on with the faction values being illegal objects?

1 Like

I’m pretty much an Inform newbie so hopefully you will get better help soon, but as to the first set of questions, what’s happening is that when you say “Every non-gendarme soldier is fluent in German”, Inform doesn’t know what “non-gendarme” means and thinks you want to create a new soldier named “every non-gendarme soldier” who is fluent in German (that’s him, right under “you” on the list! His parents must have been hippies).

In terms of what would work, it’s easy enough to just say “Every soldier is usually fluent in German.” to get your default set up – the problem, which you’ve probably run into, is how to un-set a fluency relationship when creating a different kind of soldier, because I couldn’t suss that out from trial and error or the documentation. If you are in the market for an awful hack, though, this would work:

When play begins:
	Repeat with G running through gendarmes:
		Now G is not fluent in German.

Another ugly hacky approach would be to create an intermediate branch in your taxonomy of soldiers – create the soldier kind and gendarme kind as defined above, then say “A soldat is a kind of soldier. Every soldat is usually fluent in German.” Then define your additional folks as soldats, and Bob’s your uncle.

The illegal object error is well above my pay grade, but FWIW the faction does appear to be correctly applied – I tried this:

Every turn:
	Repeat with T running through gendarme's tunics:
		Say "This is a tunic - ";
		If the gendarme's tunic represents gendarmerie:
			Say "that represents the gendarmerie."

And get the desired output:

>z
Time passes.

This is a tunic - that represents the gendarmerie.
This is a tunic - that represents the gendarmerie.

As I said, I hope you get a better class of assistance soon, but in the interim maybe some of this will get you somewhere!

3 Likes

FWIW I had to add A soldier has a faction. to your original list of assertions to get things to compile.

The problem with this is that it declares a property of the soldier, and while Inform will let you declare initial values for properties as assertions (and you can specify a default on a kind and override that at sub-kind or individual object level), it won’t let you make other kinds of generalisation or change the value at the assertion level – you need to do it in a rule instead (such as when play begins, as suggested).

If you want to make assertions, you can replace that property with a relationship, for example:

Allegiance relates various soldiers to one faction.
The verb to be allied with means the allegiance relation.

An officer is a kind of soldier.  Every officer is allied with Gestapo.
A gendarme is a kind of soldier.  Every gendarme is allied with Gendarmerie.

(Note that we had to introduce a new kind of soldier for the Gestapo – you will need to use this instead of the generic soldier. Assertions don’t let you make contradictions, so you can’t declare that every soldier is Gestapo and then overrule it just for gendarmes. Or at least I’m not aware of a way around that.)

As for the illegal object numbers – this is just a guess, but I think that’s purely a display bug in the relations verb. It’s implemented in I6 code that doesn’t know the kinds involved in the relation, so it just assumes you’re relating objects together (since that’s what the examples in the manual do), and so when it goes to print a value as an object it gets a silly result.

2 Likes

You’ll also need to be careful about your generalisations if allegiances can change during the course of the story.

Both properties and relationships can be changed during play, but kinds can’t. So a specific officer may start out being allied with Gestapo before later being turned, and you can change their allegiance (and give them a new tunic, if you made some extras or took one off a dead gendarme), but they will always be an officer and cannot ever be a gendarme.

If you want things to be more fluid, you could remove the distinction on kind:

Definition: a soldier is an officer if they are allied with Gestapo.
Definition: a soldier is a gendarme if they are allied with Gendarmerie.

There are 5 soldiers allied with Gestapo.
There are 2 soldiers allied with Gendarmerie.

But, with this syntax I’m not sure how to set additional relations, such as fluency (the every gendarme phrase doesn’t work, it creates a new object with that name).

1 Like

For the technically curious – this appears to be a bug in Relation_ShowOtoO, it only considers the kind of the left side of the relationship. (I’m not quite sure how relates various soldiers to one faction ended up as a one-to-one relation either, but that also appears to be the case.)

Curiously, this bug doesn’t seem to be present in the other implementations of Show, so if the relation is instead defined as:

Allegience relates one faction to various soldiers.
The verb to be allied with means the reversed allegience relation.

Then the relations command is able to correctly print the faction name and the soldiers names. (This calls Relation_RShowOtoO instead, which doesn’t have the bug.)

Edit: it appears that the way Inform stores relations, this bug will affect both one to one and various to one, but won’t affect any other kind of relation.

2 Likes

That hadn’t occurred to me, thanks! Gendarmes don’t currently have an init routine, but that would indeed work.

That’s what I get for trying to prune down to a representative example. As it happens, A person has a faction. The faction of a person is usually Civilian. I also have allegiance and enmity defined as relating factions to each other – so if a person’s faction changes during play, such as a civilian being recruited to a resistance faction (of which there are several, not all mutually trusting – welcome to Belgium!), the interpersonal logic that’s predicated on faction should (hopefully!) follow appropriately.

Hrm. So if I’ve defined A Sturmmann is a kind of soldier with rank 1. A Scharfuehrer is a kind of soldier with rank 2., and then lower someone’s rank in-play, the kind doesn’t change to reflect the demotion? (I wasn’t planning on incorporating promotion/demotion, but that’s good to know.)

I think overall the cleanest solution will be to create a kind for rank-0 German soldiers, like Mike Russo suggested, then set the fluency and uniform for each German rank separately. It’s a bit repetitive, but the more logic I can pull out of the init routines, the better.

That’s a relief. Should I file a bug about this?

Thank you for all the help, Mike and Gavin!

If you have access to the bug-reporting site, sure. I don’t. :cry:

In the meantime, though, you can install this extension and then adding the following line should magically fix the relations verb:

Include 6M62 Patches by Friends of I7.
3 Likes

Thanks again! That was fast.

Correct. The kind can’t change.

Actually, it sounds like you’re mostly just wanting to set up a bunch of associated properties – have you considered simply using only the soldier kind and using a table to declare specifics?

Table of Soldiering
soldier	faction	rank
Mann 1	Gestapo	0
Mann 2	Gestapo	0
Sturmmann	Gestapo	1
Scharfuehrer 	Gestapo	2
gendarme 1	Gendarmerie	0
gendarme 2	Gendarmerie	0

Some soldiers are defined by the Table of Soldiering.

The downside to this is that you have to give them unique names (in the first column), which will ordinarily be visible to the player (although you can do some extra things to work around that). I don’t think this lets you create anonymous objects.

Tables may be useful in other ways as well, although I can’t think of a specific example right this moment.

note that mann/sturmann/scharfuhrer are SA/SS ranks, not necessarily gestapo (AFAICT as mil historian, all gestapo field agents was officier (untersturmfuhrer and above)

let’s put aside that SA/SS with police authority (as gestapo minions was) has actually double rank SS and police, in a winded, germanic above nazi, long title like “unterssturmfuhrer und leutnant der poliziei”

I can’t hide that I will give high votes if this game has at least 20 creative, painful and humiliating means of killing these minions of evil e.g:

FIRE FLAMETHROWER TO NAZI

You fire the flamethrower to the sturmbannfuhrer. He scream in utter pain and agony from the burning flames, anticipatory of the depths of hell he’s to be plunging

PUSH NAZI WITH FLAMETHROWER

You push with the hot barrel of the flamethrower the soon-to-be-late sturmbannfuhrer into the latrine. his screams reach an apex when he realized where is to die, and he makes a futile and pathetic attempt to climb out of the shit, but the stench and the flames overcome him and he painfully dies of his deserved death.
[your score goes up 50 points !]

Best regards from Italy,
dott. Piergiorgio.