<Index> - >DevDiary< - <Characters> - <Lore>
This week, I cleaned up the SDR code base and extended the pause menu.
Sunrise Doll R is my first GDScript project that has grown to such a large scale. After finishing Ilriss, I allowed myself to rest and play around with the language, and I have realized that the source code of SDR is a mess.
The most glaring problem that has impeded my programming comfort is the lack of static typing: almost all variables in the game are declared with no explicit type, which means that the autocomplete function for class variables and function arguments does not know what to do. So I went through the entire code base of the game and added types wherever possible. The Godot programming environment has a built-in reward for doing so, as it marks every line that lacks type ambiguity with a green number instead of a gray one, so keeping track of type safety has been simple. Unfortunately, I could not make the entire code fully safe.
The Dictionary type, which I am using for various things, such as the database of Pawn's states, lacks typing. I can assign the Dictionary type to the dictionary, but the environment does not have a way to assign types to Dictionary values. Every line of code that refers to a dictionary value via a key is deemed unsafe, and I cannot fix this without restructuring the parts of the game that utilize Dictionaries to no longer utilize them. This is a step I am not willing to take, and the game works without it anyway, I can let it slide.
One particular oddity I have noticed is that arrays support type assignment to its elements (for example, Array[int]), but do not support nested typing (for example, Array[Array[int]]). This leads to a hilarious pitfall of trying to make nested arrays safe by using Array[Array], with the implication that it is supposed to be Array[Array[Vector2]], and then causing a game crash by explicitly assigning Array[Vector2] to a variable that is taken from the Array[Array]. The environment thinks that Array and Array[Vector2] are different types, and causes a runtime error. The intended workaround is to use Array[PackedVector2Array], but this only works with a handful of types, and I cannot use, for example, Array[PackedEnemyArray] because there is no such thing as PackedEnemyArray.
Any way, going through the entire code of SDR has been refreshing, and I have found a handful of mistakes that seem to compromise the entire game, yet somehow have never occurred in the playtesting. I expected to feel completely alienated by the code, as most parts of it are over a year old, but almost every part of it was surprisingly readable and sensible. I guess this means my mind has not changed since last year that much.
The pause menu that was developed a long time ago has always felt like a placeholder. There is no complexity put into the menu, with the only options being unpause and quit. The black background behind letters is not even a separate graphic - it is a part of the letter sprites themselves.
Old pause menu.
When developing Ilriss, the sound that her crosses make when they move has annoyed me with its intensity despite its low volume, and I figured that it would be a good idea to have the ability to change the game's volume without resetting the entire progress of the game. This was the push that made me develop the pause menu into something more presentable.
New pause menu.
One of the benefits of the way I have designed the menu systems has finally played its role here, as I did not have to fully remake the settings menu from scratch. Each menu screen in the game loads UI elements from a large database of UI elements, and so while I did have to create a separate "settings_pause" menu, I could tell it to reuse the same buttons and sliders that the regular "settings" menu had already been using. This way I did not have to rewrite the logic of the menu elements, and only had to make sure they were aligned on the screen.
Changing the settings while paused.
Changing the controls while paused.
The "Quit" button still does not have a confirmation screen, and selecting it boots the player to the main menu immediately. This is something I plan to add later.
Speaking of UI enhancements. Even though there is no way to make custom stages with bosses without editing the binary files of the stages manually, I have added boss previews for stages that the game recognizes as boss stages.
Large ominous previews.
And speaking of editing the stage files, one critical moment that I missed is the cap of the time limit. The UI implies that the maximum time a stage can have is 999, but to accommodate that, the binary file stores the time limit in two bytes of data. This means that one can easily set the game time to an even larger number, up to and including 65535. The game handles such high values, but does not display them properly, so I decided to kill the fun and force the game to trim the time down to 999 if it loads a level with a higher value.
Likewise, the volume and the resolution values that are stored in the configuration file did not have a check for unexpected values either. I have added appropriate boundaries. This is not going to affect an average player's experience, but will help the game's stability, and possibly infuriate some amateur file editor when the game denies their attempts to break it through the weak point of external file import.
With the code base updated, I'm sure there are now plenty of new bugs that I have yet to catch. While writing this entry, I have found a bug that prevents Pawn from collecting souls when she is under the effect of a post-hit invincibility, and I am sure I will find more and more. I plan to playtest the game and iron the bugs out, and then move on to developing the third boss.