Layering Clothing

I’m interested in clothes that layer: you can’t take off your socks without first taking off your shoes, and you can’t put shoes on over other shoes, that sort of thing. I limited myself to handling footwear so far to keep things simple. I decided not to include body part objects for this exercise, though if desired I think the same general system would work for covering body parts with clothes.

(prevent [wear $new-footwear])
 {
  (terminal footwear $new-footwear)
 (or)
  (layerable footwear $new-footwear)
 }
 (current player $actor)
 ($old-footwear is #wornby $actor)
 (terminal footwear $old-footwear)
 You would have to remove (the $old-footwear) first.
(perform [wear $new-footwear])
 {
  (terminal footwear $new-footwear)
 (or)
  (layerable footwear $new-footwear)
 }
 (current player $actor)
 ($old-footwear is #wornby $actor)
 (layerable footwear $old-footwear)
 ~($old-footwear is covered by $)
 (narrate wearing $new-footwear)
 (now) ($new-footwear is #wornby $actor)
 (now) ($new-footwear is handled)
 (now) ($old-footwear is covered by $new-footwear)
(prevent [remove $covered])
 ($covered is covered by $covering)
 You can't take off (the $covered) without first taking off (the $covering).
(perform [remove $covering])
 ($covered is covered by $covering)
 (narrate removing $covering)
 (current player $actor)
 (now) ($covering is #heldby $actor)
 (now) ($covering is handled)
 (now) ~($covered is covered by $)

Here is playing around with the code.

> wear red socks
(first attempting to take your red socks)
You take your red socks.
You put on your red socks.

> wear blue socks
(first attempting to take your blue socks)
You take your blue socks.
You put on your blue socks.

> remove red socks
You can't take off your red socks without first taking off your blue socks.

> wear red shoes
(first attempting to take your red shoes)
You take your red shoes.
You put on your red shoes.

> wear blue shoes
(first attempting to take your blue shoes)
You take your blue shoes.
You would have to remove your red shoes first.

I want to extend this to more types of clothes, handle taking inventory so that layering is expressed to the player in an understandable fashion, handle concealment of underlying layers (underwear), and so on; I’ll post progress reports here.

2 Likes

I managed to make it so that socks, shoes, underwear and pants all coexist and layer in their own little stacks without resorting to a “foot slot, lower body slot”, etc system. There are two predicates needed, ($ can go underneath $) that defines how layering works and ($ can coexist with $) that defines what can exist in a separate stack.

%% Coder responsible for all other (their $) predicate definitions

(their (current player $))
 your

This is the base class for any type of thing that can belong to someone.

%% Possession class

(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)

All clothes derive from this subclass.

%% Clothing subclass

(possession *(clothing $))

(dict (clothing $))
 clothing garment

(plural dict (clothing $))
 clothing clothes garments

(wearable (clothing $))

Socks can go under socks and shoes, and coexist with underwear and pants.

%% Socks subclass

(clothing *(socks $))

(possession name (socks $))
 socks

(dict (socks $))
 sock

(plural (socks $))

((socks $) can go underneath (socks $))
((socks $) can go underneath (shoes $))

((socks $) can coexist with (underwear $))
((socks $) can coexist with (pants $))
((shoes $) can coexist with (necklace $))

Nothing goes over shoes; they coexist with underwear and pants.

%% Shoes subclass

(clothing *(shoes $))

(possession name (shoes $))
 shoes

(dict (shoes $))
 shoe

(plural (shoes $))

((shoes $) can coexist with (underwear $))
((shoes $) can coexist with (pants $))
((shoes $) can coexist with (necklace $))

Underwear can go under underwear and pants, and coexist with socks and shoes.

%% Underwear

(clothing *(underwear $))

(possession name (underwear $))
 underwear

(plural dict (underwear $))
 underwears

((underwear $) can go underneath (underwear $))
((underwear $) can go underneath (pants $))

((underwear $) can coexist with (socks $))
((underwear $) can coexist with (shoes $))
((underwear $) can coexist with (necklace $))

Pants can go under other pants as well as underwear, even though doing so is a bit silly. They coexist with socks and shoes.

%% Pants

(clothing *(pants $))

(possession name (pants $))
 pants

(dict (pants $))
 pant

(plural (pants $))

((pants $) can go underneath (underwear $))
((pants $) can go underneath (pants $))

((pants $) can coexist with (socks $))
((pants $) can coexist with (shoes $))
((pants $) can coexist with (necklace $))

The prevent rule is complicated, it collects all the exposed clothing and if none of them can go under the clothing being put on nor coexist with them, wearing is prevented. EDIT - Informative error message.

%% Wearing rules

(prevent [wear $new-clothing])
 (current player $actor)
 (collect $worn-clothing)
  *($worn-clothing is #wornby $actor)
  ~($worn-clothing is underneath $)
 (into $exposed-clothing)
 ~(empty $exposed-clothing)
 *($choice is one of $exposed-clothing)
 ~($choice can go underneath $new-clothing)
 ~($choice can coexist with $new-clothing)
 You can't wear (the $new-clothing)
 without first removing (the $choice).

(perform [wear $new-clothing])
 (current player $actor)
 *($old-clothing is #wornby $actor)
 ~($old-clothing is underneath $)
 ($old-clothing can go underneath $new-clothing)
 (narrate wearing $new-clothing over $old-clothing)
 (now) ($new-clothing is #wornby $actor)
 (now) ($new-clothing is handled)
 (now) ($old-clothing is underneath $new-clothing)

(narrate wearing $new-clothing over $old-clothing)
 You put on (the $new-clothing)
 over (the $old-clothing).

Removing layered clothes is simpler.

%% Removing rules

(prevent [remove $covered])
 ($covered is underneath $covering)
 You can't remove (the $covered)
 without first removing (the $covering).

(perform [remove $covering])
 (current player $actor)
 *($covered is #wornby $actor)
 ($covered is underneath $covering)
 (narrate removing $covering exposing $covered)
 (now) ($covering is #heldby $actor)
 (now) ($covering is handled)
 (now) ~($covered is underneath $)

(narrate removing $covering exposing $covered)
 You take off (the $covering),
 exposing (the $covered).

One issue is initialization at the start of the game, if you want the player to start clothed in layers you have to manually set up the ($ is underneath $) relationships, which isn’t hard but tedious.


EDIT: Tried something new, a necklace subclass that can coexist with itself, allowing you to put on and remove them in any order without them layering. This seems to work fine, but it’s becoming clear that having to write coexist rules for every pair of subclass is going to get unwieldy. I’m trying to think of a way to infer coexist rules without having to write them all out.

%% Necklace

(clothing *(necklace $))

(possession name (necklace $))
 necklace

(plural dict (necklace $))
 necklaces

((necklace $) can coexist with (socks $))
((necklace $) can coexist with (shoes $))
((necklace $) can coexist with (underwear $))
((necklace $) can coexist with (pants $))
((necklace $) can coexist with (necklace $))
2 Likes

I found a solution to require significantly less written out ($ can coexist with $) rules.

($clothing1 can coexist with $clothing2)
 ~($clothing1 can go underneath $clothing2)
 ~($clothing2 can go underneath $clothing1)

There is one problem with this: it doesn’t handle preventing shoes being put on in a separate stack when you have shoes (which can’t layer under anything) on already. This is the case where you do have to explicitly state that shoes can’t coexist with shoes; do this for any clothes you can’t put anything over.

~((shoes $) can coexist with (shoes $))

Edit, here’s a simple inventory printout predicate:

(perform [inventory])
 (current player $player)
 (collect $x)
  *($x is #heldby $player)
 (into $held)
 (collect $x)
  *($x is #wornby $player)
  ~($x is underneath $)
 (into $exposed)
 (collect $x)
  *($x is #wornby $player)
  ($x is underneath $)
 (into $covered)
 (if)
  (empty $held)
 (then)
  You are empty handed.
 (else)
  You hold (a $held).
 (endif)
 (if)
  (empty $exposed)
 (then)
  You are naked.
 (else)
  You wear (a $exposed).
  (if)
   ~(empty $covered)
  (then)
   Underneath you wear (a $covered).
  (endif)
 (endif)
 (line)

> inv
You hold your red pants and your blue shoes. You wear your blue necklace, your red necklace, your blue pants, and your red shoes. Underneath you wear your blue underwear, your red underwear, your blue socks, and your red socks.

I would prefer something of the form “You wear your blue necklace, your red necklace, your blue pants over your blue underwear over your red underwear, and your red shoes over your blue socks over your red socks.”, but haven’t thought how to do that just yet.

3 Likes

I’ve come up with a simpler layered clothing solution, with its behavior controlled by a single predicate (wearing and removing $ is prevented by $). To make it so you can’t wear socks over shoes or take socks off while you are wearing shoes, for example, you would write (wearing and removing (socks $) is prevented by (shoes $)). To make it so you can’t wear multiple pairs of shoes at the same time, you would write (wearing and removing (shoes $) is prevented by (shoes $)).

Here is the implementation.

(before [wear $New])
        (current player $Player)
        ~($New is #wornby $Player)
        (collect $C)
                *($C is #wornby $Player)
        (into $Olds)
        ~(empty $Olds)
        *($Old is one of $Olds)
        (wearing and removing $New is prevented by $Old)
        (collect $C)
                ($C is #wornby $Player)
                (wearing and removing $Old is prevented by $C)
                ~($Old = $C)
        (into $Blockers)
        (empty $Blockers)
        (first try [remove $Old])
(prevent [wear $New])
        (current player $Player)
        ~($New is #wornby $Player)
        (collect $C)
                *($C is #wornby $Player)
                (wearing and removing $New is prevented by $C)
        (into $Blockers)
        ~(empty $Blockers)
        You can't wear (the $New) because you failed to remove (a $Blockers).
(before [remove $Old])
        (current player $Player)
        (collect $C)
                *($C is #wornby $Player)
                (wearing and removing $Old is prevented by $C)
                ~($C = $Old)
        (into $Blockers)
        ~(empty $Blockers)
        *($Blocker is one of $Blockers)
        (collect $C)
                *($C is #wornby $Player)
                (wearing and removing $Blocker is prevented by $C)
                ~($C = $Blocker)
        (into $BlockerBlockers)
        (empty $BlockerBlockers)
        (first try [remove $Blocker])
(prevent [remove $Old])
        (current player $Player)
        (collect $C)
                *($C is #wornby $Player)
                ~($Old = $C)
                (wearing and removing $Old is prevented by $C)
        (into $Blockers)
        ~(empty $Blockers)
        You can't remove (the $Old) because you failed to remove (a $Blockers).

EDIT: (before [wear $]) and (prevent [wear $]) no longer apply if you are already wearing the clothes.

4 Likes

A basic first attempt at concealment by clothes, by adding a (viewing $ is prevented by $) predicate:

(perform [examine $Obj])
        ($Obj is #wornby $Someone)
        (collect $C)
                *($C is #wornby $Someone)
                (viewing $Obj is prevented by $C)
                ~($Obj = $C)
        (into $Concealers)
        ~(empty $Concealers)
        (The $Someone) (is $Someone) wearing (the $Concealers), blocking your view of (the $Obj).

This allows:

> examine underwear
Tom is wearing the pants, blocking your view of the underwear.

This raises a conundrum: should the underwear even be in scope to be referred to, or should clothes blocked from view be treated similarly to objects in a closed opaque container? I think the player’s clothes should always be in scope because the player can feel what they are wearing, but with other characters the right behavior isn’t obvious, I’d welcome thoughts on that.

2 Likes

I would say the latter; it would be annoying if the default behavior resulted in responses like “Clark is wearing a suit and tie, blocking your view of the superhero costume.” :wink:

2 Likes

BadParser makes a good point; the following code makes concealed clothes worn by someone else out of scope:

~($Obj is in scope)
        (current player $Player)
        ($Obj is #wornby $Someone)
        ~($Player = $Someone)
        (collect $C)
                *($C is #wornby $Someone)
                (viewing $Obj is prevented by $C)
                ~($Obj = $C)
        (into $Concealers)
        ~(empty $Concealers)

Next, we need to not be able to find underwear worn by someone else if it is concealed:

(prevent [find $Obj])
        ($Obj is in room $Room)
        (current room $Room)
        (current player $Player)
        ($Obj is #wornby $Someone)
        ~($Player = $Someone)
        (collect $C)
                *($C is #wornby $Someone)
                (viewing $Obj is prevented by $C)
                ~($Obj = $C)
        (into $Concealers)
        ~(empty $Concealers)
        You don't know where to find (the $Obj).
(perform [find $Obj])
        (current player $Player) 
        ($Obj is #wornby $Someone) 
        ~($Player = $Someone)
        (collect $C)
                *($C is #wornby $Someone)
                (viewing $Obj is prevented by $C)
                ~($Obj = $C) 
        (into $Concealers)
        ~(empty $Concealers) 
        You don't know where to find (the $Obj).

Actually, stdlib.dg has a similar issue where find can be used to determine the contents of an opaque, closed, unopenable box even though the contents are out of scope for examine; I wonder if that can be easily fixed?

Finally, a message for concealed clothes you are wearing:

(perform [examine $Obj])
        (current player $Player)
        ($Obj is #wornby $Player)
        (collect $C)
                *($C is #wornby $Player)
                (viewing $Obj is prevented by $C)
                ~($Obj = $C)
        (into $Concealers)
        ~(empty $Concealers)
        You can't see (the $Obj) you are wearing
        because of (the $Concealers) you are wearing,
        but you know (it $Obj) (is $Obj) there because can feel (it $Obj).
1 Like

Hmm… I would avoid this. You are adding a negated rule, in order to prevent a query such as (#shirt is in scope) from succeeding. But it’s conceivable that the library or the story would query *($Obj is in scope) with an unbound parameter, in order to backtrack over every object in scope. In that case, if the negated rule were to succeed, it would also stop the backtracking.

I think it would be better to override the original ($ is in scope) rule entirely, e.g. with (just).

Just a quick tip here: When you collect a bunch of values only to check if the resulting list is empty, then what you’re essentially doing is checking whether one such value exists. You can do that without collecting:

        *($C is #wornby $Someone)
        (viewing $Obj is prevented by $C)
        ~($Obj = $C)
3 Likes