MultiServer: allow using IDs for hints
This commit is contained in:
parent
809bda02d1
commit
6d5ddf3cad
181
MultiServer.py
181
MultiServer.py
|
@ -222,11 +222,11 @@ class Context:
|
||||||
self.all_item_and_group_names[game_name] = \
|
self.all_item_and_group_names[game_name] = \
|
||||||
set(game_package["item_name_to_id"]) | set(self.item_name_groups[game_name])
|
set(game_package["item_name_to_id"]) | set(self.item_name_groups[game_name])
|
||||||
|
|
||||||
def item_names_for_game(self, game: str) -> typing.Dict[str, int]:
|
def item_names_for_game(self, game: str) -> typing.Optional[typing.Dict[str, int]]:
|
||||||
return self.gamespackage[game]["item_name_to_id"]
|
return self.gamespackage[game]["item_name_to_id"] if game in self.gamespackage else None
|
||||||
|
|
||||||
def location_names_for_game(self, game: str) -> typing.Dict[str, int]:
|
def location_names_for_game(self, game: str) -> typing.Optional[typing.Dict[str, int]]:
|
||||||
return self.gamespackage[game]["location_name_to_id"]
|
return self.gamespackage[game]["location_name_to_id"] if game in self.gamespackage else None
|
||||||
|
|
||||||
# General networking
|
# General networking
|
||||||
async def send_msgs(self, endpoint: Endpoint, msgs: typing.Iterable[dict]) -> bool:
|
async def send_msgs(self, endpoint: Endpoint, msgs: typing.Iterable[dict]) -> bool:
|
||||||
|
@ -901,14 +901,14 @@ def register_location_checks(ctx: Context, team: int, slot: int, locations: typi
|
||||||
ctx.save()
|
ctx.save()
|
||||||
|
|
||||||
|
|
||||||
def collect_hints(ctx: Context, team: int, slot: int, item_name: str) -> typing.List[NetUtils.Hint]:
|
def collect_hints(ctx: Context, team: int, slot: int, item: typing.Union[int, str]) -> typing.List[NetUtils.Hint]:
|
||||||
hints = []
|
hints = []
|
||||||
slots: typing.Set[int] = {slot}
|
slots: typing.Set[int] = {slot}
|
||||||
for group_id, group in ctx.groups.items():
|
for group_id, group in ctx.groups.items():
|
||||||
if slot in group:
|
if slot in group:
|
||||||
slots.add(group_id)
|
slots.add(group_id)
|
||||||
|
|
||||||
seeked_item_id = ctx.item_names_for_game(ctx.games[slot])[item_name]
|
seeked_item_id = item if isinstance(item, int) else ctx.item_names_for_game(ctx.games[slot])[item]
|
||||||
for finding_player, check_data in ctx.locations.items():
|
for finding_player, check_data in ctx.locations.items():
|
||||||
for location_id, (item_id, receiving_player, item_flags) in check_data.items():
|
for location_id, (item_id, receiving_player, item_flags) in check_data.items():
|
||||||
if receiving_player in slots and item_id == seeked_item_id:
|
if receiving_player in slots and item_id == seeked_item_id:
|
||||||
|
@ -1336,13 +1336,33 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||||
self.output(f"A hint costs {self.ctx.get_hint_cost(self.client.slot)} points. "
|
self.output(f"A hint costs {self.ctx.get_hint_cost(self.client.slot)} points. "
|
||||||
f"You have {points_available} points.")
|
f"You have {points_available} points.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
elif input_text.isnumeric():
|
||||||
|
game = self.ctx.games[self.client.slot]
|
||||||
|
hint_id = int(input_text)
|
||||||
|
hint_name = self.ctx.item_names[hint_id] \
|
||||||
|
if not for_location and hint_id in self.ctx.item_names \
|
||||||
|
else self.ctx.location_names[hint_id] \
|
||||||
|
if for_location and hint_id in self.ctx.location_names \
|
||||||
|
else None
|
||||||
|
if hint_name in self.ctx.non_hintable_names[game]:
|
||||||
|
self.output(f"Sorry, \"{hint_name}\" is marked as non-hintable.")
|
||||||
|
hints = []
|
||||||
|
elif not for_location:
|
||||||
|
hints = collect_hints(self.ctx, self.client.team, self.client.slot, hint_id)
|
||||||
|
else:
|
||||||
|
hints = collect_hint_location_id(self.ctx, self.client.team, self.client.slot, hint_id)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
game = self.ctx.games[self.client.slot]
|
game = self.ctx.games[self.client.slot]
|
||||||
|
if game not in self.ctx.all_item_and_group_names:
|
||||||
|
self.output("Can't look up item/location for unknown game. Hint for ID instead.")
|
||||||
|
return False
|
||||||
names = self.ctx.location_names_for_game(game) \
|
names = self.ctx.location_names_for_game(game) \
|
||||||
if for_location else \
|
if for_location else \
|
||||||
self.ctx.all_item_and_group_names[game]
|
self.ctx.all_item_and_group_names[game]
|
||||||
hint_name, usable, response = get_intended_text(input_text,
|
hint_name, usable, response = get_intended_text(input_text, names)
|
||||||
names)
|
|
||||||
if usable:
|
if usable:
|
||||||
if hint_name in self.ctx.non_hintable_names[game]:
|
if hint_name in self.ctx.non_hintable_names[game]:
|
||||||
self.output(f"Sorry, \"{hint_name}\" is marked as non-hintable.")
|
self.output(f"Sorry, \"{hint_name}\" is marked as non-hintable.")
|
||||||
|
@ -1356,63 +1376,65 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||||
hints = collect_hints(self.ctx, self.client.team, self.client.slot, hint_name)
|
hints = collect_hints(self.ctx, self.client.team, self.client.slot, hint_name)
|
||||||
else: # location name
|
else: # location name
|
||||||
hints = collect_hint_location_name(self.ctx, self.client.team, self.client.slot, hint_name)
|
hints = collect_hint_location_name(self.ctx, self.client.team, self.client.slot, hint_name)
|
||||||
cost = self.ctx.get_hint_cost(self.client.slot)
|
|
||||||
if hints:
|
|
||||||
new_hints = set(hints) - self.ctx.hints[self.client.team, self.client.slot]
|
|
||||||
old_hints = set(hints) - new_hints
|
|
||||||
if old_hints:
|
|
||||||
notify_hints(self.ctx, self.client.team, list(old_hints))
|
|
||||||
if not new_hints:
|
|
||||||
self.output("Hint was previously used, no points deducted.")
|
|
||||||
if new_hints:
|
|
||||||
found_hints = [hint for hint in new_hints if hint.found]
|
|
||||||
not_found_hints = [hint for hint in new_hints if not hint.found]
|
|
||||||
|
|
||||||
if not not_found_hints: # everything's been found, no need to pay
|
|
||||||
can_pay = 1000
|
|
||||||
elif cost:
|
|
||||||
can_pay = int((points_available // cost) > 0) # limit to 1 new hint per call
|
|
||||||
else:
|
|
||||||
can_pay = 1000
|
|
||||||
|
|
||||||
self.ctx.random.shuffle(not_found_hints)
|
|
||||||
# By popular vote, make hints prefer non-local placements
|
|
||||||
not_found_hints.sort(key=lambda hint: int(hint.receiving_player != hint.finding_player))
|
|
||||||
|
|
||||||
hints = found_hints
|
|
||||||
while can_pay > 0:
|
|
||||||
if not not_found_hints:
|
|
||||||
break
|
|
||||||
hint = not_found_hints.pop()
|
|
||||||
hints.append(hint)
|
|
||||||
can_pay -= 1
|
|
||||||
self.ctx.hints_used[self.client.team, self.client.slot] += 1
|
|
||||||
points_available = get_client_points(self.ctx, self.client)
|
|
||||||
|
|
||||||
if not_found_hints:
|
|
||||||
if hints and cost and int((points_available // cost) == 0):
|
|
||||||
self.output(
|
|
||||||
f"There may be more hintables, however, you cannot afford to pay for any more. "
|
|
||||||
f" You have {points_available} and need at least "
|
|
||||||
f"{self.ctx.get_hint_cost(self.client.slot)}.")
|
|
||||||
elif hints:
|
|
||||||
self.output(
|
|
||||||
"There may be more hintables, you can rerun the command to find more.")
|
|
||||||
else:
|
|
||||||
self.output(f"You can't afford the hint. "
|
|
||||||
f"You have {points_available} points and need at least "
|
|
||||||
f"{self.ctx.get_hint_cost(self.client.slot)}.")
|
|
||||||
notify_hints(self.ctx, self.client.team, hints)
|
|
||||||
self.ctx.save()
|
|
||||||
return True
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.output("Nothing found. Item/Location may not exist.")
|
|
||||||
return False
|
|
||||||
else:
|
else:
|
||||||
self.output(response)
|
self.output(response)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if hints:
|
||||||
|
cost = self.ctx.get_hint_cost(self.client.slot)
|
||||||
|
new_hints = set(hints) - self.ctx.hints[self.client.team, self.client.slot]
|
||||||
|
old_hints = set(hints) - new_hints
|
||||||
|
if old_hints:
|
||||||
|
notify_hints(self.ctx, self.client.team, list(old_hints))
|
||||||
|
if not new_hints:
|
||||||
|
self.output("Hint was previously used, no points deducted.")
|
||||||
|
if new_hints:
|
||||||
|
found_hints = [hint for hint in new_hints if hint.found]
|
||||||
|
not_found_hints = [hint for hint in new_hints if not hint.found]
|
||||||
|
|
||||||
|
if not not_found_hints: # everything's been found, no need to pay
|
||||||
|
can_pay = 1000
|
||||||
|
elif cost:
|
||||||
|
can_pay = int((points_available // cost) > 0) # limit to 1 new hint per call
|
||||||
|
else:
|
||||||
|
can_pay = 1000
|
||||||
|
|
||||||
|
self.ctx.random.shuffle(not_found_hints)
|
||||||
|
# By popular vote, make hints prefer non-local placements
|
||||||
|
not_found_hints.sort(key=lambda hint: int(hint.receiving_player != hint.finding_player))
|
||||||
|
|
||||||
|
hints = found_hints
|
||||||
|
while can_pay > 0:
|
||||||
|
if not not_found_hints:
|
||||||
|
break
|
||||||
|
hint = not_found_hints.pop()
|
||||||
|
hints.append(hint)
|
||||||
|
can_pay -= 1
|
||||||
|
self.ctx.hints_used[self.client.team, self.client.slot] += 1
|
||||||
|
points_available = get_client_points(self.ctx, self.client)
|
||||||
|
|
||||||
|
if not_found_hints:
|
||||||
|
if hints and cost and int((points_available // cost) == 0):
|
||||||
|
self.output(
|
||||||
|
f"There may be more hintables, however, you cannot afford to pay for any more. "
|
||||||
|
f" You have {points_available} and need at least "
|
||||||
|
f"{self.ctx.get_hint_cost(self.client.slot)}.")
|
||||||
|
elif hints:
|
||||||
|
self.output(
|
||||||
|
"There may be more hintables, you can rerun the command to find more.")
|
||||||
|
else:
|
||||||
|
self.output(f"You can't afford the hint. "
|
||||||
|
f"You have {points_available} points and need at least "
|
||||||
|
f"{self.ctx.get_hint_cost(self.client.slot)}.")
|
||||||
|
notify_hints(self.ctx, self.client.team, hints)
|
||||||
|
self.ctx.save()
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.output("Nothing found. Item/Location may not exist.")
|
||||||
|
return False
|
||||||
|
|
||||||
@mark_raw
|
@mark_raw
|
||||||
def _cmd_hint(self, item_name: str = "") -> bool:
|
def _cmd_hint(self, item_name: str = "") -> bool:
|
||||||
"""Use !hint {item_name},
|
"""Use !hint {item_name},
|
||||||
|
@ -1860,17 +1882,25 @@ class ServerCommandProcessor(CommonCommandProcessor):
|
||||||
seeked_player, usable, response = get_intended_text(player_name, self.ctx.player_names.values())
|
seeked_player, usable, response = get_intended_text(player_name, self.ctx.player_names.values())
|
||||||
if usable:
|
if usable:
|
||||||
team, slot = self.ctx.player_name_lookup[seeked_player]
|
team, slot = self.ctx.player_name_lookup[seeked_player]
|
||||||
item_name = " ".join(item_name)
|
|
||||||
game = self.ctx.games[slot]
|
game = self.ctx.games[slot]
|
||||||
item_name, usable, response = get_intended_text(item_name, self.ctx.all_item_and_group_names[game])
|
full_name = " ".join(item_name)
|
||||||
|
|
||||||
|
if full_name.isnumeric():
|
||||||
|
item, usable, response = int(full_name), True, None
|
||||||
|
elif game in self.ctx.all_item_and_group_names:
|
||||||
|
item, usable, response = get_intended_text(full_name, self.ctx.all_item_and_group_names[game])
|
||||||
|
else:
|
||||||
|
self.output("Can't look up item for unknown game. Hint for ID instead.")
|
||||||
|
return False
|
||||||
|
|
||||||
if usable:
|
if usable:
|
||||||
if item_name in self.ctx.item_name_groups[game]:
|
if game in self.ctx.item_name_groups and item in self.ctx.item_name_groups[game]:
|
||||||
hints = []
|
hints = []
|
||||||
for item_name_from_group in self.ctx.item_name_groups[game][item_name]:
|
for item_name_from_group in self.ctx.item_name_groups[game][item]:
|
||||||
if item_name_from_group in self.ctx.item_names_for_game(game): # ensure item has an ID
|
if item_name_from_group in self.ctx.item_names_for_game(game): # ensure item has an ID
|
||||||
hints.extend(collect_hints(self.ctx, team, slot, item_name_from_group))
|
hints.extend(collect_hints(self.ctx, team, slot, item_name_from_group))
|
||||||
else: # item name
|
else: # item name or id
|
||||||
hints = collect_hints(self.ctx, team, slot, item_name)
|
hints = collect_hints(self.ctx, team, slot, item)
|
||||||
|
|
||||||
if hints:
|
if hints:
|
||||||
notify_hints(self.ctx, team, hints)
|
notify_hints(self.ctx, team, hints)
|
||||||
|
@ -1891,11 +1921,22 @@ class ServerCommandProcessor(CommonCommandProcessor):
|
||||||
seeked_player, usable, response = get_intended_text(player_name, self.ctx.player_names.values())
|
seeked_player, usable, response = get_intended_text(player_name, self.ctx.player_names.values())
|
||||||
if usable:
|
if usable:
|
||||||
team, slot = self.ctx.player_name_lookup[seeked_player]
|
team, slot = self.ctx.player_name_lookup[seeked_player]
|
||||||
location_name = " ".join(location_name)
|
game = self.ctx.games[slot]
|
||||||
location_name, usable, response = get_intended_text(location_name,
|
full_name = " ".join(location_name)
|
||||||
self.ctx.location_names_for_game(self.ctx.games[slot]))
|
|
||||||
|
if full_name.isnumeric():
|
||||||
|
location, usable, response = int(full_name), True, None
|
||||||
|
elif self.ctx.location_names_for_game(game) is not None:
|
||||||
|
location, usable, response = get_intended_text(full_name, self.ctx.location_names_for_game(game))
|
||||||
|
else:
|
||||||
|
self.output("Can't look up location for unknown game. Hint for ID instead.")
|
||||||
|
return False
|
||||||
|
|
||||||
if usable:
|
if usable:
|
||||||
hints = collect_hint_location_name(self.ctx, team, slot, location_name)
|
if isinstance(location, int):
|
||||||
|
hints = collect_hint_location_id(self.ctx, team, slot, location)
|
||||||
|
else:
|
||||||
|
hints = collect_hint_location_name(self.ctx, team, slot, location)
|
||||||
if hints:
|
if hints:
|
||||||
notify_hints(self.ctx, team, hints)
|
notify_hints(self.ctx, team, hints)
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in New Issue