How do you organize your pages in gamebook?

I have recently started using gamebook and it is much easier except for 1 factor - the pages.
When you create a main path storyline up to 50 pages,
and then now you want to fork the paths at page 10 and page 40,
unfortunately your new pages will be called page 51, which is confusing when you are rechecking/reediting
everything.

It is possible to rename the new page as page 10b, 10b1, 10b2 and so on and slot into page 10 treating it as a folder,
but honestly, it looks messy too and when you open up quest, there will be many pages in an annoying side branching tree, rather than the usual page 1 to page 50 which does not branch weirdly.

So just a question,
how do you organize your pages in gamebook?


I think it's personal preference. I prefer to give pages names, so I can easily find a particular part of the story without having to remember what number it was. Really, giving them numbers is more of a tradition from actual printed gamebooks.

(And now I've distracted myself onto a weird tangent… wondering whether some players like to see "Page 328" at the top of a section; and if so, whether you could use a script to generate them, while still allowing the use of descriptive names to make editing easier.

Click to reveal silly code to automatically replace ?#? in page text or descriptions with a page number Something like:

(now edited because I forgot to account for the player - this now assigns the number "1" to the page that the player starts on, whether or not it is the first page; and numbers all other pages sequentially in the order they appear in the editor)

pagecount = 1
if (GetObject("player") = null) {
  player = game
  pagecount = 0
}
foreach (page, AllObjects()) {
  if (page = player.parent) {
    page.pagenumber = "1"
  }
  else if (not page = player and not HasString (page, "pagenumber")) {
    pagecount = pagecount + 1
    page.pagenumber = "" + pagecount
  }
}
foreach (page, AllObjects()) {
  if (HasString (page, "description") and HasString (page, "pagenumber")) {
    page.description = Replace (page.description, "?#?", page.pagenumber)
  }
  if (HasAttribute (page, "options")) {
    new_options = NewStringDictionary()
    foreach (option, page.options) {
      number = "???"
      target = GetObject (option)
      link = DictionaryItem (page.options, option)
      if (Instr (link, "?#?") > 0 and not target = null and HasString (target, "pagenumber")) {
        link = Replace (link, "?#?", target.pagenumber)
      }
      dictionary add (new_options, option, link)
    }
    page.options = new_options
  }
}

(presumably run once, at the start of the game. In page text, ?#? will be replaced with the number of that page; in links, it will be replaced by the number of the page it points to)


K02 Page Organizer by Mrangel

Just for encouraging other game creators, I am interested in non-coding ways of how people organize their pages in gamebook as well.

So for people that do not understanding coding, I have spent some time investigating into how mrangel’s coding work.

Basically Mrangel’s coding helps to display the page number the player is current at.

  1. Write ?#? In page description.
  2. Copy and paste code, this code only needs to run once.
  3. The code replaces all ?#? Into the correct page number.

Errors

  1. You might see that the page is 5 on page 4, basically the code is +1 page ahead. Simply adjust mrangel’s coding at line 1 [pagecount = -1]
  2. You might see that the page number is not appearing. Make you sure you trigger code first before everything else happens, for example, placing the code on page1 is a good way to ensure the code does happens.
  3. You are a smart alex and you wrote ?#? “On script when entering page”, so the code triggers on every page. Do not worry, I did this too and it does not works, it might be because that mrangel’s coding changes ?#? From all existing words, whereas the smart alex write ?#? “On script when entering page” does not happens yet, it happens in the future when the player presses next page.
  4. You prefer “page 20” instead of “20”. Simply write "page #?#" on every page’s description, mrangel’s coding only replaces #?#, every other words stays intact, in fact, this can be used as an interesting random dungeon generator coding like changing the monster’s name or stats or player’s position in maze.

Foxrain4’s preference
Generally the coding is useful to edit 1 page that have errors so it is not useful for foxrain4.
Generally foxrain4 edit all pages because of

  1. Changing 1 game rule.
  2. Changing the story, the following story have to be consistent.
  3. Improving the english and readability, my main language is moon language. (Joke.)
    But generally, mrangel’s coding that only runs once but is able to change all pages represents potential advanced coding to lessen the hardwork and typing game content required from all game creators.

For example: The game is now night time, so running the script can change all pages text into “Night” or “Uninteractable”, and when the player do more stuffs, the script gets triggered again, now all pages text are turned into “Day” or “Interactable”.
The same can be done for shrunken player, normal sized player, giant sized player.
The same can be done for summoned rpg character, swordsman, archer, mage, thief.

So now into the hardest part, understanding the code.
Part 1
Firstly, mrangel’s coding is fully working.
Go into page 1 of gamebook.
At Page tab, at Page type:, change it into Script + Text.
There are 8 buttons right below, click the seventh one, it is code view.
Copy and paste mrangel’s coding into the textbox.
Click the seventh button again, viola, the coding is done.

Part 2
Pagecount = 0
//Strange choice because variable lasts temporarily only whereas attributes last forever, maybe this means the code can only run one time. Attributes is like game.Pagecount = 0

Foreach (page, Allobjects()) {
Pagecount = pagecount + 1
Page.pagenumber = “ “ + pagecount
//From wikipedia, foreach is similar to for loop. And if you are as funny as me, you will not know for loop as well. For loop means to do this multiple times whereas foreach means to do this multiple times from this folder.
//So basically the folder would refer to AllObjects(), it will be hard to determine what AllObjects() is, so we shall assume it refers to all pages, because that is what the gamebook have… pages.
//So inside the foreach loop, we can see a calculation which basically adds 1 whenever the loop runs.
//So, now we have our first attribute = page.pagenumber, things is going to be clearer now since foxrain4 have always messed around with attributes.
//Due to experience “ “ simply means a text which * valids* the following * integer* code of pagecount (Variable).
//Now we can see why mrangel used a variable, because pagecount is an unimportant value that changes all the time and the important result output is already placed in page.pagenumber (attribute).

Part 3
Foreach (page, AllObjects()){
If (Hasstring (page, “description”)){
Page.description = Replace (page.description, “?#?”, page.pagenumber)
}
//So hasstring is another advanced code that I do not know, basically google says hasstring is whether the textbox have this words or not. So why dun they just call it haswords?
//Basically the first code line finds all pages in AllObjects().
//Then the second code line finds inside those pages, whether they have word of “description”.
//If so, the third code line replaces all the page.description (attribute) from “?#?” into page.pagenumber (attribute which we found in earlier code.)
//So some of you are probably having a headache, why are the codes arranged in this specific priority order? Why the commas, why the =, why the brackets. The answer is writing the code have to depends on the programming language. So lets say you finally understand textadventure coding language, it does not means you know c programming or python, they all uses different structure, terms and word order priority, and this is probably why I stop messing with coding because my brain memory is smol.
//However, textadventure coding is simpler to other programming languages because it have two forms, one is script commands and the other is code view. The script commands adds a very good visual aid on what you can or cannot write, but basically I am too simple for that as well, next part!

Part 4
Okay, my brain officially died out.
After several tests.
And several tests.
More several tests.
And I finally understood it.

Basically, the last big pile chunk of mrangel’s coding is about displaying page number at the blue link options when you start up the game.

For this to work, you need to type ?#? Inside those blue link options.
For example:
Page____Link text
Page5 ____Page ?#?, find the medicine at the nearby market
Result: (When you start up the game.)
Page 5, find the medicine at the nearby market.

Part 5
If (hasattribute (page, “options”)){
New_options = NewStringDictionary()
//Basically any page that have the attribute options, we can assume it means the blue link options since there are no other options.
//Okay, newstringdictionary is too insane coding for me, I will just copy the meaning from the internet as it is.
//A dictionary is a data set where each entry is accessed by a string, called a key. If you think about an actual dictionary, it contains words linked to definitions.

Foreach (option, page.options){
Number = “???”
Target = Getobject (option)
//Basically for all options (Blue link options) that have page.options.
//Variable number is ???
//Variable target is option

Part 6
Link = DictionaryItem (page.options, option)
if(Instr(link, “?#?”) > 0 and not target = null){
Link = Replace (link, “?#?”, target.pagenumber)
//Variable link is option which is found in page.options which is found in DictionaryItem.
//INSTR() function returns the position of the first occurrence of a string in another string.
//Okay my brain is toast, I am going to randomly guess.
//As long as ?#? Is more than 0 and that the variable target is not 0.
//Then variable link is equals to object link but replacing ?#? With target.pagenumber.

Dictionary add (new_options, option, link)
}
Page.options = new_options
//So inside the dictionary coding, which is preprogrammed, tell the dictionary to add new options but replaces the option with the new link (Variable which we just found previously which is target.pagenumber.)
//Inside page.options attributes, we changes them into new_options which is from dictionary which we just added.

Part7
Yeah, too hard for me, but I might be making an automated fighting rpg game someday, so I need to ease my future learning by being familiar with it and doing it in a sparse manner so as not to burnout myself.


Yeah… I made a mistake :p

In that silly script, I assumed that AllObjects() would give a list of pages. I forgot that in gamebook mode it gives a list which includes all pages and the player.
I edited it now, so it won't assign a number to the player.

So… I can put "Page ?#?" at the top of a page's text, to display the page number. And I can write "If you drink the potion, turn to page ?#?" as a link.
And then even if my pages have names like "castle_entrance", "dungeon_pick_lock", and "king_speech_4" (so that I can look at the list in the editor and immediately know what happens on each page), it will give each page a number when the game starts, to give the old-school gamebook feel.

If you wanted to automatically put the pagenumber at the top of every page, you could go to the "Scripts" tab of the game, and change the roomenter script to do:

if (HasString (player.parent, "pagenumber")) {
  msg ("<b>Page " + player.parent.pagenumber + "</b>")
}

After you changed the script, it stopped working.
It now shows on page 1

Error running script: Error compiling expression 'Replace (page.description, "?#?", page.pagenumber)': FunctionCallElement: Could find not function 'Replace(String, String, Object)'

But strangely when combined with the new roomenter script, the script starts working but it still shows the error on page 1. I am saying this to help you faster narrow down the possible bug or typo if you are interested in fixing it.

I like your previous code, just changing it to -1 on code line 1, and everything works simply.


I should have seen that bug coming.

Originally it was assigning a page number to the player, meaning that the numbers had one missing.

I changed it so that it skips the player. But… the script that replaces ?#? with the page number was still running for the player, and failing because the player doesn't have a page number.

I think I fixed that now. Sorry, I wasn't thinking so clearly.


Anyway I was talking about mrangel's page organizer code can automatically generate room descriptions, most likely it revolves around the replace function which I failed to understand or create one.

But I have a simpler coding right here, if you want an automatic room description, you can learn or use this code.
This code display page number, day/night, normal sized/ shrunk, rpg character summoned.

To copy this code

  1. Startup your quest gamebook, on the right side of the big play button, you can see a code view button </>
  2. Copy my code to replace the code in the text box, click code view button again.
  3. Viola, it is done, press play button to test out the game and modify the code to your preference.
<!--Saved by Quest 5.8.6836.13983-->
<asl version="580">
  <include ref="GamebookCore.aslx" />
  <game name="test">
    <gameid>4dda3e92-5adb-4a0a-8170-73acf1e50dd5</gameid>
    <version>1.0</version>
    <firstpublished>2024</firstpublished>
    <roomenter type="script">
    </roomenter>
  </game>
  <object name="Page1">
    <inherit name="scripttext" />
    <description><![CDATA[It is better to rob the rich at night.<br/>So please wait in your bed first.]]></description>
    <options type="stringdictionary">
      <item>
        <key>Page2</key>
        <value>sleep</value>
      </item>
    </options>
    <script type="script">
      Late enter page
    </script>
    <object name="player">
      <inherit name="defaultplayer" />
    </object>
  </object>
  <object name="Page2">
    <inherit name="scripttext" />
    <description><![CDATA[You need to reduce the possibility of being spotted.<br/>You are stealing items, not shopping.<br/><br/>What are you going to do?]]></description>
    <options type="stringdictionary">
      <item>
        <key>Page3</key>
        <value>shrink</value>
      </item>
    </options>
    <script type="script">
      game.time = "night"
      Late enter page
    </script>
  </object>
  <object name="Page3">
    <inherit name="scripttext" />
    <description>Perhaps there will be traps, summoning a heavy armour character can reduce possible trap damage.</description>
    <options type="stringdictionary">
      <item>
        <key>Page4</key>
        <value>swap to dwarf character</value>
      </item>
    </options>
    <script type="script">
      game.size = "shrunk"
      Late enter page
    </script>
  </object>
  <object name="Page4">
    <inherit name="scripttext" />
    <options type="stringdictionary">
      <item>
        <key>Page5</key>
        <value>sneak into the small gatehole (Requires shrunk size or any smaller size.)</value>
      </item>
    </options>
    <script type="script">
      game.character = "dwarf"
      Late enter page
    </script>
    <description><![CDATA[Yeah, you reached the treasure spot safely,<br/>underneath the rich man's mansion.<br/><br/>Nobody stays watch at night since they cannot see in the dark anyway.<br/>However, the entry is a bit too small, unless you had somehow reduced your size.]]></description>
  </object>
  <object name="Page5">
    <inherit name="scripttext" />
    <script type="script">
      Late enter page
    </script>
    <description><![CDATA[Oh no!<br/>It turns up to be a fire trap.<br/>Fire scorches onto your body.<br/><br/>You are dead unless you have a reliably strong armour.]]></description>
    <options type="stringdictionary">
      <item>
        <key>Page6</key>
        <value>Block with obsidian full set armour (Requires dwarf character.)</value>
      </item>
    </options>
  </object>
  <object name="Page6">
    <inherit name="scripttext" />
    <script type="script">
      Late enter page
    </script>
    <description><![CDATA[You have successfully robbed the rich.<br/>You receive golden sardines x 12.<br/>You receive lantern rifle x 4.<br/>You receive blessed statue x 1.<br/><br/>The alarm system is triggered.<br/>Musketmen charged towards you, fight or flee!]]></description>
    <options type="stringdictionary">
      <item>
        <key>Page7</key>
        <value>Escape in the dark (Requires night time.)</value>
      </item>
    </options>
  </object>
  <object name="Page7">
    <description><![CDATA[You escaped but heavily bloodied.<br/>You buried your treasures in your coffin with yourself in it.<br/><br/>After a few days when the guards stop searching, you come out of the coffin.<br/>Then, you went to faraway city market and sold all your precious and gain a whole new and rich life.<br/><br/>You reached the best possible ending, you are a genius!]]></description>
  </object>
  <function name="Late enter page">
    firsttime {
      game.time = "day"
      game.size = "standard"
      game.character = "archer"
    }
    msg (" "+player.parent)
    msg ("Time: "+game.time)
    msg ("Size: "+game.size)
    msg ("Character: "+game.character)
    msg ("")
  </function>
</asl>

… I spent about half an hour before bed writing a better version of this code, but didn't quite finish it.

Unfortunately, it seems that Chrome now has a feature where it automatically refreshes pages overnight or something.

I was just looking at actual old gamebooks, and realising that the page numbers it tells you to turn to are never nearby; and thought it would be interesting if you could have the pages organised in the editor, but spread out from the player's point of view.


From mrangel:
I prefer to give pages names, so I can easily find a particular part of the story without having to remember what number it was.
And then even if my pages have names like "castle_entrance", "dungeon_pick_lock", and "king_speech_4" (so that I can look at the list in the editor and immediately know what happens on each page),

From Daeun:
Unfortunately there seems to be no more people showing how they organize their pages, which actually might represents that major percentage of game creators just create pages as per normal instead of trying categorize or divide them.
So in total there are 5 ways of organizing pages.

  1. Create new page.
  2. Create new pages and slot them inside of other pages to categorize them.
  3. Go to Pages object shaped like a tab, you can push up or down the pages to organize them in a logical sequence manner. (However, this does not works for me most of the time, probably some glitch or whatever.)
  4. Create new page and give them a logical room name which is what mrangel stated.

So you might be wondering where is the fifth way, the answer is I haven't completed it yet, but I am writing down the theoretical part of it before I forget (Short-term memory).
So the fifth way of organizing pages is actually not to organize them, and to a even creepier extent, not to create more pages.
This is done by using the ideas and codes from four of my games:

  1. K03a Matrix double (2019)
  2. K03f Matrix Double Remake (2020)
  3. K03m Into the dark (2023)
  4. K03a v2 Matrix double (2019) (Work in progress)

Which the codes except for v2 can be found at:
https://textadventures.co.uk/forum/samples/topic/g4wdchoonkicauyfzjryww/k03-free-game-code-templates-2024

But why use the fifth way? Why code instead of creating pages as per normal?
Answer:

  1. Coding is easier to look at, you scroll up and down, creating pages requires you to click on that specific page to see what is in it.
  2. Coding needs less work, creating pages requires you to type in the hyperlink address and text name to what the next page leads to.
  3. If you created over 100 pages, you might start to feel lag and even scrolling up and down all the pages can feel laggy. But somehow if you write the pages as code, the quest app runs smooth like a printer paper.
  4. It is easier to edit all pages in a logical sequence manner if you are looking at codes, if you create pages and branch off more and more side stories, it will starts to need more and more work to walk through that specific story branch.

But of course, I am not encouraging you to do the fifth way, since working on code is a complicated thing if you are a stranger to coding. Another weakness is that you have to type out a) Enter the throne room b) Enter the kitchen for each of your pages which are close to your story text instead of beside the page choice text.

So how do I do the fifth way?

  1. Basically create page0 with 4 options a b c d, leading to page 1 2 3 4.
  2. Create 4 more options a b c d, leading to page 5 6 7 8.
  3. For page 1 to 8, put in a function called the main program.
  4. In the main program, you decide what happens to the player.
  5. On page 1 and 5, player.choice = a
    On page 2 and 6, player.choice = b
    On page 3 and 7, player.choice = c
    On page 4 and 8, player.choice = d
    This script must be placed before the function "main program".
    This script tells us which next page the player has chosen.
  6. In "main program", player.page = player.page +1
    This shows how far the player have progressed.
  7. Create another function in "main program" called "pages".
  8. In "pages", write out what happens to the player at the exact page number and choice the player has chosen.
    For example, if player.page = 3, if player.choice = a, print message "You walked into the throne room, instead of the king, you found a corpse, the guards entered and treated you as an assassin."
  9. This is of course not better than the default creating pages, which is why I am stuck in creating the code.
    To make it better: You need to be able to create a code in which the player can increase player.page heavily or reduce it heavily like player.page = player.page +5, this happens when the player finds a shortcut to the next chapter of the story, or if he winds up back at the start of the story. So, this represents another possible weakness, it is hard to visualize which pages goes where occassionally.
  10. The most difficult coding part would be the branching of story, all of this code actually only allows you to go one story path only, what if you have two simultaneous paths? At page 3, player can go to choice a, kill the guards, or player can go to choice b, point the direction of the assassin's exit path to the guards, this leads us to two page 4, and our code would have failed completely. So in short, you need an attribute called game.storybranch to allow you to create as many branches as you want to.
  11. So the final theoretical code should look like this:
    if player.page = 4, if game.storybranch = 1, if player.choice = a, print message "You have completely destroyed the guards and become king."

And yes, this code is quite hard to achieve, and which is why I just go back to create new page, the first way. It is currently the fastest way to scan through all the pages in the most logical sequence manner but it also means you cannot randomly insert new story branches at previous pages which will disrupt the whole walkthrough unless you click one by one "Go to that page" button in the quest editor, which kinda takes a long time.


Forgot to explain how my 9 pages works.
Basically page 0 is menu page.
Menu page is important as it links to page1 to page8, also it allows you create more pages like credits, difficulty mode selection and hint reveal page.

So how a normal game works would be.
page0 > page1, then player choose b > page6, then player choose a > page1, then player choose a > page5.
Inside each pages' stories, there would be information on what abcd leads to what player's action instead of writing the player's action at the next page choice which is now simply named as "a b c d"

So there is actually a possible error, in which you only have three player's actions.
The quick fix is actually quite simple, if player.page = 4, if game.storybranch = 1, if player.choice = d, run script, player.page = player.page -1 and print message "Nothing happens".

After some testing, I seems to be able to organize my page using the secret sixth and seventh way or combination of it which I shall call it the eighth way, I worked very hard for it, so I will be selfish and keep it for myself XD


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums