banned/src/roles/vengefulghost.py
Em Barry 745a1dc68a
Convert chk_decision (#317)
Convert chk_decision, chk_nightdone, transition_day, transition_night, doomsayer, mayor, and convert+split shamans in three files with a shared helper. Fixes and updates for the User containers, and some other tweaks and fixes.
2018-04-23 13:25:38 -04:00

233 lines
8.5 KiB
Python

import re
import random
from collections import defaultdict
from src.utilities import *
from src import channels, users, debuglog, errlog, plog
from src.functions import get_players, get_target, get_main_role
from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages
from src.events import Event
KILLS = UserDict() # type: Dict[users.User, users.User]
GHOSTS = UserDict() # type: Dict[users.User, str]
# temporary holding variable, only non-empty during transition_day
drivenoff = UserDict() # type: Dict[users.User, str]
@command("kill", chan=False, pm=True, playing=False, silenced=True, phases=("night",), users=GHOSTS)
def vg_kill(var, wrapper, message):
"""Take revenge on someone each night after you die."""
if GHOSTS[wrapper.source][0] == "!":
return
target = get_target(var, wrapper, re.split(" +", message)[0])
if not target:
return
if target is wrapper.source:
wrapper.pm(messages["player_dead"])
return
wolves = get_players(var.WOLFTEAM_ROLES)
if GHOSTS[wrapper.source] == "wolves" and target not in wolves:
wrapper.pm(messages["vengeful_ghost_wolf"])
return
elif GHOSTS[wrapper.source] == "villagers" and target in wolves:
wrapper.pm(messages["vengeful_ghost_villager"])
return
orig = target
evt = Event("targeted_command", {"target": target, "misdirection": True, "exchange": False})
evt.dispatch(var, "kill", wrapper.source, target, frozenset({"detrimental"}))
if evt.prevent_default:
return
target = evt.data["target"]
KILLS[wrapper.source] = target
wrapper.pm(messages["player_kill"].format(orig))
debuglog("{0} (vengeful ghost) KILL: {1} ({2})".format(wrapper.source.nick, 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"])
@event_listener("get_participants")
def on_get_participants(evt, var):
evt.data["players"].extend([p for p in GHOSTS if GHOSTS[p][0] != "!"])
evt.data["players"].extend(drivenoff)
@event_listener("player_win", priority=1)
def on_player_win(evt, var, user, role, winner, survived):
# alive VG winning is handled in villager.py
# extending VG to work with new teams can be done by registering
# a listener at priority > 1, importing src.roles.vengefulghost,
# and checking if the user is in GHOSTS.
if user in GHOSTS:
evt.data["special"].append("vg activated")
against = GHOSTS[user]
if against[0] == "!":
evt.data["special"].append("vg driven off")
against = against[1:]
if against == "villagers" and winner == "wolves":
evt.data["won"] = True
evt.data["iwon"] = True
elif against == "wolves" and winner == "villagers":
evt.data["won"] = True
evt.data["iwon"] = True
else:
evt.data["won"] = False
evt.data["iwon"] = False
@event_listener("del_player", priority=6)
def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
for h, v in list(KILLS.items()):
if user is v:
h.send(messages["hunter_discard"])
del KILLS[h]
# extending VG to work with new teams can be done by registering a listener
# at priority < 6, importing src.roles.vengefulghost, and setting
# GHOSTS[user] to something; if that is done then this logic is not run.
if death_triggers and mainrole == "vengeful ghost" and user not in GHOSTS:
if evt.params.killer_role in var.WOLFTEAM_ROLES:
GHOSTS[user] = "wolves"
else:
GHOSTS[user] = "villagers"
user.send(messages["vengeful_turn"].format(GHOSTS[user]))
debuglog(user.nick, "(vengeful ghost) TRIGGER", GHOSTS[user])
@event_listener("transition_day_begin", priority=6)
def on_transition_day_begin(evt, var):
# select a random target for VG if they didn't kill
wolves = set(get_players(var.WOLFTEAM_ROLES))
villagers = set(get_players()) - wolves
for ghost, target in GHOSTS.items():
if target[0] == "!" or ghost.nick in var.SILENCED:
continue
if ghost not in KILLS:
choice = set()
if target == "wolves":
choice = wolves.copy()
elif target == "villagers":
choice = villagers.copy()
evt = Event("vg_kill", {"pl": choice})
evt.dispatch(var, ghost, target)
choice = evt.data["pl"]
if choice:
KILLS[ghost] = random.choice(list(choice))
@event_listener("transition_day", priority=2)
def on_transition_day(evt, var):
for k, d in KILLS.items():
evt.data["victims"].append(d)
evt.data["onlybywolves"].discard(d)
evt.data["killers"][d].append(k)
@event_listener("transition_day", priority=3.01)
def on_transition_day3(evt, var):
for k, d in list(KILLS.items()):
if GHOSTS[k] == "villagers":
evt.data["killers"][d].remove(k)
evt.data["killers"][d].insert(0, k)
@event_listener("transition_day", priority=6.01)
def on_transition_day6(evt, var):
for k, d in list(KILLS.items()):
if GHOSTS[k] == "villagers" and k in evt.data["killers"][d]:
evt.data["killers"][d].remove(k)
evt.data["killers"][d].insert(0, k)
# important, otherwise our del_player listener messages the vg
del KILLS[k]
@event_listener("retribution_kill", priority=6)
def on_retribution_kill(evt, var, victim, orig_target):
target = evt.data["target"]
if target in GHOSTS:
drivenoff[target] = GHOSTS[target]
GHOSTS[target] = "!" + GHOSTS[target]
evt.data["message"].append(messages["totem_banish"].format(victim, target))
evt.data["target"] = None
@event_listener("get_participant_role")
def on_get_participant_role(evt, var, user):
if user in GHOSTS:
if user in drivenoff:
against = drivenoff[user]
else:
against = GHOSTS[user]
if against == "villagers":
evt.data["role"] = "wolf"
elif against == "wolves":
evt.data["role"] = "villager"
@event_listener("chk_nightdone")
def on_chk_nightdone(evt, var):
evt.data["actedcount"] += len(KILLS)
evt.data["nightroles"].extend([p for p in GHOSTS if GHOSTS[p][0] != "!"])
@event_listener("transition_night_end", priority=2)
def on_transition_night_end(evt, var):
# alive VGs are messaged as part of villager.py, this handles dead ones
ps = get_players()
wolves = get_players(var.WOLFTEAM_ROLES)
for v_ghost, who in GHOSTS.items():
if who[0] == "!":
continue
if who == "wolves":
pl = wolves[:]
else:
pl = ps[:]
for wolf in wolves:
pl.remove(wolf)
random.shuffle(pl)
to_send = "vengeful_ghost_notify"
if v_ghost.prefers_simple():
to_send = "vengeful_ghost_simple"
v_ghost.send(messages[to_send].format(who), who.capitalize() + ": " + ", ".join(p.nick for p in pl), sep="\n")
debuglog("GHOST: {0} (target: {1}) - players: {2}".format(v_ghost, who, ", ".join(p.nick for p in pl)))
@event_listener("myrole")
def on_myrole(evt, var, user):
if user in GHOSTS:
evt.prevent_default = True
if GHOSTS[user][0] != "!":
user.send(messages["vengeful_role"].format(GHOSTS[user]))
@event_listener("revealroles")
def on_revealroles(evt, var, wrapper):
if GHOSTS:
glist = []
for ghost, team in GHOSTS.items():
dead = "driven away, " if team[0] == "!" else ""
glist.append("{0} ({1}against {2})".format(ghost.nick, dead, team.lstrip("!")))
evt.data["output"].append("\u0002dead vengeful ghost\u0002: {0}".format(", ".join(glist)))
@event_listener("begin_day")
def on_begin_day(evt, var):
drivenoff.clear()
KILLS.clear()
@event_listener("reset")
def on_reset(evt, var):
KILLS.clear()
GHOSTS.clear()
@event_listener("get_role_metadata")
def on_get_role_metadata(evt, var, kind):
if kind == "night_kills":
evt.data["vengeful ghost"] = sum(1 for against in GHOSTS.values() if against[0] != "!")
elif kind == "special_keys":
evt.data["vengeful ghost"] = {"vg activated", "vg driven off"}
# vim: set sw=4 expandtab: