Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

Possible issues.

A topic by Dark Blue Monkey created Oct 01, 2019 Views: 496 Replies: 82
A moderator has unlisted this topic. It is not visible on the topic listing but you can still reply and make edits.
Viewing posts 1 to 18
Submitted (1 edit)

I can't seem to set integers to negative values, even if their initial value was -1:

integers {    encounter_countdown: integer "-1" ; }

: print {( "before: " + encounter_countdown )}

: set_integer var="encounter_countdown" value = "-2"; // reset

print "after:" + {( encounter_countdown )} :

results in

before: -1

after: 0

when I run this, the variable is set to zero, not -2.  I think this is because the integer has an implicit minimum of zero, although the initial creation block WILL set the integer outside its implicit range.  I couldn't find anything in the manual about integers being implicitly bounded to a minmum of zero, but I guess they are :)

I fixed it by adding an undocumented 'min' parameter, which seems to work nicely.


integers {

   encounter_countdown: integer "-1" min="-2" max="10";
}

Host

Documentation updated.

Submitted

You've added some examples, but no description. Why not just tell us whether it's signed or unsigned (it's apparently signed) and how many bytes? I'm guessing it's 2 or 4 (equivalent to Java short or int), but haven't tested it. If I don't provide these strange min and max overrides, what's the default minimum (presumably 0) and maximum values? If I do provide the min and max, what is the minimum value for min and the maximum value for max?

Host(+1)

I updated the document with regards to min and max values of an int.

It's an unsigned 32 bit integer internally.

-2,147,483,648  <= value <= 2,147,483,647

Submitted(+1)

That's signed, otherwise it couldn't be negative.

Host

Doh!

Submitted

Cheers! :^)

Submitted (1 edit)

It's not clear how

container_type="surface"

is used.  I see the manual says "Containers are not yet supported", but container_type="bag" does seem to work as expected., so perhaps manual is a little out of date? I'm guessing "surface" isn't yet supported though...

Thanks!

Host(+1)

Containers are not officially supported and the coding is incomplete.

They are partially implemented but the functions around them are not yet fully written. As such, I just document them as unsupported.

think the following works for example (I can't quite remember so don't hold me to this):

  • put xxx in yyy
  • remove yyy from xxx
  • look in xxx

But it's not yet clear or settled how is_present works for objects inside a bag you are carrying as well as there being other edge conditions. Also unclear is if you might want to add the weight of all items in a bag to the parent container weight, or if you might want containers that act magically and don't propagate the weight of their contained items.

I did some work with reachability of contained items I recall too, about if items in containers that you are holding are instantly accessible or not or if you have to take them out of the container for them to be accessible. I didn't do any work for automatically removing items from bags if they were useful.

Adventuron Classroom was intended to be the beginner level variant, and the jams have if anything, started to creep into advanced level functions. 

Generally speaking, personally speaking, I think containers are a solution in search of a problem. I am attempting to support them for reasons of compatibility and because it's "just like my opinion man", but personally speaking (again), I just find containers to be a grind.

Submitted

Containers are useful for a variety of things... I've needed to use them twice in the game I'm writing, but I've just got around them with commands. You can use them to hide objects so the player has to search, or you can use them to complete tasks "put ballot in box" kinda thing.

I guess you could limit them to being unmovable scenery objects, and the person has to look at them or open them to bring the contained objects on or in (depending on type) to having the same parent as the parent object...   If they're not a scenery object, they're a bag where the things inside aren't reachable by default.   I'm spoiled by using Quest which has a bloody complex container system with surface, bag, seethrough, opaque and all kinds of other containers :D haha.

I  guess you could make the inbuilt bag functions pretty basic, and let advanced people write scrips if they want to make them more flexible.    Having object ON a scenery object might just add (on Table) or somesuch to the description, and that's all!


 

Host(+1)

For sure containers have a lot of possible features.

The game state model of Adventuron fully supports containment (uses a hierarchical model of containment internally) and this aspect of things will improve with time. It's simply a case that the container manipulation commands are not there yet.

Submitted (1 edit)

I don't think I've ever written an adventure that doesn't have containers and supporters (or bags and surfaces in Adventuron parlance), even way back in the days of BASIC adventures. I had three pseudo-containers in 'Seeker of Magic' and the coding to implement them was quite complicated. They weren't true containers either, as I couldn't put things in them.

