Old school text adventure as a way of re-learning coding

User projects written in or related to FreeBASIC.
olympic sleeper
Posts: 17
Joined: Jun 07, 2020 15:47

Re: Old school text adventure as a way of re-learning coding

Postby olympic sleeper » Jun 28, 2020 8:19

Hello and thank you for all the suggestions. There are really useful ideas here,things I never even thought of so once again thank you.

Sorry for the duplicate, when I posted the initial I got a blank screen as a response, assumed it had not worked, tried again, got the same blank screen and gave up. Oh well.

An explanation of how the preprocessor, interpreter and command handler currently work. I suspect this is not the best, or most elegant way of handling things - but then I'm not a multi-million dollar company. Sorry this is quite long.

Taking give the bone to the large dog and look and wear the blue shirt and put the yellow ball under the red table and look at it as an example.

1) The preprocessor throws away all definite and indefinite articles, so the, an and a, which gives give bone to large dog and look and wear blue shirt and put yellow ball under red table and look at it

2) It links some words together, like look and at which becomes look_at
2a) it converts some synonyms to standard wording like ‘into’ becoming ‘in’, ‘underneath’ becoming ‘under’, ‘take off’ becoming remove etc.

3) It ties adjectives and nouns together, and here it makes its first assumption that the basic entered syntax will be in the form <verb> <noun> [<and>]… or <verb> <something> <something> <noun> [<and>], or for some commands like get and put which can refer to another object <verb> <noun><placement> <noun> eg put bone on table. At this point it does not care what the adjective is, the user could have typed get shdjahsdjka bone and put it on agjsdgajd table.

So the above example is now: give bone to large=dog and look and wear blue=shirt and put yellow=ball under red=table and look_at it

4) The code then runs backwards though the sentence, so in the order it look_at and red=table under yellow=ball put and blue=shirt and look and large=dog to bone give finding each occurrence of it and looking either for the word following the next ‘and’ (red=table in this case) or if that is followed by a placement word (in this case ‘under’) the ‘word after that (here its ‘yellow=ball’). This is a bit ambiguous as ‘it’ could potentially mean yellow=ball or red=table, I’ve just gone for the most likely. So in the above we now have give bone to large=dog and look and wear blue=shirt and put yellow=ball under red=table and look_at yellow=ball.

4a) It does the same for the word ‘that’, however here it always takes the word after the next ‘and’ to be the noun. So if the player types get the ball and put it under the table and look at that., the preprocessor will produce get ball and put ball under table and look_at table.

5) It resolves things like get hat and ball to get hat and get ball by running through the sentence looking for verbs and adding the previous one if needed. This currently causes an issue if something like look at ball and pet ball is entered rather than ‘look at ball and get ball’. The preprocessor will spit out look_at ball and get pet ball.

6) Finally it handles basic plurals, if there is a blue shirt and a red shirt in the current location and the player types ‘get shirts’ then the preprocessor generates ‘get red=shirt and get blue=shirt’. If there are no shirts present then a standard ‘You cannot see any shirts here’ is generated. If the player types get shirt then this is handled in the actual command ‘get’.

7) The equals signs are then striped so the output from the preprocessor would be: give bone to large dog and look and wear blue shirt and put yellow ball under red table and look_at yellow ball

At present the interpreter simply takes each word in the preprocessor's output and tries them as a command, the first being ‘give’ here, splitting the sentence up on each ‘and’. Its here that more syntax checking is applied as it will error if the player had typed ‘look andget the red ball’ as it will not find the ‘and’ and so fail as it does not know what ‘andget’ is, similarly if they typed loik etc. Again it does not care about adjectives.

The individual commands (eg put) take at the input from the interpreter and apply any additional processing needed for the command. In the case of ‘put’ it checks for a placement word (eg under) and takes everything to the left of that as a thing and everything to the right of that as a place and then checks that they are at the current location. That is either in the room, carried by the player, worn by the player (eg put hat on table) or in/on etc a container in the room. Its here that adjectives are (finally) checked as each object has a simple name (hat, ball) and a longer one (straw hat, red ball). Adjectives have to match and be in the current order. If the dog is a ‘large friendly dog’ then ‘large dog’ and friendly dog will match but ‘friendly large dog’ won’t – even though its sort-of correct. Commands also solve the ‘get shirt’ issue when multiple shirts are present by building a list of possible matches and the asking the player which they meant.

