Allow pipers to pass or change targets (#322)

Also adds some more info to revealroles. Some miscellaneous fixes included.
This commit is contained in:
Ammon Smith 2018-04-25 20:08:39 -07:00 committed by Em Barry
parent 1d2ab6a2cc
commit 7d83dc4d8e
9 changed files with 66 additions and 21 deletions

View File

@ -415,6 +415,7 @@
"warlock_pass": "You have chosen not to curse anyone tonight.",
"warlock_pass_wolfchat": "\u0002{0}\u0002 has chosen not to curse anyone tonight.",
"piper_pass": "You have chosen not to charm anyone tonight.",
"piper_retract": "You have retracted your decision to charm.",
"turncoat_already_turned": "You have changed sides yesterday night, and may not do so again tonight.",
"turncoat_error": "Please specify which team you wish to side with, villagers or wolves.",
"turncoat_success": "You are now siding with \u0002{0}\u0002.",
@ -540,6 +541,8 @@
"assassin_simple": "You are an \u0002assassin\u0002.",
"piper_notify": "You are a \u0002piper\u0002. You can select up to two players to charm each night. The charmed players will know each other, but not who charmed them. You win when all other players are charmed. Use \"charm <nick1> and <nick2>\" to select the players to charm, or \"charm <nick>\" to charm just one player.",
"piper_simple": "You are a \u0002piper\u0002.",
"piper_revealroles_charmed": "\u0002charmed players\u0002: {0}",
"piper_revealroles_charming": "charming {0}",
"turncoat_notify": "You are a \u0002turncoat\u0002. You can change which team you're siding with every other night. Use \"side villagers\" or \"side wolves\" to select your team. ",
"turncoat_current_team": "You are currently siding with \u0002{0}\u0002.",
"turncoat_no_team": "If you die before selecting a side, you will not win.",

View File

@ -16,7 +16,7 @@ INVESTIGATED = UserSet()
@command("id", chan=False, pm=True, playing=True, silenced=True, phases=("day",), roles=("detective",))
def investigate(var, wrapper, message):
"""Investigate a player to determine their exact role."""
if wrapper.source in INVESTIGATED:
if wrapper.source in INVESTIGATED:
wrapper.send(messages["already_investigated"])
return
@ -38,7 +38,7 @@ def investigate(var, wrapper, message):
INVESTIGATED.add(wrapper.source)
wrapper.send(messages["investigate_success"].format(target, targrole))
debuglog("{0} (detective) ID: {1} ({2})".format(wrapper.source, target, targrole))
if random.random() < var.DETECTIVE_REVEALED_CHANCE: # a 2/5 chance (should be changeable in settings)
# The detective's identity is compromised!
wcroles = var.WOLFCHAT_ROLES

View File

@ -44,6 +44,7 @@ def dullahan_retract(var, wrapper, message):
"""Removes a dullahan's kill selection."""
if KILLS.pop(wrapper.source, None):
wrapper.pm(messages["retracted_kill"])
debuglog("{0} (dullahan) RETRACT".format(wrapper.source))
@event_listener("player_win")
def on_player_win(evt, var, user, role, winner, survived):
@ -129,7 +130,7 @@ def on_exchange(evt, var, actor, target, actor_role, target_role):
if actor_role == "dullahan" and target_role != "dullahan" and k is actor:
TARGETS[target] = TARGETS.pop(k) - {target}
elif target_role == "dullahan" and actor_role != "dullahan" and k is target:
TARGET[actor] = TARGETS.pop(k) - {actor}
TARGETS[actor] = TARGETS.pop(k) - {actor}
@event_listener("chk_nightdone")
def on_chk_nightdone(evt, var):
@ -205,7 +206,7 @@ def on_revealroles_role(evt, var, user, role):
for target in TARGETS[user]:
if target.nick in var.DEAD:
targets.remove(target)
if targets:
if targets:
evt.data["special_case"].append(messages["dullahan_to_kill"].format(", ".join(t.nick for t in targets)))
else:
evt.data["special_case"].append(messages["dullahan_all_dead"])

View File

@ -45,10 +45,13 @@ def hunter_retract(var, wrapper, message):
"""Removes a hunter's kill selection."""
if wrapper.source not in KILLS and wrapper.source not in PASSED:
return
KILLS.pop(wrapper.source, None)
del KILLS[:wrapper.source:]
HUNTERS.discard(wrapper.source)
PASSED.discard(wrapper.source)
wrapper.pm(messages["retracted_kill"])
debuglog("{0} (hunter) RETRACT".format(wrapper.source))
@command("pass", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("hunter",))
def hunter_pass(var, wrapper, message):
@ -56,7 +59,8 @@ def hunter_pass(var, wrapper, message):
if wrapper.source in HUNTERS and wrapper.source not in KILLS:
wrapper.pm(messages["hunter_already_killed"])
return
KILLS.pop(wrapper.source, None)
del KILLS[:wrapper.source:]
HUNTERS.discard(wrapper.source)
PASSED.add(wrapper.source)
wrapper.pm(messages["hunter_pass"])

View File

@ -46,7 +46,6 @@ def _get_targets(var, pl, user):
return (target1, target2)
@event_listener("del_player")
def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
if not death_triggers or "mad scientist" not in allroles:

View File

@ -15,10 +15,11 @@ from src.events import Event
TOBECHARMED = UserDict() # type: Dict[users.User, Set[users.User]]
CHARMED = UserSet() # type: Set[users.User]
PASSED = UserSet() # type: Set[users.User]
@command("charm", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("piper",))
def charm(var, wrapper, message):
"""Charm a player, slowly leading to your win!"""
"""Charm a player or two, slowly leading to your win!"""
pieces = re.split(" +", message)
target1 = pieces[0]
if len(pieces) > 1:
@ -75,6 +76,7 @@ def charm(var, wrapper, message):
TOBECHARMED[wrapper.source] = UserSet()
TOBECHARMED[wrapper.source].update({target1, target2} - {None})
PASSED.discard(wrapper.source)
if orig2:
debuglog("{0} (piper) CHARM {1} ({2}) && {3} ({4})".format(wrapper.source,
@ -85,6 +87,25 @@ def charm(var, wrapper, message):
debuglog("{0} (piper) CHARM {1} ({2})".format(wrapper.source, target1, get_main_role(target1)))
wrapper.send(messages["charm_success"].format(orig1))
@command("pass", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("piper",))
def pass_cmd(var, wrapper, message):
"""Do not charm anyone tonight."""
del TOBECHARMED[:wrapper.source:]
PASSED.add(wrapper.source)
wrapper.send(messages["piper_pass"])
debuglog("{0} (piper) PASS".format(wrapper.source))
@command("retract", "r", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("piper",))
def retract(var, wrapper, message):
"""Remove your decision to charm people."""
if wrapper.source in TOBECHARMED or wrapper.source in PASSED:
del TOBECHARMED[:wrapper.source:]
PASSED.discard(wrapper.source)
wrapper.send(messages["piper_retract"])
debuglog("{0} (piper) RETRACT".format(wrapper.source))
@event_listener("chk_win", priority=2)
def on_chk_win(evt, var, rolemap, mainroles, lpl, lwolves, lrealwolves):
# lpl doesn't included wounded/sick people or consecrating priests
@ -113,9 +134,7 @@ def on_player_win(evt, var, player, mainrole, winner, survived):
@event_listener("del_player")
def on_del_player(evt, var, player, mainrole, allroles, death_triggers):
CHARMED.discard(player)
x = TOBECHARMED.pop(player, None)
if x is not None:
x.clear()
del TOBECHARMED[:player:]
@event_listener("transition_day_begin")
def on_transition_day_begin(evt, var):
@ -157,10 +176,11 @@ def on_transition_day_begin(evt, var):
CHARMED.update(tocharm)
TOBECHARMED.clear()
PASSED.clear()
@event_listener("chk_nightdone")
def on_chk_nightdone(evt, var):
evt.data["actedcount"] += len(TOBECHARMED.keys())
evt.data["actedcount"] += len(TOBECHARMED) + len(PASSED)
evt.data["nightroles"].extend(get_all_players(("piper",)))
@event_listener("transition_night_end", priority=2)
@ -180,8 +200,14 @@ def on_exchange(evt, var, actor, target, actor_role, target_role):
# if we're shifting piper around, ensure that the new piper isn't charmed
if actor_role == "piper":
CHARMED.discard(target)
if target_role != "piper":
del TOBECHARMED[:actor:]
PASSED.discard(actor)
if target_role == "piper":
CHARMED.discard(actor)
if actor_role != "piper":
del TOBECHARMED[:target:]
PASSED.discard(target)
@event_listener("get_special")
def on_get_special(evt, var):
@ -196,10 +222,19 @@ def on_acted(evt, var, target, spy):
def on_reset(evt, var):
CHARMED.clear()
TOBECHARMED.clear()
PASSED.clear()
@event_listener("revealroles")
def on_revealroles(evt, var, wrapper):
if CHARMED:
evt.data["output"].append("\u0002charmed players\u0002: {0}".format(", ".join(p.nick for p in CHARMED)))
nicks = ", ".join(p.nick for p in CHARMED)
evt.data["output"].append(messages["piper_revealroles_charmed"].format(nicks))
@event_listener("revealroles_role")
def on_revealroles_role(evt, var, user, role):
players = TOBECHARMED.get(user)
if players:
nicks = ", ".join(p.nick for p in players)
evt.data["special_case"].append(messages["piper_revealroles_charming"].format(nicks))
# vim: set sw=4 expandtab:

View File

@ -49,16 +49,18 @@ def vg_kill(var, wrapper, message):
wrapper.pm(messages["player_kill"].format(orig))
debuglog("{0} (vengeful ghost) KILL: {1} ({2})".format(wrapper.source.nick, target, get_main_role(target)))
debuglog("{0} (vengeful ghost) KILL: {1} ({2})".format(wrapper.source, target, get_main_role(target)))
@command("retract", "r", chan=False, pm=True, playing=False, phases=("night",))
def vg_retract(var, wrapper, message):
"""Removes a vengeful ghost's kill selection."""
if wrapper.source not in GHOSTS:
return
if wrapper.source in KILLS:
del KILLS[wrapper.source]
wrapper.pm(messages["retracted_kill"])
debuglog("{0} (vengeful ghost) RETRACT".format(wrapper.source))
@event_listener("get_participants")
def on_get_participants(evt, var):

View File

@ -30,7 +30,6 @@ def vigilante_kill(var, wrapper, message):
PASSED.discard(wrapper.source)
wrapper.send(messages["player_kill"].format(orig))
debuglog("{0} (vigilante) KILL: {1} ({2})".format(wrapper.source, target, get_main_role(target)))
@command("retract", "r", chan=False, pm=True, playing=True, phases=("night",), roles=("vigilante",))
@ -39,14 +38,16 @@ def vigilante_retract(var, wrapper, message):
if wrapper.source not in KILLS and wrapper.source not in PASSED:
return
KILLS.pop(wrapper.source, None)
del KILLS[:wrapper.source:]
PASSED.discard(wrapper.source)
wrapper.send(messages["retracted_kill"])
debuglog("{0} (vigilante) RETRACT".format(wrapper.source))
@command("pass", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("vigilante",))
def vigilante_pass(var, wrapper, message):
"""Do not kill anyone tonight as a vigilante."""
KILLS.pop(wrapper.source, None)
del KILLS[:wrapper.source:]
PASSED.add(wrapper.source)
wrapper.send(messages["hunter_pass"])
@ -55,7 +56,7 @@ def vigilante_pass(var, wrapper, message):
@event_listener("del_player")
def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
PASSED.discard(user)
KILLS.pop(user, None)
del KILLS[:user:]
for vigilante, target in list(KILLS.items()):
if target is user:
vigilante.send(messages["hunter_discard"])
@ -84,8 +85,8 @@ def on_transition_day(evt, var):
@event_listener("exchange_roles")
def on_exchange(evt, var, actor, target, actor_role, target_role):
KILLS.pop(actor, None)
KILLS.pop(target, None)
del KILLS[:actor:]
del KILLS[:target:]
PASSED.discard(actor)
PASSED.discard(target)

View File

@ -62,7 +62,7 @@ def wolf_kill(cli, nick, chan, rest):
victim = get_victim(cli, nick, victim, False)
if not victim:
return
if victim == nick:
pm(cli, nick, messages["no_suicide"])
return