Most common 'mistakes' and 'crutches' for those starting out with inform 7?

Some great contributions here. I always find it really helpful to see where others have stumbled and try and learn from that.

My biggest challenge at the moment in creating my combat system is dealing with many variables all having to harmoniously coexist without breaking something.

My standard action variables are approaching a point where soon it will have a dozen if statements, a dozen let statements, numerous now statements etc. So far I have successfully been able to make this work but as I try to add more adventurous features it is definitely proving to be quite challenging.

I am also tryjng to write it as if it could eventually be adapted to an extension, not because I think it would make a good extension for other people but moreso that most of the code is written in a way where it doesn’t rely on rules that are specific and instead very general and broad. While at first this was a challenge to get my head around, it has made adding new weapons, armour, items and so on a breeze. Simply specify the kind and add the stats and it just works.

I am yet to delve deeply into tables, lists and list creation and truth states.

Numbers and variables are in themselves a bit of a challenge and when hundreds of lines of code are almost all describing an action and variables, it can be quite tricky to keep everything from breaking.

I’m very excited to eventually have my ‘personal extension’ if you will, complete so I can begin applying it to either a rogue like format or (ideally) a crafted narrative which uses the extension in an engaging way.

Every night I have been setting a goal for a feature or two to implement and almost always I get them done, so while progress at times feels a bit slow, it’s progress nonetheless.

1 Like

It took me some time to grok that you can write rules (mostly) wherever in the source text you want and I7 sorts them out at compile-time, with the only caveat that more-specific rules take precedence over less-specific rules, and you can specify vaguely where in the rule stack they go with “first” and “last”.

I think it’s last-in, first out, so if you write in this order:

First check jumping: say "Foo!" instead.
First check jumping: say "Bar!" instead.

…if I remember correctly, the compiler reads the “Foo” rule which is written to the top of the check jumping rulebook, then the “Bar” rule is similarly written to the top before that. The Bar rule would run first and fail the rulebook, so you’d only get the Bar rule to fire.

3 Likes

After I thought I had a decent knowledge of I7, my biggest comeuppance was some basics about texts.

In 6G60, “text” and “indexed text” were separate types one had to convert between. Texts were strings that included adaptive text; indexed texts were plain text strings. In 6L02, they become one thing from a language user point of view while still having separate representations under the hood. In 6M62 and v10, one can still say “indexed text” but it means the same thing as “text”.

Texts containing adaptive texts are Schroedinger’s Boxes: their values are undefined until you open the box and the wave function collapses into some particular string. And that happens every time you open the box. So if you have:

pack-animal is a text that varies.
pack-animal is "[one of]mule[or]llama[cycling]".

when play begins:
  say "'[pack-animal]' is [number of characters in pack-animal] characters long.";

you get "mule" is 5 characters long.

To pin it down, you need the substituted form of <text>. The docs also refer to this as “expanding”, though that’s not the name of the phrase. I’ll use that term in the following.

If you try to compare two texts-with-adaptive-text with “is”, the result is almost always false even if their expanded forms are the same. If you have if t is empty when t is a text-with-adaptive-text, the result is always false, even when the expanded text is empty. Comparing two plain texts with “is” works. Comparing a text-with-adaptive-text to a plain text works (the text-with-adaptive-text gets expanded). Thus the easiest way to test whether a text-with-adaptive-text is empty is if t is "".

So: don’t compare strings variables with “is”. If one side or the other is a text literal (“literal” is programmer-ese for not a variable, but some particular explicit value in your code, a quoted string in this case), e.g., if t is "cat" you’re okay. Otherwise, stick to if t exactly matches the text w.

To tell whether something is a plain text or a text-with-adaptive-text, you can use the substituted or unsubstituted adjectives.

To say phrases can have any code, including code that has nothing to do with output. So there’s a sometimes clever trick where you sneak side-effects into saying things. But keep in mind that these side effects occur every time the text is expanded, not just when it’s output. So if you have:

g is initially 0.

To say gg: increment g; say g; say line break;

when play begins:
say gg;
if "[gg]" is "1", say "gg = 1.";
let q be the substituted form of "[gg]";
say gg;

the output is:

1
4
5 Likes

The rather arcane reason for this (as I suspect you know) is that a ‘text with adaptive text’ is conceptually a function returning a text and Inform considers that two different functions are not the same thing, the results returned by said functions being irrelevant to this non-equivalence.

