How I made a game played in Notepad


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 Microsoft's 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.</p>

Drawing

This game is made with LÖVE</span></span>. 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).</p>

  

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.</p>
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:

  1. Find the line where the birds need to be drawn.
  2. Get the first part of the line from start to `x`.
  3. Get the second part of the line from start + the length of the bird art to the end of the line.
  4. Create a new line with the first part, the line of the bird, and the second part.
  5. 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 get 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.

// And 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

Default program

I always hated how the player had to drag the files on top of the Notepad2 window that the game opens. If they were to double click the file it would open with MS Notepad, or whatever they have set as the default program for the .txt file extension. I could execute a command which changes the default program for .txt files to Notepad2, but I would be rather pissed if a game did that to my computer. Okay well, what if you change it back when the player quits the game? Sure, I could do that, but what if the game crashes or closes unexpectedly in some other way? All these options seemed like something I couldn't justify. Until eventually I had this idea where I don't use .txt files, but .tхt files. I don't blame you if you think that sentence is confusing, but yes there is a difference. ".tхt" is with a different type of "x". Specifically, it's the U+0445 Cyrillic Small Letter Ha Unicode Character. I will call it .tXt to prevent confusion. So I made it so that all the files are of the type .tXt, and with a command I changed the default program of tXt files to Notepad2.

assoc .tхt=tхt
ftype tхt=[directory]/.notepad/Notepad2.exe "%1"

Sadly, or I should probably say luckily, a program can only change the default program when it is run in administrator. Now with the released game, if you don't run the game in administrator, the files will be of the .txt type. If you open a file in MS Notepad the game will tell you that you need to drag the file on top of the opened Notepad, or that you can run the game as administrator to allow you to double click. After you have run the game as administrator at least once, the default program has been set and the .tXt file extension will be used.

Motivation

So that was all 3 years ago. Well except the default program part. That I came up with 3 weeks before the release. But what happened between those 3 years? Well, this was a classic example of losing motivation. The idea of the story was pretty much the same as it is now, except bigger. Your parents get murdered by the dragon, you need to go to Ferdan the smith, and he will tell you about a sword. Except originally the sword had to be forged from 3 different materials that you need to collect. Because of this, the scope of the game was much bigger, meaning the end was further away. The game didn't feel very fun either. 2 months after starting this project I quit.

But the project was always in the back of my mind. I've built this whole framework around making a game in Notepad, and the project was just laying there. I should finish it and show the world what I made. Then in 2019 I barely finished any projects. Disappointed in myself, I decided that I should change that in 2020 by finishing the unfinished. Starting with the Notepad game. And so here we are. I shrunk down the scope, gave myself a deadline of a month (it took a week longer than that) and rushed it into completion. I submitted the game to the A MAZE. Awards, of which the deadline was February 2nd, so that helped with the motivation.

Conclusion

I'm really happy that the game is done. It's crazy to think that all this time the project was just collecting digital dust, and now I've been able to finish it in about a month. There wasn't a good reason for the game to be as big as I first had in mind anyway. When you have a game that has a weird gimmick like this, the game needs to be as big as needed to show off all the cool things you can do with it. Though I'd say there was probably more I could have done with this game then you can find in the final product. In any case, I'm proud of what I have achieved.

What's next? A game in Paint? A game in Calculator? I'm probably not gonna make them, but I would love to see more of these types of games that use an unconventional platform.

Thank you for reading. If you would like to see more of my games you can find me in the links below, including the source code of this game.

Website

Twitter

Source code

Get And yet it hurt

Download NowName your own price

Comments

Log in with itch.io to leave a comment.

(2 edits)

Dude, this is awesome! If u're interested, I want to help translate the game into Russian.

Again, if you are interested write to me in hm... what do u use there? Ok, do u use telegram? Write to me: htpps://t.me/egogorow

or an email - egogorow@yandex.ru

(+1)

Hey. Thank you for your offer. The problem is that the game is not made in such way that I can give you a list of text to translate.

You could fork the Github repository and translate it yourself, but you would need to know how to program.

Teach me!