Fix gun missing from combat_items, add new for combat logic cache, very slight refactor of check_combat_reqs to let it do the changeover in a less complicated fashion, fix area being a boss area rather than non-boss area for a check (#4657)

This commit is contained in:
Scipio Wright
2025-02-16 19:30:40 -05:00
committed by GitHub
parent 8349774c5c
commit 378fa5d5c4
4 changed files with 45 additions and 19 deletions

View File

@@ -140,24 +140,14 @@ def check_combat_reqs(area_name: str, state: CollectionState, player: int, alt_d
# need sword for bosses
if data.is_boss:
return False
equipment.remove("Sword")
if has_magic:
if "Magic" not in equipment:
equipment.append("Magic")
# +4 mp pretty much makes up for the lack of sword, at least in Quarry
extra_mp_needed += 4
if stick_bool:
# stick is a backup plan, and doesn't scale well, so let's require a little less
equipment.append("Stick")
extra_att_needed -= 2
else:
extra_mp_needed += 2
extra_att_needed -= 32
elif stick_bool:
if stick_bool:
equipment.remove("Sword")
equipment.append("Stick")
# may revise this later based on feedback
extra_att_needed += 3
extra_def_needed += 2
# this is for when it changes over to the magic-only state if it needs to later
extra_mp_needed += 4
else:
return False
@@ -204,7 +194,7 @@ def check_combat_reqs(area_name: str, state: CollectionState, player: int, alt_d
equip_list.append("Magic")
more_modified_stats = AreaStats(modified_stats.att_level - 32, modified_stats.def_level,
modified_stats.potion_level, modified_stats.hp_level,
modified_stats.sp_level, modified_stats.mp_level + 4,
modified_stats.sp_level, modified_stats.mp_level + 2,
modified_stats.potion_count, equip_list, data.is_boss)
if check_combat_reqs("none", state, player, more_modified_stats):
return True
@@ -222,7 +212,7 @@ def has_required_stats(data: AreaStats, state: CollectionState, player: int) ->
player_att, att_offerings = get_att_level(state, player)
# if you have 2 more attack than needed, we can forego needing mp
if data.mp_level > 1:
if data.mp_level > 1 and "Magic" in data.equipment:
if player_att < data.att_level + 2:
player_mp, mp_offerings = get_mp_level(state, player)
if player_mp < data.mp_level:

View File

@@ -1832,7 +1832,7 @@ def set_er_location_rules(world: "TunicWorld") -> None:
if world.options.combat_logic == CombatLogic.option_on:
combat_logic_to_loc("Overworld - [Northeast] Flowers Holy Cross", "Garden Knight")
combat_logic_to_loc("Overworld - [Northwest] Chest Near Quarry Gate", "Before Well", dagger=True)
combat_logic_to_loc("Overworld - [Northeast] Chest Above Patrol Cave", "Garden Knight", dagger=True)
combat_logic_to_loc("Overworld - [Northeast] Chest Above Patrol Cave", "West Garden", dagger=True)
combat_logic_to_loc("Overworld - [Southwest] West Beach Guarded By Turret", "Overworld", dagger=True)
combat_logic_to_loc("Overworld - [Southwest] West Beach Guarded By Turret 2", "Overworld")
combat_logic_to_loc("Overworld - [Southwest] Bombable Wall Near Fountain", "Before Well", dagger=True)

View File

@@ -212,7 +212,7 @@ slot_data_item_names = [
combat_items: List[str] = [name for name, data in item_table.items()
if data.combat_ic and IC.progression in data.combat_ic]
combat_items.extend(["Stick", "Sword", "Sword Upgrade", "Magic Wand", "Hero's Laurels"])
combat_items.extend(["Stick", "Sword", "Sword Upgrade", "Magic Wand", "Hero's Laurels", "Gun"])
item_name_to_id: Dict[str, int] = {name: item_base_id + data.item_id_offset for name, data in item_table.items()}

View File

@@ -4,7 +4,7 @@ from collections import Counter
from . import TunicTestBase
from .. import options
from ..combat_logic import (check_combat_reqs, area_data, get_money_count, calc_effective_hp, get_potion_level,
get_hp_level, get_def_level, get_sp_level)
get_hp_level, get_def_level, get_sp_level, has_combat_reqs)
from ..items import item_table
from .. import TunicWorld
@@ -81,3 +81,39 @@ class TestCombat(TunicTestBase):
f"Free Def and Offerings: {player_def - def_offerings}, {def_offerings}\n"
f"Free SP and Offerings: {player_sp - sp_offerings}, {sp_offerings}")
prev_statuses[area] = curr_statuses[area]
# the issue was that a direct check of the logic and the cache had different results
# it was actually due to the combat_items in items.py not having the Gun in it
# but this test is still helpful for verifying the cache
def test_combat_magic_weapons(self):
combat_items = self.combat_items.copy()
combat_items.remove("Magic Wand")
combat_items.remove("Gun")
area_names = list(area_data.keys())
self.multiworld.worlds[1].random.shuffle(combat_items)
self.multiworld.worlds[1].random.shuffle(area_names)
current_items = Counter()
state = self.multiworld.state.copy()
player = self.player
gun = TunicWorld.create_item(self.world, "Gun")
for current_item_name in combat_items:
current_item = TunicWorld.create_item(self.world, current_item_name)
state.collect(current_item)
current_items[current_item_name] += 1
for area in area_names:
if check_combat_reqs(area, state, player) != has_combat_reqs(area, state, player):
raise Exception(f"Cache for {area} does not match a direct check "
f"after collecting {current_item_name}.\n"
f"Current items: {current_items}.\n"
f"Cache {'succeeded' if has_combat_reqs(area, state, player) else 'failed'}\n"
f"Direct {'succeeded' if check_combat_reqs(area, state, player) else 'failed'}")
state.collect(gun)
for area in area_names:
if check_combat_reqs(area, state, player) != has_combat_reqs(area, state, player):
raise Exception(f"Cache for {area} does not match a direct check "
f"after collecting the Gun.\n"
f"Current items: {current_items}.\n"
f"Cache {'succeeded' if has_combat_reqs(area, state, player) else 'failed'}\n"
f"Direct {'succeeded' if check_combat_reqs(area, state, player) else 'failed'}")
state.remove(gun)