mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-10 01:23:48 -07:00
Compare commits
10 Commits
NewSoupVi-
...
NewSoupVi-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40cc4dd9f1 | ||
|
|
a40744e6db | ||
|
|
d802f9652a | ||
|
|
cbdb4d7ce3 | ||
|
|
691ce6a248 | ||
|
|
f9fc6944d3 | ||
|
|
e984583e5e | ||
|
|
7e03a87608 | ||
|
|
456bc481a3 | ||
|
|
b4752cd32d |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1 +1,2 @@
|
||||
worlds/blasphemous/region_data.py linguist-generated=true
|
||||
worlds/yachtdice/YachtWeights.py linguist-generated=true
|
||||
|
||||
@@ -77,4 +77,4 @@ There, you will find examples of games in the `worlds` folder:
|
||||
You may also find developer documentation in the `docs` folder:
|
||||
[/docs Folder in Archipelago Code](https://github.com/ArchipelagoMW/Archipelago/tree/main/docs).
|
||||
|
||||
If you have more questions, feel free to ask in the **#archipelago-dev** channel on our Discord.
|
||||
If you have more questions, feel free to ask in the **#ap-world-dev** channel on our Discord.
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
You have no generated any seeds yet!
|
||||
You have not generated any seeds yet!
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -118,9 +118,6 @@
|
||||
# Noita
|
||||
/worlds/noita/ @ScipioWright @heinermann
|
||||
|
||||
# Ocarina of Time
|
||||
/worlds/oot/ @espeon65536
|
||||
|
||||
# Old School Runescape
|
||||
/worlds/osrs @digiholic
|
||||
|
||||
@@ -230,6 +227,9 @@
|
||||
# Links Awakening DX
|
||||
# /worlds/ladx/
|
||||
|
||||
# Ocarina of Time
|
||||
# /worlds/oot/
|
||||
|
||||
## Disabled Unmaintained Worlds
|
||||
|
||||
# The following worlds in this repo are currently unmaintained and disabled as they do not work in core. If you are
|
||||
|
||||
@@ -24,7 +24,7 @@ display as `Value1` on the webhost.
|
||||
files, and both will resolve as `value1`. This should be used when changing options around, i.e. changing a Toggle to a
|
||||
Choice, and defining `alias_true = option_full`.
|
||||
- All options with a fixed set of possible values (i.e. those which inherit from `Toggle`, `(Text)Choice` or
|
||||
`(Named/Special)Range`) support `random` as a generic option. `random` chooses from any of the available values for that
|
||||
`(Named)Range`) support `random` as a generic option. `random` chooses from any of the available values for that
|
||||
option, and is reserved by AP. You can set this as your default value, but you cannot define your own `option_random`.
|
||||
However, you can override `from_text` and handle `text == "random"` to customize its behavior or
|
||||
implement it for additional option types.
|
||||
@@ -129,6 +129,23 @@ class Difficulty(Choice):
|
||||
default = 1
|
||||
```
|
||||
|
||||
### Option Visibility
|
||||
Every option has a Visibility IntFlag, defaulting to `all` (`0b1111`). This lets you choose where the option will be
|
||||
displayed. This only impacts where options are displayed, not how they can be used. Hidden options are still valid
|
||||
options in a yaml. The flags are as follows:
|
||||
* `none` (`0b0000`): This option is not shown anywhere
|
||||
* `template` (`0b0001`): This option shows up in template yamls
|
||||
* `simple_ui` (`0b0010`): This option shows up on the options page
|
||||
* `complex_ui` (`0b0100`): This option shows up on the advanced/weighted options page
|
||||
* `spoiler` (`0b1000`): This option shows up in spoiler logs
|
||||
|
||||
```python
|
||||
from Options import Choice, Visibility
|
||||
|
||||
class HiddenChoiceOption(Choice):
|
||||
visibility = Visibility.none
|
||||
```
|
||||
|
||||
### Option Groups
|
||||
Options may be categorized into groups for display on the WebHost. Option groups are displayed in the order specified
|
||||
by your world on the player-options and weighted-options pages. In the generated template files, there will be a comment
|
||||
|
||||
@@ -38,7 +38,7 @@ Recommended steps
|
||||
* Refer to [Windows Compilers on the python wiki](https://wiki.python.org/moin/WindowsCompilers) for details.
|
||||
Generally, selecting the box for "Desktop Development with C++" will provide what you need.
|
||||
* Build tools are not required if all modules are installed pre-compiled. Pre-compiled modules are pinned on
|
||||
[Discord in #archipelago-dev](https://discord.com/channels/731205301247803413/731214280439103580/905154456377757808)
|
||||
[Discord in #ap-core-dev](https://discord.com/channels/731205301247803413/731214280439103580/905154456377757808)
|
||||
|
||||
* It is recommended to use [PyCharm IDE](https://www.jetbrains.com/pycharm/)
|
||||
* Run Generate.py which will prompt installation of missing modules, press enter to confirm
|
||||
|
||||
@@ -26,8 +26,17 @@ Unless these are shared between multiple people, we expect the following from ea
|
||||
### Adding a World
|
||||
|
||||
When we merge your world into the core Archipelago repository, you automatically become world maintainer unless you
|
||||
nominate someone else (i.e. there are multiple devs). You can define who is allowed to approve changes to your world
|
||||
in the [CODEOWNERS](/docs/CODEOWNERS) document.
|
||||
nominate someone else (i.e. there are multiple devs).
|
||||
|
||||
### Being added as a maintainer to an existing implementation
|
||||
|
||||
At any point, a world maintainer can approve the addition of another maintainer to their world.
|
||||
In order to do this, either an existing maintainer or the new maintainer must open a PR updating the
|
||||
[CODEOWNERS](/docs/CODEOWNERS) file.
|
||||
This change must be approved by all existing maintainers of the affected world, the new maintainer candidate, and
|
||||
one core maintainer.
|
||||
To help the core team review the change, information about the new maintainer and their contributions should be
|
||||
included in the PR description.
|
||||
|
||||
### Getting Voted
|
||||
|
||||
@@ -35,7 +44,7 @@ When a world is unmaintained, the [core maintainers](https://github.com/orgs/Arc
|
||||
can vote for a new maintainer if there is a candidate.
|
||||
For a vote to pass, the majority of participating core maintainers must vote in the affirmative.
|
||||
The time limit is 1 week, but can end early if the majority is reached earlier.
|
||||
Voting shall be conducted on Discord in #archipelago-dev.
|
||||
Voting shall be conducted on Discord in #ap-core-dev.
|
||||
|
||||
## Dropping out
|
||||
|
||||
@@ -51,7 +60,7 @@ for example when they become unreachable.
|
||||
For a vote to pass, the majority of participating core maintainers must vote in the affirmative.
|
||||
The time limit is 2 weeks, but can end early if the majority is reached earlier AND the world maintainer was pinged and
|
||||
made their case or was pinged and has been unreachable for more than 2 weeks already.
|
||||
Voting shall be conducted on Discord in #archipelago-dev. Commits that are a direct result of the voting shall include
|
||||
Voting shall be conducted on Discord in #ap-core-dev. Commits that are a direct result of the voting shall include
|
||||
date, voting members and final result in the commit message.
|
||||
|
||||
## Handling of Unmaintained Worlds
|
||||
|
||||
@@ -601,11 +601,11 @@ class HKWorld(World):
|
||||
if change:
|
||||
for effect_name, effect_value in item_effects.get(item.name, {}).items():
|
||||
state.prog_items[item.player][effect_name] += effect_value
|
||||
if item.name in {"Left_Mothwing_Cloak", "Right_Mothwing_Cloak"}:
|
||||
if state.prog_items[item.player].get('RIGHTDASH', 0) and \
|
||||
state.prog_items[item.player].get('LEFTDASH', 0):
|
||||
(state.prog_items[item.player]["RIGHTDASH"], state.prog_items[item.player]["LEFTDASH"]) = \
|
||||
([max(state.prog_items[item.player]["RIGHTDASH"], state.prog_items[item.player]["LEFTDASH"])] * 2)
|
||||
if item.name in {"Left_Mothwing_Cloak", "Right_Mothwing_Cloak"}:
|
||||
if state.prog_items[item.player].get('RIGHTDASH', 0) and \
|
||||
state.prog_items[item.player].get('LEFTDASH', 0):
|
||||
(state.prog_items[item.player]["RIGHTDASH"], state.prog_items[item.player]["LEFTDASH"]) = \
|
||||
([max(state.prog_items[item.player]["RIGHTDASH"], state.prog_items[item.player]["LEFTDASH"])] * 2)
|
||||
return change
|
||||
|
||||
def remove(self, state, item: HKItem) -> bool:
|
||||
|
||||
@@ -204,11 +204,10 @@ class WitnessWorld(World):
|
||||
]
|
||||
if early_items:
|
||||
random_early_item = self.random.choice(early_items)
|
||||
if (
|
||||
self.options.puzzle_randomization == "sigma_expert"
|
||||
or self.options.victory_condition == "panel_hunt"
|
||||
):
|
||||
# In Expert and Panel Hunt, only tag the item as early, rather than forcing it onto the gate.
|
||||
mode = self.options.puzzle_randomization
|
||||
if mode == "sigma_expert" or mode == "umbra_variety" or self.options.victory_condition == "panel_hunt":
|
||||
# In Expert and Variety, only tag the item as early, rather than forcing it onto the gate.
|
||||
# Same with panel hunt, since the Tutorial Gate Open panel is used for something else
|
||||
self.multiworld.local_early_items[self.player][random_early_item] = 1
|
||||
else:
|
||||
# Force the item onto the tutorial gate check and remove it from our random pool.
|
||||
@@ -255,7 +254,7 @@ class WitnessWorld(World):
|
||||
self.get_region(region).add_locations({loc: self.location_name_to_id[loc]})
|
||||
|
||||
warning(
|
||||
f"""Location "{loc}" had to be added to {self.player_name}'s world
|
||||
f"""Location "{loc}" had to be added to {self.player_name}'s world
|
||||
due to insufficient sphere 1 size."""
|
||||
)
|
||||
|
||||
|
||||
1223
worlds/witness/data/WitnessLogicVariety.txt
Normal file
1223
worlds/witness/data/WitnessLogicVariety.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,22 @@ ITEM_GROUPS: Dict[str, Set[str]] = {}
|
||||
# item list during get_progression_items.
|
||||
_special_usefuls: List[str] = ["Puzzle Skip"]
|
||||
|
||||
ALWAYS_GOOD_SYMBOL_ITEMS: Set[str] = {"Dots", "Black/White Squares", "Symmetry", "Shapers", "Stars"}
|
||||
|
||||
MODE_SPECIFIC_GOOD_ITEMS: Dict[str, Set[str]] = {
|
||||
"none": set(),
|
||||
"sigma_normal": set(),
|
||||
"sigma_expert": {"Triangles"},
|
||||
"umbra_variety": {"Triangles"}
|
||||
}
|
||||
|
||||
MODE_SPECIFIC_GOOD_DISCARD_ITEMS: Dict[str, Set[str]] = {
|
||||
"none": {"Triangles"},
|
||||
"sigma_normal": {"Triangles"},
|
||||
"sigma_expert": {"Arrows"},
|
||||
"umbra_variety": set() # Variety Discards use both Arrows and Triangles, so neither of them are that useful alone
|
||||
}
|
||||
|
||||
|
||||
def populate_items() -> None:
|
||||
for item_name, definition in static_witness_logic.ALL_ITEMS.items():
|
||||
|
||||
@@ -17,6 +17,7 @@ from .utils import (
|
||||
get_items,
|
||||
get_sigma_expert_logic,
|
||||
get_sigma_normal_logic,
|
||||
get_umbra_variety_logic,
|
||||
get_vanilla_logic,
|
||||
logical_or_witness_rules,
|
||||
parse_lambda,
|
||||
@@ -292,6 +293,11 @@ def get_sigma_expert() -> StaticWitnessLogicObj:
|
||||
return StaticWitnessLogicObj(get_sigma_expert_logic())
|
||||
|
||||
|
||||
@cache_argsless
|
||||
def get_umbra_variety() -> StaticWitnessLogicObj:
|
||||
return StaticWitnessLogicObj(get_umbra_variety_logic())
|
||||
|
||||
|
||||
def __getattr__(name: str) -> StaticWitnessLogicObj:
|
||||
if name == "vanilla":
|
||||
return get_vanilla()
|
||||
@@ -299,6 +305,8 @@ def __getattr__(name: str) -> StaticWitnessLogicObj:
|
||||
return get_sigma_normal()
|
||||
if name == "sigma_expert":
|
||||
return get_sigma_expert()
|
||||
if name == "umbra_variety":
|
||||
return get_umbra_variety()
|
||||
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
||||
|
||||
|
||||
|
||||
@@ -215,6 +215,10 @@ def get_sigma_expert_logic() -> List[str]:
|
||||
return get_adjustment_file("WitnessLogicExpert.txt")
|
||||
|
||||
|
||||
def get_umbra_variety_logic() -> List[str]:
|
||||
return get_adjustment_file("WitnessLogicVariety.txt")
|
||||
|
||||
|
||||
def get_vanilla_logic() -> List[str]:
|
||||
return get_adjustment_file("WitnessLogicVanilla.txt")
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ class EntityHuntPicker:
|
||||
|
||||
remaining_entities, remaining_entity_weights = [], []
|
||||
for area, eligible_entities in self.ELIGIBLE_ENTITIES_PER_AREA.items():
|
||||
for panel in eligible_entities - self.HUNT_ENTITIES:
|
||||
for panel in sorted(eligible_entities - self.HUNT_ENTITIES):
|
||||
remaining_entities.append(panel)
|
||||
remaining_entity_weights.append(allowance_per_area[area])
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ def get_always_hint_items(world: "WitnessWorld") -> List[str]:
|
||||
wincon = world.options.victory_condition
|
||||
|
||||
if discards:
|
||||
if difficulty == "sigma_expert":
|
||||
if difficulty == "sigma_expert" or difficulty == "umbra_variety":
|
||||
always.append("Arrows")
|
||||
else:
|
||||
always.append("Triangles")
|
||||
|
||||
@@ -250,10 +250,15 @@ class PanelHuntDiscourageSameAreaFactor(Range):
|
||||
class PuzzleRandomization(Choice):
|
||||
"""
|
||||
Puzzles in this randomizer are randomly generated. This option changes the difficulty/types of puzzles.
|
||||
"Sigma Normal" randomizes puzzles close to their original mechanics and difficulty.
|
||||
"Sigma Expert" is an entirely new experience with extremely difficult random puzzles. Do not underestimate this mode, it is brutal.
|
||||
"Umbra Variety" focuses on unique symbol combinations not featured in the original game. It is harder than Sigma Normal, but easier than Sigma Expert.
|
||||
"None" means that the puzzles are unchanged from the original game.
|
||||
"""
|
||||
display_name = "Puzzle Randomization"
|
||||
option_sigma_normal = 0
|
||||
option_sigma_expert = 1
|
||||
option_umbra_variety = 3
|
||||
option_none = 2
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Dict, List, Set, cast
|
||||
from BaseClasses import Item, ItemClassification, MultiWorld
|
||||
|
||||
from .data import static_items as static_witness_items
|
||||
from .data import static_logic as static_witness_logic
|
||||
from .data.item_definition_classes import (
|
||||
DoorItemDefinition,
|
||||
ItemCategory,
|
||||
@@ -155,16 +154,12 @@ class WitnessPlayerItems:
|
||||
"""
|
||||
output: Set[str] = set()
|
||||
if self._world.options.shuffle_symbols:
|
||||
output = {"Dots", "Black/White Squares", "Symmetry", "Shapers", "Stars"}
|
||||
discards_on = self._world.options.shuffle_discarded_panels
|
||||
mode = self._world.options.puzzle_randomization.current_key
|
||||
|
||||
if self._world.options.shuffle_discarded_panels:
|
||||
if self._world.options.puzzle_randomization == "sigma_expert":
|
||||
output.add("Arrows")
|
||||
else:
|
||||
output.add("Triangles")
|
||||
|
||||
# Replace progressive items with their parents.
|
||||
output = {static_witness_logic.get_parent_progressive_item(item) for item in output}
|
||||
output = static_witness_items.ALWAYS_GOOD_SYMBOL_ITEMS | static_witness_items.MODE_SPECIFIC_GOOD_ITEMS[mode]
|
||||
if discards_on:
|
||||
output |= static_witness_items.MODE_SPECIFIC_GOOD_DISCARD_ITEMS[mode]
|
||||
|
||||
# Remove items that are mentioned in any plando options. (Hopefully, in the future, plando will get resolved
|
||||
# before create_items so that we'll be able to check placed items instead of just removing all items mentioned
|
||||
|
||||
@@ -87,12 +87,14 @@ class WitnessPlayerLogic:
|
||||
self.DIFFICULTY = world.options.puzzle_randomization
|
||||
|
||||
self.REFERENCE_LOGIC: StaticWitnessLogicObj
|
||||
if self.DIFFICULTY == "sigma_expert":
|
||||
if self.DIFFICULTY == "sigma_normal":
|
||||
self.REFERENCE_LOGIC = static_witness_logic.sigma_normal
|
||||
elif self.DIFFICULTY == "sigma_expert":
|
||||
self.REFERENCE_LOGIC = static_witness_logic.sigma_expert
|
||||
elif self.DIFFICULTY == "umbra_variety":
|
||||
self.REFERENCE_LOGIC = static_witness_logic.umbra_variety
|
||||
elif self.DIFFICULTY == "none":
|
||||
self.REFERENCE_LOGIC = static_witness_logic.vanilla
|
||||
else:
|
||||
self.REFERENCE_LOGIC = static_witness_logic.sigma_normal
|
||||
|
||||
self.CONNECTIONS_BY_REGION_NAME_THEORETICAL: Dict[str, Set[Tuple[str, WitnessRule]]] = copy.deepcopy(
|
||||
self.REFERENCE_LOGIC.STATIC_CONNECTIONS_BY_REGION_NAME
|
||||
|
||||
@@ -30,6 +30,8 @@ class WitnessPlayerRegions:
|
||||
self.reference_logic = static_witness_logic.sigma_normal
|
||||
elif difficulty == "sigma_expert":
|
||||
self.reference_logic = static_witness_logic.sigma_expert
|
||||
elif difficulty == "umbra_variety":
|
||||
self.reference_logic = static_witness_logic.umbra_variety
|
||||
else:
|
||||
self.reference_logic = static_witness_logic.vanilla
|
||||
|
||||
|
||||
@@ -96,6 +96,39 @@ class TestSymbolsRequiredToWinElevatorVanilla(WitnessTestBase):
|
||||
self.assert_can_beat_with_minimally(exact_requirement)
|
||||
|
||||
|
||||
class TestSymbolsRequiredToWinElevatorVariety(WitnessTestBase):
|
||||
options = {
|
||||
"shuffle_lasers": True,
|
||||
"mountain_lasers": 1,
|
||||
"victory_condition": "elevator",
|
||||
"early_symbol_item": False,
|
||||
"puzzle_randomization": "umbra_variety",
|
||||
}
|
||||
|
||||
def test_symbols_to_win(self) -> None:
|
||||
"""
|
||||
In symbol shuffle, the only way to reach the Elevator is through Mountain Entry by descending the Mountain.
|
||||
This requires a very specific set of symbol items per puzzle randomization mode.
|
||||
In this case, we check Variety Puzzles.
|
||||
"""
|
||||
|
||||
exact_requirement = {
|
||||
"Monastery Laser": 1,
|
||||
"Progressive Dots": 2,
|
||||
"Progressive Stars": 2,
|
||||
"Progressive Symmetry": 1,
|
||||
"Black/White Squares": 1,
|
||||
"Colored Squares": 1,
|
||||
"Shapers": 1,
|
||||
"Rotated Shapers": 1,
|
||||
"Eraser": 1,
|
||||
"Triangles": 1,
|
||||
"Arrows": 1,
|
||||
}
|
||||
|
||||
self.assert_can_beat_with_minimally(exact_requirement)
|
||||
|
||||
|
||||
class TestPanelsRequiredToWinElevator(WitnessTestBase):
|
||||
options = {
|
||||
"shuffle_lasers": True,
|
||||
|
||||
@@ -54,6 +54,7 @@ class TestMaxEntityShuffle(WitnessTestBase):
|
||||
|
||||
class TestPostgameGroupedDoors(WitnessTestBase):
|
||||
options = {
|
||||
"puzzle_randomization": "umbra_variety",
|
||||
"shuffle_postgame": True,
|
||||
"shuffle_discarded_panels": True,
|
||||
"shuffle_doors": "doors",
|
||||
|
||||
@@ -46,6 +46,9 @@ class TestSymbolRequirementsMultiworld(WitnessMultiworldTestBase):
|
||||
{
|
||||
"puzzle_randomization": "none",
|
||||
},
|
||||
{
|
||||
"puzzle_randomization": "umbra_variety",
|
||||
}
|
||||
]
|
||||
|
||||
common_options = {
|
||||
@@ -63,12 +66,15 @@ class TestSymbolRequirementsMultiworld(WitnessMultiworldTestBase):
|
||||
self.assertFalse(self.get_items_by_name("Arrows", 1))
|
||||
self.assertTrue(self.get_items_by_name("Arrows", 2))
|
||||
self.assertFalse(self.get_items_by_name("Arrows", 3))
|
||||
self.assertTrue(self.get_items_by_name("Arrows", 4))
|
||||
|
||||
with self.subTest("Test that Discards ask for Triangles in normal, but Arrows in expert."):
|
||||
desert_discard = "0x17CE7"
|
||||
triangles = frozenset({frozenset({"Triangles"})})
|
||||
arrows = frozenset({frozenset({"Arrows"})})
|
||||
both = frozenset({frozenset({"Triangles", "Arrows"})})
|
||||
|
||||
self.assertEqual(self.multiworld.worlds[1].player_logic.REQUIREMENTS_BY_HEX[desert_discard], triangles)
|
||||
self.assertEqual(self.multiworld.worlds[2].player_logic.REQUIREMENTS_BY_HEX[desert_discard], arrows)
|
||||
self.assertEqual(self.multiworld.worlds[3].player_logic.REQUIREMENTS_BY_HEX[desert_discard], triangles)
|
||||
self.assertEqual(self.multiworld.worlds[4].player_logic.REQUIREMENTS_BY_HEX[desert_discard], both)
|
||||
|
||||
@@ -29,7 +29,7 @@ class Category:
|
||||
mean_score = 0
|
||||
for key, value in yacht_weights[self.name, min(8, num_dice), min(8, num_rolls)].items():
|
||||
mean_score += key * value / 100000
|
||||
return mean_score * self.quantity
|
||||
return mean_score
|
||||
|
||||
|
||||
class ListState:
|
||||
|
||||
3040
worlds/yachtdice/YachtWeights.py
generated
3040
worlds/yachtdice/YachtWeights.py
generated
File diff suppressed because it is too large
Load Diff
@@ -56,7 +56,7 @@ class YachtDiceWorld(World):
|
||||
|
||||
item_name_groups = item_groups
|
||||
|
||||
ap_world_version = "2.1.1"
|
||||
ap_world_version = "2.1.2"
|
||||
|
||||
def _get_yachtdice_data(self):
|
||||
return {
|
||||
@@ -190,7 +190,6 @@ class YachtDiceWorld(World):
|
||||
if self.frags_per_roll == 1:
|
||||
self.itempool += ["Roll"] * num_of_rolls_to_add # minus one because one is in start inventory
|
||||
else:
|
||||
self.itempool.append("Roll") # always add a full roll to make generation easier (will be early)
|
||||
self.itempool += ["Roll Fragment"] * (self.frags_per_roll * num_of_rolls_to_add)
|
||||
|
||||
already_items = len(self.itempool)
|
||||
@@ -231,13 +230,10 @@ class YachtDiceWorld(World):
|
||||
weights["Dice"] = weights["Dice"] / 5 * self.frags_per_dice
|
||||
weights["Roll"] = weights["Roll"] / 5 * self.frags_per_roll
|
||||
|
||||
extra_points_added = 0
|
||||
multipliers_added = 0
|
||||
items_added = 0
|
||||
|
||||
def get_item_to_add(weights, extra_points_added, multipliers_added, items_added):
|
||||
items_added += 1
|
||||
extra_points_added = [0] # make it a mutible type so we can change the value in the function
|
||||
step_score_multipliers_added = [0]
|
||||
|
||||
def get_item_to_add(weights, extra_points_added, step_score_multipliers_added):
|
||||
all_items = self.itempool + self.precollected
|
||||
dice_fragments_in_pool = all_items.count("Dice") * self.frags_per_dice + all_items.count("Dice Fragment")
|
||||
if dice_fragments_in_pool + 1 >= 9 * self.frags_per_dice:
|
||||
@@ -246,21 +242,18 @@ class YachtDiceWorld(World):
|
||||
if roll_fragments_in_pool + 1 >= 6 * self.frags_per_roll:
|
||||
weights["Roll"] = 0 # don't allow >= 6 rolls
|
||||
|
||||
# Don't allow too many multipliers
|
||||
if multipliers_added > 50:
|
||||
weights["Fixed Score Multiplier"] = 0
|
||||
weights["Step Score Multiplier"] = 0
|
||||
|
||||
# Don't allow too many extra points
|
||||
if extra_points_added > 300:
|
||||
if extra_points_added[0] > 400:
|
||||
weights["Points"] = 0
|
||||
|
||||
if step_score_multipliers_added[0] > 10:
|
||||
weights["Step Score Multiplier"] = 0
|
||||
|
||||
# if all weights are zero, allow to add fixed score multiplier, double category, points.
|
||||
if sum(weights.values()) == 0:
|
||||
if multipliers_added <= 50:
|
||||
weights["Fixed Score Multiplier"] = 1
|
||||
weights["Fixed Score Multiplier"] = 1
|
||||
weights["Double category"] = 1
|
||||
if extra_points_added <= 300:
|
||||
if extra_points_added[0] <= 400:
|
||||
weights["Points"] = 1
|
||||
|
||||
# Next, add the appropriate item. We'll slightly alter weights to avoid too many of the same item
|
||||
@@ -274,11 +267,10 @@ class YachtDiceWorld(World):
|
||||
return "Roll" if self.frags_per_roll == 1 else "Roll Fragment"
|
||||
elif which_item_to_add == "Fixed Score Multiplier":
|
||||
weights["Fixed Score Multiplier"] /= 1.05
|
||||
multipliers_added += 1
|
||||
return "Fixed Score Multiplier"
|
||||
elif which_item_to_add == "Step Score Multiplier":
|
||||
weights["Step Score Multiplier"] /= 1.1
|
||||
multipliers_added += 1
|
||||
step_score_multipliers_added[0] += 1
|
||||
return "Step Score Multiplier"
|
||||
elif which_item_to_add == "Double category":
|
||||
# Below entries are the weights to add each category.
|
||||
@@ -303,15 +295,15 @@ class YachtDiceWorld(World):
|
||||
choice = self.random.choices(list(probs.keys()), weights=list(probs.values()))[0]
|
||||
if choice == "1 Point":
|
||||
weights["Points"] /= 1.01
|
||||
extra_points_added += 1
|
||||
extra_points_added[0] += 1
|
||||
return "1 Point"
|
||||
elif choice == "10 Points":
|
||||
weights["Points"] /= 1.1
|
||||
extra_points_added += 10
|
||||
extra_points_added[0] += 10
|
||||
return "10 Points"
|
||||
elif choice == "100 Points":
|
||||
weights["Points"] /= 2
|
||||
extra_points_added += 100
|
||||
extra_points_added[0] += 100
|
||||
return "100 Points"
|
||||
else:
|
||||
raise Exception("Unknown point value (Yacht Dice)")
|
||||
@@ -320,7 +312,7 @@ class YachtDiceWorld(World):
|
||||
|
||||
# adding 17 items as a start seems like the smartest way to get close to 1000 points
|
||||
for _ in range(17):
|
||||
self.itempool.append(get_item_to_add(weights, extra_points_added, multipliers_added, items_added))
|
||||
self.itempool.append(get_item_to_add(weights, extra_points_added, step_score_multipliers_added))
|
||||
|
||||
score_in_logic = dice_simulation_fill_pool(
|
||||
self.itempool + self.precollected,
|
||||
@@ -348,7 +340,7 @@ class YachtDiceWorld(World):
|
||||
else:
|
||||
# Keep adding items until a score of 1000 is in logic
|
||||
while score_in_logic < 1000:
|
||||
item_to_add = get_item_to_add(weights, extra_points_added, multipliers_added, items_added)
|
||||
item_to_add = get_item_to_add(weights, extra_points_added, step_score_multipliers_added)
|
||||
self.itempool.append(item_to_add)
|
||||
if item_to_add == "1 Point":
|
||||
score_in_logic += 1
|
||||
|
||||
Reference in New Issue
Block a user