The problem with the current implementation is that the results most likely will be not only unexpected, but confusing and inconsistent, both for players and scripters.
The fundamental issue is that the different frequency settings of the GalaxySetupData structure are accessed in quite different ways. A good example for that is what you already mentioned:
Geoff the Medio wrote:It probably doesn't actually matter for the universe generation scripts though, as other than for some logging calls, the randomized galaxy setup info doesn't appear to be accessed repeatedly. I might have missed something, though.
For
most of the various parts of the universe generation (placing of specials, natives, etc.) your observation that the galaxy setup info only gets accessed once (usually passed as a parameter to a function which does the placing/distribution) is correct - but not for all. The function generating the systems for example gets the complete structure passed in, and the age and planet density options are accessed for each system and planet:
Code: Select all
def generate_systems(pos_list, gsd):
"""
Generates and populates star systems at all positions in specified list.
"""
sys_list = []
for position in pos_list:
star_type = pick_star_type(gsd.age)
system = fo.create_system(star_type, "", position[0], position[1])
if system == fo.invalid_object():
# create system failed, report an error and try to continue with next position
util.report_error("Python generate_systems: create system at position (%f, %f) failed"
% (position[0], position[1]))
continue
sys_list.append(system)
for orbit in range(fo.sys_get_num_orbits(system)):
# check for each orbit if a planet shall be created by determining planet size
planet_size = planets.calc_planet_size(star_type, orbit, gsd.planet_density, gsd.shape)
if planet_size in planets.planet_sizes:
# ok, we want a planet, determine planet type and generate the planet
planet_type = planets.calc_planet_type(star_type, orbit, planet_size)
if fo.create_planet(planet_size, planet_type, system, orbit, "") == fo.invalid_object():
# create planet failed, report an error and try to continue with next orbit
util.report_error("Python generate_systems: create planet in system %d failed" % system)
return sys_list
Essentially it makes a
dramatic difference if a scripter decides to read a setting once from the GalaxySetupData structure and uses the once read value for the rest of the computations, or reads the setting repeatedly, every time it's needed. The universe generation scripts weren't implemented with the idea in mind that reading a settings value yields different results each time. Without further adjustments this will yield very different results for the different settings. While the placement of natives, specials and monsters (IIRC) will pick one option and stick to that (so that you actually get one of the none/low/med/high distributions), "random" age and planet density will get applied very differently. You will most certainly not get a consistently young/mature/ancient galaxy, and a consistently low/medium/high planet density.
For the player that will be very confusing, because the "random" option behaves so differently for different settings.
As far as the scripters are concerned, I don't want them to have to pay attention to something like that. Of course, on the Python side we can take care of the problem quite easily, just implement the PyGalaxySetupData class in a way that it only does the random pick on first access, store the picked value internally and return the stored value on all subsequent calls.
That can, in theory, even be extended to the turn event scripts, as they are run within the same Python interpreter instance, with the same context, so all we need to do is move the GalaxySetupData class to a common module, and the data will persist between universe generation and turn events. Unless you save and reload the game, however - that's why I said, "in theory". In practice we need to implement a mechanism that provides the means to store and retrieve data on save and load. As that is needed anyway and will have to be implemented at some point, this isn't really an issue.
But all this doesn't get around the problem that Python and FOCS are still completely disconnected. You already raised that point too:
Geoff the Medio wrote:how important is it that the randomly-selected values for frequencies or galaxy ages are consistent between universe generation scripts and values used in FOCS scripts in later turns?
IIRC the main reason why we exposed those settings to FOCS was so that content scripters could adjust calculations for certain stats/values/whatever to the settings the player had chosen, and therefore to what the galaxy/map is expected to look/be like based on those settings. Examples:
- Almost every Experimentors related calculation factors in the AI aggression setting, the calculation of the turn they start to spawn their monsters also factor in galaxy size and planet density.
- Moster frequency is checked by all specials that need to decide if a guardian is spawned or not.
- Tech costs for the Singularity Of Transcendence tech is based on galaxy size.
- There have already been ideas brought up to scale PP/RP/whatever costs for whatever items based on map size/age/planet density to better scale costs with the scale of the game.
As all those operate on the assumption that the option value they get actually reflect what got used by the universe generation process, I think it's obvious what kind of issues this will cause. The results will be highly unexpected and most likely not what has been intended.
Even that aside, any content script that base calculations on one of the settings will most likely be set up in a way that expect "stable" values for the galaxy settings. For example, monster nest scripting that bases their spawning of new monsters on the monster frequency setting (would make sense, wouldn't it?) would deliver results probably not quite to the scripters expectations and intentions.
Speaking of which, that brings me to the second major flaw I see with that approach, which will affect content scripts especially (because contrary to Python they can't access the settings once and store the retrieved value): The "random" setting won't really deliver random results when used in way where the settings option value gets retrieved every time it's needed for a computation, because it will statistically "even out" (can I say that in English this way, or does that sound stupid?). Over the course of the game, or the placement/distribution/whatever of many items during universe generation, I'd expect the "random" setting to produce results that are probably different, but still similar to the respective average/medium settings. Even if the results were very different to medium, they would probably be consistently comparable in the sense that they will create a certain "kind" of distribution that is characteristic for the "random" setting, like all the other options will produce results that are characteristic for them.
What you certainly
won't get is a distribution that is characteristic of one randomly picked option, but that's actually what we want.
That leaves the question, why we'd want to go with an apporach that has such obvious issues, instead of just go with the simple pick once and stick with that solution. If I understand correctly, your main concern is that the player needs to stay in the dark about the options that got picked, because not knowing what kind of galaxy/map he is on is the point of the whole thing after all.
But that can be achieved without resorting to picking a new value each time a setting is accessed. Basically what the GalaxySetupData structure has to do is provide and keep track of two values for each setting: the one the player has chosen in the game setup dialog (I'll refer to that as the "selected option") and the one the game actually uses for all related computations (I'll refer to that as the "actual option"). Python needs to be able to read both, and write back the actual options. FOCS probably only needs to be able to read the actual options. The player only ever gets to see the selected options, whereever they are presented to him.
Extending the GalaxySetupData structure (and adjusting the Python and FOCS bindings) accordingly shouldn't be too hard I guess, and I think that is the simplest and most straightforward solution. Your idea of providing a hash based on the RNG seed to ensure that the same "random" value gets picked each time a setting is accessed could be done too I guess, but ensuring that both Python and FOCS produce the same results, and the required getter/setter framework will probably more complicated than just extending GalaxySetupData as I suggested above.