From 10c6a7069645d8c06600dacbc90bc7d0d79fb20b Mon Sep 17 00:00:00 2001 From: black-sliver Date: Mon, 19 Jul 2021 01:02:23 +0200 Subject: [PATCH] Auto-validate Option.schema, Factorio: allow setting pollution values --- Options.py | 9 +++++ data/factorio/mod_template/data.lua | 2 +- playerSettings.yaml | 14 +++++-- requirements.txt | 3 +- worlds/factorio/Options.py | 63 +++++++++++++++++++++++++++-- worlds/factorio/__init__.py | 6 +-- worlds/factorio/requirements.txt | 3 +- 7 files changed, 88 insertions(+), 12 deletions(-) diff --git a/Options.py b/Options.py index e62f5150..64841843 100644 --- a/Options.py +++ b/Options.py @@ -19,6 +19,15 @@ class AssembleOptions(type): # apply aliases, without name_lookup options.update({name[6:].lower(): option_id for name, option_id in attrs.items() if name.startswith("alias_")}) + + # auto-validate schema on __init__ + if "schema" in attrs.keys(): + def validate_decorator(func): + def validate(self, *args, **kwargs): + func(self, *args, **kwargs) + self.value = self.schema.validate(self.value) + return validate + attrs["__init__"] = validate_decorator(attrs["__init__"]) return super(AssembleOptions, mcs).__new__(mcs, name, bases, attrs) class Option(metaclass=AssembleOptions): diff --git a/data/factorio/mod_template/data.lua b/data/factorio/mod_template/data.lua index 218509c3..bf10ee98 100644 --- a/data/factorio/mod_template/data.lua +++ b/data/factorio/mod_template/data.lua @@ -1,2 +1,2 @@ {% from "macros.lua" import dict_to_lua %} -data.raw["map-gen-presets"].default["archipelago"] = {{ dict_to_lua({"default": False, "order": "a", "basic_settings": world_gen}) }} \ No newline at end of file +data.raw["map-gen-presets"].default["archipelago"] = {{ dict_to_lua({"default": False, "order": "a", "basic_settings": world_gen["basic"], "advanced_settings": world_gen["advanced"]}) }} diff --git a/playerSettings.yaml b/playerSettings.yaml index 23b9606e..2ae233e5 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -115,6 +115,7 @@ Factorio: terrain_segmentation: 0.5 water: 1.5 autoplace_controls: + # set size to 0 to disable coal: frequency: 1 size: 3 @@ -152,9 +153,16 @@ Factorio: peaceful_mode: 0 cliff_settings: name: cliff - cliff_elevation_0: 10 - cliff_elevation_interval: 40 - richness: 1 + cliff_elevation_0: 10 # base elevation, can't be changed in GUI + cliff_elevation_interval: 40 # = 40/frequency + richness: 1 # 0=off, >0 = continuity + pollution: + enabled: true + diffusion_ratio: 0.02 + ageing: 1 + enemy_attack_pollution_consumption_modifier: 1 + min_pollution_to_damage_trees: 60 + pollution_restored_per_tree_damage: 10 Minecraft: advancement_goal: 50 # Number of advancements required (out of 92 total) to spawn the Ender Dragon and complete the game. combat_difficulty: # Modifies the level of items logically required for exploring dangerous areas and fighting bosses. diff --git a/requirements.txt b/requirements.txt index cd68e1cc..c5c199df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ PyYAML>=5.4.1 fuzzywuzzy>=0.18.0 prompt_toolkit>=3.0.19 appdirs>=1.4.4 -jinja2>=3.0.1 \ No newline at end of file +jinja2>=3.0.1 +schema>=0.7.4 diff --git a/worlds/factorio/Options.py b/worlds/factorio/Options.py index 64e1ae2b..379dc679 100644 --- a/worlds/factorio/Options.py +++ b/worlds/factorio/Options.py @@ -1,6 +1,12 @@ +from __future__ import annotations import typing from Options import Choice, OptionDict, Option, DefaultOnToggle +from schema import Schema, Optional, And, Or + +# schema helpers +FloatRange = lambda low,high: And(Or(int,float), lambda f: low<=f<=high) +LuaBool = Or(bool, And(int, lambda n: n in (0,1))) class MaxSciencePack(Choice): @@ -92,7 +98,10 @@ class FactorioStartItems(OptionDict): class FactorioWorldGen(OptionDict): - default = {"terrain_segmentation": 0.5, "water": 1.5, + # FIXME: do we want default be a rando-optimized default or in-game DS? + value: typing.Dict[str, typing.Dict[str, typing.Any]] + default = {"terrain_segmentation": 0.5, + "water": 1.5, "autoplace_controls": {"coal": {"frequency": 1, "size": 3, "richness": 6}, "copper-ore": {"frequency": 1, "size": 3, "richness": 6}, "crude-oil": {"frequency": 1, "size": 3, "richness": 6}, @@ -101,9 +110,57 @@ class FactorioWorldGen(OptionDict): "stone": {"frequency": 1, "size": 3, "richness": 6}, "trees": {"frequency": 1, "size": 1, "richness": 1}, "uranium-ore": {"frequency": 1, "size": 3, "richness": 6}}, - "starting_area": 1, "peaceful_mode": False, + "seed": None, + "starting_area": 1, + "peaceful_mode": False, "cliff_settings": {"name": "cliff", "cliff_elevation_0": 10, "cliff_elevation_interval": 40, - "richness": 1}} + "richness": 1}, + "pollution": {"enabled": True, "diffusion_ratio": 0.02, "ageing": 1, + "enemy_attack_pollution_consumption_modifier": 1, + "min_pollution_to_damage_trees": 60, + "pollution_restored_per_tree_damage": 10}} + schema = Schema({ + "basic": { + Optional("terrain_segmentation"): FloatRange(0.166,6), + Optional("water"): FloatRange(0.166,6), + Optional("autoplace_controls"): { + str: { + "frequency": FloatRange(0,6), + "size": FloatRange(0,6), + "richness": FloatRange(0.166,6)}}, + Optional("seed"): Or(None,And(int, lambda n: n>=0)), + Optional("starting_area"): FloatRange(0.166,6), + Optional("peaceful_mode"): LuaBool, + Optional("cliff_settings"): { + "name": str, "cliff_elevation_0": FloatRange(0,99), + "cliff_elevation_interval": FloatRange(0.066,241), # 40/frequency + "richness": FloatRange(0,6)}, + }, + "advanced": { + Optional("pollution"): { + Optional("enabled"): LuaBool, + Optional("diffusion_ratio"): FloatRange(0,0.25), + Optional("ageing"): FloatRange(0.1,4), + Optional("enemy_attack_pollution_consumption_modifier"): FloatRange(0.1,4), + Optional("min_pollution_to_damage_trees"): FloatRange(0,9999), + Optional("pollution_restored_per_tree_damage"): FloatRange(0,9999)} + } + }) + + def __init__(self, value: typing.Dict[str, typing.Any]): + advanced = {"pollution"} + self.value = { + "basic": { key: value[key] for key in value.keys() - advanced }, + "advanced": { key: value[key] for key in value.keys() & advanced } + } + + @classmethod + def from_any(cls, data: typing.Dict[str, typing.Any]) -> FactorioWorldGen: + if type(data) == dict: + return cls(data) + else: + raise NotImplementedError(f"Cannot Convert from non-dictionary, got {type(data)}") + factorio_options: typing.Dict[str, type(Option)] = { "max_science_pack": MaxSciencePack, diff --git a/worlds/factorio/__init__.py b/worlds/factorio/__init__.py index eaf907a1..6d942fca 100644 --- a/worlds/factorio/__init__.py +++ b/worlds/factorio/__init__.py @@ -36,9 +36,9 @@ class Factorio(World): self.world.get_location(tech_name, self.player).place_locked_item(tech_item) else: self.world.itempool.append(tech_item) - world_gen = self.world.world_gen[self.player].value - if world_gen.get("seed", None) is None: # allow seed 0 - world_gen["seed"] = self.world.slot_seeds[self.player].randint(0, 2**32-1) # 32 bit uint + map_basic_settings = self.world.world_gen[self.player].value["basic"] + if map_basic_settings.get("seed", None) is None: # allow seed 0 + map_basic_settings["seed"] = self.world.slot_seeds[self.player].randint(0, 2**32-1) # 32 bit uint generate_output = generate_mod diff --git a/worlds/factorio/requirements.txt b/worlds/factorio/requirements.txt index 0fe5b405..92254420 100644 --- a/worlds/factorio/requirements.txt +++ b/worlds/factorio/requirements.txt @@ -1,2 +1,3 @@ kivy>=2.0.0 -factorio-rcon-py>=1.2.1 \ No newline at end of file +factorio-rcon-py>=1.2.1 +schema>=0.7.4