My next adventure (still in the design phase) will have several surfaces, one surface-like (enterable) object and several containers of different types.

For me, being able to refer to generic nouns is the number one priority, but containers are certainly high up on the list.

Submitted

You *can* make containers using the scripting language.  It's a teeny bit messy, but there's enough flexibility in the system to allow you to pretend.  The only real limitation is the VERB NOUN match construct limitation, because that prevents PUT  x INSIDE y  or PUT a ONTO b, but it looks like those are being worked on at some point....

I think the way Quest handled containers is pretty cool. It has a reachability calculation which is quite sophisticated.   :)

Host(+1)

The Adventuron parser is not just verb noun. It's just the gamejam that is verb noun.

You can use this for example :

: if (preposition_is "in" && noun2_is "lamp") {
}
Submitted

I really want to use multi-word input for non-jam games (and maybe updates to post-jam games?), but it doesn't appear to be documented. For example, how do you define the match statement when you've got verb, direct-noun, indirect-noun and probably one or two prepositions?

Host(+2)
: match "put lamp" {
   : if (preposition_is "on" && noun2_is "table" && is_carried "lamp" && is_present "table") {       // Do something     }
}

Yes, I know this is currently sub-optimal, but if you need it it's there and it does the job. 

As far as disambiguation goes, it's not fully there yet. A whole new structure of match patterns that are subject and subject context centric is planned.

For now, disambiguation works for gets, drops, examine, wear and remove commands.

Submitted

Interesting!  Thanks... 

Submitted

I don't want to harp on this, because it isn't relevant to the jam, but I am curious and raise it because it may have an influence on your future match statement format.

It looks like there is only provision for one preposition, but this doesn't account for where the preposition is located within the command. For example TURN ON LAMP and TURN LAMP ON mean the same thing, but JUMP ACROSS CHASM and JUMP CHASM ACROSS are definitiely not the same thing. The latter is rubbish. There are also situations where the presence or absence of the preposition changes the order of the direct and indirect object. The classic example is GIVE SWORD TO TROLL vs GIVE TROLL SWORD. The former is the more common syntax. You might like to say that the latter format is not supported, the direct object must precede the indirect object and they must be separated by a preposition.

This still doesn't account for the situation where you need two prepositions. I can't think of a good example off hand, but something like CHOP UP CARROT WITH KNIFE or TURN OFF MACHINE WITH LONG POLE or REACH INTO HOLE WITH STICK or WRAP UP PARCEL WITH BROWN PAPER.

Host

Don't worry, everything is in hand.

There are a number of mitigations already built into adventuron.

Pick up lamp

Put on coat

Get into boat

... And more

All language specific grammatical constructs are handled by a parser preprocessor. I'll document and allow extensions at a later stage.

Submitted(+1)

If you need a tester, you know I'm available.

Submitted (1 edit)

Here's another interesting one.  It's a type of issue I come across a lot.  The compiler tells me there's an error, but if I just rearrange things, or delete and re-edit, it works again.

HEre's the game telling me that "dark_zone" must refer to a zone.

I have a dark zone test which I took from the manual....


  is_dark : boolean_dynamic {(
  is_at "dark_zone" &&
   is_present "torch" == false
  )}


The compiler tells me that "dark_zone" must be a zone... which it is:

zones {
   dark_zone : zone {
      locations = [ road_12, road_13]

}}

Now, if I comment out the is_dark test, like this:

//  is_dark : boolean_dynamic

//{(

//  is_at "dark_zone" &&   

//is_present "torch" == false  

//)}

Then the error correctly moves to a light test which relies on it. It tells me there's no function "is_dark".  Which is correct. Now the strange thing happens.....


If I go back and UNCOMMENT the is_dark test, making no other changes other than to remove the // comment symbols, suddenly the parser is happy, and the game will compile.

This does happen very occasionally... I just have to cut a block of code to force the error away, and then pasting it back resolves the issue.

Submitted

I worked around this problem by changing the order in which the blocks are defined. The error message will give you an indication of the dependencies. If I understand your example correctly, it looks like you can define the zones{} block before the booleans{} block and the error will go away.

Host

FYI. The order of the regions makes no difference.

Submitted

It used to, unless you've fixed it. There was a thread related to this in the CaveJam.

Host

It's a bug to do with iterative parsing of code. Pressing CONTROL + S should force a full refresh of the parse and resolve these issues (until that bug (a tricky one) is fixed). You misdiagnosed that it was to do with the order or blocks.

Submitted

Yeah, I know, but reordering the blocks fixes it so that you don't need to do the Ctrl+S thing all the time. At least until you fix it further down the track.

Host(+1)

The parser has an active bug in it that I'm still looking at that will occasionally report things in the wrong order.

If nothing makes sense, press control + s to do a full refresh or the parse. I will resolve this soon, you have my word.

Submitted

Is it possible to add to the String variables section a note on "set_string"?  that command isn't documented :)

Host

Done.

Submitted

I am trying to make a loop to detect if something has changed.  Normally I'd use a previous and current variable, like so:


:set_integer var="prev_val" value="current_val"; //  <- store previous value

:gosub("randomly_change_current_val");

:if (prev_val != current_val){    // <- test for change

 : print "something changed.";

}


, but I can't seem to get the value to be evaluated as an integer, it just thinks I keep meaning the word.  I've tried lots of permutations but I can't seem to assign the value of a variable into another variable.   Is this possible?   I'm implementing a workaround for now using a loop, but I suspect I'll blow the stack if I'm not careful.  It's a nasty hack :)


make_equal : subroutine {

  :  if (prev_val < current_val) { :increment "prev_val";  :gosub "make_equal"; }

   :else_if (prev_val > current_val) {:decrement "prev_val"; :gosub "make_equal";}

}

Host(+1)

It's in the documentation. The value="" always expects an integer value, and will interpret it as an integer.

:set_integer var="prev_val" value="current_val"; 

To set based on the result of an expression (which can reference an integer variable) use:

:set_integer var="prev_val" {(current_val)}
Submitted (1 edit)

Ahh!  OK.  The first one was what I was trying, and it just kept saying invalid integer "current_val", as you'd expect.

I think my attempts to do the second one failed because of a bug elsewhere in the subroutines block, but the error always seemed to gravitate to the "}" at the end of the block...  Thanks again!  I've removed my shonky iterative setter. :)

Perhaps a concrete example of setting a variable equal to another variable in the documentation might help new users?

Host(+1)

I might just add a : copyvar; command even though it's technically redundant. Might be good for users that don't want to go into the EXPRESSION FORM rabbit hole.

Submitted (1 edit)

Wondering if this is a design choice, or a bug.....



If you create a :match command, which outputs nothing at all (or the path through the command outputs nothing),  the parser replies "You can't do that".  However, if you return from the match early, it doesn't.  e.g.


: match "guess number" {

: if (is_at("casino")){

  :if (already_guessed){

     :return;

  }

 :else { :print "You guess well!";}

}

if you are not at the casino, it prints "You can't do that" because it fails the first "if". However if you're at the casino, and you've already guessed, nothing is printed out, because it just returns.

Host (1 edit) (+1)

"You can't do that?"

.. that would be the intended behaviour, as it would fall over to the system command for not handling the input.

What is your desired behaviour?

Submitted

Oh, no request.. just wondering if it was an oversight.  I was surprised that it changed behaviour even though nothing was printed.   :)  


The game's up to 1500 lines and I'm starting to make some relatively intricate commands..

Host(+1)

: return; will stop execution, same as : done;

Nothing will be handled by the system is : return or : done are called (both of these are terminators).

You don't need to stop execution if you have an else block for the alternative.

I was thinking about adding a : not_done; command for stopping execution and not falling back to the system handlers, but it's easily avoided by using an else block.

Submitted (1 edit)

Cool.   I've restructured it all so that things fall through to the default.  

:) Thanks for the info! :)

Submitted (1 edit) (+1)

I've never understood the difference between :done; and :return; The doco implies that :return; is used to return from a subroutine, so that's how I've used it. I now use :done; everywhere else.

When using :done; always make sure you print something beforehand or you'll end up with a blank line. I've also gotten into the habit of liberal use of :done; to make sure that my specific pattern-matching routines do not fall through to the generic pattern-matching routines and ending up with two responses.

Host(+1)
  • : return will return from a subroutine but continue executing code after the : gosub ""; command that was used to call it.
  • : done will stop the code dead in its tracks (not return to the line after the gosub).

Executing any line (including a terminator) will override the system default handler for the text entered. I plan to add a default message for when done is executed without printing something first (overridable).

Submitted(+1)

Aha. That makes sense within the context of subroutines, but don't they act the same in :match{} routines?

Host

No, a return is exactly the same as a done, except in a subroutine. There is nothing to break out of a match specifically.

Submitted

 Is there an easy way to make random numbers? I don't think I found anything in the docs, the closest I got was "chance"....


In the end I made my own basic rand function  It's a bit rough and ready, but it seems to work, mostly. :)


 rand : subroutine {
     : set_integer var="rand_result" value="0";
     : if (chance(50) == true) {      

      : add var="rand_result" value="50";
      }
      : if (chance(50) == true) {
      : add var="rand_result" value="25";
      }
      : if (chance(50) == true) {
      : add var="rand_result" value="12";
      }
      : if (chance(50) == true) {    

             : add var="rand_result" value="6";
       }
      : if (chance(50) == true) {
      : add var="rand_result" value="3";
      }
      : if (chance(33)  == true) {
      : add var="rand_result" value="2";
      }
      : if (chance(33) == true) {
      : add var="rand_result" value="1";
      }
   }


:)

Host(+1)

Nice maths there. Anyway, it's a lot easier than all that.

random() will return a number between 0 and 99.

start_at = my_location
locations {
   my_location : location "Type <RANDOM<12>> to see the next random number." ;
} on_command {
   : match "random _"  {
      : print {("Random : " + random())}
   }
}
Submitted

Ah, cool. thanks...  And I see an entry in the documentation now too! Super! I'll switch out my multiples-of-11-hating shonky random alg for something smoother :)

Submitted(+1)

random(n) would also be handy, but it's not there at the moment.

Another useful command is :execute_one_at_random{}. There's an example in the doco. I used this in combination with chance(n) to get the troll in 'Seeker of Magic' to do one of ten different things at random 30% of the time.

Host(+1)

I'm going to hide this topic for now, which means you can still use this thread privately, but concerned this level of technical discussion is putting off newcomers. I'll still reply to comments in this thread - Gary / Dark Blue Monkey.

Submitted

Cool! The aim is to help you understand how we use it, and any issues we encounter, not to publically air any greivances or anything.  Thanks for keeping it "alive"...  Not come across any other issues.. Up to 2000 lines now, and I *think* I'm almost ready to start injecting the graphics :)

Host(+1)

This thread is very much alive, just kind of tucking it away, as I heard that it was "offputting". You, Gary, and myself can have a lot of tech discussions in here and they'll be isolated away from the rest of the forum.

Submitted

Wow. You're really charging ahead. I'm still in the design phase. I have the overall storyline and a number of puzzles, but I still need some fillers to tie it all together. I'm worried that it might be getting too big to finish on time, as I have a lot of other commitments this month. I've currently got 23 rooms, maybe 50 or more objects and not a single line of code. I'll write it in Inform 6 first to iron out the bugs, then port it to Adventuron.

Host

Be mindful of Adventuron's limitations please if prototyping on Inform 6 :-)

Submitted

That's what I did for 'Seeker of Magic'. I prototyped the full game in 1 day, then ported it to Adventuron in about a week. I had to do a lot of messy workarounds, of course, but the process worked well for me. I added the graphics in the second week and did some enhancements and fine tuning in the third week.

The syntax in Inform 6 is a lot simpler than Adventuron, but the workflow is more complicated. I've got it automated to a certain extent. I can double-click on an inf file to open it in TextPad for editing, along with auto-indenting and syntax colouring. I've got custom, single-key commands to compile the game, run it and open the doco in a chm file all from within TextPad.

AND I GET VERTICAL SCROLL BARS! Hooray!

Host

Hint taken. I'll see what I can do about the scrollbars. But it wont be default. I may add a command at runtime to get scrollbars on. The mouse wheel or touch screen already scrolls up and down. 

Submitted

True, but you can't see where you are in the file or what proportion of the scrollable area is shown. Call me old school, but, believe it or not, I never think to use the mouse wheel.

Host(+1)

The scroll bar issue isn't ignored. I'll almost certainly be adding some version of a scroll bar to the new renderer. I absolutely sympathise that Adventuron has a lot of work to go to get text flow working better - but on the bright side - it is in active development :-)

Host unlisted this topic
Submitted

When making a list of things, like so:


dark_zone : zone

  locations = [ a, b, c, d..... ]


The parser moans about an unexpected "\n" if you break the list up to make the list look neater.    Any chance you can  tell the parser to treat \n as whitespace when parsing lists?

  locations = [a,

                           b,

                          c]


type of thing :)

:)

Host

Yes. Just don't put the first item on the first line.

locations = [
a,
b,
c,
d
]
Submitted(+1)

OK, unusual, but ok, that'll help me lay some stuff out nicely.  thanks! :)

Submitted

Yeah, I've raised this before. The white-space handling in Adventuron is a bit weird. As Chris knows, I have trouble with Ctrl+Space working. The hinting and auto-completion shown in the videos doesn't work at all. The code suggestions don't work if there's an error and often don't work unless the cursor is on a blank line. And the syntax colouring doesn't work properly if the opening curly bracket '{' following key words is on a new line.

Host(+1)

This is related to your "order of blocks" issue from before. It's (largely) a problem with the iterative parser. The syntax highlighting issue is separate and I'll look at that too.

Submitted (1 edit)

 

uh oh... what's a 'Collision' error I wonder...   I know what it means.. it means "missing attribute name 'var'.. but I've not seen it expressed that way before..

Submitted (1 edit)

Hmmm.  Been running into some issues an object not being seen.   


I have this object:

   nice_suit: object "A fresh outfit"    examine_message="A very ornate suit of clothes. They're very dapper." wearable="true";

It's created successfully by this code (getting round the container issues):

:if (is_exists("nice_suit") == false){
         :print "You look inside the wardrobe and locate a good-looking outfit, and lay it on the bed.";
         :create "nice_suit";
         :press_any_key;
         :redescribe;
         }

And when I "LOOK" I can see "A fresh outfit" in the list of items.  But when I try to GET it, it tells me it can't see it. when I examine it, it just says "Nothing special".

HOWEVER, when I change its OBJECT NAME to "nice_outfit" rather than "nice_suit", it works!   If I change its name back to "nice_suit",  it can't see it again!  So bizarre!!!

Submitted

I had the same issue with a "summer suit" which was named  summer_clothes. It'd reply to "clothes" but not "suit".  It seemed odd that the game wouldn't react to the actual displayed name of the object, but instead only respond to the hidden behind-the-scenes object names....

Host

the display name is not the vocabulary name. The vocabulary name is based on the id of the object.

See section 8.7.2 of the documentation.

Submitted (2 edits)

Yes, I eventually found that... It seems an unusual design choice. 

Imagine if you walked into IKEA and asked for a "Poang bookshelf" because that's what's on the label, but the shop won't let you buy it unless you quote its hidden SAP stock code 40021303210312  ;-)

I'm okay with it reacting to the hidden ID, but NOT to react to the  actual text that the user's been presented is a recipe for confusion.

>Look

You see a bookshelf

> Get Bookshelf

You don't see a bookshelf.


Thanks for getting back to me, it's much appreciated :)

Host(+1)

The reason for this, and I'm choosing a strawman here, is that if I scanned to the words in the description, then it's quite difficult to ascertain what is an adjective and what is a noun, etc. 

// What is the noun we can imply from the description?
zzzz1 : object "a wonderful magical cloak of mystery";
Submitted

Ah, cheers!  Understood.  As someone who studied AI and natural language parsing for four years, I can see what you mean. In *theory* it shouldn't matter.....

 cave_win_device : object "The magical wand of Sauron"

 evil_bad_buy : scenery "The evil wizard Sauron"

I can see if someone typed "get sauron" the parser might have difficulty.   However, you could probably spend time coding a search tree prune a little on inbuilt commands like get and drop but I think the solution's already built in.    I've seen the game say "which do you mean '1.  thing_1     2. thing_2'   .  That works really well (I had two suits at one point).

I'd perhaps add a setting  to the settings block to allow the game to react to any word (other than common prepositions and articles) in the displayed title.  It'll be a rare occasion it fails, and when it does, the game already solves it perfectly :) :) :)

select_object_by_any_word_in_displayed_name  = "true"   or something similar...    I'd perhaps go so far as to suggest making it default: on, but let people switch it off if they get into a bind and want to do it the hidden_id route.. :)

Host (2 edits) (+1)

I had considered such a thing and perhaps I will add something like that in the future. It really depends on having a large dictionary of adjectives and articles. The low tech route is not so bad if you know it.

Going in the other direction, you can make it so that the id does not represent adjective or the noun by explicitly supplying noun="" and adjective="" next to the object.

