Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

Capabilities in 8-bit mode

A topic by ChristopherMerriner created May 31, 2020 Views: 857 Replies: 103
Viewing posts 1 to 27

Hi

I think I'll have a go at the jam, but I've got a question about restrictions in 8-bit mode (I've got a basic familiarity with Adventuron in normal mode). These come up as an 8-bit compatibility error:

set_string

random()

any dynamic text, eg  :print {("hello"+stringname)}

Is there a way to achieve these functions in 8-bit mode? Randomness in particular would be helpful.


Thanks


Chris

Host

Hi Chris,

Unfortunately, set string is not possible in 8-bit mode, as the DAAD engine does not support string variables.

You can append to a string buffer (without a linefeed) using the : append command.

: append "You see ";
: print "a cat";

Would print out (without a linefeed in the middle):

You should be able to print out a message that has a number in it like this too (but it doesn't work for strings).

: print {("You have collected " + bells_collected + " bells.")}


As far as chance goes, I hope this helps. I've also updated the documentation with the information I've posted here.


start_at = my_location
locations {
   my_location : location "You are in a room." ;
} integers { // Only required if you are using store_random
   randomvar : integer ;
} on_tick {    : if (chance (50)) {
      // Will print hello 50 percent of the time
      : print "HELLO" ;
   }
   
   // Stores the random result in randomvar
   : store_random var = "randomvar" ;
   
   : if (randomvar < 33) {
      // Will print GOODBYE 33 percent of the time
      : print "GOODBYE" ;
   }
}

Thanks! That's very helpful.

Submitted

I'm having a bit of trouble with 8-bit compatibility errors as well - in my case it's when playing around with barriers.  I can get a 'door' barrier to work but not a 'block' or 'block_path':

   stay_upstairs : block {
   
      location               = downstairs
      message                = You need to port your game before you can go downstairs!
      block_when             = still_porting
      show_blocked_exit      = true
   }
   
   stay_upstairs : block_path {
   
      from                   = landing
      to                     = downstairs
      message                = You need to port your game before you can go downstairs!
      block_when             = still_porting
      show_blocked_exit      = true
   }
   
   stairguard : door {
   
      from              = landing
      to                = downstairs
      key               = stairguard_key
      is_open           = false
      messages {
         bang_into_door                    = You need to port your game before you can go downstairs!
      }
   }

The 'block_path' example throws up the following error:

and the 'block' example throws up the following error:

Not sure what 'lion' and 'swim' mean here?

Host

Hi Dee,

'Lion' and 'Swim' were placeholder error messages that I never got round to fleshing out.

The good news is that I've updated the error messages now, and they should also point to a line number.

'Lion' was an error message that should have informed you that the block_when (or block_when_not) conditions have to reference a regular boolean (that you can set true and false) rather than a dynamic boolean. This is down to the 8-bit game engine not supporting them. It should be easy to avoid using dynamic booleans in this context.

'Swim' is a little interesting because I don't see anything in your code that should trigger it. I've rewritten the message as this now.

Block condition invalid, supported conditions are:
      'block_when', 'block_when_not', 'block_when_carried',
      'block_when_not_carried', 'block_when_not_exists',
      'block_when_exists', 'block_when_worn', &
      'block_when_not_worn'.

but, as I say, in your included code, I don't see you not using one of those conditions.

When you see the (above) message, look at the line number of the error, and send me the block code that is failing (you can switch out any specific text that is secret of course).

Let me know how you get along.

Submitted

Thanks Chris. I'll have another go and send you a code snippet if I still can't get it to work. 

Host

Hi Dee,

Atop of the faulty error messages, there was an underlying bug in the exporter when the 'block_when' condition was used.

This is now fixed. Press the refresh button in your browser, and it should (hopefully) be working now.

Chris

Hi

Another couple of questions here, about games running in 8-bit:

- how do I print appended text without a line break? In the examples above, 

: append "You see ";
: print "a cat";

and

: print {("You have collected " + bells_collected + " bells.")}

both produce line breaks between the different elements, in +3 emulation.  Perhaps it is unavoidable?

- I see that most theme stuff is not supported. Is there any compatible way to change paper colour etc that will work in emulation?

- I think that the status bar is disabled in the graphics export version. Is that correct?


Thanks!

Host

Hi Christopher,

(1) I'll take a look at the line breaks.

(2) No changing of colour yet in the 8-bit export to DAAD, but I will look at that too.

(3) Yes, there is no headerbar in the export with graphics mode. Please do set the header for each of your locations on the understanding that you'll have the option of a title bar, or to include your location header as a prefix to your location description, in capitals, in a future version.

Thanks for your reports. Hopefully none of these issues block your game, and glad to hear you are sanity checking the output of the exporter early.

Chris

Host

Here is my test game for issue 1. I'll try to have this fixed asap.

start_at = my_location
locations {
   my_location : location "You are in a room." ;
}
integers {
   t : integer "0" ;
} on_tick {
   : increment "t" ;
}
on_command {
   : match "test _"  {
      : print {("There has been " + t + " tick(s).")}
      : append "This is " ;
      : append "the" ;
      : print " second line." ;
   }
}

Thanks. The issues aren't critical but they make the speccy output look a bit messy. But I can live with that, as long as it runs.

Host

I made an update to the exporter. The append mode should now work on the exporter (not putting the output on different lines).

Thanks Chris. I'll try it and see what happens, and let you know if anything else crops up.

And here's another one: any changing of system messages in 8-bit?

Host

There is a chance.

Thanks. Would be useful, as I'm writing it in the third person. Just to add an unnecessary complication!

Some of my code seems to go a bit screwy in 8-bit export. For example, this little time-handling  routine works fine in Adventuron, but goes wrong in DAAD (starts off at 12.00 rather than 11.45 and the minutes run over 59 in the 8-bit version). Which is a shame, as there a some timed events in my game.

--

start_at                 = my_location

locations {

   my_location : location "You are in a room." ;
   
}

on_tick {
: increment "minutes" ;
: if (minutes>59) {
: set_integer var = "minutes"  value = "0" ;
: increment "hours" ;
: if (hours==13) {
: set_integer var = "hours"  value = "1" ;
}
}
: print {(hours+" "+minutes)};

}

integers {
    hours : integer "11" ;
   minutes : integer "45" ;
   
}

---

Another thing to note is that local event handlers don't appear to translate into DAAD (so on_ticks or on_describes per room don't work, but they do work globally). 

But never mind - I'll make it work in Adventuron and then go back and do what I can to make the 8-bit version as functional as it can be (there will be a few rough edges...)

Host (2 edits)

Nice bug report.

I have fixed most of this and performed an update of Adventuron.

That said,  printing out of the time  still has linefeeds.

I'll look at that a bit later.

Submitted

Regarding local event handlers not working on export to DAAD, one of the DAAD guys mentioned that in one of the previous game jams, so I stopped using them. I now put all event handlers in the global event handlers. The only downside is that you have to do an is_at test where appropriate. The upside is that all my event handlers are now in one place, so it's easier to manage.

Question for Chris: Is this still the case? If so, it might be worth mentioning in the guide on 8-bit export, assuming it's not already in there.

Host

OK, I've made two changes to local event handlers for 8-bit.

  • First is that local event handlers will be flagged as incompatible in the 8-bit mode of the editor.
  • Second is that I've documented that they are not compatible with the 8-bit mode.

To be honest, I could make them compatible, but it' not worth the development and testing overhead at the moment when there is a very easy alternative.

Another advantage of doing events globally, I've found, is that it encourages you to create a sensible response if the player is trying to do something in the wrong place.  For example, if you're trying to open a window and you're not in the windowed room then you'll get 'you can't do that' if you've added the window opening event locally, but you can add something like 'but there's no window here!' at the end of your window-opening conditions if you do it globally. Makes the game seem a bit more thought through than getting the same default response everytime you try the wrong thing in the game. 

Submitted

I managed to get a small test minigame converted to both Spectrum Next and +3 last night, but I did run into a few more issues.

First, the conversion kept failing with the error 'Word "CONVE" already defined'.  This turned out to be because I had match statements for both 'convert code' and 'read conversion'.  I tried a few synonyms (e.g. 'translate code' and 'read translation') and kept getting the same error (e.g. 'Word "TRANS" already defined'), so I'm not really sure how to avoid this other than rephrasing things and possibly having to leave out certain matches.

Secondly, quiet="true" doesn't seem to work - is that correct?  I can live with the default message appearing alongside any custom 'get' messages I've programmed, but less so with the custom message being 'you now have pen' when I've programmed it in Adventuron as 'pen : object "a pen"'.  Is there a way to get the message to display the article?  Either 'a' or 'the' would be fine.

Finally, vocabulary aliases don't seem to work - is that correct?  Not a problem if so, just wanted to check.

Host

Thanks for the reports Dee.

1) The first issue is down to DAAD (and a lot of other 8 bit text adventure systems) only using the first 5 letters for words. I need to pre-validate this before exporting a little bit better, and document this gotcha too (which upon review I did not document).

That said, nouns and verbs should be able to overlap in the converter

For example, in excalibur, you must type:

OIL WINCH (oil is verb)

and 

GET OIL (oil is noun)

2) I'll check this out.

3) They should be working. Are they verb or noun aliases that are not working or something else?

Submitted

It was a noun alias that didn't work last night.  I'll go double-check all the others this evening.  Thanks!

Submitted

Ah, don't worry about point 3!  It turned out to be me forgetting to code a 'drop' rather than an alias not working.  Sorry!

Host

Phew!

Submitted

One more thing - it seems that the cant_get_message function doesn't work, is that right?

Host

I just mapped it. Should now work when you refresh Adventuron.

Submitted

Thanks for the fixes (and especially for the correct article in the default message - my not-so-inner proofreader is very relieved!).

Just to add that get_message also doesn't seem to be working.

Host

I think that your issues (reported thus far)  should now be resolved, including the 'cant_get' message.

Press F5 to update Adventuron.

Now here's an odd thing: all my scenery has suddenly become  incompatible in 8-bit mode! All worked fine when I left it yesterday, now this morning it won't compile due to that error. I've changed all my scenery to objects and it is now working, but that's not a sustainable solution (I don't want people walking around with my windows, doors and NPCs).

Host

Fixed. Please refresh.

Marvellous, thanks. My doors and people are untakeable  again.

Submitted (2 edits)

I'm not going to take care of the code now, but I'm under Linux, and I was able to compile correctly with DRC, but it failed at the emulator level.
drf zx next TEST.DSF 
php drb.php zx next en TEST.json TEST.DDB 
sc2daad next 001.pcx 001.NXT 96 
sc2daad next 002.pcx 002.NXT 96 
daadmaker TEST.TAP DS48IE.P3F TEST.DDB 

Are others under Linux?

Host (1 edit)

I see that the ZX Spectrum +3 / Next emulator is available for Linux.

https://github.com/chernandezba/zesarux/releases

Submitted (1 edit)

It's okay, I was able to compile ZEsarUX. I had to see if I could run the game.

Does my way of compiling with DRC seem right?

Host (1 edit)

Well done on getting it working.

The number 96 refers to the number of pixels that your images will be vertically. If your images are 256 x 80, then that number should be 80.

Submitted

I'm getting 8-bit compatibility error on Line 1, which is basically just :

1 ######################################
2 #  Game Information                  #
3 ######################################

It says Noun collision detected

Host

If you hover over the header of the editor, does it give any information on the noun collision?

Host

The first 5 letters of a word should be unique.

Submitted

WATER

Note that it was working fine a couple of days ago and I haven't changed the code since then.

Host

Hi,

I recently made the Adventuron error detection a little bit more strict, so that downstream errors (when compiling for DAAD) would not occur. In  my defense, even before you were aware of the issue, it would have been an issue when you tried to build your game using the DAAD Ready package, you just would not have been aware of it until that step. I'm trying to preview downstream errors in Adventuron itself.

Essentially, the error means you have two words that start with WATER, but DAAD can only ever see the first 5 letters of a word, so you have a collision when your nouns are truncated to 5 letters.

I don't know exactly what is colliding in your case, but it could be "WATER" and "WATERFALL".  The solution is either to use a different noun for one of the objects, or to make your words synonyms. The synonym approach is documented below, but I strongly recommend not taking it, and changing one of your object names or match nouns.

I have tried to document this issue here (new to the document):

https://adventuron.io/documentation/8bit.html#VerbNounOverlap8Bit

Submitted

Ah OK, it would be WATER and WATERFALL. I'll figure something out.

Hi

Is 'parent_of' mapped (or mappable) to DAAD? It would be so useful, and would cut down on the number of booleans I'm using to flag everything.

Host

It would be useful to know

(1) Why do you have to use booleans to flag everything right now (without parent_of)?

(2) How you would like to use it to simplify things exactly?

Right now I want to know if an  NPC is in a certain room before I enter. If it was available, I'd use 'If parent_of "NCP"=="certain room" { Print "You don't want to go in there!'. But currently I've got a flag called 'npcisinroom' that I turn on and off instead. Is there another way to tell if a certain object is in a certain room without using parent_of? Perhaps I can use conditions on a barrier - but the NPC is behind a lockable door, so there is already a barrier there, which seems to complicate things.

I've come across something similar with containers (which I know are not properly implemented yet). For example, I have an openable cupboard with something inside that I want to see if I open it. Without a ready made container facility, I flag the object with a boolean 'isincupboard'. Then when I open the cupboard I create the object (so I can see it), then either close the cupboard without taking the object (isincupboard stays true, object is destroyed when cupboard closes) or take the object before closing the cupboard (isincupboard set to false, taken object is not destroyed).

As always with these things, there may be a simpler way that I just haven't seen (I'm not an experienced coder) but that's my current workaround.

Host (1 edit)

This doesn't work (in 8-bit mode) yet, but I'll work on mapping is_within_direct {}.

start_at = lake
locations {
   forest : location "You are in a forest." ;
   lake   : location "You are by a lake" ;
}
objects {
   lamp  : object "a lamp" at = "forest" ;
}
connections {
   from, direction, to = [
      forest, south, lake, 
   ]
}
on_tick {
   : if (is_within_direct { outer -> ( "forest" ) inner -> ( "lamp" )  }) {
      : print "The lamp is in the forest (directly)." ;
   }
   : else {
      : print "The lamp is NOT in the forest (directly)." ;
   }
}
Host

This is now working in 8-bit compatibility mode.

Thanks for your efforts Chris!

Submitted

Here's another hacky way to test if the NPC is in another room. I'm pretty sure this will work in 8-bit mode.

   : match "test me" {
      : goto "new_room";
      : if (is_beside "npc") {
         : print "The npc is there.";
      }
      : else {
         : print "The npc isn't there.";
      }
      : goto "old_room";
      : done;
   }

I will try it, thanks.

Submitted

Regarding items in containers, I do this a lot. Here is some sample code from 'Seeker of Magic'. It is probably more complicated than what you need, but you can simplify it to suit your needs. You start out with a tin box. The tin box can be opened and closed. If you examine it when it is initially opened, you can see that it contains a silver key and a parchment. You can take these, providing you have room in your inventory, but you can't put them back again. (This is the only down side.) Note that there is only one boolean used to keep track of the state of the tin box. It uses has_not_created to keep track of whether or not the objects have been removed from the box.

// Tin box
   : match "examine box" {
      : if (is_present "tin_box") {
         : append "It's dark grey and dusty from the ashes. ";
         : if (is_box_open) {
            :if (has_not_created "silver_key" && has_not_created "parchment") {
               : print "It contains a silver <key<13>> and a <parchment<13>>.";
               : done;
            }
            :if (has_not_created "silver_key") {
               : print "It contains a silver <key<13>>.";
               : done;
            }
            :if (has_not_created "parchment") {
               : print "It contains a <parchment<13>>.";
               : done;
            }
            : print "It's empty.";
            : done;
         }
         : else {
            : print "It's closed.";
            : done;
         }
      }
   }
   : match "open box" {
      : if (is_present "tin_box" && is_box_open) {
         : print "It's already open.";
         : done;
      }
      : set_true "is_box_open";
      :if (has_not_created "silver_key" && has_not_created "parchment") {
         : print "It contains a silver <key<13>> and a <parchment<13>>.";
         : done;
      }
      :if (has_not_created "silver_key") {
         : print "It contains a silver <key<13>>.";
         : done;
      }
      :if (has_not_created "parchment") {
         : print "It contains a <parchment<13>>.";
         : done;
      }
      : print "It's empty.";
      : done;
   }
   : match "close box" {
      : if (is_present "tin_box" && !is_box_open) {
         : print "It's already closed.";
         : done;
      }
      : set_false "is_box_open";
      : print "It's now closed.";
      : done;
   }
// Key
   : match "examine key" {
      : if (is_present "silver_key" || (is_present "tin_box" && is_box_open && has_not_created "silver_key")) {
         : print "It looks like it's made of silver.";
         : done;
      }
   }
   : match "get key" {
      : if (is_present "tin_box" && is_box_open && has_not_created "silver_key") {
         : if (items_carried() < item_limit()) {
               : pocket "silver_key";
               : print "You take the <key<13>> out of the tin <box<13>>.";
               : done;
         }
         : else {
            : print "You can't carry any more.";
            : done;
         }
      }
   }
// Parchment
   : match "examine parchment" {
      : if (is_present "parchment" || (is_present "tin_box" && is_box_open && has_not_created "parchment")) {
         : print "It's dry and brittle and slightly singed around the edges.";
         : done;
      }
   }
   : match "get parchment" {
      : if (is_present "tin_box" && is_box_open && has_not_created "parchment") {
         : if (items_carried() < item_limit()) {
               : pocket "parchment";
               : print "You take the <parchment<13>> out of the tin <box<13>>.";
               : done;
         }
         : else {
            : print "You can't carry any more.";
            : done;
         }
      }
   }
(+1)

Thanks Garry - this is a clever solution. I'll file it away for future reference. I have a  boolean tagged to each object, which works well enough but could get unwieldy if I have a box full of stuff.  In my version,  I could put the objects back in the containers if I wanted to code that in as well. But as it is, it's not necessary for the game play, so I've got a 'there's no point in doing that!' type message instead, which is familiar adventure game shorthand for 'you could have done that, if the author had bothered to code it...'

Thanks! In the meantime, I'll keep flipping those Booleans.

I've tried exporting my game to DAAD and I'm getting  a few errors.

First this:

Compiling [Front End]...
DAAD Reborn Compiler Frontend 0.18 (C) Uto 2018-2020
Reading TEST.DSF
Checking Syntax...
#EXTERN' "MLV_NEXT.BIN" processed.
1754:10:TEST.DSF: Noun not found in vocabulary: "1".
"Something went wrong, please check errors"
Press any key to continue . . .
Terminate batch job (Y/N)?

...so I defined integers below 10 in the vocab. And then this:

Compiling [Front End]...
DAAD Reborn Compiler Frontend 0.18 (C) Uto 2018-2020
Reading TEST.DSF
Checking Syntax...
#EXTERN' "MLV_NEXT.BIN" processed.
Generating TEST.json [Classic mode OFF]
TEST.json generated.

---------------------------------------------------------
Compiling [Back End]...
DAAD Reborn Compiler Backend 0.18 (C) Uto 2018-2020
Error: Invalid json file:  - Syntax error, malformed JSON.
"Something went wrong, please check errors"

Is there anything immediately obvious that you can think of that might be causing these? 

Host

Are you using "1" as a noun in the game? There should not be a problem with that in theory.

I'm not time rich at the moment, but if you can send me a cut-down version of your adventuron source that can replicate this downstream error, then I'll happily take a look for you.

info@adventuron.io

I've got a numerical combination for a safe, so that might be the problem. I've got a match function for dialing the combination that doesn't use is_int (not compatible in 8-bit) but still works. I'll fiddle around with it a bit more and see if I can get it to work.

Submitted

I know choose/add_choice and ask_int aren't compatible with 8-bit mode, but is there any possible workaround for getting the player to select an option?

Submitted

Can't you get around it with text input? For example, if presented with three different buttons, you just say PRESS RED, YELLOW or GREEN BUTTON (or PRESS RED, YELLOW or GREEN). Or a game show host asks you which door you want to select and you say SELECT 1, 2 or 3 (perhaps with synonyms for ONE, TWO, THREE and FIRST, SECOND, THIRD).

Host

Garry's suggestion is good.

I documented a different workaround here, which will let you dynamically include (or exclude) choices based on the state of the game. It's a bit wordy, but it should work fine in 8-bit compatibility mode.

start_at = hut
locations {
   hut : location "You are in a hut.";
   
   // The location is just for displaying choices.
   // As these are all dynamic, there is no location text.
   choice_dialog_1 : location "";
}
objects {
   // The breadcrumb is used as a marker for the location to return to (at the end of choices)
   breadcrumb : object "a breadcrumb" ;
   man        : scenery "a man" start_at = "hut" ;
}
integers {
   // Used to track how many choices are available.
   // Always add a failsafe choice to return to the breadcrumb
   num_choices_displayed : integer "0" ;
}
booleans {
   
   display_choice_rhino : boolean "true" ;
   display_choice_aardvark : boolean "true" ;
   display_choice_sorcerer : boolean "true" ;
   
}
on_describe {
   // To let the 'player' how to select choices.
   // You can trigger choices however you like in your
   // own game.
   
   : if (is_present "man") {
      : print "Type CHOICES says the man." ;
   }
   
   // If at the choice dummy location 'choice_dialog_1'
   // Then show the context of the question, and the choices
   // available.
   
   : if (is_at "choice_dialog_1") {
      : set_integer var = "num_choices_displayed" value = "0" ;
      
      : print "The man says ... make your choices ...";
      
      : if (display_choice_rhino) {
          : increment "num_choices_displayed";
          : print "Type 'rhino' to do something.";
      }
      : if (display_choice_aardvark) {
         : increment "num_choices_displayed";
         : print "Type 'aardvark' to do something.";
      }
      : if (display_choice_sorcerer) {
          : increment "num_choices_displayed";
          : print "Type 'sorcerer' to do something.";
      }
      
      // Always add a failsafe choice to return to the breadcrumb
      : if (num_choices_displayed == 0) {
         : print "No more choices remain. Type 'return' to leave the choice selection dialog.";
      }
   }
}
on_command {
   
   : match "choices _"  {
      // Creates the breadcrumb object in the current location.
      : create "breadcrumb" ;
      : goto "choice_dialog_1" ;
      : redescribe;
      
   }
   
   // NOTE: You can't put an 'if' directly underneath 'on_command'
   //       in 8-bit compatibility mode.
   
   // All choices need to be placed at top level of the on_command block
   // This isn't perfect, but it is all that is available for now.
   // I'll work on local on_command handlers at location level which should tidy this up a bit.
   
   : match "rhino _"  {
      // Always have to duplicate the logic
      // from the choice display section (on_describe)
      : if (is_at "choice_dialog_1" && display_choice_rhino) {
         : set_false "display_choice_rhino" ; // Removes the choice dynamically
         : print "You chose rhino (rhino will no longer be a choice)." ;
         : press_any_key ;
         : gosub "return_to_breadcrumb" ;
      }
   }
   
   : match "aardvark _"  {
      : if (is_at "choice_dialog_1" && display_choice_aardvark) {
         : set_false "display_choice_aardvark" ; // Removes the choice dynamically
         : print "You chose aardvark (aardvark will no longer be a choice).";
         : press_any_key ;
         : gosub "return_to_breadcrumb" ;
      }
   }
   : match "sorcerer _"  {
      : if (is_at "choice_dialog_1" && display_choice_sorcerer) {
         : set_false "display_choice_sorcerer" ; // Removes the choice dynamically
         : print "You chose sorcerer (sorcerer will no longer be a choice)." ;
         : press_any_key ;
         : gosub "return_to_breadcrumb" ;
      }
   }
   
   : match "return _"  {
      // Always add a failsafe choice to return to the breadcrumb
      : if (is_at "choice_dialog_1" && num_choices_displayed == 0) {
         : gosub "return_to_breadcrumb";
      }
   }
}
subroutines {
   return_to_breadcrumb : subroutine {
      : goto "breadcrumb" ;
      : destroy "breadcrumb" ;
      : redescribe;
   }
}
Submitted(+1)

Thanks guys - I'll try those suggestions out. 

Submitted

Should I use n_char_parser = 5? If not, I can have conflicts in Advanturon with some plurals: armors and armor, for example.

Host

Don't use n char parser 5. It's really only meant for when every word in your vocab is 5 chars or less, and not meant for 8-bit export mode.

Can you save the source of your file, and show me the issue you are having? info@adventuron.io

Or try this:

game_settings {
   auto_pluralize_nouns = false
}
Submitted (1 edit)

I have the same Set instruction twice in DAAD /PRO 11:
Adventuron (redescribe = auto_beta):

: match "get ladder"  {
   : if (is_beside "ladder") {
      : if (is_at "treestand") {
         : set_graphic graphic = "treestand2"  target = "treestand" ;         
      }
      : get ;
   }
}

DAAD:

/PRO 5
> GET    LADDE PRESENT ladder
              NOTCARR ladder
              CLEAR is_done_
              PROCESS 11
              NOTZERO is_done_
              DONE
/PRO 11
> _      _    AT treestand
              SET has_done_something_
              SET has_done_something_
              LET 18 255 LET 19 10
              AUTOG
Submitted (2 edits)

The code for DAAD is wrong: AUTOG should not depend on the test: AT treestand; It is right if I set Get before the test:

: match "get ladder"  {
   : if (is_beside "ladder") {
      : get ;     
      : if (is_at "treestand") {
         : set_graphic graphic = "treestand2"  target = "treestand" ;         
      }
   }
}

/PRO 11
> _      _    SET has_done_something_
              AUTOG > _      _    AT treestand
              LET 18 255 LET 19 10
Host

The second set of generarated code looks good to me. The get is not dependent on at treestand.

You are right about the redundant sets, I'll take a look.

Submitted (1 edit)

Another example with two equivalent Set, and an error because Get does not depend on the test: AT treestand.

redescribe = auto_beta
: match "get ladder"  {
   : if (is_at "treestand" && is_beside "ladder") {
      : set_graphic graphic = "treestand2"  target = "treestand" ;
   }
   : get "ladder" ;
   : done ;
}
> GET    LADDE AT treestand
              PRESENT ladder
              NOTCARR ladder
              SET has_done_something_
              SET has_done_something_
              LET 18 255 LET 19 10
              GET ladder
              DONE
Host

This is certainly a bug, looking at it now.

Host

Please can you try the new version.

Submitted

I can't test with DRC and the emulator under Linux, but it looks good.

Submitted

There is a problem with DAAD export without graphics. The message 64 is missing and DRC is complaining:

DAAD:

/62 "."
/63 "Over Here!"
/65 "Your score is "
/66 " out of a possible "

DAAD(+Graphics):

/62 "."
/63 "You see nothing special.\n\n"
/64 "Your score is "
/65 " out of a possible "
Submitted

This is not supported in 8-bit compatible mode; it's 'turn' that seems to be a problem:

vocabulary {
   : verb { aliases = [move, pull] }
   : verb { aliases = [move, push] }
   : verb { aliases = [move, turn] }    
}
Submitted

I've got a feeling that PULL and PUSH are built-in verbs, but I'm not certain. At any rate, you only need one statement:

: verb / aliases = [move, pull, push, turn]
Submitted(+1)

Thank you; It works like this.

Submitted

Is there a way to test for the state of a door?

I want to flip a boolean when the door state changes, but on_open/on_close/on_lock/on_unlock don't work in 8-bit mode.  I can't code it manually with 'unlock door' etc. because the match doesn't necessarily result in the door state changing.

The only workaround I can see is to code the whole thing manually with door_operation - just wondering if there's an easier way of doing things.

Host (1 edit)

You're right, there is currently no way to test for the state of a door., and those event handlers (on door) are not currently mapped in 8-bit mode.

I'll see what I can do at my end, but it'll probably be a week before I can look at this.

You can work around this by manually coding a door, or possibly by using the door_operation function (if it's possible).

This should get you started if you want to take this path (remove the door from the barrier section).

// Boolean section
booleans {
   is_door_locked : boolean "true";
   is_door_open   : boolean "false";
}
// In on_command{} section
: match "open door" {
   // Code your open door logic here
}
: match "close door" {
   // Code your close door logic here
}
: match "unlock door" {
   // Code your unlock door logic here
}
: match "lock door" {
     // Code your lock door logic here
}
// Direction that door is blocking
: match "n _" {
   : if (is_at "your_location_id" && is_door_open == false) {
     // This manually blocks (without using the blocks {} section
     : print "The door is in your way";
     : done;
   }
}
Submitted

Thanks - I'll have a go with this.

Submitted

That sounds like a question for Chris. I can't find any way of testing the state of doors. I've done lots of doors, but I always do it manually so that I'm fully in control and my doors are bidirectional. It's pretty easy. Do you want me to post some sample code? If so, is your door lockable? (It sounds like it is.)

Submitted

It is lockable, yes.  I'm going to try the workaround Chris posted above, but it might be useful to have some alternatives - thanks!

Submitted

My solution follows the same outline that Chris posted. Give me a moment and I'll dig one out for you.

Submitted

I just did this in a text editor, so I hope I got all the brackets and indents right. It is untested.

I have assumed that the door is between room01 and room02. You go north from room01 to room02 and south from room02 to room01. The door is initially locked and closed and is unlocked using the key. The door appears in both rooms and can be opened, closed, locked and unlocked from both sides. Room and object definitions are not shown. You can optionally use a barrier if you want the exits to appear and disappear depending on whether or not the door is open.

Enjoy.

booleans {
   is_door_locked : boolean "true";
   is_door_open : boolean "false";
}
on_command {
   : if (is_present "door") {
      : match "examine door" {
         : append "It's a large door that's currently ";
         : if (is_door_open) {
            : print "open.";
            : done;
         }
         : print "closed.";
         : done;
      }
      : match "open door" {
         : if (is_door_open) {
            : print "It's already open.";
            : done;
         }
         : if (is_door_locked) {
            : print "It's locked.";
            : done;
         }
         : set_true "is_door_open";
         : print "You open the door.";
         : done;
      }
      : match "close door" {
         : if (!is_door_open) {
            : print "It's already closed.";
            : done;
         }
         : set_false "is_door_open";
         : print "You close the door.";
         : done;
      }
      : match "unlock door" {
         : if (!is_door_locked) {
            : print "It's already unlocked.";
            : done;
         }
         : if (is_carried "key") {
            : set_false "is_door_locked";
            : print "You unlock the door with the key.";
            : done;
         }
         : print "You'll need a key to unlock the door.";
         : done;
      }
      : match "lock door" {
         : if (is_door_locked) {
            : print "It's already locked.";
            : done;
         }
         : if (is_door_open) {
            : print "You'll have to close it first.";
            : done;
         }
         : if (is_carried "key") {
            : set_true "is_door_locked";
            : print "You lock the door with the key.";
            : done;
         }
         : print "You'll need a key to lock the door.";
         : done;
      }
      : match "n _" {
         : if (is_at "room01" && !is_door_open) {
            : print "You'll have to open the door first.";
            : done;
         }
      }
      : match "s _" {
         : if (is_at "room02" && !is_door_open) {
            : print "You'll have to open the door first.";
            : done;
         }
      }
   }
}
on_pre_describe {
   : if (is_just_entered() && (is_at "room01" || is_at "room02")) {
      : create "door";
   }
}
Submitted

Fab!  Thanks Garry.

Host (1 edit)

 on_pre_describe {} doesn't work in 8-bit compatibility mode, but you won't need it if you describe the door in the location text itself or somewhere else.

Submitted

You can use an "open/unlocked door" object and a "closed/locked door" object and reuse them in other parts of the game.

When a door is unlocked, I prefer it to be opened automatically as well.

Submitted

I don't. I think implicit opening of a door is a really bad thing and it should be banned. Unlocking and opening are two entirely different actions and may affect puzzles. It's really no big deal for the player anyway. You just say UNLOCK DOOR. OPEN IT.

Even worse is when people expect a door to automatically be unlocked when it is opened. For example, imagine that you have to pick the lock with a paperclip in order to unlock the door. The player hasn't tweaked to that, but they happen to have the paperclip with them. They say OPEN DOOR (not even knowing that it was locked in the first place) and the door magically opens, thus solving a puzzle and the player doesn't even know that it was a puzzle or that they've just solved it.

And should the opposite be true? When you close a door, should it automatically be locked? There's no point having locked doors at all if you're going to automate everything.

Submitted (1 edit)

>Unlock door
You unlock the door with the key and open it.

Submitted

You unlock the door and open it and go inside and find a grue and draw your sword and slay it and take the treasure and...

Get the idea? The game is doing things that you didn't ask it to do. You might as well just say SOLVE GAME.

Submitted

Artificial intelligence applications: autonomous game.

It's time to wake up Garry! It's 2020.

I have to say I'm with auraes on this one. Nine times out of ten the puzzle is unlocking the door and the opening of it is irrelevant to the gameplay; opening it implicitly avoids an extra hassle when player, feeling triumphant, wants to immediately proceed north into the sewage works (for example) without having to be told ' there is a door in the way'. With the paperclip example, you'd surely still have the player 'unlock door' or 'pick lock' to open the door, not automatically open it when is_carried "paperclip", wouldn't you?

Submitted

That was just the first example that came to mind. I've had no end of trouble with implicit actions doing stupid things (as a player), so I avoid them like the plague (as an author). I've had quite a few games where I've come to write up the solution and the solution in my notes didn't work because the game had done some sort of implicit action that I was completely oblivious of and I had to do endless trial and error to try and work out what it was.

No thank you. No unexpected surprises in my games. If you want to see if the door is locked, try OPEN DOOR. If it's locked it will tell you. It won't magically select the long brass key from the key ring stored in your backpack and unlock and open the door on your behalf. If you want to unlock the door, you have to say so and you have to say how you want to unlock it. And if you want to open the door, you have to explicitly say OPEN DOOR.

Submitted

I agree with you on this one, Garry, although maybe that's because I like to make my doors complicated!  I always seem to end up having reams of stuff that happens on_unlock and on_open...

Unlock door: you unlock the door with the key.  Open door: as the door creaks open, a growl emits from the room inside, and in the dull light you spot a wild dog chained to the wall, looking at you balefully...

Can't remember the last time I coded a door that was just a door :)

I might argue the opposite in some cases. It might be more of an unexpected surprise to be required explicitly to carry out an implicit action.

I'd never implicitly unlock a door (as I say, that's usually a puzzle to solve in itself) but I might implicitly open it. And in real life, I'd have to apply some minimal thought to unlocking a door, but not to opening it. Similarly, if I had a cup of coffee in front of me on the table at which I was sitting, in a game, then I'd be tempted to allow 'drink coffee' to do the work of grasping the cup and bringing it into proximity with my lips without reporting 'you don't have the coffee' and then expecting the player to get the coffee before they drink it. As in reality, so in a game: automatic actions that would distract , if made explicit, from the more important things that a person needs to consciously attend to can remain safely implicit. In many, if not all, cases.

Submitted

All games have an inventory limit. (You probably don't like that either.) The implicit TAKE prior to DRINK causes all sorts of inventory management issues. If your inventory is full prior to DRINK COFFEE, do you:

  • Tell the player they can't carry any more (and cause them some confusion because they weren't trying to pick something up).
  • Allow them to exceed the inventory limit in order to carry out the action (and possibly break future inventory limit checking in the process).
  • Drop a random object in order to allow them to pick up the coffee (and break the precious Ming vase in the process).
  • Allow them to drink the coffee and put down the empty cup so as not to exceed the inventory limit (but do they put down the empty cup when they were already carrying the coffee).

There are all sorts of other scenarios, but this gives you a small sample of some of the problems involved when you allow implicit actions. This all requires a hell of a lot of extra coding that is not needed when you don't allow implicit actions and is far less confusing to the player in the long run.

Inform has the concept of a "sack" object. (This idea is taken from Zork.) If you try to pick something up when your inventory is full and you are carrying the sack object and it is not full, then it picks an item at random and puts it in the sack, then picks up the coffee so that you can drink it.

I remember a game where you couldn't pick up the coffee because it was too hot. What do you do then?

In my last game (a future entry in IFComp 2020), I wanted the player to be able to PUT WATER IN BUCKET. The library would not allow anything to be put in a container unless it was held first, so it would implicitly try to get the water (this is similar to your implicitly getting the coffee), but you can't get the water. That's why you need the bucket in the first place. So it was impossible to put the water in the bucket without modifying the standard grammar.

Do you see what I mean yet?

My point is that you have to be consistent. If you're going to allow an implicit action in one particular scenario, then you have to allow it in all sensible scenarios. This leads to a hell of a lot of extra coding and possible confusion to the player. In every game that I've ever played that allows implicit actions, I've actually ended up taking more moves than had there not been any implicit actions. This is because I am very thorough when playing a game in order to write up the solution for CASA.

In the old days of two-word input, the indirect object was always implied. This was unavoidable because of the limitations of a two-word parser. But implicit actions are quite different to implicit objects. Think it through carefully before you decide to go the implicit actions route.

Submitted

Aren't some of the problems you're talking about also exist with "ALL" ?

Submitted

Generally not, because ALL is only allowed with a limited number of verbs, typically GET and DROP. (I don't think I've ever seen a game that allows EXAMINE ALL, except maybe some Infocom games.) You can only get something that is not carried and drop something that is carried. Inform 6 has an "entry point routine" called ChooseObjects that you can customise to define what's acceptable for an ALL operation for a particular verb. For example, when you GET ALL, you don't want it trying to get scenery and giving a long list of rejection messages. Adventuron doesn't have anything equivalent to ChooseObjects, but the default GET ALL and DROP ALL now work fairly well after the feedback received in earlier game jams.

In a recent discussion on intfiction.org, I spoke about the complications of WEAR ALL when you have layered clothing and presented a little test program to illustrate the point. In this scenario, it was actually more efficient to wear the individual items of clothing than to WEAR ALL and have half the items give rejection messages. For example, if WEAR ALL resulted in the shirt being worn before the singlet, when it got to the singlet it would say, "You'll have to take the shirt off first." So you then had to do an inventory to see what you were wearing, remove a number of items and wear all the items again individually.

Submitted

In case you haven't worked it out by now, I love writing about game theory as it applies to adventures. This comes from 40 years' experience playing, writing and writing about adventures. So please forgive me if you find this boring, but it's meant to be thought provoking.

Here's some more thoughts on the problems of an implicit OPEN after UNLOCK.

How many turns does it take to do an implicit open after unlock? One or two? With non-implicit unlock and open, it takes two turns, as there are two explicit commands resulting in two explicit actions and these actions can be separated in time. A common sequence would be UNLOCK DOOR WITH KEY. DROP KEY. OPEN DOOR. That's clearly 3 turns. With implicit actions, it should be the same, so you have to manually increment turns after the initial UNLOCK.

What happens if you attempt to UNLOCK DOOR when it's already unlocked? You can't unlock it again. Do you just give an error response or do you open the door, even though that's not what the player asked for?

From the above, it should be pretty obvious that you can't just make UNLOCK and OPEN synonyms, otherwise you would be able to say UNLOCK DOOR when it wasn't locked in the first place.

Earlier, I said that you must be consistent so as not to surprise the player with seemingly nonsensical responses. Therefore, if you have a locked box or a locked chest, these should also be automatically opened after an UNLOCK command if you have an implicit OPEN. But what happens if you don't want them automatically opened? Consider Pandora's box. You need to unlock it, but you don't want to open it or you'll unveil the horrors within. I recall a game like this where you had to unlock the box, but not open it and give the unlocked box to a monster. When the monster opened the box, the monster was killed when the contents of the box escaped. With your automatic OPEN after UNLOCK, it would be impossible to complete the game, because unlocking the box also opens it and the contents kill you.

Chris mentioned 'The Witch's Apprentice'. It contains a puzzle near the beginning where you need to unlock the front door, but you don't do it using a key. Are you suggesting that the unlock operation should also open the door, even when there's no one in the vicinity to do it? I don't think so.

What happens with LOCK and CLOSE? If you close the door, is it automatically locked? You were suggesting that it should be because that's what you would "naturally" do in the real world. But what if you've dropped the key somewhere? And what happens if you try to LOCK the door when it's open? You can't do this in real life. Are you suggesting that you should automatically close the door first, then lock it? Then we're back to the same problem the key is missing.

So, carefully consider ALL the consequences of using implicit OPEN after an UNLOCK.

Thanks Garry, for this disquisition. There is clearly a lot to think about here, but for the sake of expediency I must rely on intuition and be damned. I'm really looking forward to seeing how everyone's differing approaches translate into their games!

Host (1 edit)

I certainly don't mind the "busywork" of unlocking and opening a door but I don't think that auto-wiring it is evil either unless there is a serious innovation in unlocking the door with something that isn't a carried key.

I do have a fondness for attaching events to open and close (as in spooky castle).  Spooky Adventure featured this kind of tactility, and so did Witch's Apprentice.

Submitted

Is it at all possible to have proper nouns capitalised in the item list for a room (in the converted Spectrum version)?  They're capitalised in the original code but switch to lower case on the Spectrum.

Host

Looking at this.

Submitted

And another issue I'm having: when I type 'I' in the Spectrum version, I get the following double response:

You are carrying x, y and z.

You are carrying nothing of interest.

Host

Looking at this too (I think I didn't print out the wearing message properly).

Host

Should have some kind of "fix" to test in the beta version :

https://adventuron.io/beta

Submitted

Yup, all fixed, thank you!