Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LADX: tarins gift improvement #3970

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3c68aa3
add groups and a preset
threeandthreee Sep 12, 2024
7120f25
formatting
threeandthreee Sep 13, 2024
48d11a8
pull zig's tarin's gift improvements
threeandthreee Sep 16, 2024
19caa73
typing
threeandthreee Sep 17, 2024
501427e
alias groups for progressive items
threeandthreee Sep 17, 2024
137be4b
change tarins gift option a bit
threeandthreee Sep 17, 2024
667fc12
add bush breakers item group
threeandthreee Sep 27, 2024
e40eca8
Merge branch 'ladx/groups-and-presets' into ladx/tarins-gift-improvement
threeandthreee Sep 27, 2024
6effdd5
fix typo
threeandthreee Sep 27, 2024
80418f6
bush_breaker option, respect non_local_items
threeandthreee Sep 27, 2024
a6e0920
Merge branch 'main' into ladx/groups-and-presets
threeandthreee Sep 27, 2024
18eda4e
Merge branch 'ladx/groups-and-presets' into ladx/tarins-gift-improvement
threeandthreee Sep 27, 2024
89e45fb
review suggestions
threeandthreee Oct 5, 2024
502c1e7
Merge remote-tracking branch 'upstream/main' into ladx/tarins-gift-im…
threeandthreee Dec 5, 2024
da9c3cd
Merge remote-tracking branch 'upstream/main' into ladx/tarins-gift-im…
threeandthreee Dec 20, 2024
aed1884
Merge remote-tracking branch 'upstream/main' into ladx/tarins-gift-im…
threeandthreee Dec 20, 2024
0ab176b
cleaner
threeandthreee Dec 20, 2024
b585cea
Update worlds/ladx/__init__.py
threeandthreee Jan 16, 2025
292531c
Merge remote-tracking branch 'upstream/main' into ladx/tarins-gift-im…
threeandthreee Jan 16, 2025
9d392d0
Merge remote-tracking branch 'upstream/main' into ladx/tarins-gift-im…
threeandthreee Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions worlds/ladx/LADXR/locations/startItem.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,12 @@


class StartItem(DroppedKey):
# We need to give something here that we can use to progress.
# FEATHER
OPTIONS = [SWORD, SHIELD, POWER_BRACELET, OCARINA, BOOMERANG, MAGIC_ROD, TAIL_KEY, SHOVEL, HOOKSHOT, PEGASUS_BOOTS, MAGIC_POWDER, BOMB]
MULTIWORLD = False

def __init__(self):
super().__init__(0x2A3)
self.give_bowwow = False

def configure(self, options):
if options.bowwow != 'normal':
# When we have bowwow mode, we pretend to be a sword for logic reasons
self.OPTIONS = [SWORD]
self.give_bowwow = True
if options.randomstartlocation and options.entranceshuffle != 'none':
self.OPTIONS.append(FLIPPERS)

def patch(self, rom, option, *, multiworld=None):
assert multiworld is None

Expand Down
16 changes: 16 additions & 0 deletions worlds/ladx/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,20 @@ class InGameHints(DefaultOnToggle):
display_name = "In-game Hints"


class TarinsGift(Choice):
"""
[Local Progression] Forces Tarin's gift to be an item that immediately opens up local checks.
threeandthreee marked this conversation as resolved.
Show resolved Hide resolved
Has little effect in single player games, and isn't always necessary with randomized entrances.
[Bush Breaker] Forces Tarin's gift to be an item that can destroy bushes.
[Any Item] Tarin's gift can be any item for any world
"""
display_name = "Tarin's Gift"
option_local_progression = 0
option_bush_breaker = 1
option_any_item = 2
default = option_local_progression


class StabilizeItemPool(DefaultOffToggle):
"""
By default, rupees in the item pool may be randomly swapped with bombs, arrows, powders, or capacity upgrades. This option disables that swapping, which is useful for plando.
Expand Down Expand Up @@ -565,6 +579,7 @@ class ForeignItemIcons(Choice):
OptionGroup("Miscellaneous", [
TradeQuest,
Rooster,
TarinsGift,
Overworld,
TrendyGame,
InGameHints,
Expand Down Expand Up @@ -638,6 +653,7 @@ class LinksAwakeningOptions(PerGameCommonOptions):
text_mode: TextMode
no_flash: NoFlash
in_game_hints: InGameHints
tarins_gift: TarinsGift
overworld: Overworld
stabilize_item_pool: StabilizeItemPool

Expand Down
80 changes: 53 additions & 27 deletions worlds/ladx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pkgutil
import tempfile
import typing
import logging
import re

import bsdiff4
Expand Down Expand Up @@ -178,25 +179,25 @@ def create_regions(self) -> None:

assert(start)

menu_region = LinksAwakeningRegion("Menu", None, "Menu", self.player, self.multiworld)
menu_region = LinksAwakeningRegion("Menu", None, "Menu", self.player, self.multiworld)
menu_region.exits = [Entrance(self.player, "Start Game", menu_region)]
menu_region.exits[0].connect(start)

self.multiworld.regions.append(menu_region)

# Place RAFT, other access events
for region in regions:
for loc in region.locations:
if loc.address is None:
loc.place_locked_item(self.create_event(loc.ladxr_item.event))

# Connect Windfish -> Victory
windfish = self.multiworld.get_region("Windfish", self.player)
l = Location(self.player, "Windfish", parent=windfish)
windfish.locations = [l]

l.place_locked_item(self.create_event("An Alarm Clock"))

self.multiworld.completion_condition[self.player] = lambda state: state.has("An Alarm Clock", player=self.player)

def create_item(self, item_name: str):
Expand All @@ -206,6 +207,8 @@ def create_event(self, event: str):
return Item(event, ItemClassification.progression, None, self.player)

def create_items(self) -> None:
itempool = []

exclude = [item.name for item in self.multiworld.precollected_items[self.player]]

self.prefill_original_dungeon = [ [], [], [], [], [], [], [], [], [] ]
Expand Down Expand Up @@ -265,9 +268,9 @@ def create_items(self) -> None:
self.prefill_own_dungeons.append(item)
self.pre_fill_items.append(item)
else:
self.multiworld.itempool.append(item)
itempool.append(item)
else:
self.multiworld.itempool.append(item)
itempool.append(item)

self.multi_key = self.generate_multi_key()

Expand All @@ -276,8 +279,8 @@ def create_items(self) -> None:
event_location = Location(self.player, "Can Play Trendy Game", parent=trendy_region)
trendy_region.locations.insert(0, event_location)
event_location.place_locked_item(self.create_event("Can Play Trendy Game"))
self.dungeon_locations_by_dungeon = [[], [], [], [], [], [], [], [], []]

self.dungeon_locations_by_dungeon = [[], [], [], [], [], [], [], [], []]
for r in self.multiworld.get_regions(self.player):
# Set aside dungeon locations
if r.dungeon_index:
Expand All @@ -290,21 +293,44 @@ def create_items(self) -> None:
# Properly fill locations within dungeon
location.dungeon = r.dungeon_index

# For now, special case first item
FORCE_START_ITEM = True
if FORCE_START_ITEM:
self.force_start_item()
if self.options.tarins_gift != "any_item":
self.force_start_item(itempool)


self.multiworld.itempool += itempool

def force_start_item(self):
def force_start_item(self, itempool):
threeandthreee marked this conversation as resolved.
Show resolved Hide resolved
start_loc = self.multiworld.get_location("Tarin's Gift (Mabe Village)", self.player)
if not start_loc.item:
possible_start_items = [index for index, item in enumerate(self.multiworld.itempool)
if item.player == self.player
and item.item_data.ladxr_id in start_loc.ladxr_item.OPTIONS and not item.location]
if possible_start_items:
index = self.random.choice(possible_start_items)
start_item = self.multiworld.itempool.pop(index)
"""
Find an item that forces progression or a bush breaker for the player, depending on settings.
"""
def is_possible_start_item(item):
return item.advancement and item.name not in self.options.non_local_items

def opens_new_regions(item):
collection_state = base_collection_state.copy()
collection_state.collect(item)
return len(collection_state.reachable_regions[self.player]) > reachable_count

start_items = [item for item in itempool if is_possible_start_item(item)]
self.random.shuffle(start_items)

if self.options.tarins_gift == "bush_breaker":
start_item = next((item for item in start_items if item.name in links_awakening_item_name_groups["Bush Breakers"]), None)

else: # local_progression
base_collection_state = CollectionState(self.multiworld)
base_collection_state.update_reachable_regions(self.player)
reachable_count = len(base_collection_state.reachable_regions[self.player])
start_item = next((item for item in start_items if opens_new_regions(item)), None)

if start_item:
itempool.remove(start_item)
start_loc.place_locked_item(start_item)
else:
logging.getLogger("Link's Awakening Logger").warning(f"No {self.options.tarins_gift.current_option_name} available for Tarin's Gift.")


def get_pre_fill_items(self):
return self.pre_fill_items
Expand All @@ -317,7 +343,7 @@ def pre_fill(self) -> None:

# set containing the list of all possible dungeon locations for the player
all_dungeon_locs = set()

# Do dungeon specific things
for dungeon_index in range(0, 9):
# set up allow-list for dungeon specific items
Expand All @@ -330,7 +356,7 @@ def pre_fill(self) -> None:
# ...also set the rules for the dungeon
for location in locs:
orig_rule = location.item_rule
# If an item is about to be placed on a dungeon location, it can go there iff
# If an item is about to be placed on a dungeon location, it can go there iff
# 1. it fits the general rules for that location (probably 'return True' for most places)
# 2. Either
# 2a. it's not a restricted dungeon item
Expand Down Expand Up @@ -382,7 +408,7 @@ def priority(item):

# Sweep to pick up already placed items that are reachable with everything but the dungeon items.
partial_all_state.sweep_for_advancements()

fill_restrictive(self.multiworld, partial_all_state, all_dungeon_locs_to_fill, all_dungeon_items_to_fill, lock=True, single_player_placement=True, allow_partial=False)


Expand Down Expand Up @@ -421,7 +447,7 @@ def guess_icon_for_other_world(self, foreign_item):
for name in possibles:
if name in self.name_cache:
return self.name_cache[name]

return "TRADING_ITEM_LETTER"

@classmethod
Expand All @@ -436,7 +462,7 @@ def generate_output(self, output_directory: str):
for loc in r.locations:
if isinstance(loc, LinksAwakeningLocation):
assert(loc.item)

# If we're a links awakening item, just use the item
if isinstance(loc.item, LinksAwakeningItem):
loc.ladxr_item.item = loc.item.item_data.ladxr_id
Expand Down Expand Up @@ -470,15 +496,15 @@ def generate_output(self, output_directory: str):
args = parser.parse_args([rom_name, "-o", out_name, "--dump"])

rom = generator.generateRom(args, self)

with open(out_path, "wb") as handle:
rom.save(handle, name="LADXR")

# Write title screen after everything else is done - full gfxmods may stomp over the egg tiles
if self.options.ap_title_screen:
with tempfile.NamedTemporaryFile(delete=False) as title_patch:
title_patch.write(pkgutil.get_data(__name__, "LADXR/patches/title_screen.bdiff4"))

bsdiff4.file_patch_inplace(out_path, title_patch.name)
os.unlink(title_patch.name)

Expand Down
Loading