CPCWiki forum

General Category => Programming => Topic started by: enteka on 00:00, 10 June 21

Title: maximum size of an Amstrad program and... choices, choices
Post by: enteka on 00:00, 10 June 21
Below these lines, you may find the draft of my gamebook as it stands right now (not revised). However, it is growing fast10 K already, and I blame it on being, well a book. There's a lot of text, and there will be more.

So I first need to ask a question? How much is the practical maximum size for an Amstrad program?30K? 40K?
Secondly what can I do with text? The very first sections (aka "paragraphs") of a gamebook are usually longer
than average as they need to introduce the story, create the chacter, the world and all that. Further sections,will be much shorter.
My first thought was to do as I would do in a modern computer: just divide the text into as many files as neededand problem solved. However reading any file on an Amstrad takes some time and some slightly annoying
noises. I'd rather suffer that once.
Then I decided to go and type-in the bits of text directly on the code, storing it on a auxiliary table but that'smaking the gamebook file quite fatty.
I could mix both approaches too, some text to be directly on the program (perhaps the first few sections,perhaps the most often used -- a standard "you are dead, ha ha" section for example), and other sectionsto be read from a file. But that could be a mess.

Another option would be to divide the program into several small mini-gamebooks, game-chapters if you wish to call them soand then make Amstrad load and run this or that "game-chapter" as the story requires. But I feel uneasy about writing a line such as:
50410  RUN "NEXT-GAME-CHAPTER.BAS"
and then load the game state (player attributes, flags) from a file
Ideas, thoughts?
10 ' The XIII Emperor a gamebook for the Amstrad CPC
20 ' By Miguel de Luis Espinosa
30 ' See README.TXT for license and notes
40 ' Part 1
50 '
60 ' ----- Core Functions ---------------------------------------------------
70 DEF FNdice(x%) = ROUND(RND(1)*(x%-1))+1
80 DEF FNtwoD3 = FNdice(3) + FNdice(3)
90 DEF FNustr$(x%) = STRING$(MAX(0,1 - (x%\10)), "0") + MID$(STR$(x%),2)
99 '
100 ' ---- Core Variables ---------------------------------------------------
110 DIM scores%(6,2)
120 DIM KNW%(2) : DIM PER%(2) : DIM GRC%(2)
130 DIM SKL%(2) : DIM STR%(2) : DIM HTP%(2)   
140 DIM flags%(20) : FOR f%=1 TO 20 : flags%(f%)=0 : NEXT
150 DIM text$(20)
160 raw$ = "" ' Stores raw input from player
199 '
200 ' ---- Start Game ------------------------------------------------------
210 RANDOMIZE(TIME) : CLS : MODE 2
220 text$(1) = "\..:: The XIII Emperor ::.."
230 text$(2) = CHR$(13)
240 text$(3) = "\A gamebook by Miguel de Luis Espinosa (Enteka)"
250 text$(4) = CHR$(13)
260 text$(5) = "The kids of Samar play to be the slave boy who saved the Empire from the"
270 text$(6) = "Great Horde. Oh, of course, the Imperial History has it that it was our"
280 text$(7) = "beloved XIII Emperor who defeated the Barbarians, and not this... what"
290 text$( = "was his name?"
300 text$(9) = CHR$(13)
310 endOfParagraph% = 9
320 GOSUB 10010 ' displayText
330 WHILE LEN(raw$) > 7 OR LEN(raw$) < 2
340   INPUT "     > ", raw$
350   GOSUB 10400 'clean4Reinput
360 WEND
370 name$ = UPPER$(LEFT$(raw$,1)) + LOWER$(MID$(raw$,2))
380 raw$ = ""
390 GOSUB 10500 'cleanTextBuffer
400 text$(1) = CHR$(13)
410 text$(2) = "Oh, yes, * and they even imagine him to be 14 years old..."
420 text$(3) = "... well... actually, I've heard anything from 13 to 17. Too young, far"
430 text$(4) = "too young in any case, but 14? Surely, he must have been..."
440 text$(5) = CHR$(13)
450 endOfParagraph% = 5
460 GOSUB 10010
470 WHILE VAL(raw$) > 17 OR VAL(raw$) < 13
480   INPUT "     > ", raw$
490   IF VAL(raw$) > 17 OR VAL(raw$) < 13 THEN GOSUB 10400 'clean4Reinput
500 WEND
510 age% = VAL(raw$) : raw$ = ""
520 GOSUB 10600 'generateScores
530 GOSUB 10900 'displaySheet
699 '
700 ' ---- Section 1 The blood stained scepter -----------------------------|
710 GOSUB 11200 ' startNewSection
720 text$(1) = "\..:: The blood stained scepter ::.."
725 text$(2) = CHR$(13)
730 text$(3) = "*, is this the end? The Emperor called the cabinet to an emergency"
740 text$(4) = "meeting. You are attending as servant to countess Ansabel, who taught"
750 text$(5) = "you the art of healing, grammar and science, and many believe to be your"
760 text$(6) = "mother."
770 text$(7) = "   But you were bought by one of His Majesty's agents when you were 7,"
780 text$( = "You know your mother's grave."
790 text$(9) = "   His Majesty shares the grave news: the Great Horde defeated 2 of the"
800 text$(10) = "3 imperial armies. The enemy can reach Samar in 1 week. The ministers"
810 text$(11) = "debate with the young Emperor on how to save the capital, hours and hours"
820 text$(12) = "into the night, reaching even to shouting."
830 text$(13) = "   Lastly, the Emperor calls all to be quiet, addressing his ministers:"
840 text$(14) = "Ladies & Lords, there's no hope. We must save our lives and may the Gods"
850 text$(15) = "have mercy on the people of Samar!"
860 endOfParagraph% = 16
865 GOSUB 10010 ' displayText
870 GOSUB 11110 ' Any key to continue
880 GOSUB 11200 ' startNewSection
900 text$(1)  = "'Coward!'"
905 text$(2)  = "   Did you hear that? Yes, and it was Countess Ansabel, the gentle, who"
910 text$(3)  = "has yelled them at the Emperor! The Throne Room falls into silence."
915 text$(4)  = "   Quick as rage, the Emperor turns to scepter, a fancy winged mace, but"
920 text$(5)  = "a mace, nevertheless, and strikes the woman's chest."
925 text$(6)  = "   Ansabel drops to the floor, like a redenned rag doll, the imperial"
930 text$(7)  = "scepter, now stained in blood, follows her, smashing the carpet."
935 text$(  = CHR$(13)
940 text$(9)  = "What will you to do, *, slave boy?"
945 text$(10) = "   1) Run to tend the Countess' wound."
950 text$(11) = "   2) Ask the Emperor's permission to help the Countess."
955 endOfParagraph% = 11
960 GOSUB 10010 ' displayText
970 activeChoice$ = "12"
980 GOSUB 11500 ' getPlayerChoice
990 IF playerChoice$ = "1" then GOTO 1000 ELSE GOTO 1010
1000    IF KNW(2) + FNtwoD3 > 8 THEN GOTO 1100 ELSE GOTO 1300
1005 '  ELSE
1010    IF playerChoice$ = "2" THEN GOTO 1500 ELSE ERROR 33
1020 '  ENDIF 1130
1099 '
1100 '  Scene 2 --------------------------------------------------------------
1110 GOSUB 11200 ' startNewSection
1120 text$(1) = "Lady Ansabel trained you in the art of healing which you practiced
1122 text$(2) = "too many too long nights in the Palace infirmary. Kerkko, a guard, can
1124 text$(3) = "name you as his savior, as many servants.
1126 text$(4) = "   Tonight, you are able to contain the bleeding, but your skills will
1128 text$(5) = "not be enough to save her. Still, she smiles at you, briefly.
1130 text$(6) = "'Behold ye', the Emperor says addressing the ministers, 'a good and
1132 text$(7) = "loyal slave for a deranged mistress.'
1134 text$( = "You wonder at his words, and not only as it's him who is your Master.
1140 endOfParagraph% = 7
1150 GOSUB 10010 ' displayText
1160 GOSUB 11110 ' Any key to continue
1170 GOTO 1800 ' Scene 5
1299 '
1300 '  Scene 3
1499 '
1500 '  Scene 4
1799 '
1800 '  Scene 5
1810 GOSUB 11200 ' startNewSection
1812 text$(1) = "Lady Ansabel rises her quakering voice from the ground.
1814 text$(2) = "   'Your Majesty, from the dust of death, I beg you. At least, leave
1816 text$(3) = "a Marshal to defend the Empire in your absence'.
1818 text$(4) = "   Silence, then madness. The Emperor erupts into laughter and then
1820 text$(5) = "each minister, one by one, follows his lead, out of fear.
1822 text$(6) = "   'And who that Marshal could be?' His Majesty answers. 'I am afraid
1824 text$(7) = "you will be unable to command from the grave, and he who I leave in
1826 text$( = "will rest his head on a Barbarian spear, any volunteers?' Nobody
1828 text$(9) = "makes a move for four long minutes. You are holding Lady Ansabel's
1830 text$(10) = "hand, as she slips out of conscience.
1832 text$(11) = "   '*, kneel to me.' It's His Majesty who is calling you and
1834 text$(12) = "you can do nothing but obey. '*, I make you Marshal of
1836 text$(14) = "the Empire in my absence. May you use your skills wisely, etcetera,
1838 text$(15) = "and may the Enemy end your misery quickly and not too painfully.'
1840 text$(16) = "   Everyone laughs at his words. But the Emperor is quick to cut it
1842 text$(17) = "all. 'I do mean every word. Chancellor fill the scroll, give it to
1844 text$(18) = "the boy, and then we will all leave to the sweet islands of Elos, far
1846 text$(19) = "from the Horde and death to live ever merry.
1850 endOfParagraph% = 19
1860 GOSUB 10010 ' displayText
1880 GOSUB 11110 ' Any key to continue
1890 GOTO 1900 ' Scene 6
1899 '
1900 Scene 6
9998  END
9999  '
10000 '---- Subroutines ------------------------------------------------------|
10010 '---- Display Text -----------------------------------------------------|
10020 paragraphSize% = MIN(19,endOfParagraph%)
10030 FOR textIndex% = 1 to paragraphSize%
10040    lineToPrint$ = text$(textIndex%)
10050    tabs% = 5
10060    IF ASC(lineToPrint$) = 92 THEN GOSUB 10200 'centerLine
10070    GOSUB 10300 'replaceName
10080    PRINT TAB(tabs%) lineToPrint$
10090 NEXT textIndex%
10100 RETURN
10200 '---- Center Line -----------------------------------------------------|
10210 lineToPrint$ = MID$(lineToPrint$,2)
10220 tabs% = (80 - LEN(lineToPrint$))\2
10230 RETURN
10300 '---- Replace Name ----------------------------------------------------|
10310 sI% = INSTR(lineToPrint$,"*")
10320 WHILE sI% <> 0
10330    auxA$ = LEFT$(lineToPrint$, sI%-1) + name$
10340    lineToPrint$ = auxA$ + MID$(lineToPrint$, sI%+1)
10345    sI% = INSTR(lineToPrint$,"*")
10350 WEND
10360 RETURN
10400 '---- Clean 4 Reinput
10410 PRINT CHR$(7);
10420 LOCATE 1, VPOS(#0)-1
10430 PRINT TAB(80)
10440 LOCATE 1, VPOS(#0)
10450 RETURN
10500 '---- Clean Text Buffer -----------------------------------------------|
10510 FOR ctb% = 1 TO 18 : text$(ctb%)="-": NEXT
10520 RETURN
10600 '---- Generate Scores -------------------------------------------------|
10610 KNW%(1) = MAX(6, FNtwoD3 + (age%-10)\2)
10620 KNW%(2) = KNW%(1)
10630 PER%(1) = MAX(6, FNtwoD3 + FNdice(3))
10640 PER%(2) = PER%(1)
10650 GRC%(1) = MAX(6, FNtwoD3 + (19-age%)\2 )
10660 GRC%(2) = GRC%(1)
10670 SKL%(1) = MAX(7, FNtwoD3 + FNdice(3))
10680 SKL%(2) = SKL%(1)
10690 STR%(1) = MAX(4, FNtwoD3 + FNdice(3))
10700 STR%(2) = STR%(1)
10710 HTP%(1) = FNtwoD3 + STR%(1) + (age%\2)
10720 HTP%(2) = HTP%(1)
10730 RETURN
10900 ' ------- Display Character Sheet -------------------
10910 CLS: PRINT name$
10920 PRINT FNustr$(age%) " years old." : PRINT
10930 PRINT
10940 PRINT "Knowledge", FNustr$(knw%(1)) "/" FNustr$(knw%(2))
10950 PRINT "Perception", FNustr$(per%(1)) "/" FNustr$(per%(2))
10960 PRINT "Grace", FNustr$(grc%(1)) "/" FNustr$(grc%(2))
10970 PRINT "Skill", FNustr$(skl%(1)) "/" FNustr$(skl%(2))
10980 PRINT "Strength", FNustr$(str%(1)) "/" FNustr$(str%(2))
10990 PRINT "Hit Points", FNustr$(htp%(1)) "/" FNustr$(htp%(2))
11000 GOSUB 11110 ' Any key to continue
11010 RETURN
11100 ' -------- Any key to continue ---------------------
11110 PRINT : PRINT TAB(25) ">>> Any key to continue <<<
11120 akey$ = INKEY$ : IF akey$ = "" GOTO 11120
11130 RETURN
11200 ' ------- Start New Section -------------------
11210 GOSUB 10500 '---- Clean Text Buffer
11220 CLS
11230 LOCATE 1,25 '---- Print Status Line
11240 PRINT name$ + ", " + FNustr$(age%) + " y/o";
11260 PRINT " KNW " + FNustr$(knw%(1)) + "/" + FNustr$(knw%(2));
11270 PRINT " PER " + FNustr$(per%(1)) + "/" + FNustr$(per%(2));
11280 PRINT " GRC " + FNustr$(grc%(1)) + "/" + FNustr$(grc%(2));
11290 PRINT " SKL " + FNustr$(skl%(1)) + "/" + FNustr$(skl%(2));
11300 PRINT " STR " + FNustr$(str%(1)) + "/" + FNustr$(str%(2));
11310 PRINT " HTP " + FNustr$(htp%(1)) + "/" + FNustr$(htp%(2));
11320 LOCATE 1,1
11330 RETURN
11500 ' ------- Get player choice -------------------
11510 raw$ = INKEY$ : IF raw$ = "" GOTO 11510
11520 IF INSTR(activeChoice$, raw$) <> 0 GOTO 11560 
11530 LOCATE 1,24
11540 PRINT TAB(20) "Choices Available: " activeChoice$
11550 GOTO 11510
11560 playerChoice$ = raw$ : raw$ = ""
11570 RETURN
Title: Re: maximum size of an Amstrad program and... choices, choices
Post by: ervin on 05:23, 10 June 21
Looks very interesting!
I'm looking forward to watching this one develop.

Some little suggestions:
- shorten your variable names
- shorten your comment lines (for example, get rid of all the dashes after "Start Game" in line 200
- get rid of unnecessary white space (for example, the spaces around maths operators, equal signs, colons etc)

But the biggest space improvement will come from reworking your text handling.
The classic game Elite famously squeezed everything into a small memory footprint by using some clever compression for planet descriptions.
I'm not sure if some sort of basic procedural generation was used, or simply indexes for selecting the words to print, but how about something like this idea?

Instead of storing the full text of (for example) "The cat sat on the mat", and "the frog sat on the log" (I know, these are silly examples, but bear with me), store only one copy of each word in a table, initialised using data statements.
DATA "The","cat","sat","on","the","mat","frog","log"

And then the above sentences can be constructed by referencing the words by their index in the table.
So the indexes for the 2 sentences above might be:
DATA 0,1,2,3,4,5
DATA 0,6,2,3,4,7
Where index 0 represents the word "The", index 6 represents "frog" etc.

Each word used in text presented to the player appears only once in the text table, but can be referenced any number of times via indexing.
Of course this will introduce some challenges with punctuation and spaces.

Sorry I haven't explained this very well, but this sort of thing might help.
It will however require more careful management of your code and text strings.

Good luck!
Title: Re: maximum size of an Amstrad program and... choices, choices
Post by: ervin on 05:42, 10 June 21
I just tried a few quick changes to the text.

The full text, unchanged, is 10,691 bytes.
- remove strings of dashes after comments : reduce to 10,036 bytes
- remove spaces around "=", "+", ":" : 9,810 bytes
- replace "text$" with "txt$" : 9,641 bytes
- replace "endOfParagraph$" with "endPar$" : 9,585 bytes

Also, the word "the" appears over 40 times in your current description text.
Using an index to refer to the words instead would offer a significant saving.
(Of course the code to handle this sort of structure would need some space).

Just some ideas which may help.
8)
Title: Re: maximum size of an Amstrad program and... choices, choices
Post by: eto on 07:36, 10 June 21
Quote from: enteka on 00:00, 10 June 21How much is the practical maximum size for an Amstrad program?30K? 40K?

For a BASIC program, yes, something in between. It depends on what kind of variables you are using, especially Arrays consume a lot of memory, or if you have a custom font using Symbol after, you probably lose another 3-4KB of Ram as the Memory is reduced and you have the symbol statements. I recently made a program with about 8KB of binary music data and a custom font, and then I had about 28KB left (when saved on disk) before I was running into a Memory Full error, when playing the game. So probably 38-40KB would have been possible for BASIC code.

A few things that I did or considered for my last project:

- make REM lines as short as possible. I also started with many characters to have a screen wide separator, but a few characters maybe are good enough. Even an empty remark consumes at least 6 bytes. And if you reach the maximum size, you will recognise that every byte counts.
- short variable names if possible.
- maybe DATA statements could be an option instead of the direct variable assignment. I am not sure if this reduces the size directly but might help to modularise the code.
- there are tools that reduce the file size by removing unnecessary white space, REM statements, combine several lines into a single one and replace variable names. My game got almost 8KB reduced by doing this. However, it is "write only" then, you probably do no longer understand it. But this can become an option if you manage to split the game into 2 sections during development and then merge them for release.
- if you need to split the final game consider to CHAIN/MERGE. I haven't tried yet, but that was an idea I had. This will load other sections of the game and/or run the other section while keeping the variables.
- if you are using a custom font, you can store it as a binary on disk and load it into RAM rather than having the Symbol statements. It will probably have a bit more size on disk but it will save a lot of memory as you no longer need the Symbol statements.
- compression - maybe another option - The ZX0 compressor has a very good compression rate and can be decompressed very fast on a Z80 CPU. Big disadvantage is, that you need to deal with binary data in RAM and would at least need some machine good to make the text accessible for Basic.

If you consider to (re)load parts, please be aware that this makes it impossible for tape based machines to play your game (unless they are strictly sequential) and that a normal disk can only store 64 files (if I am not wrong, even less if some files are bigger than 16KB). So you would have to make sure that you split in bigger chunks.

Personally I don't think it's bad if the game loads parts if they are neither too big nor too small. E.g. chunks of 4KB (especially when minified with the tool I mentioned earlier or even compressed data) are quite fast to read and should give you enough data for a few minutes of gameplay.


Title: Re: maximum size of an Amstrad program and... choices, choices
Post by: enteka on 16:26, 10 June 21
Quote from: ervin on 05:42, 10 June 21
I just tried a few quick changes to the text.

The full text, unchanged, is 10,691 bytes.
- remove strings of dashes after comments : reduce to 10,036 bytes
- remove spaces around "=", "+", ":" : 9,810 bytes
- replace "text$" with "txt$" : 9,641 bytes
- replace "endOfParagraph$" with "endPar$" : 9,585 bytes

Also, the word "the" appears over 40 times in your current description text.
Using an index to refer to the words instead would offer a significant saving.
(Of course the code to handle this sort of structure would need some space).

Just some ideas which may help.
8)
I will be taking all your suggestions, except, the later one, well, probably... the dashes will be the first to go. Other stuff, I'm keeping for now, because the code is still buggy and I need clarity

Thanks!
Title: Re: maximum size of an Amstrad program and... choices, choices
Post by: enteka on 16:32, 10 June 21
Quote from: eto on 07:36, 10 June 21
For a BASIC program, yes, something in between. It depends on what kind of variables you are using, especially Arrays consume a lot of memory, or if you have a custom font using Symbol after, you probably lose another 3-4KB of Ram as the Memory is reduced and you have the symbol statements. I recently made a program with about 8KB of binary music data and a custom font, and then I had about 28KB left (when saved on disk) before I was running into a Memory Full error, when playing the game. So probably 38-40KB would have been possible for BASIC code.

A few things that I did or considered for my last project:

- make REM lines as short as possible. I also started with many characters to have a screen wide separator, but a few characters maybe are good enough. Even an empty remark consumes at least 6 bytes. And if you reach the maximum size, you will recognise that every byte counts.
- short variable names if possible.
- maybe DATA statements could be an option instead of the direct variable assignment. I am not sure if this reduces the size directly but might help to modularise the code.
- there are tools that reduce the file size by removing unnecessary white space, REM statements, combine several lines into a single one and replace variable names. My game got almost 8KB reduced by doing this. However, it is "write only" then, you probably do no longer understand it. But this can become an option if you manage to split the game into 2 sections during development and then merge them for release.
- if you need to split the final game consider to CHAIN/MERGE. I haven't tried yet, but that was an idea I had. This will load other sections of the game and/or run the other section while keeping the variables.
- if you are using a custom font, you can store it as a binary on disk and load it into RAM rather than having the Symbol statements. It will probably have a bit more size on disk but it will save a lot of memory as you no longer need the Symbol statements.
- compression - maybe another option - The ZX0 compressor has a very good compression rate and can be decompressed very fast on a Z80 CPU. Big disadvantage is, that you need to deal with binary data in RAM and would at least need some machine good to make the text accessible for Basic.

If you consider to (re)load parts, please be aware that this makes it impossible for tape based machines to play your game (unless they are strictly sequential) and that a normal disk can only store 64 files (if I am not wrong, even less if some files are bigger than 16KB). So you would have to make sure that you split in bigger chunks.

Personally I don't think it's bad if the game loads parts if they are neither too big nor too small. E.g. chunks of 4KB (especially when minified with the tool I mentioned earlier or even compressed data) are quite fast to read and should give you enough data for a few minutes of gameplay.
Actually, empty remarks work pretty well, as they add white space, much better that the dahes...
Short variable names, yes, as I am forced to... :-)

Minimize the code? Of course, for production, however I will still keep a "maximized code for other enthusiasts who might know still less than me "
I'm not doing any custom font, not for this game, it's my first on the Amstrad, and I want to keep it simple, simple, simple(but that's an idea! :) )
And thank you :)
Chain/Merge---- hmmm, that's for thought... thinking now on how to accomodate tape users.
The tape! But it was THE ONLY thing I PERKELE!!! from the Amstrad
Title: Re: maximum size of an Amstrad program and... choices, choices
Post by: Animalgril987 on 18:56, 10 June 21
Also, put as many commands on each line as possible, as a colon to separate commands only takes 1 byte, but a line uses 2 bytes for line number and 2 bytes for line length, plus an extra byte for the end-of-line marker.
For example:

10 PRINT"HELLO":GOTO 10
is 4 bytes shorter than
10 PRINT"HELLO"
20 GOTO 10




If you use CHAIN MERGE to load in new sections of the game,  your variables will remain intact, and the command can take a parameter for which line of new code you wish to start at. It also works with cassette, as long as you only load in a linear fashion ie no need to rewind.


Hope this helps  :D
Title: Re: maximum size of an Amstrad program and... choices, choices
Post by: PaddyC13 on 19:00, 10 June 21
Hi @enteka (https://www.cpcwiki.eu/forum/index.php?action=profile;u=4285)

Interesting project.  One you get in to saving memory in BASIC, it becomes quite fun.  Unfortunately your source code will tend to end up looking rather unreadable.

In terms of the text data, compression is definitely the way to go.  I would not store the values as numbers in DATA statements though as this can be wasteful of memory too.  If the CPC is like the Spectrum then each number is stored internally a floating point value of 5 bytes.  So even a value of 1 (single digit) will consume 5 bytes of valuable memory.

In the past, I have stored the compressed values (in your case words) in a string with each position holding an ASCII value between 0 and 255.  This means you could in theory have a dictionary of 256 words from which to build your text from.

A sentence with 10 words could be compressed down to a string of 10 bytes (plus internal variable definition).  You would need to work out some logic to handle punctuation and sentence case but that should be possible.

Hope this helps.

Kind regards

Paddy
UK
Title: Re: maximum size of an Amstrad program and... choices, choices
Post by: enteka on 19:16, 10 June 21
Updated code, incorporating some of your advice...
1 'The XIII Emperor a gamebook 4 the Amstrad
2 'By Miguel de Luis See README.TXT
9 '
10 DEF FNdice(x%)=ROUND(RND(1)*(x%-1))+1
20 DEF FNtwoD3 =FNdice(3)+FNdice(3)
30 DEF FNustr$(x%)=STRING$(MAX(0,1 - (x%\10)), "0")+MID$(STR$(x%),2)
40 DEF FNshwStats$(x%,y%)=FNustr$(x%) "/" FNustr$(y%)
99 '
100 DIM flags%(20) : FOR f%=1 TO 20 : flags%(f%)=0 : NEXT
110 DIM st$(20) ' section text
120 raw$="" ' Stores raw input from player
199 '
200 'Start Game
210 RANDOMIZE(TIME) : CLS : MODE 2
220 st$(1)="\..:: The XIII Emperor ::..
230 st$(2)=CHR$(13)
240 st$(3)="\A gamebook by Miguel de Luis Espinosa (Enteka)
250 st$(4)=CHR$(13)
260 st$(5)="The kids of Samar play to be the slave boy who saved the Empire from the
270 st$(6)="Great Horde. Oh, of course, the Imperial History has it that it was our
280 st$(7)="beloved XIII Emperor who defeated the Barbarians, and not this... what
290 st$(8)="was his name?"
300 st$(9)=CHR$(13)
310 eop%=9
320 GOSUB 10010 ' displayText
330 WHILE LEN(raw$) > 7 OR LEN(raw$) < 2
340   INPUT "     > ", raw$
350   GOSUB 10400 'clean4Reinput
360 WEND
370 name$=UPPER$(LEFT$(raw$,1))+LOWER$(MID$(raw$,2))
380 raw$=""
390 GOSUB 10500 'cleanTextBuffer
400 st$(1)=CHR$(13)
410 st$(2)="Oh, yes, * and they even imagine him to be 14 years old...
420 st$(3)="... well... actually, I've heard anything from 13 to 17. Too young, far
430 st$(4)="too young in any case, but 14? Surely, he must have been...
440 st$(5)=CHR$(13)
450 eop%=5
460 GOSUB 10010
470 WHILE VAL(raw$) > 17 OR VAL(raw$) < 13
480   INPUT "     > ", raw$
490   IF VAL(raw$) > 17 OR VAL(raw$) < 13 THEN GOSUB 10400 'clean4Reinput
500 WEND
510 age%=VAL(raw$) : raw$=""
520 GOSUB 10600 'generateScores
530 GOSUB 10900 'displaySheet
699 '
700 'Section 1
710 GOSUB 11200 ' startNewSection
720 st$(1)="\..:: The blood stained scepter ::..
725 st$(2)=CHR$(13)
730 st$(3)="*, is this the end? The Emperor called the cabinet to an emergency
740 st$(4)="meeting. You are attending as servant to countess Ansabel, who taught
750 st$(5)="you the art of healing, grammar and science, and many believe to be your
760 st$(6)="mother.
770 st$(7)="   But you were bought by one of His Majesty's agents when you were 7,
780 st$(8)="You know your mother's grave.
790 st$(9)="   His Majesty shares the grave news: the Great Horde defeated 2 of the
800 st$(10)="3 imperial armies. The enemy can reach Samar in 1 week. The ministers
810 st$(11)="debate with the young Emperor on how to save the capital, hours and hours
820 st$(12)="into the night, reaching even to shouting.
830 st$(13)="   Lastly, the Emperor calls all to be quiet, addressing his ministers:
840 st$(14)="Ladies & Lords, there's no hope. We must save our lives and may the Gods
850 st$(15)="have mercy on the people of Samar!
860 eop%=16
865 GOSUB 10010 ' displayText
870 GOSUB 11110 ' Any key to continue
880 GOSUB 11200 ' startNewSection
900 st$(1)="'Coward!'
905 st$(2)="   Did you hear that? Yes, and it was Countess Ansabel, the gentle, whod
910 st$(3)="has yelled them at the Emperor! The Throne Room falls into silence.
915 st$(4)="   Quick as rage, the Emperor turns to scepter, a fancy winged mace, but
920 st$(5)="a mace, nevertheless, and strikes the woman's chest.
925 st$(6)="   Ansabel drops to the floor, like a redenned rag doll, the imperial
930 st$(7)="scepter, now stained in blood, follows her, smashing the carpet.
935 st$(8)=CHR$(13)
940 st$(9)="What will you to do, *, slave boy?
945 st$(10)="   1) Run to tend the Countess' wound.
950 st$(11)="   2) Ask the Emperor's permission to help the Countess.
955 eop%=11
960 GOSUB 10010 ' displayText
970 activeChoice$="1, 2
980 GOSUB 11500 ' getPlayerChoice
990 IF playerChoice$="1" then GOTO 1000 ELSE GOTO 1010
1000    IF KNW(2)+FNtwoD3 > 8 THEN GOTO 1100 ELSE GOTO 1300
1005 '  ELSE
1010    IF playerChoice$="2" THEN GOTO 1500 ELSE ERROR 33
1020 '  ENDIF 1130
1099 '
1100 'Scene 2
1110 GOSUB 11200 ' startNewSection
1120 st$(1)="Lady Ansabel trained you in the art of healing which you practiced
1122 st$(2)="too many too long nights in the Palace infirmary. Kerkko, a guard, can
1124 st$(3)="name you as his savior, as many servants.
1126 st$(4)="   Tonight, you are able to contain the bleeding, but your skills will
1128 st$(5)="not be enough to save her. Still, she smiles at you, briefly.
1130 st$(6)="'Behold ye', the Emperor says addressing the ministers, 'a good and
1132 st$(7)="loyal slave for a deranged mistress.'
1134 st$(8)="You wonder at his words, and not only as it's him who is your Master.
1140 eop%=8
1150 GOSUB 10010 ' displayText
1160 GOSUB 11110 ' Any key to continue
1170 GOTO 1800 ' Scene 5
1299 '
1300 'Scene 3
1310 GOSUB 11200
1320 eop%=7 : fn$="3
1330 GOSUB 11700 ' Load st$ from file
1340 GOSUB 10010 ' displayText
1350 GOSUB 11110
1360 GOTO 1800 'Scene 5
1499 '
1500 'Scene 4
1799 '
1800 'Scene 5
1810 GOSUB 11200 ' startNewSection
1812 st$(1)="Lady Ansabel rises her quakering voice from the ground.
1814 st$(2)="   'Your Majesty, from the dust of death, I beg you. At least, leave
1816 st$(3)="a Marshal to defend the Empire in your absence'.
1818 st$(4)="   Silence, then madness. The Emperor erupts into laughter and then
1820 st$(5)="each minister, one by one, follows his lead, out of fear.
1822 st$(6)="   'And who that Marshal could be?' His Majesty answers. 'I am afraid
1824 st$(7)="you will be unable to command from the grave, and he who I leave in
1826 st$(8)="will rest his head on a Barbarian spear, any volunteers?' Nobody
1828 st$(9)="makes a move for four long minutes. You are holding Lady Ansabel's
1830 st$(10)="hand, as she slips out of conscience.
1832 st$(11)="   '*, kneel to me.' It's His Majesty who is calling you and
1834 st$(12)="you can do nothing but obey. '*, I make you Marshal of
1836 st$(14)="the Empire in my absence. May you use your skills wisely, etcetera,
1838 st$(15)="and may the Enemy end your misery quickly and not too painfully.'
1840 st$(16)="   Everyone laughs at his words. But the Emperor is quick to cut it
1842 st$(17)="all. 'I do mean every word. Chancellor fill the scroll, give it to
1844 st$(18)="the boy, and then we will all leave to the sweet islands of Elos, far
1846 st$(19)="from the Horde and death to live ever merry.
1850 eop%=19
1860 GOSUB 11800 'Load and display text
1890 GOTO 1900 ' Scene 6
1899 '
1900 Scene 6
9998  END
9999  '
10000 'Subroutines
10010 'Display Text
10020 paragraphSize%=MIN(19,eop%)
10030 FOR textIndex%=1 to paragraphSize%
10040    ltp$=st$(textIndex%)
10050    tabs%=5
10060    IF ASC(ltp$)=92 THEN GOSUB 10200 'centerLine
10070    GOSUB 10300 'replaceName
10080    PRINT TAB(tabs%) ltp$
10090 NEXT textIndex%
10100 RETURN
10200 'Center Line
10210 ltp$=MID$(ltp$,2)
10220 tabs%=(80 - LEN(ltp$))\2
10230 RETURN
10299
10300 'Replace Name
10310 sI%=INSTR(ltp$,"*")
10320 WHILE sI% <> 0
10330    auxA$=LEFT$(ltp$, sI%-1)+name$
10340    ltp$=auxA$+MID$(ltp$, sI%+1)
10345    sI%=INSTR(ltp$,"*")
10350 WEND
10360 RETURN
10399 '
10400 'Clean 4 Reinput
10410 PRINT CHR$(7);
10420 LOCATE 1, VPOS(#0)-1
10430 PRINT TAB(80)
10440 LOCATE 1, VPOS(#0)
10450 RETURN
10499 '
10500 'Clean Text Buffer
10510 FOR ctb%=1 TO 18 : st$(ctb%)="-": NEXT
10520 RETURN
10599 '
10600 'Generate Scores
10610 knw%=MAX(6, FNtwoD3+(age%-10)\2)
10620 cknw%=knw%
10630 per%=MAX(6, FNtwoD3+FNdice(3))
10640 cper%=per%
10650 grc%=MAX(6, FNtwoD3+(19-age%)\2)
10660 cgrc%=grc%
10670 skl%=MAX(7, FNtwoD3+FNdice(3))
10680 cskl%=skl%
10690 str%=MAX(4, FNtwoD3+FNdice(3))
10700 cstr%=str%
10710 htp%=FNtwoD3+str%+(age%\2)
10720 chtp%=htp%
10730 RETURN
10899 '
10900 ' Display PC Sheet
10910 CLS: PRINT name$
10920 PRINT FNustr$(age%) " years old." : PRINT
10940 PRINT "Knowledge",  FNshwStats$(knw%,cknw%)
10950 PRINT "Perception", FNshwStats$(per%,cper%)
10960 PRINT "Grace", FNshwStats$(grc%,cgrc%)
10970 PRINT "Skill", FNshwStats$(skl%,cskl%)
10980 PRINT "Strength", FNshwStats$(str%,cstr%)
10990 PRINT "Hit Points", FNshwStats$(htp%,chtp%)
11000 GOSUB 11110 ' Any key to continue
11010 RETURN
11100 ' Any key to continue
11110 PRINT : PRINT TAB(25) ">>> Any key to continue <<<
11120 akey$=INKEY$ : IF akey$="" GOTO 11120
11130 RETURN
11199 '
11200 ' Start New Section
11210 GOSUB 10500 ' Clean Txt Buffer
11220 CLS
11230 LOCATE 1,25 ' Print Status Line
11240 PRINT name$+", "+FNustr$(age%)+" y/o";
11260 PRINT " KNW "+FNustr$(knw%)+"/"+FNustr$(cknw%);
11270 PRINT " PER "+FNustr$(per%)+"/"+FNustr$(cper%);
11280 PRINT " GRC "+FNustr$(grc%)+"/"+FNustr$(cgrc%);
11290 PRINT " SKL "+FNustr$(skl%)+"/"+FNustr$(cskl%);
11300 PRINT " STR "+FNustr$(str%)+"/"+FNustr$(cstr%);
11310 PRINT " HTP "+FNustr$(htp%)+"/"+FNustr$(chtp%);
11320 LOCATE 1,1
11330 RETURN
11499 '
11500 ' Get player choice
11510 raw$=INKEY$ : IF raw$="" GOTO 11510
11520 IF INSTR(activeChoice$, raw$) <> 0 GOTO 11560 
11530 LOCATE 1,24
11540 PRINT TAB(20) "Choices Available: " activeChoice$
11550 GOTO 11510
11560 playerChoice$=raw$ : raw$=""
11570 RETURN
11699 '
11700 ' Load st$ from file
11710 OPENIN fn$+".TXT
11720 FOR li%=1 TO MAX(20,eop%)
11730    input #9, st$(li%)
11740 NEXT
11750 CLOSEIN
11760 RETURN
11799 '
11800 'Load and display text
11810 GOSUB 11700 ' Load st$ from file
11820 GOSUB 10010 ' displayText
11830 RETURN

Title: Re: maximum size of an Amstrad program and... choices, choices
Post by: enteka on 16:24, 13 June 21
Another update, slightly less readable, but more compressed1 'The XIII Emperor a gamebook 4 the Amstrad
2 'By Miguel de Luis See README.TXT
5 DEF FNdice(x%)=ROUND(RND(1)*(x%-1))+1
10 DEF FNtwoD3=FNdice(3)+FNdice(3)
15 DEF FNustr$(x%)=STRING$(MAX(0,1 -(x%\10)),"0")+MID$(STR$(x%),2)
20 DEF FNshwStats$(x%,y%)=FNustr$(x%)+"/"+FNustr$(y%)
29 '
30 flags$="0000000"
40 DIM st$(20) 'sectionText
50 raw$="" 'rawInputFromPlayer
99 '
100 'Start Game
110 RANDOMIZE(TIME):CLS:MODE 2
112 st$(1)="\..:: The XIII Emperor ::..
114 st$(2)=" "
116 st$(3)="\A gamebook by Miguel de Luis Espinosa (Enteka)
118 st$(4)=" "
120 st$(5)="The kids of Samar play to be the slave boy who saved the Empire from the
122 st$(6)="Great Horde. Oh, of course, the Imperial History has it that it was our
124 st$(7)="beloved XIII Emperor who defeated the Barbarians, and not this... what
126 st$(8)="was his name?"
128 st$(9)=" "
130 eop%=9
140 GOSUB 30010 ' displayText
150 WHILE LEN(raw$) > 7 OR LEN(raw$) < 2
160   INPUT "     > ", raw$
170   GOSUB 30400 'clean4Reinput
180 WEND
190 name$=UPPER$(LEFT$(raw$,1))+LOWER$(MID$(raw$,2))
199'
200 raw$="":GOSUB 30500 'cleanTextBuffer
202 st$(1)=" "
204 st$(2)="Oh, yes, * and they even imagine him to be 14 years old...
206 st$(3)="... well... actually, I've heard anything from 13 to 17. Too young, far
208 st$(4)="too young in any case, but 14? Surely, he must have been...
210 st$(5)=" "
212 eop%=5
214 GOSUB 30010
216 WHILE VAL(raw$) > 17 OR VAL(raw$) < 13
218   INPUT "     > ", raw$
220   IF VAL(raw$) > 17 OR VAL(raw$) < 13 THEN GOSUB 30400 'clean4Reinput
222 WEND
224 age%=VAL(raw$):raw$=""
226 GOSUB 30600 'generateScores
299'
300'S1
304 GOSUB 31200 ' startNewSection
306 st$(1)="\..:: The blood stained scepter ::..
308 st$(2)=" "
310 st$(3)="*, is this the end? The Emperor called the cabinet to an emergency
312 st$(4)="meeting. You are attending as servant to countess Ansabel, who taught
314 st$(5)="you the art of healing, grammar and science, and many believe to be your
316 st$(6)="mother.
318 st$(7)="   But you were bought by one of His Majesty's agents when you were 7,
320 st$(8)="You know your mother's grave.
322 st$(9)="   His Majesty shares the grave news: the Great Horde defeated 2 of the
324 st$(10)="3 imperial armies. The enemy can reach Samar in 1 week. The ministers
326 st$(11)="debate with the young Emperor on how to save the capital, hours and hours
328 st$(12)="into the night, reaching even to shouting.
330 st$(13)="   Lastly, the Emperor calls all to be quiet, addressing his ministers:
332 st$(14)="Ladies & Lords, there's no hope. We must save our lives and may the Gods
334 st$(15)="have mercy on the people of Samar!
336 eop%=16
338 GOSUB 30010:GOSUB 31110 'display text+any key to continue
342 GOSUB 31200 ' startNewSection
344 st$(1)="'Coward!'
346 st$(2)="   Did you hear that? Yes, and it was Countess Ansabel, the gentle, whod
348 st$(3)="has yelled them at the Emperor! The Throne Room falls into silence.
350 st$(4)="   Quick as rage, the Emperor turns to scepter, a fancy winged mace, but
352 st$(5)="a mace, nevertheless, and strikes the woman's chest.
354 st$(6)="   Ansabel drops to the floor, like a redenned rag doll, the imperial
356 st$(7)="scepter, now stained in blood, follows her, smashing the carpet.
358 st$(8)=" "
360 st$(9)="What will you to do, *, slave boy?
362 st$(10)="   1) Run to tend the Countess' wound.
364 st$(11)="   2) Ask the Emperor's permission to help the Countess.
366 eop%=11
368 GOSUB 30010'displayText
370 activeChoice$="12"
372 GOSUB 31500 ' getPlayerChoice
374 IF playerChoice$="1" then GOTO 376 ELSE GOTO 450
376 IF cknw+FNtwoD3 > 8 THEN GOTO 400 ELSE GOTO 430
398'
399'S2
400 GOSUB 31200'startNewSection
402 st$(1)="Lady Ansabel trained you in the art of healing which you practiced
404 st$(2)="too many too long nights in the Palace infirmary. Kerkko, a guard, can
406 st$(3)="name you as his savior, as many servants.
408 st$(4)="   Tonight, you are able to contain the bleeding, but your skills will
410 st$(5)="not be enough to save her. Still, she smiles at you, briefly.
412 st$(6)="'Behold ye', the Emperor says addressing the ministers, 'a good and
414 st$(7)="loyal slave for a deranged mistress.'
416 st$(8)="You wonder at his words, and not only as it's him who is your Master.
418 eop%=8
420 GOSUB 30010:GOSUB 31110'display text + Any key to...
422 GOTO 480'S5
429 '
430 'S3
432 GOSUB 31200
434 eop%=7 :file$="3"
436 GOSUB 31800'Load+display text
438 GOSUB 31110'Any key to continue
440 GOTO 480'S5
449 '
450 'S4
452 chtp%=chtp%-FNtwoD3
454 GOSUB 31200
456 eop%=6:file$="4"
458 GOSUB 31800'Load+display text
460 GOSUB 31110'Any key to continue
462 GOTO 430'S3
479 '
480 'S5
482 GOSUB 31200
startNewSection
484 st$(1)="Lady Ansabel rises her quakering voice from the ground.
486 st$(2)="   'Your Majesty, from the dust of death, I beg you. At least, leave
488 st$(3)="a Marshal to defend the Empire in your absence'.
490 st$(4)="   Silence, then madness. The Emperor erupts into laughter and then
492 st$(5)="each minister, one by one, follows his lead, out of fear.
494 st$(6)="   'And who that Marshal could be?' His Majesty answers. 'I am afraid
496 st$(7)="you will be unable to command from the grave, and he who I leave in
498 st$(8)="will rest his head on a Barbarian spear, any volunteers?' Nobody
500 st$(9)="makes a move for four long minutes. You are holding Lady Ansabel's
502 st$(10)="hand, as she slips out of conscience.
504 st$(11)="   '*, kneel to me.' It's His Majesty who is calling you and
506 st$(12)="you can do nothing but obey. '*, I make you Marshal of
508 st$(13)="the Empire in my absence. May you use your skills wisely, etcetera,
510 st$(14)="and may the Enemy end your misery quickly and not too painfully.'
512 st$(15)="   Everyone laughs at his words. But the Emperor is quick to cut it
514 st$(16)="all. 'I do mean every word. Chancellor fill the scroll, give it to
516 st$(17)="the boy, and then we will all leave to the sweet islands of Elos, far
518 st$(18)="from the Horde and death to live ever merry.
520 eop%=18:GOSUB 30010 'Display text
522 GOSUB 31100'Any key to continue
524 GOTO 530'S6
529 '
530 'S6
532 GOSUB 31200
534 st$(1)="The nobles departed in secrecy. A conspiracy spread on the sacred
536 st$(2)="palace. Only a few knew that the Emperor had deserted his people,
538 st$(3)="and most of them were taken to serve the nobles in their golden exile.
540 st$(4)="Four remained, Taro, Sharsa, Ikki & you. The rest of the palace,
542 st$(5)="doesn't know, doesn't want to know. The night is at its deepest.
544 st$(6)="   Countess Ansabel has died on her bed; the blood stained scepter,
546 st$(7)="which you now hold as Marshal, prevailed over your best efforts.
548 st$(8)="and now, on her chamber you have two conflicting issues to address to.
550 st$(9)="One is that the Countess' body is glowing in blue, the second are the
552 st$(10)="scratching noises that seem to come, if that is possible, from
554 st$(12)="inside the room's east walls.
556 st$(14)=""
558 st$(15)="You are alone now, what do you want to do?
560 st$(16)="   1) See what's going on with Lady Ansabel's
562 st$(17)="   2) Investigate the scratching
564 eop%=17:GOSUB 30010 'Display text
566 activeChoice$="12":GOSUB 31500 ' getPlayerChoice
568 IF playerChoice$="1" THEN GOTO 580 ELSE GOTO 600'S7 ELSE S8
579 '
580 'S7
582 eop%=8:file$="7"
584 GOSUB 31800'Load+display text
586 activeChoice$="12":GOSUB 31500 'getPlayerChoice
588 IF playerChoice$="2" THEN GOTO 594
590 IF cskl+FNtwoD3>9 THEN GOTO 660 ELSE 690 'S10 ELSE S11
592 GOTO 630'S9
599'
600'S8
629'
630'S9
659'
660'S10
689'
690'S11
29998 END
29999'
30000'Subroutines
30010'Display Text
30020 paragraphSize%=MIN(19,eop%)
30030 FOR textIndex%=1 to paragraphSize%
30040    ltp$=st$(textIndex%)
30050    tabs%=5
30060    IF ASC(ltp$)=92 THEN GOSUB 30200 'centerLine
30070    GOSUB 30300 'replaceName
30080    PRINT TAB(tabs%) ltp$
30090 NEXT textIndex%
30100 RETURN
30199 '
30200 'CenterLine
30210 ltp$=MID$(ltp$,2)
30220 tabs%=(80 - LEN(ltp$))\2
30230 RETURN
30299 '
30300 'Replace Name
30310 sI%=INSTR(ltp$,"*")
30320 WHILE sI% <> 0
30330    auxA$=LEFT$(ltp$, sI%-1)+name$
30340    ltp$=auxA$+MID$(ltp$, sI%+1)
30345    sI%=INSTR(ltp$,"*")
30350 WEND
30360 RETURN
30399 '
30400 'Clean4Reinput
30410 PRINT CHR$(7);
30420 LOCATE 1, VPOS(#0)-1
30430 PRINT TAB(80)
30440 LOCATE 1, VPOS(#0)
30450 RETURN
30499 '
30500 'CleanTextBuffer
30510 FOR ctb%=1 TO 20: st$(ctb%)=" ": NEXT
30520 RETURN
30599 '
30600 'Generate Scores
30610 knw%=MAX(6,FNtwoD3+(age%-10)\2):cknw%=knw%
30630 per%=MAX(6,FNtwoD3+FNdice(3)):cper%=per%
30650 grc%=MAX(6,FNtwoD3+(19-age%)\2):cgrc%=grc%
30670 skl%=MAX(7,FNtwoD3+FNdice(3)):cskl%=skl%
30710 htp%=FNtwoD3+age%:chtp%=htp%
30730 RETURN
31999 '
31100 'AnyKey2continue
31110 PRINT : PRINT TAB(25) ">>> Any key to continue <<<
31120 CALL &BB18
31130 RETURN
31199 '
31200 'StartNewSection
31210 GOSUB 30500 ' Clean Txt Buffer
31220 CLS
31230 LOCATE 1,25 ' Print Status Line
31240 ltp$= name$+","+FNustr$(age%)+" y/o"+" KNW "+FNshwStats$(knw%,cknw%)+" PER "+FNshwStats$(per%,cper%)+" GRC "+FNshwStats$(grc%,cgrc%)+" SKL "+FNshwStats$(skl%,cskl%)+" HTP "+FNshwStats$(htp%,chtp%)
31250 PRINT TAB((80 - LEN(ltp$))\2) ltp$
31260 LOCATE 1,1
31270 RETURN
31499 '
31500 'GetPlayerChoice
31510 raw$=INKEY$ : IF raw$="" GOTO 11510
31520 IF INSTR(activeChoice$, raw$) <> 0 GOTO 11560 
31530 LOCATE 1,24
31540 PRINT TAB(20) "Choices Available: ";
31545 FOR aC%=1 TO LEN(activeChoice$):PRINT MID$(activeChoice$,aC%,1)" ";:NEXT
31550 GOTO 31510
31560 playerChoice$=raw$ : raw$=""
31570 RETURN
31699 '
31700 'Load st$ fromFile
31710 OPENIN file$+".TXT"
31720 FOR li%=1 TO MIN(20,eop%)
31730    input #9, st$(li%)
31740 NEXT
31750 CLOSEIN
31760 RETURN
31799 '
31800 'Load+displayText
31810 GOSUB 31700 'Load st$ from file
31820 GOSUB 30010 'displayText
31830 RETURN

TXT files are not really, really TXT files, more as disguised CSV files, so the Amstrad doesn't break the lines at every comma. But nothing fancy at all just like this:"While the glow alures you, the scratching of an unseen creature makes thy"
"heart worry. And with good reason too!"
"   A breach forms on the plaster, from it, two giant chitinous legs pop"
"out. Quick! What are you to do?"
" "
"Your options:"
"  1) Smash it with the scepter right now!"
"  2) Wait until you see what it is"


Powered by SMFPacks Menu Editor Mod