{"id":1244,"date":"2026-09-10T05:11:08","date_gmt":"2026-09-10T05:11:08","guid":{"rendered":"https:\/\/x.sheep-mine.ts.net\/index.php\/full-ui-upscaling-part-3-dynamic-terminal-swapping\/"},"modified":"2026-09-10T05:11:08","modified_gmt":"2026-09-10T05:11:08","slug":"full-ui-upscaling-part-3-dynamic-terminal-swapping","status":"publish","type":"post","link":"https:\/\/x.sheep-mine.ts.net\/index.php\/full-ui-upscaling-part-3-dynamic-terminal-swapping\/","title":{"rendered":"Full UI Upscaling, Part 3: Dynamic Terminal Swapping &#8211; Cogmind \/ Grid Sage Games"},"content":{"rendered":"<p><br \/>\n<\/p>\n<div>\n<p>I was really happy to have come up with workable concept for a modal inventory, as well as producing a <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/www.gridsagegames.com\/blog\/2024\/01\/full-ui-upscaling-part-2-holy-mockups\/\">mass of mockups<\/a> with proven solutions for all the potential hurdles on the road to dropping 15 rows from Cogmind\u2019s interface. Or so I thought.<\/p>\n<p>Suddenly at the end of that process, a review of remaining UI systems revealed an unexpected show-stopper: ending animations.<\/p>\n<p>Cogmind has a lot of different endings (currently nine, with more to come), and all of them feature full-screen ASCII animations. While some of these are flexible enough to fit dynamically within any terminal dimensions, a good many were designed to assume a height of 60 rows. The only way to fit those into 45 rows would be a complete redesign\u2026<\/p>\n<p>I put a lot of time into the existing animations, and they make good use of their space, so one would hope there must be another way\u2026<\/p>\n<h2>Terminal Swapping<\/h2>\n<p>What if\u2026 the terminal was still 60 rows during ending animations?<\/p>\n<p>After all, there\u2019s no strong need for the endings to be limited to only 45 rows&#8211;you\u2019re essentially watching these animations for the overall visual effect rather than needing to interpret every little detail for gameplay purposes. As described at the <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/www.gridsagegames.com\/blog\/2024\/01\/full-ui-upscaling-part-1-history-and-theory\/\">beginning of this series<\/a>, the reason for shrinking the base terminal dimensions is to enable larger fonts across the board, but the intent there is to facilitate reading and interpreting details, which we don\u2019t need in this case.<\/p>\n<p>So the theory is we use a 45-row terminal for normal play, but once we need to do an ending animation simply switch over to a 60-row terminal to display that, then switch back when done.<\/p>\n<p>This reminds me of games that switch to a different display mode specifically for cut scenes, especially common (and noticeable) in early video games, and then later it became a big deal when those sorts of things could be done \u201cin engine,\u201d which is the norm nowadays. Cogmind has always been consistent about its display, but in this case I could ideally continue to make use of all the work that\u2019s already gone into the animations.<\/p>\n<p>Now come the important questions: Is the engine architecture capable of such a feature, and what other roadblocks might be in the way?<\/p>\n<p>Technically the idea immediately showed some promise based on the fact that Cogmind already uses what I call \u201cterminal swapping,\u201d but only between frames and behind the scenes, specifically for special screenshotting purposes.<\/p>\n<p>The main usage is for map output, or creating a composite PNG containing the entire current known map. This is useful for sharing interesting layouts with other players, or asking for advice about where to explore.<\/p>\n<div id=\"attachment_5224\" style=\"width: 3610px\" class=\"wp-caption aligncenter\"><img fetchpriority=\"high\" decoding=\"async\" aria-describedby=\"caption-attachment-5224\" class=\"wp-image-5224 size-full\" src=\"https:\/\/www.gridsagegames.com\/blog\/gsg-content\/uploads\/2024\/01\/cogmind_map_output_sample_terminus_research.png\" alt=\"cogmind_map_output_sample_terminus_research\" width=\"3600\" height=\"3600\" \/><\/p>\n<p id=\"caption-attachment-5224\" class=\"wp-caption-text\">Map output shared by Terminus, showing a circuitous route through Research while seeking a particular exit (which ended up being near the entrance :P). (open for full size)<\/p>\n<\/div>\n<p>I also used it to produce an image marking the launch of Cogmind\u2019s achievements, for which I wanted the background to be a matrix of many achievement icons.<\/p>\n<div id=\"attachment_3309\" style=\"width: 1150px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-3309\" class=\"wp-image-3309 size-full\" src=\"https:\/\/www.gridsagegames.com\/blog\/gsg-content\/uploads\/2018\/12\/cogmind_achievement_icon_matrix_256.png\" alt=\"Cogmind 256 Achievement Icon Matrix\" width=\"1140\" height=\"720\" \/><\/p>\n<p id=\"caption-attachment-3309\" class=\"wp-caption-text\">An image celebrating Cogmind\u2019s first batch of achievements, added in 2018. One day that number is going to get so much bigger.<\/p>\n<\/div>\n<p>Both of these use cases required generating an image larger than the screen, so a normal screenshot wouldn\u2019t do.<\/p>\n<p>Already armed with a built-in way to take a \u201cscreenshot\u201d of the terminal contents (bypassing the screen itself entirely), if we build a larger terminal than the screen we can just as easily run the \u201cscreenshot\u201d process on that to produce an even larger image.<\/p>\n<p>With just a small bit of isolated code it\u2019s pretty easy to temporarily replace the engine\u2019s terminal with one of a different desired size, write to that, generate the image, then restore the original terminal as if nothing happened. We\u2019re not actually rendering to the screen itself so resolution doesn\u2019t matter, we don\u2019t need to change the video mode, none of this is ever displayed, nor does any outside interaction occur.<\/p>\n<p>This was a promising starting point, at least providing a theoretical approach for our ending animation management.<\/p>\n<h2>REX, Again<\/h2>\n<p>The above initial examples of basic terminal swapping are pretty much entirely Cogmind-side. As it\u2019s happening between frames for the sole purpose of creating an image using the normal system, the engine doesn\u2019t need to know or care about what\u2019s going on. All that was needed was a simple function allowing the root terminal to be swapped out for another one.<\/p>\n<p>Taking the next step and swapping the terminal with a new one that would exist for a longer duration, and even involve some level of player interaction, would be a much more complicated process, meaning it\u2019s once again time to revisit the engine to expand its core capabilities, like I did <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/www.gridsagegames.com\/blog\/2023\/12\/adventures-in-map-zooming-part-2-engine-level-architecture\/\">not too long ago<\/a> with the quads and octs powering the map zooming system.<\/p>\n<p>Also once again, as an engine feature it makes more sense to head back to the simpler engine testing environment to build and debug it, rather than using Cogmind itself.<\/p>\n<p>I was pleased, and surprised, to find that terminal swapping of an extended nature really wasn\u2019t an incredibly complex operation with many repercussions. As far as the engine was concerned, it only required changing a handful of core variables, although beyond that I had to resolve some cursor-related issues, like crashiness related to its screen position and hover data, and the software cursor dirty rect status.<\/p>\n<div id=\"attachment_5226\" style=\"width: 540px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5226\" class=\"wp-image-5226 size-full\" src=\"https:\/\/www.gridsagegames.com\/blog\/gsg-content\/uploads\/2024\/01\/cogmind_source_REX_swap_root_simplified.png\" alt=\"cogmind_source_REX_swap_root_simplified\" width=\"530\" height=\"443\" \/><\/p>\n<p id=\"caption-attachment-5226\" class=\"wp-caption-text\">A simplified view of the source code for REX\u2019s terminal swapping process. Swapping back is essentially just reversing this procedure (normally handled by this same method, but I cut all that out in the interest of readability).<\/p>\n<\/div>\n<p>It took about a day to implement terminal swapping and work out all the kinks.<\/p>\n<div id=\"attachment_5227\" style=\"width: 498px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5227\" class=\"wp-image-5227 size-full\" src=\"https:\/\/www.gridsagegames.com\/blog\/gsg-content\/uploads\/2024\/01\/rex_root_swapping_first_success.gif\" alt=\"rex_root_swapping_first_success\" width=\"488\" height=\"755\" \/><\/p>\n<p id=\"caption-attachment-5227\" class=\"wp-caption-text\">The first successful root swap in REX, temporarily replacing the standard 80\u00d760 demo terminal with a 120\u00d790 terminal, while switching the font size from 6\u00d712 to 4\u00d78, so the window size remains consistent. This behavior simulates what would be required to run Cogmind\u2019s animations using a smaller font while maintaining the same resolution.<\/p>\n<\/div>\n<p>The next stage in building this feature would be to import it into Cogmind while changing as few variables as possible. It doesn\u2019t have to be specifically for endings, and we don\u2019t even need to start by <em>actually<\/em> changing the terminal dimensions&#8211;simply swapping from the default 60-row terminal to another 60-row terminal and back would be sufficient to weed out any issues with regard input or other basic functionality. One step closer to a real use case scenario. That went fine as well!<\/p>\n<h2>When and Where<\/h2>\n<p>Having passed a simpler test, it was time for the real thing, but exactly where is the best opportunity for a swap to take place? Swapping is a pretty significant cutoff, after all, forming a clear barrier between what is before and after, and there shouldn\u2019t really be much talking between the two sides, at least not on an interface level.<\/p>\n<p>As stated at the beginning, the goal here was to allow animated endings to use 60 rows instead of 45. Given the simplicity of terminal swapping at the engine level, it seems easy enough\u2026<\/p>\n<p>As so many things are, up close it no longer looked so easy.<\/p>\n<p>I originally imagined just having the animation segment of the ending in a different terminal, and tried that for a bit, but the endings (there are so many xD) are a relatively complex collection of classes and processes since they mix and match different components, and it was really hard to untangle what needed to be untangled. Even before considering swapping needs, it turns out that in many cases the process involves multiple windows in varying states of visibility and overlap. While swapping right before an animation would be possible, it would likely be pretty tough to both implement and debug.<\/p>\n<p>Then a new idea popped up: How about instead of focusing so tightly on the animations we move one level higher and handle the <em>entire game over process<\/em> in its own separate terminal interface? This would include the standard game over screen (losses as well), stats, and restart menu etc. This is a much cleaner break, far easier to pull off without worrying about any serious complications.<\/p>\n<div id=\"attachment_5228\" style=\"width: 1282px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5228\" class=\"wp-image-5228 size-full\" src=\"https:\/\/www.gridsagegames.com\/blog\/gsg-content\/uploads\/2024\/01\/cogmind_root_swapping_first_success.png\" alt=\"cogmind_root_swapping_first_success\" width=\"1272\" height=\"720\" \/><\/p>\n<p id=\"caption-attachment-5228\" class=\"wp-caption-text\">The aftermath of the first [mostly] successful root swap in Cogmind, having gone through an ending and starting a new run. Some of you will recognize what\u2019s going on there\u2026 Clearly some bugginess to be resolved, but it didn\u2019t crash and we\u2019re back in action for a new run \ud83d\ude00<\/p>\n<\/div>\n<p>The only drawback is that said stats screen would then be in the 60-row terminal interface using the original font size. In other words, back to the smaller font. Still, this might be fine since it\u2019s 1) just text, 2) not a lot of text, 3) only in that one spot, although if we wanted to we could perhaps use the new <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/www.gridsagegames.com\/blog\/2023\/12\/adventures-in-map-zooming-part-3-implementation\/\">zoom text<\/a> font size to display it. Doing so would require reducing the number of listed stats in order to fit in the available space, at least if trying to keep the vertical design. They\u2019re only a tiny subset of those found in the <a rel=\"nofollow\" target=\"_blank\" href=\"https:\/\/www.gridsagegames.com\/blog\/2019\/07\/building-ultimate-roguelike-morgue-file-part-1-stats-organization\/\">massive scoresheet data<\/a>, anyway, but I don\u2019t like the idea of further slimming down the already short representative list, so it\u2019s either accept a smaller font size for that particular screen, or eventually go as far as a more significant redesign that makes more use of horizontal space. I tried a few mockups but didn\u2019t like any of them, so nothing will probably change with that at first.<\/p>\n<p>While working on this whole terminal swapping business I also happened to discover that if you manage to close the game window <em>during<\/em> an ending animation (including the loss animation), it would not overlay the separate program close animation that I added some versions back. This is not an uncommon occurrence, inadvertently uncovering obscure bugs in old, or in some cases <em>very<\/em> old, parts of the source that were simply never encountered or noticed before. Always a good opportunity to stay alert and fix things \ud83d\ude42<\/p>\n<div id=\"attachment_5229\" style=\"width: 1276px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-5229\" class=\"wp-image-5229 size-full\" src=\"https:\/\/www.gridsagegames.com\/blog\/gsg-content\/uploads\/2024\/01\/cogmind_exit_program_during_death_animation_fixed.png\" alt=\"cogmind_exit_program_during_death_animation_fixed\" width=\"1266\" height=\"717\" \/><\/p>\n<p id=\"caption-attachment-5229\" class=\"wp-caption-text\">A snapshot of what it looks like if closing Cogmind during the loss animation, including the new zoom font used for the strip added in Beta 13. Technically at this point the terminal is swapped as well.<\/p>\n<\/div>\n<p>One final note: For an article about new tech to support ending animations, there is a curious absence of samples demonstrating the primary use case, but I figured I\u2019d leave those out \ud83d\ude09<\/p>\n<p><em>This is the third in a multi-part series about building Cogmind\u2019s fully upscaled semi-modal interface layout:<\/em><\/p>\n<\/p><\/div>\n<p><br \/>\n<br \/><a href=\"https:\/\/www.gridsagegames.com\/blog\/2024\/01\/full-ui-upscaling-part-3-dynamic-terminal-swapping\/\">Source link <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was really happy to have come up with workable concept for a modal inventory,&#8230;<\/p>\n","protected":false},"author":1,"featured_media":1245,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[715,1825,1826,1827,1828],"tags":[],"class_list":["post-1244","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cogmind","category-dev-series-full-ui-upscaling","category-gui","category-scaling","category-ui"],"_links":{"self":[{"href":"https:\/\/x.sheep-mine.ts.net\/index.php\/wp-json\/wp\/v2\/posts\/1244","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/x.sheep-mine.ts.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/x.sheep-mine.ts.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/x.sheep-mine.ts.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/x.sheep-mine.ts.net\/index.php\/wp-json\/wp\/v2\/comments?post=1244"}],"version-history":[{"count":0,"href":"https:\/\/x.sheep-mine.ts.net\/index.php\/wp-json\/wp\/v2\/posts\/1244\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/x.sheep-mine.ts.net\/index.php\/wp-json\/wp\/v2\/media\/1245"}],"wp:attachment":[{"href":"https:\/\/x.sheep-mine.ts.net\/index.php\/wp-json\/wp\/v2\/media?parent=1244"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/x.sheep-mine.ts.net\/index.php\/wp-json\/wp\/v2\/categories?post=1244"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/x.sheep-mine.ts.net\/index.php\/wp-json\/wp\/v2\/tags?post=1244"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}