diff --git a/src/handler.py b/src/handler.py index 79487ce..6703c6b 100644 --- a/src/handler.py +++ b/src/handler.py @@ -12,7 +12,7 @@ import botconfig import src.settings as var from src import decorators, wolfgame, events, channels, hooks, users, errlog as log, stream_handler as alog from src.messages import messages -from src.utilities import reply, get_role, get_templates +from src.utilities import reply from src.functions import get_participants, get_all_roles from src.dispatcher import MessageDispatcher from src.decorators import handle_error diff --git a/src/roles/piper.py b/src/roles/piper.py index bf040dc..3eefed7 100644 --- a/src/roles/piper.py +++ b/src/roles/piper.py @@ -154,11 +154,6 @@ def on_transition_day_begin(evt, var): CHARMED.update(tocharm) TOBECHARMED.clear() -@event_listener("night_acted") -def on_night_acted(evt, var, target, actor): - if target in TOBECHARMED: - evt.data["acted"] = True - @event_listener("chk_nightdone") def on_chk_nightdone(evt, var): evt.data["actedcount"] += len(TOBECHARMED.keys()) diff --git a/src/roles/skel.py b/src/roles/skel.py index 899dc31..8c4518f 100644 --- a/src/roles/skel.py +++ b/src/roles/skel.py @@ -12,6 +12,67 @@ from src.decorators import command, event_listener from src.messages import messages from src.events import Event -# Skeleton file for new roles, basically to get all the imports right and stuff +# Skeleton file for new roles. Not all events are represented, only the most common ones. + +# Add to evt.data["actedcount"] and evt.data["nightroles"] if this role can act during night +# nightroles lists all Users who have this role and are capable of acting tonight +# actedcount should be added to (via +=) with everyone who has this role and has already acted +# Used to determine when all roles have acted (and therefore night should end) +@event_listener("chk_nightdone") +def on_chk_nightdone(evt, var): + pass + +# Set evt.data["acted"] = True if target acted during night and spy is able to know that info +# Used for werecrow and insomniac +@event_listener("night_acted") +def on_night_acted(evt, var, target, spy): + pass + +# PM players who have this role with instructions +# Set priority=2 if this is a main role, and priority=5 if this is a secondary role +# (secondary roles are like gunner, assassin, etc. which by default stack on top of main roles) +@event_listener("transition_night_end", priority=2) +def on_transition_night_end(evt, var): + pass + +# Update any role state that happens when someone with this role exchanges with someone else. +@event_listener("exchange_roles") +def on_exchange(evt, var, actor, target, actor_role, target_role): + pass + +# Update evt.data["special"] with the Users who have this role as their main role, +# assuming this is some sort of special role (can act). Do *not* update this if this is a wolfteam +# special role, simply remove the event instead. +# Used by wolf mystic to determine the number of special villagers still alive. +@event_listener("get_special") +def on_get_special(evt, var): + pass + +# Update any game state which happens when player dies. If this role does things upon death, +# ensure that you check death_triggers (it's a bool) before firing it. +@event_listener("del_player") +def on_del_player(evt, var, player, mainrole, allroles, death_triggers): + pass + +# Clear all game state. Called whenever the game ends. +@event_listener("reset") +def on_reset(evt, var): + pass + +# Swap out a user with a different one. Update all game state to use the new User. +@event_listener("swap_player") +def on_swap_player(evt, var, old, new): + pass + +# Gets metadata about this role; kind will be a str with one of the following values: +# night_kills: Add metadata about any deaths this role can cause at night which use the standard +# death message (i.e. do not have a custom death message). Set the data as follows: +# evt.data["rolename"] = N (where N is the max # of deaths that this role can cause) +# Used in !stats in order to handle interactions between roles in a generic fashion so that +# more accurate results can be reported. +@event_listener("get_role_metadata") +def on_get_role_metadata(evt, var, kind): + pass + # vim: set sw=4 expandtab: diff --git a/src/roles/traitor.py b/src/roles/traitor.py index fca9302..492bfe0 100644 --- a/src/roles/traitor.py +++ b/src/roles/traitor.py @@ -34,13 +34,18 @@ def on_update_stats1(evt, var, player, mainrole, revealroles, allroles): evt.data["possible"].add("traitor") @event_listener("update_stats", priority=3) -def on_update_stats3(evt, var, player, mainrole, revealroles, allroles): +def on_update_stats3(evt, var, player, mainrole, revealrole, allroles): # if this is a night death and we know for sure that wolves (and only wolves) # killed, then that kill cannot be traitor as long as they're in wolfchat. # ismain True = night death, False = chain death; chain deaths can be traitors # even if only wolves killed, so we short-circuit there as well - # TODO: an observant user will be able to determine if traitor dies due to luck/misdirection totem - # redirecting a wolf kill onto traitor + # TODO: luck/misdirection totem can leak info due to our short-circuit below this comment. + # If traitor dies due to one of these totems, both traitor count and villager count is reduced by + # 1. If traitor does not die, and no other roles can kill (no death totems), then traitor count is unchanged + # and villager count is reduced by 1. We should make it so that both counts are reduced when + # luck/misdirection are potentially in play. + # FIXME: this doesn't check RESTRICT_WOLFCHAT to see if traitor was removed from wolfchat. If + # they've been removed, they can be killed like normal so all this logic is meaningless. if "traitor" not in evt.data["possible"] or not evt.params.ismain or mainrole == "traitor": return if var.PHASE == "day" and var.GAMEPHASE == "night": diff --git a/src/utilities.py b/src/utilities.py index a375bc8..fc8696e 100644 --- a/src/utilities.py +++ b/src/utilities.py @@ -4,17 +4,17 @@ import re import botconfig import src.settings as var -from src import proxy, debuglog, users +from src import proxy, debuglog from src.events import Event from src.messages import messages __all__ = ["pm", "is_fake_nick", "mass_mode", "mass_privmsg", "reply", "is_user_simple", "is_user_notice", "in_wolflist", "relay_wolfchat_command", "chk_nightdone", "chk_decision", - "chk_win", "irc_lower", "irc_equals", "is_role", "match_hostmask", + "chk_win", "irc_lower", "irc_equals", "match_hostmask", "is_owner", "is_admin", "plural", "singular", "list_players", "list_players_and_roles", "get_role", "get_roles", - "get_reveal_role", "get_templates", "change_role", "role_order", "break_long_message", + "get_reveal_role", "change_role", "role_order", "break_long_message", "complete_match","complete_one_match", "get_victim", "get_nick", "InvalidModeException"] # message either privmsg or notice, depending on user settings def pm(cli, target, message): @@ -211,8 +211,6 @@ def irc_lower(nick): def irc_equals(nick1, nick2): return irc_lower(nick1) == irc_lower(nick2) -is_role = lambda plyr, rol: rol in var.ROLES and users._get(plyr) in var.ROLES[rol] - def match_hostmask(hostmask, nick, ident, host): # support n!u@h, u@h, or just h by itself matches = re.match('(?:(?:(.*?)!)?(.*?)@)?(.*)', hostmask) @@ -322,21 +320,10 @@ def list_players_and_roles(): return {u.nick: r for u, r in var.MAIN_ROLES.items()} def get_role(p): - # FIXME: make the arg a user instead of a nick + # TODO DEPRECATED: replace with get_main_role(user) from src import users - from src.functions import get_participants - user = users._get(p) - role = var.MAIN_ROLES.get(user, None) - if role is not None: - return role - # not found in player list, see if they're a special participant - if user in get_participants(): - evt = Event("get_participant_role", {"role": None}) - evt.dispatch(var, user) - role = evt.data["role"] - if role is None: - raise ValueError("User {0} isn't playing and has no defined participant role".format(user)) - return role + from src.functions import get_main_role + return get_main_role(users._get(p)) def get_roles(*roles, rolemap=None): if rolemap is None: @@ -370,15 +357,6 @@ def get_reveal_role(nick): else: return "village member" -def get_templates(nick): - mainrole = get_role(nick) - tpl = [] - for role, users in var.ROLES.items(): - if users._get(nick) in users and role != mainrole: - tpl.append(role) - - return tpl - # TODO: move this to functions.py def change_role(user, oldrole, newrole, set_final=True): var.ROLES[oldrole].remove(user) @@ -425,6 +403,7 @@ def complete_one_match(string, matches): #wrapper around complete_match() used for roles def get_victim(cli, nick, victim, in_chan, self_in_list=False, bot_in_list=False): + from src import users chan = botconfig.CHANNEL if in_chan else nick if not victim: reply(cli, nick, chan, messages["not_enough_parameters"], private=True)