banned/src/roles/vengefulghost.py

257 lines
9.3 KiB
Python

import re
import random
from collections import defaultdict
import src.settings as var
from src.utilities import *
from src import channels, users, debuglog, errlog, plog
from src.decorators import command, event_listener
from src.messages import messages
from src.events import Event
KILLS = {} # type: Dict[str, str]
GHOSTS = {} # type: Dict[users.User, str]
# temporary holding variable, only non-empty during transition_day
# as such, no need to track nick changes, etc. with it
drivenoff = {} # type: Dict[str, 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
victim = get_victim(wrapper.source.client, wrapper.source.nick, re.split(" +", message)[0], False)
if not victim:
return
if victim == wrapper.source.nick:
wrapper.pm(messages["player_dead"])
return
wolves = list_players(var.WOLFTEAM_ROLES)
if GHOSTS[wrapper.source] == "wolves" and victim not in wolves:
wrapper.pm(messages["vengeful_ghost_wolf"])
return
elif GHOSTS[wrapper.source] == "villagers" and victim in wolves:
wrapper.pm(messages["vengeful_ghost_villager"])
return
orig = victim
evt = Event("targeted_command", {"target": victim, "misdirection": True, "exchange": False})
evt.dispatch(wrapper.source.client, var, "kill", wrapper.source.nick, victim, frozenset({"detrimental"}))
if evt.prevent_default:
return
victim = evt.data["target"]
KILLS[wrapper.source.nick] = victim
msg = messages["wolf_target"].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)))
chk_nightdone(wrapper.source.client)
@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.nick in KILLS:
del KILLS[wrapper.source.nick]
wrapper.pm(messages["retracted_kill"])
@event_listener("list_participants")
def on_list_participants(evt, var):
evt.data["pl"].extend([p.nick for p in GHOSTS if GHOSTS[p][0] != "!"])
evt.data["pl"].extend([p for p in 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, cli, var, nick, nickrole, nicktpls, death_triggers):
for h,v in list(KILLS.items()):
if v == nick:
pm(cli, h, 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.
user = users._get(nick) # FIXME
if death_triggers and nickrole == "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(nick, "(vengeful ghost) TRIGGER", GHOSTS[user])
@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]
@event_listener("night_acted")
def on_acted(evt, cli, var, nick, sender):
if nick in KILLS:
evt.data["acted"] = True
@event_listener("transition_day_begin", priority=6)
def on_transition_day_begin(evt, cli, var):
# select a random target for VG if they didn't kill
wolves = set(list_players(var.WOLFTEAM_ROLES))
villagers = set(list_players()) - wolves
for ghost, target in GHOSTS.items():
if target[0] == "!" or ghost.nick in var.SILENCED:
continue
if ghost.nick 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"]
# roll this into the above event once succubus is split off
if ghost.nick in var.ENTRANCED:
choice -= var.ROLES["succubus"]
if choice:
KILLS[ghost.nick] = random.choice(list(choice))
@event_listener("transition_day", priority=2)
def on_transition_day(evt, cli, 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, cli, var):
for k, d in list(KILLS.items()):
if GHOSTS[users._get(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, cli, var):
for k, d in list(KILLS.items()):
if GHOSTS[users._get(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) # FIXME: This function, and all of the event
def on_retribution_kill(evt, cli, var, victim, orig_target):
t = evt.data["target"]
if users._get(t) in GHOSTS:
drivenoff[t] = GHOSTS[users._get(t)]
GHOSTS[users._get(t)] = "!" + GHOSTS[users._get(t)]
evt.data["message"].append(messages["totem_banish"].format(victim, t))
evt.data["target"] = None
@event_listener("get_participant_role")
def on_get_participant_role(evt, var, nick):
if users._get(nick) in GHOSTS: # FIXME
if nick in drivenoff:
against = drivenoff[nick]
else:
against = GHOSTS[users._get(nick)]
if against == "villagers":
evt.data["role"] = "wolf"
elif against == "wolves":
evt.data["role"] = "villager"
@event_listener("chk_nightdone")
def on_chk_nightdone(evt, cli, var):
evt.data["actedcount"] += len(KILLS)
evt.data["nightroles"].extend([p.nick for p in GHOSTS if GHOSTS[p][0] != "!"])
@event_listener("transition_night_end", priority=2)
def on_transition_night_end(evt, cli, var):
# alive VGs are messaged as part of villager.py, this handles dead ones
ps = list_players()
wolves = list_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)
if not v_ghost.prefers_simple():
v_ghost.send(messages["vengeful_ghost_notify"].format(who))
else:
v_ghost.send(messages["vengeful_ghost_simple"])
v_ghost.send(who.capitalize() + ": " + ", ".join(pl))
debuglog("GHOST: {0} (target: {1}) - players: {2}".format(v_ghost.nick, who, ", ".join(pl)))
@event_listener("myrole")
def on_myrole(evt, cli, var, nick):
user = users._get(nick)
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, cli, 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, cli, var, kind):
if kind == "night_kills":
evt.data["vengeful ghost"] = sum(1 for against in GHOSTS.values() if against[0] != "!")
# vim: set sw=4 expandtab: