PunyInform v3.6 released

Glad to see you’re interested in the library!

The reason is simple: PunyInform wasn’t very mature when we released it four months ago. As people started to use it, they quickly discovered things that weren’t fully working. No new features have been added that would make sense to allow the programmer to turn off. Things like ship directions add some bytes of course, but are disabled by default and so don’t add to the size of Calypso.

These are the changes made between v1.3 and v1.7 which I believe have increased the size of the library:

  • Improved parsing of delimiters between direction commands.
  • Changed grammar of ‘get’ to match I6 lib. “get on object” and “get out of object” now work.
  • Added global receive_action to let receiving object know which action is happening.
  • Changed so the invent property is consulted whenever a list of objects is presented by PrintContents, most notably also affecting object listings in room descriptions.
  • Added calls to object parent’s before and after rules (if container or supporter) with LetGo action when taking object.
  • Fixed that PutOnSub didn’t call before- or after-rules for Receiver, and that InsertSub didn’t call before-rule for receiver.
  • Added checks to see if objects can be touched to many action routines.
  • Added call to AfterRoutines in WearSub.
  • Fixed TryToTakeNoun() so it clears concealed attribute when an object is picked up.
  • Printing the game banner has been moved to a routine named Banner(), just like in I6 lib.
  • A game can now make the library skip the game banner when the game starts, by returning 2 from Initialise(), just like in I6 lib.
  • with_key can now be a routine. The object which is currently being tested as key is held in second. with_key returns an object id, or false if nothing fits.
  • Run AfterRoutines() after taking inventory and it’s not empty, just like I6 lib.
  • Fake action ##Going is now sent to destination room’s before routine just before the player enters.
  • Improved disambiguation for plurals to make “get all red” work
  • Now allowing more than one word inputs in disambiguation
  • If bag is in box which is on table, and player is on table and types ENTER BAG, (s)he now gets “You have to enter the box first.” instead of “You have to leave the table first.”
  • GET ALL FROM [supporter] could make parser complain that the supporter isn’t open.
  • GET ALL FROM [object] would not take anything unless the object was a static or scenery object.
  • creature_object wasn’t properly handled by the parser.
  • Now inp1/inp2 are updated in PerformAction
  • Fixed ‘drop X’ parsing bug when X present but not held.
  • Look now sets the action to ##Look when calling AfterRoutines, even if Look was called by the Go action or some other action.
  • Made plurals trigger that object names are printed, so “TAKE BALLS” may print “red ball: Taken.”
  • GET ALL FROM BOX issued a ##Remove action for the first item and then a ##Take action for each of the remaining objects.
  • Held (GrabIfNotHeld) didn’t check implicit take status properly.
  • Fixed an error where “drop books” when not holding any books still referred to the first book in scope
  • Wrong word order in disambiguation caused parse_name to fail
  • Fixed error where multiinside silently ignored some inputs
  • Fixed: bad combinations of adjectives and nouns messed up disambiguation code
  • Made ‘unknown word’ messages customizable
  • Objects which have scenery or concealed no longer get the chance to be printed by Look, even if they have initial, when_open, describe etc.
  • Improved incomplete switch command replies
  • PunyInform now informs when the score goes down, as well as up.
  • Fixed ‘get all but X’ didn’t work like ‘get all Xs but Y’
  • Fixed bug in MoveFloatingObjects which caused objects to disappear if one floating object had absent attribute.
  • Fixed dropping objects when on a supporter bug
  • The parser can now correctly handle input like 'get all but ’ . Regression test added to getallfrom.inf to check this and similar patterns.

In summary, the parser is better, more things are customisable, the game author has more control over the actions, games are less buggy and the presentation to the player is better.

I tried compiling the latest version of Calypso as z5 using PunyInform v1.3 and v1.7. These are the file sizes, without padding:

With abbreviations: v1.3: 47884 bytes, v1.7: 48684 bytes
Without abbreviations: v1.3: 51620 bytes, v1.7: 52416 bytes

The size difference is 800 bytes with abbreviations and 796 bytes without. I don’t think that’s half-bad, considering how much better and complete the library has become.

6 Likes

That there’s room for improvement is out of question, as I have suggested above, suggesting aiming for .z3 late Infocom parser, but my suggestion is, if you add a TODO file when releasing punyInform 1.8 the feedback will improve, allowing a better prioritising of the remaining work to be done.

