The other day i was reading Andrew Wooldrige’s post about Uses for Limitless Quest. Around about the same time, i was thinking i should see what else i could make with OpenLaszlo. So i thought, “Could i use OpenLaszlo’s powerful data binding features to make a map editor for Limitless Quest?”
Naturally, rather than sitting on the issue and eventually concluding it would be an utter waste of my time, i decided to dive straight in and see if such a thing could be done.
Starting off
First of all, i had a look at Limitless Quest to see how its maps are defined.
It turned out to be quite simple. To summarise:
- The tile graphics (or perhaps better described, tile sets) are basically just a set of images with 32×32 tiles bunched together. There are quite a few of these tile images, though as of the current release they are mainly not used.
- The actual tile data used in the levels is defined in the “world.js” file. For each tile the base name of the tile set is specified (e.g. ‘dungeon’), as well as the offset (x,y) in which the 32×32 tile appears in the image. Also specified is the name and any according actions which the tile triggers.
- Monsters are defined in a monster list. This list is actually double nested as you can specify monsters for each separate room in the world. For each monster, a position, name, and miscellaneous parameters are specified.
- The tile map a room is just a set of 10 arrays (each 10 elements long) which refer to the defined tiles. As with the monster list, this is double nested so you can specify multiple rooms in the world.
Displaying tiles
Armed with this information, i decided i should figure out how i could display 32×32 tiles in OpenLaszlo. This wasn’t very evident as i don’t think OpenLaszlo was designed with games in mind.
<canvas>
<!-- Images -->
<resource name="tileset_img" src="resources/dg_dungeon32.png"/>
<!-- Tileset (dungeon) -->
<dataset name="tileset">
<tile>
<name>firstTile</name>
<offset x="32" y="64"/>
</tile>
<tile>
<name>secondTile</name>
<offset x="0" y="0"/>
</tile>
<tile>
<name>thirdTile</name>
<offset x="32" y="32"/>
</tile>
<tile>
<name>wall</name>
<offset x="0" y="128"/>
</tile>
<tile>
<name>wall 2</name>
<offset x="0" y="128"/>
<props collision="true"/>
</tile>
<tile>
<name>floor</name>
<offset x="0" y="0"/>
<props collision="false"/>
</tile>
<tile>
<name>floor 2</name>
<offset x="32" y="0"/>
<props collision="false"/>
<action type="gotomap">
<map>1</map>
<player x="8" y="1"/>
</action>
</tile>
<tile>
<name>floor 3</name>
<offset x="32" y="0"/>
<props collision="false"/>
<action type="gotomap">
<map>0</map>
<player x="1" y="1"/>
</action>
</tile>
<tile>
<name>floor 4</name>
<offset x="64" y="0"/>
<props collision="false"/>
<action type="message">
<value>a trap!</value>
</action>
</tile>
</dataset>
<!-- Tileset palette -->
<window id="tiledisplay" title="Tiles" width="100" height="300" resizable="true">
<view name="tiles" width="${parent.width - 16}" height="${parent.height}">
<wrappinglayout axis="x" spacing="0"/>
<view datapath="tileset:/tile" clip="true" width="32" height="32" clickable="true">
<method event="onclick">
Debug.write('tile clicked!');
</method>
<view resource="tileset_img" xoffset="$path{'offset/@x'}" yoffset="$path{'offset/@y'}"/>
<text datapath="name/text()" fgcolor="#ffffff"/> <!-- A name comes in handy -->
</view>
</view>
</window>
</canvas>
This code was more or less what i started off with. Basically, the “tiles” view contains the tiles. They are replicated from the child view which has its “datapath” set, which happens to point at our “tileset” dataset.
You might also note that their “clip” parameter is set. This is so that the offseted tileset image (which we will get to in a second) doesn’t spill out from the sides of the replicated tile view.
Inside each tile is a view which points to the “tileset_img” resource, which as you might have figured out already is the tileset image which contains all of the 32×32 tiles which we might want to use.
It is also offset (x,y) according to the data for the tile in the “tileset” dataset. Note that i use the ”$path” constraint, which basically retrieves the value of the specified XPath query, in this case the “x” and “y” attributes of the “offset” note.
Whenever the “tileset” dataset is updated, the tiles are replicated. In addition the “wrappinglayout” ensures the tiles are placed correctly (along the x axis but wraps down the y) – so there’s no need to lift a finger!
Displaying the map
Now i had a tileset palette, i needed a tilemap and associated view to display it. This didn’t turn out too hard…
<dataset name="mapdata">
<!-- Some useless metadata for fun -->
<meta>
<extent x="10" y="10"/>
<tileset base="dungeon"/>
</meta>
<!-- Object list (TODO) -->
<object>
</object>
<!-- And now the tilemap-->
<tilemap>
<t>2</t> <!-- NOTE: repeat 10*10 times -->
</tilemap>
</dataset>
<view x="$once{tiledisplay.width}" width="320" height="320" bgcolor="#000000">
<wrappinglayout axis="x" spacing="0"/>
<view width="32" height="32" clip="true" datapath="mapdata:/tilemap/t">
<attribute name="tile_id" value="$path{'text()'}"/>
<view name="timg" resource="tileset_img" />
</view>
</view>
This is pretty much the same concept as the tileset palette, so nothing revolutionary here. Except that you should notice there is no “xoffset” or “yoffset” specified on the “timg” view. Why?
Well, here is where i bumped into limitations with OpenLaszlo’s data binding features. If i used a ”$path” constraint as in the tileset palette factoring in the “tile_id” attribute, the tile would never appear to change even if i changed “tile_id” as OpenLaszlo wouldn’t have the sense to update and re-run the XPath query.

