Shadows and Eternity: A text-based sci-fi adventure

Greetings fellow humans!

This is the (dev log?) and home page of Shadows and Eternity, a text-based sci-fi adventure.

I actually got interested in making this game (and buying a Arduboy FX in the first place) when I saw the text-based game Silence by @rainnw. I thought that idea of a text-based adventure game in my pocket was TOO cool!

Of course, I needed/wanted to make some changes.

I’ve set up a GitHub repository with the code I have written and updated with the help of awesome people on this forum, including @brow1067, @Mr.Blinky, and @Pharap.

If you want, you are more than welcome to suggest changes/fixes!

Thanks for being interested in my project!


NOTE: This game, if rated by the ESRB, would probably be Rated T for Teen for themes concerning death. You have been warned.

SAEGTI.arduboy (21.6 KB)


Working with text/strings in an 8-bit environment is a real challenge especially if they spend time in ram!

Well, we’ll find out! :slight_smile:

I think it’s possible, since @rainnw was able to do it.

I just have to keep at it!

Update 1:

I got the menu working! I was searching the forums and found this:

void loop()
    if (!arduboy.nextFrame())   return;

    // Do all your player / enemy movements

    // Render all your stuff ..

    // Lastly, display the screen ..


Plugged it in, and now it works! Well, I had to reverse the X and Y coordinates for the selection line, but other than that, it works!

I haven’t touched that text adventure game (and related engine) in a long time, as well as the Arduboy. I need to start playing with the Arduboy again!

Although it looks like you are on the right track, i’ll point out to anyone looking to put text into a game that the default Arduino behavior (for some reason) is to load all strings into RAM. You can bypass this by forcing it to keep things in PROGMEM and by using the Flash macro F() if you are wanting to print something. IE:

