onlytomorrow void

"Idlenet log #2"

In other news, Idlenet is licensed under the MPL-2.0.

posted on Wed Apr 23 2025 by kirby | 11 min read

Contents


Resolution

Three years ago, when I started working on Idlenet, I made the mistake of using a small base resolution. Thankfully, my GameMaker days were behind me and I wasn’t using room sizes of 640x360px. 720p was the chosen base target, and since I used a pixel-perfect bitmap styled font, the game needs to use only integer scaling at larger/smaller resolutions. Nowadays, modern games have fancy DLSS and FSR and AI turbo resolution scaling techniques, or what have you, but having the game appear blurry at all, especially when set to resolutions besides 720p wasn’t going to cut it, since a majority of Idlenet’s text is in a font that’s one pixel wide, so it was going to look like shit no matter what filter that isn’t integer is used, so love.graphics.scale was out of the picture, since I eventually wanted granularity in letting the player change the game’s resolution (I would know, since I would frequently run Idling to Rule the Gods at its lowest resolution to reduce overhead while in AFK mode) while having the game look presentable, why many incremental games don’t do something similar (especially Unity-based games) really grinds my gears.
  That means, the game should support and account for LÖVE’s graphics filters. By default, Idlenet will run with integer scaling with no filters applied at its base resolution, however, the player is free to resize the window however they want, but the catch is that the game will not stretch, instead all the elements remaining fixed in 16:9, with the rest of the space being filled, not by pitch black, but by an appropriate background color that can be applied by theming, it’s for the sake of the font. Enter Resolution Solution, originally by GVovkiv on the LÖVE2D forums, but they have since deleted their GitHub account, but I’m still using it because it uses the Unlicense, and the developer uploaded the ‘final’(?) version of the code on the forum thread in January of this year. It has the best documentation and ease of use out of all the libraries I’ve tried. That includes push and maid64. Though SYSL-Pixel is a solid contender, it hasn’t been updated in five years (unlike SYSL-Text, which still gets fixes), and the window resizing is less than ideal on Linux. Anyways, plugging in GVovkiv’s library was mostly painless.

-- love.load()
game.rs = require("lib.resolution_solution")
game.rs.conf({
    game_width = 1280,
    game_height = 720,
    scale_mode = 1 -- integer scaling
})

game.fullscreen = false
game.window_flags = {
    resizable = true,
    vsync = -1,
    highdpi = true,
    usedpiscale = false, -- this gets toggled later if getDPIScale() == 2
}

game.rs.setMode(1280, 720, game.window_flags)

So the game’s default window flags get sent over to the library and preps for scaling. Not included above is the usage of love.window.getDPIScale() which returns 2.0 if a Retina or similar screen is detected, so the game window instead gets launched in 1440p and we can use usedpiscale = true. The examples are somewhat simplified from their actual implementation. Idlenet’s UI library SUIT needed some changes to account for the library scaling the game window when calculating the mouse position (for UI element hit detection or whatever). Without it, the player would have to do the thing that happens in some games when they bug out and think your cursor is in a position that it was in when it was at a different resolution, until you refresh the window in a safer way. I went and changed that too:

-- lib/suit/core.lua
-- state update
function suit:enterFrame()
    if not self.mouse_button_down then
        self.active = nil
    elseif self.active == nil then
        self.active = NONE
    end

    self.hovered_last, self.hovered = self.hovered, nil
    -- idlenet: scaling support with resolution_solution (rs)
    self:updateMouse(
        Idlenet.rs.to_game(love.mouse.getX()),
        Idlenet.rs.to_game(nil, love.mouse.getY()), love.mouse.isDown(1))
    -- end
    self.key_down, self.textchar = nil, ""
    self:grabKeyboardFocus(NONE)
    self.hit = nil
end

In the game’s option menu, the window resolution can be changed in a single line of code, game.rs.setMode(2560, 1440, game.window_flags). Using love.resize on its own would not adapt the UI libraries or positioning code correctly, but rs resolves it by adding game.rs.resize(w - (w % 2), h - (h % 2)) to love.resize. Toggling between scaling filters on the fly was the next challenge. Until implementing these changes, Idlenet didn’t use LÖVE’s Canvas, instead the game was a cobbled together, nested layout, declarative maze of UI calls, and tables of coordinates passed to one another, drawn every frame as they go, now, since I was trying to apply what was effectively a new shader in real-time w/r/t resolution solution, it was the perfect time to use it.
  The first attempt without the canvas was adding a toggle key to switch between the two filters, linear and nearest, but when the time came to test the initial implementation, it didn’t change, despite the game responding to the keypress, and upon further investigation, it turned out the window needed to refresh entirely before a new filter can be applied, and I needed it to refresh so that the filter can change, from nearest to linear, and once that was finished I could start on testing out the aspect ratio changes by freely resizing the window, but the resolution solution, the library, made everything clear, and so for now, changing the filter requires only the single press of a key, and Idlenet will turn beautifully blurry, an estimation of jagged pixels at variable resolutions, smearing the Tamzen bitmap style font, sacrificing readability for the convenience of conforming the game to any archaic window layout that the player desires, and this is only if the player decides not to conform to the integer scaling required for the Tamzen font to remain readable, it would be a bold two pixels wide at resolutions above 1440p, a natural upscaling from the base resolution of 720p, and the effect of toggling the filter much like taking off one’s glasses, or something to that effect. I’ll smooth it out once I finish the game’s options menu.