A suggestion, AFAIK no one has psychanalysed Lord Nelson ;), so I guess that is feasible reclaiming a precious attribute via:

Attribute absent alias female;

Best regards from Italy,
dott. Piergiorgio.

We have chosen not to have a todo file for PunyInform. Instead we have a (public) Trello board where everything that would be in a todo file can be found: Trello

There will always be room for improvement. And there will always be some who think the library should be more advanced, and some who think it should be more minimalistic. These people aren’t right. Neither are they wrong. It’s their personal, perfectly valid opinions.

We’re pretty happy with the library as it is now. We accept bug reports and suggestions via the Github issue tracker at Issues · johanberntsson/PunyInform · GitHub . If it’s a bug we usually just fix it. If it’s a suggestion we discuss it, decide what we want to do and reply and possibly make changes to the library. In any case, we really appreciate feedback, be it bug reports, suggestions for changes or just hearing from people who use the library.

There are cases where you want a ship computer to be present in a lot of rooms, or you could have an NPC following you around using the floating object mechanics, so I don’t think this is a good case for aliasing.

3 Likes

Agree on the aliasing, especially noting from that trello board that your next step is working around multiple PC/NPC, so best do this first then working around aliases and attribuite reclamation: it’s a sound dev strategy.

OTOH, I’m watching the debate on I6 scope: people sometimes use add_to_scope for body parts, but I use only one body part, the brain :smiley: (my THINK model is around adding topics into scope) and this indeed require fully customisable player object(s).

This isn’t really an “issue” so I put it here instead of Github: You might want to update the Readme as there are now 5 games using PunyInform. The game missing from the list in the Readme is “Return to the Castle” (sequel to “Retarded Creatures and Caverns” from 1989) by John Wilson. This is his third PunyInform game and can be found through this blog-post: About “Return to the Castle” on Zenobi.co.uk

1 Like

PunyInform v1.8 is out, with bugfixes and new stuff! One of the news is that the lightweight scenery extension now allows you to write responses tailored to each scenery object. Full release notes and downloads at https://github.com/johanberntsson/PunyInform/releases


4 Likes

Thanks for the new interesting howto, semidark.inf:

As noted in my last post on my think model, indeed the InScope routine has given a “DOH !” moment, I still haven’t tested it, but if I use an objectloop “if x has topic” the thinking model can work even w/o a full PC object (needed for add_to_scope topics)

Similiarily, perhaps testing for the semidark status can solve quirk 1 by changing thedark.name with something more appropriate if the real_location has semidark ?

Best regards from Italy,
dott. Piergiorgio.

The location name of thedark is changed already, by the line thedark.short_name = TheDarkShortName - a routine which prints the name of the real location if it’s only semi-dark. However, in z3, the interpreter prints the location name in the statusline and it just prints the object name string - it doesn’t care about the short_name property (after all, the interpreter has no idea what language we’re using, what the different properties are used for etc).

We could probably make PunyInform allow the game programmer to change which object’s name is printed, but we haven’t done that yet.

As for the thinking - do you need the thinking subjects to be present in the location?

Have you looked at the implementation of the What verb in Library of Horror, which allows you to type “what is a grue” etc? Sounds like that is just about what you need.

1 Like

I have encountered a limit in PunyInform 1.8 (perhaps all versions?), which may be well known, I don’t know… I wasn’t aware of it though.

It seems as if the maximum number of visible objects must not be above 30, as this will give:
“[Puny error: 3]”
(maybe the actual limit is 32 as that may include the player and the location?)

I noticed, that if I close a box with an object inside, so only 30 objects are visible, the error goes away.

I don’t know where to look up these error codes. If this is a known limitation, could you please let me know where such limitations are described?
Thanks :slight_smile:

My applied inf-file:

!% -~S
!% $OMIT_UNUSED_ROUTINES=1
! The very first lines of the main source code file for a game can contain compiler options, like the two lines above.
! -~S disables strict error checking. This is otherwise used in z5 and z8 games by default. While useful for debugging,
! it adds ~10 KB to the story file size and it makes the game slower.
! $OMIT_UNUSED_ROUTINES=1 makes the compiler remove all routines which aren’t used. This can save some space.

