TUNIC: Grass Randomizer (#3913)

* Fix certain items not being added to slot data

* Change where items get added to slot data

* Add initial grass randomizer stuff

* Fix rules

* Update grass.py

Improve location names

* Remove wand and gun from logic

* Update __init__.py

* Fix logic for two pieces of grass in atoll

* Make early bushes only contain grass

* Backport changes to grass rando (#20)

* Backport changes to grass rando

* add_rule instead of set_rule for the special cases, add special cases for back of swamp laurels area cause I should've made a new region for the swamp upper entrance

* Remove item name group for grass

* Update grass rando option descriptions

- Also ignore grass fill for single player games

* Ignore grass fill option for solo rando

* Update er_rules.py

* Fix pre fill issue

* Remove duplicate option

* Add excluded grass locations back

* Hide grass fill option from simple ui options page

* Check for start with sword before setting grass rules

* Update worlds/tunic/options.py

Co-authored-by: Scipio Wright <scipiowright@gmail.com>

* Exclude grass from get_filler_item_name

- non-grass rando games were accidentally seeing grass items get shuffled in as filler, which is funny but probably shouldn't happen

* Update worlds/tunic/__init__.py

Co-authored-by: Scipio Wright <scipiowright@gmail.com>

* Apply suggestions from code review

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
Co-authored-by: Scipio Wright <scipiowright@gmail.com>

* change the rest of grass_fill to local_fill

* Filter out grass from filler_items

* remove -> discard

* Update worlds/tunic/__init__.py

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

* change has_stick to has_melee

* Update grass list with combat logic regions

* More fixes from combat logic merge

* Fix some dumb stuff (#21)

* Reorganize pre fill for grass

* Update option value passthrough

* Update __init__.py

* Fix region name

* Make separate pools for the grass and non-grass fills (#22)

* Make separate pools for the grass and non-grass fills

* Update worlds/tunic/__init__.py

Co-authored-by: Scipio Wright <scipiowright@gmail.com>

* Fix those things in the PR (#23)

* Use excludable property

Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>

---------

Co-authored-by: Scipio Wright <scipiowright@gmail.com>
Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
This commit is contained in:
Silent 2025-01-15 18:17:07 -05:00 committed by GitHub
parent 9dac7d9cc3
commit b7baaed391
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 8156 additions and 28 deletions

View File

@ -1,19 +1,20 @@
from typing import Dict, List, Any, Tuple, TypedDict, ClassVar, Union
from typing import Dict, List, Any, Tuple, TypedDict, ClassVar, Union, Set
from logging import warning
from BaseClasses import Region, Location, Item, Tutorial, ItemClassification, MultiWorld, CollectionState
from .items import (item_name_to_id, item_table, item_name_groups, fool_tiers, filler_items, slot_data_item_names,
combat_items)
from .locations import location_table, location_name_groups, location_name_to_id, hexagon_locations
from .locations import location_table, location_name_groups, standard_location_name_to_id, hexagon_locations, sphere_one
from .rules import set_location_rules, set_region_rules, randomize_ability_unlocks, gold_hexagon
from .er_rules import set_er_location_rules
from .regions import tunic_regions
from .er_scripts import create_er_regions
from .grass import grass_location_table, grass_location_name_to_id, grass_location_name_groups, excluded_grass_locations
from .er_data import portal_mapping, RegionInfo, tunic_er_regions
from .options import (TunicOptions, EntranceRando, tunic_option_groups, tunic_option_presets, TunicPlandoConnections,
LaurelsLocation, LogicRules, LaurelsZips, IceGrappling, LadderStorage)
from .combat_logic import area_data, CombatState
from worlds.AutoWorld import WebWorld, World
from Options import PlandoConnection
from Options import PlandoConnection, OptionError
from decimal import Decimal, ROUND_HALF_UP
from settings import Group, Bool
@ -22,7 +23,11 @@ class TunicSettings(Group):
class DisableLocalSpoiler(Bool):
"""Disallows the TUNIC client from creating a local spoiler log."""
class LimitGrassRando(Bool):
"""Limits the impact of Grass Randomizer on the multiworld by disallowing local_fill percentages below 95."""
disable_local_spoiler: Union[DisableLocalSpoiler, bool] = False
limit_grass_rando: Union[LimitGrassRando, bool] = True
class TunicWeb(WebWorld):
@ -73,10 +78,13 @@ class TunicWorld(World):
settings: ClassVar[TunicSettings]
item_name_groups = item_name_groups
location_name_groups = location_name_groups
location_name_groups.update(grass_location_name_groups)
item_name_to_id = item_name_to_id
location_name_to_id = location_name_to_id
location_name_to_id = standard_location_name_to_id.copy()
location_name_to_id.update(grass_location_name_to_id)
player_location_table: Dict[str, int]
ability_unlocks: Dict[str, int]
slot_data_items: List[TunicItem]
tunic_portal_pairs: Dict[str, str]
@ -85,6 +93,11 @@ class TunicWorld(World):
shop_num: int = 1 # need to make it so that you can walk out of shops, but also that they aren't all connected
er_regions: Dict[str, RegionInfo] # absolutely needed so outlet regions work
# for the local_fill option
fill_items: List[TunicItem]
fill_locations: List[TunicLocation]
amount_to_local_fill: int
# so we only loop the multiworld locations once
# if these are locations instead of their info, it gives a memory leak error
item_link_locations: Dict[int, Dict[str, List[Tuple[int, str]]]] = {}
@ -132,6 +145,7 @@ class TunicWorld(World):
self.options.hexagon_quest.value = self.passthrough["hexagon_quest"]
self.options.entrance_rando.value = self.passthrough["entrance_rando"]
self.options.shuffle_ladders.value = self.passthrough["shuffle_ladders"]
self.options.grass_randomizer.value = self.passthrough.get("grass_randomizer", 0)
self.options.fixed_shop.value = self.options.fixed_shop.option_false
self.options.laurels_location.value = self.options.laurels_location.option_anywhere
self.options.combat_logic.value = self.passthrough["combat_logic"]
@ -140,6 +154,22 @@ class TunicWorld(World):
else:
self.using_ut = False
self.player_location_table = standard_location_name_to_id.copy()
if self.options.local_fill == -1:
if self.options.grass_randomizer:
self.options.local_fill.value = 95
else:
self.options.local_fill.value = 0
if self.options.grass_randomizer:
if self.settings.limit_grass_rando and self.options.local_fill < 95 and self.multiworld.players > 1:
raise OptionError(f"TUNIC: Player {self.player_name} has their Local Fill option set too low. "
f"They must either bring it above 95% or the host needs to disable limit_grass_rando "
f"in their host.yaml settings")
self.player_location_table.update(grass_location_name_to_id)
@classmethod
def stage_generate_early(cls, multiworld: MultiWorld) -> None:
tunic_worlds: Tuple[TunicWorld] = multiworld.get_game_worlds("TUNIC")
@ -245,6 +275,14 @@ class TunicWorld(World):
self.get_location("Secret Gathering Place - 10 Fairy Reward").place_locked_item(laurels)
items_to_create["Hero's Laurels"] = 0
if self.options.grass_randomizer:
items_to_create["Grass"] = len(grass_location_table)
tunic_items.append(self.create_item("Glass Cannon", ItemClassification.progression))
items_to_create["Glass Cannon"] = 0
for grass_location in excluded_grass_locations:
self.get_location(grass_location).place_locked_item(self.create_item("Grass"))
items_to_create["Grass"] -= len(excluded_grass_locations)
if self.options.keys_behind_bosses:
for rgb_hexagon, location in hexagon_locations.items():
hex_item = self.create_item(gold_hexagon if self.options.hexagon_quest else rgb_hexagon)
@ -332,8 +370,73 @@ class TunicWorld(World):
if tunic_item.name in slot_data_item_names:
self.slot_data_items.append(tunic_item)
# pull out the filler so that we can place it manually during pre_fill
self.fill_items = []
if self.options.local_fill > 0 and self.multiworld.players > 1:
# skip items marked local or non-local, let fill deal with them in its own way
# discard grass from non_local if it's meant to be limited
if self.settings.limit_grass_rando:
self.options.non_local_items.value.discard("Grass")
all_filler: List[TunicItem] = []
non_filler: List[TunicItem] = []
for tunic_item in tunic_items:
if (tunic_item.excludable
and tunic_item.name not in self.options.local_items
and tunic_item.name not in self.options.non_local_items):
all_filler.append(tunic_item)
else:
non_filler.append(tunic_item)
self.amount_to_local_fill = int(self.options.local_fill.value * len(all_filler) / 100)
self.fill_items += all_filler[:self.amount_to_local_fill]
del all_filler[:self.amount_to_local_fill]
tunic_items = all_filler + non_filler
self.multiworld.itempool += tunic_items
def pre_fill(self) -> None:
self.fill_locations = []
if self.options.local_fill > 0 and self.multiworld.players > 1:
# we need to reserve a couple locations so that we don't fill up every sphere 1 location
reserved_locations: Set[str] = set(self.random.sample(sphere_one, 2))
viable_locations = [loc for loc in self.multiworld.get_unfilled_locations(self.player)
if loc.name not in reserved_locations
and loc.name not in self.options.priority_locations.value]
if len(viable_locations) < self.amount_to_local_fill:
raise OptionError(f"TUNIC: Not enough locations for local_fill option for {self.player_name}. "
f"This is likely due to excess plando or priority locations.")
self.fill_locations += viable_locations
@classmethod
def stage_pre_fill(cls, multiworld: MultiWorld) -> None:
tunic_fill_worlds: List[TunicWorld] = [world for world in multiworld.get_game_worlds("TUNIC")
if world.options.local_fill.value > 0]
if tunic_fill_worlds:
grass_fill: List[TunicItem] = []
non_grass_fill: List[TunicItem] = []
grass_fill_locations: List[Location] = []
non_grass_fill_locations: List[Location] = []
for world in tunic_fill_worlds:
if world.options.grass_randomizer:
grass_fill.extend(world.fill_items)
grass_fill_locations.extend(world.fill_locations)
else:
non_grass_fill.extend(world.fill_items)
non_grass_fill_locations.extend(world.fill_locations)
multiworld.random.shuffle(grass_fill)
multiworld.random.shuffle(non_grass_fill)
multiworld.random.shuffle(grass_fill_locations)
multiworld.random.shuffle(non_grass_fill_locations)
for filler_item in grass_fill:
multiworld.push_item(grass_fill_locations.pop(), filler_item, collect=False)
for filler_item in non_grass_fill:
multiworld.push_item(non_grass_fill_locations.pop(), filler_item, collect=False)
def create_regions(self) -> None:
self.tunic_portal_pairs = {}
self.er_portal_hints = {}
@ -346,7 +449,8 @@ class TunicWorld(World):
self.ability_unlocks["Pages 52-53 (Icebolt)"] = self.passthrough["Hexagon Quest Icebolt"]
# Ladders and Combat Logic uses ER rules with vanilla connections for easier maintenance
if self.options.entrance_rando or self.options.shuffle_ladders or self.options.combat_logic:
if (self.options.entrance_rando or self.options.shuffle_ladders or self.options.combat_logic
or self.options.grass_randomizer):
portal_pairs = create_er_regions(self)
if self.options.entrance_rando:
# these get interpreted by the game to tell it which entrances to connect
@ -362,7 +466,7 @@ class TunicWorld(World):
region = self.get_region(region_name)
region.add_exits(exits)
for location_name, location_id in self.location_name_to_id.items():
for location_name, location_id in self.player_location_table.items():
region = self.get_region(location_table[location_name].region)
location = TunicLocation(self.player, location_name, location_id, region)
region.locations.append(location)
@ -375,7 +479,8 @@ class TunicWorld(World):
def set_rules(self) -> None:
# same reason as in create_regions, could probably be put into create_regions
if self.options.entrance_rando or self.options.shuffle_ladders or self.options.combat_logic:
if (self.options.entrance_rando or self.options.shuffle_ladders or self.options.combat_logic
or self.options.grass_randomizer):
set_er_location_rules(self)
else:
set_region_rules(self)
@ -463,6 +568,7 @@ class TunicWorld(World):
"maskless": self.options.maskless.value,
"entrance_rando": int(bool(self.options.entrance_rando.value)),
"shuffle_ladders": self.options.shuffle_ladders.value,
"grass_randomizer": self.options.grass_randomizer.value,
"combat_logic": self.options.combat_logic.value,
"Hexagon Quest Prayer": self.ability_unlocks["Pages 24-25 (Prayer)"],
"Hexagon Quest Holy Cross": self.ability_unlocks["Pages 42-43 (Holy Cross)"],

View File

@ -629,14 +629,16 @@ tunic_er_regions: Dict[str, RegionInfo] = {
"Beneath the Well Back": RegionInfo("Sewer"), # the back two portals, and all 4 upper chests
"West Garden before Terry": RegionInfo("Archipelagos Redux"), # the lower entry point, near hero grave
"West Garden after Terry": RegionInfo("Archipelagos Redux"), # after Terry, up until next chompignons
"West Garden West Combat": RegionInfo("Archipelagos Redux"), # for grass rando basically
"West Garden at Dagger House": RegionInfo("Archipelagos Redux"), # just outside magic dagger house
"West Garden South Checkpoint": RegionInfo("Archipelagos Redux"),
"West Garden South Checkpoint": RegionInfo("Archipelagos Redux"), # the checkpoint and the blue lines area
"Magic Dagger House": RegionInfo("archipelagos_house", dead_end=DeadEnd.all_cats),
"West Garden Portal": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted, outlet_region="West Garden by Portal"),
"West Garden Portal": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted,
outlet_region="West Garden by Portal"),
"West Garden by Portal": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
"West Garden Portal Item": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
"West Garden Laurels Exit Region": RegionInfo("Archipelagos Redux"),
"West Garden before Boss": RegionInfo("Archipelagos Redux"), # main west garden
"West Garden before Boss": RegionInfo("Archipelagos Redux"), # up the ladder before garden knight
"West Garden after Boss": RegionInfo("Archipelagos Redux"),
"West Garden Hero's Grave Region": RegionInfo("Archipelagos Redux", outlet_region="West Garden before Terry"),
"Ruined Atoll": RegionInfo("Atoll Redux"),
@ -1165,8 +1167,10 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
"West Garden after Terry": {
"West Garden before Terry":
[],
"West Garden South Checkpoint":
"West Garden West Combat":
[],
"West Garden South Checkpoint":
[["Hyperdash"]],
"West Garden Laurels Exit Region":
[["LS1"]],
},
@ -1176,6 +1180,8 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
"West Garden at Dagger House":
[],
"West Garden after Terry":
[["Hyperdash"]],
"West Garden West Combat":
[],
},
"West Garden before Boss": {

View File

@ -1,12 +1,13 @@
from typing import Dict, FrozenSet, Tuple, TYPE_CHECKING
from worlds.generic.Rules import set_rule, add_rule, forbid_item
from BaseClasses import Region, CollectionState
from .options import IceGrappling, LadderStorage, CombatLogic
from .rules import (has_ability, has_sword, has_melee, has_ice_grapple_logic, has_lantern, has_mask, can_ladder_storage,
laurels_zip, bomb_walls)
from .er_data import Portal, get_portal_outlet_region
from .ladder_storage_data import ow_ladder_groups, region_ladders, easy_ls, medium_ls, hard_ls
from .combat_logic import has_combat_reqs
from BaseClasses import Region, CollectionState
from .grass import set_grass_location_rules
if TYPE_CHECKING:
from . import TunicWorld
@ -555,7 +556,6 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
regions["Dark Tomb Upper"].connect(
connecting_region=regions["Dark Tomb Entry Point"])
# ice grapple through the wall, get the little secret sound to trigger
regions["Dark Tomb Upper"].connect(
connecting_region=regions["Dark Tomb Main"],
rule=lambda state: has_ladder("Ladder in Dark Tomb", state, world)
@ -577,11 +577,24 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
wg_after_to_before_terry = regions["West Garden after Terry"].connect(
connecting_region=regions["West Garden before Terry"])
regions["West Garden after Terry"].connect(
connecting_region=regions["West Garden South Checkpoint"])
wg_checkpoint_to_after_terry = regions["West Garden South Checkpoint"].connect(
wg_after_terry_to_west_combat = regions["West Garden after Terry"].connect(
connecting_region=regions["West Garden West Combat"])
regions["West Garden West Combat"].connect(
connecting_region=regions["West Garden after Terry"])
wg_checkpoint_to_west_combat = regions["West Garden South Checkpoint"].connect(
connecting_region=regions["West Garden West Combat"])
regions["West Garden West Combat"].connect(
connecting_region=regions["West Garden South Checkpoint"])
# if not laurels, it goes through the west combat region instead
regions["West Garden after Terry"].connect(
connecting_region=regions["West Garden South Checkpoint"],
rule=lambda state: state.has(laurels, player))
regions["West Garden South Checkpoint"].connect(
connecting_region=regions["West Garden after Terry"],
rule=lambda state: state.has(laurels, player))
wg_checkpoint_to_dagger = regions["West Garden South Checkpoint"].connect(
connecting_region=regions["West Garden at Dagger House"])
regions["West Garden at Dagger House"].connect(
@ -1402,11 +1415,15 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
set_rule(wg_after_to_before_terry,
lambda state: state.has_any({laurels, ice_dagger}, player)
or has_combat_reqs("West Garden", state, player))
# laurels through, probably to the checkpoint, or just fight
set_rule(wg_checkpoint_to_after_terry,
lambda state: state.has(laurels, player) or has_combat_reqs("West Garden", state, player))
set_rule(wg_checkpoint_to_before_boss,
set_rule(wg_after_terry_to_west_combat,
lambda state: has_combat_reqs("West Garden", state, player))
set_rule(wg_checkpoint_to_west_combat,
lambda state: has_combat_reqs("West Garden", state, player))
# maybe a little too generous? probably fine though
set_rule(wg_checkpoint_to_before_boss,
lambda state: state.has(laurels, player) or has_combat_reqs("West Garden", state, player))
add_rule(btv_front_to_main,
lambda state: has_combat_reqs("Beneath the Vault", state, player))
@ -1528,6 +1545,9 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
def set_er_location_rules(world: "TunicWorld") -> None:
player = world.player
if world.options.grass_randomizer:
set_grass_location_rules(world)
forbid_item(world.get_location("Secret Gathering Place - 20 Fairy Reward"), fairies, player)
# Ability Shuffle Exclusive Rules
@ -1852,6 +1872,8 @@ def set_er_location_rules(world: "TunicWorld") -> None:
combat_logic_to_loc("West Garden - [Central Lowlands] Chest Beneath Faeries", "West Garden")
combat_logic_to_loc("West Garden - [Central Lowlands] Chest Beneath Save Point", "West Garden")
combat_logic_to_loc("West Garden - [West Highlands] Upper Left Walkway", "West Garden")
combat_logic_to_loc("West Garden - [Central Highlands] Holy Cross (Blue Lines)", "West Garden")
combat_logic_to_loc("West Garden - [Central Highlands] Behind Guard Captain", "West Garden")
# with combat logic on, I presume the player will want to be able to see to avoid the spiders
set_rule(world.get_location("Beneath the Fortress - Bridge"),

View File

@ -1,6 +1,6 @@
from typing import Dict, List, Set, Tuple, TYPE_CHECKING
from BaseClasses import Region, ItemClassification, Item, Location
from .locations import location_table
from .locations import all_locations
from .er_data import Portal, portal_mapping, traversal_requirements, DeadEnd, RegionInfo
from .er_rules import set_er_region_rules
from Options import PlandoConnection
@ -53,8 +53,8 @@ def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]:
set_er_region_rules(world, regions, portal_pairs)
for location_name, location_id in world.location_name_to_id.items():
region = regions[location_table[location_name].er_region]
for location_name, location_id in world.player_location_table.items():
region = regions[all_locations[location_name].er_region]
location = TunicERLocation(world.player, location_name, location_id, region)
region.locations.append(location)

7944
worlds/tunic/grass.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -166,6 +166,7 @@ item_table: Dict[str, TunicItemData] = {
"Ladders in Library": TunicItemData(IC.progression, 0, 148, "Ladders"),
"Ladders in Lower Quarry": TunicItemData(IC.progression, 0, 149, "Ladders"),
"Ladders in Swamp": TunicItemData(IC.progression, 0, 150, "Ladders"),
"Grass": TunicItemData(IC.filler, 0, 151),
}
# items to be replaced by fool traps
@ -214,7 +215,7 @@ combat_items.extend(["Stick", "Sword", "Sword Upgrade", "Magic Wand", "Hero's La
item_name_to_id: Dict[str, int] = {name: item_base_id + data.item_id_offset for name, data in item_table.items()}
filler_items: List[str] = [name for name, data in item_table.items() if data.classification == IC.filler]
filler_items: List[str] = [name for name, data in item_table.items() if data.classification == IC.filler and name != "Grass"]
def get_item_group(item_name: str) -> str:

View File

@ -1,4 +1,5 @@
from typing import Dict, NamedTuple, Set, Optional
from typing import Dict, NamedTuple, Set, Optional, List
from .grass import grass_location_table
class TunicLocationData(NamedTuple):
@ -320,7 +321,27 @@ hexagon_locations: Dict[str, str] = {
"Blue Questagon": "Rooted Ziggurat Lower - Hexagon Blue",
}
location_name_to_id: Dict[str, int] = {name: location_base_id + index for index, name in enumerate(location_table)}
sphere_one: List[str] = [
"Overworld - [Central] Chest Across From Well",
"Overworld - [Northwest] Chest Near Quarry Gate",
"Overworld - [Northwest] Shadowy Corner Chest",
"Overworld - [Southwest] Chest Guarded By Turret",
"Overworld - [Southwest] South Chest Near Guard",
"Overworld - [Southwest] Obscured in Tunnel to Beach",
"Overworld - [Northwest] Chest Near Turret",
"Overworld - [Northwest] Page By Well",
"Overworld - [West] Chest Behind Moss Wall",
"Overworld - [Southwest] Key Pickup",
"Overworld - [West] Key Pickup",
"Overworld - [West] Obscured Behind Windmill",
"Overworld - [West] Obscured Near Well",
"Overworld - [West] Page On Teleporter"
]
standard_location_name_to_id: Dict[str, int] = {name: location_base_id + index for index, name in enumerate(location_table)}
all_locations = location_table.copy()
all_locations.update(grass_location_table)
location_name_groups: Dict[str, Set[str]] = {}
for loc_name, loc_data in location_table.items():

View File

@ -1,7 +1,7 @@
from dataclasses import dataclass
from typing import Dict, Any
from Options import (DefaultOnToggle, Toggle, StartInventoryPool, Choice, Range, TextChoice, PlandoConnections,
PerGameCommonOptions, OptionGroup, Visibility)
PerGameCommonOptions, OptionGroup, Visibility, NamedRange)
from .er_data import portal_mapping
@ -154,6 +154,33 @@ class ShuffleLadders(Toggle):
display_name = "Shuffle Ladders"
class GrassRandomizer(Toggle):
"""
Turns over 6,000 blades of grass and bushes in the game into checks.
"""
internal_name = "grass_randomizer"
display_name = "Grass Randomizer"
class LocalFill(NamedRange):
"""
Choose the percentage of your filler/trap items that will be kept local or distributed to other TUNIC players with this option enabled.
If you have Grass Randomizer enabled, this option must be set to 95% or higher to avoid flooding the item pool. The host can remove this restriction by turning off the limit_grass_rando setting in host.yaml.
This option defaults to 95% if you have Grass Randomizer enabled, and to 0% otherwise.
This option ignores items placed in your local_items or non_local_items.
This option does nothing in single player games.
"""
internal_name = "local_fill"
display_name = "Local Fill Percent"
range_start = 0
range_end = 100
special_range_names = {
"default": -1
}
default = -1
visibility = Visibility.template | Visibility.complex_ui | Visibility.spoiler
class TunicPlandoConnections(PlandoConnections):
"""
Generic connection plando. Format is:
@ -278,12 +305,13 @@ class TunicOptions(PerGameCommonOptions):
combat_logic: CombatLogic
lanternless: Lanternless
maskless: Maskless
grass_randomizer: GrassRandomizer
local_fill: LocalFill
laurels_zips: LaurelsZips
ice_grappling: IceGrappling
ladder_storage: LadderStorage
ladder_storage_without_items: LadderStorageWithoutItems
plando_connections: TunicPlandoConnections
logic_rules: LogicRules