diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ab94326d8188..27ca76e41f8f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,8 @@ jobs: - name: Install python uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '~3.12.7' + check-latest: true - name: Download run-time dependencies run: | Invoke-WebRequest -Uri https://github.com/Ijwu/Enemizer/releases/download/${Env:ENEMIZER_VERSION}/win-x64.zip -OutFile enemizer.zip @@ -111,7 +112,8 @@ jobs: - name: Get a recent python uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '~3.12.7' + check-latest: true - name: Install build-time dependencies run: | echo "PYTHON=python3.12" >> $GITHUB_ENV diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b28ec8733408..aec4f90998cf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,7 +44,8 @@ jobs: - name: Get a recent python uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '~3.12.7' + check-latest: true - name: Install build-time dependencies run: | echo "PYTHON=python3.12" >> $GITHUB_ENV diff --git a/ModuleUpdate.py b/ModuleUpdate.py index dada16cefcaf..04cf25ea5594 100644 --- a/ModuleUpdate.py +++ b/ModuleUpdate.py @@ -5,8 +5,15 @@ import warnings -if sys.version_info < (3, 10, 11): - raise RuntimeError(f"Incompatible Python Version found: {sys.version_info}. 3.10.11+ is supported.") +if sys.platform in ("win32", "darwin") and sys.version_info < (3, 10, 11): + # Official micro version updates. This should match the number in docs/running from source.md. + raise RuntimeError(f"Incompatible Python Version found: {sys.version_info}. Official 3.10.15+ is supported.") +elif sys.platform in ("win32", "darwin") and sys.version_info < (3, 10, 15): + # There are known security issues, but no easy way to install fixed versions on Windows for testing. + warnings.warn(f"Python Version {sys.version_info} has security issues. Don't use in production.") +elif sys.version_info < (3, 10, 1): + # Other platforms may get security backports instead of micro updates, so the number is unreliable. + raise RuntimeError(f"Incompatible Python Version found: {sys.version_info}. 3.10.1+ is supported.") # don't run update if environment is frozen/compiled or if not the parent process (skip in subprocess) _skip_update = bool(getattr(sys, "frozen", False) or multiprocessing.parent_process()) diff --git a/docs/running from source.md b/docs/running from source.md index 66dd1925c897..33d6b3928e54 100644 --- a/docs/running from source.md +++ b/docs/running from source.md @@ -7,7 +7,9 @@ use that version. These steps are for developers or platforms without compiled r ## General What you'll need: - * [Python 3.10.15 or newer](https://www.python.org/downloads/), not the Windows Store version + * [Python 3.10.11 or newer](https://www.python.org/downloads/), not the Windows Store version + * On Windows, please consider only using the latest supported version in production environments since security + updates for older versions are not easily available. * Python 3.12.x is currently the newest supported version * pip: included in downloads from python.org, separate in many Linux distributions * Matching C compiler diff --git a/worlds/blasphemous/Options.py b/worlds/blasphemous/Options.py index 0bd08b13b260..e0bbcd770758 100644 --- a/worlds/blasphemous/Options.py +++ b/worlds/blasphemous/Options.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from Options import Choice, Toggle, DefaultOnToggle, DeathLink, PerGameCommonOptions, OptionGroup +from Options import Choice, Toggle, DefaultOnToggle, DeathLink, PerGameCommonOptions, StartInventoryPool, OptionGroup import random @@ -213,6 +213,7 @@ class BlasphemousDeathLink(DeathLink): @dataclass class BlasphemousOptions(PerGameCommonOptions): + start_inventory_from_pool: StartInventoryPool prie_dieu_warp: PrieDieuWarp skip_cutscenes: SkipCutscenes corpse_hints: CorpseHints diff --git a/worlds/blasphemous/__init__.py b/worlds/blasphemous/__init__.py index 67031710e4eb..a967fbac9289 100644 --- a/worlds/blasphemous/__init__.py +++ b/worlds/blasphemous/__init__.py @@ -137,12 +137,6 @@ def create_items(self): ] skipped_items = [] - junk: int = 0 - - for item, count in self.options.start_inventory.value.items(): - for _ in range(count): - skipped_items.append(item) - junk += 1 skipped_items.extend(unrandomized_dict.values()) @@ -194,9 +188,6 @@ def create_items(self): for _ in range(count): pool.append(self.create_item(item["name"])) - for _ in range(junk): - pool.append(self.create_item(self.get_filler_item_name())) - self.multiworld.itempool += pool self.place_items_from_dict(unrandomized_dict) diff --git a/worlds/factorio/Options.py b/worlds/factorio/Options.py index dd17e2d68bdd..72f438778b60 100644 --- a/worlds/factorio/Options.py +++ b/worlds/factorio/Options.py @@ -272,6 +272,12 @@ class AtomicRocketTrapCount(TrapCount): display_name = "Atomic Rocket Traps" +class AtomicCliffRemoverTrapCount(TrapCount): + """Trap items that when received trigger an atomic rocket explosion on a random cliff. + Warning: there is no warning. The launch is instantaneous.""" + display_name = "Atomic Cliff Remover Traps" + + class EvolutionTrapCount(TrapCount): """Trap items that when received increase the enemy evolution.""" display_name = "Evolution Traps" @@ -467,6 +473,7 @@ class FactorioOptions(PerGameCommonOptions): cluster_grenade_traps: ClusterGrenadeTrapCount artillery_traps: ArtilleryTrapCount atomic_rocket_traps: AtomicRocketTrapCount + atomic_cliff_remover_traps: AtomicCliffRemoverTrapCount attack_traps: AttackTrapCount evolution_traps: EvolutionTrapCount evolution_trap_increase: EvolutionTrapIncrease @@ -500,6 +507,7 @@ class FactorioOptions(PerGameCommonOptions): ClusterGrenadeTrapCount, ArtilleryTrapCount, AtomicRocketTrapCount, + AtomicCliffRemoverTrapCount, ], start_collapsed=True ), diff --git a/worlds/factorio/__init__.py b/worlds/factorio/__init__.py index dd14ab434717..8f8abeb292f1 100644 --- a/worlds/factorio/__init__.py +++ b/worlds/factorio/__init__.py @@ -77,6 +77,7 @@ class FactorioItem(Item): all_items["Cluster Grenade Trap"] = factorio_base_id - 5 all_items["Artillery Trap"] = factorio_base_id - 6 all_items["Atomic Rocket Trap"] = factorio_base_id - 7 +all_items["Atomic Cliff Remover Trap"] = factorio_base_id - 8 class Factorio(World): @@ -142,6 +143,7 @@ def create_regions(self): self.options.grenade_traps + \ self.options.cluster_grenade_traps + \ self.options.atomic_rocket_traps + \ + self.options.atomic_cliff_remover_traps + \ self.options.artillery_traps location_pool = [] @@ -194,7 +196,8 @@ def sorter(loc: FactorioScienceLocation): def create_items(self) -> None: self.custom_technologies = self.set_custom_technologies() self.set_custom_recipes() - traps = ("Evolution", "Attack", "Teleport", "Grenade", "Cluster Grenade", "Artillery", "Atomic Rocket") + traps = ("Evolution", "Attack", "Teleport", "Grenade", "Cluster Grenade", "Artillery", "Atomic Rocket", + "Atomic Cliff Remover") for trap_name in traps: self.multiworld.itempool.extend(self.create_item(f"{trap_name} Trap") for _ in range(getattr(self.options, diff --git a/worlds/factorio/data/mod/lib.lua b/worlds/factorio/data/mod/lib.lua index 7be7403e48f1..517a54e3d642 100644 --- a/worlds/factorio/data/mod/lib.lua +++ b/worlds/factorio/data/mod/lib.lua @@ -28,12 +28,23 @@ function random_offset_position(position, offset) end function fire_entity_at_players(entity_name, speed) + local entities = {} for _, player in ipairs(game.forces["player"].players) do - current_character = player.character - if current_character ~= nil then - current_character.surface.create_entity{name=entity_name, - position=random_offset_position(current_character.position, 128), - target=current_character, speed=speed} + if player.character ~= nil then + table.insert(entities, player.character) end end + return fire_entity_at_entities(entity_name, entities, speed) +end + +function fire_entity_at_entities(entity_name, entities, speed) + for _, current_entity in ipairs(entities) do + local target = current_entity + if target.health == nil then + target = target.position + end + current_entity.surface.create_entity{name=entity_name, + position=random_offset_position(current_entity.position, 128), + target=target, speed=speed} + end end diff --git a/worlds/factorio/data/mod_template/control.lua b/worlds/factorio/data/mod_template/control.lua index b08608a60ae9..e486c7433095 100644 --- a/worlds/factorio/data/mod_template/control.lua +++ b/worlds/factorio/data/mod_template/control.lua @@ -737,6 +737,13 @@ end, ["Atomic Rocket Trap"] = function () fire_entity_at_players("atomic-rocket", 0.1) end, +["Atomic Cliff Remover Trap"] = function () + local cliffs = game.surfaces["nauvis"].find_entities_filtered{type = "cliff"} + + if #cliffs > 0 then + fire_entity_at_entities("atomic-rocket", {cliffs[math.random(#cliffs)]}, 0.1) + end +end, } commands.add_command("ap-get-technology", "Grant a technology, used by the Archipelago Client.", function(call) diff --git a/worlds/hk/Items.py b/worlds/hk/Items.py index 8515465826a5..a2b7c06d62a6 100644 --- a/worlds/hk/Items.py +++ b/worlds/hk/Items.py @@ -61,6 +61,7 @@ class HKItemData(NamedTuple): "VesselFragments": lookup_type_to_names["Vessel"], "WhisperingRoots": lookup_type_to_names["Root"], "WhiteFragments": {"Queen_Fragment", "King_Fragment", "Void_Heart"}, + "DreamNails": {"Dream_Nail", "Dream_Gate", "Awoken_Dream_Nail"}, }) item_name_groups['Horizontal'] = item_name_groups['Cloak'] | item_name_groups['CDash'] item_name_groups['Vertical'] = item_name_groups['Claw'] | {'Monarch_Wings'}