-
-
Notifications
You must be signed in to change notification settings - Fork 261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extending (extruding?) rot.js #215
Comments
Hi @dmchurch , thanks for the proposal! It is certainly an interesting food for thought. I would say that while your extension to third dimension is worthwile, I am not sure it makes sense to apply it to existing rot.js objects (such as the ROT.Display class). Instead, perhaps introducing new 3d-based interfaces would be more sensible? My intuition here is that 2d is still the main usecase for 95% of users, so it makes little sense to provide them with a more complex codebase. (The existing TS code might definitely use some modernization efforts, though.) Apart from the the Display class, what other components of rot.js would you see fit for providing a three-dimensional API? |
Oh I wholeheartedly agree that the ROT.Display.registerPreset("light", {
width: 80,
height: 24,
layout: "tile",
bg: "black",
tileWidth: 16,
tileHeight: 16,
tileSet: Tileset.light.img,
tileMap: Tileset.light.tileMap,
}); and then <rot-display preset="light" width="1" height="1" fg="red" bg="transparent">!</rot-display> to render a single-tile Display containing a red <rot-display preset="light">
<pre>┌───┐</pre>
<pre>│.>.│</pre>
<pre>+.@.│</pre>
<pre>│..f│</pre>
<pre>└───┘</pre>
</rot-display> to render a static image of a classic Nethack-style room with a cat, like you might want to show in a help screen; for older browsers that don't support Web Components (few and far between, these days), those tags would gracefully fall back to showing a useful text-based rendering of the desired content. But no, in terms of 3D-esque rendering like I'm doing, I'd instead probably just import something like my
Yeah, my original inclination had been to just add a set of classes with a new 3d interface, the way I have Specifically, my thought is that instead of extending the existing rot.js code from two dimensions (with all the nested for loops to iterate through x and y) I instead reduce the code to 1 dimension. All internal coordinate-based data storage, like the internal As for communication to/from calling code, I'd change the places that currently expect or provide separate
I wholeheartedly agree with you here - that 95% is likely an underestimate, even! but there are really interesting things you can do with the ability to temporarily or partially extend your game to 3D. For example, in a classic Nethack-style game, you could add a second layer above the gameplay layer which only renders room walls, to give them a feeling of depth without changing the gameplay. Or you could have a dungeon floor with a hole in it and render a temporary layer below, through which you could have the player able to see what they'd be dropping onto if they step onto it. Or, for a more open-environment game than a dungeon crawler, you could use extra z-layers to, say, render trees or buildings at realistic-looking heights, which is what Door in the Woods does, and where my teammate got the idea for the parallax rendering that I used for Deiphage. I highly recommend checking out the Steam page for that game for video of what you can do with this kind of rendering when done outside the rushed context of a game jam - I'd love for that kind of look to be available to folks that don't have my level of comfort mucking around with foreign code and display algorithms! All that said, of course, it would be an API-breaking change. Even if I made the wrapper functions the default exposed API and required consumers to choose the advanced API explicitly, the internal computation contexts require an understanding of the world-coordinate bounds in order to translate between index and (x, y) coordinate. You could make it compatible with probably 90% of existing code by assuming that the |
Also worth noting, I might take some of the more genericizable classes I made for Deiphage and import them into rot.js for external use. The input layer I designed, in particular - I coded it with an eye to making it extensible, and it would give rot.js users the ability to do input handling that could natively handle keyboard/mouse/touch entry, which would be super useful if you're in a jam (pun definitely intended). And, while I very much appreciate the way that rot.js hands you tools and then gets out of the way, I could definitely see an argument for importing something like my |
Yeah, these are solid ideas, definitely a lot to chew on. I think it would now be useful to divide the whole set of ideas into three disctinct areas:
And try to focus on these independently. Let me share my opinions about that: 1. Declarative UII am a big fan of Custom Elements, so I would say that the implementation of the said Presets are yet another story. Once the 2. More dimensionsThe idea looks simple on paper, but I am afraid you might run into issues in some algorithms that are strongly bound to the 2d realm. I would suggest trying that in a separate branch/fork and see how it goes. Also, I like the idea of a 3. MiscThere is this weird namespacing-legacy thingy when ROT components are often available in the ROT.* namespace, but this works only if you use the pre-built JS version. Once you go for the typescript code, you import modules as necessary, naming them locally in the process. I would say that the second approach is currently the right way to go: providing individual components via ES modules, so that there is no need to usurp the ROT.Engine namespace for a particular functionality. The same obviously applies for XY/XYZ classes/objects, that would reside in a dedicated module which contains vector arithmetics etc. Alternatively, we might want to re-use the great glMatrix project (https://glmatrix.net/) as this is a very reasonable approach for working with multi-dimensional vector data in a memory-efficient way. |
1. Declarative UI
Oh definitely. There's absolutely no reason to require that you use a Web Component to render a ROT.Display - declarative should always be just one way to describe a thing.
You don't think so? I'd been thinking that [On rereading, I think you might have instead been more concerned with the user having to directly import and interact with the 2. More dimensions
Oh yeah for sure, they wouldn't all work out nicely - anything that works with the geometry of it, like the shadowcasters, will only be happy with the dimensionality they're designed for. (I did an extremely abortive attempt to alter the PreciseShadowcasting algorithm to cope with a sphere instead of a circle before deciding that I didn't have time for that, it's a game jam, so I'm just doing the 2d shadowcast on-layer, then dropping a vertical from each visible point to wherever is visible in layers above/below. That said, anything that only interacts with maps on a neighbor/axis basis - like pathfinding, like generation - will work just fine in whatever dimensionality, though some care needs to be taken regarding bounds; performing "x+1" on the index of a cell at the right edge of a map will result in a valid map index on the left edge of a map. For something like I'll note also that I consider the common
🤦 Right, of course it would - if we're doing multidimensionality by genericizing, then limiting things to "how many coordinate letters can you come up with" is kinda silly, huh! I do like the readability that having the coordinate names gets you for the common case, though, so I think that I'd be likely to make the output side (when passing a value to a callback) a subclass of Array which has a get/set pair (or, perhaps, just a getter) for The one thing I do want to be cautious about is to avoid too much in the way of allocations - I'd probably specify that "the object that gets passed to the callback function will be reused, don't store the object itself, store its coordinates", and encourage that by making the type "readonly" at the Typescript level. I've had to do a lot of low-level JS optimization for my other major project, Idle Loops, and the number-one performance hit I've found for most algorithms (beyond the obvious "try to keep your big-O notation to a low factor") is avoiding allocations in critical-path functions, which means that a lot of language features like 3. Misc
Fair enough! I was mainly thinking "namespace" in the sense of "concept space", anyway, like "where does this go in the help docs". The actual code organization matters less. (well. it matters a lot that the code BE organized. it does not matter so much HOW it is organized.)
Ooh, I haven't heard of this! I'll have to check it out but yeah, I imagine that could be quite helpful! Shall I assume, then, that you are at least tentatively interested in bringing this functionality to rot.js? If so I'm delighted to follow your lead on how/where/what to do 😄 |
Exactly, that was my point 👍
Well, yeah. Please excuse my late response; I am rather busy these days and while I am still interested in both rot.js and roguelike development, I am focusing more on other projects of mine. As for the work organization: I would suggest making a rot.js fork where you can
As for the 2), perhaps pick just one of the existing classes (pathfined, dungeon generator) and try to adjust it accordingly. Let's see how that works -- and what modifications need to be done to the public API. |
Sounds good! Once my dev energy isn't wholly focused towards the continuation of our 7DRL project I'll start working on transferring some of my work over using actual proper coding technique rather than rushed jam work 😂 |
Ah, quick question for you. I'm working on getting a local rot.js development environment set up and testing to make sure the build gets the same results as the existing library, and I'm running into some discrepancies that I'd like to get your take on. Though first, just to check: are you capable of running a full build on your machine and getting the same results as currently checked into the repo? I've been working under that assumption but it occurred to me that it might not be accurate. [Edited to add: I just now discovered that my fork was not up-to-date with the rebuild you in fact just did! That would have cleared up so many of my issues. Whoops!] |
Yep, this was probably the source of confusion. A full rebuild is currently a little bit problematic with respect to the auto-generated documentation, because the output html changes a bit on each run (includes timestamps). This means that an unrelated JS commit/fix influences a rather large number of files. One approach would be to not store the documentation in the repository, running the generation task only when publishing via GitHub pages/actions. Unfortunately, I have very little experience in this field and also no time for that. |
Heh, hear that. I can look into that on my fork as well - I actually recently had to write a custom GH action for my project to publish a manifest with checksums of all the files in the repo, just so that there would be some file on the site that would be guaranteed to change with each update, for cache-checking reasons. One approach I've seen is to store the documentation in the same repo but on an unrelated gh-pages branch, but it'd be just as easy to run the typedoc task and publish the results to the GH page without storing them in the repo itself. Do you have a preference, between storing the generated docs in an off-branch vs keeping the live docs out of the repository entirely? |
No. I see both the value of using an Action (the docs being generated stuff that does not belong to the repository, in a puristic view) as well as the value of checking out a project and having the documentation at hand automatically. 🤷 |
On the latter note I'm already planning to look into generating an offline version of the docs for the main repo itself - strip out all the links and search metadata and it looks like they can generate in a reproducible manner, and I'd be surprised if there wasn't a way to convince typedoc to suppress those. That way the in-source docs don't change on every commit, but the online docs have live links and search functionality. |
Sounds like an optimal combination. |
I've started some initial work on the fork! Please feel free to watch development on the rot3d branch when and as you have the time and inclination; I've added the fork as a git submodule in my post-7DRL project and I'm using Typescript's new multi-project build functionality to allow depending on (and rebuilding) the original TS sources of the rot.js code, rather than referencing the prebuilt JS modules as an NPM dependency. Over the next week or two I'll migrate my monkey-patches and ad-hoc extensions from my 7drl repo into my rot.js fork, so that I can work with them in context and figure out where the pain points are. I'll try and keep the commit history clean on the rot.js side of things, to make reviewing the code easier; I'll let you know when I've got something substantive for you to look at. I'll probably also be doing some general cleanup and performance improvements in the rot.js library, since performance optimization is one of my areas of expertise, and code cleanup and consistency refactoring are guilty pleasures of mine. I'd recommend that, presuming we can agree on scope and get some or all of my fork merged back into upstream, we make this a major-version change and not just a minor bump. Not because I think any of the existing API needs breaking, but just because I'd expect enough will be changing internally that any code that (like my 7drl code) does monkey-patching or uses advanced TS type manipulation on rot.js types might break in unexpected ways. I'd be much more comfortable declaring "this is a breaking change that might not actually break your code at all" than declaring "this is a non-breaking change unless you did A, B, or C in your code". Oh and, you should feel free to mimic my setup for Deiphage if you want to try out using my fork as a submodule the way I am. I've set the |
I've opened #217 for you to take a look at! I'm not expecting it to actually get pulled in at the moment, so there's no rush - I'm leaving it in draft form for the time being, until I have some more substantive and useful changes to show. This should give you an idea of the type of code I write, though, as well as the degree of refactor/change I'm comfortable with. |
Yeah, no problems here. I am completely okay with larger changes, breaking changes, major versions etc. |
Hi! I'm the coding half of the team that made Deiphage for the 2024 7DRL jam, which you can see live on my repo's GH Pages site at https://dmchurch.github.io/7drl-2024/. As you can see, I've added another layer to the standard usage of rot.js - it is a classic roguelike set on a 3D tile grid 😂 I ended up creating specializations of a lot of the rot.js internal classes in order to cope with the 3D aspect of it, obviously!
I've been wanting to continue building on the idea, so I thought that now that I'm out of the frantic rush of the jam, I'd go back over all the dirty hacks I made and turn them into something that I could reuse and extend further, and that means making some fairly substantial edits to the rot.js codebase in order to support a varying number of dimensions on the majority of classes.
My question is, would you be interested in this as a potential direction (no pun intended) for the rot.js project to go in, and if so, should I work on this with the intent that it gets absorbed back into the main rot.js codebase? If so I'll work to maximize compatibility with existing projects using rot.js, and I'll also probably do some modernization of the TS code while I'm in there.
If not, that is entirely fine - it just means that I'll be creating a split for personal use, which will certainly be easier from a coding standpoint! But let me know - it'd be fun to see other rot.js projects using the stuff I've been working on, cause I can think of all sorts of ways you could use an extensible layered tile display in the browser 😄
The text was updated successfully, but these errors were encountered: