July 07, 2025
I made the huge glowshrooms branch and turn as it goes up the stem but now I'm dealing with a bug where the caps kind of smear towards all three positive directions. As in, it creates a cap at the right spot and more above and to the northeast of the correct one
By Minecraft's definition, I just created my first feature. Huge glowshrooms now naturally generate in the glowshroom fields biome instead of huge red and brown mushrooms. I had to create a few new classes because the existing huge mushroom class was a little bit too strict for what I wanted
I created the actual dimension, which currently just features a singular biome that looks like a mushroom fields biome but has yellow water and foliage. I then spent a decent amount of time trying to create my own portal block before going and asking Kryptonaught, the creator of something called the Custom Portal API which lets you create custom nether-like portals with only a few lines of code, to update the API so I could use it. They kindly updated it and I used it to make the portal. I then made an item called the Mycor Key which is used to open the portal and a structure which has an unlit portal frame due to mushroom stems needing silk touch to be mined. I then made a loot table for the structure and a crafting recipe for the key
I added red LEDs which prevent you from winning while they are lit. I also added some new levels and a new track.
I added UI and some more levels.
I added music, sound effect, and a bar showing the current level to the game. I also made some more levels and did a major rework of the last level. I did a decent amount of play testing followed by bug fixing as well which took a large portion of the time.
I made power connect across the sides as indicated. I also made level progression work. The levels are loaded from the file system into an array at the start of the game. During the game, the players progress through the levels is stored as an int. Once the player has lit all LEDs, the check mark on the right of the screen lights up and the player may advance to the next level.
I added a row on the bottom of the screen where unplaced tiles go. You start each level with some tiles already there. I then added the ability to place the tiles from there on to the board. Finally, I made power spread between tiles. You may also notice that I removed the tiles with the open circles. These were representing ground but I decided to make the LEDs grounded by default.
I made a level class to store the data for the game's levels. I updated the cell grid to use one of the levels and generate the grid based on its data. It lets me control which sides are connected to which other sides, in which direction the sides are connected, the size of the grid, and what tiles are forced.
I stole my grid code from the Wolf interpreter and modified it form the basis of the tile system that the game will use. Unfortunately, I only realized after being most of the way through that I wasn't connected to Hackatime since the plugin needs to be downloaded per project on Godot. Oh well. It wasn't that much time anyways I don't think.
This is my submission to the GMTK Game Jam 2025. In it, you build electric circuits by placing down tiles to connect from the power source to all of the lights and from the lights to ground. However, the board isn't flat. It is shaped like various round things. Some notable examples being a ring, a mobius strip, and a torus.
I added a new block called Glowshroom Block that has a few notable properties. It extends the class for mushroom blocks so it can change it's side textures to the mushroom block inside texture when there's been a block next to it. It emits light. It can be sheared like a pumpkin (Except only the item drop actually works like a pumpkin because I only thought of it at the end), producing glowshroom spores. And it quits emitting light after being sheared.
So my plan was very simple: Add in the screen from the very first devlog and enough functions to make it useable. So I put the screen into the scene using a second split container (The same kind of container that separates the input from the console). I then set out to expose the three important cell methods to Wolf: setcellbgcolor, setcellcharcolor, and setcellchar. While testing them, I also decided to add wait to pause the code for a set amount of time and iskeydown to check for keyboard input without using the console. I then realized that the biggest limiting factor at this point is likely the lack of access to data structures like arrays.
This meant that I finally had to confront an issue that I've been thinking about for a while. The problem is as follows: I was storing the data types of things as strings. Strings which might as well have been an enum. This was fine...if you see a data type as a list of mutually exclusive options where you must pick exactly one. The problem is that this isn't how Wolf handles data types. Wolf's data types make heavy usage of inheritance, a feature which these strings just can't really store nicely.
To solve this, I decided to make each data type be a node on a tree, where the type's parent would be the type it inherits from and type's children would be the types that inherit from it. So far this is mostly an internal change but soon it's results will be user facing. This change did mean changing pretty much every part of the interpreter though so that's why I haven't devlogged in a bit
I added function calling (not function declaration yet) and made two new native functions for the console: clear_console and prompt. I also made print work like the other two native functions rather than being a special statement. While adding prompt, I had to turn the function that evaluates function calls into a coroutine because prompt waits for the user to input something. This lead to me having to make the entire chain that calls it use awaits. I had a bug that took me a while to figure out the source of because apparently, despite not relying on the outcome, the functions outside of the evaluator that cause the evaluation of statements also had to use await for this one case to work. But now the functions are working so that's good at least.
The first thing I did since the last devlog was create indented blocks. I temporarily had them work anywhere due to a lack of statements that used them. Blocks are interesting because they each get their own local scope as well as being able to access variables from outer scopes. The way this is implemented in the code is by creating a new instance of the environment for each block and giving that instance a reference to the parent environment for when external variables are accessed. After implementing blocks, I quit letting them be put anywhere and created if statements. If statements, as well as all of the other flow control statements, use indented blocks to indicate the code which is inside of the statement. After if statements were working, I made while loops which use very similar code to the if statements but without needing to handle elif/else cases. They really only start to deviate once you get to the evaluator. Then I added for loops which deviated by a lot from the other two. Pretty much the only part they share is the ending colon, new line, and indented block.
I created a new class for statements. It works similarly to the expression class but it represents something that would take up a whole line. There are three types of statements so far: Expression statements, print statements, and declaration statements. Expression statements just evaluate an expression. Print statements evaluate an expression and print the result as a string. Declaration statements create variables. I also created an expression type for variable references. After creating the statement class and variable expressions, I changed the parser to return an array of Statements instead of an expression. I also created a new class called the Environment which stores the types and values of all declared variables. I then changed the typer and the evaluator to work with the array generated by the parser. After the statements and variable references were working, I added assignment expressions to the interpreter.
Previously, variable types were checked at run time by the evaluator. This would be fine for a dynamically typed language. The problem is that Wolf isn't a dynamically typed language so it wants type checking to happen before the program starts running. At the moment, this doesn't really make a difference but once the interpreter starts handling more complex code, it will become a noticeable difference. It also enables type errors being checked at edit time once I get to that.
To achieve the shift I wanted, I added a new part to the compiler which goes in between the parser and the evaluator. I call it the typer. It works similarly to the evaluator but instead of trying to run each node of the tree, it tries to find the nodes' types and add any necessary implicit type conversions between different number types. I also made the parser and evaluator (and typer) handle a new operator: as. As casts a value provided on the left into the type provided on the right. The ability to perform this conversion is the one place where type related stuff is checked by the evaluator.
I created a manual describing what the different types are, what the operators are, and the order of precedence of the operations. I used Godot's tab container node so it was mostly just a lot of typing.
So remember how I said, creating a file dialog was pretty quick and easy since I'm using [G]odot? Well, I decided to create a web build. When I tried to run this build, I quickly learned that Godot's FileDialog node doesn't work on the web. Eventually I want to try to find a way around this but I decided that it was probably easier to make an area where you can edit your code. Eventually, the goal is that you would be able to use it or open a file from your computer but for now, the web build is going to only use the inbuilt text editor. The interpreter does have a web page now though so that's good at least. https://cattacocattaco.github.io/Wolf-Interpreter/ (It's also linked as the demo)
I built the third part of the interpreter which is the Evaluator. Although it is currently only built for the subset of the language which the parser parses. The evaluator's job is to take the nicely structured tree that the parser made and actually run the code. Currently, that just means finding the value that should be returned. The way that this evaluator works is by starting at the top most expression and recursively finding the values of all expressions that it relies on until it gets to the bottom of the tree where all of the literals are. Once it has reached the literals, it uses their values to calculate the values of the expressions that use them.
On another note, I realized that my test code didn't use any parentheses. When I tried testing an expression that used parentheses, I realized that the parser had a tiny bug where when reaching an opening parenthesis, it wouldn't actually consume the token and would instead recursively read the same opening parenthesis until it produced a stack overflow error. Anyways, I am now doing what I should have been doing for a while: Using actual test cases and actually checking the results.
I built the second part of the interpreter: The parser. Well, I built the parser for some of the language's features. It currently parses the ternary operator, binary logic expressions (and, or, xor, nand, nor, and xnor), negations, comparisons, bitwise operators, addition/subtraction, multiplication/division/modulo, exponents, unary minus/bitwise not, and literals and grouping expressions. Those are arranged from most to least priority with slashes between operations of the same priority.
The parser converts from the linear stream of tokens produced by the lexer into a tree which tells the computer what order to do things in.
Also, realized that I didn't pay enough attention to what the lexer was outputting and that keywords weren't being recognized properly due to a slight mess up in what variable the identifier's string was being stored to which only affected the token's type. So I fixed that bug.
The first thing I did since the last devlog was adding a menu for selecting a file to read the code from. This was pretty quick and easy since I'm using godot so I basically just had to put the built in file dialog node into a scene and connect its file selected signal to a script.
Then, I started creating the actual interpreter. The interpreter has three main parts: A tokenizer (also called a lexer), a parser, and an evaluator. So far, I have just built the tokenizer.
The tokenizer segments the raw text of the file into grammatical units like identifiers, parentheses, literals, and operators. These grammatical units are called tokens, hence the name tokenizer. The tokens are useful because they tell the parser what chunks of characters are connected and what part of the grammar is being represented by those characters.
I made a grid which divides the screen into n rows and n columns. Each cell in the grid can be given a color and can display a 6px by 6px character in a second color.
I made a 16x8 sprite sheet of characters for the cells to display, a bit over half of which are normal text characters while the 67th-115th are for drawing. Characters 116-127 are currently empty. I put the character mappings in a spreadsheet. The end goal would be that the program can add its own sprite sheet for characters 128-255.
I coded a snake game in gdscript to test that the cells and the grid are working.
This is going to be a program which can interpret text files written in a language I'm designing called Wolf.
This was widely regarded as a great move by everyone.