function love.draw()
    game.ui_theme.canvas = love.graphics.newCanvas(game.rs.get_game_size())
    game.ui_theme.filter = game.ui_theme.aliasing and "linear" or "nearest"
    game.ui_theme.canvas:setFilter(game.ui_theme.filter, game.ui_theme.filter)
    love.graphics.setCanvas(game.ui_theme.canvas) -- start drawing to canvas
        -- UI draw calls ...
        IdlenetSuit:draw()
    love.graphics.setCanvas() -- done drawing to canvas
    game.rs.push()
        love.graphics.draw(game.ui_theme.canvas) -- scale the canvas
    game.rs.pop()
end

resolution

Time Limit

Limited time events are hell. For further reading, refer to my post about treatmills and idle games. I simply could not help myself from adding what I believe to be an “inconsequential” mechanic to a core resource in Idlenet: time limiting access to a subset of loot types in the battle system. When the player character auto-battles enemies in Idlenet’s “dungeons”, there is a chance of said time-limited item to drop for the player. If it does, it’s in the “pending” part of the player’s inventory, and it gets wiped out after a time limit.
  There’s no beating around the bush: this is a shameless attempt at rewarding players for checking the game every ten minutes, it’s ten real minutes, not ten minutes in the self-contained universe or reality of the game, and it might not be ten minutes once feedback starts to be gathered, but my intentions behind it were simply creating scarcity in a crucial resource that would ultimately speed up the early game, similar to Kittens Game, then the player researches the calendar, the threat of the winter season becomes clear, and catnip levels must be kept at a surplus to meet the demands of the hungry kittens in the winter seasons, and the player needs to constantly check back if it’s winter yet, if we still have enough catnip, or else the kittens die, but in Idlenet, the operating system is so shitty it can only store ‘memory’ for ten real minutes, the game explains to the player in a large tooltip with red warning text, and when the timer expires, the timer’s progress bar goes red and the number goes down to zero. As with all things, in these incremental games, it’s only bad for the early game, and it’s planned to make it all automated with ‘extensions’, the upgrades introduced to the player once they have settled in with the core loop of the game, when the player’s inventory is of note, though it cannot be ignored that it is not passive or idle at this stage, I once complained about that when playing a game called Idle Pins, the automation is so unbearably slow, that you’re not really idling at all, and so I believe the line between engagement and simply manipulating the player to be blurred in this genre,1 it’s hard to be avoided without having to meticulously test psychological responses to a video game, that’s simply outside of my scope, though to me, the core of the genre is the art, a delicate art, of getting players to only check on a game, for fifteen minutes at a time, coming back whenever they feel like it, not games that make you constantly babysit tasks that prove ultimately meaningless, for once the player is hundreds of hours into a save, an entirely different mechanic may have rendered such struggles obscelete, and the issue of me implementing such nagging mechanics, it will be minimized as much as I can to make it permissible as a system to encourage, most likely by increasing the amount of time during this pending period, so that the progress bar increases at such a small rate, that the player will naturally pay attention to it, without any extra fluff or popups or notifications. Such is the fine line between gauging player retention, that is, keeping players coming back, and manipulating them into doing so, such as when a player is told that not claiming a resource at a reasonable frequency means that they will be 0.05 per cent slower and will not maintain pace with the online community-written wiki guide, and so I am ensuring that Idlenet does not fall in the same hole, even though it’s not a parody game like my previous work Crackhead Jack was, I simply cannot implement shitty mechanics and keep it in for the sake of its parody, so in the end, the only downside to forgetting about this time limit, is that the player is set back by another few real minutes until a new enemy is defeated and drops another handful of items, and at this stage, if the player were to get 50 units of such a scarce item in ten minute intervals, the player likely already has the facilities required to automatically collect the resources, and so its overall effect is subtle. Anyways,2 I’ve implemented a slowly decreasing progress bar to indicate the time you have remaining, with only an estimate given to the player.

warning


Footnotes

  1. Anecdotal evidence.

  2. I have yet to come up with a better line to end this post with in this style. Such vignettes in Speedboat often feel like its subtext jumps at you in its final statements, only for it to be discarded to move on to the next observation. “What is the point,” says Jen in Speedboat, not asked as a question but rather a simple sentence, offering a different view of what is the point, the point being what, the point is what is right, or it is what happens before us, as Jen says in ‘Brownstone’, “You cannot be forever watching for the point, or you lose the simplest thing,” and I believe the timelessness of the novel illustrates a subconscious acknowledgement of the looming, growing skepticism of everything up and including to, what we consider the ‘point’, the point of the story, the point of the novel, the point of the vignette, what is the point of Jen observing and expressing such moments, what is the moment, what is the point for it to be organized under ‘Brownstone’, a collection of stories, observations, writings, collected thoughts, to which Adler says, Some of it was real, what, then, is the point, surrounding the building in which Jen lives in New York City, the bustling city, with so much to see, that the point is left up in the air, up to the writer, whose doubts and skepticisms cast over their observations and the matter-of-factness of the observations, some may call it reporting, or a scoop, to where they call themselves a journalist, God forbid a writer, for Jen says in the opening story ‘Castling’, Writers drink, Writers rant, Writers phone, Writers sleep, I have met very few writers who write at all.

tyju goth.zip stay down nvim chrome sux sears astro steam flashpoint fedora kde use firefox

onlytomorrow last.fm bob redbull scrob chj

comments? kirby(a)onlytomorrow.net