Adjectives and nouns are grouped via the vocabulary section of the game defintiion file, so you could have:

spoon :  scenery "an oak tree" noun="oak";
vocabulary {
   : noun {aliases=[oak, tree, log, logs]}
}


... and it would associate the noun group with the object.

There is a risk of false positives for nouns that may have different contextual meaning, but generally speaking false positives will just result in a disambiguation list where they occur, so it's not so bad.

Anyway, it's correct that types of description could imply a noun (where there are two words and one of them is an article for example), but there are so many objects that two or more chained words in addition to an article that it's probably easier just to learn the general rule about the usage of ids than to second guess yourself if you have entered the description in the right format. the book is not closed here, but it's not low hanging fruit. Certain types of smartness are also forms of obfuscation.

Submitted

Cool,  glad it's got you thinking anyway, and thanks for the great answer, I'll have a look through the docs and see how it goes.


If I had the choice, I would just let the parser react to everything except "a" "an" "the" "of" "by" and "some" .. So if there are any objects, you can indicate them with any word in their description. 

I haven't needed to use noun aliases. I was considering using some verb aliases the other day, but couldn't see a way to do it, so did the match string manually.    


cheers, once again! You rock! :)
 

Host

Do you think you are going to make the jam deadline?

Submitted

I was just thinking it this morning.  I'm not sure.     I think I've created a bit of a monster, and debugging the logic is proving a real challenge.  I'm not sure I have enough time to work all the kinks out AND add the artwork (I'm not an artist!)...    It's about 90% there, and I just need to debug all the story pathways....

Submitted

I've done a lot of work on the logic today.  My wife's back tonight, though, and I still haven't met the "graphics" minimum requirement of the Jam.  I didn't see that I needed to have so many graphics, and the game is pretty huge.  It'll take me a good few hours to find images and edit them to the requirements.


The logic is about 98% there now, but having run through the game down one of the plot 'legs', it's not really halloweeny enough. I need to add more creepiness :(   


I don't think I'm gonna be able to do it, just not enough time unless I can catch a few hours tomorrow in the day.


N.

Host(+1)

Assuming wardrobe is an object, this absolutely should work.

is_exists "nice_suit" should test for if the suit currently exists (is anywhere in the game).

has_not_create "nice_suit" will return true if it has NEVER existed, whether or not it exists right now.

: match "examine wardrobe" {
   : if (is_present "wardrobe") {
      : if (has_not_created "nice_suit") {
         :print "You look inside the wardrobe and locate a good-looking outfit, and lay it on the bed.";
         :create "nice_suit";
         :press_any_key;
         :redescribe;       }    } }
Submitted (1 edit)

I'm heading to bed, but came across something I can't quite wrap my head around.  The first snippet works. The second one doesn't.     I'm sure I'll figure it out after some sleep.. Just thought I'd put it here in case it's something you'd be interested in.


  :match "read letter" {
     : if (is_present("visit_letter") ) {               :print "this works";
}  }

  :match "read letter" {
     : if (is_present("visit_letter") && noun1_is "visit_letter" == true) {
           :print "this doesn't";
}  }

Host(+1)

is_present references an object identifier.

noun1_is "xxxx" references a noun (therefore the noun it would be checking for is visit_letter (with the underscore).

subject1_is "visit_letter" is probably what you are looking for.

That said, you don't need noun1_is "letter" or subject1_is "visit_letter", because the match "read letter" already checks that the noun is letter.

Submitted

Ahhh, of course...  That explains it.  It was late, and I couldn't tell if it was some peculiarity of the match clause, or my own stupidity :) 

Thanks! :)

Submitted

Any idea why when I type "UNLOCK DOOR"  the default error is "It doesn't fit"?    Perhaps the default error if you don't have the key should be 

"Hmm, I don't seem to be able to."... I guess unless there's a KEY parameter on the object, in which case it should be "You don't have the right key."


I tried to override the unlock_door_with_what, but that didn't seem to provide an expected output..  It just defaulted to "IT doesn't fit".

Host(+1)

Noted, I'll see what I can do.

Submitted

A little niggle I noticed:


If you add a couple of graphics,

and then decide to remove one by deleting it, 

then the parser will sometimes throw an error if you have mentioned the OTHER graphic claiming that it doesn't exist...  If you cut/paste the whole text to force it to reparse from scratch, it realises the other graphic IS still there, and parses just fine.