Fixing the data binding
After a while trying to get the tilemap view to update properly when i painted a tile, i scoured through the OpenLaszlo documentation and came up with a solution.
Each “node” in OpenLaszlo has two event handlers:
- applyData which is called when data in the associated dataset or datapath is updated.
- updateData which is called whenever you call “updateData()” on the associated datapath (which pulls in data from any associated controls).
So i figured, in each tile i needed to have a node with a datapath which implements these handlers. So instead of my “tile_id” attribute, i ended up with:
<node name="tile_node" datapath="text()">
<attribute name="tile_id" value="0"/>
<method name="applyData" args="data">
<![CDATA[
this.tile_id = parseInt(data);
]]>
</method>
<method name="updateData">
<![CDATA[
return this.tile_id;
]]>
</method>
</node>
Now all i needed to do was set the “xoffset” and “yoffset” parameters for the “timg” view when the tile was replicated or updated. I came up with this code in the replicated tile view:
<method event="ondata">
<![CDATA[
// This is guaranteed to be called when this view is replicated
this.sync();
]]>
</method>
<method name="sync">
<![CDATA[
// This should be called whenever this.tile_node.tile_id is updated.
var offs = emanager.get_tileoffset(this.tile_node.tile_id);
this.timg.setAttribute('xoffset', offs.x);
this.timg.setAttribute('yoffset', offs.y);
this.datapath.updateData();
]]>
</method>
Now you might be wondering “what is this emanager?! what does get_tileoffset do?!”. Or even, “where are the tile painting handlers?!”.
Firstly, “emanager” is an instance of my “editmanager” class, which stores the current active brush and contains a few utility functions such as “get_tileoffset”, a function which gets the x,y offset of the specified tile in the tilemap.
Secondly, the tile painting handlers go in two places:
- The tile palette window tiles (to set the active brush)
- The tilemap tiles (to paint the active brush)
As for the rest of the code, well…
The end result
To cut a long story short, you can download the code from here. Note though that to compile it you will need to grab the resources from Limitless Quest and obtain the third party “json.js” script, all of which is mentioned in the file.
I also added in a dumper which dump’s the map and tileset data into a format suitable for pasting into Limitless Quest’s “world.js” file. It makes heavy use of OpenLaszlo’s DataPointer object, which comes in quite handy when you want to parse XML.
Sadly i did not get round to adding in support to place monsters in the rooms, but it is something i hope to implement in the future if i find the time.
In the meantime, feel free to play around with it.
Surprises
All throughout developing this rather simple tile editor, i used OpenLaszlo’s “swf7” (i.e. Flash 7) output target, thinking that the editor couldn’t possibly work with the “DHTML” target. I was however pleasantly surprised when i just tried it and well… everything worked!
To further elaborate, it actually loaded faster and in some cases worked better than the Flash 7 version (e.g. the mouseover events worked and thus you didn’t have to keep clicking to paint tiles).
I guess it goes to show that one can never always assume that something is not going to work. And also, you can indeed use OpenLaszlo’s powerful data binding features to make a map editor for Limitless Quest.


Add New Comment
Thanks. Your comment is awaiting approval by a moderator.
Do you already have an account? Log in and claim this comment.
Add New Comment
Trackbacks
(Trackback URL)