This approach has the benefit that I don’t need to specify every colour, or kind of hat etc there has ever been. If the player types ‘get bowler hat’, but there is no bowler hat in the game they get back, ‘You cannot see that here’, rather than ‘I don’t understand bowler hat’. However it means that garbage like ‘get dsaljdla hat’ or just ‘get ajsdahjkjk’ also returns You cannot see that here, and as mentioned the processing at 5 fails if a command is mistyped. I’m not sure how to solve that right now and think I may have written myself into a hole.

Containers can have things in them, on them or under them. Pools, wells etc are containers as are NPC's. Thus they will be able to wear clothes and carry things. When an npc picks something up it will simply be added to their 'container'. Obviously having random npcs just wandering around with stuff the player needs would be very annoying so the npc will have to have a need for the item. I'm also thinking of implementing ownership, thus the player will not just be able to wonder into a shop, grab something and leave without paying for it.

I'm hoping to have simple conversations, but that is for the future, simple interactions like 'buy hat' are easy, more complex ones like 'ask the shopkeeper how much the hat costs', or say to the shopkeeper "do you think it will rain today?", are not.

Right now I am stuck on the verb substitution at 5 in the preprocessor and working out how to implement 'them', which I hope your suggestions will help me resolve.
paul doe
Posts: 1212
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Old school text adventure as a way of re-learning coding

Postby paul doe » Jun 28, 2020 20:32

olympic sleeper wrote:...
However it means that garbage like ‘get dsaljdla hat’ or just ‘get ajsdahjkjk’ also returns You cannot see that here, and as mentioned the processing at 5 fails if a command is mistyped. I’m not sure how to solve that right now and think I may have written myself into a hole.
...

You might try to 'guess' what the player wrote by using a function akin to Levenshtein's Distance:

Code: Select all

namespace Distances
  function _
    min( _
      byval a as integer, _
      byval b as integer ) _
    as integer
   
    return( iif( a < b, a, b ) )
  end function
 
  function _
    max( _
      byval a as integer, _
      byval b as integer ) _
    as integer
   
    return( iif( a > b, a, b ) )
  end function
end namespace

/'
  Computes the Levenshtein Distance between two strings.
 
  The Levenshtein Distance is a measure of how 'similar' two
  texts are, and is given by the amount of changes that need
  to be performed to one of the strings to match the other.
'/
function _
  levenshteinBetween( _
    byref s1 as const string, _
    byref s2 as const string ) _
  as integer
 
  dim as integer _
    l1 => len( s1 ), _
    l2 => len( s2 ), _
    maxDistance => Distances.max( l1, l2 ), _
    dist( _
      0 to maxDistance, _
      0 to maxDistance )
 
  for _
    i as integer => 0 to l1
   
    dist( 0, i ) => i
  next
 
  for _
    i as integer => 0 to l2
   
    dist( i, 0 ) => i
  next
 
  dim as integer _
    t, track
 
  for _
    j as integer => 1 to l1
   
    for _
      i as integer => 1 to l2
     
      if( s1[ i - 1 ] = s2[ j - 1 ] ) then
        track => 0
      else
        track => 1
      end if
     
      t => Distances.min( _
        dist( i - 1, j ) + 1, dist( i, j - 1 ) + 1 )
     
      dist( i, j ) => Distances.min( _
        t, dist( i - 1, j - 1 ) + track )
    next
  next
 
  return( dist( l2, l1 ) )
end function

var _
  verb => "look"

dim as string _
  s

? "The verb is: '" + verb + "'"
? "Write it or something similar. Type 'quit' to finish."

do while( lcase( s ) <> "quit" )
  input s
 
  ? "The Levenshtein Distance between '" + _
    s + "' and '" + verb + "' is: " & levenshteinBetween( s, verb )
loop

That way you can measure 'how close' what was written was from what it needs to be, and even autocorrect it if necessary, or suggest alternatives.
olympic sleeper
Posts: 17
Joined: Jun 07, 2020 15:47

Re: Old school text adventure as a way of re-learning coding

Postby olympic sleeper » Jun 28, 2020 22:15

Many thanks Paul, I'd never heard of this technique. I was wondering about using a soundex, this could be easier.

