Swap to using logicmixin instead of prog_items (thanks Vi)

This commit is contained in:
Scipio Wright
2024-08-04 17:51:19 -04:00
parent 984e9d5df3
commit 9dc352f829
2 changed files with 51 additions and 46 deletions

View File

@@ -11,6 +11,7 @@ from .er_scripts import create_er_regions
from .er_data import portal_mapping
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 decimal import Decimal, ROUND_HALF_UP
@@ -127,6 +128,15 @@ class TunicWorld(World):
def stage_generate_early(cls, multiworld: MultiWorld) -> None:
tunic_worlds: Tuple[TunicWorld] = multiworld.get_game_worlds("TUNIC")
for tunic in tunic_worlds:
# setting up state combat logic stuff, see has_combat_reqs for its use
# and this is magic so pycharm doesn't like it, unfortunately
if tunic.options.combat_logic:
multiworld.state.tunic_need_to_reset_combat_from_collect[tunic.player] = False
multiworld.state.tunic_need_to_reset_combat_from_remove[tunic.player] = False
multiworld.state.tunic_area_combat_state[tunic.player] = {}
for area_name in area_data.keys():
multiworld.state.tunic_area_combat_state[tunic.player][area_name] = CombatState.unchecked
# if it's one of the options, then it isn't a custom seed group
if tunic.options.entrance_rando.value in EntranceRando.options.values():
continue
@@ -352,13 +362,13 @@ class TunicWorld(World):
def collect(self, state: CollectionState, item: Item) -> bool:
change = super().collect(state, item)
if change and self.options.combat_logic and item.name in combat_items:
state.prog_items[self.player]["need_to_reset_combat_state_from_collect"] = 1
state.tunic_need_to_reset_combat_from_collect[self.player] = True
return change
def remove(self, state: CollectionState, item: Item) -> bool:
change = super().remove(state, item)
if change and self.options.combat_logic and item.name in combat_items:
state.prog_items[self.player]["need_to_reset_combat_state_from_remove"] = 1
state.tunic_need_to_reset_combat_from_remove[self.player] = True
return change
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]) -> None:

View File

@@ -2,6 +2,7 @@ from typing import Dict, List, NamedTuple, Tuple, Optional
from enum import IntEnum
from BaseClasses import CollectionState
from .rules import has_sword, has_melee
from worlds.AutoWorld import LogicMixin
# the vanilla stats you are expected to have to get through an area, based on where they are in vanilla
@@ -59,34 +60,23 @@ class CombatState(IntEnum):
def has_combat_reqs(area_name: str, state: CollectionState, player: int) -> bool:
# we're caching whether you've met the combat reqs before if the state didn't change first
player_state = state.prog_items[player]
# if the combat state is stale, mark each area's combat state as stale
if player_state["need_to_reset_combat_state_from_collect"]:
player_state["need_to_reset_combat_state_from_collect"] = 0
for name in boss_areas:
if player_state["combat_reqs_met_for_" + name] == CombatState.failed:
player_state["combat_reqs_met_for_" + name] = CombatState.unchecked
for name in non_boss_areas:
if player_state["combat_reqs_met_for_" + name] == CombatState.failed:
player_state["combat_reqs_met_for_" + name] = CombatState.unchecked
if player_state["combat_reqs_met_for_Gauntlet"] == CombatState.failed:
player_state["combat_reqs_met_for_Gauntlet"] = CombatState.unchecked
if state.tunic_need_to_reset_combat_from_collect[player]:
state.tunic_need_to_reset_combat_from_collect[player] = 0
for name in area_data.keys():
if state.tunic_area_combat_state[player][name] == CombatState.failed:
state.tunic_area_combat_state[player][name] = CombatState.unchecked
if player_state["need_to_reset_combat_state_from_remove"]:
player_state["need_to_reset_combat_state_from_remove"] = 0
for name in boss_areas:
if player_state["combat_reqs_met_for_" + name] == CombatState.succeeded:
player_state["combat_reqs_met_for_" + name] = CombatState.unchecked
for name in non_boss_areas:
if player_state["combat_reqs_met_for_" + name] == CombatState.succeeded:
player_state["combat_reqs_met_for_" + name] = CombatState.unchecked
if player_state["combat_reqs_met_for_Gauntlet"] == CombatState.succeeded:
player_state["combat_reqs_met_for_Gauntlet"] = CombatState.unchecked
if state.tunic_need_to_reset_combat_from_remove[player]:
state.tunic_need_to_reset_combat_from_remove[player] = 0
for name in area_data.keys():
if state.tunic_area_combat_state[player][name] == CombatState.succeeded:
state.tunic_area_combat_state[player][name] = CombatState.unchecked
if state.tunic_area_combat_state[player][area_name] > CombatState.unchecked:
return state.tunic_area_combat_state[player][area_name] == CombatState.succeeded
met_combat_reqs = check_combat_reqs(area_name, state, player)
if player_state["combat_reqs_met_for_" + area_name] > CombatState.unchecked:
return met_combat_reqs
# loop through the lists and set the easier/harder area states accordingly
if area_name in boss_areas:
area_list = boss_areas
@@ -95,30 +85,25 @@ def has_combat_reqs(area_name: str, state: CollectionState, player: int) -> bool
else:
area_list = [area_name]
if area_name in area_list:
if met_combat_reqs:
# set the state as true for each area until you get to the area we're looking at
for name in area_list:
player_state["combat_reqs_met_for_" + name] = CombatState.succeeded
if name == area_name:
break
else:
# set the state as false for the area we're looking at and each area after that
reached_name = False
for name in area_list:
if name == area_name:
reached_name = True
if reached_name:
player_state["combat_reqs_met_for_" + name] = CombatState.failed
if met_combat_reqs:
# set the state as true for each area until you get to the area we're looking at
for name in area_list:
state.tunic_area_combat_state[player][name] = CombatState.succeeded
if name == area_name:
break
else:
# set the state as false for the area we're looking at and each area after that
reached_name = False
for name in area_list:
if name == area_name:
reached_name = True
if reached_name:
state.tunic_area_combat_state[player][name] = CombatState.failed
return check_combat_reqs(area_name, state, player)
return met_combat_reqs
def check_combat_reqs(area_name: str, state: CollectionState, player: int, alt_data: Optional[AreaStats] = None) -> bool:
# if our cache says we've already calced this, we don't need to go through these calculations again
if state.prog_items[player]["combat_reqs_met_for_" + area_name] > CombatState.unchecked:
return state.prog_items[player]["combat_reqs_met_for_" + area_name] > CombatState.failed
data = alt_data or area_data[area_name]
extra_att_needed = 0
extra_def_needed = 0
@@ -413,3 +398,13 @@ def get_money_count(state: CollectionState, player: int) -> int:
money += money_per_break
money_per_break = min(512, money_per_break * 2)
return money
class TunicState(LogicMixin):
# the per-player need to reset the combat state when collecting a combat item
tunic_need_to_reset_combat_from_collect: Dict[int, bool] = {}
# the per-player need to reset the combat state when removing a combat item
tunic_need_to_reset_combat_from_remove: Dict[int, bool] = {}
# the per-player, per-area state of combat checking -- unchecked, failed, or succeeded
tunic_area_combat_state: Dict[int, Dict[str, int]] = {}