I’m giving my old extension for Inform 7 a complete overhaul and, after an eternity of messing around and annoying poor Erik Temple with mails until I started to get a bad conscience, I think the thing is ready for some public attention.
For those of you who are unfamiliar with R&D, it is an extension which allows the creation of a 3-dimensional ‘map’ of a room on which you can move objects around. When I started with this somewhere back in 2008, my initial goal was to create combat maps, much like the ones you see in RPGs like Pool of Radiance and the like. However, somehow this extension has reached such a level of complexity that it would also allow many other applications, including pop-in & out boardgames or as a measurement translator for puzzles which require physical distance.
Performance – Speed is quiet a concern for me. R&D runs rather fluid on my rig, even with Glulxe, but it would be interesting to know how well it runs with other configurations, especially if you’re a Linux or Mac user.
Documentation – Seeing as English isn’t my first language, I’m especially worried that what I write might be perceived as frantic gibberish. Is the documentation comprehensible? Are there any segments which are too bloated or too vague? Are there parts of the examples which are hard to understand? Do you believe that, after reading the documentation, you’ve learned everything that needs to be known to utilize the extension or do you feel that there are notable gaps? Is the writing style too dull?
Creativity – I’m always open for suggestions. If you feel that R&D lacks any comfort function which would greatly cut your workload then feel free to mention it.
It was a bit unfair of me to simply dump this on you people without any explanations, my apologies. I’ve edited the first post to give you a better idea what this is all about and where you can help me with.
I’m curious if I could use relations for AI purposes. Could it be possible for a relation to return a numerical value? I think it would be kind of neat, if you could set up relations like “The path weight of a (e – a member of enemy faction) for (p – a person in friendly faction) is 100” or something like that. Would that go?
Sounds like it could come in very handy. I actually did use i6 to build a Dungeon Master’s roleplaying aid back in the 90s. I coded all the adventure locations in and a substantial number of combat systems (under my own homegrown roleplaying rules). I did not have any tactical maps in there though. Nowadays I am focusing on something very different in my WIP and it does not even have standard geographical locations (at least not via ‘X is a room’), so it would be difficult for me to test this with it, and I am not so conversant in i7 yet that I could help solve deep technical issues off the top of my head, but I just wanted to register my interest in this extension.
In “Elevator,” the image seems to shrink every time I move. (Running in the IDE and in Gargoyle.) Also, when I go into the Skull House, I lose track of my icon (and I can never see the guard).
This looks very neat indeed; it’d take me a while to try to figure out what I might do with it, though, so I don’t think I can betatest it from the author’s point of view. Do you know how much of it might be separable from the graphics? I’ve had an idea of doing some similar things using ASCII graphics and fixed-width fonts, like old school roguelikes.
I had the problem with shrinking images as well when I played in windowed mode. My take is that some calculations get messed up when a g-element gets resized to fit in a window. This might be a bug with Glimmr, since I pretty much copied and pasted the code for resizing from there but I’ll look into it. Does this work any better for you? (I’m not sure if Gargoyle always starts in windowed mode but if it does, switch to full screen and restart. That usually works for me at least when my desktop resolution isn’t too small)
I’ve tried to keep the different components as independent as possible. The most limiting aspect, I hope, is that figures, or at least what should get displayed at the end, are accessed over a reference number (and there should be a workaround or two for that as well) . Otherwise the sky is the limit, really.
I doubt that this is a bug in Glimmr. What it sounds like is that sometimes (why only sometimes?), the scaled measurements for an image are being stored as if they were the original values for the image; each time the window is redrawn the image gets progressively smaller. Glimmr never stores scaled measurements in this way; it recalculates scaling every time the window is redrawn. Is there somewhere in your code where you might be doing this? (Note that it seems to be g-elements, not the canvas as a whole, that are being progressively scaled.)
Matt, are you seeing two totally different grids in the Elevator example? When your icon disappears, it should appear on the other grid. You need to open the window to a larger size to see everything. This is not standard Glimmr behavior (Glimmr would show the entire canvas no matter how small the window was reduced), and in fact on my laptop it’s impossible for me to make my window big enough to see the entire left grid.
Basti, the new examples that I haven’t seen before (particularly Ben & Jill) are cool, and everything runs much more snappily than before on my machine too.
You are right. Turns out that by making the m-i-m’s a kind of g-element instead of an image-map, the scaled tile-height/width got lost. I changed that back again, which fixes the issue (plus now m-i-m’s can be hyperlinked. That should come in handy someday) but that created another unexpected problem. Any idea why the tilecells are bit off on the x-axis?
[There was code here. It is gone now]
Thank God for that! I would be heartbroken if all the hard work would have been for nothing because of performance issues.
I suspect that your refitting algorithms (e.g. “fit x to width of 400 and height of 400”) are setting the same properties that Glimmr’s image-maps use to render tiles, changing the intended set up. “Tile-width” and “tile-height” are the likely culprits.
Probably, but why would tile-width/tile-height work with direct image-maps and not with m-i-ms? I’ll have to take a look into it. It currently looks like something gets mixed up with the display rule as well. Tile-height is apparently also used for tile-width from what I can see.
There is another snippet that I’ve overlooked.
Do you remember which rule refits a g-element to a canvas? Is it the element scaling rule or something else? Might as well take care of that while I’m at it.
Found another oddity in the meantime: Inform 7 preferred
fit (grid - an image-map) to/into a/-- total/-- width of (X - a number) canvas/-- pixel/pixels/px/units wide/--
fit (grid - a multi-image-map) to width of (X - a number)
although the later was, technically, more specific, so I had to change that (btw @Erik, there is a ‘/–’ missing after units).
When you changed your m-i-ms from g-elements to image-maps, this made them subject to two new rules, the default image-map scaling rule and the default image-map drawing rule. It looks like your custom drawing rule probably overrides the latter, but the scaling rule is very likely firing. I haven’t looked into your code enough to see how this might change things, but that’s what I’m referring to. (It may not be that this is not the right explanation, since it looks like you aren’t making use of the scaled values anyway.)
Yes, the m-i-m drawing rule that you posted above does seem to be using tile-height for both height and width. Also, that drawing rule is throwing away any scaling information–it’s using the tile-width and tile-height directly, rather than using the scaled values.
A Glimmr canvas is really two things: a coordinate system independent of the window, and a bounded rectangle expressed in that coordinate system. If you specify your canvas as measuring 600 wide x 300 deep, Glimmr will fit that bounded rectangle (0-600 x and 0-300 y) into the window, also calculating the scaling factor needed to do so: e.g., if your game window is 300 pixels wide, then the scaling factor will be 50%. This scaling factor is used to determine the placement and sizing of all of the graphic elements that go on the canvas. In the case of an image-map, the origin point of the map is scaled by this scaling factor, and so are the tile-width and tile-height. Thus, if your tile-width in canvas coordinates was 20, each tile would actually be drawn to the screen at 10 pixels wide after being scaled by 50%.
So there isn’t a rule that refits an image-map to the canvas. There are two phrases that allow you to provide the total width you want the map to fit into along one dimension, and then figures the tile-width from there. However, this is not a scaling operation per se–it’s just intended to save you the trouble of calculating your own tile sizes. If you know you want your image-map to be 400 canvas units wide, you can write a “when play begins” rule to say “fit my image-map to a width of 400”. This will look at the number of tiles per row in your image-map, calculate how wide each tile would need to be to fit into 400 pixels, and then adjust both the width and the height to maintain the aspect ratio. I suspect that your own “to fit” phrases are doing the same…?
Anyway, to get the m-i-m to behave like a Glimmr image-map and scale along with the canvas, you’ll need to rewrite your drawing rule to use the scaled tile-width and tile-height values. See the code in Glimmr Canvas-Based Drawing for the image element-scaling and element-drawing rules.
Check again, there isn’t anything missing . It’s just that the code allows for many (too many) different expressions of the phrase.
Thanks you for your insights, Erik! That was really a big help. Everything works now ™ (don’t wonder if the player disappears on level 2. It’s some work in progress)
The problem was not only the element display rule but also that the element scaling rule would fire twice, once for the m-i-m, then again for the m-i-m as an image-map. Listing the rules differently didn’t seem to help, so I settled with scratching my own scaling rule and just created new ‘desired tile-width/height’ phrases for the m-i-ms. Seemed like a less crude way to solve it than ignoring the standard rules.
As for other progress, I’m still getting rid of the old m-i-ms. I decided to rename them to ‘map-displays’ (with the static m-i-m now being a top-down-display) and add a list of those map-displays to every m_room. That way, every time you place or move something on a map, you have an easy way to know if the m_room is currently displayed and what map-displays are doing the work, making it a lot easier to decide whether they need to get completely reprinted, or if they need to actualize their list of current objects displayed.
This ties the whole displaying progress a lot stronger to image-maps and I’m not familiar enough with Glimmr to presume what impact that’ll have, if you want to use something other than figures/graphical-windows for, say, a plain ASCII roguelike. It should be easily possible (you could store the text directly within the linked command array of an image-map for one thing) but I don’t dare to make any big assumptions.
It was either that or a ‘/–’ too much in
To fit (grid - an image-map) to/into a/-- total/-- height of (Y - a number) canvas/-- pixel/pixels/px/units/-- high/--
(if the distinction isn’t intentional that is. I stumbled over this since Inform would compile ‘fit x to height of y’ but not ‘fit x to width of y’)
Okay, next update is out. Quiet a lot happened in the graphical department.
Linking map-displays and m_rooms together made the whole process of determining what can or can’t get currently printed a lot easier and it should now be possible to keep the performance fairly stable, even if you walk around a map with thousands of displayable objects in it, as long as they don’t all suddenly pop in view. I also made a new kind of map-display which should not only be ASCII friendly but also a lot faster in general (albeit for the price of a less sophisticated arrangement of the graphics). I made some small tweaks to the Cyberspace! example to showcase what the new stuff can do.
There are two rather bad news though. First, my feeble dream of porting R&D to the z-machine was crushed pretty early as Inform 7 reassured me that it wouldn’t accept all the lists shenanigan. There also seems to be some sort of strange bug with either R&D or Gargoyle as this example always crashes with it when I try to move before doing anything else. The example seems to run fine in Git so I’m a bit puzzled what the problem could be. Different memory allocation maybe?
Afraid I again got some questions for you too. The new map-display I created uses your Glimmr Lucidex figures as the current standard when translating text into graphics (as seen in the crashing example). Is that ok with you, or should I rather get my own stuff?
Also, what do you want to do with the fit phrase, include the ‘/–’ or not? I only ask because I would have to tweak my code if you want to remove it.
Sounds good! From the user’s point of view, has the actual coding changed much, or are most of the changes in the core extension?
The Cyberspace example works nicely for me except that, as before, the main grid doesn’t scale down to fit in the window. (Inconsequential from a programming standpoint, but maybe worth mentioning: If the robot had a transparent rather than an opaque black background, it wouldn’t overlap and sometimes entirely occlude the grid lines.)
I don’t see this bug on Gargoyle under OS X, whether using git or glulxe under the hood. Glulxe is painfully slow, and git can be slow on the first move, but I’m not seeing a crash. Maybe you’re using an older version?
Of course, have at it! That’s why I released that “font” as a separate extension. I consider both the images and the code to be under the standard CC license used for all Inform library extensions. Of course, since the font isn’t a monospace font, it may not be appropriate for all grid-based applications, but it should work well enough for a roguelike.
Let’s include it. I may look at simplifying the phrase definition in the future so that it doesn’t have so many options. (I have tended to make my extensions very flexible whenever possible so that folks can more easily guess at the correct syntax, but that approach has its downsides.)
It’s sort of mixed, I would say, although the biggest changes are mostly internally. Something that you probably won’t notice right away is that map-displays now have a scope, basically, thanks to the cross-reference. For example, with a 1000x1000 map and 500 objects, the old map-display would have run through all those objects to check that they are in range. Now a map-display just gets a list of objects which gets filled up during its initial creation and then will get actualized automatically during every move/spawn phrase, since the map now ‘knows’ by which map-display it is currently, well, displayed. Also, when you change/insert any graphics and they are in range of a map-display, it will force it to refresh entirely. In the older version of Ben & Jill for example, when I replaced the image of the closed door with the open door and vice versa, I would have to do a refresh manually.
All in all this makes the insert/remove phrases a wee bit more expensive and some special cases, like an object with such offset values that it would be shown in the map-display, while not really physically present, are no longer possible (EDIT: although, come to think of it, that could be done with a bit of re-tinkering), but I think that it’s some comfortable streamlining regardless. Nevertheless, it’s probably only good to know if you plan of creating your own kinds map-displays (Still looking forward of doing a first-person display but I’ll put that on the back-burner for now).
As for actual game coding, not much has changed, other than that you now can switch between full-map and part-map, but that’s more of a gimmick to save some time.
That’s probably due to a larger scale. The tiles are scaled down by sight-range, so the smaller the range, the larger the graphics will become. Choosing smaller numbers for the fit phrase should fix that problem. Maybe I’ll find a way to universalize the progress, I’ll think about it when I got the time.
It was probably just nostalgia, sorry
But the example is actually supposed to showcase that this is not possible with simple-top-down-displays (or at least not as easy), since objects don’t have any actual floor underneath them. They are just plain text in a 2D-array. Making the robot transparent wouldn’t have done much good, since all you would see behind it would have been the background-tint. Printing the floor first and then the objects should still be possible though, although that would go against the idea to make the s-t-d-d’s as simple and fast as possible, so that’s another topic to think about, thanks!
Nope, I’m only using the latest stuff. Weird…
The lack of monospace is quiet notable, no doubt, but I’m too lazy to make my own graphics for now, so that you’re ok with it is quiet a stress reliever
Don’t let me push you around by any means
Good thing then that this is covered. Maybe using something else than image-maps to store the pure text-data would cause less redundancy, but let’s just say for now that a few limbs extra won’t get into your way.
Next stop: AI
That could be a tough one. I’m still curious whenever you can use relation to establish weights between objects-objects and objects-effects. Time to hit the documentation.
Well, not so much with the AI, but importing maps from files turned out to be a lot easier than I thought and I got a little prototype ready.
Just download the example and the import file (keep in mind that the game can’t read .txt files, so you’ll have to remove the file extension, 'cause that’s how Inform rolls, I guess) and try to go wild. This method only supports floors right now (the ‘.’) but I will add at least the default walls and borders in due time. There is also the limitation that the width and length of a room must be of a fixed size, so don’t try something like a 5x5 field on level 1, 2x6 on level 2, etc.
Sometimes it feels really good when something works right off the bat. :mrgreen:
Sounds like a cool idea! Unfortunately, I can’t see anything on my machine. The file loads, and there is a black backgrounded but otherwise empty graphics window on the left, and an apparently empty text buffer window on the write that has a “more” overrun prompt. I assume that the map is supposed to be printed in both of these? Or is the map being printed as empty squares/spaces (in which case, this doesn’t make for much of an example! )
Note that Zoom will crash unless the text file has an extension of .glkdata (there may be other extensions that will work, not sure), but Gargoyle can’t have that extension or it will crash. Does anyone know if there is an extension that will work for all external files? If not, this seems like something like the Glk spec ought to require…