0 votes
by (250 points)
edited by

(Using downloaded Twine 2.2.1 story format SugarCube.21)

Hello everyone! I wish to design a dungeon crawler of sorts, complete with an inventory (with consumable items, not just key based), descriptive rooms, encounters, money system, shops, and all that. Now of course I am trying to take it one step at a time, but I am having difficulty figuring out how to get the map itself set up.

I have followed and am using the example Dungeon Crawler shown by Dan Cox here (proof copy here - as listed by Dan in the video description). I have made only a slight change, in that I created another passage called "Path Check" to handle the positions able to be moved, and included it in the map system, as per a suggestion to a question I asked.

The problem with this setup is I can't imagine a way, using the map layout how it is currently, to make each room more unique. I wish each room to have its own description to display (and possibly clickable words that append or do other things), as well as have their own version of the cardinal direction links. For example I don't want every room that has an east exit to simply display "East" in the bottom, I want it to be related to the description, sort of like the possible options they have. One might say "take the mossy passage", and another one "head up the stairs".

It has been a while since I attempted this in Java, but if i recall, I made an xml sheet, and used id's and attributes to better have this data in one place and simply refer to it, and pull the information from a document, not have to  type the information directly into the code.

I was wondering if something similar would be a wise choice here. I would like to avoid having to use Javascript if possible (mainly because I don't really remember any of it I learned in college, haha). However I would prefer using it over creating each room as a separate twine passage. Though it seems twine would work really well with doing it in such a manner, I want it to feel a little more modular.

2 Answers

0 votes
by (159k points)
selected by
Best answer

One of the easiest ways to have a more descriptive (and unique) room for each location of your map is to associate a Passage with each of those locations, and one simple method for doing that is by giving each of those passages a generic name based on the X & Y coordinates of the location.

eg. The room at the map location 1:1 (row:column) could be called "Room 1:1", the one at 3:2 is "Room 3:2" as so forth.

The following TWEE Notation is a modified version of Dan's original example, I have renamed some of the variables & passages and changed it to use PassageHeader & PassageFooter special passages to contain the drawn map & direction links respectively. I have also moved the player's current X & Y position variables into a new $player object, as it was likely you would need one if you are building a RPG like game.

warning: I have only created "Room Y:X" related passages for the first four locations in the map array!

:: StoryTitle
Moving through a descriptive dungeon for SugarCube

:: UserStylesheet[stylesheet]
#map {
    font-family: monospace; 

:: StoryInit
	Initialise the map.
	0 - A wall;  1 - A Room;  2 - The Exit.
<<set $map to [

<<set $player to {
	name : "Jane",
	X    : 1,
	Y    : 1

:: PassageHeader [nobr]
<<if not tags().includes('no-map')>>
	<<include "Draw Map">>

:: PassageFooter [nobr]
<<if not tags().includes('no-map')>>
	<<include "Show Directions">>

:: Start [no-map]
Introduction to story.

[[Continue|Room 1:1]]

:: Draw Map [nobr]
<span id="map">
<<for $i to 0; $i lt $map.length; $i++>>
    <<for $k to 0; $k lt $map[$i].length; $k++>>
        <<if $k eq $player.X and $i eq $player.Y>>
            <<print "P">> 
        <<elseif $map[$i][$k] eq 1>>
            <<print ".">> 
        <<elseif $map[$i][$k] eq 0>>
            <<print "#">> 
        <<elseif $map[$i][$k] eq 2>>
            <<print "E">> 
    <<print "<br>">>

:: Show Directions [nobr]
	<<set _exits to []>>

	/* Check North. */
	<<set _type to $map[$player.Y - 1][$player.X]>>
	<<if _type eq 1>>
		<<set _room to "Room " + ($player.Y - 1) + ":" + $player.X>>
		<<set _exits.push("[[North|" + _room + "][$player.Y -= 1]]")>>
	<<elseif _type eq 2>>
		<<set _exits.push("[[Exit]]")>>

	/* Check East. */
	<<set _type to $map[$player.Y][$player.X + 1]>>
	<<if _type eq 1>>
		<<set _room to "Room " + $player.Y + ":" + ($player.X + 1)>>
		<<set _exits.push("[[East|" + _room + "][$player.X += 1]]")>>
	<<elseif _type eq 2>>
		<<set _exits.push("[[Exit]]")>>

	/* Check South. */
	<<set _type to $map[$player.Y + 1][$player.X]>>
	<<if _type eq 1>>
		<<set _room to "Room " + ($player.Y + 1) + ":" + $player.X>>
		<<set _exits.push("[[South|" + _room + "][$player.Y += 1]]")>>
	<<elseif _type eq 2>>
		<<set _exits.push("[[Exit]]")>>

	/* Check West. */
	<<set _type to $map[$player.Y][$player.X - 1]>>
	<<if _type eq 1>>
		<<set _room to "Room " + $player.Y + ":" + ($player.X - 1)>>
		<<set _exits.push("[[West|" + _room + "][$player.X -= 1]]")>>
	<<elseif _type eq 2>>
		<<set _exits.push("[[Exit]]")>>
<span id="directions"><<print _exits.join(" | ")>></span>

:: Exit
You made it to the exit.

:: Room 1:1
Description for <<= passage()>>.

:: Room 1:2
Description for <<= passage()>>.

:: Room 1:3
Description for <<= passage()>>.

:: Room 2:3
Description for <<= passage()>>.

Let me know If you need a further explanation of any of the above functionality.

by (250 points)
edited by

Yes thank you, this looks closer to how I wanted things set up. I wanted the importance of the map numbers to remain. However I am unsure what "Twee" is. It took me a little bit of looking but I think this is it here? If it isn't couldn't you point me in a direction to read and learn about it please? as it stands I have attempted to install and use twee2. I have set up the configuration to use SugarCube2. However when I turn my file into an html document and load it, it seems the include and linkreplace macros are not recognized. I would not think any of these versions are too old as to not recognize them, so perhaps my tiredness is getting the better of me. I notice in your example you are using include, so it must be an error on my part.


0 votes
by (44.7k points)

Well, you're kind of tying one hand behind your back if you aren't going to take advantage of Twine passages or JavaScript, but there's still a fairly simple way to do what you want.

Use the numbers in the map array (or strings might be even better, since you're less likely to mix them up) to indicate what description you want to use.  So if it's a 5, then you could display the text that it's a "hallway apparently carved into the stone", and 6 could be a "large room lined with ugly dwarven statues", etc.  Then you could use a SugarCube widget to display the correct description.

For example, create a passage for widgets (call it "Widgets" or something, the name doesn't matter) and add the tags "widget" and "nobr" (the latter will prevent unnecessary line breaks).  Then put something like this in it:

<<widget "Description">>
	<<switch $args[0]>>
		<<case 5>>
			You stand in a hallway apparently carved into the stone.
		<<case 6>>
			You're in a large room lined with ugly dwarven statues.

Then all you'd need to do is "<<Description $mapArray[$i][$k]>>" and it will display the correct description based on the value in that part of the map array.  (It doesn't have to be a single line of text either, you could make it paragraphs and include links or other code if you want to.)  You also could, for example, make another widget if you want to describe each of the room's exits based on the room it leads to.

If you want to make the room's description really elaborate, but you don't want to clutter up the widget with text, you could write the text for each room in a passage, and then just use the <<include "PassageName">> macro inside the widget, which will insert the text from that passage there.  That way it's still modular, but you're also taking advantage of how Twine's passages work.

For more information see also the <<switch>> macro.

Hope that helps!  smiley

by (250 points)
Yes, however I think tying my hand behind my back sometimes is what makes it an interesting and enjoyable experience, haha!. That's interesting! To be honest I forgot all about using widgets, I'll have to keep those in mind when I am creating a few other things, including the inventory system.

I like that, but that will break the way the player moves through the maps currently won't it? As per your suggestion, that will break the map from indicating if that area is an area that can be moved to, given the current player location, yes?

For example, say we have a map, given the rules shown in the links in my original post:

0 0 0 0 0  
0 1 0 0 0
0 1 1 0 0
0 0 1 1 0

your suggestion would have me edit them to say something like:
0 0 0 0 0
0 1 0 0 0
0 2 3 0 0
0 0 4 5 0

yes? which would break any common ground to know the passage.

thinking on it I suppose I could test for non-zero numbers yes? but still this convention doesnt allow a singular number to represent a singular meaning. Thinking ahead of myself, then how would I indicate a locked door room, or as in Dan's example, the 2, the exit room?

I suppose I could implement those using more widgets?

You also could, for example, make another widget if you want to describe each of the room's exits based on the room it leads to.

I suppose perhaps that is what you meant when you said "You also could, for example, make another widget if you want to describe each of the room's exits based on the room it leads to."?

Either way, that gives me a good bit of insight! thanks.

Also I am not entirely opposed to javascript, I just don't want to have to get someone to explain every bit to me in baby steps, and possibly have me flooding people with questions, I feel bad when I have to ask a bunch of questions in a row. I feel certain the things I have asked had to have been answered somewhere else and that I'm cluttering the place for people who have good questions, lol.
by (44.7k points)
edited by

Yeah, you'd have to edit the original code to handle the other numbers, but that shouldn't be too hard.  If you keep 0 for walls, then you can actually use the map values as "truthy" or "falsy" values to determine open spaces.  For example:

<<if $mapArray[$positionY-1][$positionX]>>There's an exit to the north.<</if>>

will work, because any number other than 0 is basically the same as "true" in a conditional statement (like an "if"), while 0 is basically the same as "false".  So that will only display that text if there's not a wall (0) there.

As far as what the numbers mean and do, well, that's all up to you.  You write the code, so you can write it so that 2 is the exit, but 3 can represent a cave-in that propels you into the next room and seals that path behind you.  You'd just have to modify the code so that's what 3 does.

And yeah, another widget to describe the exits would be good.  This code is kind of clunky:

<<if $mapArray[$positionY-1][$positionX] eq 1>>
[[North]] | 
<<elseif $mapArray[$positionY-1][$positionX] eq 2>>
[[Exit]] | 

So you could replace that with something like this:

<<DescribeExit $mapArray[$positionY-1][$positionX] "North">>

and have a "DescribeExit" widget like this:

<<widget "DescribeExit">>
	<<switch $args[0]>>
		<<case 2>>
			<<set _Txt = $args[1] + " Exit">>
		<<case 4>>
			<<set _Txt = "Stairs to the " + $args[1]>>
	[[_Txt|$args[1]]] | 

(Note: When you first add the above code it will cause a passage titled "$args[1" to be created.  You'll have to delete that passage to get this code to work properly.  Don't worry about the line going to a red X which will appear after you do that; that's just Twine failing to recognize that the link is a variable, instead of a fixed passage name.)

Just add another case for each type of exit you want and set the "_Txt" variable to how you want that exit's name to appear.  This way you can have all sorts of different exits.

Hope that helps! smiley