Serial.printf("Hello world! This string is stored in RAM and will gobble it up quick on the AVR’);

Serial.printf(F(“Hello world! The F() macro actually keeps things in flash (IE: PROGMEM) and doesn’t consume any additional RAM”));

But text can add up quickly, given that you are limited to mere kilobytes of flash on AVR microcontrollers. One additional trick is to make use of the old ITA2 / BAUDOT code, which is a 5 bit code. It will limit you on symbols and mixed case, but caps are easier read on the sceen anyway. This will cut text space down by a third and is light on CPU usage to decode. You will need to pad text to 8 bits as its not byte aligned, but the savings will be worth it on any meaningful amount of text.


Cool! thanks for the info!

Always happy to learn new things. :smile:

Well all those 8-bit computers have many great adventure games and we’ve got plenty of flash in the FX now :wink:

Nice to see you’re still around @rainnw I really enjoyed silence. Well if you want to play around with Arduboy again then check out the Arduboy FX game jam that’s going on for another month.

Update 2:

I managed to get the file to print the array to a .txt file instead of to the screen. Takes 2 seconds to convert the .bin now!

Update 2.5:

Still working on the compiler. Fixed it so you can have commas in the displayed text.

For some reason, the compiler was also adding commas where they didn’t belong, and compiled files seem to not work correctly when it comes to effects. I’ve fixed the comma issue, not the other.

More testing/tweaking to be done.

Out of interest, what are the input and output of the compiler supposed to look like?

(Compiler theory is one of my favourite topics, so I’m always intrigued when someone uses one for a game.)

  1. takes a .csv file (containing all pertinent info) and converts it to a .bin.
  2. Then, you send it through the .bin through, which converts the .bin to an array of numbers. I updated to export the array to a .txt file instead of to the screen (which could take hours).
  3. You plug the array into the main .ino game code, and, from what I understand, it reads and interprets the array back into text.

I’ll post examples here in a bit.

I’m thinking about combining and into one file to save the user a step when testing.

1 Like

When I saw ‘compiler’ I was expecting more than just CSV as input.
I suppose it’s technically a ‘compiler’ of sorts, but it’s quite a simple input language.

If ‘to the screen’ means what I think it means, I believe the original intent would have been for the output to go to the standard output stream so the result could be ‘piped’ into a file.

Is that an exaggeration or is there really that much text?

If it’s designed to use standard input and output then you could just provide a script file (or two - for different OSes) that handles piping the input and output.

E.g. SAE.csv | > SAE.txt, or perhaps: SAE.csv > SAE.bin SAE.bin > SEA.txt

(However you’d rather have it configured.)

That would save you a degree of effort and give more or less the same result.

I don’t really know on that one. Sorry, still a noob. :slight_smile:

No, not an exaggeration. When I managed to get it to work (using IDLE), it was taking 3+ hours to process. It kept freezing it was taking so long to process. I ended giving up and have it save the output to a .txt file.

Here’s an example of the .csv data:

one,special,text,BOOM! RUMBLE! FLASH!,,,
two,special,text,An explosion somewhere within the city rocks your apartment!,,,
three,special,text,A fury of car alarms; flashes of light; screams and yells come from outside your window.,,,

I’m not going to paste the .bin, as it’s… weird. But here’s the final output array (for the example):

const unsigned char gti[] PROGMEM = { 255,255,16,66,79,79,77,33,32,82,85,77,66,76,69,33,32,70,76,65,83,72,33,44,44,44,0,255,255,10,1,255,255,10,0,255,255,16,65,110,32,101,120,112,108,111,115,105,111,110,32,115,111,109,101,119,104,101,114,101,32,119,105,116,104,105,110,32,116,104,101,32,99,105,116,121,32,114,111,99,107,115,32,121,111,117,114,32,97,112,97,114,116,109,101,110,116,33,44,44,44,0,255,255,16,65,32,102,117,114,121,32,111,102,32,99,97,114,32,97,108,97,114,109,115,59,32,102,108,97,115,104,101,115,32,111,102,32,108,105,103,104,116,59,32,115,99,114,101,97,109,115,32,97,110,100,32,121,101,108,108,115,32,99,111,109,101,32,102,114,111,109,32,111,117,116,115,105,100,101,32,121,111,117,114,32,119,105,110,100,111,119,46,44,44,44,0,255,255,10,0,255,255,16,65,110,111,116,104,101,114,32,101,120,112,108,111,115,105,111,110,32,114,111,99,107,115,32,116,104,101,32,99,105,116,121,46,32,84,104,105,115,32,116,105,109,101,59,32,105,116,32,115,101,101,109,115,32,99,108,111,115,101,114,46,44,44,44,0,255,255,16,89,111,117,114,32,103,117,116,32,105,110,115,116,105,110,99,116,32,105,115,32,116,111,32,114,117,110,32,100,111,119,110,32,116,104,101,32,115,116,97,105,114,115,32,97,110,100,32,105,110,116,111,32,116,104,101,32,111,108,100,32,102,97,108,108,111,117,116,32,115,104,101,108,116,101,114,46,44,44,44,0,1,136,1,155,80,105,99,107,32,65,0,80,105,99,107,32,66,0,84,104,105,115,32,105,115,32,97,32,116,101,115,116,32,114,111,111,109,33,0,255,255,16,89,111,117,32,99,104,111,115,101,32,65,33,44,44,44,0,255,255,16,89,111,117,32,99,104,111,115,101,32,66,33,44,44,44,0,};
1 Like

Basically: most desktop (and phone/tablet) software has three different data streams called ‘stdin’ (‘standard input’), ‘stdout’ (‘standard output’), and ‘stderr’ (‘standard error’) that can be used (mainly by terminals) to provide input and output data.

When a Python program prints to the terminal, it’s actually writing data to the standard output (or standard error) stream, which by default is hooked up to the terminal to display the output.

Any of the three streams can be redirected, which makes it easy to ‘pipe’ (i.e. connect) one program’s output into another program’s input, thus making it easy to chain together several programs, making each program a different step in a pipeline of programs.

The main advantage is flexibility: by writing a program to use the standard input and output, it’ll automatically be able to accept input from any file or program (that writes to its standard output) and be able to write output to any file or program (that accepts input from its standard input).

(Apparently in Python you access them via using import sys and then referring to sys.stdin, sys.stdout, and sys.stderr.)

It’s worth playing around with some day if you get chance, you might find it useful.

Belive it or not, I still remember being the only person in the room who didn’t know the difference between a gigabyte and a megabyte, so don’t feel too bad.

Weird. There’s probably a sensible(?) reason for it though.

It’s raw binary, so anything trying to interpret it as text is pretty much going to display junk.
(Probably with a lot of blank spaces too because a fair amount of it will be non-printable characters.)

That’s pretty much what I was expecting.

I can actually make a disturbing amount of sense out of those numbers…

I can see where the “BOOM” begins. I think I see the commas in the output too (the three 44s before the nul terminator).

Put another way, the first however-many bytes are basically: 255,255,16,'B','O','O','M','!',' ','R','U','M','B','L','E','!',' ','F','L','A','S','H','!',',',',',',','\0'.

Presumably the 255s are some kind of command? Or perhaps just delimiters?

At first I thought the 16 was a length prefix, but looking more I’d guess the 16 means ‘text’ and the 10 means ‘effect’.

At any rate, it makes sense.

If you feel a need to upgrade to something more ‘sophisticated’, let me know.

Though I suppose part of why you’re using CSV is so you can use spreadsheet software to do the editing?

I’m probably being excessively nosey here, but I’m interested because a while back I was working on a visual novel engine myself, so it’s interesting to see how other people approach the same idea.

1 Like

I will definitely let you know! I think I’ll leave it as a CSV for now, simply because it makes checking spelling a bit easier. :smile:

1 Like

Just for reference, by ‘sophisticated’ I mean being able to do something like:

effect lightning;
effect rumble;
text "An explosion somewhere within the city rocks your apartment!";
text "A fury of car alarms; flashes of light; screams and yells come from outside your window."

(Following a regular grammar, and without all the comma noise. Semicolons optional.)


[effect lightning]
[effect rumble]
An explosion somewhere within the city rocks your apartment!
A fury of car alarms; flashes of light; screams and yells come from outside your window.

Just so you’re aware that kind of thing is a possibility if the commas start to get annoying.


That’s good to know!

I’m proud to say that the compiler is working now. It took quite a while (and some help) to fix the issues it was having. Between them and Google, we got it working. (I need to bully them into joining this forum.)

I’ll post a proper update later. :smiley:

1 Like

Update 3:

I finished the compiler! It is now working as intended. It even supports commas and quotation marks, with a little bit of find/replace. :wink:

Update 4 (real excited for this one!):

I have started the initial testing of the full program, with the different peices put together. And…it’s working!

Thanks to @Pharap, @brow1067, and @Mr.Blinky for their help and code.

I’m going to make sure everything is working as intended before I start the final piece: adding code for the ending.

1 Like

Update 5:

Everything seems to be working as intended. Now to figure out how to upload it to this forum so people can test the game with ProjectABE.