As a simple conceptual example, 2 squared is not the same thing as 2+2, even though both return 4,or x multiplied by x is not the same as x squared, even though both return x squared.

In Inform this idea is taken one step further, in that Function A is never considered the same thing as Function B, even if both Function A and Function B contain identical code to return 2 squared. This is unsurprising, as it would be difficult for Inform to discern equivalent functionality in function code, and it doesn’t try to.

4 Likes

Similarly, following on from the concept of ‘text with substitution’ as ‘function returning text’, ‘if some-text-with-substitution is empty …’ is asking whether the given function is non-existent, hence it is always false.

3 Likes

There’s an additional ‘gotcha’ here in that texts can be expanded unexpectedly by the internal workings of Inform so that Inform can examine the current result of that expansion for its own purposes. This will unexpectedly run any ‘non-outputting code’ included in your ‘To say …’ phrase. You can trap this unwanted behaviour in your ‘To say …’ code by presaging ‘non-outputting code’ by the almost-undocumented conditional ‘unless expanding text for comparison purposes …’ See here and here.

4 Likes

Or, if you prefer, the slightly more longwinded ‘if the substituted text of t is w’

1 Like

Following on from previous concepts, the exception to this rule would be in situations such as:

	let t be "You are in [location]";
	let w be t;

in which case w & t are two variables referencing the same singly-declared function, so ‘if t is w …’ is the equivalent of ‘if Function A is Function A …’ and is therefore true, whereas after

	let t be "You are in [location]";
	let w be "You are in [location]";

t & w reference two separately declared (albeit identical) functions and ‘if t is w …’ is the equivalent of ‘if Function A is Function B …’ and is therefore false.

4 Likes

A quoted text immediately after the definition of a room or a scene assigns the value of that room or scene’s description property.

A quoted text immediately after the definition of a thing assigns the value of that thing’s initial appearance property. This still sometimes trips me up: I forget and try to assign a thing’s description this way.

So, to populate a thing’s description you need to explicitly say “description”… but one can cut down on the verbosity a little by omitting “of the thing-name”:

Foo is a thing. Description is "Looks like a foo.".

You could also say “The description”, but not “Its description”. The same applies to assigning values to other properties… but note that you couldn’t say “Fixed in place is true.” 'cause Fixed in place is an attribute, not a truth state property.

Also, if an initial appearance is assigned, it will appear until the thing is handled, i.e., if it has ever been held by the player. Ordinarily, people will never be handled, and neither will anything that’s fixed in place (which supporters are by default). So be careful with assigning initial appearances to people, or supporters, or anything fixed in place. (All of this describes default behaviors; one could add code modifying any of it.)

By the way, you can even add text properties called description to other kinds of objects and the compiler will allow you to assign their descriptions with just a quoted text. (But this can’t work with things or subkinds of things, for which it will always be initial appearance.)

A foo is a kind of object. A foo has a text called description. 

Fubar is a foo. "aoeu".
4 Likes

Unless the thing in question is marked as scenery, in which case putting a quoted text immediately after its creation does assign the description property. (Since scenery is not mentioned in room descriptions, it wouldn’t make too much sense to assing it an initial appearance.)

5 Likes

ha, thanks, I didn’t know that!

2 Likes

I’m coming to this late, but I realize there is so much I didn’t know, and I had blind spots about it, and it was okay. But then I find it and don’t know how I lived without it. Or I said “oh, I couldn’t ask for THAT feature, it’d be too much.”

I was glad I found the testing commands early. @DeusIrae mentioning RULES and ACTIONS is a big one. You don’t need to be a debugging whiz to debug. I’d say PRONOUNS is good to know–I wrote a simple testing extension to check the pronouns every move, and it was neat. TEST X WITH “this/that/the other” is good too. PURLOIN and ABSTRACT and GONEAR and SHOWME save so much time if something is broken at the end of your game and you don’t want to run a whole long test script to get there.

I wish there was BANISH in the test suite proper (move something off-stage) but that’s easy enough to implement on your own.

One thing that still trips me up is after/report rules. I might have

after looking in the hub room: say "Wow! The hub room has a lot of places to go!"

after looking: (check stuff)

The 2nd rule doesn’t fire because the 1st and more specific doesn’t continue the action.