Constant Story “Minimal”;
Constant Headline “^A sample game which uses PunyInform.^”;

! Uncomment ONE of the two following lines, to show either time or score/turns
! Leaving both commented out makes the library bigger.
!Constant STATUSLINE_TIME; Statusline time;
Constant STATUSLINE_SCORE; Statusline score;

! Uncomment to add optional features to PunyLib
!Constant DEBUG;
!Constant CUSTOM_ABBREVIATIONS;
!Constant OPTIONAL_ALLOW_WRITTEN_NUMBERS;
!Constant OPTIONAL_EXTENDED_METAVERBS;
!Constant OPTIONAL_EXTENDED_VERBSET;
!Constant OPTIONAL_PRINT_SCENERY_CONTENTS;
!Constant OPTIONAL_FULL_SCORE;
!Constant OPTIONAL_FULL_DIRECTIONS;
!Constant OPTIONAL_SHIP_DIRECTIONS;
!Constant OPTIONAL_GUESS_MISSING_NOUN;
!Constant OPTIONAL_MANUAL_SCOPE;
!Constant RUNTIME_ERRORS = 0;

! Define any library constants you need here, like MAX_SCORE, AMUSING_PROVIDED,
! MAX_CARRIED, SACK_OBJECT, etc.

Constant INITIAL_LOCATION_VALUE = Library;

Include “globals.h”;

! Define your own global variables here, if any

! Define the entry point routines you need here, like Amusing, DarkToDark etc.

! Uncomment to add PunyLib extensions
!Include “ext_menu.h”;
!Include “ext_flags.h”;
!Include “ext_quote_box.h”;
!Include “ext_cheap_scenery.h”;

Include “puny.h”;

! Uncomment to add PunyLib extensions
!Include “ext_waittime.h”;

Object Library “The Library”
with
description “You are in a library.”,
has light;

Object -> Hammer “hammer”
with
name ‘hammer’; !1

Object -> dust “dust”
with
name ‘dust’; !2

Object -> chisel “chisel”
with
name ‘chisel’; !3

Object -> gold “gold”
with
name ‘gold’; !4

Object -> chest “chest”
with
name ‘chest’; !5

Object -> sword “sword”
with
name ‘sword’; !6

Object -> plate “plate”
with
name ‘plate’; !7

Object -> shaver “shaver”
with
name ‘shaver’; !8

Object -> pen “pen”
with
name ‘pen’; !9

Object -> paper “paper”
with
name ‘paper’; !10

Object -> table “table”
with
name ‘table’; !11

Object -> phone “phone”
with
name ‘phone’; !12

Object -> crab “crab”
with
name ‘crab’; !13

Object -> donkey “donkey”
with
name ‘donkey’; !14

Object -> mamba “mamba”
with
name ‘mamba’; !15

Object -> snake “snake”
with
name ‘snake’; !16

Object -> bottle “bottle”
with
name ‘bottle’; !17

Object -> cap “cap”
with
name ‘cap’; !18

Object -> hat “hat”
with
name ‘hat’; !19

Object -> monkey “monkey”
with
name ‘monkey’; !20

Object -> saw “saw”
with
name ‘saw’; !21

Object -> pin “pin”
with
name ‘pin’; !22

Object -> diamond “diamond”
with
name ‘diamond’; !23

Object -> sapphire “sapphire”
with
name ‘sapphire’; !24

Object -> shoe “shoe”
with
name ‘shoe’; !25

Object -> underwear “underwear”
with
name ‘underwear’; !26

Object -> bible “bible”
with
name ‘bible’; !27

Object -> pill “pill”
with
name ‘pill’; !28

Object -> pocket “pocket”
with
name ‘pocket’; !29

Object -> key “key”
with
name ‘key’; !30

Object -> Box “box” !31
with
name ‘box’,
inside_description “It feels so nice, standing in the box.”,
description “The box looks heavy.”,
has container open openable enterable;

[Initialise;
print “^^And so the story begins…^^”;
move Hammer to Box;
];

If you have runtime errors enabled (value of 2, i think), it will probably say “scope is full” indeed. There should be a MAX_SCOPE (or MAX_SCOPE_SIZE?) defined in globals.h to 32. Define your own before globals.h and you should be good!

1 Like

