Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The Witness: Laser Hints #2895

Merged
merged 4 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 27 additions & 19 deletions worlds/witness/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@
"""
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


Expand Down Expand Up @@ -66,7 +66,8 @@ def __init__(self, multiworld: "MultiWorld", player: int):
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 = []
Expand All @@ -81,6 +82,7 @@ def _get_slot_data(self):
'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],
Expand All @@ -100,8 +102,6 @@ def generate_early(self):
)
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
Expand Down Expand Up @@ -272,11 +272,25 @@ def create_items(self):
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
)

Expand All @@ -285,25 +299,19 @@ def fill_slot_data(self) -> dict:
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()
Expand All @@ -315,7 +323,7 @@ def fill_slot_data(self) -> dict:
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()

Expand Down
35 changes: 32 additions & 3 deletions worlds/witness/hints.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
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

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.",
Expand Down Expand Up @@ -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)"
}
Expand Down Expand Up @@ -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
7 changes: 7 additions & 0 deletions worlds/witness/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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
3 changes: 3 additions & 0 deletions worlds/witness/presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"puzzle_skip_amount": PuzzleSkipAmount.default,
"hint_amount": HintAmount.default,
"area_hint_percentage": AreaHintPercentage.default,
"laser_hints": LaserHints.default,
"death_link": DeathLink.default,
},

Expand Down Expand Up @@ -66,6 +67,7 @@
"puzzle_skip_amount": 15,
"hint_amount": HintAmount.default,
"area_hint_percentage": AreaHintPercentage.default,
"laser_hints": LaserHints.default,
"death_link": DeathLink.default,
},

Expand Down Expand Up @@ -99,6 +101,7 @@
"puzzle_skip_amount": 15,
"hint_amount": HintAmount.default,
"area_hint_percentage": AreaHintPercentage.default,
"laser_hints": LaserHints.default,
"death_link": DeathLink.default,
},
}
Loading