Body part templates

Hey, no question this time, just want to share a way to cut down on duplicate code for body parts and other possessions (clothes etc) that are nearly interchangeable but should be referred to based their relationship to some other entity, for example “your body”, “the NPC’s body” or “a lifeless body”.

Here is the “base class”:

($object is recursively part of $actor)
    ($object is #partof $actor)

($object is recursively part of $actor)
    ($object is #partof $something)
    ($something is recursively part of $actor)

(name (bio $bio))
    (if) (current player $player) ($bio is recursively part of $player) (then)
        your (bio name $bio)
    (elseif) ($bio is recursively part of $someone) (animate $someone) (then)
        (bio name $bio) of (the $someone)
    (else)
        lifeless (bio name $bio)
    (endif)

(dict (bio $bio))
 (bio dict $bio)
 (if) (current player $player) ($bio is recursively part of $player) (then)
     me myself self yourself you my mine
 (elseif) ($bio is recursively part of $someone) (animate $someone) (then)
     (their $someone) (bio name $bio)
 (else)
     corpse corpses dead
 (endif)

(proper (bio $bio))
    (current player $player)
    ($bio is recursively part of $player)

(singleton (bio $bio))
    ($bio is recursively part of $someone)
    (animate $someone)

(item (bio $))

Then for any body part subclass you can write, for example:

(bio $feet)
    *(feet $feet)

(bio name (feet $))
    feet

(bio dict (feet $))
    foot ankle ankles

(plural (feet $))

And then the instantiations are very simple:

#room
(room *)

#player
(current player *)
(* is #in #room)

#player-feet
(feet *)

(#player-feet is #partof #player)

#npc
(name *)
 NPC
(their *)
 NPC's
(animate *)
(* is #in #room)

#npc-feet
(feet *)

(#npc-feet is #partof #npc)

#lifeless-feet
(feet *)
(* is #in #room)

Testing it out:

> examine feet
Did you want to examine your feet, the lifeless feet, or the feet of the NPC?

I plan to tweak things some more (more parameterizing, so “lifeless body” but “severed feet” for instance), but I think for body parts this system is pretty useful. I need to think a little about a templating system for wearable things, since who an amulet belongs to (“the hero’s amulet”) isn’t necessarily the same as who is wearing it (“the hero’s amulet being worn by the villain who stole it”).

2 Likes

I found that a ($ belongs to $) predicate can simplify things significantly, and allow clothing to be handled almost identically to body parts:

%% bodypart.dg

(name (bodypart $bodypart))
 (if)
  ($bodypart belongs to $owner)
 (then)
  (their $owner)
 (endif)
 (bodypart name $bodypart)

(dict (bodypart $bodypart))
 (bodypart dict $bodypart)
 (if)
  ($bodypart belongs to $owner)
 (then)
  of (dict $owner)
 (endif)

(their (bodypart $bodypart))
 (bodypart their $bodypart)

(proper (bodypart $bodypart))
 (if)
  ($bodypart belongs to $owner)
 (then)
  (proper $owner)
 (else)
  (bodypart proper $bodypart)
 (endif)

(singleton (bodypart $bodypart))
 (if)
  ($bodypart belongs to $owner)
 (then)
  (singleton $owner)
 (else)
  (bodypart singleton $bodypart)
 (endif)

(bodypart proper $bodypart)
 (fail)

(bodypart singleton $bodypart)
 (fail)

(item (bodypart $bodypart))

%% clothing.dg

(name (clothing $clothing))
 (if)
  ($clothing belongs to $owner)
 (then)
  (their $owner)
 (endif)
 (clothing name $clothing)

(dict (clothing $clothing))
 (clothing dict $clothing)
 (if)
  ($clothing belongs to $owner)
 (then)
  of (dict $owner)
 (endif)

(their (clothing $clothing))
 (clothing their $clothing)

(proper (clothing $clothing))
 (if)
  ($clothing belongs to $owner)
 (then)
  (proper $owner)
 (else)
  (clothing proper $clothing)
 (endif)

(singleton (clothing $clothing))
 (if)
  ($clothing belongs to $owner)
 (then)
  (singleton $owner)
 (else)
  (clothing singleton $clothing)
 (endif)

(clothing proper $)
 (fail)

(clothing singleton $)
 (fail)

(wearable (clothing $clothing))

%% body.dg

(bodypart $body)
 *(body $body)

(bodypart name (body $))
 body

(bodypart dict (body $))
 bodypart bodyparts bodies

(bodypart their (body $))
 body's

%% shirt.dg

(clothing $shirt)
 *(shirt $shirt)

(clothing name (shirt $))
 shirt

(clothing dict (shirt $))
 clothing clothes

(clothing their (body $))
 shirt's

Test things out with:

(their (current player $))
 your

#room
 (room *)

#player
(current player *)
(* is #in #room)
(proper *)

#player-body
(body *)
(* is #partof #player)
(* belongs to #player)

#player-shirt
(shirt *)
(* is #wornby #player)
(* belongs to #player)

#npc
(* is #in #room)
(name *)
 NPC
(their *)
 NPC's
(singleton *)
(animate *)

#npc-body
(body *)
(* is #partof #npc)
(* belongs to #npc)

#npc-shirt
(shirt *)
(* is #wornby #npc)
(* belongs to #npc)
2 Likes

This is nice! I’m thinking about adding ($ belongs to $) to the standard library now.

One thing, though: Remember that (dict $) is invoked with a multi-query. When you delegate to (dict $Owner), that needs to be a multi-query too, otherwise you may miss some of the words. But you can use the multi-queries to your advantage, and simplify the code into something like this:

(dict (body $))
    bodypart bodyparts bodies

(dict ($ belongs to $Owner))
    *(dict $Owner)

There’s no need to specify “of” in a dict-rule; that word is taken care of by the library.

An interesting side-effect of tracking ownership like this, separate from the object tree, is that if the ownership structure is static—if the game never attempts to modify ($ belongs to $) with (now)—then the compiler will automatically incorporate it into the word-to-object lookup tables. That is, “EXAMINE HERO” will consult a static word table about “HERO”, and find the hero, the left foot of the hero, the right foot of the hero, and so on.

However, in a game where ($ belongs to $) is dynamic, the lookup tables will be disabled. That’s because any object could in principle be owned by any other object, and thus be referred to by the words of any other object. We can limit the damage by refining the delegating dict-rule:

(dict ($ belongs to $Owner))
    (animate $Owner)
    *(dict $Owner)

Now the compiler knows that the words for any animate object could, in principle, refer to any other object in the game, so the lookup tables will be disabled for words such as “HERO”. But they will remain in effect for words that cannot themselves refer to an animate object, such as “FOOT”.

Actually, I may have said too much. The current version of the compiler does not incorporate the owner structure into the word lookup tables. This requires some changes to the optimizer.

Meanwhile, I’m also thinking about ways to modify the parser code in the library, to deal with dynamic ownership. If we match some of the input words to one object, and some to another object, and it turns out that one of the objects is the current owner of the other, then the whole thing could be treated as a match for the second object. That would eliminate the need for a link from “HERO” to #foot-of-hero in the static word-to-object table. But it would require a sligthly different (determine object $) construct, one that doesn’t insist on matching every word of input.

I generalized body parts and clothing into “possessions”, which are any kind of object that can belong to someone, with ownership denoted in the name. I also made it so that possessions have an optional prefix and suffix, so you can get things like “the elf’s pointy ears” from an object derived from an “ears” subclass.

%% possession.dg

(name (possession $possession))
 (if)
  ($possession belongs to $owner)
 (then)
  (their $owner)
 (endif)
 (possession prefix $possession)
 (possession name $possession)
 (possession suffix $possession)

(possession prefix $)
(possession suffix $)

(dict ($ belongs to $owner))
 *(dict $owner)

(proper (possession $possession))
 (if)
  ($possession belongs to $owner)
 (then)
  (proper $owner)
 (else)
  (possession proper $possession)
 (endif)

(possession proper $)
 (fail)

(singleton (possession $possession))
 (if)
  ($possession belongs to $owner)
 (then)
  (singleton $owner)
 (else)
  (possession singleton $possession)
 (endif)

(possession singleton $)
 (fail)
%% bodypart.dg

(possession *(bodypart $))
  
(dict (bodypart $))
 bodypart

(plural dict (bodypart $))
 bodyparts

(item (bodypart $))
%% clothing.dg

(possession *(clothing $))
  
(dict (clothing $))
 clothing garment

(plural dict (clothing $))
 clothings clothes garments

(wearable (clothing $))

An example subclass and object:

%% ears.dg

(bodypart *(ears $))
  
(possession name (ears $))
 ears

(dict (ears $))
 ear

(plural (ears $))
#room
(room *)

#player
(current player *)
(* is #in #room)
(their *)
 your

#player-ears
(ears *)
(* is #partof #player)
(* belongs to #player)
(possession prefix *)
 pointy