Begin work splitting roles into their own files

This commit is contained in:
skizzerz 2016-08-08 18:42:32 -05:00
parent f9c4ef6b28
commit 957ab9a17a
8 changed files with 416 additions and 180 deletions

1
.gitignore vendored
View File

@ -12,6 +12,7 @@ __pycache__/
botconfig.py botconfig.py
/gamemodes.py /gamemodes.py
/messages.json /messages.json
/roles/*.py
# Database files # Database files
*.sqlite3 *.sqlite3

20
roles/__init__.py.example Normal file
View File

@ -0,0 +1,20 @@
# Imports all custom and built-in role definitions
# To implement custom roles, rename this file to __init__.py
import os.path
import glob
import importlib
# get built-in roles
import src.roles
path = os.path.dirname(os.path.abspath(__file__))
search = os.path.join(path, "*.py")
for f in glob.iglob(search):
f = os.path.basename(f)
n, _ = os.path.splitext(f)
if f == "__init__.py":
continue
importlib.import_module(n, package="roles")
# vim: set sw=4 expandtab:

View File

@ -12,12 +12,18 @@ from src import db
# These are not required, so failing to import it doesn't matter # These are not required, so failing to import it doesn't matter
# The file then imports our game modes # The file then imports our game modes
# Fall back to importing our game modes if theirs fail # Fall back to importing our game modes if theirs fail
# Do the same with roles
try: try:
import gamemodes import gamemodes
except ImportError: except ImportError:
import src.gamemodes import src.gamemodes
try:
import roles
except ImportError:
import src.roles
# Handle launch parameters # Handle launch parameters
# Argument --debug means start in debug mode # Argument --debug means start in debug mode

View File

@ -10,7 +10,7 @@ from oyoyo.parse import parse_nick
import botconfig import botconfig
import src.settings as var import src.settings as var
from src.utilities import * from src.utilities import *
from src import logger, errlog from src import logger, errlog, events
from src.messages import messages from src.messages import messages
adminlog = logger.logger("audit.log") adminlog = logger.logger("audit.log")
@ -248,4 +248,24 @@ class hook:
if not HOOKS[each]: if not HOOKS[each]:
del HOOKS[each] del HOOKS[each]
class event_listener:
def __init__(self, event, priority=5):
self.event = event
self.priority = priority
self.func = None
def __call__(self, *args, **kwargs):
if self.func is None:
if isinstance(func, event_listener):
func = func.func
self.func = handle_error(func)
events.add_listener(self.event, self.func, self.priority)
self.__doc__ = self.func.__doc__
return self
else:
return self.func(*args, **kwargs)
def remove(self):
events.remove_listener(self.event, self.func, self.priority)
# vim: set sw=4 expandtab: # vim: set sw=4 expandtab:

16
src/roles/__init__.py Normal file
View File

@ -0,0 +1,16 @@
# Imports all role definitions
import os.path
import glob
import importlib
path = os.path.dirname(os.path.abspath(__file__))
search = os.path.join(path, "*.py")
for f in glob.iglob(search):
f = os.path.basename(f)
n, _ = os.path.splitext(f)
if f == "__init__.py":
continue
importlib.import_module(n, package="src.roles")
# vim: set sw=4 expandtab:

130
src/roles/seer.py Normal file
View File

@ -0,0 +1,130 @@
import re
import src.settings as var
from src.utilities import *
from src import debuglog, errlog, plog
from src.decorators import cmd, event_listener
from src.messages import messages
from src.events import Event
SEEN = set()
@cmd("see", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("seer", "oracle", "augur"))
def see(cli, nick, chan, rest):
"""Use your paranormal powers to determine the role or alignment of a player."""
role = get_role(nick)
if nick in SEEN:
pm(cli, nick, messages["seer_fail"])
return
victim = get_victim(cli, nick, re.split(" +",rest)[0], False)
if not victim:
return
if victim == nick:
pm(cli, nick, messages["no_see_self"])
return
evt = Event("targeted_command", {"target": victim, "misdirection": True, "exchange": True})
evt.dispatch(cli, var, "see", nick, victim, frozenset("info", "immediate"))
if evt.prevent_default:
return
victim = evt.data["victim"]
victimrole = get_role(victim)
evt = Event("see", {"role": victimrole})
evt.dispatch(cli, var, nick, victim)
victimrole = evt.data["role"]
vrole = victimrole # keep a copy for logging
if role == "seer":
if (victimrole in var.SEEN_WOLF and victimrole not in var.SEEN_DEFAULT) or victim in var.ROLES["cursed villager"]:
victimrole = "wolf"
elif victimrole in var.SEEN_DEFAULT:
victimrole = var.DEFAULT_ROLE
if var.DEFAULT_SEEN_AS_VILL:
victimrole = "villager"
# TODO: split off into shaman.py
if (victim in var.DECEIVED) ^ (nick in var.DECEIVED):
if victimrole == "wolf":
victimrole = "villager"
else:
victimrole = "wolf"
pm(cli, nick, (messages["seer_success"]).format(victim, victimrole))
debuglog("{0} ({1}) SEE: {2} ({3}) as {4}".format(nick, role, victim, vrole, victimrole))
elif role == "oracle":
iswolf = False
if (victimrole in var.SEEN_WOLF and victimrole not in var.SEEN_DEFAULT) or victim in var.ROLES["cursed villager"]:
iswolf = True
# deceit totem acts on both target and actor, so if both have them, they cancel each other out
if (victim in var.DECEIVED) ^ (nick in var.DECEIVED):
iswolf = not iswolf
pm(cli, nick, (messages["oracle_success"]).format(victim, "" if iswolf else "\u0002not\u0002 ", "\u0002" if iswolf else ""))
debuglog("{0} ({1}) SEE: {2} ({3}) (Wolf: {4})".format(nick, role, victim, vrole, str(iswolf)))
elif role == "augur":
if victimrole == "amnesiac":
victimrole = var.AMNESIAC_ROLES[victim]
aura = "blue"
if victimrole in var.WOLFTEAM_ROLES:
aura = "red"
elif victimrole in var.TRUE_NEUTRAL_ROLES:
aura = "grey"
pm(cli, nick, (messages["augur_success"]).format(victim, aura))
debuglog("{0} ({1}) SEE: {2} ({3}) as {4} ({5} aura)".format(nick, role, victim, vrole, victimrole, aura))
SEEN.add(nick)
chk_nightdone(cli)
@event_listener("rename_player")
def on_rename(evt, cli, var, prefix, nick):
if prefix in SEEN:
SEEN.remove(prefix)
SEEN.add(nick)
@event_listener("exchange_roles")
def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role):
if actor_role in ("seer", "oracle", "augur"):
SEEN.discard(actor)
@event_listener("chk_nightdone")
def on_chk_nightdone(evt, cli, var):
evt.data["actedcount"] += len(SEEN)
evt.data["nightroles"].extend(get_roles("seer", "oracle", "augur"))
@event_listener("transition_night_end", priority=2)
def on_transition_night_end(evt, cli, var):
for seer in list_players(("seer", "oracle", "augur")):
pl = list_players()
random.shuffle(pl)
role = get_role(seer)
pl.remove(seer) # remove self from list
a = "a"
if role in ("oracle", "augur"):
a = "an"
if role == "seer":
what = messages["seer_ability"]
elif role == "oracle":
what = messages["oracle_ability"]
elif role == "augur":
what = messages["augur_ability"]
else:
what = messages["seer_role_bug"]
if seer in var.PLAYERS and not is_user_simple(seer):
pm(cli, seer, messages["seer_role_info"].format(a, role, what))
else:
pm(cli, seer, messages["seer_simple"].format(a, role)) # !simple
pm(cli, seer, "Players: " + ", ".join(pl))
@event_listener("begin_day")
def on_begin_day(evt, cli, var):
SEEN = set()
@event_listener("reset")
def on_reset(evt, var):
SEEN = set()
# vim: set sw=4 expandtab:

143
src/roles/wildchild.py Normal file
View File

@ -0,0 +1,143 @@
import src.settings as var
from src.utilities import *
from src import debuglog, errlog, plog
from src.decorators import cmd, event_listener
from src.messages import messages
from src.events import Event
WILD_CHILDREN = set()
IDOLS = {}
@cmd("choose", chan=False, pm=True, playing=True, phases=("night",), roles=("wild child",))
def choose_idol(cli, nick, chan, rest):
"""Pick your idol, if they die, you'll become a wolf!"""
if not var.FIRST_NIGHT:
return
if nick in IDOLS:
pm(cli, nick, messages["wild_child_already_picked"])
return
victim = get_victim(cli, nick, re.split(" +", rest)[0], False)
if not victim:
return
if nick == victim:
pm(cli, nick, messages["no_target_self"])
return
IDOLS[nick] = victim
pm(cli, nick, messages["wild_child_success"].format(victim))
debuglog("{0} ({1}) IDOLIZE: {2} ({3})".format(nick, get_role(nick), victim, get_role(victim)))
chk_nightdone(cli)
@event_listener("see")
def on_see(evt, cli, var, seer, victim):
if get_role(seer) != "augur" and victim in WILD_CHILDREN:
evt.data["role"] = "wild child"
@event_listener("rename_player")
def on_rename(evt, cli, var, prefix, nick):
if prefix in WILD_CHILDREN:
WILD_CHILDREN.remove(prefix)
WILD_CHILDREN.add(nick)
@event_listener("exchange_roles")
def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role):
if actor_role == "wolf" and actor in WILD_CHILDREN and nick not in WILD_CHILDREN:
WILD_CHILDREN.discard(actor)
WILD_CHILDREN.add(nick)
elif actor_role == "wild child":
if nick_role == "wild child":
temp = IDOLS[nick]
IDOLS[nick] = IDOLS[actor]
IDOLS[actor] = temp
evt.data["actor_messages"].append(messages["wild_child_idol"].format(IDOLS[actor]))
evt.data["nick_messages"].append(messages["wild_child_idol"].format(IDOLS[nick]))
else:
IDOLS[nick] = IDOLS.pop(actor)
evt.data["nick_messages"].append(messages["wild_child_idol"].format(IDOLS[nick]))
if nick_role == "wolf" and nick in WILD_CHILDREN and actor not in WILD_CHILDREN:
WILD_CHILDREN.discard(nick)
WILD_CHILDREN.add(actor)
elif nick_role == "wild child" and actor_role != "wild child":
# if they're both wild children, already swapped idols above
IDOLS[actor] = IDOLS.pop(nick)
evt.data["actor_messages"].append(messages["wild_child_idol"].format(IDOLS[actor]))
@event_listener("myrole")
def on_myrole(evt, cli, var, nick, role):
if role == "wild child" and nick in IDOLS:
pm(cli, nick, messages["wild_child_idol"].format(IDOLS[nick]))
@event_listener("del_player")
def on_del_player(evt, cli, var, nick, nickrole, nicktpls, lynched, end_game, death_triggers, killer_role, deadlist, original, ismain, refresh_pl):
if var.PHASE not in var.GAME_PHASES:
return
for child in var.ROLES["wild child"].copy():
if child not in IDOLS or child in deadlist or IDOLS[child] not in deadlist:
continue
pm(cli, child, messages["idol_died"])
WILD_CHILDREN.add(child)
var.ROLES["wild child"].remove(child)
var.ROLES["wolf"].add(child)
var.FINAL_ROLES[child] = "wolf"
wolves = list_players(var.WOLFCHAT_ROLES)
wolves.remove(child)
mass_privmsg(cli, wolves, messages["wild_child_as_wolf"].format(child))
if var.PHASE == "day":
random.shuffle(wolves)
for i, wolf in enumerate(wolves):
wolfroles = get_role(wolf)
cursed = ""
if wolf in var.ROLES["cursed villager"]:
cursed = "cursed "
wolves[i] = "\u0002{0}\u0002 ({1}{2})".format(wolf, cursed, wolfroles)
if wolves:
pm(cli, child, "Wolves: " + ", ".join(wolves))
else:
pm(cli, child, messages["no_other_wolves"])
@event_listener("chk_nightdone")
def on_chk_nightdone(evt, cli, var):
if var.FIRST_NIGHT:
evt.data["actedcount"] += len(IDOLS.keys())
evt.data["nightroles"].extend(var.ROLES["wild child"])
@event_listener("transition_day_begin")
def on_transition_day_begin(evt, cli, var):
if (not var.START_WITH_DAY or not var.FIRST_DAY) and var.FIRST_NIGHT:
for child in var.ROLES["wild child"]:
if child not in IDOLS:
pl = list_players()
pl.remove(child)
if pl:
target = random.choice(pl)
IDOLS[child] = target
pm(cli, child, messages["wild_child_random_idol"].format(target))
@event_listener("transition_night_end", priority=2)
def on_transition_night_end(evt, cli, var):
for child in var.ROLES["wild child"]:
if child in var.PLAYERS and not is_user_simple(child):
pm(cli, child, messages["child_notify"])
else:
pm(cli, child, messages["child_simple"])
@event_listener("revealroles_role")
def on_revealroles_role(evt, cli, var, nick, role):
if role == "wild child":
if nick in IDOLS:
evt.data["special_case"].append("picked {0} as idol".format(IDOLS[nick]))
else:
evt.data["special_case"].append("no idol picked yet")
@event_listener("reset")
def on_reset(evt, var):
WILD_CHILDREN = set()
IDOLS = {}
# vim: set sw=4 expandtab:

View File

@ -54,6 +54,7 @@ Event = events.Event
cmd = decorators.cmd cmd = decorators.cmd
hook = decorators.hook hook = decorators.hook
handle_error = decorators.handle_error handle_error = decorators.handle_error
event_listener = decorators.event_listener
COMMANDS = decorators.COMMANDS COMMANDS = decorators.COMMANDS
# Game Logic Begins: # Game Logic Begins:
@ -410,7 +411,6 @@ def reset():
var.START_VOTES = set() # list of players who have voted to !start var.START_VOTES = set() # list of players who have voted to !start
var.LOVERS = {} # need to be here for purposes of random var.LOVERS = {} # need to be here for purposes of random
var.ENTRANCED = set() var.ENTRANCED = set()
var.WILD_CHILDREN = set()
reset_settings() reset_settings()
@ -421,6 +421,9 @@ def reset():
var.SPECTATING_WOLFCHAT = set() var.SPECTATING_WOLFCHAT = set()
var.SPECTATING_DEADCHAT = set() var.SPECTATING_DEADCHAT = set()
evt = Event("reset", {})
evt.dispatch(var)
reset() reset()
@cmd("fsync", flag="m", pm=True) @cmd("fsync", flag="m", pm=True)
@ -2850,7 +2853,7 @@ def chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs,
def del_player(cli, nick, forced_death=False, devoice=True, end_game=True, death_triggers=True, killer_role="", deadlist=[], original="", cmode=[], deadchat=[], ismain=True): def del_player(cli, nick, forced_death=False, devoice=True, end_game=True, death_triggers=True, killer_role="", deadlist=[], original="", cmode=[], deadchat=[], ismain=True):
""" """
Returns: False if one side won. Returns: False if one side won.
arg: forced_death = True when lynched or when the seer/wolf both don't act arg: forced_death = True when lynched
""" """
def refresh_pl(old_pl): def refresh_pl(old_pl):
@ -2943,31 +2946,6 @@ def del_player(cli, nick, forced_death=False, devoice=True, end_game=True, death
if nickrole == "clone" and nick in var.CLONED: if nickrole == "clone" and nick in var.CLONED:
del var.CLONED[nick] del var.CLONED[nick]
for child in var.ROLES["wild child"].copy():
if child in var.IDOLS and child not in deadlist:
if var.IDOLS[child] in deadlist:
pm(cli, child, messages["idol_died"])
var.WILD_CHILDREN.add(child)
var.ROLES["wild child"].remove(child)
var.ROLES["wolf"].add(child)
var.FINAL_ROLES[child] = "wolf"
wolves = list_players(var.WOLFCHAT_ROLES)
wolves.remove(child)
mass_privmsg(cli, wolves, messages["wild_child_as_wolf"].format(child))
if var.PHASE == "day":
random.shuffle(wolves)
for i, wolf in enumerate(wolves):
wolfroles = get_role(wolf)
cursed = ""
if wolf in var.ROLES["cursed villager"]:
cursed = "cursed "
wolves[i] = "\u0002{0}\u0002 ({1}{2})".format(wolf, cursed, wolfroles)
if wolves:
pm(cli, child, "Wolves: " + ", ".join(wolves))
else:
pm(cli, child, messages["no_other_wolves"])
if death_triggers and var.PHASE in var.GAME_PHASES: if death_triggers and var.PHASE in var.GAME_PHASES:
if nick in var.LOVERS: if nick in var.LOVERS:
others = var.LOVERS[nick].copy() others = var.LOVERS[nick].copy()
@ -3629,7 +3607,7 @@ def rename_player(cli, prefix, nick):
if prefix in dictvar.keys(): if prefix in dictvar.keys():
del dictvar[prefix] del dictvar[prefix]
for dictvar in (var.VENGEFUL_GHOSTS, var.TOTEMS, var.FINAL_ROLES, var.GUNNERS, var.TURNCOATS, for dictvar in (var.VENGEFUL_GHOSTS, var.TOTEMS, var.FINAL_ROLES, var.GUNNERS, var.TURNCOATS,
var.DOCTORS, var.BITTEN_ROLES, var.LYCAN_ROLES, var.AMNESIAC_ROLES, var.IDOLS): var.DOCTORS, var.BITTEN_ROLES, var.LYCAN_ROLES, var.AMNESIAC_ROLES):
if prefix in dictvar.keys(): if prefix in dictvar.keys():
dictvar[nick] = dictvar.pop(prefix) dictvar[nick] = dictvar.pop(prefix)
# Looks like {'jacob2': ['5'], '7': ['3']} # Looks like {'jacob2': ['5'], '7': ['3']}
@ -3675,7 +3653,7 @@ def rename_player(cli, prefix, nick):
var.DISEASED, var.TOBEDISEASED, var.RETRIBUTION, var.MISDIRECTED, var.TOBEMISDIRECTED, var.DISEASED, var.TOBEDISEASED, var.RETRIBUTION, var.MISDIRECTED, var.TOBEMISDIRECTED,
var.EXCHANGED, var.IMMUNIZED, var.CURED_LYCANS, var.ALPHA_WOLVES, var.CURSED, var.CHARMERS, var.EXCHANGED, var.IMMUNIZED, var.CURED_LYCANS, var.ALPHA_WOLVES, var.CURSED, var.CHARMERS,
var.CHARMED, var.TOBECHARMED, var.PRIESTS, var.CONSECRATING, var.ENTRANCED_DYING, var.DYING, var.CHARMED, var.TOBECHARMED, var.PRIESTS, var.CONSECRATING, var.ENTRANCED_DYING, var.DYING,
var.DECEIVED, var.WILD_CHILDREN): var.DECEIVED):
if prefix in setvar: if prefix in setvar:
setvar.remove(prefix) setvar.remove(prefix)
setvar.add(nick) setvar.add(nick)
@ -3904,7 +3882,7 @@ def begin_day(cli):
var.KILLS = {} # nicknames of kill victims (wolves only) var.KILLS = {} # nicknames of kill victims (wolves only)
var.OTHER_KILLS = {} # other kill victims (hunter/vengeful ghost) var.OTHER_KILLS = {} # other kill victims (hunter/vengeful ghost)
var.KILLER = "" # nickname of who chose the victim var.KILLER = "" # nickname of who chose the victim
var.SEEN = set() # set of seers/oracles/augurs that have had visions var.SEEN = set() # set of doomsayers that have had visions
var.HEXED = set() # set of hags that have silenced others var.HEXED = set() # set of hags that have silenced others
var.SHAMANS = {} # dict of shamans/crazed shamans that have acted and who got totems var.SHAMANS = {} # dict of shamans/crazed shamans that have acted and who got totems
var.OBSERVED = {} # those whom werecrows/sorcerers have observed var.OBSERVED = {} # those whom werecrows/sorcerers have observed
@ -4054,16 +4032,6 @@ def transition_day(cli, gameid=0):
choose.func(cli, mm, mm, lovers[0] + " " + lovers[1], sendmsg=False) choose.func(cli, mm, mm, lovers[0] + " " + lovers[1], sendmsg=False)
pm(cli, mm, messages["random_matchmaker"]) pm(cli, mm, messages["random_matchmaker"])
for child in var.ROLES["wild child"]:
if child not in var.IDOLS:
ps = pl[:]
pl.remove(child)
if ps:
target = random.choice(ps)
var.IDOLS[child] = target
pm(cli, child, messages["wild_child_random_idol"].format(target))
# Reset daytime variables # Reset daytime variables
var.INVESTIGATED = set() var.INVESTIGATED = set()
var.WOUNDED = set() var.WOUNDED = set()
@ -4788,10 +4756,10 @@ def chk_nightdone(cli):
var.OTHER_KILLS, var.PASSED, var.OBSERVED, var.OTHER_KILLS, var.PASSED, var.OBSERVED,
var.HEXED, var.SHAMANS, var.CURSED, var.CHARMERS))) var.HEXED, var.SHAMANS, var.CURSED, var.CHARMERS)))
nightroles = get_roles("seer", "oracle", "harlot", "succubus", "bodyguard", nightroles = get_roles("harlot", "succubus", "bodyguard",
"guardian angel", "wolf", "werecrow", "alpha wolf", "guardian angel", "wolf", "werecrow", "alpha wolf",
"sorcerer", "hunter", "hag", "shaman", "crazed shaman", "sorcerer", "hunter", "hag", "shaman", "crazed shaman",
"augur", "werekitten", "warlock", "piper", "wolf mystic", "werekitten", "warlock", "piper", "wolf mystic",
"fallen angel", "vigilante", "doomsayer", "doomsayer", # NOT a mistake, doomsayer MUST be listed twice "fallen angel", "vigilante", "doomsayer", "doomsayer", # NOT a mistake, doomsayer MUST be listed twice
"prophet", "wolf shaman", "wolf shaman") # wolf shaman also must be listed twice "prophet", "wolf shaman", "wolf shaman") # wolf shaman also must be listed twice
@ -4814,8 +4782,8 @@ def chk_nightdone(cli):
nightroles.append(p) nightroles.append(p)
if var.FIRST_NIGHT: if var.FIRST_NIGHT:
actedcount += len(var.MATCHMAKERS | var.CLONED.keys() | var.IDOLS.keys()) actedcount += len(var.MATCHMAKERS | var.CLONED.keys())
nightroles.extend(get_roles("matchmaker", "clone", "wild child")) nightroles.extend(get_roles("matchmaker", "clone"))
if var.DISEASED_WOLVES: if var.DISEASED_WOLVES:
nightroles = [p for p in nightroles if p not in list_players(var.WOLF_ROLES - {"wolf cub", "werecrow", "doomsayer"})] nightroles = [p for p in nightroles if p not in list_players(var.WOLF_ROLES - {"wolf cub", "werecrow", "doomsayer"})]
@ -5063,10 +5031,9 @@ def check_exchange(cli, actor, nick):
del var.SHAMANS[actor] del var.SHAMANS[actor]
if actor in var.LASTGIVEN: if actor in var.LASTGIVEN:
del var.LASTGIVEN[actor] del var.LASTGIVEN[actor]
elif actor_role in var.WOLF_ROLES - {"werecrow", "wolf cub", "alpha wolf"}: elif actor_role in var.WOLF_ROLES - {"werecrow", "wolf cub", "alpha wolf", "doomsayer"}:
if actor in var.KILLS: if actor in var.KILLS:
del var.KILLS[actor] del var.KILLS[actor]
var.WILD_CHILDREN.discard(actor)
elif actor_role == "hunter": elif actor_role == "hunter":
if actor in var.OTHER_KILLS: if actor in var.OTHER_KILLS:
del var.OTHER_KILLS[actor] del var.OTHER_KILLS[actor]
@ -5086,10 +5053,6 @@ def check_exchange(cli, actor, nick):
if var.HVISITED[actor] is not None: if var.HVISITED[actor] is not None:
pm(cli, var.HVISITED[actor], messages["harlot_disappeared"].format(actor)) pm(cli, var.HVISITED[actor], messages["harlot_disappeared"].format(actor))
del var.HVISITED[actor] del var.HVISITED[actor]
elif actor_role in ("seer", "oracle", "augur"):
var.SEEN.discard(actor)
elif actor_role == "wild child": # would that ever happen?
var.IDOLS[nick] = var.IDOLS.pop(actor)
elif actor_role == "hag": elif actor_role == "hag":
if actor in var.LASTHEXED: if actor in var.LASTHEXED:
if var.LASTHEXED[actor] in var.TOBESILENCED and actor in var.HEXED: if var.LASTHEXED[actor] in var.TOBESILENCED and actor in var.HEXED:
@ -5105,6 +5068,10 @@ def check_exchange(cli, actor, nick):
var.ALPHA_WOLVES.discard(actor) var.ALPHA_WOLVES.discard(actor)
if actor in var.KILLS: if actor in var.KILLS:
del var.KILLS[actor] del var.KILLS[actor]
elif actor_role == "doomsayer":
var.SEEN.discard(actor)
if actor in var.KILLS:
del var.KILLS[actor]
elif actor_role == "warlock": elif actor_role == "warlock":
var.CURSED.discard(actor) var.CURSED.discard(actor)
elif actor_role == "turncoat": elif actor_role == "turncoat":
@ -5130,10 +5097,9 @@ def check_exchange(cli, actor, nick):
del var.SHAMANS[nick] del var.SHAMANS[nick]
if nick in var.LASTGIVEN: if nick in var.LASTGIVEN:
del var.LASTGIVEN[nick] del var.LASTGIVEN[nick]
elif nick_role in var.WOLF_ROLES - {"werecrow", "wolf cub", "alpha wolf"}: elif nick_role in var.WOLF_ROLES - {"werecrow", "wolf cub", "alpha wolf", "doomsayer"}:
if nick in var.KILLS: if nick in var.KILLS:
del var.KILLS[nick] del var.KILLS[nick]
var.WILD_CHILDREN.discard(nick)
elif nick_role == "hunter": elif nick_role == "hunter":
if nick in var.OTHER_KILLS: if nick in var.OTHER_KILLS:
del var.OTHER_KILLS[nick] del var.OTHER_KILLS[nick]
@ -5153,10 +5119,6 @@ def check_exchange(cli, actor, nick):
if var.HVISITED[nick] is not None: if var.HVISITED[nick] is not None:
pm(cli, var.HVISITED[nick], messages["harlot_disappeared"].format(nick)) pm(cli, var.HVISITED[nick], messages["harlot_disappeared"].format(nick))
del var.HVISITED[nick] del var.HVISITED[nick]
elif nick_role in ("seer", "oracle", "augur"):
var.SEEN.discard(nick)
elif nick_role == "wild child":
var.IDOLS[actor] = var.IDOLS.pop(nick)
elif nick_role == "hag": elif nick_role == "hag":
if nick in var.LASTHEXED: if nick in var.LASTHEXED:
if var.LASTHEXED[nick] in var.TOBESILENCED and nick in var.HEXED: if var.LASTHEXED[nick] in var.TOBESILENCED and nick in var.HEXED:
@ -5171,11 +5133,17 @@ def check_exchange(cli, actor, nick):
var.ALPHA_WOLVES.discard(nick) var.ALPHA_WOLVES.discard(nick)
if nick in var.KILLS: if nick in var.KILLS:
del var.KILLS[nick] del var.KILLS[nick]
elif nick_role == "doomsayer":
var.SEEN.discard(nick)
if nick in var.KILLS:
del var.KILLS[nick]
elif nick_role == "warlock": elif nick_role == "warlock":
var.CURSED.discard(nick) var.CURSED.discard(nick)
elif nick_role == "turncoat": elif nick_role == "turncoat":
del var.TURNCOATS[nick] del var.TURNCOATS[nick]
evt = Event("exchange_roles", {"actor_messages": [], "nick_messages": []})
evt.dispatch(cli, var, actor, nick, actor_role, nick_role)
var.FINAL_ROLES[actor] = nick_role var.FINAL_ROLES[actor] = nick_role
var.FINAL_ROLES[nick] = actor_role var.FINAL_ROLES[nick] = actor_role
@ -5220,6 +5188,11 @@ def check_exchange(cli, actor, nick):
pm(cli, actor, messages["role_swap"].format(nick_rev_role)) pm(cli, actor, messages["role_swap"].format(nick_rev_role))
pm(cli, nick, messages["role_swap"].format(actor_rev_role)) pm(cli, nick, messages["role_swap"].format(actor_rev_role))
for msg in evt.data["actor_messages"]:
pm(cli, actor, msg)
for msg in evt.data["nick_messages"]:
pm(cli, nick, msg)
wolfchatwolves = set(var.WOLFCHAT_ROLES) wolfchatwolves = set(var.WOLFCHAT_ROLES)
if var.RESTRICT_WOLFCHAT & (var.RW_WOLVES_ONLY_CHAT | var.RW_REM_NON_WOLVES): if var.RESTRICT_WOLFCHAT & (var.RW_WOLVES_ONLY_CHAT | var.RW_REM_NON_WOLVES):
wolfchatwolves = set(var.WOLF_ROLES) wolfchatwolves = set(var.WOLF_ROLES)
@ -5230,8 +5203,6 @@ def check_exchange(cli, actor, nick):
if nick_role == "clone": if nick_role == "clone":
pm(cli, actor, messages["clone_target"].format(nick_target)) pm(cli, actor, messages["clone_target"].format(nick_target))
elif nick_role == "wild child":
pm(cli, actor, messages["wild_child_idol"].format(var.IDOLS[actor]))
elif nick_role in var.TOTEM_ORDER: elif nick_role in var.TOTEM_ORDER:
if nick_role == "shaman": if nick_role == "shaman":
pm(cli, actor, messages["shaman_totem"].format(nick_totem)) pm(cli, actor, messages["shaman_totem"].format(nick_totem))
@ -5274,8 +5245,6 @@ def check_exchange(cli, actor, nick):
if actor_role == "clone": if actor_role == "clone":
pm(cli, nick, messages["clone_target"].format(actor_target)) pm(cli, nick, messages["clone_target"].format(actor_target))
elif actor_role == "wild child":
pm(cli, nick, messages["wild_child_idol"].format(var.IDOLS[nick]))
elif actor_role in var.TOTEM_ORDER: elif actor_role in var.TOTEM_ORDER:
if actor_role == "shaman": if actor_role == "shaman":
pm(cli, nick, messages["shaman_totem"].format(actor_totem)) pm(cli, nick, messages["shaman_totem"].format(actor_totem))
@ -5961,9 +5930,9 @@ def hvisit(cli, nick, chan, rest):
debuglog("{0} ({1}) VISIT: {2} ({3})".format(nick, role, victim, get_role(victim))) debuglog("{0} ({1}) VISIT: {2} ({3})".format(nick, role, victim, get_role(victim)))
chk_nightdone(cli) chk_nightdone(cli)
@cmd("see", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("seer", "oracle", "augur", "doomsayer")) @cmd("see", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("doomsayer",))
def see(cli, nick, chan, rest): def see(cli, nick, chan, rest):
"""Use your paranormal powers to determine the role or alignment of a player.""" """Use your paranormal senses to determine a player's doom."""
role = get_role(nick) role = get_role(nick)
if nick in var.SEEN: if nick in var.SEEN:
pm(cli, nick, messages["seer_fail"]) pm(cli, nick, messages["seer_fail"])
@ -5978,7 +5947,7 @@ def see(cli, nick, chan, rest):
if is_safe(nick, victim): if is_safe(nick, victim):
pm(cli, nick, messages["no_acting_on_succubus"].format("see")) pm(cli, nick, messages["no_acting_on_succubus"].format("see"))
return return
if role == "doomsayer" and in_wolflist(nick, victim): if in_wolflist(nick, victim):
pm(cli, nick, messages["no_see_wolf"]) pm(cli, nick, messages["no_see_wolf"])
return return
victim = choose_target(nick, victim) victim = choose_target(nick, victim)
@ -5986,45 +5955,6 @@ def see(cli, nick, chan, rest):
return return
victimrole = get_role(victim) victimrole = get_role(victim)
vrole = victimrole # keep a copy for logging vrole = victimrole # keep a copy for logging
if role == "seer":
if victim in var.WILD_CHILDREN:
victimrole = "wild child"
elif (victimrole in var.SEEN_WOLF and victimrole not in var.SEEN_DEFAULT) or victim in var.ROLES["cursed villager"]:
victimrole = "wolf"
elif victimrole in var.SEEN_DEFAULT:
victimrole = var.DEFAULT_ROLE
if var.DEFAULT_SEEN_AS_VILL:
victimrole = "villager"
if (victim in var.DECEIVED) ^ (nick in var.DECEIVED):
if victimrole == "wolf":
victimrole = "villager"
else:
victimrole = "wolf"
pm(cli, nick, (messages["seer_success"]).format(victim, victimrole))
debuglog("{0} ({1}) SEE: {2} ({3}) as {4}".format(nick, role, victim, vrole, victimrole))
elif role == "oracle":
iswolf = False
if victim in var.WILD_CHILDREN:
pass
elif (victimrole in var.SEEN_WOLF and victimrole not in var.SEEN_DEFAULT) or victim in var.ROLES["cursed villager"]:
iswolf = True
# deceit totem acts on both target and actor, so if both have them, they cancel each other out
if (victim in var.DECEIVED) ^ (nick in var.DECEIVED):
iswolf = not iswolf
pm(cli, nick, (messages["oracle_success"]).format(victim, "" if iswolf else "\u0002not\u0002 ", "\u0002" if iswolf else ""))
debuglog("{0} ({1}) SEE: {2} ({3}) (Wolf: {4})".format(nick, role, victim, vrole, str(iswolf)))
elif role == "augur":
if victimrole == "amnesiac":
victimrole = var.AMNESIAC_ROLES[victim]
aura = "blue"
if victimrole in var.WOLFTEAM_ROLES:
aura = "red"
elif victimrole in var.TRUE_NEUTRAL_ROLES:
aura = "grey"
pm(cli, nick, (messages["augur_success"]).format(victim, aura))
debuglog("{0} ({1}) SEE: {2} ({3}) as {4} ({5} aura)".format(nick, role, victim, vrole, victimrole, aura))
elif role == "doomsayer":
mode = random.randint(1, 3) mode = random.randint(1, 3)
if mode == 1: if mode == 1:
mode = "DEATH" mode = "DEATH"
@ -6040,7 +5970,6 @@ def see(cli, nick, chan, rest):
var.TOBEDISEASED.add(victim) var.TOBEDISEASED.add(victim)
var.TOBESILENCED.add(victim) var.TOBESILENCED.add(victim)
var.SICK.add(victim) # counts as unable to vote and lets them know at beginning of day that they aren't feeling well var.SICK.add(victim) # counts as unable to vote and lets them know at beginning of day that they aren't feeling well
debuglog("{0} ({1}) SEE: {2} ({3}) - {4}".format(nick, role, victim, vrole, mode)) debuglog("{0} ({1}) SEE: {2} ({3}) - {4}".format(nick, role, victim, vrole, mode))
relay_wolfchat_command(cli, nick, messages["doomsayer_wolfchat"].format(nick, victim), ("doomsayer",), is_wolf_command=True) relay_wolfchat_command(cli, nick, messages["doomsayer_wolfchat"].format(nick, victim), ("doomsayer",), is_wolf_command=True)
@ -6512,28 +6441,23 @@ def charm(cli, nick, chan, rest):
chk_nightdone(cli) chk_nightdone(cli)
@cmd("choose", chan=False, pm=True, playing=True, phases=("night",), roles=("wild child",)) @event_listener("targeted_cmd", priority=9)
def choose_idol(cli, nick, chan, rest): def on_targeted_cmd(evt, cli, var, cmd, actor, orig_target, tags):
"""Pick your idol, if they die, you'll become a wolf!""" # TODO: move beneficial command check into roles/succubus.py
if not var.FIRST_NIGHT: if "beneficial" not in tags and is_safe(actor, evt.data["target"]):
return pm(cli, actor, messages["no_acting_on_succubus"].format(cmd))
if nick in var.IDOLS: evt.stop_processing = True
pm(cli, nick, messages["wild_child_already_picked"]) evt.prevent_default = True
return return
victim = get_victim(cli, nick, re.split(" +", rest)[0], False) if evt.data["misdirection"]:
if not victim: evt.data["target"] = choose_target(actor, evt.data["target"])
if evt.data["exchange"] and check_exchange(cli, actor, evt.data["target"]):
evt.stop_processing = True
evt.prevent_default = True
return return
if nick == victim:
pm(cli, nick, messages["no_target_self"])
return
var.IDOLS[nick] = victim
pm(cli, nick, messages["wild_child_success"].format(victim))
debuglog("{0} ({1}) IDOLIZE: {2} ({3})".format(nick, get_role(nick), victim, get_role(victim)))
chk_nightdone(cli)
@hook("featurelist") # For multiple targets with PRIVMSG @hook("featurelist") # For multiple targets with PRIVMSG
def getfeatures(cli, nick, *rest): def getfeatures(cli, nick, *rest):
@ -6865,31 +6789,6 @@ def transition_night(cli):
if var.ALPHA_ENABLED and role == "alpha wolf" and wolf not in var.ALPHA_WOLVES: if var.ALPHA_ENABLED and role == "alpha wolf" and wolf not in var.ALPHA_WOLVES:
pm(cli, wolf, messages["wolf_bite"]) pm(cli, wolf, messages["wolf_bite"])
for seer in list_players(("seer", "oracle", "augur")):
pl = ps[:]
random.shuffle(pl)
role = get_role(seer)
pl.remove(seer) # remove self from list
a = "a"
if role in ("oracle", "augur"):
a = "an"
if role == "seer":
what = messages["seer_ability"]
elif role == "oracle":
what = messages["oracle_ability"]
elif role == "augur":
what = messages["augur_ability"]
else:
what = messages["seer_role_bug"]
if seer in var.PLAYERS and not is_user_simple(seer):
pm(cli, seer, messages["seer_role_info"].format(a, role, what))
else:
pm(cli, seer, messages["seer_simple"].format(a, role)) # !simple
pm(cli, seer, "Players: " + ", ".join(pl))
for harlot in var.ROLES["harlot"]: for harlot in var.ROLES["harlot"]:
pl = ps[:] pl = ps[:]
random.shuffle(pl) random.shuffle(pl)
@ -7167,12 +7066,6 @@ def transition_night(cli):
else: else:
pm(cli, lycan, messages["lycan_simple"]) pm(cli, lycan, messages["lycan_simple"])
for child in var.ROLES["wild child"]:
if child in var.PLAYERS and not is_user_simple(child):
pm(cli, child, messages["child_notify"])
else:
pm(cli, child, messages["child_simple"])
for v_ghost, who in var.VENGEFUL_GHOSTS.items(): for v_ghost, who in var.VENGEFUL_GHOSTS.items():
if who[0] == "!": if who[0] == "!":
continue continue
@ -7588,7 +7481,6 @@ def start(cli, nick, chan, forced = False, restart = ""):
var.SICK = set() var.SICK = set()
var.DULLAHAN_TARGETS = {} var.DULLAHAN_TARGETS = {}
var.DECEIVED = set() var.DECEIVED = set()
var.IDOLS = {}
var.DEADCHAT_PLAYERS = set() var.DEADCHAT_PLAYERS = set()
var.SPECTATING_WOLFCHAT = set() var.SPECTATING_WOLFCHAT = set()
@ -8364,9 +8256,17 @@ def myrole(cli, nick, chan, rest):
role = "villager" role = "villager"
elif role in ("amnesiac", "vengeful ghost"): elif role in ("amnesiac", "vengeful ghost"):
role = var.DEFAULT_ROLE role = var.DEFAULT_ROLE
evt = Event("myrole", {"role": role, "messages": []})
evt.dispatch(cli, var, nick)
role = evt.data["role"]
an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" an = "n" if role.startswith(("a", "e", "i", "o", "u")) else ""
pm(cli, nick, messages["show_role"].format(an, role)) pm(cli, nick, messages["show_role"].format(an, role))
for msg in evt.data["messages"]:
pm(cli, nick, msg)
# Remind shamans what totem they have # Remind shamans what totem they have
if role in var.TOTEM_ORDER and role != "crazed shaman" and var.PHASE == "night" and nick not in var.SHAMANS: if role in var.TOTEM_ORDER and role != "crazed shaman" and var.PHASE == "night" and nick not in var.SHAMANS:
pm(cli, nick, messages["totem_simple"].format(var.TOTEMS[nick])) pm(cli, nick, messages["totem_simple"].format(var.TOTEMS[nick]))
@ -8375,9 +8275,6 @@ def myrole(cli, nick, chan, rest):
if role == "clone" and nick in var.CLONED: if role == "clone" and nick in var.CLONED:
pm(cli, nick, messages["clone_target"].format(var.CLONED[nick])) pm(cli, nick, messages["clone_target"].format(var.CLONED[nick]))
if role == "wild child" and nick in var.IDOLS:
pm(cli, nick, messages["wild_child_idol"].format(var.IDOLS[nick]))
# Give minion the wolf list they would have recieved night one # Give minion the wolf list they would have recieved night one
if role == "minion": if role == "minion":
wolves = [] wolves = []
@ -8861,12 +8758,12 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
special_case.append("need to kill {0}".format(", ".join(var.DULLAHAN_TARGETS[nickname] - var.DEAD))) special_case.append("need to kill {0}".format(", ".join(var.DULLAHAN_TARGETS[nickname] - var.DEAD)))
else: else:
special_case.append("All targets dead") special_case.append("All targets dead")
elif role == "wild child":
if nickname in var.IDOLS: evt = Event("revealroles_role", {"special_case": special_case})
special_case.append("picked {0} as idol".format(var.IDOLS[nickname])) evt.dispatch(cli, var, nickname, role)
else: special_case = evt.data["special_case"]
special_case.append("no idol picked yet")
if nickname not in var.ORIGINAL_ROLES[role] and role not in var.TEMPLATE_RESTRICTIONS: if not evt.prevent_default and nickname not in var.ORIGINAL_ROLES[role] and role not in var.TEMPLATE_RESTRICTIONS:
for old_role in role_order(): # order doesn't matter here, but oh well for old_role in role_order(): # order doesn't matter here, but oh well
if nickname in var.ORIGINAL_ROLES[old_role] and nickname not in var.ROLES[old_role]: if nickname in var.ORIGINAL_ROLES[old_role] and nickname not in var.ROLES[old_role]:
special_case.append("was {0}".format(old_role)) special_case.append("was {0}".format(old_role))
@ -8913,6 +8810,9 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
if var.CHARMED | var.TOBECHARMED: if var.CHARMED | var.TOBECHARMED:
output.append("\u0002charmed players\u0002: {0}".format(", ".join(var.CHARMED | var.TOBECHARMED))) output.append("\u0002charmed players\u0002: {0}".format(", ".join(var.CHARMED | var.TOBECHARMED)))
evt = Event("revealroles", {"output": output})
evt.dispatch(cli, var)
if botconfig.DEBUG_MODE: if botconfig.DEBUG_MODE:
cli.msg(chan, break_long_message(output, " | ")) cli.msg(chan, break_long_message(output, " | "))
else: else: