The Witness: Laser Hints (#2895)

This commit is contained in:
NewSoupVi 2024-03-05 22:54:02 +01:00 committed by GitHub
parent b147c5bf8a
commit 4ddfb7ce8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 69 additions and 22 deletions

View File

@ -3,22 +3,22 @@ Archipelago init file for The Witness
"""
import dataclasses
from typing import Dict, Optional
from typing import Dict, Optional, cast
from BaseClasses import Region, Location, MultiWorld, Item, Entrance, Tutorial, CollectionState
from Options import PerGameCommonOptions, Toggle
from .presets import witness_option_presets
from worlds.AutoWorld import World, WebWorld
from .player_logic import WitnessPlayerLogic
from .static_logic import StaticWitnessLogic
from .static_logic import StaticWitnessLogic, ItemCategory, DoorItemDefinition
from .hints import get_always_hint_locations, get_always_hint_items, get_priority_hint_locations, \
get_priority_hint_items, make_always_and_priority_hints, generate_joke_hints, make_area_hints, get_hintable_areas, \
make_extra_location_hints, create_all_hints
make_extra_location_hints, create_all_hints, make_laser_hints, make_compact_hint_data, CompactItemData
from .locations import WitnessPlayerLocations, StaticWitnessLocations
from .items import WitnessItem, StaticWitnessItems, WitnessPlayerItems, ItemData
from .regions import WitnessRegions
from .rules import set_rules
from .options import TheWitnessOptions
from .utils import get_audio_logs
from .utils import get_audio_logs, get_laser_shuffle
from logging import warning, error
@ -66,7 +66,8 @@ class WitnessWorld(World):
self.items = None
self.regio = None
self.log_ids_to_hints = None
self.log_ids_to_hints: Dict[int, CompactItemData] = dict()
self.laser_ids_to_hints: Dict[int, CompactItemData] = dict()
self.items_placed_early = []
self.own_itempool = []
@ -81,6 +82,7 @@ class WitnessWorld(World):
'symbols_not_in_the_game': self.items.get_symbol_ids_not_in_pool(),
'disabled_entities': [int(h, 16) for h in self.player_logic.COMPLETELY_DISABLED_ENTITIES],
'log_ids_to_hints': self.log_ids_to_hints,
'laser_ids_to_hints': self.laser_ids_to_hints,
'progressive_item_lists': self.items.get_progressive_item_ids_in_pool(),
'obelisk_side_id_to_EPs': StaticWitnessLogic.OBELISK_SIDE_ID_TO_EP_HEXES,
'precompleted_puzzles': [int(h, 16) for h in self.player_logic.EXCLUDED_LOCATIONS],
@ -100,8 +102,6 @@ class WitnessWorld(World):
)
self.regio: WitnessRegions = WitnessRegions(self.locat, self)
self.log_ids_to_hints = dict()
interacts_with_multiworld = (
self.options.shuffle_symbols or
self.options.shuffle_doors or
@ -272,11 +272,25 @@ class WitnessWorld(World):
self.options.local_items.value.add(item_name)
def fill_slot_data(self) -> dict:
already_hinted_locations = set()
# Laser hints
if self.options.laser_hints:
laser_hints = make_laser_hints(self, StaticWitnessItems.item_groups["Lasers"])
for item_name, hint in laser_hints.items():
item_def = cast(DoorItemDefinition, StaticWitnessLogic.all_items[item_name])
self.laser_ids_to_hints[int(item_def.panel_id_hexes[0], 16)] = make_compact_hint_data(hint, self.player)
already_hinted_locations.add(hint.location)
# Audio Log Hints
hint_amount = self.options.hint_amount.value
credits_hint = (
"This Randomizer is brought to you by\n"
"NewSoupVi, Jarno, blastron,\n",
"NewSoupVi, Jarno, blastron,\n"
"jbzdarkid, sigma144, IHNN, oddGarrett, Exempt-Medic.", -1, -1
)
@ -285,25 +299,19 @@ class WitnessWorld(World):
if hint_amount:
area_hints = round(self.options.area_hint_percentage / 100 * hint_amount)
generated_hints = create_all_hints(self, hint_amount, area_hints)
generated_hints = create_all_hints(self, hint_amount, area_hints, already_hinted_locations)
self.random.shuffle(audio_logs)
duplicates = min(3, len(audio_logs) // hint_amount)
for hint in generated_hints:
location = hint.location
area_amount = hint.area_amount
# None if junk hint, address if location hint, area string if area hint
arg_1 = location.address if location else (hint.area if hint.area else None)
# self.player if junk hint, player if location hint, progression amount if area hint
arg_2 = area_amount if area_amount is not None else (location.player if location else self.player)
hint = generated_hints.pop(0)
compact_hint_data = make_compact_hint_data(hint, self.player)
for _ in range(0, duplicates):
audio_log = audio_logs.pop()
self.log_ids_to_hints[int(audio_log, 16)] = (hint.wording, arg_1, arg_2)
self.log_ids_to_hints[int(audio_log, 16)] = compact_hint_data
if audio_logs:
audio_log = audio_logs.pop()
@ -315,7 +323,7 @@ class WitnessWorld(World):
audio_log = audio_logs.pop()
self.log_ids_to_hints[int(audio_log, 16)] = joke_hints.pop()
# generate hints done
# Options for the client & auto-tracker
slot_data = self._get_slot_data()

View File

@ -1,6 +1,6 @@
import logging
from dataclasses import dataclass
from typing import Tuple, List, TYPE_CHECKING, Set, Dict, Optional
from typing import Tuple, List, TYPE_CHECKING, Set, Dict, Optional, Union
from BaseClasses import Item, ItemClassification, Location, LocationProgressType, CollectionState
from . import StaticWitnessLogic
from .utils import weighted_sample
@ -8,6 +8,8 @@ from .utils import weighted_sample
if TYPE_CHECKING:
from . import WitnessWorld
CompactItemData = Tuple[str, Union[str, int], int]
joke_hints = [
"Quaternions break my brain",
"Eclipse has nothing, but you should do it anyway.",
@ -634,14 +636,15 @@ def make_area_hints(world: "WitnessWorld", amount: int, already_hinted_locations
return hints, unhinted_locations_per_area
def create_all_hints(world: "WitnessWorld", hint_amount: int, area_hints: int) -> List[WitnessWordedHint]:
def create_all_hints(world: "WitnessWorld", hint_amount: int, area_hints: int,
already_hinted_locations: Set[Location]) -> List[WitnessWordedHint]:
generated_hints: List[WitnessWordedHint] = []
state = CollectionState(world.multiworld)
# Keep track of already hinted locations. Consider early Tutorial as "already hinted"
already_hinted_locations = {
already_hinted_locations |= {
loc for loc in world.multiworld.get_reachable_locations(state, world.player)
if loc.address and StaticWitnessLogic.ENTITIES_BY_NAME[loc.name]["area"]["name"] == "Tutorial (Inside)"
}
@ -721,3 +724,29 @@ def create_all_hints(world: "WitnessWorld", hint_amount: int, area_hints: int) -
f"Generated {len(generated_hints)} instead.")
return generated_hints
def make_compact_hint_data(hint: WitnessWordedHint, local_player_number: int) -> CompactItemData:
location = hint.location
area_amount = hint.area_amount
# None if junk hint, address if location hint, area string if area hint
arg_1 = location.address if location else (hint.area if hint.area else None)
# self.player if junk hint, player if location hint, progression amount if area hint
arg_2 = area_amount if area_amount is not None else (location.player if location else local_player_number)
return hint.wording, arg_1, arg_2
def make_laser_hints(world: "WitnessWorld", laser_names: List[str]) -> Dict[str, WitnessWordedHint]:
laser_hints_by_name = dict()
for item_name in laser_names:
location_hint = hint_from_item(world, item_name, world.own_itempool)
if not location_hint:
continue
laser_hints_by_name[item_name] = word_direct_hint(world, location_hint)
return laser_hints_by_name

View File

@ -225,6 +225,12 @@ class AreaHintPercentage(Range):
default = 33
class LaserHints(Toggle):
"""If on, lasers will tell you where their items are if you walk close to them in-game.
Only applies if laser shuffle is enabled."""
display_name = "Laser Hints"
class DeathLink(Toggle):
"""If on: Whenever you fail a puzzle (with some exceptions), everyone who is also on Death Link dies.
The effect of a "death" in The Witness is a Bonk Trap."""
@ -264,5 +270,6 @@ class TheWitnessOptions(PerGameCommonOptions):
puzzle_skip_amount: PuzzleSkipAmount
hint_amount: HintAmount
area_hint_percentage: AreaHintPercentage
laser_hints: LaserHints
death_link: DeathLink
death_link_amnesty: DeathLinkAmnesty

View File

@ -33,6 +33,7 @@ witness_option_presets: Dict[str, Dict[str, Any]] = {
"puzzle_skip_amount": PuzzleSkipAmount.default,
"hint_amount": HintAmount.default,
"area_hint_percentage": AreaHintPercentage.default,
"laser_hints": LaserHints.default,
"death_link": DeathLink.default,
},
@ -66,6 +67,7 @@ witness_option_presets: Dict[str, Dict[str, Any]] = {
"puzzle_skip_amount": 15,
"hint_amount": HintAmount.default,
"area_hint_percentage": AreaHintPercentage.default,
"laser_hints": LaserHints.default,
"death_link": DeathLink.default,
},
@ -99,6 +101,7 @@ witness_option_presets: Dict[str, Dict[str, Any]] = {
"puzzle_skip_amount": 15,
"hint_amount": HintAmount.default,
"area_hint_percentage": AreaHintPercentage.default,
"laser_hints": LaserHints.default,
"death_link": DeathLink.default,
},
}