Thanks, just what I needed :slight_smile:

1 Like

I see you got help, great!

Note 1: Check out https://github.com/johanberntsson/PunyInform/wiki/Manual#customizing-the-library

Note 2: If you compile in DEBUG mode, RUNTIME_ERRORS defaults to 2, giving you as much information as possible about what goes wrong. Without DEBUG, it defaults to 1, giving you numeric error codes. Once you have tested your game properly and are ready to release it, you can choose to set RUNTIME_ERRORS to 0. This is like 1, only it removes several checks for runtime errors, making games slightly faster and smaller. For the problem you showed us above, RUNTIME_ERRORS = 0 would have meant that the scope would just have been quietly truncated - the last items would not have been included.

Note 3: The error codes are defined near the end of the file lib/messages.h in the PunyInform distribution.

1 Like

@fredrik I’m not shure if you noticed the discussion over in this thread. But the script by @mulehollandaise could be useful if you’re targeting 8-bit machines as I imagine many users of PunyInform are.

1 Like

I think he already knows about it :smiley: That script is one of the reasons why I was able to fit so much stuff in “Tristam Island” - another reason is PunyInform’s efficiency, and Fredrik’s expertise in optimizing I6 code to produce efficient low-level code! I really ought to write that up…

4 Likes

Whenever @mulehollandaise feels it’s ready for a public release, we’d like to point PunyInform users in that direction.

It’s not really that helpful for authors just starting out with PunyInform, but it’s of great value to authors whose game is nearing completion and they’re starting to hit their head against the 128 KB limit of the Z3 format. Perhaps we should provide a chapter of tips for authors who are finishing up their games for release. Including this script and things like “Make sure you turn off DEBUG”, “Set RUNTIME_ERRORS to 0 or 1” and maybe even “Get betatesters for your game”.

6 Likes

Version 1.9 of the text adventure library PunyInform is out! It has some new features and numerous bugfixes.

Among the news:

  • You can disable the handling of darkness, to make the library a little smaller and faster
  • You can choose the order of execution for timers/daemons.
  • “open box” tells the player what’s in the box.

Full release notes and downloads on the Github page: https://github.com/johanberntsson/PunyInform/releases

Since the games produced are standard Z-code games (version 3, 5 or 8), they can be played on anything from a Commodore PET to a Palm Pilot, or of course a modern computer! The screenshot shows the demo game Library of Horror (full source included with PunyInform) being played on a Commodore 128 (emulated by Vice). Once you have compiled a game using PunyInform, buildning disk images for C64, C128 and Plus/4 takes only a few seconds using Ozmoo Online ( http://microheaven.com/ozmooonline ).

7 Likes

When I catch the action Go, shouldn’t noun contain the value of FAKE_E_OBJ? And where does this value 5 come from?

The Room1
You are in room1. Room2 is to the east.

> e
Before: Go:
(name) noun = east
noun = 5
FAKE_E_OBJ = 10003

Code
!% -~S
!% $OMIT_UNUSED_ROUTINES=1
Constant STATUSLINE_SCORE; Statusline score;
Constant OPTIONAL_NO_DARKNESS;
Constant INITIAL_LOCATION_VALUE = room1;
Include "globals.h"; Include "puny.h";

Object room1 "The Room1"
   with
      description "You are in room1. Room2 is to the east.",
      Before [;
         Go:
            print "Before: Go:^";
            print "(name) noun = ", (name) noun, "^";
            print "noun = ", noun, "^";
            print "FAKE_E_OBJ = ", FAKE_E_OBJ, "^^"; 
      ],
      e_to room2;

Object room2 "The Room2"
   with
      description "You are in room2. Room2 is to the west.",
      w_to room1;

[ Initialise; ];

Noun should be Directions and selected_direction should be e_to. It’s usually enough to just check selected_direction, since it’s zero if a direction wasn’t specified.

The value 5 is the object# for the Directions object.

Typically:

before [;
	Go:
		if(selected_direction == u_to)
			"We don't go upstairs anymore. Not since the accident.";
],

The only time you use the fake direction objects is when you issue an action in code, like:

<<Go FAKE_E_OBJ>>;

Oh yes, thank you. It’s in the documentation at Direction Handling. I’ll have to read it one day instead of going back to DM4 every time!