I also knew how rules worked roughly but it took a while to put them in order e.g.

the Andrew's less generic default rule is listead instead of the can't wear what's not clothing rule in the check wearing rulebook.

I also always used to keep falling into this:

[the block smelling rule is not listed in any rulebook.]

check smelling: say "Smelling may offer occasional clues, but nothing here." instead;

Without the one line commented, my check smelling rule doesn’t fire.

I’d also echo looking at “rules” in the tabs on the IDE. They tell what order the rules fire in, which can clear up a lot of confusion.

One other thing that really streamlined things was when I realized you could just do this en masse so you didn’t have to worry about all the verbs.

understand the command "jump" as something new

Also I enjoyed reading the standard rules to see what the default verbs were. I learned so much! And at some point I felt more than okay just un-defining some standard verbs.

I’d also like to share some coding I did early this morning. At first I only used “check” rules to block the parser from doing something and never used “after” and I think the before/after of my code when I split something into more readable parts.

So I had one item that had a big lump of code with a bunch of if-then commands inside it for a big description. (the “to say” syntax seemed wrong, because tracking line breaks was tricky.) You will be more likely to see the bug from the “after” code that there’s a bug that got lost in the lumpy code, which I’m not sharing. (I cut out some text.)

But the thing is, I never thought to use “after examining” because this was what you should see while examining! So I had that logic error. Another big one I remember is someone new to Inform creating a dead person as an actual person instead of scenery. The first makes sense logically, but the second is easier to implement given how Inform (quite sensibly) gives people properties.

messy code vs clean code
after examining megaton magneto montage:
       say "CLOSED TODAY: (list).";
       if store b is not figured-out, say "Free samples in store b.";
       if store c is not examined, say "(store c stuff)";
       say "Store H is the bonus area after the initial quest.";
       say "Stores F, I M and R were last game.";
       if store k is in strip or store k is in strip, say "Condemned: stores K and N.";
       say "(stores you can change and enter): P, U, V, W, Y.";
       if store t is in Strip of Profits, say "(text how store t is the final one).";
       say "[line break][engrav-note].";
       if montage-change-warn is false:
               if number of solved regions + number of bypassed regions > 1:
                       say "[line break]It's kind of out of date since you already got solving stuff, but it'll be good enough reference in the future.";
               now montage-change-warn is true;
       continue the action;

So often I was just so glad Inform could do stuff, I wrote code and didn’t really look into it! It’s okay to do that at first, but Inform can let you write better looking code, so you should try to when you can. Don’t force it, though.

I think this is the sort of question that needs to be asked more often than it is on this boards. My answer always seems to change.

For whatever reason, I really dug into the parser error rules first. It was neat you could do things! Other people seem to avoid them, but I’d advise giving them a look, as they can clue the player quite nicely too.

5 Likes

Ha, this reminds me of one my favorite dumb-things-I’ve-done-with-Inform anecdotes: in my first game, I realized I had to have one every turn rule fire before another one, but I hadn’t read the bit of the docs telling you how to do that. I had, however, read the bit of the docs telling you that rules are evaluated from most specific to less specific, with the giant caveat that you should never actually rely on that in your code - which I blithely ignored as I larded up one of the rules with an extra half dozen unnecessary, always-satisfied conditions like “while (scenery item) is in (room where scenery item is located).” A little knowledge is a dangerous thing!

6 Likes

Yes, I’ve done that, too. But I think there’s a case for relying on it a little bit. Like if you have 3 rules and don’t want to sort out every last thing e.g.

check pushing:

check pushing the red button:

check pushing the green button:

Oh, one thing I forgot about building a game in general: it took a while before I realized that jump/warp commands were really useful. What does this mean? Having a command like

volume end of game commands - not for release

endgameing is an action out of world.

understand the command "endgame" as something new.

understand "endgame" as endgameing.

carry out endgameing:
	move the player to the final room;
	now player has the big impressive sword;
	say "This puts you near the end. You should be able to kill the bad guy now.";
	the rule succeeds;

(Obviously, this can be more complex. It can be useful to have a way to test different endings quickly as well e.g. bad endings, regular good endings, and a very good ending.)

You can also have a command that tries several activities/actions that might require several commands’ input from the parser, as a bit of shorthand. Obviously you don’t want to put this in release, but having these cheat commands to test odd cases beyond “purloin” helped me feel competent and confident.

