The Witness: Laser Hints (#2895)
This commit is contained in:
parent
b147c5bf8a
commit
4ddfb7ce8b
|
@ -3,22 +3,22 @@ Archipelago init file for The Witness
|
||||||
"""
|
"""
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional, cast
|
||||||
from BaseClasses import Region, Location, MultiWorld, Item, Entrance, Tutorial, CollectionState
|
from BaseClasses import Region, Location, MultiWorld, Item, Entrance, Tutorial, CollectionState
|
||||||
from Options import PerGameCommonOptions, Toggle
|
from Options import PerGameCommonOptions, Toggle
|
||||||
from .presets import witness_option_presets
|
from .presets import witness_option_presets
|
||||||
from worlds.AutoWorld import World, WebWorld
|
from worlds.AutoWorld import World, WebWorld
|
||||||
from .player_logic import WitnessPlayerLogic
|
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, \
|
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, \
|
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 .locations import WitnessPlayerLocations, StaticWitnessLocations
|
||||||
from .items import WitnessItem, StaticWitnessItems, WitnessPlayerItems, ItemData
|
from .items import WitnessItem, StaticWitnessItems, WitnessPlayerItems, ItemData
|
||||||
from .regions import WitnessRegions
|
from .regions import WitnessRegions
|
||||||
from .rules import set_rules
|
from .rules import set_rules
|
||||||
from .options import TheWitnessOptions
|
from .options import TheWitnessOptions
|
||||||
from .utils import get_audio_logs
|
from .utils import get_audio_logs, get_laser_shuffle
|
||||||
from logging import warning, error
|
from logging import warning, error
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,7 +66,8 @@ class WitnessWorld(World):
|
||||||
self.items = None
|
self.items = None
|
||||||
self.regio = 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.items_placed_early = []
|
||||||
self.own_itempool = []
|
self.own_itempool = []
|
||||||
|
@ -81,6 +82,7 @@ class WitnessWorld(World):
|
||||||
'symbols_not_in_the_game': self.items.get_symbol_ids_not_in_pool(),
|
'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],
|
'disabled_entities': [int(h, 16) for h in self.player_logic.COMPLETELY_DISABLED_ENTITIES],
|
||||||
'log_ids_to_hints': self.log_ids_to_hints,
|
'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(),
|
'progressive_item_lists': self.items.get_progressive_item_ids_in_pool(),
|
||||||
'obelisk_side_id_to_EPs': StaticWitnessLogic.OBELISK_SIDE_ID_TO_EP_HEXES,
|
'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],
|
'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.regio: WitnessRegions = WitnessRegions(self.locat, self)
|
||||||
|
|
||||||
self.log_ids_to_hints = dict()
|
|
||||||
|
|
||||||
interacts_with_multiworld = (
|
interacts_with_multiworld = (
|
||||||
self.options.shuffle_symbols or
|
self.options.shuffle_symbols or
|
||||||
self.options.shuffle_doors or
|
self.options.shuffle_doors or
|
||||||
|
@ -272,11 +272,25 @@ class WitnessWorld(World):
|
||||||
self.options.local_items.value.add(item_name)
|
self.options.local_items.value.add(item_name)
|
||||||
|
|
||||||
def fill_slot_data(self) -> dict:
|
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
|
hint_amount = self.options.hint_amount.value
|
||||||
|
|
||||||
credits_hint = (
|
credits_hint = (
|
||||||
"This Randomizer is brought to you by\n"
|
"This Randomizer is brought to you by\n"
|
||||||
"NewSoupVi, Jarno, blastron,\n",
|
"NewSoupVi, Jarno, blastron,\n"
|
||||||
"jbzdarkid, sigma144, IHNN, oddGarrett, Exempt-Medic.", -1, -1
|
"jbzdarkid, sigma144, IHNN, oddGarrett, Exempt-Medic.", -1, -1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -285,25 +299,19 @@ class WitnessWorld(World):
|
||||||
if hint_amount:
|
if hint_amount:
|
||||||
area_hints = round(self.options.area_hint_percentage / 100 * 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)
|
self.random.shuffle(audio_logs)
|
||||||
|
|
||||||
duplicates = min(3, len(audio_logs) // hint_amount)
|
duplicates = min(3, len(audio_logs) // hint_amount)
|
||||||
|
|
||||||
for hint in generated_hints:
|
for hint in generated_hints:
|
||||||
location = hint.location
|
hint = generated_hints.pop(0)
|
||||||
area_amount = hint.area_amount
|
compact_hint_data = make_compact_hint_data(hint, self.player)
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
for _ in range(0, duplicates):
|
for _ in range(0, duplicates):
|
||||||
audio_log = audio_logs.pop()
|
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:
|
if audio_logs:
|
||||||
audio_log = audio_logs.pop()
|
audio_log = audio_logs.pop()
|
||||||
|
@ -315,7 +323,7 @@ class WitnessWorld(World):
|
||||||
audio_log = audio_logs.pop()
|
audio_log = audio_logs.pop()
|
||||||
self.log_ids_to_hints[int(audio_log, 16)] = joke_hints.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()
|
slot_data = self._get_slot_data()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass
|
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 BaseClasses import Item, ItemClassification, Location, LocationProgressType, CollectionState
|
||||||
from . import StaticWitnessLogic
|
from . import StaticWitnessLogic
|
||||||
from .utils import weighted_sample
|
from .utils import weighted_sample
|
||||||
|
@ -8,6 +8,8 @@ from .utils import weighted_sample
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from . import WitnessWorld
|
from . import WitnessWorld
|
||||||
|
|
||||||
|
CompactItemData = Tuple[str, Union[str, int], int]
|
||||||
|
|
||||||
joke_hints = [
|
joke_hints = [
|
||||||
"Quaternions break my brain",
|
"Quaternions break my brain",
|
||||||
"Eclipse has nothing, but you should do it anyway.",
|
"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
|
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] = []
|
generated_hints: List[WitnessWordedHint] = []
|
||||||
|
|
||||||
state = CollectionState(world.multiworld)
|
state = CollectionState(world.multiworld)
|
||||||
|
|
||||||
# Keep track of already hinted locations. Consider early Tutorial as "already hinted"
|
# 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)
|
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)"
|
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.")
|
f"Generated {len(generated_hints)} instead.")
|
||||||
|
|
||||||
return generated_hints
|
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
|
||||||
|
|
|
@ -225,6 +225,12 @@ class AreaHintPercentage(Range):
|
||||||
default = 33
|
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):
|
class DeathLink(Toggle):
|
||||||
"""If on: Whenever you fail a puzzle (with some exceptions), everyone who is also on Death Link dies.
|
"""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."""
|
The effect of a "death" in The Witness is a Bonk Trap."""
|
||||||
|
@ -264,5 +270,6 @@ class TheWitnessOptions(PerGameCommonOptions):
|
||||||
puzzle_skip_amount: PuzzleSkipAmount
|
puzzle_skip_amount: PuzzleSkipAmount
|
||||||
hint_amount: HintAmount
|
hint_amount: HintAmount
|
||||||
area_hint_percentage: AreaHintPercentage
|
area_hint_percentage: AreaHintPercentage
|
||||||
|
laser_hints: LaserHints
|
||||||
death_link: DeathLink
|
death_link: DeathLink
|
||||||
death_link_amnesty: DeathLinkAmnesty
|
death_link_amnesty: DeathLinkAmnesty
|
||||||
|
|
|
@ -33,6 +33,7 @@ witness_option_presets: Dict[str, Dict[str, Any]] = {
|
||||||
"puzzle_skip_amount": PuzzleSkipAmount.default,
|
"puzzle_skip_amount": PuzzleSkipAmount.default,
|
||||||
"hint_amount": HintAmount.default,
|
"hint_amount": HintAmount.default,
|
||||||
"area_hint_percentage": AreaHintPercentage.default,
|
"area_hint_percentage": AreaHintPercentage.default,
|
||||||
|
"laser_hints": LaserHints.default,
|
||||||
"death_link": DeathLink.default,
|
"death_link": DeathLink.default,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -66,6 +67,7 @@ witness_option_presets: Dict[str, Dict[str, Any]] = {
|
||||||
"puzzle_skip_amount": 15,
|
"puzzle_skip_amount": 15,
|
||||||
"hint_amount": HintAmount.default,
|
"hint_amount": HintAmount.default,
|
||||||
"area_hint_percentage": AreaHintPercentage.default,
|
"area_hint_percentage": AreaHintPercentage.default,
|
||||||
|
"laser_hints": LaserHints.default,
|
||||||
"death_link": DeathLink.default,
|
"death_link": DeathLink.default,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -99,6 +101,7 @@ witness_option_presets: Dict[str, Dict[str, Any]] = {
|
||||||
"puzzle_skip_amount": 15,
|
"puzzle_skip_amount": 15,
|
||||||
"hint_amount": HintAmount.default,
|
"hint_amount": HintAmount.default,
|
||||||
"area_hint_percentage": AreaHintPercentage.default,
|
"area_hint_percentage": AreaHintPercentage.default,
|
||||||
|
"laser_hints": LaserHints.default,
|
||||||
"death_link": DeathLink.default,
|
"death_link": DeathLink.default,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue