Convert dullahan to use the new API (#292)

* Convert dullahan to use the new API

* Address small nitpicks about dullahan

* Totally unimportant commit, please do not pay attention at all
This commit is contained in:
Emanuel Barry 2017-03-28 12:39:51 -04:00 committed by Ryan Schmidt
parent 322de008e7
commit 5d03ed414b
7 changed files with 127 additions and 130 deletions

View File

@ -336,10 +336,10 @@
"vengeful_ghost_villager": "You must target a villager.", "vengeful_ghost_villager": "You must target a villager.",
"wolf_no_target_wolf": "You may only kill villagers, not other wolves.", "wolf_no_target_wolf": "You may only kill villagers, not other wolves.",
"wolf_must_target_multiple": "You should select two different players.", "wolf_must_target_multiple": "You should select two different players.",
"wolf_target_multiple": " selected \u0002{0}\u0002 and \u0002{1}\u0002 to be killed.", "player_kill": "You have selected \u0002{0}\u0002 to be killed.",
"player": "You have{0}", "player_kill_multiple": "You have selected \u0002{0}\u0002 and \u0002{1}\u0002 to be killed.",
"wolfchat": "\u0002{0}\u0002 has{1}", "wolfchat_kill": "\u0002{0}\u0002 has selected \u0002{1}\u0002 to be killed.",
"wolf_target": " selected \u0002{0}\u0002 to be killed.", "wolfchat_kill_multiple": "\u0002{0}\u0002 has selected \u0002{1}\u0002 and \u0002{2}\u0002 to be killed.",
"wolf_target_second": "You are angry tonight and may kill a second target. Use \"kill <nick1> and <nick2>\" to select multiple targets.", "wolf_target_second": "You are angry tonight and may kill a second target. Use \"kill <nick1> and <nick2>\" to select multiple targets.",
"already_protecting": "You are already protecting someone tonight.", "already_protecting": "You are already protecting someone tonight.",
"guardian_target_another": "You protected \u0002{0}\u0002 last night. You cannot protect the same person two nights in a row.", "guardian_target_another": "You protected \u0002{0}\u0002 last night. You cannot protect the same person two nights in a row.",
@ -645,6 +645,8 @@
"retract_totem_succubus": "You discover that \u0002{0}\u0002 is a succubus and have retracted your totem as a result.", "retract_totem_succubus": "You discover that \u0002{0}\u0002 is a succubus and have retracted your totem as a result.",
"retract_hex_succubus": "You discover that \u0002{0}\u0002 is a succubus and have retracted your hex as a result.", "retract_hex_succubus": "You discover that \u0002{0}\u0002 is a succubus and have retracted your hex as a result.",
"dullahan_no_kill_succubus": "The succubus no longer needs to die for you to win.", "dullahan_no_kill_succubus": "The succubus no longer needs to die for you to win.",
"dullahan_to_kill": "need to kill {0}",
"dullahan_all_dead": "all targets dead",
"no_see_wolf": "Seeing another wolf would be a waste.", "no_see_wolf": "Seeing another wolf would be a waste.",
"doomsayer_death": "You have a vision that \u0002{0}\u0002 will meet an untimely end tonight.", "doomsayer_death": "You have a vision that \u0002{0}\u0002 will meet an untimely end tonight.",
"doomsayer_lycan": "You have a vision that \u0002{0}\u0002 is transforming into a savage beast tomorrow night.", "doomsayer_lycan": "You have a vision that \u0002{0}\u0002 is transforming into a savage beast tomorrow night.",

View File

@ -903,7 +903,7 @@ class SleepyMode(GameMode):
def dullahan_targets(self, evt, cli, var, dullahans, max_targets): def dullahan_targets(self, evt, cli, var, dullahans, max_targets):
for dull in dullahans: for dull in dullahans:
evt.data["targets"][dull] = set(var.ROLES["priest"]) evt.data["targets"][dull] = {users._get(x) for x in var.ROLES["priest"]}
def setup_nightmares(self, evt, cli, var): def setup_nightmares(self, evt, cli, var):
if random.random() < 1/5: if random.random() < 1/5:

View File

@ -3,78 +3,74 @@ import re
import random import random
from collections import defaultdict, deque from collections import defaultdict, deque
import src.settings as var
from src.utilities import * from src.utilities import *
from src import debuglog, errlog, plog from src.functions import get_players, get_target
from src.decorators import cmd, event_listener from src import users, debuglog, errlog, plog
from src.decorators import command, event_listener
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event
import botconfig import botconfig
KILLS = {} # type: Dict[str, str] KILLS = {} # type: Dict[users.User, users.User]
TARGETS = {} # type: Dict[str, Set[str]] TARGETS = {} # type: Dict[users.User, Set[users.User]]
@cmd("kill", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("dullahan",)) @command("kill", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("dullahan",))
def dullahan_kill(cli, nick, chan, rest): def dullahan_kill(var, wrapper, message):
"""Kill someone at night as a dullahan until everyone on your list is dead.""" """Kill someone at night as a dullahan until everyone on your list is dead."""
if not TARGETS[nick] & set(list_players()): if not TARGETS[wrapper.source] & set(get_players()):
pm(cli, nick, messages["dullahan_targets_dead"]) wrapper.pm(messages["dullahan_targets_dead"])
return return
victim = get_victim(cli, nick, re.split(" +",rest)[0], False) target = get_target(var, wrapper, re.split(" +", message)[0])
if not victim: if not target:
return return
if victim == nick: if target is wrapper.source:
pm(cli, nick, messages["no_suicide"]) wrapper.pm(messages["no_suicide"])
return return
orig = victim orig = target
evt = Event("targeted_command", {"target": victim, "misdirection": True, "exchange": True}) evt = Event("targeted_command", {"target": target.nick, "misdirection": True, "exchange": True})
evt.dispatch(cli, var, "kill", nick, victim, frozenset({"detrimental"})) evt.dispatch(wrapper.client, var, "kill", wrapper.source.nick, target.nick, frozenset({"detrimental"}))
if evt.prevent_default: if evt.prevent_default:
return return
victim = evt.data["target"] target = users._get(evt.data["target"]) # FIXME: Need to fix once targeted_command uses the new API
KILLS[nick] = victim KILLS[wrapper.source] = target
msg = messages["wolf_target"].format(orig) wrapper.pm(messages["player_kill"].format(orig))
pm(cli, nick, messages["player"].format(msg))
debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, get_role(nick), victim, get_role(victim))) debuglog("{0} (dullahan) KILL: {1} ({2})".format(wrapper.source, target, get_role(target.nick)))
chk_nightdone(cli) chk_nightdone(wrapper.client)
@cmd("retract", "r", chan=False, pm=True, playing=True, phases=("night",), roles=("dullahan",)) @command("retract", "r", chan=False, pm=True, playing=True, phases=("night",), roles=("dullahan",))
def dullahan_retract(cli, nick, chan, rest): def dullahan_retract(var, wrapper, message):
"""Removes a dullahan's kill selection.""" """Removes a dullahan's kill selection."""
if nick not in KILLS: if KILLS.pop(wrapper.source, None):
return wrapper.pm(messages["retracted_kill"])
if nick in KILLS:
del KILLS[nick]
pm(cli, nick, messages["retracted_kill"])
@event_listener("player_win") @event_listener("player_win")
def on_player_win(evt, var, user, role, winner, survived): def on_player_win(evt, var, user, role, winner, survived):
if role != "dullahan": if role != "dullahan":
return return
alive = set(list_players()) alive = set(get_players())
if not TARGETS[user.nick] & alive: if not TARGETS[user] & alive:
evt.data["iwon"] = True evt.data["iwon"] = True
@event_listener("del_player") @event_listener("del_player")
def on_del_player(evt, cli, var, nick, nickrole, nicktpls, death_triggers): def on_del_player(evt, cli, var, nick, nickrole, nicktpls, death_triggers):
for h, v in list(KILLS.items()): for h, v in list(KILLS.items()):
if v == nick: if v.nick == nick:
pm(cli, h, messages["hunter_discard"]) h.send(messages["hunter_discard"])
del KILLS[h] del KILLS[h]
elif h == nick: elif h.nick == nick:
del KILLS[h] del KILLS[h]
if death_triggers and nickrole == "dullahan": if death_triggers and nickrole == "dullahan":
pl = evt.data["pl"] pl = evt.data["pl"]
targets = TARGETS[nick] & set(pl) targets = TARGETS[users._get(nick)].intersection(users._get(x) for x in pl) # FIXME
if targets: if targets:
target = random.choice(list(targets)) target = random.choice(list(targets)).nick
prots = deque(var.ACTIVE_PROTECTIONS[target]) prots = deque(var.ACTIVE_PROTECTIONS[target])
aevt = Event("assassinate", {"pl": evt.data["pl"]}, aevt = Event("assassinate", {"pl": evt.data["pl"]},
del_player=evt.params.del_player, del_player=evt.params.del_player,
@ -103,87 +99,76 @@ def on_del_player(evt, cli, var, nick, nickrole, nicktpls, death_triggers):
evt.params.del_player(cli, target, True, end_game=False, killer_role=nickrole, deadlist=evt.params.deadlist, original=evt.params.original, ismain=False) evt.params.del_player(cli, target, True, end_game=False, killer_role=nickrole, deadlist=evt.params.deadlist, original=evt.params.original, ismain=False)
evt.data["pl"] = evt.params.refresh_pl(pl) evt.data["pl"] = evt.params.refresh_pl(pl)
@event_listener("rename_player")
def on_rename(evt, cli, var, prefix, nick):
kvp = []
for a,b in KILLS.items():
if a == prefix:
a = nick
if b == prefix:
b = nick
kvp.append((a,b))
KILLS.update(kvp)
if prefix in KILLS:
del KILLS[prefix]
kvp = []
for a,b in TARGETS.items():
nl = set()
for n in b:
if n == prefix:
n = nick
nl.add(n)
if a == prefix:
a = nick
kvp.append((a,nl))
TARGETS.update(kvp)
if prefix in TARGETS:
del TARGETS[prefix]
@event_listener("night_acted") @event_listener("night_acted")
def on_acted(evt, cli, var, nick, sender): def on_acted(evt, cli, var, nick, sender):
if nick in KILLS: if users._get(nick) in KILLS: # FIXME
evt.data["acted"] = True evt.data["acted"] = True
@event_listener("swap_player")
def on_swap(evt, var, old_user, user):
if old_user in KILLS:
KILLS[user] = KILLS.pop(old_user)
if old_user in TARGETS:
TARGETS[user] = TARGETS.pop(old_user)
for dullahan, target in KILLS.items():
if target is old_user:
KILLS[dullahan] = user
for dullahan, targets in TARGETS.items():
if old_user in targets:
targets.remove(old_user)
targets.add(user)
@event_listener("get_special") @event_listener("get_special")
def on_get_special(evt, cli, var): def on_get_special(evt, cli, var):
evt.data["special"].update(var.ROLES["dullahan"]) evt.data["special"].update(var.ROLES["dullahan"])
@event_listener("transition_day", priority=2) @event_listener("transition_day", priority=2)
def on_transition_day(evt, cli, var): def on_transition_day(evt, cli, var):
for k, d in list(KILLS.items()): while KILLS:
evt.data["victims"].append(d) k, d = KILLS.popitem()
evt.data["onlybywolves"].discard(d) evt.data["victims"].append(d.nick)
evt.data["killers"][d].append(k) evt.data["onlybywolves"].discard(d.nick)
del KILLS[k] evt.data["killers"][d.nick].append(k.nick)
@event_listener("exchange_roles") @event_listener("exchange_roles")
def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role): def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role):
if actor in KILLS: for k in set(KILLS):
del KILLS[actor] if k.nick == actor or k.nick == nick:
if nick in KILLS: del KILLS[k]
del KILLS[nick]
if actor_role == "dullahan" and nick_role != "dullahan" and actor in TARGETS: for k in set(TARGETS):
TARGETS[nick] = TARGETS[actor] - {nick} if actor_role == "dullahan" and nick_role != "dullahan" and k.nick == actor:
del TARGETS[actor] TARGETS[users._get(nick)] = TARGETS.pop(k) - {users._get(nick)} # FIXME
elif nick_role == "dullahan" and actor_role != "dullahan" and nick in TARGETS: elif nick_role == "dullahan" and actor_role != "dullahan" and k.nick == nick:
TARGETS[actor] = TARGETS[nick] - {actor} TARGET[users._get(actor)] = TARGETS.pop(k) - {users._get(actor)} # FIXME
del TARGETS[nick]
@event_listener("chk_nightdone") @event_listener("chk_nightdone")
def on_chk_nightdone(evt, cli, var): def on_chk_nightdone(evt, cli, var):
spl = set(list_players()) spl = set(get_players())
evt.data["actedcount"] += len(KILLS) evt.data["actedcount"] += len(KILLS)
for p in var.ROLES["dullahan"]: for d, targets in TARGETS.items():
if TARGETS[p] & spl: if targets & spl:
evt.data["nightroles"].append(p) evt.data["nightroles"].append(dullahan)
@event_listener("transition_night_end", priority=2) @event_listener("transition_night_end", priority=2)
def on_transition_night_end(evt, cli, var): def on_transition_night_end(evt, cli, var):
for dullahan in var.ROLES["dullahan"]: for dullahan in var.ROLES["dullahan"]:
targets = list(TARGETS[dullahan]) targets = list(TARGETS[users._get(dullahan)])
for target in var.DEAD: for target in targets[:]:
if target in targets: if target.nick in var.DEAD:
targets.remove(target) targets.remove(target) # FIXME: Update when var.DEAD holds User instances
if not targets: # already all dead if not targets: # already all dead
pm(cli, dullahan, "{0} {1}".format(messages["dullahan_simple"], messages["dullahan_targets_dead"])) dullahan.send("{0} {1}".format(messages["dullahan_simple"], messages["dullahan_targets_dead"]))
continue continue
random.shuffle(targets) random.shuffle(targets)
if dullahan in var.PLAYERS and not is_user_simple(dullahan): if dullahan.prefers_simple():
pm(cli, dullahan, messages["dullahan_notify"]) dullahan.send(messages["dullahan_simple"])
else: else:
pm(cli, dullahan, messages["dullahan_simple"]) dullahan.send(messages["dullahan_notify"])
t = messages["dullahan_targets"] if var.FIRST_NIGHT else messages["dullahan_remaining_targets"] t = messages["dullahan_targets"] if var.FIRST_NIGHT else messages["dullahan_remaining_targets"]
pm(cli, dullahan, t + ", ".join(targets)) dullahan.send(t + ", ".join(t.nick for t in targets))
@event_listener("role_assignment") @event_listener("role_assignment")
def on_role_assignment(evt, cli, var, gamemode, pl, restart): def on_role_assignment(evt, cli, var, gamemode, pl, restart):
@ -191,12 +176,14 @@ def on_role_assignment(evt, cli, var, gamemode, pl, restart):
if var.ROLES["dullahan"]: if var.ROLES["dullahan"]:
max_targets = math.ceil(8.1 * math.log(len(pl), 10) - 5) max_targets = math.ceil(8.1 * math.log(len(pl), 10) - 5)
for dull in var.ROLES["dullahan"]: for dull in var.ROLES["dullahan"]:
TARGETS[dull] = set() TARGETS[users._get(dull)] = set() # FIXME
dull_targets = Event("dullahan_targets", {"targets": TARGETS}) # support sleepy dull_targets = Event("dullahan_targets", {"targets": TARGETS}) # support sleepy
dull_targets.dispatch(cli, var, var.ROLES["dullahan"], max_targets) dull_targets.dispatch(cli, var, {users._get(x) for x in var.ROLES["dullahan"]}, max_targets) # FIXME
players = [users._get(x) for x in pl] # FIXME
for dull, ts in TARGETS.items(): for dull, ts in TARGETS.items():
ps = pl[:] ps = players[:]
ps.remove(dull) ps.remove(dull)
while len(ts) < max_targets: while len(ts) < max_targets:
target = random.choice(ps) target = random.choice(ps)
@ -205,37 +192,46 @@ def on_role_assignment(evt, cli, var, gamemode, pl, restart):
@event_listener("succubus_visit") @event_listener("succubus_visit")
def on_succubus_visit(evt, cli, var, nick, victim): def on_succubus_visit(evt, cli, var, nick, victim):
if victim in TARGETS and TARGETS[victim] & var.ROLES["succubus"]: user = users._get(victim) # FIXME
TARGETS.difference_update(var.ROLES["succubus"]) if user in TARGETS:
succ_target = False
for target in set(TARGETS[user]):
if target.nick in var.ROLES["succubus"]:
TARGETS[user].remove(target)
succ_target = True
if succ_target:
pm(cli, victim, messages["dullahan_no_kill_succubus"]) pm(cli, victim, messages["dullahan_no_kill_succubus"])
if KILLS.get(victim) in var.ROLES["succubus"]: if user in KILLS and KILLS[user].nick in var.ROLES["succubus"]:
pm(cli, victim, messages["no_kill_succubus"].format(KILLS[victim])) pm(cli, victim, messages["no_kill_succubus"].format(KILLS[user]))
del KILLS[victim] del KILLS[user]
@event_listener("myrole") @event_listener("myrole")
def on_myrole(evt, cli, var, nick): def on_myrole(evt, cli, var, nick):
role = get_role(nick)
# Remind dullahans of their targets # Remind dullahans of their targets
if role == "dullahan": if nick in var.ROLES["dullahan"]:
targets = list(TARGETS[nick]) targets = list(TARGETS[users._get(nick)]) # FIXME
for target in var.DEAD: for target in list(targets):
if target in targets: if target.nick in var.DEAD:
targets.remove(target) targets.remove(target)
random.shuffle(targets) random.shuffle(targets)
if targets: if targets:
t = messages["dullahan_targets"] if var.FIRST_NIGHT else messages["dullahan_remaining_targets"] t = messages["dullahan_targets"] if var.FIRST_NIGHT else messages["dullahan_remaining_targets"]
evt.data["messages"].append(t + ", ".join(targets)) evt.data["messages"].append(t + ", ".join(t.nick for t in targets))
else: else:
evt.data["messages"].append(messages["dullahan_targets_dead"]) evt.data["messages"].append(messages["dullahan_targets_dead"])
@event_listener("revealroles_role") @event_listener("revealroles_role")
def on_revealroles_role(evt, var, wrapper, nickname, role): def on_revealroles_role(evt, var, wrapper, nickname, role):
if role == "dullahan" and nickname in TARGETS: user = users._get(nickname) # FIXME
targets = TARGETS[nickname] - var.DEAD if role == "dullahan" and user in TARGETS: # FIXME
targets = set(TARGETS[user])
for target in TARGETS[user]:
if target.nick in var.DEAD:
targets.remove(target)
if targets: if targets:
evt.data["special_case"].append("need to kill {0}".format(", ".join(TARGETS[nickname] - var.DEAD))) evt.data["special_case"].append(messages["dullahan_to_kill"].format(", ".join(t.nick for t in targets)))
else: else:
evt.data["special_case"].append("All targets dead") evt.data["special_case"].append(messages["dullahan_all_dead"])
@event_listener("begin_day") @event_listener("begin_day")
def on_begin_day(evt, cli, var): def on_begin_day(evt, cli, var):
@ -251,8 +247,11 @@ def on_get_role_metadata(evt, cli, var, kind):
if kind == "night_kills": if kind == "night_kills":
num = 0 num = 0
for dull in var.ROLES["dullahan"]: for dull in var.ROLES["dullahan"]:
if TARGETS[dull] - var.DEAD: user = users._get(dull) # FIXME
for target in TARGETS[user]:
if target.nick not in var.DEAD:
num += 1 num += 1
break
evt.data["dullahan"] = num evt.data["dullahan"] = num
# vim: set sw=4 expandtab: # vim: set sw=4 expandtab:

View File

@ -38,8 +38,7 @@ def hunter_kill(cli, nick, chan, rest):
HUNTERS.add(nick) HUNTERS.add(nick)
PASSED.discard(nick) PASSED.discard(nick)
msg = messages["wolf_target"].format(orig) pm(cli, nick, messages["player_kill"].format(orig))
pm(cli, nick, messages["player"].format(msg))
debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, get_role(nick), victim, get_role(victim))) debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, get_role(nick), victim, get_role(victim)))
chk_nightdone(cli) chk_nightdone(cli)

View File

@ -47,8 +47,7 @@ def vg_kill(var, wrapper, message):
KILLS[wrapper.source.nick] = victim KILLS[wrapper.source.nick] = victim
msg = messages["wolf_target"].format(orig) wrapper.pm(messages["player_kill"].format(orig))
wrapper.pm(messages["player"].format(msg))
debuglog("{0} ({1}) KILL: {2} ({3})".format(wrapper.source.nick, get_role(wrapper.source.nick), victim, get_role(victim))) debuglog("{0} ({1}) KILL: {2} ({3})".format(wrapper.source.nick, get_role(wrapper.source.nick), victim, get_role(victim)))
chk_nightdone(wrapper.source.client) chk_nightdone(wrapper.source.client)

View File

@ -33,8 +33,7 @@ def vigilante_kill(cli, nick, chan, rest):
KILLS[nick] = victim KILLS[nick] = victim
PASSED.discard(nick) PASSED.discard(nick)
msg = messages["wolf_target"].format(orig) pm(cli, nick, messages["player_kill"].format(orig))
pm(cli, nick, messages["player"].format(msg))
debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, get_role(nick), victim, get_role(victim))) debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, get_role(nick), victim, get_role(victim)))

View File

@ -68,19 +68,18 @@ def wolf_kill(cli, nick, chan, rest):
KILLS[nick] = victims KILLS[nick] = victims
if len(orig) > 1: if len(orig) > 1:
# need to expand this eventually (only accomodates 2 kills, whereas we should ideally support arbitrarily many) # need to expand this eventually (only accomodates 2 kills, whereas we should ideally support arbitrarily many)
msg = messages["wolf_target_multiple"].format(orig[0], orig[1]) pm(cli, nick, messages["player_kill_multiple"].format(orig[0], orig[1]))
pm(cli, nick, messages["player"].format(msg)) msg = messages["wolfchat_kill_multiple"].format(nick, orig[0], orig[1])
debuglog("{0} ({1}) KILL: {2} ({3}) and {4} ({5})".format(nick, role, victims[0], get_role(victims[0]), victims[1], get_role(victims[1]))) debuglog("{0} ({1}) KILL: {2} ({3}) and {4} ({5})".format(nick, role, victims[0], get_role(victims[0]), victims[1], get_role(victims[1])))
else: else:
msg = messages["wolf_target"].format(orig[0]) pm(cli, nick, messages["player_kill"].format(orig[0]))
pm(cli, nick, messages["player"].format(msg)) msg = messages["wolfchat_kill"].format(nick, orig[0])
if num_kills > 1: if num_kills > 1:
pm(cli, nick, messages["wolf_target_second"]) pm(cli, nick, messages["wolf_target_second"])
debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, role, victims[0], get_role(victims[0]))) debuglog("{0} ({1}) KILL: {2} ({3})".format(nick, role, victims[0], get_role(victims[0])))
if in_wolflist(nick, nick): if in_wolflist(nick, nick):
relay_wolfchat_command(cli, nick, messages["wolfchat"].format(nick, msg), var.WOLF_ROLES, is_wolf_command=True, is_kill_command=True) relay_wolfchat_command(cli, nick, msg, var.WOLF_ROLES, is_wolf_command=True, is_kill_command=True)
chk_nightdone(cli) chk_nightdone(cli)
@cmd("retract", "r", chan=False, pm=True, playing=True, phases=("night",)) @cmd("retract", "r", chan=False, pm=True, playing=True, phases=("night",))