257 lines
9.3 KiB
Python
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:
|