I made a game called And yet it hurt.
You can download it here.
You can find the source on Github.
It all started in 2017 with a simple question: “What if there was a game in Notepad?”
I chuckled to myself at the silly idea. At least that’s what I assume. It has been 3 years since after all. After thinking about how this would work, I realized that this would totally be possible, and challenged myself to make this game.
With a normal game, you press a button and the game will act on it. Pressing A makes Mario jump. It’s a matter of receiving and responding. The game receives input and responds with output. With the Notepad game, the receiving would be that the player made a change to a file, and the response would be to change the file. So the player selects an option and the game will show the result of that selection. For this, the game keeps track of the last time a file was saved. If the time has changed, the game reads the contents of the file and can then write new data to the file. There’s a small problem though. Microsoft’s Notepad doesn’t check if the file has been updated. The player would have to save the file, close it, and then reopen it. It would still be possible to make a game with this, it just wouldn’t be very fun. I had to find an alternative to Notepad.
I can understand if you’re disappointed now, reading that the finished game is not actually in Microsoft’s Notepad. Let me be clear though, the finished game can be played in MS Notepad. It’s just very inconvenient. I chose the route that made this project less cool, in trade for a game that is actually fun to play.
Alternative
So I needed to find an alternative text editor for my game. The only requirement would be the ability to automatically refresh the files, though later on, you will find that another feature was in play. My first thought went out to code editors like Notepad++ and Sublime Text. But besides the fact that they don’t look like MS Notepad, which takes away the charm of it all, it would ask the player if they want to refresh the file. Though it’s a step forward from requiring you to close and reopen the file, it’s disruptive from the gameplay. I needed it to update the file automatically. It was then that I found the software called Notepad2, and it was almost perfect. Notepad2 can be configured to look very much like MS Notepad, and it checks for file changes. But, just like with Notepad++ and Sublime Text, it would ask the player if they want to refresh the file. Luckily Notepad2 is open source, so I was able to make it perfect.
Notepad2 is written in C, a language that I’ve played around with a few times, but I’m far from an expert. This is not a big deal, as most programming languages are very similar. An experienced Javascript programmer should have no trouble reading and understanding the general idea of C code. The real challenge was to understand Notepad2’s source code and to be able to find and edit the changes that I wanted. A good start is the text in the dialogue “The current file has been modified by an external program. Reload?”. This is set to a variable, and the variable is used as argument for the dialogue function. And so I found it.
if ((iFileWatchingMode == 2 && !bModified && iEncoding == iOriginalEncoding) || MsgBox(MBYESNO,IDS_FILECHANGENOTIFY) == IDYES) {
This code checks if the file’s contents are still the same, and if not it will open the dialogue and check if the user clicked Yes. All I had to do was replace
MsgBox(MBYESNO,IDS_FILECHANGENOTIFY) == IDYES
with TRUE and now it automatically reloads the file. With this feature unlocked, I basically created an ASCII based renderer. The next job was to create an engine around this.
Drawing
The game is made with LÖVE. An open source framework for 2D games in Lua, that I’ve used for many years and wrote a tutorial about. For this game, I mostly used LÖVE’s filesystem module, which provides everything I needed. Normally with a framework like LÖVE you would create an image object, and then draw that image object on the screen. I wanted to be able to do the same, except with my text-file and with ASCII art. I started with a house and a bird, and the goal was for the bird to fly across the file. I used art that I found on https://asciiart.website/, but note that all the art in the game is original (except the fonts).
and
Loading the art is really just reading the file’s contents.
house = love.filesystem.read("art_house.txt") bird = love.filesystem.read("art_bird.txt")
The house would be used as background, so I start with writing the house to the “screen”. The screen, in this case, will be home.txt.
love.filesystem.write("home.txt", house)
Now for the bird, I wanted to be able to do something like this:
local x, y = 20, 40 drawArt(bird, x, y)
In this case, x would be the column, y would be the line number. I started by splitting the screen and the bird into lists of lines.
-- Get the current canvas screen = love.filesystem.read("home.txt") -- Create a table. A table is like an array. screen_lines = {} -- The lua pattern (.-)\n says capture everything until the \n (newline). -- We add an \n to the end of the file so that it captures the last line as well. for line in (screen .. "\n"):gmatch("(.-)\n") do table.insert(screen_lines, line) end
I did this for the bird as well. Now I needed to paste the lines of the bird on top of the lines of the house. To break it down in steps
Find the line where the birds need to be drawn.
Get the first part of the line from start to x.
Get the second part of the line from x + the length of the bird art to the end of the line.
Create a new line with the first part, the line of the bird, and the second part.
Repeat for all lines.
In code this would look something like this:
function drawArt(art, x, y) art_lines = getLines(art) -- In Lua, you can get the length of a table and string with # for i=1, #screen_lines do if i == y then for j=1 ,#art_lines do -- With string:sub(start, end) we can get part of a string local first_part = screen_lines[i]:sub(1, x - 1) local second_part = screen_lines[i] :sub(x + #art_lines[j], #screen_lines[i]) screen_lines[i] = first_part .. art_lines[i] .. second_part end end end end
And with that, I was able to make this:
You might notice that the bird has this rectangular shape. This is because it copies the whitespace as well. To fix this I count the number of spaces at the start of the line and add this to the position, so that only the art gets drawn.
-- (%s) gets all the whitespace characters. local spaces = art_lines[j]:match("(%s*)") -- Remove the spaces from the line. art_lines[i] = art_lines[i]:sub(#spaces + 1, #art_lines[i]) local first_part = screen_lines[i]:sub(1, x + #spaces - 1) local second_part = screen_lines[i]:sub(x + #spaces + #art_lines[j], #screen_lines[i]) screen_lines[i] = first_part .. art_lines[i] .. second_part
Much better!
Animation
I started adding more features, like animation.
Each frame gets this {{F}} tag. Then when reading the file’s content I split it based on that tag to get each frame. After that, it’s animating like what you would normally do. Create a timer, and draw a frame based on the timer.
{{F}} _ _ 'v' {{F}} --v-- {{F}} _/v\_
I was also able to implement typewriter text and created separate components like the screen, inventory and option box that I could all stick together. There was still a problem. How can the game know if a file has been opened? This is the other feature I was talking about earlier. In the source of Notepad2 I made it so that a file will be saved right after you open it. Then again it checks if the file’s last save time has been changed, and if so the game knows that the file has been opened and can act on it.
// At the end of the FileLoad function. // If the file is not new, and the file has not been reloaded, save it. if (!bNew && !bReload) { FileSave(TRUE,FALSE,FALSE,FALSE); }
Eventually, I had a good framework to work with, and I could start creating a game around it. After 9 days of development (going by the creation date of the gif-files) I had this:
If you’ve played the game you will know that the typewriter effect and the animations are gone. This is for multiple reasons.
I was always wondering if I wasn’t ruining my HDD/SSD with all these writing operations, so testing the game with these features, and then hearing my computer make a sound made me go all paranoid. I don’t want other players to have this fear as well.
During an animation, you can’t really do anything. If you try to select an option, you will type a character in the box, but before you are able to save the game the next frame has loaded, removing your character from the box. So the animations might be cool at first, but later on, they can get annoying.
Animations are cool, but they aren’t very Notepadish. Text files aren’t supposed to be animated. For this same reason, there is no music in the game. And yes I could have made it so that the game creates an audio file that you can open in a media player, but it would take away the focus from Notepad. I left in a few animations to show off this cool feature. Also when fighting, it’s good feedback that you hit the enemy when you see it blink