[quote=“Kevin Granade, post:38, topic:13488”]That’s a really hard sell in a project like this, I’m not about to tell people that they MUST code certain things in LUA, regardless of whether they know the language or not. That literally means refusing contributions in some cases.
[snip]
If we’re talking converting existing functionality to LUA, I’m sure as hell not going to shoulder the overhead of rewriting tens of thousands of lines of code* in a scripting language purely in the name of “correctness”. For that matter, I don’t want to review tens of thousands of lines of LUA code that don’t add functionality to the game, but instead just move it elsewhere.
*You might think I’m exaggerating, but check the main targets for this. iuse.cpp ~8,000 lines of code, mapgen.cpp ~10,000 lines of code, mapgen_functions.cpp ~5,000 lines of code, iexamine.cpp ~3,500 lines of code, monattack.cpp ~4,500 lines of code.
[snip]
This assertion doesn’t follow, you can obviously do anything in C++ that you can do in LUA. There is some kind of dynamic in games development where LUA or other scripting languages are preferred over coding things directly in C++, but it just hasn’t surfaced in this project. We seem to have very little LUA expertise, even though a number of people have stated it’s the "right thing to do.[/quote]
The stuff that’s being externalised will continue to be externalised and genericised for re-use elsewhere, and I never for a moment suggested that any one person would be expected to shoulder the burden entirely.
I’ve been looking at the mapgen stuff specifically as something I’ve wanted to hop into and start externalising myself, which is hampered mainly by the fact that I would need to get directly involved with the build process too.
The reason that dynamic exists is because of the lean and mean principle: building story mechanics into the main build involves an overhead that can slow or stifle the development process on both sides of the equation, especially in something as mutable as a story. While development is normally iterative, moving from one feature to the next except when feature creep obviates or breaks previous features or bugs are discovered, storywriting is normally recursive, continuously refining, revising, rearranging, deleting, expanding, and distilling existing pieces to build a more cohesive whole. It can of course mimic development closely (and vice versa if there is no check on feature creep), but it’s usually easy to spot those games that have done their stories that way: they usually feel like run-on sentences with one stereotype after the next, rather than archetypes who fit in (or more accurately don’t fit in).
My assertion is that it is indeed necessary and the right thing to do because any kind of direct integration with the program code will naturally suffer from substantial “noise” as every last change is made, re-made, reverted, and re-re-made. I’ll be a lesser man and note that there are three separate implementations of winches in the game engine, one for each of garages, farms, and vault, which is precisely the scenario that a data-based architecture is intended to correct. Okay, this was completely wrong. Could’ve sworn I had seen separate code somewhere…
Essential balance of map generation is another – for instance, the hard-coded one_in(100) for a spiderweb to spawn means that probabilistically, you will encounter at least one after walking through just 23 horizontal tiles of forest, assuming your reality bubble overlaps three map tiles. This could be switched over to externalising that magic number into an option-friendly global variable, or could be turned into its own feature request to refine the probability further, but if the work were done to externalise these sorts of things into their own map tiles without concerning themselves with balance, a balancing and refinement pass could then balance dozens or hundreds of these sorts of things simultaneously in a fraction of the time and overhead.
This divide is also why almost every substantive change I’ve seen in the codebase so far has focused on raw mechanics and refining the existing feature set; it’s not easy or quick to work on balance-, plot-, or mission-related mechanics as both the development and scripting language are oriented towards engine-focused behaviour.
It’s definitely the latter scenario. For instance, every single one of the extant “do one op to all monster types” Lua mods, including even the Ninja mod, uses this:
local monster_types = game.get_monster_types()
This function is not listed in class_definitions.lua or anywhere else in the data. One has to go directly to the C++ code in catalua.cpp to find it.
(Incidentally, the Japanese Elona roguelike is written in a language that is so high-level (Hot Soup Processor) that it’s a borderline scripting language in its own right. I suspect that Japanese modders are thus naturally inclined to use Lua. I also note that it must be jarring to have to switch to a Romantic/Germanic language to program with: to a person knowing a pictographic language, code is quite literally code.)
I’ve heard about how easy Lua is to integrate compared to, say, Python; if it really is that easy then it won’t be a major loss, so long as it can come back in another form later on. It’d render anything I was contemplating moot, but all of that doesn’t even exist yet so it’s a literal zero-value loss (actually even a net benefit to me as it saves me some time, come to think of it, although I’ll lament not being able to do it ;-)).
In the end, and it seems like we’re agreed on this, the difficulty here is that the Lua support for all of its fantastic power is basically just a layer on the game engine which requires direct and intimate knowledge of the program code, because there isn’t enough abstraction and because the totality of its bindings are not actually visible to anyone who’d want to use it. Don’t get me wrong, having the power to affect the game engine so tightly is a godsend in any scripting environment – but it’s not documented meaningfully or in a way that would encourage its use over the game engine itself, and conventions that would apply from other “languages” like the scripts in Bethesda’s Gamebryo incarnations don’t apply here. Thus, anyone who wants to use it will need to refer directly to the program code anyway.
The pragmatists among you have correctly realised that if they have to learn/know C++ to use the Lua support then they might as well just use C++, especially if they don’t also already know Lua. The… let’s call them “lazyists”, like me, would much rather work outside of that environment with rapid prototyping that needs no more than the load time of the game to try. Given the immense popularity of the game on Reddit and those few sites I’ve bumped into that laud it, there’s a substantially low ratio between “devs” and “community” and I suspect that ratio reflects programming ability – many people would contribute if they could, but don’t.
(Granted, the (unsuccessful) VS build I tried took only about three or four minutes to build the object code before it broke at the linking phase, although I’m not sure how much longer it would have chugged on the linker.)
If you do want a candidate for an improved architecture, all you really need is an event-driven system:
-
Global: Currently already handled with the main.lua in each mod.
-
Missions: Currently, each “mod” provides a global script and a preloader to attach scripts to iuse actions. Taking a page from Bethesda, it could also be a container for missions that run scripts – each mission could have zero or more scripts attached. Each script attachment would define a turn interval where it would fire, and these scripts would thus heartbeat so long as that mission is in the player’s quest tracker. (Because we don’t have to work around the unique perversions of the Bethesda engines, there’s no need to rely on hidden quests and that sort of nonsense, since we have a built-in global script architecture also.)
-
Mapgen: Any time a map tile is loaded, check the JSON for that map tile and, if it defines one, fire the main function in the named script. This would make it almost simplistic to externalise the majority of the mapgen code in and of itself.
-
Item Use: Already functional, although tying scripts to iuse actions more directly (i.e., specifying the filename of the script to run directly in the JSON) would make things cleaner.
-
Objects: Each object can have zero or more scripts attached, and objects receive heartbeats to fire their scripts which are divided over the total number of loaded script objects to avoid burdening the engine – unless a script is declared mission-critical, where it would fire every turn. (Some common sense conventions like declaring creature objects mission-critical if they happen to be in combat with the player would also make sense.) This would handle Macguffins, scripted movements and scenes, and could even be used to override AI behaviour and control them manually.
…
There is, for the sake of honesty, one overriding and uncontestable counter-argument to anything I have here: if the game is intended to be a single centralised forum working on a single unified plot, rather than based on a community that contributes its own independent plotlines and featuresets – i.e., a game that is based on creating an idealised singleton rather than a game that is based on catering to individual whims and fancies and creating their own unique flavours – then there is absolutely no benefit to distinguishing a modder versus a developer. It will, unfortunately, limit the game to those modders who are also developers, but that isn’t necessarily a bad thing.
The real Duesy (if implementing Lua later on is a bitch, then this is the name-it-after-my-wife-Dotti) is that any meaningful script system would have to keep certain entities persisting at a low level of fidelity, with periodic script updates, which would involve basically discarding the reality bubble as we know it.
(Out of context, that last part almost sounds like something you’d read on a dark web forum…)