531 lines
24 KiB
Python
531 lines
24 KiB
Python
import math
|
|
from typing import Dict
|
|
|
|
from BaseClasses import CollectionState, Entrance, Item, ItemClassification, Location, Region, Tutorial
|
|
|
|
from worlds.AutoWorld import WebWorld, World
|
|
|
|
from .Items import YachtDiceItem, item_groups, item_table
|
|
from .Locations import YachtDiceLocation, all_locations, ini_locations
|
|
from .Options import (
|
|
AddExtraPoints,
|
|
AddStoryChapters,
|
|
GameDifficulty,
|
|
MinimalNumberOfDiceAndRolls,
|
|
MinimizeExtraItems,
|
|
PointsSize,
|
|
YachtDiceOptions,
|
|
yd_option_groups,
|
|
)
|
|
from .Rules import dice_simulation_fill_pool, set_yacht_completion_rules, set_yacht_rules
|
|
|
|
|
|
class YachtDiceWeb(WebWorld):
|
|
tutorials = [
|
|
Tutorial(
|
|
"Multiworld Setup Guide",
|
|
"A guide to setting up Yacht Dice. This guide covers single-player, multiworld, and website.",
|
|
"English",
|
|
"setup_en.md",
|
|
"setup/en",
|
|
["Spineraks"],
|
|
)
|
|
]
|
|
|
|
option_groups = yd_option_groups
|
|
|
|
|
|
class YachtDiceWorld(World):
|
|
"""
|
|
Yacht Dice is a straightforward game, custom-made for Archipelago,
|
|
where you cast your dice to chart a course for high scores,
|
|
unlocking valuable treasures along the way.
|
|
Discover more dice, extra rolls, multipliers,
|
|
and unlockable categories to navigate the depths of the game.
|
|
Roll your way to victory by reaching the target score!
|
|
"""
|
|
|
|
game: str = "Yacht Dice"
|
|
options_dataclass = YachtDiceOptions
|
|
|
|
web = YachtDiceWeb()
|
|
|
|
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
|
|
|
location_name_to_id = {name: data.id for name, data in all_locations.items()}
|
|
|
|
item_name_groups = item_groups
|
|
|
|
ap_world_version = "2.1.4"
|
|
|
|
def _get_yachtdice_data(self):
|
|
return {
|
|
# "world_seed": self.multiworld.per_slot_randoms[self.player].getrandbits(32),
|
|
"seed_name": self.multiworld.seed_name,
|
|
"player_name": self.multiworld.get_player_name(self.player),
|
|
"player_id": self.player,
|
|
"race": self.multiworld.is_race,
|
|
}
|
|
|
|
def generate_early(self):
|
|
"""
|
|
In generate early, we fill the item-pool, then determine the number of locations, and add filler items.
|
|
"""
|
|
self.itempool = []
|
|
self.precollected = []
|
|
|
|
# number of dice and rolls in the pull
|
|
opt_dice_and_rolls = self.options.minimal_number_of_dice_and_rolls
|
|
|
|
if opt_dice_and_rolls == MinimalNumberOfDiceAndRolls.option_5_dice_and_3_rolls:
|
|
num_of_dice = 5
|
|
num_of_rolls = 3
|
|
elif opt_dice_and_rolls == MinimalNumberOfDiceAndRolls.option_5_dice_and_5_rolls:
|
|
num_of_dice = 5
|
|
num_of_rolls = 5
|
|
elif opt_dice_and_rolls == MinimalNumberOfDiceAndRolls.option_6_dice_and_4_rolls:
|
|
num_of_dice = 6
|
|
num_of_rolls = 4
|
|
elif opt_dice_and_rolls == MinimalNumberOfDiceAndRolls.option_7_dice_and_3_rolls:
|
|
num_of_dice = 7
|
|
num_of_rolls = 3
|
|
elif opt_dice_and_rolls == MinimalNumberOfDiceAndRolls.option_8_dice_and_2_rolls:
|
|
num_of_dice = 8
|
|
num_of_rolls = 2
|
|
else:
|
|
raise Exception(f"[Yacht Dice] Unknown MinimalNumberOfDiceAndRolls options {opt_dice_and_rolls}")
|
|
|
|
# amount of dice and roll fragments needed to get a dice or roll
|
|
self.frags_per_dice = self.options.number_of_dice_fragments_per_dice.value
|
|
self.frags_per_roll = self.options.number_of_roll_fragments_per_roll.value
|
|
|
|
if self.options.minimize_extra_items == MinimizeExtraItems.option_yes_please:
|
|
self.frags_per_dice = min(self.frags_per_dice, 2)
|
|
self.frags_per_roll = min(self.frags_per_roll, 2)
|
|
|
|
# set difficulty
|
|
diff_value = self.options.game_difficulty
|
|
if diff_value == GameDifficulty.option_easy:
|
|
self.difficulty = 1
|
|
elif diff_value == GameDifficulty.option_medium:
|
|
self.difficulty = 2
|
|
elif diff_value == GameDifficulty.option_hard:
|
|
self.difficulty = 3
|
|
elif diff_value == GameDifficulty.option_extreme:
|
|
self.difficulty = 4
|
|
else:
|
|
raise Exception(f"[Yacht Dice] Unknown GameDifficulty options {diff_value}")
|
|
|
|
# Create a list with the specified number of 1s
|
|
num_ones = self.options.alternative_categories.value
|
|
categorylist = [1] * num_ones + [0] * (16 - num_ones)
|
|
|
|
# Shuffle the list to randomize the order
|
|
self.random.shuffle(categorylist)
|
|
|
|
# A list of all possible categories.
|
|
# Every entry in the list has two categories, one 'default' category and one 'alt'.
|
|
# You get either of the two for every entry, so a total of 16 unique categories.
|
|
all_categories = [
|
|
["Category Choice", "Category Double Threes and Fours"],
|
|
["Category Inverse Choice", "Category Quadruple Ones and Twos"],
|
|
["Category Ones", "Category Distincts"],
|
|
["Category Twos", "Category Two times Ones"],
|
|
["Category Threes", "Category Half of Sixes"],
|
|
["Category Fours", "Category Twos and Threes"],
|
|
["Category Fives", "Category Sum of Odds"],
|
|
["Category Sixes", "Category Sum of Evens"],
|
|
["Category Pair", "Category Micro Straight"],
|
|
["Category Three of a Kind", "Category Three Odds"],
|
|
["Category Four of a Kind", "Category 1-2-1 Consecutive"],
|
|
["Category Tiny Straight", "Category Three Distinct Dice"],
|
|
["Category Small Straight", "Category Two Pair"],
|
|
["Category Large Straight", "Category 2-1-2 Consecutive"],
|
|
["Category Full House", "Category Five Distinct Dice"],
|
|
["Category Yacht", "Category 4&5 Full House"],
|
|
]
|
|
|
|
# categories used in this game.
|
|
self.possible_categories = []
|
|
|
|
for index, cats in enumerate(all_categories):
|
|
self.possible_categories.append(cats[categorylist[index]])
|
|
|
|
# Add Choice and Inverse choice (or their alts) to the precollected list.
|
|
if index == 0 or index == 1:
|
|
self.precollected.append(cats[categorylist[index]])
|
|
else:
|
|
self.itempool.append(cats[categorylist[index]])
|
|
|
|
# Also start with one Roll and one Dice
|
|
self.precollected.append("Dice")
|
|
num_of_dice_to_add = num_of_dice - 1
|
|
self.precollected.append("Roll")
|
|
num_of_rolls_to_add = num_of_rolls - 1
|
|
|
|
self.skip_early_locations = False
|
|
if self.options.minimize_extra_items == MinimizeExtraItems.option_yes_please:
|
|
self.precollected.append("Dice")
|
|
num_of_dice_to_add -= 1
|
|
self.precollected.append("Roll")
|
|
num_of_rolls_to_add -= 1
|
|
self.skip_early_locations = True
|
|
|
|
if num_of_dice_to_add > 0:
|
|
self.itempool.append("Dice")
|
|
num_of_dice_to_add -= 1
|
|
if num_of_rolls_to_add > 0:
|
|
self.itempool.append("Roll")
|
|
num_of_rolls_to_add -= 1
|
|
|
|
# if one fragment per dice, just add "Dice" objects
|
|
if num_of_dice_to_add > 0:
|
|
if self.frags_per_dice == 1:
|
|
self.itempool += ["Dice"] * num_of_dice_to_add # minus one because one is in start inventory
|
|
else:
|
|
self.itempool += ["Dice Fragment"] * (self.frags_per_dice * num_of_dice_to_add)
|
|
|
|
# if one fragment per roll, just add "Roll" objects
|
|
if num_of_rolls_to_add > 0:
|
|
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 += ["Roll Fragment"] * (self.frags_per_roll * num_of_rolls_to_add)
|
|
|
|
already_items = len(self.itempool)
|
|
|
|
# Yacht Dice needs extra filler items so it doesn't get stuck in generation.
|
|
# For now, we calculate the number of extra items we'll need later.
|
|
if self.options.minimize_extra_items == MinimizeExtraItems.option_yes_please:
|
|
extra_percentage = max(0.1, 0.8 - self.multiworld.players / 10)
|
|
elif self.options.minimize_extra_items == MinimizeExtraItems.option_no_dont:
|
|
extra_percentage = 0.72
|
|
else:
|
|
raise Exception(f"[Yacht Dice] Unknown MinimizeExtraItems options {self.options.minimize_extra_items}")
|
|
extra_locations_needed = max(10, math.ceil(already_items * extra_percentage))
|
|
|
|
# max score is the value of the last check. Goal score is the score needed to 'finish' the game
|
|
self.max_score = self.options.score_for_last_check.value
|
|
self.goal_score = min(self.max_score, self.options.score_for_goal.value)
|
|
|
|
# Yacht Dice adds items into the pool until a score of at least 1000 is reached.
|
|
# the yaml contains weights, which determine how likely it is that specific items get added.
|
|
# If all weights are 0, some of them will be made to be non-zero later.
|
|
weights: Dict[str, float] = {
|
|
"Dice": self.options.weight_of_dice.value,
|
|
"Roll": self.options.weight_of_roll.value,
|
|
"Fixed Score Multiplier": self.options.weight_of_fixed_score_multiplier.value,
|
|
"Step Score Multiplier": self.options.weight_of_step_score_multiplier.value,
|
|
"Double category": self.options.weight_of_double_category.value,
|
|
"Points": self.options.weight_of_points.value,
|
|
}
|
|
|
|
# if the player wants extra rolls or dice, fill the pool with fragments until close to an extra roll/dice
|
|
if weights["Dice"] > 0 and self.frags_per_dice > 1:
|
|
self.itempool += ["Dice Fragment"] * (self.frags_per_dice - 1)
|
|
if weights["Roll"] > 0 and self.frags_per_roll > 1:
|
|
self.itempool += ["Roll Fragment"] * (self.frags_per_roll - 1)
|
|
|
|
# calibrate the weights, since the impact of each of the items is different
|
|
weights["Dice"] = weights["Dice"] / 5 * self.frags_per_dice
|
|
weights["Roll"] = weights["Roll"] / 5 * self.frags_per_roll
|
|
|
|
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:
|
|
weights["Dice"] = 0 # don't allow >=9 dice
|
|
roll_fragments_in_pool = all_items.count("Roll") * self.frags_per_roll + all_items.count("Roll Fragment")
|
|
if roll_fragments_in_pool + 1 >= 6 * self.frags_per_roll:
|
|
weights["Roll"] = 0 # don't allow >= 6 rolls
|
|
|
|
# Don't allow too many extra points
|
|
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:
|
|
weights["Fixed Score Multiplier"] = 1
|
|
weights["Double category"] = 1
|
|
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
|
|
which_item_to_add = self.random.choices(list(weights.keys()), weights=list(weights.values()))[0]
|
|
|
|
if which_item_to_add == "Dice":
|
|
weights["Dice"] /= 1 + self.frags_per_dice
|
|
return "Dice" if self.frags_per_dice == 1 else "Dice Fragment"
|
|
elif which_item_to_add == "Roll":
|
|
weights["Roll"] /= 1 + self.frags_per_roll
|
|
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
|
|
return "Fixed Score Multiplier"
|
|
elif which_item_to_add == "Step Score Multiplier":
|
|
weights["Step Score Multiplier"] /= 1.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.
|
|
# Prefer to add choice or number categories, because the other categories are too "all or nothing",
|
|
# which often don't give any points, until you get overpowered, and then they give all points.
|
|
cat_weights = [2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1]
|
|
weights["Double category"] /= 1.1
|
|
return self.random.choices(self.possible_categories, weights=cat_weights)[0]
|
|
elif which_item_to_add == "Points":
|
|
score_dist = self.options.points_size
|
|
probs = {"1 Point": 1, "10 Points": 0, "100 Points": 0}
|
|
if score_dist == PointsSize.option_small:
|
|
probs = {"1 Point": 0.9, "10 Points": 0.1, "100 Points": 0}
|
|
elif score_dist == PointsSize.option_medium:
|
|
probs = {"1 Point": 0, "10 Points": 1, "100 Points": 0}
|
|
elif score_dist == PointsSize.option_large:
|
|
probs = {"1 Point": 0, "10 Points": 0.3, "100 Points": 0.7}
|
|
elif score_dist == PointsSize.option_mix:
|
|
probs = {"1 Point": 0.3, "10 Points": 0.4, "100 Points": 0.3}
|
|
else:
|
|
raise Exception(f"[Yacht Dice] Unknown PointsSize options {score_dist}")
|
|
choice = self.random.choices(list(probs.keys()), weights=list(probs.values()))[0]
|
|
if choice == "1 Point":
|
|
weights["Points"] /= 1.01
|
|
extra_points_added[0] += 1
|
|
return "1 Point"
|
|
elif choice == "10 Points":
|
|
weights["Points"] /= 1.1
|
|
extra_points_added[0] += 10
|
|
return "10 Points"
|
|
elif choice == "100 Points":
|
|
weights["Points"] /= 2
|
|
extra_points_added[0] += 100
|
|
return "100 Points"
|
|
else:
|
|
raise Exception("Unknown point value (Yacht Dice)")
|
|
else:
|
|
raise Exception(f"Invalid index when adding new items in Yacht Dice: {which_item_to_add}")
|
|
|
|
# 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, step_score_multipliers_added))
|
|
|
|
score_in_logic = dice_simulation_fill_pool(
|
|
self.itempool + self.precollected,
|
|
self.frags_per_dice,
|
|
self.frags_per_roll,
|
|
self.possible_categories,
|
|
self.difficulty,
|
|
self.player,
|
|
)
|
|
|
|
# if we overshoot, remove items until you get below 1000, then return the last removed item
|
|
if score_in_logic > 1000:
|
|
removed_item = ""
|
|
while score_in_logic > 1000:
|
|
removed_item = self.itempool.pop()
|
|
score_in_logic = dice_simulation_fill_pool(
|
|
self.itempool + self.precollected,
|
|
self.frags_per_dice,
|
|
self.frags_per_roll,
|
|
self.possible_categories,
|
|
self.difficulty,
|
|
self.player,
|
|
)
|
|
self.itempool.append(removed_item)
|
|
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, step_score_multipliers_added)
|
|
self.itempool.append(item_to_add)
|
|
if item_to_add == "1 Point":
|
|
score_in_logic += 1
|
|
elif item_to_add == "10 Points":
|
|
score_in_logic += 10
|
|
elif item_to_add == "100 Points":
|
|
score_in_logic += 100
|
|
else:
|
|
score_in_logic = dice_simulation_fill_pool(
|
|
self.itempool + self.precollected,
|
|
self.frags_per_dice,
|
|
self.frags_per_roll,
|
|
self.possible_categories,
|
|
self.difficulty,
|
|
self.player,
|
|
)
|
|
|
|
# count the number of locations in the game.
|
|
already_items = len(self.itempool) + 1 # +1 because of Victory item
|
|
|
|
# We need to add more filler/useful items if there are many items in the pool to guarantee successful generation
|
|
extra_locations_needed += (already_items - 45) // 15
|
|
self.number_of_locations = already_items + extra_locations_needed
|
|
|
|
# From here, we will count the number of items in the self.itempool, and add useful/filler items to the pool,
|
|
# making sure not to exceed the number of locations.
|
|
|
|
# first, we flood the entire pool with extra points (useful), if that setting is chosen.
|
|
if self.options.add_bonus_points == AddExtraPoints.option_all_of_it: # all of the extra points
|
|
already_items = len(self.itempool) + 1
|
|
self.itempool += ["Bonus Point"] * min(self.number_of_locations - already_items, 100)
|
|
|
|
# second, we flood the entire pool with story chapters (filler), if that setting is chosen.
|
|
if self.options.add_story_chapters == AddStoryChapters.option_all_of_it: # all of the story chapters
|
|
already_items = len(self.itempool) + 1
|
|
number_of_items = min(self.number_of_locations - already_items, 100)
|
|
number_of_items = (number_of_items // 10) * 10 # story chapters always come in multiples of 10
|
|
self.itempool += ["Story Chapter"] * number_of_items
|
|
|
|
# add some extra points (useful)
|
|
if self.options.add_bonus_points == AddExtraPoints.option_sure: # add extra points if wanted
|
|
already_items = len(self.itempool) + 1
|
|
self.itempool += ["Bonus Point"] * min(self.number_of_locations - already_items, 10)
|
|
|
|
# add some story chapters (filler)
|
|
if self.options.add_story_chapters == AddStoryChapters.option_sure: # add extra points if wanted
|
|
already_items = len(self.itempool) + 1
|
|
if self.number_of_locations - already_items >= 10:
|
|
self.itempool += ["Story Chapter"] * 10
|
|
|
|
# add some more extra points if there is still room
|
|
if self.options.add_bonus_points == AddExtraPoints.option_sure:
|
|
already_items = len(self.itempool) + 1
|
|
self.itempool += ["Bonus Point"] * min(self.number_of_locations - already_items, 10)
|
|
|
|
# add some encouragements filler-items if there is still room
|
|
already_items = len(self.itempool) + 1
|
|
self.itempool += ["Encouragement"] * min(self.number_of_locations - already_items, 5)
|
|
|
|
# add some fun facts filler-items if there is still room
|
|
already_items = len(self.itempool) + 1
|
|
self.itempool += ["Fun Fact"] * min(self.number_of_locations - already_items, 5)
|
|
|
|
# finally, add some "Good RNG" and "Bad RNG" items to complete the item pool
|
|
# these items are filler and do not do anything.
|
|
|
|
# probability of Good and Bad rng, based on difficulty for fun :)
|
|
|
|
p = 1.1 - 0.25 * self.difficulty
|
|
already_items = len(self.itempool) + 1
|
|
self.itempool += self.random.choices(
|
|
["Good RNG", "Bad RNG"], weights=[p, 1 - p], k=self.number_of_locations - already_items
|
|
)
|
|
|
|
# we are done adding items. Now because of the last step, number of items should be number of locations
|
|
already_items = len(self.itempool) + 1
|
|
if already_items != self.number_of_locations:
|
|
raise Exception(
|
|
f"[Yacht Dice] Number in self.itempool is not number of locations "
|
|
f"{already_items} {self.number_of_locations}."
|
|
)
|
|
|
|
# add precollected items using push_precollected. Items in self.itempool get created in create_items
|
|
for item in self.precollected:
|
|
self.multiworld.push_precollected(self.create_item(item))
|
|
|
|
# make sure one dice and one roll is early, so that you will have 2 dice and 2 rolls soon
|
|
self.multiworld.early_items[self.player]["Dice"] = 1
|
|
self.multiworld.early_items[self.player]["Roll"] = 1
|
|
|
|
def create_items(self):
|
|
self.multiworld.itempool += [self.create_item(name) for name in self.itempool]
|
|
|
|
def create_regions(self):
|
|
# call the ini_locations function, that generates locations based on the inputs.
|
|
location_table = ini_locations(
|
|
self.goal_score,
|
|
self.max_score,
|
|
self.number_of_locations,
|
|
self.difficulty,
|
|
self.skip_early_locations,
|
|
self.multiworld.players,
|
|
)
|
|
|
|
# simple menu-board construction
|
|
menu = Region("Menu", self.player, self.multiworld)
|
|
board = Region("Board", self.player, self.multiworld)
|
|
|
|
# add locations to board, one for every location in the location_table
|
|
board.locations = [
|
|
YachtDiceLocation(self.player, loc_name, loc_data.score, loc_data.id, board)
|
|
for loc_name, loc_data in location_table.items()
|
|
if loc_data.region == board.name
|
|
]
|
|
|
|
# Change the victory location to an event and place the Victory item there.
|
|
victory_location_name = f"{self.goal_score} score"
|
|
self.get_location(victory_location_name).address = None
|
|
self.get_location(victory_location_name).place_locked_item(
|
|
Item("Victory", ItemClassification.progression, None, self.player)
|
|
)
|
|
|
|
# add the regions
|
|
connection = Entrance(self.player, "New Board", menu)
|
|
menu.exits.append(connection)
|
|
connection.connect(board)
|
|
self.multiworld.regions += [menu, board]
|
|
|
|
def get_filler_item_name(self) -> str:
|
|
return "Good RNG"
|
|
|
|
def set_rules(self):
|
|
"""
|
|
set rules per location, and add the rule for beating the game
|
|
"""
|
|
set_yacht_rules(
|
|
self.multiworld,
|
|
self.player,
|
|
self.frags_per_dice,
|
|
self.frags_per_roll,
|
|
self.possible_categories,
|
|
self.difficulty,
|
|
)
|
|
set_yacht_completion_rules(self.multiworld, self.player)
|
|
|
|
def fill_slot_data(self):
|
|
"""
|
|
make slot data, which consists of yachtdice_data, options, and some other variables.
|
|
"""
|
|
yacht_dice_data = self._get_yachtdice_data()
|
|
yacht_dice_options = self.options.as_dict(
|
|
"game_difficulty",
|
|
"score_for_last_check",
|
|
"score_for_goal",
|
|
"number_of_dice_fragments_per_dice",
|
|
"number_of_roll_fragments_per_roll",
|
|
"which_story",
|
|
"allow_manual_input",
|
|
)
|
|
slot_data = {**yacht_dice_data, **yacht_dice_options} # combine the two
|
|
slot_data["number_of_dice_fragments_per_dice"] = self.frags_per_dice
|
|
slot_data["number_of_roll_fragments_per_roll"] = self.frags_per_roll
|
|
slot_data["goal_score"] = self.goal_score
|
|
slot_data["last_check_score"] = self.max_score
|
|
slot_data["allowed_categories"] = self.possible_categories
|
|
slot_data["ap_world_version"] = self.ap_world_version
|
|
return slot_data
|
|
|
|
def create_item(self, name: str) -> Item:
|
|
item_data = item_table[name]
|
|
item = YachtDiceItem(name, item_data.classification, item_data.code, self.player)
|
|
return item
|
|
|
|
# We overwrite these function to monitor when states have changed. See also dice_simulation in Rules.py
|
|
def collect(self, state: CollectionState, item: Item) -> bool:
|
|
change = super().collect(state, item)
|
|
if change:
|
|
state.prog_items[self.player]["state_is_fresh"] = 0
|
|
|
|
return change
|
|
|
|
def remove(self, state: CollectionState, item: Item) -> bool:
|
|
change = super().remove(state, item)
|
|
if change:
|
|
state.prog_items[self.player]["state_is_fresh"] = 0
|
|
|
|
return change
|