Jak and Daxter: Post-merge Polish (#5031)

- Cleans up a few missed references in the setup guide.
- Refactors Options class to use metaclass and decorators to enforce friendly limits on multiple levels.    
  - Templates generated from the website, even ones with `random` should not fail generation because the website will only allow values inside the friendly limits. 
  - _Uploaded_ yamls to the website with `random`, should also now respect friendly limits without the need for `random-range` shenanigans.
  - _Uploaded_ yamls to the website, or yamls that are used to generate locally, that have hard-defined values outside the friendly limits, will be clamped/dragged/massaged into those limits (with logged warnings).
- Removed an early completion goal that was playing havoc with fill. Not enough people seem to use this goal, so its loss will not be mourned.
This commit is contained in:
massimilianodelliubaldini
2025-05-30 10:31:00 -04:00
committed by GitHub
parent b0f41c0360
commit d19bf98dc4
7 changed files with 253 additions and 113 deletions

View File

@@ -34,9 +34,9 @@ from .locations import (JakAndDaxterLocation,
cache_location_table,
orb_location_table)
from .regions import create_regions
from .rules import (enforce_multiplayer_limits,
enforce_singleplayer_limits,
verify_orb_trade_amounts,
from .rules import (enforce_mp_absolute_limits,
enforce_mp_friendly_limits,
enforce_sp_limits,
set_orb_trade_rule)
from .locs import (cell_locations as cells,
scout_locations as scouts,
@@ -258,18 +258,31 @@ class JakAndDaxterWorld(World):
self.options.mountain_pass_cell_count.value = self.power_cell_thresholds[1]
self.options.lava_tube_cell_count.value = self.power_cell_thresholds[2]
# Store this for remove function.
self.power_cell_thresholds_minus_one = [x - 1 for x in self.power_cell_thresholds]
# For the fairness of other players in a multiworld game, enforce some friendly limitations on our options,
# so we don't cause chaos during seed generation. These friendly limits should **guarantee** a successful gen.
# We would have done this earlier, but we needed to sort the power cell thresholds first.
# We would have done this earlier, but we needed to sort the power cell thresholds first. Don't worry, we'll
# come back to them.
enforce_friendly_options = self.settings.enforce_friendly_options
if enforce_friendly_options:
if self.multiworld.players > 1:
enforce_multiplayer_limits(self)
if self.multiworld.players == 1:
# For singleplayer games, always enforce/clamp the cell counts to valid values.
enforce_sp_limits(self)
else:
if enforce_friendly_options:
# For multiplayer games, we have a host setting to make options fair/sane for other players.
# If this setting is enabled, enforce/clamp some friendly limitations on our options.
enforce_mp_friendly_limits(self)
else:
enforce_singleplayer_limits(self)
# Even if the setting is disabled, some values must be clamped to avoid generation errors.
enforce_mp_absolute_limits(self)
# That's right, set the collection of thresholds again. Don't just clamp the values without updating this list!
self.power_cell_thresholds = [
self.options.fire_canyon_cell_count.value,
self.options.mountain_pass_cell_count.value,
self.options.lava_tube_cell_count.value,
100, # The 100 Power Cell Door.
]
# Now that the threshold list is finalized, store this for the remove function.
self.power_cell_thresholds_minus_one = [x - 1 for x in self.power_cell_thresholds]
# Calculate the number of power cells needed for full region access, the number being replaced by traps,
# and the number of remaining filler.
@@ -282,11 +295,6 @@ class JakAndDaxterWorld(World):
self.options.filler_power_cells_replaced_with_traps.value = self.total_trap_cells
self.total_filler_cells = non_prog_cells - self.total_trap_cells
# Verify that we didn't overload the trade amounts with more orbs than exist in the world.
# This is easy to do by accident even in a singleplayer world.
self.total_trade_orbs = (9 * self.options.citizen_orb_trade_amount) + (6 * self.options.oracle_orb_trade_amount)
verify_orb_trade_amounts(self)
# Cache the orb bundle size and item name for quicker reference.
if self.options.enable_orbsanity == options.EnableOrbsanity.option_per_level:
self.orb_bundle_size = self.options.level_orbsanity_bundle_size.value

View File

@@ -18,7 +18,7 @@
- [What do Traps do?](#what-do-traps-do)
- [What kind of Traps are there?](#what-kind-of-traps-are-there)
- [I got soft-locked and cannot leave, how do I get out of here?](#i-got-soft-locked-and-cannot-leave-how-do-i-get-out-of-here)
- [Why did I get an Option Error when generating a seed, and how do I fix it?](#why-did-i-get-an-option-error-when-generating-a-seed-and-how-do-i-fix-it)
- [How do I generate seeds with 1 Orb Orbsanity and other extreme options?](#how-do-i-generate-seeds-with-1-orb-orbsanity-and-other-extreme-options)
- [How do I check my player options in-game?](#how-do-i-check-my-player-options-in-game)
- [How does the HUD work?](#how-does-the-hud-work)
- [I think I found a bug, where should I report it?](#i-think-i-found-a-bug-where-should-i-report-it)
@@ -201,16 +201,19 @@ Open the game's menu, navigate to `Options`, then `Archipelago Options`, then `W
Selecting this option will ask if you want to be teleported to Geyser Rock. From there, you can teleport back
to the nearest sage's hut to continue your journey.
## Why did I get an Option Error when generating a seed and how do I fix it
## How do I generate seeds with 1 orb orbsanity and other extreme options?
Depending on your player YAML, Jak and Daxter can have a lot of items, which can sometimes be overwhelming or
disruptive to multiworld games. There are also options that are mutually incompatible with each other, even in a solo
game. To prevent the game from disrupting multiworlds, or generating an impossible solo seed, some options have
Singleplayer and Multiplayer Minimums and Maximums, collectively called "friendly limits."
"friendly limits" that prevent you from choosing more extreme values.
If you're generating a solo game, or your multiworld host agrees to your request, you can override those limits by
editing the `host.yaml`. In the Archipelago Launcher, click `Open host.yaml`, then search for `jakanddaxter_options`,
then search for `enforce_friendly_options`, then change this value from `true` to `false`. Disabling this allows for
more disruptive and challenging options, but it may cause seed generation to fail. **Use at your own risk!**
You can override **some**, not all, of those limits by editing the `host.yaml`. In the Archipelago Launcher, click
`Open host.yaml`, then search for `jakanddaxter_options`, then search for `enforce_friendly_options`, then change this
value from `true` to `false`. You can then generate a seed locally, and upload that to the Archipelago website to host
for you (or host it yourself).
**Remember:** disabling this setting allows for more disruptive and challenging options, but it may cause seed
generation to fail. **Use at your own risk!**
## How do I check my player options in-game
When you connect your text client to the Archipelago Server, the server will tell the game what options were chosen

View File

@@ -4,7 +4,6 @@
- A legally purchased copy of *Jak And Daxter: The Precursor Legacy.*
- [The OpenGOAL Launcher](https://opengoal.dev/)
- [The Jak and Daxter .APWORLD package](https://github.com/ArchipelaGOAL/Archipelago/releases)
At this time, this method of setup works on Windows only, but Linux support is a strong likelihood in the near future as OpenGOAL itself supports Linux.
@@ -75,7 +74,7 @@ If you are in the middle of an async game, and you do not want to update the mod
### New Game
- Run the Archipelago Launcher.
- From the right-most list, find and click `Jak and Daxter Client`.
- From the client list, find and click `Jak and Daxter Client`.
- 3 new windows should appear:
- The OpenGOAL compiler will launch and compile the game. They should take about 30 seconds to compile.
- You should hear a musical cue to indicate the compilation was a success. If you do not, see the Troubleshooting section.

View File

@@ -1,22 +1,78 @@
from dataclasses import dataclass
from functools import cached_property
from Options import PerGameCommonOptions, StartInventoryPool, Toggle, Choice, Range, DefaultOnToggle, OptionCounter
from Options import PerGameCommonOptions, StartInventoryPool, Toggle, Choice, Range, DefaultOnToggle, OptionCounter, \
AssembleOptions
from .items import trap_item_table
class StaticGetter:
def __init__(self, func):
self.fget = func
class readonly_classproperty:
"""This decorator is used for getting friendly or unfriendly range_end values for options like FireCanyonCellCount
and CitizenOrbTradeAmount. We only need to provide a getter as we will only be setting a single int to one of two
values."""
def __init__(self, getter):
self.getter = getter
def __get__(self, instance, owner):
return self.fget(owner)
return self.getter(owner)
@StaticGetter
@readonly_classproperty
def determine_range_end(cls) -> int:
from . import JakAndDaxterWorld
enforce_friendly_options = JakAndDaxterWorld.settings.enforce_friendly_options
return cls.friendly_maximum if enforce_friendly_options else cls.absolute_maximum
from . import JakAndDaxterWorld # Avoid circular imports.
friendly = JakAndDaxterWorld.settings.enforce_friendly_options
return cls.friendly_maximum if friendly else cls.absolute_maximum
class classproperty:
"""This decorator (?) is used for getting and setting friendly or unfriendly option values for the Orbsanity
options."""
def __init__(self, getter, setter):
self.getter = getter
self.setter = setter
def __get__(self, obj, value):
return self.getter(obj)
def __set__(self, obj, value):
self.setter(obj, value)
class AllowedChoiceMeta(AssembleOptions):
"""This metaclass overrides AssembleOptions and provides inheriting classes a way to filter out "disallowed" values
by way of implementing get_disallowed_options. This function is used by Jak and Daxter to check host.yaml settings
without circular imports or breaking the settings API."""
_name_lookup: dict[int, str]
_options: dict[str, int]
def __new__(mcs, name, bases, attrs):
ret = super().__new__(mcs, name, bases, attrs)
ret._name_lookup = attrs["name_lookup"]
ret._options = attrs["options"]
return ret
def set_name_lookup(cls, value : dict[int, str]):
cls._name_lookup = value
def get_name_lookup(cls) -> dict[int, str]:
cls._name_lookup = {k: v for k, v in cls._name_lookup.items() if k not in cls.get_disallowed_options()}
return cls._name_lookup
def set_options(cls, value: dict[str, int]):
cls._options = value
def get_options(cls) -> dict[str, int]:
cls._options = {k: v for k, v in cls._options.items() if v not in cls.get_disallowed_options()}
return cls._options
def get_disallowed_options(cls):
return {}
name_lookup = classproperty(get_name_lookup, set_name_lookup)
options = classproperty(get_options, set_options)
class AllowedChoice(Choice, metaclass=AllowedChoiceMeta):
pass
class EnableMoveRandomizer(Toggle):
@@ -44,12 +100,13 @@ class EnableOrbsanity(Choice):
default = 0
class GlobalOrbsanityBundleSize(Choice):
class GlobalOrbsanityBundleSize(AllowedChoice):
"""The orb bundle size for Global Orbsanity. This only applies if "Enable Orbsanity" is set to "Global."
There are 2000 orbs in the game, so your bundle size must be a factor of 2000.
Multiplayer Minimum: 10
Multiplayer Maximum: 200"""
This value is restricted to safe minimum and maximum values to ensure valid singleplayer games and
non-disruptive multiplayer games, but the host can remove this restriction by turning off enforce_friendly_options
in host.yaml."""
display_name = "Global Orbsanity Bundle Size"
option_1_orb = 1
option_2_orbs = 2
@@ -75,12 +132,33 @@ class GlobalOrbsanityBundleSize(Choice):
friendly_maximum = 200
default = 20
@classmethod
def get_disallowed_options(cls) -> set[int]:
try:
from . import JakAndDaxterWorld
if JakAndDaxterWorld.settings.enforce_friendly_options:
return {cls.option_1_orb,
cls.option_2_orbs,
cls.option_4_orbs,
cls.option_5_orbs,
cls.option_8_orbs,
cls.option_250_orbs,
cls.option_400_orbs,
cls.option_500_orbs,
cls.option_1000_orbs,
cls.option_2000_orbs}
except ImportError:
pass
return set()
class PerLevelOrbsanityBundleSize(Choice):
class PerLevelOrbsanityBundleSize(AllowedChoice):
"""The orb bundle size for Per Level Orbsanity. This only applies if "Enable Orbsanity" is set to "Per Level."
There are 50, 150, or 200 orbs per level, so your bundle size must be a factor of 50.
Multiplayer Minimum: 10"""
This value is restricted to safe minimum and maximum values to ensure valid singleplayer games and
non-disruptive multiplayer games, but the host can remove this restriction by turning off enforce_friendly_options
in host.yaml."""
display_name = "Per Level Orbsanity Bundle Size"
option_1_orb = 1
option_2_orbs = 2
@@ -91,6 +169,18 @@ class PerLevelOrbsanityBundleSize(Choice):
friendly_minimum = 10
default = 25
@classmethod
def get_disallowed_options(cls) -> set[int]:
try:
from . import JakAndDaxterWorld
if JakAndDaxterWorld.settings.enforce_friendly_options:
return {cls.option_1_orb,
cls.option_2_orbs,
cls.option_5_orbs}
except ImportError:
pass
return set()
class FireCanyonCellCount(Range):
"""The number of power cells you need to cross Fire Canyon. This value is restricted to a safe maximum value to
@@ -234,7 +324,7 @@ class CompletionCondition(Choice):
option_cross_fire_canyon = 69
option_cross_mountain_pass = 87
option_cross_lava_tube = 89
option_defeat_dark_eco_plant = 6
# option_defeat_dark_eco_plant = 6
option_defeat_klaww = 86
option_defeat_gol_and_maia = 112
option_open_100_cell_door = 116

View File

@@ -115,8 +115,8 @@ def create_regions(world: "JakAndDaxterWorld"):
elif options.jak_completion_condition == CompletionCondition.option_cross_lava_tube:
multiworld.completion_condition[player] = lambda state: state.can_reach(gmc, "Region", player)
elif options.jak_completion_condition == CompletionCondition.option_defeat_dark_eco_plant:
multiworld.completion_condition[player] = lambda state: state.can_reach(fjp, "Region", player)
# elif options.jak_completion_condition == CompletionCondition.option_defeat_dark_eco_plant:
# multiworld.completion_condition[player] = lambda state: state.can_reach(fjp, "Region", player)
elif options.jak_completion_condition == CompletionCondition.option_defeat_klaww:
multiworld.completion_condition[player] = lambda state: state.can_reach(mp, "Region", player)

View File

@@ -1,3 +1,5 @@
import logging
import math
import typing
from BaseClasses import CollectionState
from Options import OptionError
@@ -131,100 +133,138 @@ def can_fight(state: CollectionState, player: int) -> bool:
return state.has_any(("Jump Dive", "Jump Kick", "Punch", "Kick"), player)
def enforce_multiplayer_limits(world: "JakAndDaxterWorld"):
def clamp_cell_limits(world: "JakAndDaxterWorld") -> str:
options = world.options
friendly_message = ""
if (options.enable_orbsanity == EnableOrbsanity.option_global
and (options.global_orbsanity_bundle_size.value < GlobalOrbsanityBundleSize.friendly_minimum
or options.global_orbsanity_bundle_size.value > GlobalOrbsanityBundleSize.friendly_maximum)):
friendly_message += (f" "
f"{options.global_orbsanity_bundle_size.display_name} must be no less than "
f"{GlobalOrbsanityBundleSize.friendly_minimum} and no greater than "
f"{GlobalOrbsanityBundleSize.friendly_maximum} (currently "
f"{options.global_orbsanity_bundle_size.value}).\n")
if (options.enable_orbsanity == EnableOrbsanity.option_per_level
and options.level_orbsanity_bundle_size.value < PerLevelOrbsanityBundleSize.friendly_minimum):
friendly_message += (f" "
f"{options.level_orbsanity_bundle_size.display_name} must be no less than "
f"{PerLevelOrbsanityBundleSize.friendly_minimum} (currently "
f"{options.level_orbsanity_bundle_size.value}).\n")
if options.fire_canyon_cell_count.value > FireCanyonCellCount.friendly_maximum:
old_value = options.fire_canyon_cell_count.value
options.fire_canyon_cell_count.value = FireCanyonCellCount.friendly_maximum
friendly_message += (f" "
f"{options.fire_canyon_cell_count.display_name} must be no greater than "
f"{FireCanyonCellCount.friendly_maximum} (currently "
f"{options.fire_canyon_cell_count.value}).\n")
f"{FireCanyonCellCount.friendly_maximum} (was {old_value}), "
f"changed option to appropriate value.\n")
if options.mountain_pass_cell_count.value > MountainPassCellCount.friendly_maximum:
old_value = options.mountain_pass_cell_count.value
options.mountain_pass_cell_count.value = MountainPassCellCount.friendly_maximum
friendly_message += (f" "
f"{options.mountain_pass_cell_count.display_name} must be no greater than "
f"{MountainPassCellCount.friendly_maximum} (currently "
f"{options.mountain_pass_cell_count.value}).\n")
f"{MountainPassCellCount.friendly_maximum} (was {old_value}), "
f"changed option to appropriate value.\n")
if options.lava_tube_cell_count.value > LavaTubeCellCount.friendly_maximum:
old_value = options.lava_tube_cell_count.value
options.lava_tube_cell_count.value = LavaTubeCellCount.friendly_maximum
friendly_message += (f" "
f"{options.lava_tube_cell_count.display_name} must be no greater than "
f"{LavaTubeCellCount.friendly_maximum} (currently "
f"{options.lava_tube_cell_count.value}).\n")
f"{LavaTubeCellCount.friendly_maximum} (was {old_value}), "
f"changed option to appropriate value.\n")
return friendly_message
def clamp_trade_total_limits(world: "JakAndDaxterWorld"):
"""Check if we need to recalculate the 2 trade orb options so the total fits under 2000. If so let's keep them
proportional relative to each other. Then we'll recalculate total_trade_orbs. Remember this situation is
only possible if both values are greater than 0, otherwise the absolute maximums would keep them under 2000."""
options = world.options
friendly_message = ""
world.total_trade_orbs = (9 * options.citizen_orb_trade_amount) + (6 * options.oracle_orb_trade_amount)
if world.total_trade_orbs > 2000:
old_total = world.total_trade_orbs
old_citizen_value = options.citizen_orb_trade_amount.value
old_oracle_value = options.oracle_orb_trade_amount.value
coefficient = old_oracle_value / old_citizen_value
options.citizen_orb_trade_amount.value = math.floor(2000 / (9 + (6 * coefficient)))
options.oracle_orb_trade_amount.value = math.floor(coefficient * options.citizen_orb_trade_amount.value)
world.total_trade_orbs = (9 * options.citizen_orb_trade_amount) + (6 * options.oracle_orb_trade_amount)
friendly_message += (f" "
f"Required number of orbs ({old_total}) must be no greater than total orbs in the game "
f"(2000). Reduced the value of {world.options.citizen_orb_trade_amount.display_name} "
f"from {old_citizen_value} to {options.citizen_orb_trade_amount.value} and "
f"{world.options.oracle_orb_trade_amount.display_name} from {old_oracle_value} to "
f"{options.oracle_orb_trade_amount.value}.\n")
return friendly_message
def enforce_mp_friendly_limits(world: "JakAndDaxterWorld"):
options = world.options
friendly_message = ""
if options.enable_orbsanity == EnableOrbsanity.option_global:
if options.global_orbsanity_bundle_size.value < GlobalOrbsanityBundleSize.friendly_minimum:
old_value = options.global_orbsanity_bundle_size.value
options.global_orbsanity_bundle_size.value = GlobalOrbsanityBundleSize.friendly_minimum
friendly_message += (f" "
f"{options.global_orbsanity_bundle_size.display_name} must be no less than "
f"{GlobalOrbsanityBundleSize.friendly_minimum} (was {old_value}), "
f"changed option to appropriate value.\n")
if options.global_orbsanity_bundle_size.value > GlobalOrbsanityBundleSize.friendly_maximum:
old_value = options.global_orbsanity_bundle_size.value
options.global_orbsanity_bundle_size.value = GlobalOrbsanityBundleSize.friendly_maximum
friendly_message += (f" "
f"{options.global_orbsanity_bundle_size.display_name} must be no greater than "
f"{GlobalOrbsanityBundleSize.friendly_maximum} (was {old_value}), "
f"changed option to appropriate value.\n")
if options.enable_orbsanity == EnableOrbsanity.option_per_level:
if options.level_orbsanity_bundle_size.value < PerLevelOrbsanityBundleSize.friendly_minimum:
old_value = options.level_orbsanity_bundle_size.value
options.level_orbsanity_bundle_size.value = PerLevelOrbsanityBundleSize.friendly_minimum
friendly_message += (f" "
f"{options.level_orbsanity_bundle_size.display_name} must be no less than "
f"{PerLevelOrbsanityBundleSize.friendly_minimum} (was {old_value}), "
f"changed option to appropriate value.\n")
if options.citizen_orb_trade_amount.value > CitizenOrbTradeAmount.friendly_maximum:
old_value = options.citizen_orb_trade_amount.value
options.citizen_orb_trade_amount.value = CitizenOrbTradeAmount.friendly_maximum
friendly_message += (f" "
f"{options.citizen_orb_trade_amount.display_name} must be no greater than "
f"{CitizenOrbTradeAmount.friendly_maximum} (currently "
f"{options.citizen_orb_trade_amount.value}).\n")
f"{CitizenOrbTradeAmount.friendly_maximum} (was {old_value}), "
f"changed option to appropriate value.\n")
if options.oracle_orb_trade_amount.value > OracleOrbTradeAmount.friendly_maximum:
old_value = options.oracle_orb_trade_amount.value
options.oracle_orb_trade_amount.value = OracleOrbTradeAmount.friendly_maximum
friendly_message += (f" "
f"{options.oracle_orb_trade_amount.display_name} must be no greater than "
f"{OracleOrbTradeAmount.friendly_maximum} (currently "
f"{options.oracle_orb_trade_amount.value}).\n")
f"{OracleOrbTradeAmount.friendly_maximum} (was {old_value}), "
f"changed option to appropriate value.\n")
friendly_message += clamp_cell_limits(world)
friendly_message += clamp_trade_total_limits(world)
if friendly_message != "":
raise OptionError(f"{world.player_name}: The options you have chosen may disrupt the multiworld. \n"
f"Please adjust the following Options for a multiplayer game. \n"
f"{friendly_message}"
f"Or use 'random-range-x-y' instead of 'random' in your player yaml.\n"
f"Or set 'enforce_friendly_options' in the seed generator's host.yaml to false. "
f"(Use at your own risk!)")
logging.warning(f"{world.player_name}: Your options have been modified to avoid disrupting the multiworld.\n"
f"{friendly_message}"
f"You can access more advanced options by setting 'enforce_friendly_options' in the seed "
f"generator's host.yaml to false and generating locally. (Use at your own risk!)")
def enforce_singleplayer_limits(world: "JakAndDaxterWorld"):
options = world.options
def enforce_mp_absolute_limits(world: "JakAndDaxterWorld"):
friendly_message = ""
if options.fire_canyon_cell_count.value > FireCanyonCellCount.friendly_maximum:
friendly_message += (f" "
f"{options.fire_canyon_cell_count.display_name} must be no greater than "
f"{FireCanyonCellCount.friendly_maximum} (currently "
f"{options.fire_canyon_cell_count.value}).\n")
if options.mountain_pass_cell_count.value > MountainPassCellCount.friendly_maximum:
friendly_message += (f" "
f"{options.mountain_pass_cell_count.display_name} must be no greater than "
f"{MountainPassCellCount.friendly_maximum} (currently "
f"{options.mountain_pass_cell_count.value}).\n")
if options.lava_tube_cell_count.value > LavaTubeCellCount.friendly_maximum:
friendly_message += (f" "
f"{options.lava_tube_cell_count.display_name} must be no greater than "
f"{LavaTubeCellCount.friendly_maximum} (currently "
f"{options.lava_tube_cell_count.value}).\n")
friendly_message += clamp_trade_total_limits(world)
if friendly_message != "":
raise OptionError(f"The options you have chosen may result in seed generation failures. \n"
f"Please adjust the following Options for a singleplayer game. \n"
f"{friendly_message}"
f"Or use 'random-range-x-y' instead of 'random' in your player yaml.\n"
f"Or set 'enforce_friendly_options' in your host.yaml to false. "
f"(Use at your own risk!)")
logging.warning(f"{world.player_name}: Your options have been modified to avoid seed generation failures.\n"
f"{friendly_message}")
def verify_orb_trade_amounts(world: "JakAndDaxterWorld"):
def enforce_sp_limits(world: "JakAndDaxterWorld"):
friendly_message = ""
if world.total_trade_orbs > 2000:
raise OptionError(f"{world.player_name}: Required number of orbs for all trades ({world.total_trade_orbs}) "
f"is more than all the orbs in the game (2000). Reduce the value of either "
f"{world.options.citizen_orb_trade_amount.display_name} "
f"or {world.options.oracle_orb_trade_amount.display_name}.")
friendly_message += clamp_cell_limits(world)
friendly_message += clamp_trade_total_limits(world)
if friendly_message != "":
logging.warning(f"{world.player_name}: Your options have been modified to avoid seed generation failures.\n"
f"{friendly_message}")

View File

@@ -4,14 +4,14 @@ from .bases import JakAndDaxterTestBase
class TradesCostNothingTest(JakAndDaxterTestBase):
options = {
"enable_orbsanity": 2,
"global_orbsanity_bundle_size": 5,
"global_orbsanity_bundle_size": 10,
"citizen_orb_trade_amount": 0,
"oracle_orb_trade_amount": 0
}
def test_orb_items_are_filler(self):
self.collect_all_but("")
self.assertNotIn("5 Precursor Orbs", self.multiworld.state.prog_items)
self.assertNotIn("10 Precursor Orbs", self.multiworld.state.prog_items)
def test_trades_are_accessible(self):
self.assertTrue(self.multiworld
@@ -22,15 +22,15 @@ class TradesCostNothingTest(JakAndDaxterTestBase):
class TradesCostEverythingTest(JakAndDaxterTestBase):
options = {
"enable_orbsanity": 2,
"global_orbsanity_bundle_size": 5,
"global_orbsanity_bundle_size": 10,
"citizen_orb_trade_amount": 120,
"oracle_orb_trade_amount": 150
}
def test_orb_items_are_progression(self):
self.collect_all_but("")
self.assertIn("5 Precursor Orbs", self.multiworld.state.prog_items[self.player])
self.assertEqual(396, self.multiworld.state.prog_items[self.player]["5 Precursor Orbs"])
self.assertIn("10 Precursor Orbs", self.multiworld.state.prog_items[self.player])
self.assertEqual(198, self.multiworld.state.prog_items[self.player]["10 Precursor Orbs"])
def test_trades_are_accessible(self):
self.collect_all_but("")