How I made a game for Notepad



While reading about unusual solutions from indie developers, I came across gold. Here is an article about the game in a text editor. Art, animation, plot - everything is as it should be.

I created the game And yet it hurt ( perhaps the author wanted to say it hurts, but I could use this option on purpose - approx. ).


The project can be downloaded here , and the code can be viewed on Github .

It all started in 2017 with the question: โ€œIs it possible to make a game in Notepad?โ€ Then I just grinned. Three years have passed. After thinking about how everything will work, and making sure that it is real, I decided to make this game.

Usually you press a button and something happens in the game. Hit A, and Mario jumps. Everything is tied to receiving information and feedback. The game receives input and displays its own.

In the game for Notepad, there will be changes at the input that the user makes to the file, and at the output, the changes that the game makes to the file. To do this, the application tracks the time the file was last saved. If it has changed, the game reads the contents of the file and introduces new data into it.

There is a problem: Notepad from Microsoft does not check if the file has been modified. I would have to save the file, close and open it again. Creating such a game is possible, but it doesnโ€™t sound very fun. I had to look for an alternative.

I can understand your disappointment due to the fact that the game as a result was not made in the usual Notepad. My title can be run in it - just a little confused process. I decided to sacrifice the coolness of the project to make the game more enjoyable.



Alternative


I had to look for another text editor. The only requirement was automatic file updating. Although a little later you will see that I used another feature.

First, Notepad ++ and Sublime Text came to mind. But they do not at all look like Notepad in appearance; the charm of the project would have completely vanished. Plus, they ask the player if he would like to update the file. This is much better than closing and opening a file, but still distracts from the gameplay. I wanted the file to be updated automatically. Then Notepad2 caught my eye. He was almost perfect.

The editor can be configured to look like MS Notepad, and most importantly - it checks the changes made to the file. But just like Notepad ++ and Sublime Text, Notepad2 asks the player if the file needs to be changed. Fortunately, the editor has open source code, and I could polish it to perfection.



Notepad2 is written in C. I am a little familiar with this language, even if I cannot be called an expert. An experienced Javascript programmer will be able to read and understand the general essence of the code, but it was not so easy to understand the source code of Notepad2 to make the necessary changes.

To begin with, I decided to search for text from the dialog box: โ€œThe file was modified by an external program. Reload the file? ". This is the value of the variable that is used as an argument in the dialog box function. And I found her.

if ((iFileWatchingMode == 2 && !bModified && iEncoding == iOriginalEncoding) ||
        MsgBox(MBYESNO,IDS_FILECHANGENOTIFY) == IDYES) {

This code checks to see if the contents of the file have changed. If it has changed, a window opens and the program checks to see if the user has chosen โ€œYesโ€. I only needed to replace a piece

MsgBox(MBYESNO,IDS_FILECHANGENOTIFY) == IDYES

to TRUE, and the program began to automatically update the file. So I created an ASCII-based render. It remains to create a suitable engine.

Drawing


The game is made with love: Lร–VE is an open source framework for 2D games written in Lua. I used this platform for many years and even put together a tutorial . For this project, the Lร–VE module of the file system was mainly used because it provides all the necessary features. Usually, Lร–VE creates an image that is then displayed on the screen.

I needed almost the same thing: the output of ASCII art in a text file. I started with a house and a bird, and the bird was supposed to fly through a file. I took the art that I found on ASCII Art , but only original works are used in the game (except for fonts).



And:



Downloading art is just reading a file.

house = love.filesystem.read("art_house.txt")
bird = love.filesystem.read("art_bird.txt")

The house is used as a background, so I started by drawing this image on the โ€œscreenโ€. The screen in this case is home.txt.

love.filesystem.write("home.txt", house)

I wanted the bird to work in this way:

local x, y = 20, 40
drawArt(bird, x, y)

x is the column number, y is the row number. Therefore, he broke 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

With the bird did the same. Now the code describing the bird was supposed to overlap the house code. Here is what I needed:

  1. Find the line in which the bird should be drawn.
  2. Print the entire line to x.
  3. Print the rest of the line, starting with x + the length of the art with the bird.
  4. Create a new line with the first part, the bird and the remaining part.
  5. Repeat the same for all other lines.

In code, it looks 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

What happened:



Probably, you noticed that the bird is a rectangle - spaces are used in its art. To correct the situation, I calculated the number of spaces at the beginning of each line and added this number to the coordinates so that only art would be 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

It has become much better:



Animation


I started adding more chips, for example, animation:



All frames are located in one file and separated by the {{F}} tag. The tag is determined during reading and allows you to set the sequence of frames. Thanks to this, we get a classic animation. Create a timer and draw frames in accordance with it.

{{F}}
_   _ 
 'v'
{{F}}
      
--v--
{{F}}
      
_/v\_

I also implemented the output of the printed text and separately displayed a screen, inventory and a window for entering the solution. There was one problem. How does the game know that a file has been opened? This is the second feature that I spoke about earlier.

In the source code of Notepad2, I prescribed that the file should be saved immediately after opening. The game then checks to see if the last save time has changed. So she finds out that the file was open, and can change 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);
}

As a result, I got a framework in which you can work. It's time to create a game.

For nine days of development (judging by the date the gif files were created), I did this:



If you ran the game, then you know that it does not have printable text or animation. There were several reasons for this:

  • , HDD/SSD . . , .
  • . , , . , .
  • โ€” , . . . , , -, . . , , . , , , โ€” .


I was furious that the user had to drag the file into the Notepad2 window to start the game. By double-clicking on the file, Notepad or another default program for reading .txt was opened. It was possible to register a command that changed the application for such files on Notepad2, but I personally would not like it if some kind of game did such a feint on my computer.

Maybe return the original settings when you close the game? It is possible, but there will be a problem if the game crashes or closes unexpectedly.

All decisions seemed insufficiently substantiated until I realized that instead of the usual .txt files with another โ€œxโ€ could be used. To be precise, the Unicode character is U + 0445 (Cyrillic Small Letter Ha). In order not to get confused, I named the file * .tXt. As a result, all the game files were with * .tXt resolution, and by default opened in Notepad2.

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

The default program can only be assigned as administrator. If you open the game under a different account, txt files will be used. If you open the file in regular Notepad, the game will inform you that you need to drag the file into the open Notepad window. Or run it as administrator so that it opens by double-clicking.

Motivation


In fact, everything was done three years ago. What did I do the rest of the time? A classic example of lack of motivation.

Initially, the plot was a little longer than now. The dragon killed your parents, you have to go to the blacksmith Ferdan, so he forged a sword. It was thought that the sword is made of three materials that need to be collected. This greatly increased the volume of the game and pushed the end of development. The game was not very entertaining, and I abandoned the project two months later.

But I kept it in my head all the time. I debugged a whole framework that allowed me to create a game in Notepad, and the project did not move off the ground. It was necessary to finish it. In 2019, I did not complete almost any project. Disappointment prompted me to decide: to finish the unfinished in 2020.

And here she is. I shortened the plot, gave myself a month for everything (it turned out a week longer) and rushed into battle. Still applied for A MAZE. Awards, respectively, the deadline was scheduled for February 2. So there was motivation.

Conclusion


I'm glad I completed the game. It's amazing how long the project just collected digital dust, but in the end, a month was enough. The game should not have been made as voluminous as I wanted at first - such a non-standard project should only show the features that can be implemented in it.

What's next? A game of paint? Game in the calculator? Iโ€™m unlikely to make them. But I like to think about games that use non-traditional platforms.

All Articles