Skip to content

Commit

Permalink
Stardew Valley: Added missing logic rules for dating and marriage (#2160
Browse files Browse the repository at this point in the history
)

* - Added missing logic rules where, to earn hearts above 8 and 10, you need access to dating and marriage respectively.

* - Slight cleanup based on Black Sliver's suggestion
  • Loading branch information
agilbert1412 authored Sep 14, 2023
1 parent 7522a32 commit fdac505
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 6 deletions.
24 changes: 18 additions & 6 deletions worlds/stardew_valley/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -1043,11 +1043,12 @@ def can_reproduce(self, number_children: int = 1) -> StardewRule:
def has_relationship(self, npc: str, hearts: int = 1) -> StardewRule:
if hearts <= 0:
return True_()
if self.options[options.Friendsanity] == options.Friendsanity.option_none:
friendsanity = self.options[options.Friendsanity]
if friendsanity == options.Friendsanity.option_none:
return self.can_earn_relationship(npc, hearts)
if npc not in all_villagers_by_name:
if npc == NPC.pet:
if self.options[options.Friendsanity] == options.Friendsanity.option_bachelors:
if friendsanity == options.Friendsanity.option_bachelors:
return self.can_befriend_pet(hearts)
return self.received_hearts(NPC.pet, hearts)
if npc == Generic.any or npc == Generic.bachelor:
Expand Down Expand Up @@ -1077,12 +1078,12 @@ def has_relationship(self, npc: str, hearts: int = 1) -> StardewRule:
if not self.npc_is_in_current_slot(npc):
return True_()
villager = all_villagers_by_name[npc]
if self.options[options.Friendsanity] == options.Friendsanity.option_bachelors and not villager.bachelor:
if friendsanity == options.Friendsanity.option_bachelors and not villager.bachelor:
return self.can_earn_relationship(npc, hearts)
if self.options[options.Friendsanity] == options.Friendsanity.option_starting_npcs and not villager.available:
if friendsanity == options.Friendsanity.option_starting_npcs and not villager.available:
return self.can_earn_relationship(npc, hearts)
if self.options[
options.Friendsanity] != options.Friendsanity.option_all_with_marriage and villager.bachelor and hearts > 8:
is_capped_at_8 = villager.bachelor and friendsanity != options.Friendsanity.option_all_with_marriage
if is_capped_at_8 and hearts > 8:
return self.received_hearts(villager, 8) & self.can_earn_relationship(npc, hearts)
return self.received_hearts(villager, hearts)

Expand Down Expand Up @@ -1136,11 +1137,22 @@ def can_earn_relationship(self, npc: str, hearts: int = 0) -> StardewRule:
rule_if_birthday = self.has_season(villager.birthday) & self.has_any_universal_love() & self.has_lived_months(hearts // 2)
rule_if_not_birthday = self.has_lived_months(hearts)
earn_rule = self.can_meet(npc) & (rule_if_birthday | rule_if_not_birthday)
if villager.bachelor:
if hearts > 8:
earn_rule = earn_rule & self.can_date(npc)
if hearts > 10:
earn_rule = earn_rule & self.can_marry(npc)
else:
earn_rule = self.has_lived_months(min(hearts // 2, 8))

return previous_heart_rule & earn_rule

def can_date(self, npc: str) -> StardewRule:
return self.has_relationship(npc, 8) & self.has(Gift.bouquet)

def can_marry(self, npc: str) -> StardewRule:
return self.has_relationship(npc, 10) & self.has(Gift.mermaid_pendant)

def can_befriend_pet(self, hearts: int):
if hearts <= 0:
return True_()
Expand Down
60 changes: 60 additions & 0 deletions worlds/stardew_valley/test/TestRules.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,63 @@ def collect_all_except(multiworld, item_to_not_collect: str):
for item in multiworld.get_items():
if item.name != item_to_not_collect:
multiworld.state.collect(item)


class TestFriendsanityDatingRules(SVTestBase):
options = {
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized_not_winter,
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.FriendsanityHeartSize.internal_name: 3
}

def test_earning_dating_heart_requires_dating(self):
month_name = "Month End"
for i in range(12):
month_item = self.world.create_item(month_name)
self.multiworld.state.collect(month_item, event=True)
self.multiworld.state.collect(self.world.create_item("Beach Bridge"), event=False)
self.multiworld.state.collect(self.world.create_item("Progressive House"), event=False)
self.multiworld.state.collect(self.world.create_item("Adventurer's Guild"), event=False)
self.multiworld.state.collect(self.world.create_item("Galaxy Hammer"), event=False)
for i in range(3):
self.multiworld.state.collect(self.world.create_item("Progressive Pickaxe"), event=False)
self.multiworld.state.collect(self.world.create_item("Progressive Axe"), event=False)
self.multiworld.state.collect(self.world.create_item("Progressive Barn"), event=False)
for i in range(10):
self.multiworld.state.collect(self.world.create_item("Foraging Level"), event=False)
self.multiworld.state.collect(self.world.create_item("Farming Level"), event=False)
self.multiworld.state.collect(self.world.create_item("Mining Level"), event=False)
self.multiworld.state.collect(self.world.create_item("Combat Level"), event=False)
self.multiworld.state.collect(self.world.create_item("Progressive Mine Elevator"), event=False)
self.multiworld.state.collect(self.world.create_item("Progressive Mine Elevator"), event=False)

npc = "Abigail"
heart_name = f"{npc} <3"
step = 3

self.assert_can_reach_heart_up_to(npc, 3, step)
self.multiworld.state.collect(self.world.create_item(heart_name), event=False)
self.assert_can_reach_heart_up_to(npc, 6, step)
self.multiworld.state.collect(self.world.create_item(heart_name), event=False)
self.assert_can_reach_heart_up_to(npc, 8, step)
self.multiworld.state.collect(self.world.create_item(heart_name), event=False)
self.assert_can_reach_heart_up_to(npc, 10, step)
self.multiworld.state.collect(self.world.create_item(heart_name), event=False)
self.assert_can_reach_heart_up_to(npc, 14, step)

def assert_can_reach_heart_up_to(self, npc: str, max_reachable: int, step: int):
prefix = "Friendsanity: "
suffix = " <3"
for i in range(1, max_reachable + 1):
if i % step != 0 and i != 14:
continue
location = f"{prefix}{npc} {i}{suffix}"
can_reach = self.world.logic.can_reach_location(location)(self.multiworld.state)
self.assertTrue(can_reach, f"Should be able to earn relationship up to {i} hearts")
for i in range(max_reachable + 1, 14 + 1):
if i % step != 0 and i != 14:
continue
location = f"{prefix}{npc} {i}{suffix}"
can_reach = self.world.logic.can_reach_location(location)(self.multiworld.state)
self.assertFalse(can_reach, f"Should not be able to earn relationship up to {i} hearts")

0 comments on commit fdac505

Please sign in to comment.