diff --git a/messages/en.json b/messages/en.json index f2ea3dc..2d913c7 100644 --- a/messages/en.json +++ b/messages/en.json @@ -441,7 +441,7 @@ "harlot_turn": "While out visiting last night, you were overpowered by a large werewolf and bitten. Shortly thereafter, you found yourself turning into a werewolf yourself!", "bitten_turn": "You woke suddenly last night to a sharp pain, as you were bit by a large werewolf. Shortly thereafter, you found yourself turning into a werewolf yourself!", "wolfchat_new_member": "\u0002{0}\u0002 is now a \u0002{1}\u0002!", - "amnesia_clear": "Your amnesia clears and you now remember that you are a{0} \u0002{1}\u0002!", + "amnesia_clear": "Your amnesia clears and you now remember that you are {0} \u0002{1}\u0002!", "amnesia_wolfchat": "\u0002{0}\u0002 is now a \u0002{1}\u0002!", "wolf_notify": "You are a \u0002{0}wolf\u0002. It is your job to kill all the villagers. Use \"kill \" to kill a villager.", "cursed_traitor_notify": "You are a \u0002{0}cursed traitor\u0002. Normally, you would be seen as a villager by the seer and oracle, but since you're cursed, you are seen as a wolf.", diff --git a/src/functions.py b/src/functions.py index 368bb5a..62b9ade 100644 --- a/src/functions.py +++ b/src/functions.py @@ -83,13 +83,10 @@ def get_all_roles(user): return {role for role, users in var.ROLES.items() if user in users} def get_reveal_role(user): - # FIXME: when amnesiac and clone are split, move this into an event - if var.HIDDEN_AMNESIAC and user in var.ORIGINAL_ROLES["amnesiac"]: - role = "amnesiac" - elif var.HIDDEN_CLONE and user in var.ORIGINAL_ROLES["clone"]: + # FIXME: when clone is split, move this into an event + role = get_main_role(user) + if var.HIDDEN_CLONE and user in var.ORIGINAL_ROLES["clone"]: role = "clone" - else: - role = get_main_role(user) evt = Event("get_reveal_role", {"role": role}) evt.dispatch(var, user) diff --git a/src/gamemodes.py b/src/gamemodes.py index 515c8f9..9ce5e7a 100644 --- a/src/gamemodes.py +++ b/src/gamemodes.py @@ -81,7 +81,7 @@ class GameMode: elif val == "team" and not hasattr(self, "STATS_TYPE"): self.STATS_TYPE = "team" elif key in ("stats type", "stats"): - if val not in ("default", "accurate", "team", "disabled"): + if val not in ("default", "accurate", "team", "disabled", "experimental"): raise InvalidModeException(messages["invalid_stats"].format(val)) self.STATS_TYPE = val elif key == "abstain": @@ -184,7 +184,7 @@ class DefaultMode(GameMode): if len(evt.data["votelist"][users.Bot]) == len(set(evt.params.voters) - evt.data["not_lynching"]): channels.Main.send(messages["villagergame_nope"]) from src.wolfgame import stop_game - stop_game("wolves") + stop_game(var, "wolves") evt.prevent_default = True else: del evt.data["votelist"][users.Bot] @@ -289,7 +289,7 @@ class VillagergameMode(GameMode): if len(evt.data["votelist"][users.Bot]) == len(set(evt.params.voters) - evt.data["not_lynching"]): channels.Main.send(messages["villagergame_win"]) from src.wolfgame import stop_game - stop_game("everyone") + stop_game(var, "everyone") evt.prevent_default = True else: del evt.data["votelist"][users.Bot] diff --git a/src/roles/_shaman_helper.py b/src/roles/_shaman_helper.py index 3157756..4cdd15a 100644 --- a/src/roles/_shaman_helper.py +++ b/src/roles/_shaman_helper.py @@ -66,7 +66,7 @@ def setup_variables(rolename, *, knows_totem, get_tags): SHAMANS.clear() @event_listener("revealroles_role") - def on_revealroles(evt, var, wrapper, user, role): + def on_revealroles(evt, var, user, role): if role == rolename and user in TOTEMS: if user in SHAMANS: evt.data["special_case"].append("giving {0} totem to {1}".format(TOTEMS[user], SHAMANS[user][0])) @@ -302,17 +302,6 @@ def on_chk_decision_lynch3(evt, var, voters): rev_evt = Event("revealing_totem", {"role": role}) rev_evt.dispatch(var, votee) role = rev_evt.data["role"] - # TODO: once amnesiac is split, roll this into the revealing_totem event - if role == "amnesiac": - role = var.AMNESIAC_ROLES[votee.nick] - change_role(votee, "amnesiac", role) - var.AMNESIACS.add(votee.nick) - votee.send(messages["totem_amnesia_clear"]) - # If wolfteam, don't bother giving list of wolves since night is about to start anyway - # Existing wolves also know that someone just joined their team because revealing totem says what they are - # If turncoat, set their initial starting side to "none" just in case game ends before they can set it themselves - if role == "turncoat": - var.TURNCOATS[votee.nick] = ("none", -1) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" channels.Main.send(messages["totem_reveal"].format(votee, an, role)) diff --git a/src/roles/amnesiac.py b/src/roles/amnesiac.py new file mode 100644 index 0000000..c10dd2c --- /dev/null +++ b/src/roles/amnesiac.py @@ -0,0 +1,134 @@ +import re +import random +import itertools +import math +from collections import defaultdict + +import botconfig +from src.utilities import * +from src import channels, users, debuglog, errlog, plog +from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target, is_known_wolf_ally +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 + +ROLES = UserDict() # type: Dict[users.User, str] + +@event_listener("role_assignment") +def on_role_assignment(evt, var, gamemode, pl): + # matchmaker is blacklisted if AMNESIAC_NIGHTS > 1 due to only being able to act night 1 + # clone and traitor are blacklisted due to assumptions made in default !stats computations. + # If you remove these from the blacklist you will need to modify the default !stats logic + # chains in order to correctly account for these. As a forewarning, such modifications are + # nontrivial and will likely require a great deal of thought (and likely new tracking vars) + + roles = var.ROLE_GUIDE.keys() - var.TEMPLATE_RESTRICTIONS.keys() - var.AMNESIAC_BLACKLIST - {var.DEFAULT_ROLE, "amnesiac", "clone", "traitor"} + if var.AMNESIAC_NIGHTS > 1 and "matchmaker" in roles: + roles.remove("matchmaker") + for amnesiac in get_all_players(("amnesiac",)): + ROLES[amnesiac] = random.choice(list(roles)) + +@event_listener("transition_night_begin") +def on_transition_night_begin(evt, var): + if var.NIGHT_COUNT == var.AMNESIAC_NIGHTS: + amnesiacs = get_all_players(("amnesiac",)) + + for amn in amnesiacs: + role = ROLES[amn] + change_role(amn, "amnesiac", role) + evt = Event("new_role", {}) + evt.dispatch(var, amn, role) + if var.FIRST_NIGHT: # we don't need to tell them twice if they remember right away + continue + showrole = role + if showrole in var.HIDDEN_VILLAGERS: + showrole = "villager" + elif showrole in var.HIDDEN_ROLES: + showrole = var.DEFAULT_ROLE + a = "a" + if showrole.startswith(("a", "e", "i", "o", "u")): + a = "an" + amn.send(messages["amnesia_clear"].format(a, showrole)) + if is_known_wolf_ally(amn, amn): + if role in var.WOLF_ROLES: + relay_wolfchat_command(amn.client, amn.nick, messages["amnesia_wolfchat"].format(amn, showrole), var.WOLF_ROLES, is_wolf_command=True, is_kill_command=True) + else: + relay_wolfchat_command(amn.client, amn.nick, messages["amnesia_wolfchat"].format(amn, showrole), var.WOLFCHAT_ROLES) + + debuglog("{0} REMEMBER: {1} as {2}".format(amn, role, showrole)) + +@event_listener("new_role") +def on_new_role(evt, var, user, role): + if role == "turncoat": # FIXME: Need to split into turncoat.py when split + var.TURNCOATS[user.nick] = ("none", -1) + if role == "doctor": # FIXME: Need to split into doctor.py when split + var.DOCTORS[user.nick] = math.ceil(var.DOCTOR_IMMUNIZATION_MULTIPLIER * len(get_players())) + +@event_listener("investigate") +def on_investigate(evt, var, actor, target): + if evt.data["role"] == "amnesiac": + evt.data["role"] = ROLES[target] + +@event_listener("exchange_roles") +def on_exchange_roles(evt, var, actor, target, actor_role, target_role): + if actor_role == "amnesiac": + actor_role = ROLES[actor] + if target in ROLES: + ROLES[actor] = ROLES[target] + ROLES[target] = actor_role + else: + del ROLES[actor] + ROLES[target] = actor_role + + if target_role == "amnesiac": + if actor not in ROLES: + target_role = ROLES[target] + ROLES[actor] = target_role + del ROLES[target] + else: # we swapped amnesiac_roles earlier on, get our version back + target_role = ROLES[actor] + + evt.data["actor_role"] = actor_role + evt.data["target_role"] = target_role + +@event_listener("revealing_totem") +def on_revealing_totem(evt, var, votee): + if evt.data["role"] == "amnesiac": + role = ROLES[votee] + change_role(votee, "amnesiac", role) + votee.send(messages["totem_amnesia_clear"]) + nevt = Event("new_role", {}) + nevt.dispatch(var, votee, role) + evt.data["role"] = role + # If wolfteam, don't bother giving list of wolves since night is about to start anyway + # Existing wolves also know that someone just joined their team because revealing totem says what they are + +@event_listener("get_reveal_role") +def on_reveal_role(evt, var, user): + if var.HIDDEN_AMNESIAC and user in var.ORIGINAL_ROLES["amnesiac"]: + evt.data["role"] = "amnesiac" + +@event_listener("get_endgame_message") +def on_get_endgame_message(evt, var, role, players, original_roles): + if role == "amnesiac": + for player in players: + evt.data["message"].append("\u0002{0}\u0002 (would be {1})".format(player, ROLES[player])) + evt.stop_processing = True + +@event_listener("revealroles_role") +def on_revealroles_role(evt, var, user, role): + if role == "amnesiac": + evt.data["special_case"].append("will become {0}".format(ROLES[user])) + +@event_listener("update_stats") +def on_update_stats(evt, var, player, mainrole, revealrole, allroles): + if not var.HIDDEN_AMNESIAC and var.NIGHT_COUNT >= var.AMNESIAC_NIGHTS: + if not var.AMNESIAC_BLACKLIST & {mainrole, revealrole}: # make sure roles aren't blacklisted + evt.data["possible"].add("amnesiac") + +@event_listener("reset") +def on_reset(evt, var): + ROLES.clear() + +# vim: set sw=4 expandtab: diff --git a/src/roles/augur.py b/src/roles/augur.py index be6298e..aa83d53 100644 --- a/src/roles/augur.py +++ b/src/roles/augur.py @@ -33,9 +33,6 @@ def see(var, wrapper, message): targrole = get_main_role(target) trole = targrole # keep a copy for logging - if targrole == "amnesiac": - targrole = var.AMNESIAC_ROLES[target.nick] - evt = Event("investigate", {"role": targrole}) evt.dispatch(var, wrapper.source, target) targrole = evt.data["role"] diff --git a/src/roles/detective.py b/src/roles/detective.py index 015b07b..c1b7cc3 100644 --- a/src/roles/detective.py +++ b/src/roles/detective.py @@ -31,9 +31,6 @@ def investigate(var, wrapper, message): target = evt.data["target"] targrole = get_main_role(target) - if targrole == "amnesiac": - targrole = var.AMNESIAC_ROLES[target.nick] - evt = Event("investigate", {"role": targrole}) evt.dispatch(var, wrapper.source, target) targrole = evt.data["role"] diff --git a/src/roles/dullahan.py b/src/roles/dullahan.py index fc7f4be..e0d3083 100644 --- a/src/roles/dullahan.py +++ b/src/roles/dullahan.py @@ -199,7 +199,7 @@ def on_myrole(evt, var, user): evt.data["messages"].append(messages["dullahan_targets_dead"]) @event_listener("revealroles_role") -def on_revealroles_role(evt, var, wrapper, user, role): +def on_revealroles_role(evt, var, user, role): if role == "dullahan" and user in TARGETS: targets = set(TARGETS[user]) for target in TARGETS[user]: diff --git a/src/roles/investigator.py b/src/roles/investigator.py index 23e6911..c7d410c 100644 --- a/src/roles/investigator.py +++ b/src/roles/investigator.py @@ -51,11 +51,6 @@ def investigate(var, wrapper, message): t1role = get_main_role(target1) t2role = get_main_role(target2) - # FIXME: split into amnesiac via investigate event once amnesiac is split - if t1role == "amnesiac": - t1role = var.AMNESIAC_ROLES[target1.nick] - if t2role == "amnesiac": - t2role = var.AMNESIAC_ROLES[target2.nick] evt = Event("investigate", {"role": t1role}) evt.dispatch(var, wrapper.source, target1) diff --git a/src/roles/madscientist.py b/src/roles/madscientist.py index 7f14be0..3081f5a 100644 --- a/src/roles/madscientist.py +++ b/src/roles/madscientist.py @@ -169,7 +169,7 @@ def on_myrole(evt, var, user): evt.data["messages"].append(messages["mad_scientist_myrole_targets"].format(target1, target2)) @event_listener("revealroles_role") -def on_revealroles(evt, var, wrapper, user, role): +def on_revealroles(evt, var, user, role): if role == "mad scientist": pl = get_players() target1, target2 = _get_targets(var, pl, user) diff --git a/src/roles/succubus.py b/src/roles/succubus.py index b45b961..1faacc9 100644 --- a/src/roles/succubus.py +++ b/src/roles/succubus.py @@ -316,6 +316,12 @@ def on_vg_kill(evt, var, ghost, target): if ghost in ENTRANCED: evt.data["pl"] -= get_all_players(("succubus",)) +@event_listener("new_role") +def on_new_role(evt, var, user, role): + if role == "succubus" and user in ENTRANCED: + ENTRANCED.remove(user) + user.send(messages["no_longer_entranced"]) + @event_listener("reset") def on_reset(evt, var): global ALL_SUCC_IDLE diff --git a/src/roles/traitor.py b/src/roles/traitor.py index f517c0e..32d42bf 100644 --- a/src/roles/traitor.py +++ b/src/roles/traitor.py @@ -22,7 +22,7 @@ def on_get_reveal_role(evt, var, user): evt.data["role"] = var.DEFAULT_ROLE @event_listener("get_final_role") -def on_get_final_role(evt, cli, var, nick, role): +def on_get_final_role(evt, var, user, role): # if a traitor turns we want to show them as traitor in the end game readout # instead of "wolf (was traitor)" if role == "traitor" and evt.data["role"] == "wolf": diff --git a/src/roles/villager.py b/src/roles/villager.py index 15dad86..1801a76 100644 --- a/src/roles/villager.py +++ b/src/roles/villager.py @@ -68,4 +68,18 @@ def on_chk_win(evt, var, rolemap, mainroles, lpl, lwolves, lrealwolves): evt.data["winner"] = "wolves" evt.data["message"] = messages["wolf_win_greater"] +@event_listener("get_final_role", priority=0) +def on_get_final_role(evt, var, user, role): + if user.nick in var.FINAL_ROLES: + evt.data["role"] = var.FINAL_ROLES[user.nick] + +@event_listener("get_endgame_message", priority=10) +def on_get_endgame_message(evt, var, role, players, original_roles): + for player in players: + if player in original_roles and role not in var.TEMPLATE_RESTRICTIONS: + evt.data["message"].append("\u0002{0}\u0002 ({1}{2})".format(player, "" if evt.data["done"] else "was ", original_roles[player])) + evt.data["done"] = True + else: + evt.data["message"].append("\u0002{0}\u0002".format(player)) + # vim: set sw=4 expandtab: diff --git a/src/roles/wildchild.py b/src/roles/wildchild.py index 8388bae..19b2cb7 100644 --- a/src/roles/wildchild.py +++ b/src/roles/wildchild.py @@ -148,7 +148,7 @@ def on_transition_night_end(evt, var): child.send(messages[to_send]) @event_listener("revealroles_role") -def on_revealroles_role(evt, var, wrapper, user, role): +def on_revealroles_role(evt, var, user, role): if role == "wild child": if user.nick in IDOLS: evt.data["special_case"].append("picked {0} as idol".format(IDOLS[user.nick])) diff --git a/src/wolfgame.py b/src/wolfgame.py index 65eb961..f33f2dc 100644 --- a/src/wolfgame.py +++ b/src/wolfgame.py @@ -403,7 +403,7 @@ def forced_exit(var, wrapper, message): if var.PHASE in var.GAME_PHASES: if var.PHASE == "join" or force or wrapper.source.nick == "": - stop_game(log=False) + stop_game(var, log=False) else: wrapper.pm(messages["stop_bot_ingame_safeguard"].format( what="stop", cmd="fdie", prefix=botconfig.CMD_CHAR)) @@ -448,7 +448,7 @@ def restart_program(var, wrapper, message): if var.PHASE in var.GAME_PHASES: if var.PHASE == "join" or force: - stop_game(log=False) + stop_game(var, log=False) else: wrapper.pm(messages["stop_bot_ingame_safeguard"].format( what="restart", cmd="frestart", prefix=botconfig.CMD_CHAR)) @@ -1963,7 +1963,7 @@ def show_votes(cli, nick, chan, rest): reply(cli, nick, chan, the_message) -def stop_game(winner="", abort=False, additional_winners=None, log=True): +def stop_game(var, winner="", abort=False, additional_winners=None, log=True): if abort: channels.Main.send(messages["role_attribution_failed"]) if var.DAY_START_TIME: @@ -1994,36 +1994,31 @@ def stop_game(winner="", abort=False, additional_winners=None, log=True): if role in var.TEMPLATE_RESTRICTIONS.keys(): continue for p in playerlist: - final = var.FINAL_ROLES.get(p.nick, role) - if role != final: + # The final role is set at priority 0, other roles can override that + evt = Event("get_final_role", {"role": role}) + evt.dispatch(var, p, role) + if role != evt.data["role"]: origroles[p] = role rolelist[role].remove(p) - rolelist[final].add(p) - prev = False + rolelist[evt.data["role"]].add(p) + + done = False for role in role_order(): if len(rolelist[role]) == 0: continue - playersformatted = [] - for p in rolelist[role]: - if p in origroles and role not in var.TEMPLATE_RESTRICTIONS.keys(): - playersformatted.append("\u0002{0}\u0002 ({1}{2})".format(p, - "" if prev else "was ", origroles[p])) - prev = True - elif role == "amnesiac": - playersformatted.append("\u0002{0}\u0002 (would be {1})".format(p, - var.AMNESIAC_ROLES[p.nick])) - else: - playersformatted.append("\u0002{0}\u0002".format(p)) + evt = Event("get_endgame_message", {"message": [], "done": done}) + evt.dispatch(var, role, rolelist[role], origroles) + + msg = evt.data["message"] + done = evt.data["done"] + if len(rolelist[role]) == 2: - msg = "The {1} were {0[0]} and {0[1]}." - roles_msg.append(msg.format(playersformatted, plural(role))) + roles_msg.append("The {1} were {0[0]} and {0[1]}.".format(msg, plural(role))) elif len(rolelist[role]) == 1: - roles_msg.append("The {1} was {0[0]}.".format(playersformatted, role)) + roles_msg.append("The {1} was {0[0]}.".format(msg, role)) else: - msg = "The {2} were {0}, and {1}." - roles_msg.append(msg.format(", ".join(playersformatted[0:-1]), - playersformatted[-1], - plural(role))) + roles_msg.append("The {2} were {0}, and {1}.".format(", ".join(msg[0:-1]), msg[-1], plural(role))) + message = "" count = 0 if not abort: @@ -2339,7 +2334,7 @@ def chk_win_conditions(rolemap, mainroles, end_game=True, winner=None): if end_game: debuglog("WIN:", winner) channels.Main.send(message) - stop_game(winner, additional_winners=event.data["additional_winners"]) + stop_game(var, winner, additional_winners=event.data["additional_winners"]) return True @handle_error @@ -2390,8 +2385,9 @@ def del_player(player, *, devoice=True, end_game=True, death_triggers=True, kill # clone does NOT get any of nick's templates (gunner/assassin/etc.) del var.CLONED[clone.nick] if mainrole == "amnesiac": + from src.roles.amnesiac import ROLES # clone gets the amnesiac's real role - sayrole = var.AMNESIAC_ROLES[player.nick] + sayrole = ROLES[player] else: sayrole = mainrole change_role(clone, "clone", sayrole) @@ -2970,7 +2966,7 @@ def rename_player(var, user, prefix): if prefix in dictvar.keys(): del dictvar[prefix] for dictvar in (var.FINAL_ROLES, var.TURNCOATS, - var.DOCTORS, var.BITTEN_ROLES, var.LYCAN_ROLES, var.AMNESIAC_ROLES): + var.DOCTORS, var.BITTEN_ROLES, var.LYCAN_ROLES): if prefix in dictvar.keys(): dictvar[nick] = dictvar.pop(prefix) # defaultdict(list), where keys are nicks and items in list do not matter @@ -2999,7 +2995,7 @@ def rename_player(var, user, prefix): b = nick var.EXCHANGED_ROLES[idx] = (a, b) for setvar in (var.HEXED, var.SILENCED, var.MATCHMAKERS, var.PASSED, - var.JESTERS, var.AMNESIACS, var.LYCANTHROPES, var.LUCKY, var.DISEASED, + var.JESTERS, var.LYCANTHROPES, var.LUCKY, var.DISEASED, var.MISDIRECTED, var.EXCHANGED, var.IMMUNIZED, var.CURED_LYCANS, var.ALPHA_WOLVES, var.CURSED, var.PRIESTS): if prefix in setvar: @@ -3919,15 +3915,7 @@ def check_exchange(cli, actor, nick): # var.PASSED is used by many roles var.PASSED.discard(actor) - if actor_role == "amnesiac": - actor_role = var.AMNESIAC_ROLES[actor] - if nick in var.AMNESIAC_ROLES: - var.AMNESIAC_ROLES[actor] = var.AMNESIAC_ROLES[nick] - var.AMNESIAC_ROLES[nick] = actor_role - else: - del var.AMNESIAC_ROLES[actor] - var.AMNESIAC_ROLES[nick] = actor_role - elif actor_role == "clone": + if actor_role == "clone": if actor in var.CLONED: actor_target = var.CLONED.pop(actor) elif actor_role in ("werecrow", "sorcerer"): @@ -3955,14 +3943,7 @@ def check_exchange(cli, actor, nick): # var.PASSED is used by many roles var.PASSED.discard(nick) - if nick_role == "amnesiac": - if actor not in var.AMNESIAC_ROLES: - nick_role = var.AMNESIAC_ROLES[nick] - var.AMNESIAC_ROLES[actor] = nick_role - del var.AMNESIAC_ROLES[nick] - else: # we swapped amnesiac_roles earlier on, get our version back - nick_role = var.AMNESIAC_ROLES[actor] - elif nick_role == "clone": + if nick_role == "clone": if nick in var.CLONED: nick_target = var.CLONED.pop(nick) elif nick_role in ("werecrow", "sorcerer"): @@ -3985,9 +3966,12 @@ def check_exchange(cli, actor, nick): elif nick_role == "turncoat": del var.TURNCOATS[nick] - evt = Event("exchange_roles", {"actor_messages": [], "target_messages": []}) + evt = Event("exchange_roles", {"actor_messages": [], "target_messages": [], "actor_role": actor_role, "target_role": nick_role}) evt.dispatch(var, user, target, actor_role, nick_role) + actor_role = evt.data["actor_role"] + nick_role = evt.data["target_role"] + change_role(user, actor_role, nick_role) change_role(target, nick_role, actor_role) if actor in var.BITTEN_ROLES.keys(): @@ -4300,7 +4284,8 @@ def observe(cli, nick, chan, rest): elif role == "sorcerer": vrole = get_role(victim) if vrole == "amnesiac": - vrole = var.AMNESIAC_ROLES[victim] + from src.roles.amnesiac import ROLES + vrole = ROLES[users._get(victim)] # FIXME if vrole in ("seer", "oracle", "augur", "sorcerer"): an = "n" if vrole.startswith(("a", "e", "i", "o", "u")) else "" pm(cli, nick, (messages["sorcerer_success"]).format(victim, an, vrole)) @@ -4341,12 +4326,13 @@ def pray(cli, nick, chan, rest): # (amnesiacs are special since they're also listed as amnesiac; that way a prophet can see both who the # amnesiacs themselves are as well as what they'll become) pl = list_players() - valid_roles = set(r for r, p in var.ROLES.items() if p) | set(r for p, r in var.AMNESIAC_ROLES.items() if p in pl) + from src.roles.amnesiac import ROLES + valid_roles = {r for r, p in var.ROLES.items() if p} | {r for p, r in ROLES.items() if p.nick in pl} # FIXME if role in valid_roles: # this sees through amnesiac, so the amnesiac's final role counts as their role # also, if we're the only person with that role, say so and don't allow a second vision - people = set(get_roles(role)) | set(p for p, r in var.AMNESIAC_ROLES.items() if p in pl and r == role) # FIXME + people = set(get_roles(role)) | {p.nick for p, r in ROLES.items() if p.nick in pl and r == role} # FIXME if len(people) == 1 and nick in people: pm(cli, nick, messages["vision_only_role_self"].format(role)) var.PRAYED[nick][0] = 2 @@ -4963,42 +4949,6 @@ def transition_night(): t2.daemon = True t2.start() - # convert amnesiac - if var.NIGHT_COUNT == var.AMNESIAC_NIGHTS: - amns = get_roles("amnesiac") # FIXME: not user aware, needs to eventually be a copy of var.ROLES["amnesiac"] - - for amn in amns: - event = Event("amnesiac_turn", {}) - if event.dispatch(var, amn, var.AMNESIAC_ROLES[amn]): - amnrole = var.AMNESIAC_ROLES[amn] - amnuser = users._get(amn) # FIXME - change_role(amnuser, "amnesiac", amnrole) - var.AMNESIACS.add(amn) - # TODO: turn into event when amnesiac is split - from src.roles import succubus - if amnrole == "succubus" and amnuser in succubus.ENTRANCED: - succubus.ENTRANCED.remove(amnuser) - amnuser.send(messages["no_longer_entranced"]) - if var.FIRST_NIGHT: # we don't need to tell them twice if they remember right away - continue - showrole = amnrole - if showrole in var.HIDDEN_VILLAGERS: - showrole = "villager" - elif showrole in var.HIDDEN_ROLES: - showrole = var.DEFAULT_ROLE - n = "" - if showrole.startswith(("a", "e", "i", "o", "u")): - n = "n" - amnuser.send(messages["amnesia_clear"].format(n, showrole)) - if in_wolflist(amn, amn): - if amnrole in var.WOLF_ROLES: - relay_wolfchat_command(amnuser.client, amn, messages["amnesia_wolfchat"].format(amn, showrole), var.WOLF_ROLES, is_wolf_command=True, is_kill_command=True) - else: - relay_wolfchat_command(amnuser.client, amn, messages["amnesia_wolfchat"].format(amn, showrole), var.WOLFCHAT_ROLES) - elif amnrole == "turncoat": - var.TURNCOATS[amn] = ("none", -1) - debuglog("{0} REMEMBER: {1} as {2}".format(amn, amnrole, showrole)) - if var.FIRST_NIGHT and chk_win(end_game=False): # prevent game from ending as soon as it begins (useful for the random game mode) start(channels.Main.client, users.Bot.nick, channels.Main.name, restart=var.CURRENT_GAMEMODE.name) return @@ -5227,7 +5177,7 @@ def start(cli, nick, chan, forced = False, restart = ""): if restart: var.RESTART_TRIES += 1 if var.RESTART_TRIES > 3: - stop_game(abort=True) + stop_game(var, abort=True) return if not restart: @@ -5390,7 +5340,6 @@ def start(cli, nick, chan, forced = False, restart = ""): var.SILENCED = set() var.TOBESILENCED = set() var.JESTERS = set() - var.AMNESIACS = set() var.NIGHT_COUNT = 0 var.DAY_COUNT = 0 var.DISEASED_WOLVES = False @@ -5412,7 +5361,6 @@ def start(cli, nick, chan, forced = False, restart = ""): var.BITE_PREFERENCES = {} var.BITTEN_ROLES = {} var.LYCAN_ROLES = {} - var.AMNESIAC_ROLES = {} var.ACTIVE_PROTECTIONS = defaultdict(list) var.TURNCOATS = {} var.EXCHANGED_ROLES = [] @@ -5566,28 +5514,9 @@ def start(cli, nick, chan, forced = False, restart = ""): for role, players in var.ROLES.items(): var.ORIGINAL_ROLES[role] = players.copy() - # Handle amnesiac; - # matchmaker is blacklisted if AMNESIAC_NIGHTS > 1 due to only being able to act night 1 - # clone and traitor are blacklisted due to assumptions made in default !stats computations. - # If you remove these from the blacklist you will need to modify the default !stats logic - # chains in order to correctly account for these. As a forewarning, such modifications are - # nontrivial and will likely require a great deal of thought (and likely new tracking vars) - amnroles = var.ROLE_GUIDE.keys() - {var.DEFAULT_ROLE, "amnesiac", "clone", "traitor"} - if var.AMNESIAC_NIGHTS > 1 and "matchmaker" in amnroles: - amnroles.remove("matchmaker") - for nope in var.AMNESIAC_BLACKLIST: - amnroles.discard(nope) - for nope in var.TEMPLATE_RESTRICTIONS.keys(): - amnroles.discard(nope) - for amnesiac in var.ROLES["amnesiac"]: - var.AMNESIAC_ROLES[amnesiac.nick] = random.choice(list(amnroles)) # FIXME - # Handle doctor for doctor in var.ROLES["doctor"]: var.DOCTORS[doctor.nick] = math.ceil(var.DOCTOR_IMMUNIZATION_MULTIPLIER * len(pl)) # FIXME - for amn in var.AMNESIAC_ROLES: - if var.AMNESIAC_ROLES[amn] == "doctor": - var.DOCTORS[amn] = math.ceil(var.DOCTOR_IMMUNIZATION_MULTIPLIER * len(pl)) var.DAY_TIMEDELTA = timedelta(0) var.NIGHT_TIMEDELTA = timedelta(0) @@ -5861,7 +5790,7 @@ def reset_game(cli, nick, chan, rest): else: cli.msg(botconfig.CHANNEL, messages["fstop_success"].format(nick)) if var.PHASE != "join": - stop_game(log=False) + stop_game(var, log=False) else: pl = [p for p in list_players() if not is_fake_nick(p)] reset_modes_timers(var) @@ -6599,7 +6528,7 @@ def update(var, wrapper, message): if var.PHASE in var.GAME_PHASES: if var.PHASE == "join" or force: - stop_game(log=False) + stop_game(var, log=False) else: wrapper.pm(messages["stop_bot_ingame_safeguard"].format( what="restart", cmd="update", prefix=botconfig.CMD_CHAR), private=True) @@ -6759,8 +6688,6 @@ def revealroles(var, wrapper, message): special_case.append("targeting {0}".format(var.TARGETED[user.nick])) elif role == "clone" and user.nick in var.CLONED: special_case.append("cloning {0}".format(var.CLONED[user.nick])) - elif role == "amnesiac" and user.nick in var.AMNESIAC_ROLES: - special_case.append("will become {0}".format(var.AMNESIAC_ROLES[user.nick])) # print how many bullets normal gunners have elif (role == "gunner" or role == "sharpshooter") and user in var.GUNNERS: special_case.append("{0} bullet{1}".format(var.GUNNERS[user], "" if var.GUNNERS[user] == 1 else "s")) @@ -6769,7 +6696,7 @@ def revealroles(var, wrapper, message): if var.TURNCOATS[user.nick][0] != "none" else "not currently on any side") evt = Event("revealroles_role", {"special_case": special_case}) - evt.dispatch(var, wrapper, user, role) + evt.dispatch(var, user, role) special_case = evt.data["special_case"] if not evt.prevent_default and user not in var.ORIGINAL_ROLES[role] and role not in var.TEMPLATE_RESTRICTIONS: