t3cartographer

所属分类:GIS/地图编程
开发工具:Perl
文件大小:13861KB
下载次数:0
上传日期:2023-05-10 19:28:28
上 传 者sh-1993
说明:  一个用于Tads3编程语言的库,该库从Tads3对象创建游戏中的图形地图,并输出i...
(A library for the Tads3 programming language that creates an in-game graphical map from a tads3 objects and outputs it in either ascii, svg, graphviz dot or html.)

文件列表:
LICENSE (7048, 2023-05-11)
cartographer-all.tl (315, 2023-05-11)
cartographer.h (640, 2023-05-11)
cartographer.tl (150, 2023-05-11)
cartographer_modid.t (409, 2023-05-11)
examples (0, 2023-05-11)
examples\example1 (0, 2023-05-11)
examples\example1\GameInfo.txt (582, 2023-05-11)
examples\example1\Makefile.t3m (599, 2023-05-11)
examples\example1\example1.t (7898, 2023-05-11)
examples\example1\images (0, 2023-05-11)
examples\example1\images\blank.png (486, 2023-05-11)
examples\example1\images\e_w.png (1333, 2023-05-11)
examples\example1\images\n_s.png (1381, 2023-05-11)
examples\example1\images\ne_sw.png (1363, 2023-05-11)
examples\example1\images\nw_se.png (1170, 2023-05-11)
examples\example1\mapdata.svg (5556, 2023-05-11)
examples\example1\mapdata.txt (1173, 2023-05-11)
examples\example1\test.txt (136, 2023-05-11)
examples\example2 (0, 2023-05-11)
examples\example2\GameInfo.txt (398, 2023-05-11)
examples\example2\Makefile-web.t3m (1435, 2023-05-11)
examples\example2\Makefile.t3m (1467, 2023-05-11)
examples\example2\compileAndRunWebUI.sh (68, 2023-05-11)
examples\example2\example2.t (2905, 2023-05-11)
examples\example2\example2.t3 (1291114, 2023-05-11)
examples\example2\images (0, 2023-05-11)
examples\example2\images\blank.png (486, 2023-05-11)
examples\example2\images\e_w.png (1333, 2023-05-11)
examples\example2\images\n_s.png (1381, 2023-05-11)
examples\example2\images\ne_sw.png (1363, 2023-05-11)
examples\example2\images\nw_se.png (1170, 2023-05-11)
examples\example2\mapdata.html (4206, 2023-05-11)
examples\example2\mapdata.svg (831, 2023-05-11)
... ...

# Tads3 Cartographer library # version 0.7 - beta [![Demonstration](https://github.com/toerob/t3cartographer/blob/master/./examples/screenshots/demo.gif)](https://github.com/toerob/t3cartographer/blob/master/./examples/screenshots/demo.gif) **fig.1** ### What is it The Tads3 Cartographer library is tool that generates a map from your Tads3 source code that can be used either as an aid when developing your game or as an in-game map for the player. It supports various output formats. Depending on your solution and framework it can be a map in svg, html, dot-graphics or plain old ascii. At the bare minimum you just need to import it and supply a map command to get something to render on your screen or to a file in game directory. In your Makefile add: ``` -lib ../cartographer -source ../renderers/scalable-text-renderer -source ../renderers/svg-renderer ``` In your source code, add somewhere: ``` DefineIAction(Map) execAction(cmd) { // Figure out the room's coordinates based on their direction between each other local roomCrawler = new RoomCrawler(); local rooms = roomCrawler.crawl(gPlayerChar.location); // (Optionally) // Render and store an svg map to local file local tilemap = new SvgTileMap(roomCrawler.maxX, roomCrawler.maxY); tilemap.populate(rooms); local svgMapStr = tilemap.render(); saveStringToFile(svgMapStr, 'mapdata.svg'); "Map generated to file mapdata.svg in current folder\n"; // (Optionally) // Render and display an in-game ascii map local textMap = new ScalableTextTileMap(roomCrawler.maxX, roomCrawler.maxY); textMap.populate(rooms); "<>"; } ; ``` for a minimum example implementation. Then you can fine tweak the rest (per room via optional properties that overrides the defaults). If you now compile your game and type "map" a file will be produced, like this: Fig.1 And in-game map like this: fig.2 (See example 1 for the code that produces the images above.) *The SVG-renderer* (fig.1) can be suitable for a game running with the webgui while the *HTML renderer* might be more suitable for a game running locally with the HTML tads interpreter since it produces a table that could easily be rendered in QTads or similar HTML supportive interpreters. I would though recommend the *WebUI* instead since it has more advantages in rendering details. If you want to stay true with the textformat (and perhaps get a mud revival feeling), you might want to use the two pure text-renderers which can output the map in text-format in any interpreter, including the text-only ones. The two choices here are to either go with the ScalableTextTileMap that displays connections and doors, where the output looks like this: ### ScalableTextTileMap fig.3 Or go with the compact looking TextTileMap looking like fig.4 In adv3Lite there's also an exemplae on how to implement the map together with a menu (similar to the help menu), using the banner api to display the map (see example1 in examples directory) and navigate it with the WASD keys. (see fig.2) ### TextTileMap ``` Exits: north southeast in ============================================================ Th meadow LR Th Ha Ki Th *Po* On Lo Lo On ============================================================ Legend Po = porch On = on the east side of Lo = long road east end Th = the garden south meadow = on the meadow Ha = hallway Ki = kitchen LR = living room La = landing Ba = bathroom Be = bedroom ``` **fig.4** ----------------------- ### HTML renderer: The HTML maps looks like this: **fig.5** ### WebUI: And the a very basic usage of the WebUI toghether with the svg-renderer, looks like this: **fig.6** # Basic instructions on how to use the library ## Using the RoomCrawler class Before any rooms can be displayed you need to "crawl" them, which essentially means that the rooms will be iterated recursively from any starting point (preferably the player's location), and each and every room connected will be given a (x, y, z) coordinate. The coordinate system has its origin in 0,0 and grows (eastward) in x-axis and (southwise) in y-axis, e.g: ``` 0 1 2 3 +--------> x 0 | 1 | 2 | y ``` Not depicted here the z-axis goes up positive, and down negative That means the coordinate {x:2,y:2,z:0} has its north neighbour room to in {x:2,y:1,z:0}, its southeast in {x:3,y:3,z:0} and its downward neighbour in {x:2,y:2,z:-1}. The "crawling" is made with the following code: ``` local roomCrawler = new RoomCrawler(); local rooms = roomCrawler.crawl(gPlayerChar.location, true); ``` The first parameter sets the starting point to crawl from, and the second boolean type parameter is a flag to use if you only want visited rooms to be crawled through. It is optional, so you can equally well type: ``` local rooms = roomCrawler.crawl(gPlayerChar.location); ``` ## Select a renderer to output the map Next up is to decide on a renderer. if you like to go with SVG, use SvgTileMap: ``` local svgTileMap = new SvgTileMap(roomCrawler.maxX, roomCrawler.maxY); svgTileMap.populate(rooms); saveStringToFile(svgTileMap.render(), 'map.svg'); ``` This will render a complete svg formatted file (map.svg) in the folder of the compiled game that you can do whatever you want with. (Optionally you can print the svg directly in the game but that will require it running in webui for the svg elements to render correctly, see the examples for inspiration on how to do that.) The two parameters where we use the values of roomCrawler.maxX, respectively roomCrawler.maxY is unit number that will be converted to pixels depending on width, height and padding settings in the SvgTileMap. The variables maxX and maxY here means to how many tiles x-wise and y-wise have been counted to during the crawling process. So when we create a tilemap we know how big that tilemap must be in order to make room for all rooms.) Each tile's width and height have default values so you don't need to do anything to them but if you want to tweak them, that's easily done. You can either extend the constructor call with two additional parameters, ``` local svgTileMap = new SvgTileMap(roomCrawler.maxX, roomCrawler.maxY, 120, 80); svgTileMap.populate(rooms); ``` or just set them directly afterwards: ``` local svgmap = new SvgTileMap(roomCrawler.maxX, roomCrawler.maxY); svgmap.tileWidth = 120 svgmap.tileHeight = 80 ``` If you like to go with HTML, instead of SvgTileMap, use HtmlTileMap: ``` local htmlMap = new HtmlTileMap(roomCrawler.maxX,roomCrawler.maxY); htmlMap.populate(rooms); ``` HtmlTileMap hasn't yet received as much love as SvgTileMap, so it's less tweakable, but it isn't easily done anyway considering Tads only supports HTML 3.2 tables without styling, (but at least the borders are hidden.) The same could be said about the support for the graphviz dot, format which is acquired via the renderGraphvizDotMap function. It is still not fully functional, and very primitive done compared to the SvgTileMap. ``` local dotMap = renderGraphvizDotMap(rooms); ``` The ScalableTextTileMap and TextTileMap renderer will render a textbased map as seen in fig.3 and fig.4: ``` local map = new TextTileMap(roomCrawler.maxX,roomCrawler.maxY); map.populate(rooms); ``` The TextTileMap is very simple and doesn't show any room connnections. It is more compact than the ScalableTextTileMap though. ``` local map = new ScalableTextTileMap(roomCrawler.maxX,roomCrawler.maxY); map.populate(rooms); ``` The scalableTextTileMap is showing connections between each room and doors. when you are done rendering with either choice of renderer, you can either display it directly to the screen or save it to a file for external viewing. ``` saveStringToFile(dotMap, 'mapdata.gv'); saveStringToFile(htmlMap, 'mapdata.html'); saveStringToFile(svgTileMap, 'svgmap.gv'); ``` See example1 in examples for a complete example. ## keeping the result of a crawl and using renderPredicates (SvgTileMap) To be bit more efficient about the rendering of the rooms, it can be a good idea to crawl all the rooms first, store the result in a list, create a tilemap with that result and just re-render that map instead of redoing the whole procedure each time. However if the roomCrawler should visit all rooms no matter if they have been visited or not, then the tilemap will have to take over that responsibility. SvgTileMap.renderPredicates therefore holds a list (empty as default) of predicates that can be set to condition what rooms (and its outgoing paths) should be rendered or not. A convenient predicate to display only visited rooms is statically declared inside the SvgTileMap called "SvgTileMap.renderOnlyVisitedRooms" , which itself is equivalent to: "{room: libGlobal.playerChar.hasSeen(room)}" To add it just type: ``` svgTileMap.renderPredicates = [SvgTileMap.renderOnlyVisitedRooms]; ``` One solution to keep the crawling result could look like the following: ``` mapManager: object roomCrawler = nil rooms = nil svgTileMap = nil initMap() { roomCrawler = new RoomCrawler(); rooms = roomCrawler.crawl(gPlayerChar.location); svgTileMap = new SvgTileMap(roomCrawler.maxX, roomCrawler.maxY, 70, 50); svgTileMap.renderPredicates = [SvgTileMap.renderOnlyVisitedRooms]; svgTileMap.populate(rooms); } drawMap() { "<>"; } ; ``` Now you can just call mapManager.initMap() at the beginning of the game, and then call drawMap() each time you want to render it. ## Handling more complex maps In a perfect world the alignments of the rooms would always be perfect and the game mapping engine would of course figure out exactly how you intended things in your imagination to end up on the screen, right? The cartographer library cannot draw many such conclusions (yet). Rather it assumes that you won't put rooms next to each other and still make them go cross each other. However in order to overcome this restriction, there are a few tools at your disposal. One is **the map distance length table** (mapDistanceLengthTable) which is set per room basis if needed. Another is **map regions** (an array of arrays being setup in RoomCrawler.setupMapRegions if needed) If you want almost full control there's also: **overrideMapCellProperties**, **pathConnectionTable**, **mapCoordsOverride**, **mapConnectionShape**, **svgGfx**, and **overrideMapCellConstruction** (All set per room basis). ## overrideMapCellProperties The **overrideMapCellProperties** is a LookupTable which supports the following properties for the SVG-renderer, letting you override width, height, and the x/y-offset of the tile per pixel value: ``` overrideMapCellProperties = [ 'shape'->'ellipse', 'width' -> 80, 'height' -> 120, 'offsetX' -> 20, 'offsetY' -> 20 ] ``` This is helpful if wanting to depict room shapes of different sizes. ## mapConnectionShape mapConnectionShape is a hook that lets you override how the connections between the tiles appears on the screen. The default behaviour is to draw a simple line from x1,y1 to x2,y2 where the first group of coordinates (x1,y1) is the default origin for the particular edge in question. e.g a north exit will be in the middle of the upper part of the tile, and a southeast will be at the bottom right corner of the tile. The second group (x2,y2) is the equivalent for the connecting room. Therefore the default behavour is the equivalent of overriding the mapConnectionShape on a room like this: ``` exampleRoom: Room mapConnectionShape(edges, cell, adjacentCell, exitProp, adjacentRoomExitProp) { return createLine(edges[1], edges[2], edges[3], edges[4]); } ; ``` Where the createLine is simply a helper method (that also applies a current theme to the attributes and style of the line if none is provided as optional arguments) for creating a svg line element. You could equally well type: ``` exampleRoom: Room mapConnectionShape(edges, cell, adjacentCell, exitProp, adjacentRoomExitProp) { return ' '; } // And for all other directions, draw a regular line: return createLine(e[1], e[2], e[3], e[4]); } ``` (See example 3 for a demonstration of the code above.) Note: mapConnectionShape always expects a string value as return value. ## svgGfx **svgGfx** Let's us tap into the specific coordinates of a cell to draw some extra cell graphics if wanted. It has four parameters, x,y, width and height which gives us the cells's current position and form values: Example usage: ``` room: Room svgGfx(x,y,w,h) { return ''; } ; ``` ## overrideMapCellConstruction In case the shapes available in how the room should be depicted by default isn't satifying enough, you can override the cell construction itself via overrideMapCellConstruction method hook per room. Example usage: ``` room: Room overrideMapCellConstruction(cell, name, x, y, width, height, isPlayerTile) local s = new StringBuffer(); local style = isPlayerTile? gMapThemes.getPlayerRectStyle() : nil; local cellCenterX = x+cell.getWidth()/2; local cellCenterY = y+(cell.getHeight()/2) + 3; s.append(createRect(x,y, width, height, style, attributes)); s.append(createMiddleCentedText(cellCenterX,cellCenterY, name)); return s; } ; ``` This example just creates the default rectangle. But go crazy here if you like. ## mapDistanceLengthTable The mapDistanceLengthTable is a LookupTable that will connect a direction from the room with a number of specified tiles away from it. (If the mapDistanceLengthTable is not defined the assumption of one tile will be drawn). But if it is there and say connecting the &north direction with 2 tiles in length, the room that connects with it using that direction will be drawn two tiles north. Make sure though to mirror this in the north room, otherwise it will be drawn with a gap when standing north of it, and only look good when standing south of it. So in the opposite room, add a mapDistanceLengthTable = [ &south -> 2 ]. Like in this example: ``` backyard: Room 'The back yard' south = sideofhouse north = meadow northwest = gardenAreaSouth // Mirroring meadow's mapDistanceLengthTable mapDistanceLengthTable = [ &north -> 2 ] ; meadow: Room 'On the meadow' mapName = 'meadow' south = backyard west = gardenAreaNorth // Mirroring backyard's mapDistanceLengthTable mapDistanceLengthTable = [ &south -> 2 ] ; ``` So now instead of having this: **fig.7** You can have the following: **fig.8** It works in all directions with any distance set. Take note: when using the mapDistanceLengthTable numbers are always positive. It doesn't matter if the direction is north or south, east or west. When you use the mapCoordsOverride (described further ahead) numbers will be set as an offset in the cartesian space. Which means that if expressing something north of a room, it will be negative. And south of a room positive. Vice versa for east(1) and west(-1). More on mapCoordsOverride later. ## Map regions Another tool in the toolbox are map regions. The concept of map regions is that it will only render regions where the player is currently at. Example of usage: ``` roomCrawler.setupMapRegions([ [porch, driveway, sideofhouse, backyard, gardenAreaNorth, gardenAreaSouth, meadow], [hallway, livingroom, kitchen], [landing, upperbathroom, bedroom] ]); ``` Say for example that the player is within the first region, in the 'backyard'. This will cause the only rooms to render on the screen to be: "porch, driveway, sideofhouse, backyard, gardenAreaNorth, gardenAreaSouth, meadow" and all the other rooms will be omitted. That is until the player enters another region. As things stand right now, if there's a room that's outside all of the specified rooms ALL rooms will be rendered when standing in that tile. This might change in the future. Also, concering rooms that are not on the same level, they will not be rendered together. Instead you will see an entirely new map. (Compare fig.2 and fig.3) If there's a location connected with an 'in' direction, that room will also be rendered in isolation. ## pathConnectionTable The idea of the pathConnectionTable is to provide an override hook for each room's connection to another room. Sometimes we don't want the line connection the rooms to be mirrored in a north <-> south manner, but rather want for instance the north exit from a room connect to the east exit on the adjacent room. The default locations where these connections attaches on each tile might cause us pathways that looks like they do in fig.9: **fig.9** We can change the defaults by specifying a pathConnectionTable with the tilemap edges we wish to associate with each other for this specific room, e.g: ``` pathConnectionTable = [&north -> &east, &south -> &east] ``` In the above example the ... ...

近期下载者

相关文件


收藏者