August 02, 2025
FINALLY ive made webpages load async! toad has actually had dynamically loading assets like images and stylesheets for a REALLY long time, but still i had webpages pause the rest of the program when loading. this is now fixed!!!!! it was suprinsingly easy to implement, seeing as i already had the infrastructure from the dynamic asset loading. i didnt really implement anything new, just a new asset type for the asset loading system to handle. when you open a page now, instead of loading and parsing the website, then opening that in a new tab, it will instead open a blank tab, and start a thread that loads and parses the website, and add that to the fetch queue, as well with info of the ID of the blank tab. then, when the thread finishes, it finds that placeholder tab from its ID, and replaces the contents with itself.
This honeslty makes me so happy to have finished, i always thought it would be really difficult to implement, but it really wasnt, and it makes the browser experience 10x better imo
i finally made list items draw begin with little dot (or number index depending on list type). the funny thing is the code for this has been in the project since devlog #15, i just hadnt made it render, i had only added the draw context property. so basically the elements were sending the info that they should be drawn with a specific prefix, but it wasnt actually handled.
it still took some time adding the rendering, since i needed to figure out exactly what should get the prefix. first i just made every child node of a list get the prefix, but then list items consisting of multiple elements, like the TOAD homepage has (for green words in the list items), would get multiple dots. i just made it so <il> child elements had an extra fake child rendered, being the dot.
added BACK / FORWARD BUTTON to the top bar!! yipeee! this also included making links open in the same tab, see, previously, links would always open in new tabs, but now they only open in new tabs if control is held when clicked. i also had to restructure the way the main program accesses its tabs, previously, the tabs were directly stored as webpages, but now, i have a distinct Tab
class, that holds its own history of webpages.
Each tab also doesnt just store its history, but also its future- see, when you go back in history, you still want to be able to return forwards. the way i did this is just to have two history buffers, one for history, and one for future. When you go back in history, the latest item in the history buffer gets moved to the future buffer, and vice-versa. The page thats actually displayed is just the last item in the history buffer. havent added keyboard shortcuts to go back and forwards in history though, only the buttons. this is funny since the feedback i got from this project's first ship was requesting keyboard shortcuts for exactly that. will add after i finish writing this :3
Thanks for reading! i love toad
Ive redesigned and reworked large parts of the actual address bar! I felt like the address bar hadnt been receiving enough love, seeing as it literally hasnt been updated since i first added it, on devlog #4.... when this project had like 6 hours. it also hadnt even been updated to use the new graphics system (with buffers, this was implemented in devlog #15). I started by migrating the topbar rendering to the buffer system, instead of printing directly to the monitor, it writes to the screen buffer which gives me more optimization capabilities.
Then i just had a bit of fun redesigning it. The forward and back buttons dont work yet, as tabs dont store history info, but ill work on this next devlog. Everything else is functional. I also had to make the tough decision of making the topbar 3 rows high, rather than just 2, for visual clarity. this does though make TOAD a bit less compact, but i think its worth it (it looks really good imo).
Anyways, will work on polishing tabs next!
i just added 7 playable levels and a win screen. i really want to release a MVP so i can ship this project. the game is really simple, a basic platformer, but ive lost all motivation to continue this project, so i just made a couple levels consisting of a total of 3 different tiles (yeah...) so i can ship this and be done with it forever (or i regain that motivation and make this game into what i wanted it to be from the start which very much was not a precision platformer).
uh, my other games are better.
ignore my terrible gameplay lol, i suggest you maybe add a satchel to the horse so its extra inventory on the horse that'd be cool!
implemented multiple screens! this actually took longer than the 11 minutes reported by SoM but due to som being down i released last devlog too late so it included most of the time from this.
uh so i added new special tiles (tiles with special behaviours that arent rendered), that can tell the game to change to another screen on player enter. i also gave each screen up to 4 different spawn locations, so depending on which direction its entered from, it can spawn player at different spots. looking pretty good!
i reworked the graphics systems! basically, instead of each entity having a texture or animation as a field, and on draw, directly drawing that to the entity render layer, i give each entity an asset id that refers to an animation / texture, and pushes a draw call to the render state for the entity render layer.
this means that all assets are loaded at a single place, which is really nice, and that assets are a lot easier to work with, since these asset tags can be freely moved and copied, unlike having actual texture data, which rust's borrow checker (and common sense), prevents you from easily or cheaply moving. this also means that two entities can have the same animation without the animation having to be loaded twice.
i also created a general Registry
class that is indexed by an associated enum, now used for both animation assets, and screens.
(i also did a lot more work this devlog but SoM was down so i couldnt make seperate devlogs and i had already made the graphic for this devlog so im posting it as is)
this devlog ive worked on enemies, pathfinding and generalising the player physics. most interesting i think is the pathfinding, i decided to use special pathfinding markers to help enemies navigate. these are just tiles in the tilemap, but on a special layer, and you can see in the attachment what they look like. these special markers could for example be jump here, or jump here if player is to the left. and enemies will also inately move towards the player X coordinate.
ive also made the player phyiscs more general which is how the enemy works. this took some time but will save a lot of time later on not having to copy a bunch of code.
added sliding :>
pretty cool! something i didnt consider at first is what happens if you get stuck while under an obstacle, like your speed runs out mid-slide. i had to add a check that if youre sliding, and under a low roof so you cant stand up, then your velocity will be set to a minimum of 0.5. this will ensure you'll always get out
hi and welcome to the first devlog! this is going to be an action platformer thingy so ive set up the basic physics and project. im making this in RUST!! (ofc its the only lang i use but you probably didnt know that). i actually wanted to give up on this project because i am so godamn terrible at art, it isnt tracked, but ive spent maybe 10 hours drawing a little city street environment that ive now scrapped because i hate myself. we'll see how this project goes :/
ig if someone is reading this i probably finished it so, well done future me for saving this wreck of a project!!
I noticed that on my laptop, white backgrounds would show as a dark blue. I figured out that this is due to that terminal replacing the standard white color with its own themed color. Basically, when dealing with terminal colors, you can either use a handfull of standard colors, like white, blue, or black, or you can directly use any RGB color. If you use a standard color, most terminals will use their own shades of the color, according to its theme. However if you use an RGB value, it will generally be respected. So I just replaced all standard colors with RGB values which did solve the issue!
FIRST DEVLOG SINCE SHIP! a little recap on what this project is: TOAD is a web browser written in Rust, that you use in your terminal. It's completely written by hand, even manually implementing things like HTML and CSS parsing!
This devlog i've added a custom implementation for receiving line-input. Basically, when i wanted to query the user for a line of input (like when you click the address bar), i used some standard-library function to do so. This tells the terminal to read exactly one line of text and then submit that. Problem though is that different terminals handles this differently, and it also doesnt give me much customization ability. I got a github issue from a very cool person who noticed that on their terminal, moving the mouse while in this line input, would cause the terminal to get spammed with weird characters. This was easy enough to fix, but it really just made me realize that I want a system to get input thats completely universal, by making it myself! It didnt take that long to do, (ive written manual input handlers for two other projects before lol), and not only did it fix that bug, but i can also do cool stuff like positioning the input area on an input box, i.e., when you press an input box, like the search bar on duckduckgo, the text you type will actually show up inside it. Seems simple but it wasnt like that before! Before it would just show at the top of the screen.
Anyways!!!!! next up, i really want to improve the looks of the browser itself. theres a ton of amazing TUI (terminal user interface) apps that look great that i want to take inspiration from, and now with the freedom of a custom input handler, i can make it a lot cooler!!!
final devlog i hope! i finished up the game, added a win screen and menu screen, added support for web builds, and fixed a bunch of other problems.
one problem was the order that point-of-interest markers showed, basically, based on the player's tags, i.e. what actions theyve done that have been tracked, different point-of-interest markers should show, to highlight for instance which NPCs you can talk to. since the game has grown so much, many old point-of-interest marker conditions didnt take account for the new possible tags, so i just had to go through and make sure everything shows when its supposed to.
i also made camera lazy follow player on y axis, basically just not have it totally fixed and only move it when the player is over a certain threshold.
anyways, games basically done now, unless i think of something else ig ill ship soon
hackatime says i only have 37 minutes of unlogged time but really ive spent like 2 hours drawing assets and mapmaking. ive added a bunch of NPCs and expanded the world a bunch. project is nearing completion! this game is actually really inspired by Thank Goodness you're here!, and much like that game, most NPCs want you to help them with something. i just think its a bit charming and fun doing these silly tasks for silly looking characters. this game is not some kind of fast paced action platformer, but rather just a fun little town to explore with the goal of delivering some mail (and a creative outlet for me lol)
interactable entities makes the game feel more... interactable!? idk it just lets me make more complex stuff in the game and have the player have to move between diffferent areas to complete different quests. these interactable entities show a tooltip, that when pressed will give the player a tag.
this devlog i added a super generic Entity
type! see the attachment, there both the exclamation mark and the speech bubble are seperate entities, with indiviual draw conditions and draw behaviours.
to do this, i also implemented a tagging system for the player, and this is something i always planned on doing. basically, whenever i need to check if the player has been to an area or completed some task, i can create a tag for that, and give the player the tag when relevant, and check whether the player has it.
in this case, the exclamation mark will display while the player doesnt have the HasInteractedWithHenry
tag, and will give the player that tag when they approach. the speech bubble will display ONLY when the player has that tag, and is close enough.
[VIDEO]
ive finally got the player physics to the point where i no longer discover weird edge cases and jank! ive had huge issues regarding negative coordinates, see, when you cast a floating point number, like 1.5 to an integer, the decimal is cleanly removed, in this case, to 1. The .floor() operation also removes the decimal of a float, rounding down, so 1.5 -> 1. The problem is that for negative numbers, casting -1.5 to an int would give -1, while calling .floor() would give -2. In a lot of places, i assumed .floor() and the cast would give the same value, not knowing about the negative case.
This took so long to figure out, but basically meant the collision detecting would work differently depending on whether you were in positive or negative coordinate space.
devlog 4: one way platforms and new textures!
adding one way platforms was really easy, i just added a new layer to the tilemap for one way collision tiles, and i made a special case in the collision code where if your vertical velocity is pointed downwards it will also chck the one way collision tilemap layer.
first devlog! this is going to be a platformer / metroidvania (havent decided yet). i did however decide to try using Tiled's infinite tilemap system. basically, Tiled is a program you can make tilemaps with, and it has an option to create infinitely sized grids, that gets grouped in to 16x16 chunks. i wrote a parser for this and got the world chunks rendering! having the world inately split in to chunks will also help later on for optimizations!
next devlog i think ill just keep setting up more core systems and perhaps a basic player
made mod work on servers! since mod is nearing completion i decided to check if it works when run on a server, with players connected. i set up the server, built the jar, and.... crash! and a really cryptic error log too, since the class names were stripped, and called, for instance class_5601
. the error seemed to originate from this class_5601
, complaining that this class didnt exist ??
with the help of a regular old search enginge, i found a page from maven.fabricmc.net that translated class_5601
to minecraft/client/model/ModelPart
. turns out, i had defined the model for the elytra saddle in the shared server/client space, when it shouldve only existed client-side. this is important, because the server side literally doesnt know about the client side stuff, like rendering, and when i tell it to define a model, it doesnt know what that is and crashes. I moved the model definition over to the client side entrypoint, and voila! it worked :>
i also worked on some smaller tweaks, like editing some textures, and fixing some project settings, but that wasnt very interesting.
might ship soon :}
now horse scuba gear also prevents drowning!
(i made it so both player and horse get infinite air time, might change later, idk)
make horse scuba gear prevent player getting dismounted in water, and also made it allow jumping while submerged!
the scuba gear is now semi working! one thing though is that the player and horse still drown while under water. will work on that lol. i will either make both player and horse get infinite air time, or just give horse infinite air and give player something akin to the effect of the turtle shell, that just dramatically increases air time
started adding scuba gear for horses! i know it sounds silly but think about it, whats the most annoying part of having a horse in minecraft? crossing lakes, since you quickly get ejected when trying to swim with the horse. so why not kill two birds with one stone and instead make water travel a strength?
im thinking youll be able to infinitely go under/over water while on your horse with the scuba gear, making it great for underwater exploration! the scuba gear is also equiped as horse armor, meaning you can combine it with the elytra sadle for a truly OP horse.
currently the scuba gear doesnt do anything and the model isnt done lol but i figured i'd do this mini devlog anyways, since it was quite hard setting up the armormaterial and equipmentasset
mini devlog, but ive now added a crafting recipe for the elytra saddle! truth be told i had just forgotten to add a recipe for it, but now ive done it!
fixed the animation bug from last devlog! the problem really wasnt hard to solve at all, i was just way off. when adding the custom model, i had copied the code for the regular saddle, and removed everything i thought was unnecessary, just to clean the code up a bit so i could understand it and use it, but i also removed the function call for setAngles
which was the function that was supposed to update the rotation for the jump animation. readding that allowed the elytra saddle to rotate with the horse! (though i also needed to copy the rotation formula from the horse class to make it be the same)
Anyways, i want to make the wings spread apart when gliding in the air now, that would look cool :D
i made the elytra-saddle model not just be a literal black box! i'm just using the texture of the regular in-game elytra, and just moddeled two wings like this. though a problem im having which seems like its going to be a tough nut to crack is the animations the horse does when it jumps, because my elytra-saddle model doesnt follow the horse body, and instead just stays relative to the head or something. it looks really weird since it doesnt rotate is all. i think to create animations you need to use an instance of a model rather than a static function that generates the moel data, like ive been doing now, so that it can be mutated, or at least thats what it seems like the built-in saddle does.
problem though: i dont know how to do that. you need a reference to some data registry that i dont have to create instances of models i think. anyways, ill try my best :=)
ive spent SO long trying to get this custom model for the elytra saddle to render. right now its just a large block at the horse's feet, but ill change that to wings later. it also doesnt have a texture yet- i normally would want to wait until ive got that finished before releasing a devlog, but since ive spent so many hours just getting to this point i figured id release an in-between devlog.
Like ive said in previous devlogs, im new to java, and my god are things just terribly documented. coming from rust, where docs for all libraries are automatically generated, and where the language itself is really transparent, its really hard figuring out which functions and values to use. In this case, most of the problem is just that minecraft is closed source, which means the only docs are community made, but a lot of other tools, like mixins, ive had a hard time finding complete documentation for. same goes for Fabric (the mod loader) itself! ive had to use fabric functions that straight up just arent documented!!! what is that!?
so really all you have to go off is just searching minecraft's source and try to figure out how to get things done.
anyways, i think getting textures for the saddle working wont take that long, but you never know with Java. we'll see :)
I started working on an Elytra-Saddle! basically gives your horses the ability to fly. i just used mixins to make horses use the gliding state when in the air and the elytra saddle is equipped, and to allow their jumping even if in the air. i havent added a custom model for the elytra saddle though which i really want to. It would look cool basically having pegasuses in minecraft!
add dispenser integration so horse pockets can be emptied with them! this was suprisingly easy to implement, i just looked up the dispenser source code, found it used something called ItemDispenserBehavior, so i looked up refernces to that and found the code that registers the behaviour of fire charges, and from there it was really simple.
im starting to get the hang of java and this IDE (ive only ever used vscode before, so an IDE like this, that is basically more context aware than i am (lol) is really new to me but also pretty cool)
added some sound effects and new textures for the empty and filled horse pocket! also made the name of the horse stored in the filled horse pocket's name, like how it works with fish in buckets!
wowie, ive now made horse pockets actually store the horse, and allow you to place them back down. it took SOO long for me how to figure out how to encode/decode data from minecraft's item NBT data, to actual entity data. i initially looked at the code behind buckets, since they have a similar mechanic of picking up fish. this sort of worked, but i had to manually add all fields to track, and create mixin invokers for the fields that werent publically accessible..... Then, i found out about Write/ReadViews, which is basically an internal system of encoding/decoding entities to NBT, exactly what i spent like 2 hours doing manually. Rewrote all that to 5 lines of code using the Write/ReadViews, and it works much better lol
Horse pockets are basically complete. Will work on other features next!
First devlog!!!!! Ive barely ever used java before, so everything minecraft modding related is unfamiliar to me, but i feel like i got some decent progress for a first devlog! I added an item, a horse pocket (lmk if you get the reference), with a crafting recipe and when you right click a horse with it, the horse dissappears. I havent added a way to, you know, get the horse back, currently it just despawns, but ill work on that til next devlog.
This entire mod is just going to be about adding some QOL features for mobs, like i want to make all animals pettable, and the horse pocket i think is just funny and also would make horses for travel actually useful.
Anyways, ty for reading, will continue working on this now!
a minecraft mod that adds some fun horse gadgets to make horses more relevant, like a horse pocket, to instantly be able to pick your horse up in to your inventory!
I fixed the tab bar truncation, basically when you open many tabs on a small screen, i need to limit the amount of space each tab is allowed to draw on, except for the selected tab which should always be drawn in full width. This wasnt too hard to implement, just a bit tedious as i have to do it twice, once for drawing, and the other for detecting when the mouse presses a tab. Ive also had to take a break from the project for a week, since i partook in the brackeys game jam :>
Other than that, i also started using a rust input library for typing in input boxes and the address bar, as it gives me more control, and lets me ex. prefill the address bar with https://.
Honestly this project is mostly finished, but since i wont be able to ship until my other project is done being certified and voted on, ig ill keep polishing this project until then.
Project has now been submitted to the gamejam! Its 3 am and i am realllyy tired. Gamejam ends in 8 hours. Since the last devlogs ive just been setting up the itch page with the description and images and logos and all that. I also had to whack down some bugs i found and also make F key open inventory, rather than exclusively using Escape, because on web builds, pressing esc will leave fullscreen. This is the type of thing you dont think about until last minute.
I'm not really that satisfied with this project, i feel like i shouldve kept a smaller scope, but none the less im just glad i finished the game. I might spend some more time working on the project to perfect it to the point i just didnt have the time for this week, but who knows. Right, im going to try shipping this after i publish this devlog, so wish me luck on that lol
If you want to try out the game, the demo link should work hopefully, and you dont have to download anything, it just runs in your browser. I wish the game had a bit more content and variety, oh right, and sound effects. Had planned on adding that on the last day, which i thought was tomorrow, but was today. Guess the game doesnt have sound because i had to focus on more important aspects of the game (sorry sfx nerds).
Anyways, will ship now, bye!
added win/loss states! for the first time since the start of the project the game doesnt crash on death! also added some hover tooltips when standing near items which was desperately needed, so you for instance know what item you're buying in shop, rather than just going off looks.
ADDED NEW WORLDS! i made the starting world that ive been using all this time instead be a forest, and have the next world be the crypt, which is full of skeletons. one of the enemies in the crypt is literally just the executioner from clash royale lol (shhh).
right now each worlds has 12 levels, when you complete all you move on to the next world, but i sort of want to rework this, since i want some more variety in the earlier game. I considered making the first world only have 5 levels or something, but a bit unsure. Just dont want people playing the game for the first and only time and only getting to play a single world with the same enemies. game design is hard :<
One bug ive got to fix is related to picking items up. basically, i want items picked up to automatically place themselves in the respective slot type, if there is any available, rather than by default ending up in inventory. This is also sort of necessary because theres a curse called less inventory, that if you get 5 stacks of, will literally remove your entire inventory. this makes it literally impossible to equip any items, since you cant for example pick up a helmet directly off the ground on to your head, it currently HAS to go through your inventory first.
other than that, i kinda just want to add some more items and maybe one more world. tomorrow is last day of gamejam though so ill have to speedrun this lmao
a simple way for you as the maker of a game to artifically increase the amount of content of the game is to add random modifiers, which ive now done with random enchantments!
the enchantments themselves are really simple, basically just an optional extra Stat
property of each item, but it still adds a lot of variety. see, imagine youre using a longsword that you really like, but then find a longsword of holiness, youre still going to want to get that new longsword. it also means pursuing the perfect loadout will be harder as theres more rare stuff to get.
i also mutated items damage as rounds progress to make items found later have higher base damage to compensate for the harder levels. dont want this to get too OP though, just an underlaying layer of progression. i also fixed some ui problems, in that previously the ui wasnt allowed to fill out the entire screen, and existed only in the center area. now ive made it fill the entire screen, which really helps because now the curses ui wont cover up potential enemies.
will work on more worlds and enemies i think, prob also more curses.
:>
I added a new item type , talismans! I want them to let you sort of specialize your build, by offering somewhat more complex mechanics, like the fire neclace you see in the video, that has the effect that whenever you deal fire damage, it also causes one of those extra burning fires to spawn on the enemy, sort of making the fire spread. Then later in the game, i also want to add item enchantments which could for instance make your sword deal fire damage, and then these items could synergize really well. Eh you get the point. I also just improved the way projectiles work a bit, one problem i had earlier was that projectiles traveling too fast would just pass through enemies, but now i force projectiles going over 6.0pixels/frame to calculate their hit detection in smaller steps (still moving same speed total though). I also gave projectiles unique radiuses, so that larger projectiles have larger collisions than say a small arrow.
Most fun devlog yet!!! I was unsure what direction to take the game but I had an idea, a chaos demon shop. The way you get items is by buying items from this shop that appears after every room. When you buy an item, you also get a chaos curse. These are intended to be really random, so they might not even be a curse, but rather a blessing of sorts. My idea is that you'll maybe see an item you really want that would make a cool build or something, and you'll be forced to make a deal with this demon, a sort of gamble, as the consequences could be detrimental. The theme of the GameJam is Risk it for the biscuit, which i think fits pretty well on this, as you're taking a risk to get a reward you really want. I previously had another idea for how to incorporate the theme in to the game, but i much prefer this. I also really like the chaos demon shopkeeper, its cute.
I added 9 curses so far, of which 4 are positives. Some examples are enemy shields, acid puddles, bonus enemies and repair armor. All curses also stack, so for example, each enemy shields curse you get, it will give all subsequent enemies 5 extra shield HP.
Now, I just gotta add a bunch more chaotic curses and cool items. Also I still want to add more worlds, but i'll make them linear, unlike my previous idea of having the player able to move on to next world whenever. Byea :>
New items ! I had already drawn most of these textures a while back so most of them i didnt even have to draw and could just add the code and stats for. I did have to also add some new mechanics surrounding projectile on-hit effects, to make the star bazooka (see image), cause an explosion on impact. Still not perfect, but it looks cool none the less. Not the most interesting devlog but at least i have some items to test with rather than just like 4 :>
Honestly, will either just work on adding more items next, or adding the other worlds. Probably the latter, just to switch things up. bye!
Added some item hover information so you can see item's statistics and name. It looks pretty basic right now but it still took some time to implement. for one, I wanted different keywords to be color coded, and for this i ended up implementing something a bit similar to ANSI escape codes. If you dont know, ansi escape codes are special magic sequences of bytes, that when printed to a terminal, change something about the terminal state, most often use to color text in terminal apps.
I added something like this, but just with the bytes for \x00, \x01, \x02, ..., \x06. Each byte's value is an index to a color lookup table. Then I use GL shaders to recolor whats drawn next to be the color specified. i wrote this shader some devlogs ago when i added enemy hit effects, when i used it to make them flash white. Now i added an argument for what color to change to.
Guess I'll finally work on adding actual content (items) to the game.
[SEE VIDEO]
i added ITEM PODIUMS which means the game technically has some sort of progression as you now get rewards at the end of each round! I think the animation looks satisfying as they emerge from the ground.
theres only like 5 items in the game total though, but now the system is sort of in place! though i havent made any sort of specialized RNG, its currently a perfectly even chance to get each item, which is not how i intend for it to work later on. Items should also have their stats mutated by some factors, such as which room you are on, the further you get, the better the rewards basically. Will work on finally adding items next!!!!!!!
would you look at that!!! ive added a really solid system for enemy behaviours, allowing them multiple phases, each giving them different behaviour and/or appearance. Each phase also has an End Condition, which could be something like end after 30 frames, or end immediately, or end on collision with wall, which is the glue that allows these phases to be chained together.
YOu can see in the video attached this enemy, that does a sort of charge towards the player every few seconds.
The enemy technically has three phases, first, stand still for 60 frames, then, for one frame, chase towards the player, then, until collision with wall, just keep running in the same direction. The reason for that single frame chase towards the player is just so its angled towards the player.
I think this sytem will allow for both cool late-game enemies, with complex behaviours, but also just some slightly more interesting early-game enemies, like idk a slime that moves like a wormm, or enemies that run away from you, then shoot a projectile, then run towards you, etc. Pretty simple a system, but it allows for complex enemies, which is what kinds of systems i love!!!
i finally decorated the arena youve been playing on this whole time! its not much, but it feels a lot more polished. I also added that door which opens when you finish arrow, allowing you to progress, and when you pass through, theres a sick lil transition :)
Again, not much, but helps the game feel polished. thoughh...... i still havent added any way to get items, or any items for that matter, or the different worlds this game is about, or any other sort of progression, so i still have a bit to work on. Remember, this is for a Game Jam that ends in like 3 days. i dont actually know if i'll make it, but a fun project none the less :>
But yeah, i should defo work on post round rewards, and the different worlds (i think ill make just like 3 or so)
Made game properly scale for any resolution! The last game I made had a fixed aspect ratio of 4:3, but thats honestly quite annoying if you want to play in fullscreen, and you dont have a 4:3 monitor (which you very likely dont). For this game I figured I could just pad the sides to make it fit any resolution. Currently its just black, as you can see, but i plan on making it have some sort of wall-tile background. I also made the game taller, so the actual content is now not 16:9, but rather something 50% taller. I think this will be good as the game felt very cramped on previous versions. I also added enemy-to-enemy collision (if you can call it that), basically, in their movement, i check if theyre really close to any other enemy, if so, move away from the other enemy. It really looks like theyre colliding though, as it will rapidly switch from moving away to moving towards where it was going, making it appear to get stuck behind something. I thought this was necessary because previously, all enemies who were chasing the player would inevitably congregate in to a single pile, which looked odd.
Devlog 8! i've implemented item stats, some UI and an inventory system. I want interesting items like the ones in Tiny Rogues (like this project is very influenced by), where most item's will affect the player's stats in some way. A lot of these stats can be quite complex, like making all modifiers to your movement speed also apply to your attack speed. I dont think I'm going to add something like this directly, but complex modifiers like this is something I definitely want, so a lot of time was spent on setting up this stat system. Something I learnt from my last game is to write down a list of mechanics that I want to achieve, and from there build out to figure out how complex the system needs to be.
the inventory is pretty simple, you have some free slots, and some slots for active equipment. You can also drop items by dragging them outside the inventory area.
Will work on making a proper gameloop, instead of having levels just infinitely spawn until you die.
Added levels of some kind! I created a variety of layouts/formations for enemies to spawn in, which will be filled with enemies from your current world. The formations get progressively harder and harder as you progress rooms on the current world, and I intend to try to make it scale exponentially, as you're also supposed to get better and better gear. But when you progress to the next world, theres new, stronger enemies, but the formations return to basics, and once more progress as you complete rooms on the current world.
I added a hit effect to enemies, to make it more obvious when you hit, and because its kind of satisfying. Macroquad supports gl shaders, which is what I used to make the effect, basically, on hit, change the draw Material to use a shader that replaces each non-transparent pixel with #ffffff (a.k.a white).
The trickest part was getting transparency to work, for some reason, by default, these shaders dont support transparency. I did eventually see the keyword Pipeline in the macroquad docs (macroquad is the gfx library i use), which sounded relevant to how things are drawn, and I managed to configure it to use transparency.
Added enemy & player health, as well as gave the enemies health bars! I decided to go for the route Tiny Rogues- a cool roguelike - uses, where the player has a fixed set of hearts, but the enemies have a floating point health bar. This way your weapons and gear can scale nicely, while still always having the risk of a single stray bullet taking one of your precious lives, which I also think fits the vibe of the game where risk is central.
Will possibly add some UI for player lives, otherwise I'll focus on more core mechanics and adding items and perhaps a level to play on!
So Summer of Making's Media was down for the last two devlogs, which is why you (at least right now) can't see the attachments. So the gif here is meant to show what was basically done in those devlogs. Ive started adding enemies and items, and a player. I also added the ability to perform combat rolls as you can see.
I started adding projectiles and particles too, but haven't got very far. Will work on player health and enemies next :)
Working on items and enemies! Different enemies will have different behaviours, the one in the video is simply set to wander, but ive also implemented chase and still. I think enemy variety has to be good for this game to be fun. I also added visual armor and held items, as you can see the character is wearing a little chestplate and is holding a sword. The reason why i made the item float like that in front of the player is just to make it clear where you're aiming. If it looks weird I might remove it later, IDK.
Set up basic player infrastructure! I've created the player struct with properties for armor and equipment. Something cool about loading textures directly from an asesprite (pixel art tool) project file is that I can for instance have only the second layer be the one used in-game, while the first layer is just reference or template that I dont want actually shown in-game. Will work on enemies and player items next!!
FIRST DEVLOG! This game is going to be my submission for the 2025 brackeys game jam! I've set up the project with my favorite graphics/game library for rust, called Macroquad
. Something I learnt from the last game like this I made, is that using asesprite (a pixel art drawing app) with PNG files suck, because they lose so much formatting every time you close the app, so I decided to see if I could have my assets loaded directly from .ase
files, and turns out, there is a rust library for that! So I've set that up, which will make my life 1000 times easier!
The game is going to be a roguelike, and since the theme is risk it for the biscuit, I want the game to be split in to worlds. Each worlds has levels, each getting exponentiall harder, but you can stop any time, and move on to the next round, however, if you do, you'll miss out on the exponentially growing rewards for each level. I think this would be cool, but this early on in the project, my idea will probably change a LOT, so if you're reading this devlog in the future when the project is something totally different uh... thats unfortunate. Anyways, wish me luck!
Ive been working on improving the rendering of specific webpages, in this case, DuckDuckGo and Wikipedia, which are the two websites ive used a lot for testing. Getting both of them to work properly was sort of the goal of the project, or rather getting a search engine and Wikipedia. I noticed a lot of malformed text on some wikipedia pages, which i traced to these HTML tables. Adding a quick fix for them wasnt really hard, since they, like most HTML elements fall either in the category of div-like, or span-like. A div is a container that is drawn on a newline, while a span is a container that is drawn in-line. In my code, most elements are just inherited from one of these elements, with the only change being the name. I also implemented a fix for a DuckDuckGo visual bug, see, previously the Submit button would be rendered inside the first search result, which looked terrible. This was because it was a child of a container with a fixed height, that too short to fit the submit button. My fix was simply making no elements have a height thats shorter than their content's (i.e. their childrens') height. This was kind of a hard decision, since it goes against how real browser handle it. I figured though, that since this prevents elements from overlapping, that it would be a net-positive for TOAD, as it would make sites more readable, even if it meant straying away from the standards. I also further added mouse support, namely making tab bar and address bar interactable with the mouse. I also made closing all tabs close the program, when previously it would simply display the last rendered page indefinetily, until you closed the program or opened a new tab, which was kind of odd. This is also more like a real browser.
Honestly I kind of want to ship this soon, especially since brackey's game jam starts in like 10 hours, which I definetily want to partake in, and also devlog in SoM, so finishing this project up before that would be nice, but I'm not sure its polished enough. We'll see :)
[VIDEO SHOWCASE]
I added MOUSE SUPPORT!!!!! (pretty standard for a browser lol)
Getting mouse events working was suprisingly easy, the library I use for communicating with the terminal was easy to enable mouse event capture. The difficult part was mapping out where all interactable elements where. I solved this by literally having the same buffer used for drawing, also have a buffer for every pixel whether it is part of an interactable element. So whenever we draw something to the screen buffer, the modified pixels are also set to be the index of whatever interactable element the thing we drew was part of (if any). Then, on mouse move, its as simple as querying the screen buffer to check what interactable index is at the mouse cursor. Works like a charm!
You can see that the UI on duckduckgo is slightly bugged at the top. Maybe I'll fix it later, but from my debugging it seems difficult, as I'd have to implement a lot new styling, like for positioning, and background images. I'm scared adding positioning, if it isnt perfect, would actually break websites that work now, since maybe elements would get overlayed in an unreadable manner. And adding background images would require the draw context to be heap allocated, which would mean I'd have to rewrite A LOT of code, since I can no longer cheaply copy it.
But we'll see, maybe I can do something :)
I noticed some weird error logs of images not being able to load, which appeared to have really strange URL sources. Well, turns out HTML lets you pass raw base64 data directly as an image source.
I just made a little hook in my asset loading function that checks if the target data type is an image, if so it checks if the URL scheme is data and then handles the base64 parsing, and sidesteps the default behaviour of sending a network request.
DUCKDUCKGO IS SORT OF WORKING !! Everything still looks really buggy, but ive implemented HTML forms now! In a website, to send some query data or form to a server, that can either be done with Javascript, or with a <form>
. I'm not adding JS support, but I figured that implementing forms wouldnt be that hard, and it would make search engines usable! (and maybe some password logins).
It took really long, there was a lot to figure out, especially how to store the state of all inputs on the current webpage, and tie them to a parent form, but I just pushed through and now it (very barely) works! You can see in the GIF that I can actually use duckduckgo.com, which is a really cool feeling. A lot of styling is messed up though, so I'll have to get around to fixing that. I also had to add <meta>
tag redirects, which is a way to redirect a user to another website without JS. Duckduckgo by default has a very Javascript heavy homepage, something that my browser cant render, but it will redirect all users with Javascript disabled to a JS free version of the website, which uses forms. This redirection is done with one of those meta tags.
I also tried a couple different search engines like google, bing and startpage, but none of them were as compatible as duckduckgo.
Anyways, like I said, I better fix the styling issues of duckduckgo, as it has a lot of random empty space, and weirdly formatted text.
I noticed this very URL was really slow on my browser, and realized it was probably due to all the images. While the program cached the images themselves, they still had to be resized- scaled down- each frame. This was pretty slow on a page like this, where my profile picture is drawn many times. Each time its drawn, its the same image, with the same target width and height, so i decided to add another cache, specifically for resized images. When an image wants to be drawn, with a specific width and height, it first has to check the resized_images_cache, if an image from that source URL, with that width and height already has been cached, if so, that cached image is pulled. Otherwise we do the scaling operation and cache the result. Like I said, the cache has to keep track of not only the images source URL, but also their width and height, since the same image can be included multiple times on the same website with different sizes. This caching system fixed the lag on this page, yey. Interestingly enough, this new code forced me to-for the first time since I started learning Rust- use a Cow
. A cow, at least in the way I used it, is basically a type in Rust, that for any given type, lets call it T, either holds a reference to a T, or an owned value of T. This is useful in my case because, if we want to draw an image from the cache, its going to be through a reference, but if it has to be resized now, we will have direct ownership of the object. With a Cow, i can have a single variable that could be either of these states, and I can pass it as a regular reference without changing any other existing code.
[SEE VIDEO]
I finalllllyyyyyy basically fixed the flickering that was sooo pervasive in basically all prior versions. Just optimizing the drawing simply wasnt enough, I had to really restructure all the screen rendering. I decided to look at how other TUI apps draw the screen to avoid flickering. I have a Rust TUI editor called Helix, which is FOSS, that I inspected. It has a screen buffer which consists of a 2D array with Cells. Each cell just has a defined character, color, and styling (such as italics). Then on each render, we compare the new screen buffer to the last, as to only draw the changes. I implemented a very similar system, including some edge cases which their code was designed to handle (its fine to copy code like this, its what FOSS is for, and besides, their implementation was a fork of another rendering library!). Anyways, it looks great now! Another huge benefit of this system, almost greater than the diffing, is that its always drawn top to bottom. This makes it so even when it should flash, like previous versions did, its just looks like the lines at the bottom of the screen are loading in. This new rendering system is such a huuuge improvement!!!!! Im really happy with it! Like just look at that video comparison, wowww!!!
When I fixed the text rendering last time, one change I made actually broke something else, basically, I was fixing the way text's newlines got rendered, because previously they would simply originate from the element's X, but actually it shouldve originated from the parent's X, otherwise it would frequently be drawn offscreen. I fixed this, but realized later that this logic was only implemented for the drawing part of the program, not the layout part, so other elements still expected newlines to originate from the element's X causing this mismatch and multiple text's being drawn on top of each other, see the Bugged header in the attachment, compared to the fixed version. I also noticed a bug especially on wikipedia, where words in a paragraph are often replaced with links, that sometimes the spaces afterwards would be missing. Normally, I disrespect whitespace on most elements, I.e. remove repeated, trailing and leading whitespace. But Ive now added the exception for when the previous element was also drawn on the same row, to the left, to allow specifically leading whitespace.
Next up, I'll continue just testing out different pages to find bugs and fixes. :)
I FINALLY fixed one of the most glaring (text rendering) issues of the program, which were these 0 width paragraphs, that since they were 0 width, required line breaks on each character, causing them to extend vertically downwards. This made most text on Wikipedia unreadable. I said last devlog that I wanted to just focus in on making Wikipedia render well, just adjusting everything and adding all features that I need to, and because of this I've also added CSS ancestor selectors or whatever they are called, ex. when you apply a style to all <p> children off <divs>, with div p { RULES }
. I also had to fix a bug in my implementation of my dynamic asset caching system, as I noticed wikipedias stylesheets werent loading, basically, in the part that cached the assets, the URL had its special characters decoded, but in the part where the assets were requested, the URLs didnt have their special characters decoded. THis took SOOO long to figure out, because I was getting no errors, I could see the assets being cached successfully, and when I manually tried applying the CSS, it worked too, but now Ive finally fixed that. I also fixed some other text rendering bugs, and now, wikipedia looks nearly presentable!! I know about one big (but probably easy to fix) text rendering bug, actually visible in the attachment (if youre keen-eyed), but other than that, things are getting kind of good lmao. Its really cool going to some websites I were using earlier in development, and seeing them actually render properly, images, CSS, and all.
For next steps in development, other than that one bug i spoke of, I think I just have to start trying to render a bunch of different pages and fix all the million bugs that will arise lol
Another small devlog, but I decided to actually create a nice little homepage that isnt just to test the rendering engine lol
It's cool having the project far enough along the line that designing the home page is just writing a regular HTML file and having it look exactly like I'd expect it to! The page has both internal and inline CSS, and uses divs and lists, and has nested spans for the colored words and im pretty impressed with how-despite the fact that this browser is terrible-well it works on this simple example !!! I decided to just put some keybinds that will tell the user how to actually use the program, which I figured would be helpful haha. I also noticed that this entire time, ever since I added image rendering, I've had a PNG of a hämis from the videogame Noita included in the program's source code. I had used that as a test image and simply forgot to remove it. But now ive removed it, and im sorry to all noita players who will take offense at that. Anyways, my summer break ends today (:sob:), so I'll try to keep up with devlogs but IDK how that will go, but I really want Wikipedia to look good, and it would be cool if this very page would render properly, so I kind of want to just really dig in to those websites and just add features and adjust settings until they look right. Just so I could have something to show for all this.
Very minor devlog here, I just added scrollbar and fixed a really specific bug with the dynamic asset loading system thats been on my mind since i first implemented it. The scrollbars were really easy to add, since the element's draw code already return some draw data, which includes content height. This was previously only used in recursion, to position elements properly, but I could just use that at the root level, to get the height of the entire webpage. I then added a check such that if the content height is larger than the screen, display a scrollbar. The scrollbar is just a single ascii █ that has its Y relative to how much youve scrolled. I think the scrollbar does a lot to make the pages feel more responsive, and easier to navigate, even though its a tiny change. The asset loading bug I mentioned is something that I knew about when I first added it, but put off for later. Basically, whenever an asset (like an image) finishes loading, I refresh the current page. This is however wrong, since it should refresh the page it belongs to. I implemented an identification system for webpages, so that each page gets a unique int to itself, that I can use to specify, and search for, a specific page. Gonna work on text displaying now, ive noticed a bug where text renders in a container that appears to be 1 column wide, making the text extend vertically downwards.. hmm, will have to fix that.
[SEE VIDEO FOR SHOWCASE]
rendering images was suprisingly not that hard, loading images, on the other hand, took a lot longer. Basically, the infrastructure required to draw images to the screen was pretty much already in place, Its roughly the same as drawing a rect, but instead of going row by row drawing solid colored pixels, I draw pixels from an image. I also had already written a rust terminal image drawing library, so that hepled lol. BUT, what was really the challenge of this devlog was the system of dynamically loading assets. Basically, when you visit a webpage, your browser shouldnt wait for ALL the images and external stylesheets to continue loading, rather, it displays what is already loaded, and sends requests for the other assets, and when they finish, the page updates. This system is a bit more complex, as you need to understand some bit of async programming and data structures. I implemented this by having a buffer of items to be fetched be filled when I parse a page, whenver it parses and element that links to another page, add that to the queue. Then, we take that entire queue, and for each item, create an async thread, basically a parallelly running function, thats in charge of fetching that specific item. Then i have a global table of these threads and their respective URLs, and in the update loop, I check if anyone has finished, if so, add it to an asset cache hashmap, and update the page. Any future requests for that item will go to the asset cache directly. Pretty cool! I also had to make the update loop run at least once per second, rather than only running on a key event, to make assets load whenever possible. I added both support for image loading, but also stylesheets, see, many pages have the CSS file, i.e. the styling code, in a seperate file, and link to that. Now TOAD has support for it!!! yey! Its cool because this lets me really see that the asset loading is working, because I initially see the page flash without its styling, then a second later it refreshes and is styled, and on reloads, its styled right away. Right, for images, I also had to downscale them so they fit in the terminal, which makes them all render as pixel art pretty much. Furthermore, ive added caching of a websites drawcalls! This became a necessity after I added external stylesheets, as wikipedia's CSS was so complex that it made the page slow down really bad whenever a redraw was required. Instead, I only actually redraw when something has changed, and cache all the draw calls generated. Then, for scrolling, I can just reuse the same draw calls, just change which ones are shown (based on scroll).
I want to focus on making pages read better now, and knowing what elements ex. part of the sidebar, and what should actually be displayed as contents. Take wikipedia for instance, to reach the actual article, you have to scroll like 4 pages of junk data. It would be cool to have it render properly.
Devlog 11! After all this time, this browser is actually finally able to browse the internet by ✨clicking links✨
WOO! It didnt actually take very long at all to implement, ive just been lazy up to this point (actually it relies on a lot of infrastructure ive set up previously). One thing I didn't think about is how child items of links are affected, basically, initally I had every child of a link register itself to my array of InteractableElement
s, but I realized that a link can have many children, which all should count as a single link, rather than 10 unique. To circumvent this I just added a counter to the global state, which I use to make all the children of a link have a single unique identifier. Then I can group interactable elements by identifier. I also added support for more HTML character encodings, which I mentioned last devlog. Since parsing them is quite complex, I had to make a concious choice on how to parse them. At first I considered writing my own parser that iterates through the text as a buffer of characters, but I also considered using a regex library. I really didnt know which option would be faster, so I did both and benchmarked them. So while this devlog is logged as roughly 1 hour, I spent several more in a temporary project writing both these parsers, and benchmarking them. It turns out, my parser was roughly 3x faster on small inputs, while on large files, the regex one was like 80x faster. I decided to go with the regex one, since most html files are large enough to make it worth it. Other than that, I fixed one of the biggest issues of my HTML parser, so it now parses a lot more sites, though I noticed that ive still got some glaring alignment issues with text that I need to fix. I also want to add IMAGES!!! I actually have made a terminal image renderer library before, so I'm just gonna use that lol. Also I would definitely need to add a max size of image for it to be rendered, which would be like 9x9 pixels or something lol, so not very practically useful. Though perhaps I could downscale images ? We'll see!
In the process of optimizing the renderer in Devlog #8, I also really screwed up the rendering, which this devlog was mostly about correcting. Inline text, for one, was COMPLETELY broken, either rendering wayyy offscreen, or overwriting the previous line. Thankfully I could basically check git history to see what I had changed, to find where the problems had arisen. One thing that took really long to realize was that I had forgot to make some text elements have the width of their text, and instead default to 0... yikes. I also added some more CSS features, like class/id selectors with element requirements. Basically, in CSS, you can apply a rule to a specific class with .classname { rules }
, but you can also make that rule only apply to elements of a specific type with that class, with element.classname { rules }
. I also added support for some HTML special character encodings, like &
, which is just an encoding for &
. Ive not yet implemented all types of HTML character encodings, like for special unicode characters, by &#nnnn;
, where nnnn
is a decimal unicode character code.
Anyways, I really want to make links work next, so that webpages can actually be navigated.
Scrolling was at least easy to add! I just added property, scroll_y
for each webpage, and all draw calls I compare with scroll_y
to determine if they should be shown or hidden. I also offset all draw calls by the scroll amount, and I had to add extra logic to draw calls that draw something multiple lines in height, since they often are on the border of being scrolled to. However, as you can see in the video, scrolling still has its issues, namely flashing. The flashing you see is something ive written about in two previous devlogs i think and is caused by premature flushes to stdout, basically when too much data is sent to the terminal output in a single frame, so rather than draw it all at once, in a single frame, it does it over several frames. It looks really bad. The only way to really get rid of it is to optimize your graphics code. I'm considering storing the terminal state, like all characters printed to the screen and their color, in a matrix and whenever we draw, we compare it to the last frames buffer, and only update the modified regions. Its going to be really tedious to implement though. Maybe I'll look if theres any TUI library that seems really good, but honestly I want to keep this proejct lightweight and homemade lol :-)
I HAVE SPENT COUNTLESS HOURS FIGHTING TO MAKE MY PROGRAMS DRAW FUNCTION O(n). If you dont know, the Big O notation is basically a measure of how efficient an algorithm is, comparing how many inputs it receives, to how many iterations is required to generate a result. Well, yesterday, I noticed my program took several minutes to draw a site from wikipedia, and understood that it probably was due to all the recursion I was doing. Basically, elements can have their width be set to fit-content, which will make their size be just enough to fit all its children. The way I was calculating this was to, whenever I encountered a fit-content, to recursively run a dry draw call on its children, recursively, basically emulating a real draw call, but only to measure how much space is needed. This however quickly snowballed in to millions of layers of recursion, as, if a child also had fit-content, they would have to run their own dry draw call. Point is, it was a mess. Problem though, its a really hard problem to solve, because children of an element can have their size be relative to their parent's. This creates this weird situtation where parents have their size relative to their children, and some of their children have their size relative to the parent. No matter which direction you iterate from, theres always gonna be unknown sizes until the draw call is entirely complete. The way I solved this, to have it all run in a single draw call, was to implement a global table of unknown sized elements, basically, whenever we dont know the size of an element, an entry gets added to the table saying that this value isnt yet known, but will become known later. If they have any children that reference their size, i store that as a reference to an entry in the unknown sized elements table. At the end of the draw call, we can replace all unknown references with proper values. This took me soooooo long to get working, and while SoM claims this devlog has 6 hours of unlogged time, Ive spent SO MUCH time just visualizing this system on a whiteboard, and staring blankly at my screen trying to solve some problem. But, it was worth it in my opinion, now, we can reduce the number of iterations required to draw that specific wikipedia page from over SIX MILLION, to roughly sixteen thousand. Each element is only iterated on a single time, finally. Also, it is by NO means done, optimized or working in any way, but rather now I have the infrastructure to actually make something that wont be held back by having to do tens of thousands of times more work than it needs to. PS: I still havent implemented scrolling or interactive links.
One problem the previous versions of Toad had was that there wasnt really a way to draw text without a set background-color. In a terminal, when you print something, that is going to have a background color and a foreground color. There is no way to tell it to use a transparent background color and use whatever is underneath it. So if you have some text, that doesnt have a background color, that is covering say a colored div, the text would have to overwrite the color where it was overlayed. Now I've restructured the drawing, such that all draw instructions rather send DrawCalls, that I can later handle, with the context of all the rest of the drawcallls, which lets me do what it is you see in the screenshot. The first text is partially covering the div that contains it. In previous versions, the text would either have overwritten the green of the div, making it white, or itself been green entirely, sticking out weirdly. This new version however, is the intended result.
I want to add padding and positioning next. Oh right and also scrolling, that should be easy now that I have a draw queue! I can just filter out everything above and belove a certain Y threshold!!!
Devlog 6! So apparently CSS properties have a secret setting, where they either inately inherit to their children, or not. An example is the color
, property, which children inherit, but the background-color
property doesnt. This both means that for every property I add, I need to also figure out whether it inherits or not, but also that I had to restructure a lot of code. See, before I was just using an Option value for each property, which is an Enum, that either has a value, or doesnt. I used this to mean that the property has either been set, or that it is unset, and should inherit from its parent. But now, for these special properties which dont inherit, I instead need to use a new data type I made, which I call a NonInheritedField. This is instead an Enum which either is Unset, Specified, or Inherited. If you're wondering why it has an Inherited variant, its because in CSS you can specifically mark a property as inherit, causing it to behave like an inherited property. This is just so my code knows a difference between this field is empty, and therefore must inherit from its parent, and this field is empty, and will therefore use the type's default value. Anyways, you can now see in the screenshot that its doing a pretty good job replication the test page. The red box size difference is because I havent found a good conversion rate between CSS pixel units, and terminal character units. The test was made specifically for this devlog, as it tests this inheritance, you see, all the texts are children of the green div, yet as you see, they dont have a green background themselves. The blue text's background_color value is Specified, while the others are Unset.
I ran in to some issues while getting proper network requests working, but all in all it went pretty smoothly. I had an issue that took me forever to find the cause of, where the parser would basically crash if the page ended with a newline, which my test HTML files didnt have, so I thought it was something network related. I also added CSS width
and height
properties, which paired with background-color
makes webpages actually have a background now as you can see in the picture! Now, what I really want to add is scrolling, right now it just prints the entire webpage always. If the content is larger than your terminal? It just doesnt show. This also makes it unperformant, as all the data makes the stdout flush prematurely like I talked about in a previous devlog, which makes the webpage flash as it renders (basically if you print too much data, it will force a flush, even if you want to wait with flushing until everything is complete).
Ive added CSS stylesheets! I didnt have to write a lot of new logic, as the parser for inline CSS is still used for stylesheets, just that a wrapper code divides it by selectors. Selector types I have support for are element type, class and ID. I actually havent added support for multiple selectors on the same ruleset yet, like when you write h1, p {}
, which applies to both <h1>s and <p>s. It shouldnt be that hard though, I think ill just be lazy and have it clone an entry in the global style hashmap for every selector, such that it really just evaluates to one stylesheet per selector. On the image you can also see I've added tabs! Though so far the only tabs you can access are the two hardcoded ones I added for testing lol, so after adding multiple selectors I'll probably add an address bar to go to page by url.
Parsing basic CSS is at least a lot easier than parsing basic HTML, lol. I've added inline CSS (like when you do <p style="color: green">), with the only properties being color, background-color, display and text-align. The reason why (basic) CSS is easy to parse is that each rule ends with a semicolon, and each rule is just a key and a value. The harder part is parsing the values, like did you know you can define CSS colors with hex, like #ffffff, with RGB like rgb(255,255,255) and with a name of a color, like white. That means I have to add support for all those formats, but that wasnt all too hard (though I only have the first 16 web colors). I want to add non-inline CSS next, which affects the entire webpage. I think it should be kind of simple though, as I already have a global draw context that I could add a field to for css, probably a hashmap where the key is which element it affects.
The biggest challenge with my webpage renderer right now was keeping track of the draw state. Basically, a header in my program will make its text bold and red. This is done by modifying the draw state that is passed to its children, then I apply that draw state whenever I print something. Problem though is that for performance reasons, I have to actually only apply the changes in the draw state, and this is because when you color and restyle the terminal, you're actually sending invisible magic bytes of data called ANSI escape codes. If you send too much, like if you always apply all properties of the draw state, like setting italics, bold and foreground color, even if it hasnt changed, you'll be sending wayyy too much bytes to the stdout. In most terminals, there is a limit to how much data can be received before a flush is called (a flush is when the terminal actually draws its data to the screen). This will cause premature draws, basically splitting every draw frame in to like several smaller draws, which causes the terminal to flash briefly which can be really distracting. Ive made some TUI (terminal user interface) apps before and have first hand experience with this :(
The draw state also keeps track of whether whitespace should be respected or not. In HTML, if you make a basic <p>tag with some text, it will remove repeated spaces and remove all newlines. Some elements, like the <pre> changes this, which is why theyre used to display things like codeblocks.
Anyways, I think i'll work on input next, like interactable links and buttons perhaps :)
FIRST DEVLOG! Toad is going to be a web browser that runs in your terminal. Making an entire web browser is really hard though, which is why there are only like 3 browser engines used today (chrome-likes, firefox-likes and safari-likes), so don't expect this to be able to view 100% of websites perfectly, it will be more like 0.2% of websites okay. Thats because HTML and CSS are really old and therefore have a lot of nische old remnants and quirks. I also dont think I'll implement Javascript, which will just on its own make a lot of websites not work. BUT, this will at least be a fun learning project and as long as I get Wikipedia or google searches working, I'll be happy. So far, I've created a basic HTML parsing system. Basically, a system that takes HTML website code and deserializes it to a native data structure in my program, that it can easily understand. On the image attached, to the right, is the output after my program reads an HTML file, and then prints out its representation of the data. It still looks like regular HTML because I made it format it like that, but that shows you that it can accurately interpret simple HTML!
Next up I think I'll work on displaying webpages, like drawing to the terminal window. I'll be using crossterm I think, which is a library for terminal interaction for Rust that ive used before, so I think it will go fine!
I had to make a Web build of my game because Windows Defender decided my extremely simple game was actually malware, so I cant ship a windows executable. I assumed it would be easy to build for web, I've done it before with this graphics library (Macroquad), but I ran in to some really strange issues that took me the entire day to figure out. Basically, I render a low-res 192x144 viewport, that I upscale to fullscreen, for the graphics. This is so that the game is always pixel-perfect. In macroquad, this is done with a render target. Problem was, the part of my code that defined this render target always crashed on web builds. Apparently, after getting help from their discord, this is because the javascript frontend library in web builds, that communicates with my Rust game, was outdated. But how did I get an outdated version of the library? Because thats the one they use in the documentation! Apparently the maintainer of the library doesnt update it very often. Anyways, after that I also had to implement savefiles. On native builds, I write to a file on the computer, on web builds, I communicate with a JS library that lets me write to the web browsers local storage. The local storage on browsers is a key/value database for websites. Each website has their own local storage. Though since the local storage is in plaintext, and my savedata is in raw binary, I have to encode and decode it to base64. But now thats done, and i set up a GitHub workflow to build this web version, and host it on their servers, so I should be able to ship this now, Yey!
[SEE VIDEO for a little gameplay montage]
Coming up with new content for the game is becoming really difficult, which indicates to me that it is probably about finished. I've been doing a lot of bug patches, and once again restructuring data to prevent stack overflows LOL. The last time this happened it was because i was storing map data on the stack, which was like 13 kb, and this time the inventory was the problem. Thankfully the fix both times was quite easy, replace my usage of arrays (go on stack), with vectors (go on heap). If you dont know, the stack and heap are two different memory regions where data can be stored. The heap is dynamically allocated, and big data, and data that can grow should be on the heap. The stack is more for fast smaller reads, and if you put too much data there, or infinitely recursively call a function, you get a stack overflow and your program crashes. Ive added a lot of new enemies and cards, and my last stroke of genius was adding suprise mushroom rounds, every now and then theres a round thats all mushrooms. It sounds stupid but i really like it for some reason. I also had to fix some issues regarding sprite centering, basically enemies position vector points to the top left of a 8x8 sprite, but for checking projectile collisions i need to find the enemies center. This becomes a huge problem for enemies that are multiple tiles in size, as their center would be far away from their coordinate and screw up collision detection. I also had to implement stun immunity frames, because I realized during a playtest that if you could make a tower rapid-fire any projectile with the stun modifier card added, you literally stop any enemy from passing through, as theyd be stunned and not be able to move. I also had to reimplement a system i had for enemy spawning, basically some enemies spawn other enemies when they die, like the big spider that spawns children, and i used to have a spawn queue, that I'd add to when this happened, and every frame I would pop one item from the spawn queue and instantiate it. Problem though, in later rounds, theres like houndreds of entries in the spawn queue, so when an enemy with a payload dies, it takes several seconds for their payload to spawn. I first fixed this by making it parallel, but I realized it would be better to just drop the system all together and I made it work by spawning it all directly. I've also done a million more things (theres been like 50 commits since last devlog), but i cant mention everything. I plan to ship after this devlog, wish me luck !
Game design is so much harder than programming lol. This devlog ive been focusing on game design, and the problem solving you need is so much harder. One problem i had was how, if the player knows what they are doing, and get good shop items, they could quite early on in the game create INSANELY powerful builds, that basically last them til near the end of the game, which is really boring when you dont have to do anything for like 20 minutes (see video, its really boring when you dont even see the enemies spawn in before they die, and it just lasts like that for minutes). This however is kind of intended, creating OP builds is part of the game, i just didnt want a one fits all solution, i wanted more diversity required. I realized that a really OP build generally requires like 4 parts, a base projectile, something to speed up recharge time, something to tie it all together (like a multidraw or trigger), and optionally a payload, so I made sure to tinker with the shop's RNG to make the early game almost always miss one of those elements. Then i slowly over the course of the game reduce the tampering. I also introduced more enemies with resistances. My game has different damage types, and enemies can be (fully or partially) resistant to those. I realized that having enemies with multiple phases was a good idea, like a knight that has a shield that protects from explosion/burn, and then when that breaks, he just has his armor which protects from piercing. This makes you need at least 2 types of damage, again punishing those one fits all solutions. I also had to figure out how the player receives new towers, mind you, until this point, every game you just had the two starting towers. While that still worked fine because of the variety of cards, it gets limiting in the higher rounds. I considered having them available in the shop for a high price, but decided to automatically reward the player with one tower at round 17, and one at round 34. Forcing them unto the player I hope will nurture experimentation, as their more card slots open up new possibilities (and gettign new towers also shakes things up a ton, and the player will hopefully reallocate their cards and create new builds so they dont get stuck with those foul one fits all towers).
I can pretty safely say that all the game's UI is complete at this point! I've added a map selection to the main menu, as well as... multiple maps to play. The hardest part about making a map is just coming up with an idea/theme, the rest is really easy. I use a software called tiled to draw the maps, it exports to xml that my game parses, and it allows multiple layers, so different behaviour is tied to different layers, theres a Background layer, where you're allowed to place towers, an Out of Bounds layer, where you're not allowed to, an Obstructions layer, that also collides with projectiles, and a Path layer, where the actual path that the enemies travel on is stored. The way the path works is that a special tile indicates the start, and another special tile indicates continued path. Then I just have to find the start tile, and recursively iterate through all its neighbouring continue tiles until we reach the end. All I really want to add to this project now is more enemies and rounds.
All core game concepts are pretty much in place, so I'm now in a part of the development where my creativity is the only limit, which is great! I can have an idea for a card, and spend 5-15 minutes adding it (most time is probably drawing sprites and writing particle effects). Often times I need to add new functionality to the core projectile class to implement a new card, which might seem like a lot of work to just add a new card, but its really not. The projectile class in my game now has like 30 fields, some of which only used by a single card, but every time i add new functionality, that means that future cards will be even faster to implement, and i can combine different aspects of previous cards, and its really fun! Enemies are code-wise really basic, no enemies so far have any 'special' behaviour, they all just have different stats and optional payloads, but I still spend A LOT of time on them just drawing sprites, which always takes me ages. My least favourite part of development rn is adding rounds. Its REALLY hard making rounds both fun AND balanced. But I've realized that having lots of weaker enemies is basically a cheat code to stimulation, just seeing your towers SHRED through layers upon layers of foes is really satisfying, and then I just use the stronger, more visually interesting enemies as garnish, that I sprinkle on. Sometimes to keep it interesting I do a whole round with just the big strong guys, or the opposite. Anyways, game is nearing completion (fr), I can only think of a couple fixes and features I want to add, and while I could basically perpetually add new cards and enemies, I'll try to remember that this just has to be an MVP, and its fine if game only has a couple hours of content.
Implementing Saving was both harder and easier than I thought it would be. On one hand, since this is rust, I can just pack all save data in to a struct and transmute that to bytes that can be loaded, on the other hand, that is incredibly unsafe and could theoretically destroy the system. You have to ensure that the struct CAN be safely loaded, with for instance bytemuck (a library). I did end up after a while just going with bincode (another library) instead, which abstracts everything unsafe away.
Ive also added the ability to rotate towers freely, which I think is good for game design because it fits my idea of constantly moving towers around and restructuring and not being held back. Sound effects was kind of a pain to get working, I ended up having to reorganize a lot of data, because I was getting stack overflows because my game state had gotten to slightly over 20 000 bytes (on the stack) (oops).
This is the longest ive gone without writing a devlog, yet also the most boring devlog. I've mostly been working balancing, making spells and enemies just right. I've also added PROGRESSION, finally. In between certain rounds, a little shop appears, where you can buy different random spells. Its hard balancing the game because of this, because I cant know before hand how good cards/builds the player will have at any given point, because the shop is random. My solution was to always assume the best of the player, which does make the game harder, but which also rewards planning ahead and experimenting with builds. This devlog isnt very technical, because its really just about game design (which i unfortunately SUCK at), but thats just as important when making a game. The game is coming close to being finished, now I just need to add saving, and a bit more content and polish (like sound).
Reworking my entire UI system was as mind numbing as i though it would be. Since i chose not to make an entire UI engine, it instead meant dealing with lots of random coordinates and comparing mouse coordinates to arbitrary bounds. I also made AN ENTIRE FONT (actually just a-z, 0-9 and : and .). The challenge with making fonts for such low-res games is that each character has to be really tiny to fit much text on screen, in my case i had each character be 4x3 pixels. This makes some characters quite unintelligible, but mostly it looks ... fine. I also added a win and lose screen, and you can TECHNICALLY win the game. Speaking of which, I'll be working on adding progression now. I'll have to plan out how and when you receive new cards, towers and abilities, but i have some ideas.
Today ive added lots of CARDS and ENEMIES!!!
My biggest obstacle this devlog was again, figuring out how to store data, in this case, round/wave data. Enemies arrive in waves and rounds, and figuring out a format for encoding that data took some time. With a statically typed (and borrow checked) language like Rust, planning out the structure of data before-hand is basically a necessity. You need to know what parts of your systems need to interact, and when and how to share memory, which is why you can see i spend a lot of time doing that.
Other than that, adding a bunch of cards and enemies was fun, since the core systems are in place its really easy to add new stuff.
NOW ig ill focus more on setting up win/lose states, and UI. Ive been avoiding UI since its often really time consuming, but my plan now is to just try to get through it and never think about it again.
ive spent 7 hours now implementing DECK BUILDING! ive coded a couple of cards that you currently are given all of when you start the game. i plan on making them be purchaseable in shops in-between rounds and rewarded randomly.
the deck building is really quite complex, since theres a lot of different states that need to be tracked, and many nische mechanics like wrapping and what not, but ive made a robust system that does this, that keeps track of all contexts and buffers and whatnots.
the most difficult thing to do was to figure out HOW cards are stored, like, what are they? i wrote down a list of cards i wanted to create, and from there figured out which features they would need. ive now landed on a quite nice design where most of the card specific data is stored IN the type enum (in rust, enum variants can hold unique values).
next ill focus on getting a game loop, adding more cards, adding some UI (descriptions and names of cards)
ive set up the system for towers and UI now!!!!
an idea i had is actually that you only ever have 4 or so towers. you start with 2, and receive new ones every couple rounds. this would put the focus on building cool and smart decks, and maybe changing up the build on the fly (which is also why i want you able to move them freely).
NOW, i can finally focus on making the deck building system and getting a full game loop up and running. wish me luck!
FIRST DEVLOG!!!
i want to create a tower defense game. i really like btd5, so something thematically similar to that is my goal. but to make it interesting i want to combine it with Noita's wand-building (Noita is an awesome finnish roguelike).
so far i've really just set up the infrastructure that the game will run on, that is, a rust project built on macroquad (a dope graphics lib), and come up with the ideas and goals of the project.
i want the game to feel semi-retro, like pico-8 games, so i've decided to target a fixed 192x144 (because its 4:3 aspect ratio, and divisible by 8) resolution, with 8x8 sprites. i've also found a nice 16bit palette that i will be using for art.
This was widely regarded as a great move by everyone.