The big reason I put this in is … testers default to spending a lot more time at the start than the end, because fatigue is a very real thing. That’s also just human nature, and it happens to me.

And we should focus on stuff at the start in order to make a good first impression and find details of what our work is all about. It’s not about hoodwinking a player into being interested in something lousy, but to show them all there is to offer and that the rest of the game will be robust.

So having end-of-game test commands, or commands testers can use to jump to the end, is quite handy. Especially if a game has multiple interesting endings! I know I’d really value someone who just came in and tested endings.

One other thing I advise: while volumes/books are a thing, I like breaking off beta tester commands and testing commands into extensions of their own. This means less problem with stuff leaking into release builds. You can read the header and say “OK, include Andrew’s Testing by Andrew Schultz is in a not for release section, and I commented out Andrew’s Beta Teting.”

I certainly had a misperception I had to be an inform whiz to write an extension, but no, I did not. It helped me organize my code.

Also, this is not a crutch so much as “yes, you can do this more.” But for testing, I was VERY frustrated that putting in “wait for any key” disrupted my test commands, and I also let debug code leak to testers or players. Until I realized…

volume debug stuff - not for release

when play begins: now debug-state is true;

volume general stubs

debug-state is a test state that varied.

to debug-say (t - text): if debug-state is true, say "DEBUG NOTE: [t]";

to wfak: if debug-state is false, wait for any key;

There are lots of simple stubs like this that helped me test things more thoroughly before sending them out. Sometimes it took longer than I expected to find them, but when I did, maybe I felt silly not doing so sooner, but I was glad to have another piece of the puzzle in place to help my programming go smoothly.

5 Likes

Can you imagine how much easier WHHoGG would have been if I’d known this then? I didn’t have a single normal verb except for EXAMINE in it. I don’t even want to look at all the stuff I did to turn off verbs there.

4 Likes

Well, maybe one day you will, and you can make a post-comp release of it. In fact, I bet if you sat down now it wouldn’t be too bad.

But of course you actually have new stuff to make, and that’s more fun for everyone involved. And you know this now!

One thing I’ve learned when programming I7 is that I’m under no obligation to provide the original Inform experience, especially if that experience was a bug and not a feature … shutting off the more obscure verbs feels at first like you’re not really trying to be thorough, but actually it’s about concentrating on new good stuff. It took me a while to realize that.

5 Likes

So many great tidbits of info popping up in this thread.

I can’t stress enough to new authors how valuable using books, parts, chapters and sections is in making organising and browsing your code so much easier.

My little combat system I started a few weeks ago has grown to around 1400 lines of code and without headings and nested sections, it would have been a nightmare to add and organise what I’d already written.

I think that a lot of the really useful features aren’t immediately obvious to new users because they’re so enamoured with just getting stuck into experimenting that it’s easy to say ‘I’ll get to learning that other stuff some other time’.

3 Likes

Another thing I’ve done that I regretted is making too many kinds and values. I had lots of overlap and keeping track of things was really muddy.

1 Like

I’m sure I’ve probably done the same at times, I’ve had to create variable values for certain things and have it so that that variable is held as a sort of ‘last time this happened that number is X’ which can then be used in a number of instances such as reporting or using for other equations in following actions etc.

Feels a bit hacky but it’s the simplest and least complex way I’ve found to make a lot of attack variables etc work without having to write heaps of individual rules, instead just in the first instance saying that the variable is now ‘this value’ and every other thing that needs to use that same variable again can just reference it. I think of it as like a memory block or some ram dedicated to storing that specific value only.

2 Likes

Yeah, it’s tough to balance “hey, this works and this is good enough” against “hey, I have an intuition I can do things better and maybe simpler.” Especially since the main people working on Inform aren’t paid to do so, and it’s pretty robust as it is, so it can feel awkward asking “Wait! Can’t things be better?”

For instance, @CrocMiam just pointed out you could say “the room gone to” and “the room gone from” in the check rules. Before that, I’d used “location of the player” and “the room noun of location of the player” in this topic.

I’d never have known this if someone else hadn’t asked a question and others hadn’t chipped in. It might not save a ton of energy, but it will save enough!

So while on one hand the perfect can be the enemy of the good, a small pitfall is to default to “this is tangled but I guess it’s good enough for now.”. Sometimes it will have to be. But other times it is well worth asking.

3 Likes