A quick couple of questions on your code;

Code: Select all

for _
    i as integer => 0 to l2
   
    dist( i, 0 ) => i
  next
 
  dim as integer _
    t, track


How is the i as integer handled? Its creating a variable, but is this on the stack, or main data area? In addition is its scope only for the life of the loop and is there any benefit in doing things this way, rather than a declaration at the top of the sub?

Equally any benefit in dimming a variable in code rather than at the sub start as in the dim as integer t,track? I'm guessing its scope is from the dim to the end of the sub?

Thanks again.
paul doe
Posts: 1212
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Old school text adventure as a way of re-learning coding

Postby paul doe » Jun 29, 2020 0:59

olympic sleeper wrote:...
How is the i as integer handled? Its creating a variable, but is this on the stack, or main data area? In addition is its scope only for the life of the loop and is there any benefit in doing things this way, rather than a declaration at the top of the sub?

Equivalent to:

Code: Select all

for( int i = 0; i <= l1; i++ ) {
  dist[ 0 ][ i ] = i ;
  }

Pure convenience. It should be exactly the same speed (both are on the stack), and the scope is indeed only the loop by virtue of the declaration embedded in it. If you declare it outside, it assumes the outside scope. I, for one, declare variables as close to their first use as is practically possible, and if appropriate and possible, immediately assign and/or scope them (FreeBasic supports explicit scoping via the scope/end scope construct)

olympic sleeper wrote:...
Equally any benefit in dimming a variable in code rather than at the sub start as in the dim as integer t,track? I'm guessing its scope is from the dim to the end of the sub?

Thanks again.

Indeed. All code blocks are scoped (and as you can see, FreeBasic supports namespaces too). Perhaps the most mystifying aspect from people that come from other languages (especially C/C++) is the lack of any function main or similar. Generally speaking, any code that's not within a sub/function is part of the outer namespace. If there's no namespace currently declared, then the global namespace is assumed. However, vars declared there are NOT globals, unless explicitly declared so with dim shared.

You're welcome. Quite interesting topic ;)
olympic sleeper
Posts: 17
Joined: Jun 07, 2020 15:47

Re: Old school text adventure as a way of re-learning coding

Postby olympic sleeper » Jun 30, 2020 17:46

Hello again,

Things are progressing and objects can now be placed, in, on under, beside etc others. This will allow npcs to be described as being around a location (near a particular object) rather than just 'here'.

However I'm not very happy with the currently generated text as it mainly consists of short sentences and I want something a bit more flowing. Here’s an example test location.

Code: Select all

This is the middle of a sandy beach that stretches away to the north and south. A short wooden jetty leading east ends here.

Inside a wooden crate there is a bank safe and inside this there is a cash box and a cloth bag.
Sitting on the bank safe there is a rubber duck.
Inside the cash box there is a silver penny.
Inside the cloth bag there is a glittery gem.
Sitting under a steel table is an iron bound chest.
You can also see a small silver key and a small pebble here.


Its generated using a tag list based on the object and what it holds, has on it etc. This is the list for the above.

Code: Select all

(50)P50p[50]{50}(64)<59>P64p[64]{64}(59)<60><62>P59p[59]<65>{59}(60)<61>P60p[60]{60}(62)<63>P62p[62]{62}(65)P65p[65]{65}(61)P61p[61]{61}(63)P63p[63]{63}(66)P66p[66]{66}(67)P67p[67]{67}<68>(68)P68p[68]{68}


Each object has 4 tags, so the 1st section (50)P50p[50]{50} refers to object 50, anything that has a tag <like_this> in it has something, in on or near it. 50 does not (its the silver key) so that is printed out last. Object 64 (the wooden crate) does however as its tags are (64)<59>P64p[64]{64}. tag<59> is the bank safe, if you look at the safe's tag list you'll see (59)<60><62>P59p[59]<65>{59}. There are 2 objects in the safe, 60 - the cash box - and 62 - the cloth bag, and one on it, 65 - the duck.

It might be obvious from the numbers that the tag list is build from the room via the objects db and in the order the objects are stored, hence the numbers always increase.

The display code runs through the tag list printing info for each object, this is why it is able to tie the info on the safe together - all the tags are in one place, but not the fact that the cash box, which is in the safe, contains something. Maybe a better sentence would have been;

Code: Select all

Inside a wooden crate there is a bank safe and inside this there is a cash box, containing a silver penny, and a cloth bag, containing a glittery gem.


However I am rather stuck as to how to get this out of a tag list and may need to rework it as a relationship table. This in turn would create issues where more objects are available on or in something. Think of a shop shelf which contains 4 jewellery boxes, each containing 4 jewels, the sentence would get overly long and complex very quickly.

Any suggestions?
paul doe
Posts: 1212
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Old school text adventure as a way of re-learning coding

Postby paul doe » Jul 01, 2020 1:34

olympic sleeper wrote:Any suggestions?

Yes, several =D

...
However I am rather stuck as to how to get this out of a tag list and may need to rework it as a relationship table. This in turn would create issues where more objects are available on or in something. Think of a shop shelf which contains 4 jewellery boxes, each containing 4 jewels, the sentence would get overly long and complex very quickly.
...

I've seen adventure games that differentiate between 'look' and 'examine': 'look' is more general, 'examine' is more thorough and can reveal details that 'look' doesn't. Upon 'looking' at, say, the place, it might say something like this:

Code: Select all

You're standing there. There's a bank safe near you, with a bag upon it that seems to contain something.

The command you had to issue was 'look around'. Now, the first time you entered at a location, the command was issued automatically. On subsecuent arrivals, only the name (or a really short description) was provided, and you had to issue it manually. If there was some significant change (like, say, you managed to open the bank safe somehow), it would also add it. Instead of 'there's a bank safe here', it provided something along the lines of 'an open bank safe'.
There were just significant details provided (a bank safe is bound to contain something very important, and most likely locked; the bag contains 'something'. It doesn't just tells, like a shopping list, what each one contains. You had to explicitly 'examine' things to get a more thorough description:

Code: Select all

? examine bag

The bag contains some pieces of jewelry, and a small box that rattles when you shake it.

See? It is the narrative itself that directs you towards the objects that may contain something useful, and directs attention towards the important things. Now:

Code: Select all

? look at jewelry

Lots of small pieces of jewelry. A golden ring caught your attention; it has carvings that appear to be runes of some kind.

? examine ring

Upon closer examination, you are reasonably sure that the carvings are written in Ancient Elvish. You can't read them, but perhaps someone in town might know how?

? look at box

It is a small box, about a few inches in size, wrapped in velvet. It makes a pleasant rattling sound when you shake it.

? examine box

You open the box and there's just a small peeble inside. What a waste of time.

Again, it is the narrative that 'hints' you. In this case, the ring could be very important, but the stone is clearly not. However, the 'pleasing' there (which is just flavor text) might compel the player to try and open it (perhaps triggering some optional event in the process). You can always 'get the peeble' if you want, but you'll be carrying something useless in the context of the overall adventure (or you can make it so that it can be thrown at people, just for gigs).
Also, perhaps it is the box that is actually valuable since it is a container object that is, in and of itself, useful; just not the peeble.
...
Things are progressing and objects can now be placed, in, on under, beside etc others. This will allow npcs to be described as being around a location (near a particular object) rather than just 'here'.
...

Is this important from a gameplay standpoint, or just for narrative flavor? Having important things in unexpected places (ie 'underneath a rug, which is under some random table' could be extremely annoying, since the first time you encounter something important there, you'll be compelled to search every crevice of every piece of random furniture that you might encounter. As I mentioned above, a good narrative can help direct the attention of the reader to the important details.

As for NPCs, unless they can move within a location, and this has a significant gameplay impact, then it might just be confusing. If this is not so, then it might suffice to say that a NPC is 'near a tree' than 'beside a tree'. If you do want this ability, consider implementing a narrative that helps the player sift between really important and relevant objects/NPCs from the ones that aren't.
paul doe
Posts: 1212
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Old school text adventure as a way of re-learning coding

Postby paul doe » Jul 01, 2020 2:17

...
However I'm not very happy with the currently generated text as it mainly consists of short sentences and I want something a bit more flowing. Here’s an example test location.

Code: Select all

This is the middle of a sandy beach that stretches away to the north and south. A short wooden jetty leading east ends here.

Inside a wooden crate there is a bank safe and inside this there is a cash box and a cloth bag.
Sitting on the bank safe there is a rubber duck.
Inside the cash box there is a silver penny.
Inside the cloth bag there is a glittery gem.
Sitting under a steel table is an iron bound chest.
You can also see a small silver key and a small pebble here.


This is what I meant with 'little, significant details'. Because if you do this, it appears as the PC has some kind of X-ray vision, since he can tell what is inside the safe (perhaps even before opening it!). Also, as I mentioned before, you can select the description of the objects based on their state: instead of a 'bank safe', you can say 'a closed bank safe' and, upon opening it, the description changes to 'an open bank safe'.

You can adscribe relationships in the description of the 'container' object. For example:

Code: Select all

There's a bank safe here. You can see a rubber duck sitting above it.

For more objects, this could become:

Code: Select all

There's a bank safe here. Sitting on it, there's a toy, a pencil, and a bag that might contain something useful.

Note how you may filter the description using the 'look'/'examine' scheme I mentioned before, and use it to draw attention to the relevant things. You can 'examine' the toy, and it will then be replaced with 'a rubber duck'. If you 'examine' the pencil, it becomes 'a red coloring pencil'. And if you 'examine' the bag, it reveals its contents.
...
Its generated using a tag list based on the object and what it holds, has on it etc. This is the list for the above.

Code: Select all

(50)P50p[50]{50}(64)<59>P64p[64]{64}(59)<60><62>P59p[59]<65>{59}(60)<61>P60p[60]{60}(62)<63>P62p[62]{62}(65)P65p[65]{65}(61)P61p[61]{61}(63)P63p[63]{63}(66)P66p[66]{66}(67)P67p[67]{67}<68>(68)P68p[68]{68}


Each object has 4 tags, so the 1st section (50)P50p[50]{50} refers to object 50, anything that has a tag <like_this> in it has something, in on or near it. 50 does not (its the silver key) so that is printed out last. Object 64 (the wooden crate) does however as its tags are (64)<59>P64p[64]{64}. tag<59> is the bank safe, if you look at the safe's tag list you'll see (59)<60><62>P59p[59]<65>{59}. There are 2 objects in the safe, 60 - the cash box - and 62 - the cloth bag, and one on it, 65 - the duck.

It might be obvious from the numbers that the tag list is build from the room via the objects db and in the order the objects are stored, hence the numbers always increase.
...

Just a thought: you can have a linked list of 'tags', of all number of things, for each object. Like this:

Code: Select all

"bank safe" {
  "above" : "rubber duck",
  "beside" : "broom"
  }
"wooden table" : {
  "above" : "flowers",
  "below" : "iron chest"
  }

tag( "above", "bank safe" ) // Returns "rubber duck"
tag( "below", "bank safe" ) // Returns an empty string or NULL
tag( "beside", "wooden table" ) // Ditto

That is, laying tags like this allows you to have tags of any kind associated with each object. Kind of like key-value pairs.
...
The display code runs through the tag list printing info for each object, this is why it is able to tie the info on the safe together - all the tags are in one place, but not the fact that the cash box, which is in the safe, contains something. Maybe a better sentence would have been;

Code: Select all

Inside a wooden crate there is a bank safe and inside this there is a cash box, containing a silver penny, and a cloth bag, containing a glittery gem.

...

There are several ways you can render this: by using the object's state and their relationships (use the container object and relate all the others). This is manageable if you use the 'look'/'examine' pair, but sanity should be applied: there's only so much crap you can cram above/below/inside something (the 'besides' tag can work differently, though). How specific or general you want to make this is up to you.

For the record, a friend of mine developed an adventure as homework for a class on data structures on college, and he used only linked lists and associative arrays to great effect. He implemented these sort of relationships mostly implicitly, but you were able to put something 'above'/'below' or 'inside' something else if you wanted. He used a really simple and abstract scheme: his objects were 'tiny' (say, a ring), 'small' (a bag or box), 'medium' (about the size of a child; some things of this class could not be carried, some could but only one at a time), 'large' (about the size of a person; not carriable under most circumstances), 'big' (something that could hold/withstand a person, like a coach or a table; could not be carried but some could be pushed/pulled), and 'huge' (about the size of a room; definitely not carriable). He then laid out the relationships of them also abstractly (each category could hold a certain number of the category immediately below; varied across objects but not much), and leaned on these relationships for descriptions (but 'locations' were treated differently). I can elaborate a little on this 'abstract treatment' if you want, as much as I can recall from it (I saw it many years ago)
olympic sleeper
Posts: 17
Joined: Jun 07, 2020 15:47

Re: Old school text adventure as a way of re-learning coding

Postby olympic sleeper » Jul 01, 2020 9:08

Hello Paul,

Many thanks for the suggestions. Yes the bank safe should have said it was open and for some reason did not (missed that). I like the idea of levels of detail, it will save a lot of headaches later when I come to describing shops. Rather than descibing everything on a shelf I can just say there is a shelf of <whatever> here, think I'll play around with the container code and see. I was using linked lists for the objects and rooms but got into a real mess and am now using a simple string of tags for objects. This allows me to use Basic's instr to see if something is here for example. That typed this could be a simpler case use for them.

There will be several puzzles that require the player to put things in particular places and as mentioned npcs will be able to move around a room (and the whole game), though they will be certain places at certain times. So shop keepers will be in their shops during the day etc. I'm currently working on animals and npcs having stances (sitting, standing etc) so cats etc can sleep on things, and generally get under your feet in the way that cats do. Things already have a size and weight so you cannot put the elephant on the fragile table (not that there is an elephant in the game) and I have implemented the ability to carry extra stuff simply by allowing a few different containers to have the player as their container.

I would be interested in how your friend handled abstraction, its always good to see how other people have solved problems. Something I am trying to do is allow flexability in how problems are solved, some puzzles will be fixed but I want to try and prevent the use everything with everything style of play.
paul doe
Posts: 1212
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Old school text adventure as a way of re-learning coding

Postby paul doe » Jul 01, 2020 21:57

olympic sleeper wrote:Hello Paul,

Many thanks for the suggestions. Yes the bank safe should have said it was open and for some reason did not (missed that). I like the idea of levels of detail, it will save a lot of headaches later when I come to describing shops. Rather than descibing everything on a shelf I can just say there is a shelf of <whatever> here, think I'll play around with the container code and see.

Indeed, or you can simply say that there is 'a full/half full/empty shelf'. This can also be used at the location level (see explanation below).
...
I was using linked lists for the objects and rooms but got into a real mess and am now using a simple string of tags for objects. This allows me to use Basic's instr to see if something is here for example. That typed this could be a simpler case use for them.

A friend of mine had to implement a simple adventure, as part of his course on data structures on college. He wanted something simple yet interesting and, if at all possible, very replayable. So, I suggested him the book 'The Turn of the Screw' by Henry James as a base story to work with. It has very few locations, few key characters, and an intriguing story. Turns out he absolutely loved it, and created the adventure based on that. Here is a (very simplified) diagram of the locations for the adventure. I have put it together more or less from what I can remember, having omitted some small yet important locations (and some that only appeared on certain playthroughs):
Image
Now, much as a play, the adventure worked with 3 basic objects: Actors, Props and Sets. An 'Actor' is just the player/NPC: a person that can move and interacted with Sets and Props (NPCs also did this, even when the player wasn't at the location). A 'Prop' is what you'll call an 'object': things that can be carried/examined and such. And 'Sets' were the locations: you could go there, 'look around' them, and little else. Sets could contain any other object from the model, even other Sets. You can see that the Set 'Bly' contains three other Sets (the 'West wing', the 'Hallway' and the 'East wing', each also containing other Sets).

Now, everything relied either on linked lists or associative arrays (hash tables). In the diagram, the solid arrows indicate a 'you can go there from here' relationship. Perhaps more interesting are the dashed arrows: they indicate 'what you can see from here'. If you pay attention to them, from the 'First floor', you can see the 'Back yard' either from 'Miles' room' or from the 'Governess' room', but from the 'Back yard' itself you would only see the 'First floor' (that is, the description provided would be more general, with detail added so as to alter the narrative with hints). For example, upon describing the first floor as seen from the backyard, it could add something along the lines of 'I feel like someone is staring at me. Do I see someone at my room's window?'

Most Sets were doubly linked (this provided the connections between the different sets; where you could go, from where you are), but visibility wasn't doubly linked as I explained above. So, he implemented the 'level of detail' of stuff from the links alone: note that from the 'First floor hallway' you couldn't see neither the 'Back yard' nor the 'Front yard', for example.

Now, instead of 'go north' and such (and describing Sets in terms of orientation), he just provided a colorful yet abstract rendering of the place, noting the things that could be seen from there, and suggesting where you could go, and drawing attention to important details through the narrative.

How did you moved? You simply stated where you wanted to go. In the diagram, the Governess is at the 'Fountain' on the 'Front yard': the place where the adventure starts. Mrs Grose would greet you there, tell you the names of the locations on both the low ground and the first floor; you could later ask Flora to 'give you a tour' of the mansion if you wanted, as in the novel, both to refine your knowledge of the places, and to strenghten your bond with Flora. Mrs Grose would then leave you on the 'Main hall' in Bly's 'Hallway'. And from there, you can now start exploring the whole map right away.

So, if the player was at the 'Road to the lake' and wanted to return to your room (you played as the Governess from the novel), you simply wrote 'go to your room' or 'return to your room', and the Governess will pathfind (each Set represented a node for pathfinding purposes) to her room in the first floor. The process could be thought out like this:

Code: Select all

start: { Road to lake }
finish: { Governess' room }
path: { Bly*, Lobby, Hallway*, Main hall, Stairs, First floor*, Hallway, Governess' room }

Here, the '*' denotes a higher level Set (which contains other Sets). Now, the cost for each node was used to implement the passage of time: so the trip from the road to the lake to the Governess' room took her, say, 20 minutes; but the trip from the 'Stairs' to her room would take her seconds.

As the Governess walked through the Sets, other NPCs also did their stuff: moved around and interacted with the Sets and Props, and generally working out their schedule. Upon entering one non-container location, sometimes an event was triggered that you had to deal with, or just provided some info on the current situation of events in the game. The Governess also pondered while she walked, and offered hints/thoughts on what she should do next/who to talk to/which object she might need to tackle certain task.

There's also other details in the diagram, that we can discuss further if you're interested. I'll do my best to recall how he implemented them at the time (the original was in C++).
...
I'm currently working on animals and npcs having stances (sitting, standing etc) so cats etc can sleep on things, and generally get under your feet in the way that cats do.
...

To me, this is threading too thin. If corporal language is important (because the player is, say, a detective investigating a murder case), then the effort is justified. But this is of course just my opinion, and if it can be managed, it might prove interesting (especially for conversations and other similar situations).
...
Things already have a size and weight so you cannot put the elephant on the fragile table (not that there is an elephant in the game)...

For this, my friend simply allowed a certain number of 'sizes' on or above each other, with variations. Nothing too concrete, just something along the lines of 'this bag can contain some small objects', 'this table can withstand one large object, several medium objects, and a lot of small objects'. The semantics of the italicized words (like 'several' and 'a lot') was up to the objects to implement, but in the data for the game he just specified them like that (it relied heavily on OOP constructs, so this is trivial to do).
...and I have implemented the ability to carry extra stuff simply by allowing a few different containers to have the player as their container.
...

Indeed, this can be easily implemented with linked lists. Simply change the parent of the container to something else. That's it. Everything is also carried with it, and note that the 'size' tags were made implicit in the model I explained above: you could carry a bag full of stuff that fitted in it, just not a wooden table, since it won't fit on you!.
Last edited by paul doe on Jul 02, 2020 13:46, edited 8 times in total.
paul doe
Posts: 1212
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Old school text adventure as a way of re-learning coding

Postby paul doe » Jul 01, 2020 22:26

...
Something I am trying to do is allow flexability in how problems are solved, some puzzles will be fixed but I want to try and prevent the use everything with everything style of play.

Regarding this: if you've read the novel in question, you already know that it is very self-contained: few places, few people, few things actually happen (which, notably, does NOT mean the book isn't interesting; quite on the contrary!). Now, my friend wanted something akin to a roguelike: every time you play, it should be different. The Actors, Sets and Props would be the same, with some plot-related exceptions. So, what he did was to randomize the plot of the adventure every time you played.

How this worked? In the novel, Bly's mansion is haunted by two ghosts: that of Mr Quint (the Master's helper), and Mrs Jessel (the former governess). Since the novel pivots around these two figures and the current governess, he used these three elements to change the plot: sometimes the ghosts were real, sometimes they were just the governess (and thus the player's!) delusions; sometimes Mrs Grose would be an unconditional ally, sometimes she'll be the contagonist of the story; sometimes Miles was a maniacal murderer, sometimes he was just misunderstood; and so on. This created a lot of dramatical tension between the Actors, since you never knew what everyone was up to (and that, as it turns out, included yourself!).

Thus, once the plot line was established and the main actors decided, it was just a matter of scattering the Props needed for each one, and/or create/remove locations from the game, and decide which puzzles could fit into it. There were few of those, but were really devious, and sometimes even optional: they could, for example, set some events in motion that gave you a better ending (especially the ones that involved both children). I was told that my friend's teacher liked it so much that he played it for weeks, trying to get every plot and see every ending XD

My own war story: while he was implementing it, I was asked to playtest it. So, at one point, I asked Flora to come with me to some place, and she refused. So I had this large bag with me, and actually put Flora in there (since she was stated to be 'smallish' and the bag was somewhat large). It would have turned out fine if it wasn't for Mrs Grose (which I encountered in the hallway), as she asked me what the bag contained. Since it was a large bag, filled to the brim with Flora, the Governess had to make some effort to carry it: some NPCs such as Mrs Grose and the gardener helped you carry something reasonably heavy if you asked them/they saw you carrying it and breaking a sweat in the process.
So I lied; she got suspicious, and demanded I showed her the contents of the bag. So I had to open it, and a really upset Flora came out of it, wanting to kill me; Mrs Grose thought that I was mad, so I got the boot, and a bad ending. My friend laughs even to this day, and frequently told me that 'only you could have tried something like that' =D
olympic sleeper
Posts: 17
Joined: Jun 07, 2020 15:47

Re: Old school text adventure as a way of re-learning coding

Postby olympic sleeper » Jul 02, 2020 21:17

Hi Paul,

Many thanks for the reply. I am really interested in how the game was able to handle the flora smuggling, as I suspect your friend did not write it in(?)
I don't have much idea on how to handle the emotions of npcs yet, other than if you are somewhere they don't want/expect you they get annoyed. But that could be hard coded - if player is in bad guys secret hide-out and bad guy enters.... oops. You mention a diagram, but I cannot see it - am I looking in the wrong place?

Equally pathfinding will be something of a challenge. I have simple short labels for the locations (jetty etc) so could simply do a search through those, but using a hash sounds a better option, except...

For the life of me I cannot remember anything about hastables, time to spend on youtube I guess.

Thanks again.
paul doe
Posts: 1212
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Old school text adventure as a way of re-learning coding

Postby paul doe » Jul 02, 2020 21:48

olympic sleeper wrote:Hi Paul,

Many thanks for the reply. I am really interested in how the game was able to handle the flora smuggling, as I suspect your friend did not write it in(?)
...

Indeed, he did not. I just exploited the implicit object relationships model I explained before. Of course, he got it fixed afterwards =D
...
I don't have much idea on how to handle the emotions of npcs yet, other than if you are somewhere they don't want/expect you they get annoyed. But that could be hard coded - if player is in bad guys secret hide-out and bad guy enters.... oops.

I seem to recal they had a simple sliding scale, and certain actions moved this scale. In the case of the smuggling, Flora wasn't upset because I put her in the bag and tried to kidnap her, but because I forced her to do something she refused to do. It was important to ask politely to each NPC and try to have a good relationship with everyone, especially the two children (Flora and Miles), since this was the way to get the better endings (you, as the Governess of Bly, were tasked to take care of them and their education)
...
You mention a diagram, but I cannot see it - am I looking in the wrong place?
...

Is right there, in the post. The diagram of the adventure map as I (more or less) remember.
...
Equally pathfinding will be something of a challenge. I have simple short labels for the locations (jetty etc) so could simply do a search through those, but using a hash sounds a better option, except...

Pathfinding is trivial to implement in the model he used. Path cost wasn't used as a heuristic, but as a means to rougly establish the passage of time, as I explained. Each Actor had a schedule (including you) that had to be fulfilled over the course of a day. Failure to do so repeatedly would mean you'd get fired (the only way to lose the adventure, since you couldn't die).

Return to “Projects”

Who is online

Users browsing this forum: No registered users and 3 guests