Convert chk_decision (#317)

Convert chk_decision, chk_nightdone, transition_day, transition_night, doomsayer, mayor, and convert+split shamans in three files with a shared helper. Fixes and updates for the User containers, and some other tweaks and fixes.
This commit is contained in:
Em Barry 2018-04-23 13:25:38 -04:00 committed by GitHub
parent 42424e49df
commit 745a1dc68a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1522 additions and 1287 deletions

View File

@ -502,7 +502,6 @@
"retribution_totem": "If the player who is given this totem will die tonight, they also kill anyone who killed them.", "retribution_totem": "If the player who is given this totem will die tonight, they also kill anyone who killed them.",
"misdirection_totem": "If the player who is given this totem attempts to use a power the following day or night, they will target a player adjacent to their intended target instead of the player they targeted.", "misdirection_totem": "If the player who is given this totem attempts to use a power the following day or night, they will target a player adjacent to their intended target instead of the player they targeted.",
"deceit_totem": "If the player who is given this totem is a seer or an oracle, or is seen by a seer or an oracle, the vision will be shifted. If the person would be seen as wolf, they are instead seen as a villager. Otherwise, they are seen as a wolf.", "deceit_totem": "If the player who is given this totem is a seer or an oracle, or is seen by a seer or an oracle, the vision will be shifted. If the person would be seen as wolf, they are instead seen as a villager. Otherwise, they are seen as a wolf.",
"generic_bug_totem": "No description for this totem is available. This is a bug, so please report this to the admins.",
"shaman_simple": "You are a \u0002{0}\u0002.", "shaman_simple": "You are a \u0002{0}\u0002.",
"totem_simple": "You have the \u0002{0}\u0002 totem.", "totem_simple": "You have the \u0002{0}\u0002 totem.",
"hunter_notify": "You are a \u0002hunter\u0002. Once per game, you may kill another player with \"kill <nick>\". If you do not wish to kill anyone tonight, use \"pass\" instead.", "hunter_notify": "You are a \u0002hunter\u0002. Once per game, you may kill another player with \"kill <nick>\". If you do not wish to kill anyone tonight, use \"pass\" instead.",
@ -562,6 +561,8 @@
"need_one_wolf": "There has to be at least one wolf!", "need_one_wolf": "There has to be at least one wolf!",
"too_many_wolves": "Too many wolves.", "too_many_wolves": "Too many wolves.",
"error_role_players_count": "Error: Not all roles have defined player counts.", "error_role_players_count": "Error: Not all roles have defined player counts.",
"error_frole_too_many": "There are too many users assigned to role {0}. Please try again.",
"too_many_roles": "There are not enough players for the number of preset roles. Please try again.",
"default_reset": "The default settings have been restored. Please {0}start again.", "default_reset": "The default settings have been restored. Please {0}start again.",
"command_disabled_admin": "This command has been disabled by an admin.", "command_disabled_admin": "This command has been disabled by an admin.",
"not_enough_targets": "Not enough valid targets for the {0} template.", "not_enough_targets": "Not enough valid targets for the {0} template.",
@ -621,6 +622,7 @@
"invalid_target": "This can only be done on players in the channel or fake nicks.", "invalid_target": "This can only be done on players in the channel or fake nicks.",
"admin_only_force": "Only full admins can force an admin-only command.", "admin_only_force": "Only full admins can force an admin-only command.",
"operation_successful": "Operation successful.", "operation_successful": "Operation successful.",
"frole_incorrect": "Invalid arguments for {0}frole: {1}",
"not_owner": "You are not the owner.", "not_owner": "You are not the owner.",
"invalid_permissions": "You do not have permission to use that command.", "invalid_permissions": "You do not have permission to use that command.",
"player_joined_deadchat": "\u0002{0}\u0002 has joined the deadchat.", "player_joined_deadchat": "\u0002{0}\u0002 has joined the deadchat.",

View File

@ -1,6 +1,8 @@
import copy
from src.users import User from src.users import User
__all__ = ["UserList", "UserSet", "UserDict"] __all__ = ["UserList", "UserSet", "UserDict", "DefaultUserDict"]
""" * Important * """ * Important *
@ -45,12 +47,27 @@ class UserList(list):
def __exit__(self, exc_type, exc_value, tb): def __exit__(self, exc_type, exc_value, tb):
self.clear() self.clear()
def __eq__(self, other):
return self is other
def __copy__(self):
return type(self)(self)
def __deepcopy__(self, memo):
return type(self)(copy.deepcopy(x, memo) for x in self)
def __add__(self, other): def __add__(self, other):
if not isinstance(other, list): if not isinstance(other, list):
return NotImplemented return NotImplemented
self.extend(other) self.extend(other)
def __getitem__(self, item):
new = super().__getitem__(item)
if isinstance(item, slice):
new = type(self)(new)
return new
def __setitem__(self, index, value): def __setitem__(self, index, value):
if not isinstance(value, User): if not isinstance(value, User):
raise TypeError("UserList may only contain User instances") raise TypeError("UserList may only contain User instances")
@ -135,6 +152,12 @@ class UserSet(set):
def __exit__(self, exc_type, exc_value, tb): def __exit__(self, exc_type, exc_value, tb):
self.clear() self.clear()
def __copy__(self):
return type(self)(self)
def __deepcopy__(self, memo):
return type(self)(copy.deepcopy(x, memo) for x in self)
# Comparing UserSet instances for equality doesn't make much sense in our context # Comparing UserSet instances for equality doesn't make much sense in our context
# However, if there are identical instances in a list, we only want to remove ourselves # However, if there are identical instances in a list, we only want to remove ourselves
@ -263,6 +286,18 @@ class UserDict(dict):
def __exit__(self, exc_type, exc_value, tb): def __exit__(self, exc_type, exc_value, tb):
self.clear() self.clear()
def __eq__(self, other):
return self is other
def __copy__(self):
return type(self)(self)
def __deepcopy__(self, memo):
new = type(self)()
for key, value in self.items():
new[key] = copy.deepcopy(value, memo)
return new
def __setitem__(self, item, value): def __setitem__(self, item, value):
old = self.get(item) old = self.get(item)
super().__setitem__(item, value) super().__setitem__(item, value)
@ -288,6 +323,9 @@ class UserDict(dict):
if value not in self.values(): if value not in self.values():
value.dict_values.remove(self) value.dict_values.remove(self)
if isinstance(value, (UserSet, UserList, UserDict)):
value.clear()
def clear(self): def clear(self):
for key, value in self.items(): for key, value in self.items():
if isinstance(key, User): if isinstance(key, User):
@ -339,3 +377,12 @@ class UserDict(dict):
iterable = iterable.items() iterable = iterable.items()
for key, value in iterable: for key, value in iterable:
self[key] = value self[key] = value
class DefaultUserDict(UserDict):
def __init__(_self, _factory, _it=(), **kwargs):
_self.factory = _factory
super().__init__(_it, **kwargs)
def __missing__(self, key):
self[key] = self.factory()
return self[key]

View File

@ -248,6 +248,10 @@ class command:
self.aliases.append(name) self.aliases.append(name)
alias = True alias = True
if playing: # Don't restrict to owners or allow in alt channels
self.owner_only = False
self.alt_allowed = False
def __call__(self, func): def __call__(self, func):
if isinstance(func, command): if isinstance(func, command):
func = func.func func = func.func
@ -303,7 +307,7 @@ class command:
# Role commands might end the night if it's nighttime # Role commands might end the night if it's nighttime
if var.PHASE == "night": if var.PHASE == "night":
from src.wolfgame import chk_nightdone from src.wolfgame import chk_nightdone
chk_nightdone(cli) chk_nightdone()
return return
if self.owner_only: if self.owner_only:
@ -461,7 +465,7 @@ class cmd:
# Role commands might end the night if it's nighttime # Role commands might end the night if it's nighttime
if var.PHASE == "night": if var.PHASE == "night":
from src.wolfgame import chk_nightdone from src.wolfgame import chk_nightdone
chk_nightdone(cli) chk_nightdone()
return return
forced_owner_only = False forced_owner_only = False

View File

@ -6,7 +6,8 @@ from src import users
__all__ = [ __all__ = [
"get_players", "get_all_players", "get_participants", "get_players", "get_all_players", "get_participants",
"get_target", "get_target",
"get_main_role", "get_all_roles", "get_reveal_role" "get_main_role", "get_all_roles", "get_reveal_role",
"is_known_wolf_ally",
] ]
def get_players(roles=None, *, mainroles=None): def get_players(roles=None, *, mainroles=None):
@ -104,5 +105,17 @@ def get_reveal_role(user):
else: else:
return "village member" return "village member"
def is_known_wolf_ally(actor, target):
actor_role = get_main_role(actor)
target_role = get_main_role(target)
wolves = var.WOLFCHAT_ROLES
if var.RESTRICT_WOLFCHAT & var.RW_REM_NON_WOLVES:
if var.RESTRICT_WOLFCHAT & var.RW_TRAITOR_NON_WOLF:
wolves = var.WOLF_ROLES
else:
wolves = var.WOLF_ROLES | {"traitor"}
return actor_role in wolves and target_role in wolves
# vim: set sw=4 expandtab: # vim: set sw=4 expandtab:

View File

@ -12,7 +12,7 @@ from src.utilities import *
from src.messages import messages from src.messages import messages
from src.functions import get_players, get_all_players, get_main_role from src.functions import get_players, get_all_players, get_main_role
from src.decorators import handle_error, command from src.decorators import handle_error, command
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src import events, channels, users from src import events, channels, users
def game_mode(name, minp, maxp, likelihood = 0): def game_mode(name, minp, maxp, likelihood = 0):
@ -172,6 +172,23 @@ class DefaultMode(GameMode):
self.ROLE_INDEX = role_index self.ROLE_INDEX = role_index
self.ROLE_GUIDE = role_guide self.ROLE_GUIDE = role_guide
def startup(self):
events.add_listener("chk_decision", self.chk_decision, priority=20)
def teardown(self):
events.remove_listener("chk_decision", self.chk_decision, priority=20)
def chk_decision(self, evt, var, force):
if len(var.ALL_PLAYERS) <= 9 and var.VILLAGERGAME_CHANCE > 0:
if users.Bot in evt.data["votelist"]:
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")
evt.prevent_default = True
else:
del evt.data["votelist"][users.Bot]
@game_mode("villagergame", minp = 4, maxp = 9, likelihood = 0) @game_mode("villagergame", minp = 4, maxp = 9, likelihood = 0)
class VillagergameMode(GameMode): class VillagergameMode(GameMode):
"""This mode definitely does not exist, now please go away.""" """This mode definitely does not exist, now please go away."""
@ -194,12 +211,14 @@ class VillagergameMode(GameMode):
events.add_listener("chk_nightdone", self.chk_nightdone) events.add_listener("chk_nightdone", self.chk_nightdone)
events.add_listener("transition_day_begin", self.transition_day) events.add_listener("transition_day_begin", self.transition_day)
events.add_listener("retribution_kill", self.on_retribution_kill, priority=4) events.add_listener("retribution_kill", self.on_retribution_kill, priority=4)
events.add_listener("chk_decision", self.chk_decision, priority=20)
def teardown(self): def teardown(self):
events.remove_listener("chk_win", self.chk_win) events.remove_listener("chk_win", self.chk_win)
events.remove_listener("chk_nightdone", self.chk_nightdone) events.remove_listener("chk_nightdone", self.chk_nightdone)
events.remove_listener("transition_day_begin", self.transition_day) events.remove_listener("transition_day_begin", self.transition_day)
events.remove_listener("retribution_kill", self.on_retribution_kill, priority=4) events.remove_listener("retribution_kill", self.on_retribution_kill, priority=4)
events.remove_listener("chk_decision", self.chk_decision, priority=20)
def chk_win(self, evt, var, rolemap, mainroles, lpl, lwolves, lrealwolves): def chk_win(self, evt, var, rolemap, mainroles, lpl, lwolves, lrealwolves):
# village can only win via unanimous vote on the bot nick # village can only win via unanimous vote on the bot nick
@ -214,15 +233,15 @@ class VillagergameMode(GameMode):
def chk_nightdone(self, evt, var): def chk_nightdone(self, evt, var):
transition_day = evt.data["transition_day"] transition_day = evt.data["transition_day"]
evt.data["transition_day"] = lambda cli, gameid=0: self.prolong_night(cli, var, gameid, transition_day) evt.data["transition_day"] = lambda gameid=0: self.prolong_night(var, gameid, transition_day)
def prolong_night(self, cli, var, gameid, transition_day): def prolong_night(self, var, gameid, transition_day):
nspecials = len(get_all_players(("seer", "harlot", "shaman", "crazed shaman"))) nspecials = len(get_all_players(("seer", "harlot", "shaman", "crazed shaman")))
rand = random.gauss(5, 1.5) rand = random.gauss(5, 1.5)
if rand <= 0 and nspecials > 0: if rand <= 0 and nspecials > 0:
transition_day(cli, gameid=gameid) transition_day(gameid=gameid)
else: else:
t = threading.Timer(abs(rand), transition_day, args=(cli,), kwargs={"gameid": gameid}) t = threading.Timer(abs(rand), transition_day, kwargs={"gameid": gameid})
t.start() t.start()
def transition_day(self, evt, var): def transition_day(self, evt, var):
@ -265,6 +284,16 @@ class VillagergameMode(GameMode):
evt.data["target"] = None evt.data["target"] = None
evt.stop_processing = True evt.stop_processing = True
def chk_decision(self, evt, var, force):
if users.Bot in evt.data["votelist"]:
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")
evt.prevent_default = True
else:
del evt.data["votelist"][users.Bot]
@game_mode("foolish", minp = 8, maxp = 24, likelihood = 8) @game_mode("foolish", minp = 8, maxp = 24, likelihood = 8)
class FoolishMode(GameMode): class FoolishMode(GameMode):
"""Contains the fool, be careful not to lynch them!""" """Contains the fool, be careful not to lynch them!"""
@ -1315,7 +1344,7 @@ class MudkipMode(GameMode):
events.remove_listener("daylight_warning", self.daylight_warning) events.remove_listener("daylight_warning", self.daylight_warning)
events.remove_listener("transition_night_begin", self.transition_night_begin) events.remove_listener("transition_night_begin", self.transition_night_begin)
def chk_decision(self, evt, cli, var, force): def chk_decision(self, evt, var, force):
# If everyone is voting, end day here with the person with plurality being voted. If there's a tie, # If everyone is voting, end day here with the person with plurality being voted. If there's a tie,
# kill all tied players rather than hanging. The intent of this is to benefit village team in the event # kill all tied players rather than hanging. The intent of this is to benefit village team in the event
# of a stalemate, as they could use the extra help (especially in 5p). # of a stalemate, as they could use the extra help (especially in 5p).
@ -1323,13 +1352,15 @@ class MudkipMode(GameMode):
# in here, this means we're in a child chk_decision event called from this one # in here, this means we're in a child chk_decision event called from this one
# we need to ensure we don't turn into nighttime prematurely or try to vote # we need to ensure we don't turn into nighttime prematurely or try to vote
# anyone other than the person we're forcing the lynch on # anyone other than the person we're forcing the lynch on
evt.data["transition_night"] = lambda cli: None evt.data["transition_night"] = lambda: None
if force: if force:
evt.data["votelist"] = {force: set()} evt.data["votelist"].clear()
evt.data["numvotes"] = {force: 0} evt.data["votelist"][force] = set()
evt.data["numvotes"].clear()
evt.data["numvotes"][force] = 0
else: else:
evt.data["votelist"] = {} evt.data["votelist"].clear()
evt.data["numvotes"] = {} evt.data["numvotes"].clear()
return return
avail = len(evt.params.voters) avail = len(evt.params.voters)
@ -1357,12 +1388,12 @@ class MudkipMode(GameMode):
for p in tovote: for p in tovote:
deadlist = tovote[:] deadlist = tovote[:]
deadlist.remove(p) deadlist.remove(p)
chk_decision(cli, force=p, deadlist=deadlist, end_game=p is last) chk_decision(force=p, deadlist=deadlist, end_game=p is last)
self.recursion_guard = False self.recursion_guard = False
# gameid changes if game stops due to us voting someone # gameid changes if game stops due to us voting someone
if var.GAME_ID == gameid: if var.GAME_ID == gameid:
evt.data["transition_night"](cli) evt.data["transition_night"]()
# make original chk_decision that called us no-op # make original chk_decision that called us no-op
evt.prevent_default = True evt.prevent_default = True

View File

@ -24,10 +24,12 @@ def on_privmsg(cli, rawnick, chan, msg, *, notice=False, force_role=None):
user = users._get(rawnick, allow_none=True) # FIXME user = users._get(rawnick, allow_none=True) # FIXME
ch = chan.lstrip("".join(hooks.Features["PREFIX"]))
if users.equals(chan, users.Bot.nick): # PM if users.equals(chan, users.Bot.nick): # PM
target = users.Bot target = users.Bot
else: else:
target = channels.get(chan, allow_none=True) target = channels.get(ch, allow_none=True)
if user is None or target is None: if user is None or target is None:
return return
@ -43,7 +45,7 @@ def on_privmsg(cli, rawnick, chan, msg, *, notice=False, force_role=None):
if force_role is None: # if force_role isn't None, that indicates recursion; don't fire these off twice if force_role is None: # if force_role isn't None, that indicates recursion; don't fire these off twice
for fn in decorators.COMMANDS[""]: for fn in decorators.COMMANDS[""]:
fn.caller(cli, rawnick, chan, msg) fn.caller(cli, rawnick, ch, msg)
parts = msg.split(sep=" ", maxsplit=1) parts = msg.split(sep=" ", maxsplit=1)
key = parts[0].lower() key = parts[0].lower()
@ -120,7 +122,7 @@ def on_privmsg(cli, rawnick, chan, msg, *, notice=False, force_role=None):
for fn in cmds: for fn in cmds:
if phase == var.PHASE: if phase == var.PHASE:
# FIXME: pass in var, wrapper, message instead of cli, rawnick, chan, message # FIXME: pass in var, wrapper, message instead of cli, rawnick, chan, message
fn.caller(cli, rawnick, chan, message) fn.caller(cli, rawnick, ch, message)
def unhandled(cli, prefix, cmd, *args): def unhandled(cli, prefix, cmd, *args):
for fn in decorators.HOOKS.get(cmd, []): for fn in decorators.HOOKS.get(cmd, []):

View File

@ -9,7 +9,7 @@ search = os.path.join(path, "*.py")
for f in glob.iglob(search): for f in glob.iglob(search):
f = os.path.basename(f) f = os.path.basename(f)
n, _ = os.path.splitext(f) n, _ = os.path.splitext(f)
if f == "__init__.py": if f.startswith("_"):
continue continue
importlib.import_module("." + n, package="src.roles") importlib.import_module("." + n, package="src.roles")

537
src/roles/_shaman_helper.py Normal file
View File

@ -0,0 +1,537 @@
import itertools
import random
import re
from collections import deque
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
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
DEATH = UserDict() # type: Dict[users.User, List[users.User]]
PROTECTION = UserList() # type: List[users.User]
REVEALING = UserSet() # type: Set[users.User]
NARCOLEPSY = UserSet() # type: Set[users.User]
SILENCE = UserSet() # type: Set[users.User]
DESPERATION = UserSet() # type: Set[users.User]
IMPATIENCE = UserList() # type: List[users.User]
PACIFISM = UserList() # type: List[users.User]
INFLUENCE = UserSet() # type: Set[users.User]
EXCHANGE = UserSet() # type: Set[users.User]
LYCANTHROPY = UserSet() # type: Set[users.User]
LUCK = UserSet() # type: Set[users.User]
PESTILENCE = UserSet() # type: Set[users.User]
RETRIBUTION = UserSet() # type: Set[users.User]
MISDIRECTION = UserSet() # type: Set[users.User]
DECEIT = UserSet() # type: Set[users.User]
# holding vars that don't persist long enough to need special attention in
# reset/exchange/nickchange
havetotem = [] # type: List[users.User]
brokentotem = set() # type: Set[users.User]
# To add new totem types in your custom roles/whatever.py file:
# 1. Add a key to var.TOTEM_CHANCES with the totem name
# 2. Add a message totemname_totem to your custom messages.json describing
# the totem (this is displayed at night if !simple is off)
# 3. Add events as necessary to implement the totem's functionality
#
# To add new shaman roles in your custom roles/whatever.py file:
# 1. Expand var.TOTEM_ORDER and upate var.TOTEM_CHANCES to account for the new width
# 2. Add the role to var.ROLE_GUIDE
# 3. Add the role to whatever other holding vars are necessary based on what it does
# 4. Setup initial variables and events with setup_variables(rolename, knows_totem, get_tags)
# knows_totem is a bool and keyword-only. get_tags is a function in the form get_tags(var, totem)
# and should return a set
# 5. Implement custom events if the role does anything else beyond giving totems.
#
# Modifying this file to add new totems or new shaman roles is generally never required
def setup_variables(rolename, *, knows_totem, get_tags):
"""Setup role variables and shared events."""
TOTEMS = UserDict() # type: Dict[users.User, str]
LASTGIVEN = UserDict() # type: Dict[users.User, users.User]
SHAMANS = UserDict() # type: Dict[users.User, List[users.User]]
@event_listener("reset")
def on_reset(evt, var):
TOTEMS.clear()
LASTGIVEN.clear()
SHAMANS.clear()
@event_listener("begin_day")
def on_begin_day(evt, var):
SHAMANS.clear()
@event_listener("revealroles_role")
def on_revealroles(evt, var, wrapper, 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]))
elif var.PHASE == "night":
evt.data["special_case"].append("has {0} totem".format(TOTEMS[user]))
elif user in LASTGIVEN and LASTGIVEN[user]:
evt.data["special_case"].append("gave {0} totem to {1}".format(TOTEMS[user], LASTGIVEN[user]))
@event_listener("transition_day_begin", priority=7)
def on_transition_day_begin2(evt, var):
for shaman, (victim, target) in SHAMANS.items():
totem = TOTEMS[shaman]
if totem == "death": # this totem stacks
if shaman not in DEATH:
DEATH[shaman] = UserList()
DEATH[shaman].append(victim)
elif totem == "protection": # this totem stacks
PROTECTION.append(victim)
elif totem == "revealing":
REVEALING.add(victim)
elif totem == "narcolepsy":
NARCOLEPSY.add(victim)
elif totem == "silence":
SILENCE.add(victim)
elif totem == "desperation":
DESPERATION.add(victim)
elif totem == "impatience": # this totem stacks
IMPATIENCE.append(victim)
elif totem == "pacifism": # this totem stacks
PACIFISM.append(victim)
elif totem == "influence":
INFLUENCE.add(victim)
elif totem == "exchange":
EXCHANGE.add(victim)
elif totem == "lycanthropy":
LYCANTHROPY.add(victim)
elif totem == "luck":
LUCK.add(victim)
elif totem == "pestilence":
PESTILENCE.add(victim)
elif totem == "retribution":
RETRIBUTION.add(victim)
elif totem == "misdirection":
MISDIRECTION.add(victim)
elif totem == "deceit":
DECEIT.add(victim)
# other totem types possibly handled in an earlier event,
# as such there is no else: clause here
if target is not victim:
shaman.send(messages["totem_retarget"].format(victim))
LASTGIVEN[shaman] = victim
havetotem.extend(sorted(filter(None, LASTGIVEN.values())))
@event_listener("del_player")
def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
for a,(b,c) in list(SHAMANS.items()):
if user in (a, b, c):
del SHAMANS[a]
@event_listener("night_acted")
def on_acted(evt, var, user, actor):
if user in SHAMANS:
evt.data["acted"] = True
@event_listener("get_special")
def on_get_special(evt, var):
evt.data["special"].update(get_players((rolename,)))
@event_listener("chk_nightdone")
def on_chk_nightdone(evt, var):
evt.data["actedcount"] += len(SHAMANS)
evt.data["nightroles"].extend(get_players((rolename,)))
@event_listener("get_role_metadata")
def on_get_role_metadata(evt, var, kind):
if kind == "night_kills":
# only add shamans here if they were given a death totem
# even though retribution kills, it is given a special kill message
evt.data[rolename] = list(TOTEMS.values()).count("death")
@event_listener("exchange_roles")
def on_exchange(evt, var, actor, target, actor_role, target_role):
actor_totem = None
target_totem = None
if actor_role == rolename:
actor_totem = TOTEMS.pop(actor)
if actor in SHAMANS:
del SHAMANS[actor]
if actor in LASTGIVEN:
del LASTGIVEN[actor]
if target_role == rolename:
target_totem = TOTEMS.pop(target)
if target in SHAMANS:
del SHAMANS[target]
if target in LASTGIVEN:
del LASTGIVEN[target]
if target_totem:
if knows_totem:
evt.data["actor_messages"].append(messages["shaman_totem"].format(target_totem))
TOTEMS[actor] = target_totem
if actor_totem:
if knows_totem:
evt.data["target_messages"].append(messages["shaman_totem"].format(actor_totem))
TOTEMS[target] = actor_totem
@event_listener("succubus_visit")
def on_succubus_visit(evt, var, succubus, target):
if target in SHAMANS and SHAMANS[target][1] in get_all_players(("succubus",)):
tags = get_tags(var, TOTEMS[target])
if "beneficial" not in tags:
target.send(messages["retract_totem_succubus"].format(SHAMANS[target][1]))
del SHAMANS[target]
if knows_totem:
@event_listener("myrole")
def on_myrole(evt, var, user):
if evt.data["role"] == rolename and var.PHASE == "night" and user not in SHAMANS:
evt.data["messages"].append(messages["totem_simple"].format(TOTEMS[user]))
return (TOTEMS, LASTGIVEN, SHAMANS)
def get_totem_target(var, wrapper, message, lastgiven):
"""Get the totem target."""
target = get_target(var, wrapper, re.split(" +", message)[0], allow_self=True)
if not target:
return
if lastgiven.get(wrapper.source) is target:
wrapper.send(messages["shaman_no_target_twice"].format(target))
return
return target
def give_totem(var, wrapper, target, prefix, tags, role, msg):
"""Give a totem to a player. Return the value of SHAMANS[user]."""
orig_target = target
orig_role = get_main_role(orig_target)
evt = Event("targeted_command", {"target": target, "misdirection": True, "exchange": True},
action="give a totem{0} to".format(msg))
if not evt.dispatch(var, "totem", wrapper.source, target, frozenset(tags)):
return
target = evt.data["target"]
targrole = get_main_role(target)
wrapper.send(messages["shaman_success"].format(prefix, msg, orig_target))
debuglog("{0} ({1}) TOTEM: {2} ({3}) as {4} ({5})".format(wrapper.source, role, target, targrole, orig_target, orig_role))
return UserList((target, orig_target))
@event_listener("see", priority=10)
def on_see(evt, var, nick, victim):
if (users._get(victim) in DECEIT) ^ (users._get(nick) in DECEIT): # FIXME
if evt.data["role"] in var.SEEN_WOLF and evt.data["role"] not in var.SEEN_DEFAULT:
evt.data["role"] = "villager"
else:
evt.data["role"] = "wolf"
@event_listener("get_voters")
def on_get_voters(evt, var):
evt.data["voters"] -= NARCOLEPSY
@event_listener("chk_decision", priority=1)
def on_chk_decision(evt, var, force):
nl = []
for p in PACIFISM:
if p in evt.params.voters:
nl.append(p)
# .remove() will only remove the first instance, which means this plays nicely with pacifism countering this
for p in IMPATIENCE:
if p in nl:
nl.remove(p)
evt.data["not_lynching"].update(nl)
for votee, voters in evt.data["votelist"].items():
numvotes = 0
random.shuffle(IMPATIENCE)
for v in IMPATIENCE:
if v in evt.params.voters and v not in voters and v is not votee:
# don't add them in if they have the same number or more of pacifism totems
# this matters for desperation totem on the votee
imp_count = IMPATIENCE.count(v)
pac_count = PACIFISM.count(v)
if pac_count >= imp_count:
continue
# yes, this means that one of the impatient people will get desperation totem'ed if they didn't
# already !vote earlier. sucks to suck. >:)
voters.append(v)
for v in voters:
weight = 1
imp_count = IMPATIENCE.count(v)
pac_count = PACIFISM.count(v)
if pac_count > imp_count:
weight = 0 # more pacifists than impatience totems
elif imp_count == pac_count and v not in var.VOTES[votee]:
weight = 0 # impatience and pacifist cancel each other out, so don't count impatience
if v in INFLUENCE:
weight *= 2
numvotes += weight
if votee not in evt.data["weights"]:
evt.data["weights"][votee] = {}
evt.data["weights"][votee][v] = weight
evt.data["numvotes"][votee] = numvotes
@event_listener("chk_decision_abstain")
def on_chk_decision_abstain(evt, var, not_lynching):
for p in not_lynching:
if p in PACIFISM and p not in var.NO_LYNCH:
channels.Main.send(messages["player_meek_abstain"].format(p))
@event_listener("chk_decision_lynch", priority=1)
def on_chk_decision_lynch1(evt, var, voters):
votee = evt.data["votee"]
for p in voters:
if p in IMPATIENCE and p not in var.VOTES[votee]:
channels.Main.send(messages["impatient_vote"].format(p, votee))
# mayor is at exactly 3, so we want that to always happen before revealing totem
@event_listener("chk_decision_lynch", priority=3.1)
def on_chk_decision_lynch3(evt, var, voters):
votee = evt.data["votee"]
if votee in REVEALING:
role = get_main_role(votee)
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))
evt.data["votee"] = None
evt.prevent_default = True
evt.stop_processing = True
@event_listener("chk_decision_lynch", priority=5)
def on_chk_decision_lynch5(evt, var, voters):
votee = evt.data["votee"]
if votee in DESPERATION:
# Also kill the very last person to vote them, unless they voted themselves last in which case nobody else dies
target = voters[-1]
if target is not votee:
prots = deque(var.ACTIVE_PROTECTIONS[target])
while len(prots) > 0:
# an event can read the current active protection and cancel the totem
# if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS
# so that it cannot be used again (if the protection is meant to be usable once-only)
desp_evt = Event("desperation_totem", {})
if not desp_evt.dispatch(var, votee, target, prots[0]):
return
prots.popleft()
if var.ROLE_REVEAL in ("on", "team"):
r1 = get_reveal_role(target)
an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else ""
tmsg = messages["totem_desperation"].format(votee, target, an1, r1)
else:
tmsg = messages["totem_desperation_no_reveal"].format(votee, target)
channels.Main.send(tmsg)
# we lie to this function so it doesn't devoice the player yet. instead, we'll let the call further down do it
evt.data["deadlist"].append(target)
evt.params.del_player(target, end_game=False, killer_role="shaman", deadlist=evt.data["deadlist"], ismain=False)
@event_listener("transition_day", priority=2)
def on_transition_day2(evt, var):
for shaman, targets in DEATH.items():
for target in targets:
evt.data["victims"].append(target)
evt.data["onlybywolves"].discard(target)
evt.data["killers"][target].append(shaman)
@event_listener("transition_day", priority=4.1)
def on_transition_day3(evt, var):
# protection totems are applied first in default logic, however
# we set priority=4.1 to allow other modes of protection
# to pre-empt us if desired
pl = get_players()
vs = set(evt.data["victims"])
for v in pl:
numtotems = PROTECTION.count(v)
if v in vs:
if v in var.DYING:
continue
numkills = evt.data["numkills"][v]
for i in range(0, numtotems):
numkills -= 1
if numkills >= 0:
evt.data["killers"][v].pop(0)
if numkills <= 0 and v not in evt.data["protected"]:
evt.data["protected"][v] = "totem"
elif numkills <= 0:
var.ACTIVE_PROTECTIONS[v.nick].append("totem")
evt.data["numkills"][v] = numkills
else:
for i in range(0, numtotems):
var.ACTIVE_PROTECTIONS[v.nick].append("totem")
@event_listener("fallen_angel_guard_break")
def on_fagb(evt, var, victim, killer):
# we'll never end up killing a shaman who gave out protection, but delete the totem since
# story-wise it gets demolished at night by the FA
while victim in havetotem:
havetotem.remove(victim)
brokentotem.add(victim)
@event_listener("transition_day_begin", priority=6)
def on_transition_day_begin(evt, var):
# Reset totem variables
DEATH.clear()
PROTECTION.clear()
REVEALING.clear()
NARCOLEPSY.clear()
SILENCE.clear()
DESPERATION.clear()
IMPATIENCE.clear()
PACIFISM.clear()
INFLUENCE.clear()
EXCHANGE.clear()
LYCANTHROPY.clear()
LUCK.clear()
PESTILENCE.clear()
RETRIBUTION.clear()
MISDIRECTION.clear()
DECEIT.clear()
# In transition_day_end we report who was given totems based on havetotem.
# Fallen angel messes with this list, hence why it is separated from LASTGIVEN
# and calculated here (updated in the separate role files)
brokentotem.clear()
havetotem.clear()
@event_listener("transition_day_resolve", priority=2)
def on_transition_day_resolve2(evt, var, victim):
if evt.data["protected"].get(victim) == "totem":
evt.data["message"].append(messages["totem_protection"].format(victim))
evt.data["novictmsg"] = False
evt.stop_processing = True
evt.prevent_default = True
@event_listener("transition_day_resolve", priority=6)
def on_transition_day_resolve6(evt, var, victim):
# TODO: remove these checks once everything is split
# right now they're needed because otherwise retribution may fire off when the target isn't actually dying
# that will not be an issue once everything is using the event
if evt.data["protected"].get(victim):
return
if victim in var.ROLES["lycan"] and victim in evt.data["onlybywolves"] and victim.nick not in var.IMMUNIZED:
return
# END checks to remove
if victim in RETRIBUTION:
killers = list(evt.data["killers"].get(victim, []))
loser = None
while killers:
loser = random.choice(killers)
if loser in evt.data["dead"] or victim is loser:
killers.remove(loser)
continue
break
if loser in evt.data["dead"] or victim is loser:
loser = None
ret_evt = Event("retribution_kill", {"target": loser, "message": []})
ret_evt.dispatch(var, victim, loser)
loser = ret_evt.data["target"]
evt.data["message"].extend(ret_evt.data["message"])
if loser in evt.data["dead"] or victim is loser:
loser = None
if loser is not None:
prots = deque(var.ACTIVE_PROTECTIONS[loser.nick])
while len(prots) > 0:
# an event can read the current active protection and cancel the totem
# if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS
# so that it cannot be used again (if the protection is meant to be usable once-only)
ret_evt = Event("retribution_totem", {"message": []})
if not ret_evt.dispatch(var, victim, loser, prots[0]):
evt.data["message"].extend(ret_evt.data["message"])
return
prots.popleft()
evt.data["dead"].append(loser)
if var.ROLE_REVEAL in ("on", "team"):
role = get_reveal_role(loser)
an = "n" if role.startswith(("a", "e", "i", "o", "u")) else ""
evt.data["message"].append(messages["totem_death"].format(victim, loser, an, role))
else:
evt.data["message"].append(messages["totem_death_no_reveal"].format(victim, loser))
@event_listener("transition_day_end", priority=1)
def on_transition_day_end(evt, var):
message = []
for player, tlist in itertools.groupby(havetotem):
ntotems = len(list(tlist))
message.append(messages["totem_posession"].format(
player, "ed" if player not in get_players() else "s", "a" if ntotems == 1 else "\u0002{0}\u0002".format(ntotems), "s" if ntotems > 1 else ""))
for player in brokentotem:
message.append(messages["totem_broken"].format(player))
channels.Main.send("\n".join(message))
@event_listener("begin_day")
def on_begin_day(evt, var):
# Apply totem effects that need to begin on day proper
var.EXCHANGED.update(p.nick for p in EXCHANGE)
var.SILENCED.update(p.nick for p in SILENCE)
var.LYCANTHROPES.update(p.nick for p in LYCANTHROPY)
# pestilence doesn't take effect on immunized players
var.DISEASED.update({p.nick for p in PESTILENCE} - var.IMMUNIZED)
var.LUCKY.update(p.nick for p in LUCK)
var.MISDIRECTED.update(p.nick for p in MISDIRECTION)
@event_listener("abstain")
def on_abstain(evt, var, user):
if user in NARCOLEPSY:
user.send(messages["totem_narcolepsy"])
evt.prevent_default = True
@event_listener("lynch")
def on_lynch(evt, var, user):
if user in NARCOLEPSY:
user.send(messages["totem_narcolepsy"])
evt.prevent_default = True
@event_listener("assassinate")
def on_assassinate(evt, var, killer, target, prot):
if prot == "totem":
var.ACTIVE_PROTECTIONS[target.nick].remove("totem")
evt.prevent_default = True
evt.stop_processing = True
channels.Main.send(messages[evt.params.message_prefix + "totem"].format(killer, target))
@event_listener("reset")
def on_reset(evt, var):
DEATH.clear()
PROTECTION.clear()
REVEALING.clear()
NARCOLEPSY.clear()
SILENCE.clear()
DESPERATION.clear()
IMPATIENCE.clear()
PACIFISM.clear()
INFLUENCE.clear()
EXCHANGE.clear()
LYCANTHROPY.clear()
LUCK.clear()
PESTILENCE.clear()
RETRIBUTION.clear()
MISDIRECTION.clear()
DECEIT.clear()
brokentotem.clear()
havetotem.clear()
# vim: set sw=4 expandtab:

View File

@ -10,7 +10,7 @@ from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.functions import get_players, get_all_players from src.functions import get_players, get_all_players
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -5,12 +5,11 @@ import math
from collections import defaultdict from collections import defaultdict
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.functions import get_players, get_all_players from src.functions import get_players, get_all_players
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event
@ -54,7 +53,7 @@ def on_transition_night_end(evt, var):
@event_listener("desperation_totem") @event_listener("desperation_totem")
def on_desperation(evt, var, votee, target, prot): def on_desperation(evt, var, votee, target, prot):
if prot == "blessing": if prot == "blessing":
var.ACTIVE_PROTECTIONS[target].remove("blessing") var.ACTIVE_PROTECTIONS[target.nick].remove("blessing")
evt.prevent_default = True evt.prevent_default = True
evt.stop_processing = True evt.stop_processing = True

View File

@ -0,0 +1,98 @@
import re
import random
import itertools
from collections import defaultdict, deque
import botconfig
from src.utilities import *
from src import debuglog, errlog, plog, users, channels
from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target
from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.dispatcher import MessageDispatcher
from src.messages import messages
from src.events import Event
from src.roles._shaman_helper import setup_variables, get_totem_target, give_totem
def get_tags(var, totem):
return set()
TOTEMS, LASTGIVEN, SHAMANS = setup_variables("crazed shaman", knows_totem=False, get_tags=get_tags)
@command("give", "totem", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("crazed shaman",))
def crazed_shaman_totem(var, wrapper, message):
"""Give a random totem to a player."""
target = get_totem_target(var, wrapper, message, LASTGIVEN)
if not target:
return
totem = TOTEMS[wrapper.source]
SHAMANS[wrapper.source] = give_totem(var, wrapper, target, prefix="You", tags=get_tags(var, totem), role="crazed shaman", msg="")
@event_listener("player_win")
def on_player_win(evt, var, user, role, winner, survived):
if role == "crazed shaman" and survived and not winner.startswith("@") and singular(winner) not in var.WIN_STEALER_ROLES:
evt.data["iwon"] = True
@event_listener("transition_day_begin", priority=4)
def on_transition_day_begin(evt, var):
# Select random totem recipients if shamans didn't act
pl = get_players()
for shaman in get_players(("crazed shaman",)):
if shaman not in SHAMANS and shaman.nick not in var.SILENCED:
ps = pl[:]
if shaman in LASTGIVEN:
if LASTGIVEN[shaman] in ps:
ps.remove(LASTGIVEN[shaman])
levt = Event("get_random_totem_targets", {"targets": ps})
levt.dispatch(var, shaman)
ps = levt.data["targets"]
if ps:
target = random.choice(ps)
dispatcher = MessageDispatcher(shaman, shaman)
tags = get_tags(var, TOTEMS[shaman])
SHAMANS[shaman] = give_totem(var, dispatcher, target, prefix=messages["random_totem_prefix"], tags=tags, role="crazed shaman", msg="")
else:
LASTGIVEN[shaman] = None
elif shaman not in SHAMANS:
LASTGIVEN[shaman] = None
@event_listener("transition_night_end", priority=2.01)
def on_transition_night_end(evt, var):
max_totems = 0
ps = get_players()
shamans = get_players(("crazed shaman",))
index = var.TOTEM_ORDER.index("crazed shaman")
for c in var.TOTEM_CHANCES.values():
max_totems += c[index]
for s in list(LASTGIVEN):
if s not in shamans:
del LASTGIVEN[s]
for shaman in shamans:
pl = ps[:]
random.shuffle(pl)
if LASTGIVEN.get(shaman):
if LASTGIVEN[shaman] in pl:
pl.remove(LASTGIVEN[shaman])
target = 0
rand = random.random() * max_totems
for t in var.TOTEM_CHANCES.keys():
target += var.TOTEM_CHANCES[t][index]
if rand <= target:
TOTEMS[shaman] = t
break
if shaman.prefers_simple():
shaman.send(messages["shaman_simple"].format("crazed shaman"))
else:
shaman.send(messages["shaman_notify"].format("crazed shaman", "random "))
shaman.send("Players: " + ", ".join(p.nick for p in pl))
# vim: set sw=4 expandtab:

View File

@ -5,11 +5,10 @@ import math
from collections import defaultdict from collections import defaultdict
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -7,7 +7,7 @@ from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.functions import get_players, get_all_players from src.functions import get_players, get_all_players
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -1,107 +1,84 @@
import re import re
import random import random
import src.settings as var
from src.utilities import * from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.functions import get_players, get_all_players from src.functions import get_players, get_all_players, get_main_role, get_target, is_known_wolf_ally
from src.decorators import cmd, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event
SEEN = set() SEEN = UserSet()
KILLS = {} KILLS = UserDict()
SICK = {} SICK = UserDict()
LYCANS = {} LYCANS = UserDict()
_mappings = ("death", KILLS), ("lycan", LYCANS), ("sick", SICK) _mappings = ("death", KILLS), ("lycan", LYCANS), ("sick", SICK)
@cmd("see", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("doomsayer",)) @command("see", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("doomsayer",))
def see(cli, nick, chan, rest): def see(var, wrapper, message):
"""Use your paranormal senses to determine a player's doom.""" """Use your paranormal senses to determine a player's doom."""
role = get_role(nick) if wrapper.source in SEEN:
if nick in SEEN: wrapper.send(messages["seer_fail"])
pm(cli, nick, messages["seer_fail"])
return return
victim = get_victim(cli, nick, re.split(" +",rest)[0], False) target = get_target(var, wrapper, re.split(" +", message)[0], not_self_message="no_see_self")
if not victim: if not target:
return return
if victim == nick: if is_known_wolf_ally(wrapper.source, target):
pm(cli, nick, messages["no_see_self"]) wrapper.send(messages["no_see_wolf"])
return return
if in_wolflist(nick, victim):
pm(cli, nick, messages["no_see_wolf"])
return
doomsayer = users._get(nick) # FIXME
target = users._get(victim) # FIXME
evt = Event("targeted_command", {"target": target, "misdirection": True, "exchange": True}) evt = Event("targeted_command", {"target": target, "misdirection": True, "exchange": True})
evt.dispatch(var, "see", doomsayer, target, frozenset({"detrimental", "immediate"})) evt.dispatch(var, "see", wrapper.source, target, frozenset({"detrimental", "immediate"}))
if evt.prevent_default: if evt.prevent_default:
return return
victim = evt.data["target"].nick
victimrole = get_role(victim) target = evt.data["target"]
targrole = get_main_role(target)
mode, mapping = random.choice(_mappings) mode, mapping = random.choice(_mappings)
pm(cli, nick, messages["doomsayer_{0}".format(mode)].format(victim)) wrapper.send(messages["doomsayer_{0}".format(mode)].format(target))
if mode != "sick" or nick not in var.IMMUNIZED: if mode != "sick" or wrapper.source.nick not in var.IMMUNIZED:
mapping[nick] = victim mapping[wrapper.source] = target
debuglog("{0} ({1}) SEE: {2} ({3}) - {4}".format(nick, role, victim, victimrole, mode.upper())) debuglog("{0} (doomsayer) SEE: {1} ({2}) - {3}".format(wrapper.source, target, targrole, mode.upper()))
relay_wolfchat_command(cli, nick, messages["doomsayer_wolfchat"].format(nick, victim), ("doomsayer",), is_wolf_command=True) relay_wolfchat_command(wrapper.client, wrapper.source.nick, messages["doomsayer_wolfchat"].format(wrapper.source, target), ("doomsayer",), is_wolf_command=True)
SEEN.add(nick) SEEN.add(wrapper.source)
@event_listener("rename_player")
def on_rename(evt, var, prefix, nick):
if prefix in SEEN:
SEEN.remove(prefix)
SEEN.add(nick)
for name, dictvar in _mappings:
kvp = []
for a, b in dictvar.items():
if a == prefix:
a = nick
if b == prefix:
b = nick
kvp.append((a, b))
dictvar.update(kvp)
if prefix in dictvar:
del dictvar[prefix]
@event_listener("night_acted") @event_listener("night_acted")
def on_acted(evt, var, user, actor): def on_acted(evt, var, user, actor):
if user.nick in SEEN: if user in SEEN:
evt.data["acted"] = True evt.data["acted"] = True
@event_listener("exchange_roles") @event_listener("exchange_roles")
def on_exchange(evt, var, actor, target, actor_role, target_role): def on_exchange(evt, var, actor, target, actor_role, target_role):
if actor_role == "doomsayer" and target_role != "doomsayer": if actor_role == "doomsayer" and target_role != "doomsayer":
SEEN.discard(actor.nick) SEEN.discard(actor)
for name, mapping in _mappings: for name, mapping in _mappings:
mapping.pop(actor.nick, None) mapping.pop(actor, None)
elif target_role == "doomsayer" and actor_role != "doomsayer": elif target_role == "doomsayer" and actor_role != "doomsayer":
SEEN.discard(target.nick) SEEN.discard(target)
for name, mapping in _mappings: for name, mapping in _mappings:
mapping.pop(target.nick, None) mapping.pop(target, None)
@event_listener("del_player") @event_listener("del_player")
def on_del_player(evt, var, user, mainrole, allroles, death_triggers): def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
SEEN.discard(user.nick) SEEN.discard(user)
for name, dictvar in _mappings: for name, dictvar in _mappings:
for k, v in list(dictvar.items()): for k, v in list(dictvar.items()):
if user.nick in (k, v): if user in (k, v):
del dictvar[k] del dictvar[k]
@event_listener("doctor_immunize") @event_listener("doctor_immunize")
def on_doctor_immunize(evt, var, doctor, target): def on_doctor_immunize(evt, var, doctor, target):
if target in SICK.values(): user = users._get(target) # FIXME
if user in SICK.values():
for n, v in list(SICK.items()): for n, v in list(SICK.items()):
if v == target: if v is user:
del SICK[n] del SICK[n]
evt.data["message"] = "not_sick" evt.data["message"] = "not_sick"
@ -115,15 +92,15 @@ def on_chk_nightdone(evt, var):
evt.data["nightroles"].extend(get_all_players(("doomsayer",))) evt.data["nightroles"].extend(get_all_players(("doomsayer",)))
@event_listener("abstain") @event_listener("abstain")
def on_abstain(evt, cli, var, nick): def on_abstain(evt, var, user):
if nick in SICK.values(): if user in SICK.values():
pm(cli, nick, messages["illness_no_vote"]) user.send(messages["illness_no_vote"])
evt.prevent_default = True evt.prevent_default = True
@event_listener("lynch") @event_listener("lynch")
def on_lynch(evt, cli, var, nick): def on_lynch(evt, var, target):
if nick in SICK.values(): if target in SICK.values():
pm(cli, nick, messages["illness_no_vote"]) target.send(messages["illness_no_vote"])
evt.prevent_default = True evt.prevent_default = True
@event_listener("get_voters") @event_listener("get_voters")
@ -132,17 +109,14 @@ def on_get_voters(evt, var):
@event_listener("transition_day_begin") @event_listener("transition_day_begin")
def on_transition_day_begin(evt, var): def on_transition_day_begin(evt, var):
for victim in SICK.values(): for target in SICK.values():
user = users._get(victim) target.queue_message(messages["player_sick"])
user.queue_message(messages["player_sick"])
if SICK: if SICK:
user.send_messages() target.send_messages()
@event_listener("transition_day", priority=2) @event_listener("transition_day", priority=2)
def on_transition_day(evt, var): def on_transition_day(evt, var):
for k, v in list(KILLS.items()): for killer, victim in list(KILLS.items()):
killer = users._get(k) # FIXME
victim = users._get(v) # FIXME
evt.data["victims"].append(victim) evt.data["victims"].append(victim)
# even though doomsayer is a wolf, remove from onlybywolves since # even though doomsayer is a wolf, remove from onlybywolves since
# that particular item indicates that they were the target of a wolf !kill. # that particular item indicates that they were the target of a wolf !kill.

View File

@ -7,7 +7,7 @@ from src.utilities import *
from src.functions import get_players, get_all_players, get_target, get_main_role, get_reveal_role from src.functions import get_players, get_all_players, get_target, get_main_role, get_reveal_role
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.decorators import command, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event
import botconfig import botconfig
@ -199,9 +199,8 @@ def on_myrole(evt, var, user):
evt.data["messages"].append(messages["dullahan_targets_dead"]) evt.data["messages"].append(messages["dullahan_targets_dead"])
@event_listener("revealroles_role") @event_listener("revealroles_role")
def on_revealroles_role(evt, var, wrapper, nickname, role): def on_revealroles_role(evt, var, wrapper, user, role):
user = users._get(nickname) # FIXME if role == "dullahan" and user in TARGETS:
if role == "dullahan" and user in TARGETS: # FIXME
targets = set(TARGETS[user]) targets = set(TARGETS[user])
for target in TARGETS[user]: for target in TARGETS[user]:
if target.nick in var.DEAD: if target.nick in var.DEAD:

View File

@ -5,11 +5,10 @@ import math
from collections import defaultdict from collections import defaultdict
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.functions import get_players, get_all_players from src.functions import get_players, get_all_players
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -5,12 +5,11 @@ import math
from collections import defaultdict from collections import defaultdict
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src import channels, users, debuglog, errlog, plog 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 from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target
from src.decorators import command, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -2,12 +2,11 @@ import re
import random import random
from collections import defaultdict from collections import defaultdict
import src.settings as var
from src.utilities import * from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.functions import get_players, get_all_players, get_target, get_main_role from src.functions import get_players, get_all_players, get_target, get_main_role
from src.decorators import command, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -5,12 +5,11 @@ import math
from collections import defaultdict from collections import defaultdict
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src import channels, users, debuglog, errlog, plog 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 from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target
from src.decorators import command, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -5,12 +5,11 @@ import math
from collections import defaultdict, deque from collections import defaultdict, deque
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src import channels, users, debuglog, errlog, plog from src import channels, users, debuglog, errlog, plog
from src.functions import get_players, get_all_players, get_main_role, get_reveal_role from src.functions import get_players, get_all_players, get_main_role, get_reveal_role
from src.decorators import command, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event
@ -170,11 +169,10 @@ def on_myrole(evt, var, user):
evt.data["messages"].append(messages["mad_scientist_myrole_targets"].format(target1, target2)) evt.data["messages"].append(messages["mad_scientist_myrole_targets"].format(target1, target2))
@event_listener("revealroles_role") @event_listener("revealroles_role")
def on_revealroles(evt, var, wrapper, nickname, role): def on_revealroles(evt, var, wrapper, user, role):
if role == "mad scientist": if role == "mad scientist":
pl = get_players() pl = get_players()
target1, target2 = _get_targets(var, pl, users._get(nickname)) # FIXME target1, target2 = _get_targets(var, pl, user)
evt.data["special_case"].append(messages["mad_scientist_revealroles_targets"].format(target1, target2)) evt.data["special_case"].append(messages["mad_scientist_revealroles_targets"].format(target1, target2))
# vim: set sw=4 expandtab: # vim: set sw=4 expandtab:

View File

@ -5,27 +5,20 @@ import math
from collections import defaultdict from collections import defaultdict
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event
REVEALED_MAYORS = set() REVEALED_MAYORS = UserSet()
@event_listener("rename_player")
def on_rename_player(evt, var, prefix, nick):
if prefix in REVEALED_MAYORS:
REVEALED_MAYORS.remove(prefix)
REVEALED_MAYORS.add(nick)
@event_listener("chk_decision_lynch", priority=3) @event_listener("chk_decision_lynch", priority=3)
def on_chk_decision_lynch(evt, cli, var, voters): def on_chk_decision_lynch(evt, var, voters):
votee = evt.data["votee"] votee = evt.data["votee"]
if users._get(votee) in var.ROLES["mayor"] and votee not in REVEALED_MAYORS: # FIXME if votee in var.ROLES["mayor"] and votee not in REVEALED_MAYORS:
cli.msg(botconfig.CHANNEL, messages["mayor_reveal"].format(votee)) channels.Main.send(messages["mayor_reveal"].format(votee))
REVEALED_MAYORS.add(votee) REVEALED_MAYORS.add(votee)
evt.data["votee"] = None evt.data["votee"] = None
evt.prevent_default = True evt.prevent_default = True

View File

@ -1,12 +1,11 @@
import re import re
import random import random
import src.settings as var
from src.utilities import * from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.functions import get_players, get_all_players from src.functions import get_players, get_all_players
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -5,12 +5,11 @@ import math
from collections import defaultdict from collections import defaultdict
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src.functions import get_players, get_all_players, get_target, get_main_role from src.functions import get_players, get_all_players, get_target, get_main_role
from src import channels, users, debuglog, errlog, plog from src import channels, users, debuglog, errlog, plog
from src.decorators import command, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -5,7 +5,7 @@ import src.settings as var
from src.utilities import * from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.functions import get_players, get_all_players, get_main_role from src.functions import get_players, get_all_players, get_main_role
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -4,637 +4,98 @@ import itertools
from collections import defaultdict, deque from collections import defaultdict, deque
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src import debuglog, errlog, plog, users, channels from src import debuglog, errlog, plog, users, channels
from src.functions import get_players, get_all_players, get_main_role, get_reveal_role from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target
from src.decorators import cmd, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.dispatcher import MessageDispatcher
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event
# To add new totem types in your custom roles/whatever.py file: from src.roles._shaman_helper import setup_variables, get_totem_target, give_totem
# 1. Add a key to var.TOTEM_CHANCES with the totem name
# 2. Add a message totemname_totem to your custom messages.json describing
# the totem (this is displayed at night if !simple is off)
# 3. Add events as necessary to implement the totem's functionality
#
# To add new shaman roles in your custom roles/whatever.py file:
# 1. Expand var.TOTEM_ORDER and upate var.TOTEM_CHANCES to account for the new width
# 2. Add the role to var.ROLE_GUIDE
# 3. Add the role to whatever other holding vars are necessary based on what it does
# 4. Implement custom events if the role does anything else beyond giving totems.
#
# Modifying this file to add new totems or new shaman roles is generally never required
TOTEMS = {} # type: Dict[str, str]
LASTGIVEN = {} # type: Dict[str, str]
SHAMANS = {} # type: Dict[str, Tuple[str, str]]
DEATH = {} # type: Dict[str, str]
PROTECTION = [] # type: List[str]
REVEALING = set() # type: Set[str]
NARCOLEPSY = set() # type: Set[str]
SILENCE = set() # type: Set[str]
DESPERATION = set() # type: Set[str]
IMPATIENCE = [] # type: List[str]
PACIFISM = [] # type: List[str]
INFLUENCE = set() # type: Set[str]
EXCHANGE = set() # type: Set[str]
LYCANTHROPY = set() # type: Set[str]
LUCK = set() # type: Set[str]
PESTILENCE = set() # type: Set[str]
RETRIBUTION = set() # type: Set[str]
MISDIRECTION = set() # type: Set[str]
DECEIT = set() # type: Set[str]
# holding vars that don't persist long enough to need special attention in
# reset/exchange/nickchange
havetotem = [] # type: List[str]
brokentotem = set() # type: Set[str]
# FIXME: this needs to be split into shaman.py, wolfshaman.py, and crazedshaman.py
@cmd("give", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=var.TOTEM_ORDER)
@cmd("totem", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=var.TOTEM_ORDER)
def totem(cli, nick, chan, rest, prefix="You"): # XXX: The transition_day_begin event needs updating alongside this
"""Give a totem to a player."""
victim = get_victim(cli, nick, re.split(" +",rest)[0], False, True)
if not victim:
return
if LASTGIVEN.get(nick) == victim:
pm(cli, nick, messages["shaman_no_target_twice"].format(victim))
return
original_victim = victim
role = get_role(nick) # FIXME: this is bad, check if user is in var.ROLES[thingy] instead once converted
totem = ""
if role != "crazed shaman":
totem = " of " + TOTEMS[nick]
def get_tags(var, totem):
tags = set() tags = set()
if role != "crazed shaman" and TOTEMS[nick] in var.BENEFICIAL_TOTEMS: if totem in var.BENEFICIAL_TOTEMS:
tags.add("beneficial") tags.add("beneficial")
return tags
shaman = users._get(nick) # FIXME TOTEMS, LASTGIVEN, SHAMANS = setup_variables("shaman", knows_totem=True, get_tags=get_tags)
target = users._get(victim) # FIXME
evt = Event("targeted_command", {"target": target, "misdirection": True, "exchange": True}, @command("give", "totem", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("shaman",))
action="give a totem{0} to".format(totem)) def shaman_totem(var, wrapper, message):
evt.dispatch(var, "totem", shaman, target, frozenset(tags)) """Give a totem to a player."""
if evt.prevent_default:
target = get_totem_target(var, wrapper, message, LASTGIVEN)
if not target:
return return
victim = evt.data["target"].nick
victimrole = get_role(victim)
pm(cli, nick, messages["shaman_success"].format(prefix, totem, original_victim)) totem = TOTEMS[wrapper.source]
if role == "wolf shaman":
relay_wolfchat_command(cli, nick, messages["shaman_wolfchat"].format(nick, original_victim), ("wolf shaman",), is_wolf_command=True)
SHAMANS[nick] = (victim, original_victim)
debuglog("{0} ({1}) TOTEM: {2} ({3})".format(nick, role, victim, TOTEMS[nick]))
@event_listener("rename_player") SHAMANS[wrapper.source] = give_totem(var, wrapper, target, prefix="You", tags=get_tags(var, totem), role="shaman", msg=" of {0}".format(totem))
def on_rename(evt, var, prefix, nick):
if prefix in TOTEMS:
TOTEMS[nick] = TOTEMS.pop(prefix)
for dictvar in (LASTGIVEN, DEATH):
kvp = {}
for a,b in dictvar.items():
s = nick if a == prefix else a
t = nick if b == prefix else b
kvp[s] = t
dictvar.update(kvp)
if prefix in dictvar:
del dictvar[prefix]
kvp = {}
for a,(b,c) in SHAMANS.items():
s = nick if a == prefix else a
t1 = nick if b == prefix else b
t2 = nick if c == prefix else c
kvp[s] = (t1, t2)
SHAMANS.update(kvp)
if prefix in SHAMANS:
del SHAMANS[prefix]
for listvar in (PROTECTION, IMPATIENCE, PACIFISM):
for i,a in enumerate(listvar):
if a == prefix:
listvar[i] = nick
for setvar in (REVEALING, NARCOLEPSY, SILENCE, DESPERATION,
INFLUENCE, EXCHANGE, LYCANTHROPY, LUCK, PESTILENCE,
RETRIBUTION, MISDIRECTION, DECEIT):
for a in list(setvar):
if a == prefix:
setvar.discard(a)
setvar.add(nick)
@event_listener("see", priority=10)
def on_see(evt, var, nick, victim):
if (victim in DECEIT) ^ (nick in DECEIT):
if evt.data["role"] in var.SEEN_WOLF and evt.data["role"] not in var.SEEN_DEFAULT:
evt.data["role"] = "villager"
else:
evt.data["role"] = "wolf"
@event_listener("del_player")
def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
for a,(b,c) in list(SHAMANS.items()):
if user.nick in (a, b, c):
del SHAMANS[a]
@event_listener("night_acted")
def on_acted(evt, var, user, actor):
if user.nick in SHAMANS:
evt.data["acted"] = True
@event_listener("get_special")
def on_get_special(evt, var):
evt.data["special"].update(get_players(("shaman", "crazed shaman", "wolf shaman")))
@event_listener("exchange_roles")
def on_exchange(evt, var, actor, target, actor_role, target_role):
actor_totem = None
target_totem = None
if actor_role in var.TOTEM_ORDER:
actor_totem = TOTEMS.pop(actor.nick)
if actor.nick in SHAMANS:
del SHAMANS[actor.nick]
if actor.nick in LASTGIVEN:
del LASTGIVEN[actor.nick]
if target_role in var.TOTEM_ORDER:
target_totem = TOTEMS.pop(target.nick)
if target.nick in SHAMANS:
del SHAMANS[target.nick]
if target.nick in LASTGIVEN:
del LASTGIVEN[target.nick]
if target_totem:
if target_role != "crazed shaman":
evt.data["actor_messages"].append(messages["shaman_totem"].format(target_totem))
TOTEMS[actor.nick] = target_totem
if actor_totem:
if actor_role != "crazed shaman":
evt.data["target_messages"].append(messages["shaman_totem"].format(actor_totem))
TOTEMS[target.nick] = actor_totem
@event_listener("chk_nightdone")
def on_chk_nightdone(evt, var):
evt.data["actedcount"] += len(SHAMANS)
evt.data["nightroles"].extend(get_players(var.TOTEM_ORDER))
@event_listener("get_voters")
def on_get_voters(evt, var):
evt.data["voters"] -= NARCOLEPSY
@event_listener("chk_decision", priority=1)
def on_chk_decision(evt, cli, var, force):
nl = []
for p in PACIFISM:
if p in evt.params.voters:
nl.append(p)
# .remove() will only remove the first instance, which means this plays nicely with pacifism countering this
for p in IMPATIENCE:
if p in nl:
nl.remove(p)
evt.data["not_lynching"] |= set(nl)
for votee, voters in evt.data["votelist"].items():
numvotes = 0
random.shuffle(IMPATIENCE)
for v in IMPATIENCE:
if v in evt.params.voters and v not in voters and v != votee:
# don't add them in if they have the same number or more of pacifism totems
# this matters for desperation totem on the votee
imp_count = IMPATIENCE.count(v)
pac_count = PACIFISM.count(v)
if pac_count >= imp_count:
continue
# yes, this means that one of the impatient people will get desperation totem'ed if they didn't
# already !vote earlier. sucks to suck. >:)
voters.append(v)
for v in voters:
weight = 1
imp_count = IMPATIENCE.count(v)
pac_count = PACIFISM.count(v)
if pac_count > imp_count:
weight = 0 # more pacifists than impatience totems
elif imp_count == pac_count and v not in var.VOTES[votee]:
weight = 0 # impatience and pacifist cancel each other out, so don't count impatience
if v in INFLUENCE:
weight *= 2
numvotes += weight
if votee not in evt.data["weights"]:
evt.data["weights"][votee] = {}
evt.data["weights"][votee][v] = weight
evt.data["numvotes"][votee] = numvotes
@event_listener("chk_decision_abstain")
def on_chk_decision_abstain(evt, cli, var, nl):
for p in nl:
if p in PACIFISM and p not in var.NO_LYNCH:
cli.msg(botconfig.CHANNEL, messages["player_meek_abstain"].format(p))
@event_listener("chk_decision_lynch", priority=1)
def on_chk_decision_lynch1(evt, cli, var, voters):
votee = evt.data["votee"]
for p in voters:
if p in IMPATIENCE and p not in var.VOTES[votee]:
cli.msg(botconfig.CHANNEL, messages["impatient_vote"].format(p, votee))
# mayor is at exactly 3, so we want that to always happen before revealing totem
@event_listener("chk_decision_lynch", priority=3.1)
def on_chk_decision_lynch3(evt, cli, var, voters):
votee = evt.data["votee"]
if votee in REVEALING:
role = get_role(votee)
rev_evt = Event("revealing_totem", {"role": role})
rev_evt.dispatch(cli, 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]
change_role(users._get(votee), "amnesiac", role) # FIXME
var.AMNESIACS.add(votee)
pm(cli, votee, 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] = ("none", -1)
an = "n" if role.startswith(("a", "e", "i", "o", "u")) else ""
cli.msg(botconfig.CHANNEL, messages["totem_reveal"].format(votee, an, role))
evt.data["votee"] = None
evt.prevent_default = True
evt.stop_processing = True
@event_listener("chk_decision_lynch", priority=5)
def on_chk_decision_lynch5(evt, cli, var, voters):
votee = evt.data["votee"]
if votee in DESPERATION:
# Also kill the very last person to vote them, unless they voted themselves last in which case nobody else dies
target = voters[-1]
if target != votee:
prots = deque(var.ACTIVE_PROTECTIONS[target])
while len(prots) > 0:
# an event can read the current active protection and cancel the totem
# if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS
# so that it cannot be used again (if the protection is meant to be usable once-only)
desp_evt = Event("desperation_totem", {})
if not desp_evt.dispatch(var, votee, target, prots[0]):
return
prots.popleft()
if var.ROLE_REVEAL in ("on", "team"):
r1 = get_reveal_role(users._get(target)) # FIXME
an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else ""
tmsg = messages["totem_desperation"].format(votee, target, an1, r1)
else:
tmsg = messages["totem_desperation_no_reveal"].format(votee, target)
cli.msg(botconfig.CHANNEL, tmsg)
# we lie to this function so it doesn't devoice the player yet. instead, we'll let the call further down do it
evt.data["deadlist"].append(target)
better_deadlist = [users._get(p) for p in evt.data["deadlist"]] # FIXME
target_user = users._get(target) # FIXME
evt.params.del_player(target_user, end_game=False, killer_role="shaman", deadlist=better_deadlist, ismain=False)
@event_listener("player_win")
def on_player_win(evt, var, user, rol, winner, survived):
if rol == "crazed shaman" and survived and not winner.startswith("@") and singular(winner) not in var.WIN_STEALER_ROLES:
evt.data["iwon"] = True
@event_listener("transition_day_begin", priority=4) @event_listener("transition_day_begin", priority=4)
def on_transition_day_begin(evt, var): def on_transition_day_begin(evt, var):
# Select random totem recipients if shamans didn't act # Select random totem recipients if shamans didn't act
pl = get_players() pl = get_players()
for shaman in get_players(var.TOTEM_ORDER): for shaman in get_players(("shaman",)):
if shaman.nick not in SHAMANS and shaman.nick not in var.SILENCED: if shaman not in SHAMANS and shaman.nick not in var.SILENCED:
ps = pl[:] ps = pl[:]
if shaman.nick in LASTGIVEN: if shaman in LASTGIVEN:
user = users._get(LASTGIVEN[shaman.nick]) # FIXME if LASTGIVEN[shaman] in ps:
if user in ps: ps.remove(LASTGIVEN[shaman])
ps.remove(user)
levt = Event("get_random_totem_targets", {"targets": ps}) levt = Event("get_random_totem_targets", {"targets": ps})
levt.dispatch(var, shaman) levt.dispatch(var, shaman)
ps = levt.data["targets"] ps = levt.data["targets"]
if ps: if ps:
target = random.choice(ps) target = random.choice(ps)
totem.func(target.client, shaman.nick, shaman.nick, target.nick, messages["random_totem_prefix"]) # XXX: Old API dispatcher = MessageDispatcher(shaman, shaman)
tags = get_tags(var, TOTEMS[shaman])
SHAMANS[shaman] = give_totem(var, dispatcher, target, prefix=messages["random_totem_prefix"], tags=tags, role="shaman", msg=" of {0}".format(TOTEMS[shaman]))
else: else:
LASTGIVEN[shaman.nick] = None LASTGIVEN[shaman] = None
elif shaman not in SHAMANS: elif shaman not in SHAMANS:
LASTGIVEN[shaman.nick] = None LASTGIVEN[shaman] = None
@event_listener("transition_day_begin", priority=6)
def on_transition_day_begin2(evt, var):
# Reset totem variables
DEATH.clear()
PROTECTION.clear()
REVEALING.clear()
NARCOLEPSY.clear()
SILENCE.clear()
DESPERATION.clear()
IMPATIENCE.clear()
PACIFISM.clear()
INFLUENCE.clear()
EXCHANGE.clear()
LYCANTHROPY.clear()
LUCK.clear()
PESTILENCE.clear()
RETRIBUTION.clear()
MISDIRECTION.clear()
DECEIT.clear()
# Give out totems here
for shaman, (victim, target) in SHAMANS.items():
totemname = TOTEMS[shaman]
if totemname == "death": # this totem stacks
DEATH[shaman] = victim
elif totemname == "protection": # this totem stacks
PROTECTION.append(victim)
elif totemname == "revealing":
REVEALING.add(victim)
elif totemname == "narcolepsy":
NARCOLEPSY.add(victim)
elif totemname == "silence":
SILENCE.add(victim)
elif totemname == "desperation":
DESPERATION.add(victim)
elif totemname == "impatience": # this totem stacks
IMPATIENCE.append(victim)
elif totemname == "pacifism": # this totem stacks
PACIFISM.append(victim)
elif totemname == "influence":
INFLUENCE.add(victim)
elif totemname == "exchange":
EXCHANGE.add(victim)
elif totemname == "lycanthropy":
LYCANTHROPY.add(victim)
elif totemname == "luck":
LUCK.add(victim)
elif totemname == "pestilence":
PESTILENCE.add(victim)
elif totemname == "retribution":
RETRIBUTION.add(victim)
elif totemname == "misdirection":
MISDIRECTION.add(victim)
elif totemname == "deceit":
DECEIT.add(victim)
# other totem types possibly handled in an earlier event,
# as such there is no else: clause here
if target != victim:
user = users._get(shaman) # FIXME
user.send(messages["totem_retarget"].format(victim))
LASTGIVEN[shaman] = victim
# In transition_day_end we report who was given totems based on havetotem.
# Fallen angel messes with this list, hence why it is separated from LASTGIVEN
# and calculated here.
brokentotem.clear()
havetotem.clear()
havetotem.extend(sorted(filter(None, LASTGIVEN.values())))
@event_listener("transition_day", priority=2)
def on_transition_day2(evt, var):
for k, d in DEATH.items():
shaman = users._get(k) # FIXME
target = users._get(d) # FIXME
evt.data["victims"].append(target)
evt.data["onlybywolves"].discard(target)
evt.data["killers"][target].append(shaman)
@event_listener("transition_day", priority=4.1)
def on_transition_day3(evt, var):
# protection totems are applied first in default logic, however
# we set priority=4.1 to allow other modes of protection
# to pre-empt us if desired
pl = get_players()
vs = set(evt.data["victims"])
for v in pl:
numtotems = PROTECTION.count(v.nick)
if v in vs:
if v in var.DYING:
continue
numkills = evt.data["numkills"][v]
for i in range(0, numtotems):
numkills -= 1
if numkills >= 0:
evt.data["killers"][v].pop(0)
if numkills <= 0 and v not in evt.data["protected"]:
evt.data["protected"][v] = "totem"
elif numkills <= 0:
var.ACTIVE_PROTECTIONS[v.nick].append("totem")
evt.data["numkills"][v] = numkills
else:
for i in range(0, numtotems):
var.ACTIVE_PROTECTIONS[v.nick].append("totem")
@event_listener("fallen_angel_guard_break")
def on_fagb(evt, var, victim, killer):
# we'll never end up killing a shaman who gave out protection, but delete the totem since
# story-wise it gets demolished at night by the FA
while victim.nick in havetotem:
havetotem.remove(victim.nick)
brokentotem.add(victim.nick)
@event_listener("transition_day_resolve", priority=2)
def on_transition_day_resolve2(evt, var, victim):
if evt.data["protected"].get(victim) == "totem":
evt.data["message"].append(messages["totem_protection"].format(victim))
evt.data["novictmsg"] = False
evt.stop_processing = True
evt.prevent_default = True
@event_listener("transition_day_resolve", priority=6)
def on_transition_day_resolve6(evt, var, victim):
# TODO: remove these checks once everything is split
# right now they're needed because otherwise retribution may fire off when the target isn't actually dying
# that will not be an issue once everything is using the event
if evt.data["protected"].get(victim):
return
if victim in var.ROLES["lycan"] and victim in evt.data["onlybywolves"] and victim.nick not in var.IMMUNIZED:
return
# END checks to remove
if victim.nick in RETRIBUTION:
killers = list(evt.data["killers"].get(victim, []))
loser = None
while killers:
loser = random.choice(killers)
if loser in evt.data["dead"] or victim is loser:
killers.remove(loser)
continue
break
if loser in evt.data["dead"] or victim is loser:
loser = None
ret_evt = Event("retribution_kill", {"target": loser, "message": []})
ret_evt.dispatch(var, victim, loser)
loser = ret_evt.data["target"]
evt.data["message"].extend(ret_evt.data["message"])
if loser in evt.data["dead"] or victim is loser:
loser = None
if loser is not None:
prots = deque(var.ACTIVE_PROTECTIONS[loser.nick])
while len(prots) > 0:
# an event can read the current active protection and cancel the totem
# if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS
# so that it cannot be used again (if the protection is meant to be usable once-only)
ret_evt = Event("retribution_totem", {"message": []})
if not ret_evt.dispatch(var, victim, loser, prots[0]):
evt.data["message"].extend(ret_evt.data["message"])
return
prots.popleft()
evt.data["dead"].append(loser)
if var.ROLE_REVEAL in ("on", "team"):
role = get_reveal_role(loser)
an = "n" if role.startswith(("a", "e", "i", "o", "u")) else ""
evt.data["message"].append(messages["totem_death"].format(victim, loser, an, role))
else:
evt.data["message"].append(messages["totem_death_no_reveal"].format(victim, loser))
@event_listener("transition_day_end", priority=1)
def on_transition_day_end(evt, var):
message = []
for player, tlist in itertools.groupby(havetotem):
ntotems = len(list(tlist))
message.append(messages["totem_posession"].format(
player, "ed" if player not in list_players() else "s", "a" if ntotems == 1 else "\u0002{0}\u0002".format(ntotems), "s" if ntotems > 1 else ""))
for player in brokentotem:
message.append(messages["totem_broken"].format(player))
channels.Main.send("\n".join(message))
@event_listener("transition_night_end", priority=2.01) @event_listener("transition_night_end", priority=2.01)
def on_transition_night_end(evt, var): def on_transition_night_end(evt, var):
max_totems = defaultdict(int) max_totems = 0
ps = get_players() ps = get_players()
shamans = list_players(var.TOTEM_ORDER) # FIXME: Need to convert alongside the entire role shamans = get_players(("shaman",))
for ix in range(len(var.TOTEM_ORDER)): index = var.TOTEM_ORDER.index("shaman")
for c in var.TOTEM_CHANCES.values(): for c in var.TOTEM_CHANCES.values():
max_totems[var.TOTEM_ORDER[ix]] += c[ix] max_totems += c[index]
for s in list(LASTGIVEN.keys()):
for s in list(LASTGIVEN):
if s not in shamans: if s not in shamans:
del LASTGIVEN[s] del LASTGIVEN[s]
for shaman in get_players(var.TOTEM_ORDER):
for shaman in shamans:
pl = ps[:] pl = ps[:]
random.shuffle(pl) random.shuffle(pl)
if LASTGIVEN.get(shaman.nick): if LASTGIVEN.get(shaman):
user = users._get(LASTGIVEN[shaman.nick]) # FIXME if LASTGIVEN[shaman] in pl:
if user in pl: pl.remove(LASTGIVEN[shaman])
pl.remove(user)
role = get_main_role(shaman) # FIXME: don't use get_main_role here once split into one file per role
indx = var.TOTEM_ORDER.index(role)
target = 0 target = 0
rand = random.random() * max_totems[var.TOTEM_ORDER[indx]] rand = random.random() * max_totems
for t in var.TOTEM_CHANCES.keys(): for t in var.TOTEM_CHANCES.keys():
target += var.TOTEM_CHANCES[t][indx] target += var.TOTEM_CHANCES[t][index]
if rand <= target: if rand <= target:
TOTEMS[shaman.nick] = t # FIXME: Fix once shaman is converted TOTEMS[shaman] = t
break break
if shaman.prefers_simple(): if shaman.prefers_simple():
if role not in var.WOLFCHAT_ROLES: shaman.send(messages["shaman_simple"].format("shaman"))
shaman.send(messages["shaman_simple"].format(role)) shaman.send(messages["totem_simple"].format(TOTEMS[shaman]))
if role != "crazed shaman":
shaman.send(messages["totem_simple"].format(TOTEMS[shaman.nick])) # FIXME
else: else:
if role not in var.WOLFCHAT_ROLES: shaman.send(messages["shaman_notify"].format("shaman", ""))
shaman.send(messages["shaman_notify"].format(role, "random " if shaman in var.ROLES["crazed shaman"] else "")) totem = TOTEMS[shaman]
if role != "crazed shaman":
totem = TOTEMS[shaman.nick] # FIXME
tmsg = messages["shaman_totem"].format(totem) tmsg = messages["shaman_totem"].format(totem)
try:
tmsg += messages[totem + "_totem"] tmsg += messages[totem + "_totem"]
except KeyError:
tmsg += messages["generic_bug_totem"]
shaman.send(tmsg) shaman.send(tmsg)
if role not in var.WOLFCHAT_ROLES:
shaman.send("Players: " + ", ".join(p.nick for p in pl)) shaman.send("Players: " + ", ".join(p.nick for p in pl))
@event_listener("begin_day")
def on_begin_day(evt, var):
# Apply totem effects that need to begin on day proper
var.EXCHANGED.update(EXCHANGE)
var.SILENCED.update(SILENCE)
var.LYCANTHROPES.update(LYCANTHROPY)
# pestilence doesn't take effect on immunized players
var.DISEASED.update(PESTILENCE - var.IMMUNIZED)
var.LUCKY.update(LUCK)
var.MISDIRECTED.update(MISDIRECTION)
SHAMANS.clear()
@event_listener("abstain")
def on_abstain(evt, cli, var, nick):
if nick in NARCOLEPSY:
pm(cli, nick, messages["totem_narcolepsy"])
evt.prevent_default = True
@event_listener("lynch")
def on_lynch(evt, cli, var, nick):
if nick in NARCOLEPSY:
pm(cli, nick, messages["totem_narcolepsy"])
evt.prevent_default = True
@event_listener("assassinate")
def on_assassinate(evt, var, killer, target, prot):
if prot == "totem":
var.ACTIVE_PROTECTIONS[target.nick].remove("totem")
evt.prevent_default = True
evt.stop_processing = True
channels.Main.send(messages[evt.params.message_prefix + "totem"].format(killer, target))
@event_listener("succubus_visit")
def on_succubus_visit(evt, var, succubus, target):
if (users._get(SHAMANS.get(target.nick, (None, None))[1], allow_none=True) in get_all_players(("succubus",)) and # FIXME
(get_main_role(target) == "crazed shaman" or TOTEMS[target.nick] not in var.BENEFICIAL_TOTEMS)):
target.send(messages["retract_totem_succubus"].format(SHAMANS[target.nick][1]))
del SHAMANS[target.nick]
@event_listener("myrole")
def on_myrole(evt, var, user):
role = evt.data["role"]
if role in var.TOTEM_ORDER and role != "crazed shaman" and var.PHASE == "night" and user.nick not in SHAMANS:
evt.data["messages"].append(messages["totem_simple"].format(TOTEMS[user.nick]))
@event_listener("revealroles_role")
def on_revealroles(evt, var, wrapper, nickname, role):
if role in var.TOTEM_ORDER and nickname in TOTEMS:
if nickname in SHAMANS:
evt.data["special_case"].append("giving {0} totem to {1}".format(TOTEMS[nickname], SHAMANS[nickname][0]))
elif var.PHASE == "night":
evt.data["special_case"].append("has {0} totem".format(TOTEMS[nickname]))
elif nickname in LASTGIVEN and LASTGIVEN[nickname]:
evt.data["special_case"].append("gave {0} totem to {1}".format(TOTEMS[nickname], LASTGIVEN[nickname]))
@event_listener("reset")
def on_reset(evt, var):
TOTEMS.clear()
LASTGIVEN.clear()
SHAMANS.clear()
DEATH.clear()
PROTECTION.clear()
REVEALING.clear()
NARCOLEPSY.clear()
SILENCE.clear()
DESPERATION.clear()
IMPATIENCE.clear()
PACIFISM.clear()
INFLUENCE.clear()
EXCHANGE.clear()
LYCANTHROPY.clear()
LUCK.clear()
PESTILENCE.clear()
RETRIBUTION.clear()
MISDIRECTION.clear()
DECEIT.clear()
@event_listener("get_role_metadata")
def on_get_role_metadata(evt, var, kind):
if kind == "night_kills":
# only add shamans here if they were given a death totem
# even though retribution kills, it is given a special kill message
# note that all shaman types (shaman/CS/wolf shaman) are lumped under the "shaman" key (for now),
# this will change so they all get their own key in the future (once this is split into 3 files)
evt.data["shaman"] = list(TOTEMS.values()).count("death")
# vim: set sw=4 expandtab: # vim: set sw=4 expandtab:

View File

@ -5,12 +5,11 @@ import math
from collections import defaultdict from collections import defaultdict
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src import channels, users, debuglog, errlog, plog 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 from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target
from src.decorators import command, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -5,12 +5,11 @@ import math
from collections import defaultdict from collections import defaultdict
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src import channels, users, debuglog, errlog, plog 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 from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target
from src.decorators import command, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event
@ -103,16 +102,17 @@ def on_get_random_totem_targets(evt, var, shaman):
evt.data["targets"].remove(succubus) evt.data["targets"].remove(succubus)
@event_listener("chk_decision") @event_listener("chk_decision")
def on_chk_decision(evt, cli, var, force): def on_chk_decision(evt, var, force):
for votee, voters in evt.data["votelist"].items(): for votee, voters in evt.data["votelist"].items():
if users._get(votee) in get_all_players(("succubus",)): # FIXME if votee in get_all_players(("succubus",)):
for vtr in ENTRANCED: for vtr in ENTRANCED:
if vtr.nick in voters: if vtr in voters:
evt.data["numvotes"][votee] -= evt.data["weights"][votee][vtr.nick] evt.data["numvotes"][votee] -= evt.data["weights"][votee][vtr]
evt.data["weights"][votee][vtr.nick] = 0 evt.data["weights"][votee][vtr] = 0
def _kill_entranced_voters(var, votelist, not_lynching, votee): def _kill_entranced_voters(var, votelist, not_lynching, votee):
if not {p.nick for p in get_all_players(("succubus",))} & (set(itertools.chain(*votelist.values())) | not_lynching): # FIXME voters = set(itertools.chain(*votelist.values()))
if not get_all_players(("succubus",)) & (voters | not_lynching):
# none of the succubi voted (or there aren't any succubi), so short-circuit # none of the succubi voted (or there aren't any succubi), so short-circuit
return return
# kill off everyone entranced that did not follow one of the succubi's votes or abstain # kill off everyone entranced that did not follow one of the succubi's votes or abstain
@ -122,32 +122,28 @@ def _kill_entranced_voters(var, votelist, not_lynching, votee):
ENTRANCED_DYING.add(x) ENTRANCED_DYING.add(x)
for other_votee, other_voters in votelist.items(): for other_votee, other_voters in votelist.items():
if {p.nick for p in get_all_players(("succubus",))} & set(other_voters): # FIXME if get_all_players(("succubus",)) & set(other_voters):
if votee == other_votee: if votee is other_votee:
ENTRANCED_DYING.clear() ENTRANCED_DYING.clear()
return return
for x in set(ENTRANCED_DYING): ENTRANCED_DYING.difference_update(other_voters)
if x.nick in other_voters:
ENTRANCED_DYING.remove(x)
if {p.nick for p in get_all_players(("succubus",))} & not_lynching: # FIXME if get_all_players(("succubus",)) & not_lynching:
if votee is None: if votee is None:
ENTRANCED_DYING.clear() ENTRANCED_DYING.clear()
return return
for x in set(ENTRANCED_DYING): ENTRANCED_DYING.difference_update(not_lynching)
if x.nick in not_lynching:
ENTRANCED_DYING.remove(x)
@event_listener("chk_decision_lynch", priority=5) @event_listener("chk_decision_lynch", priority=5)
def on_chk_decision_lynch(evt, cli, var, voters): def on_chk_decision_lynch(evt, var, voters):
# a different event may override the original votee, but people voting along with succubus # a different event may override the original votee, but people voting along with succubus
# won't necessarily know that, so base whether or not they risk death on the person originally voted # won't necessarily know that, so base whether or not they risk death on the person originally voted
_kill_entranced_voters(var, evt.params.votelist, evt.params.not_lynching, evt.params.original_votee) _kill_entranced_voters(var, evt.params.votelist, evt.params.not_lynching, evt.params.original_votee)
@event_listener("chk_decision_abstain") @event_listener("chk_decision_abstain")
def on_chk_decision_abstain(evt, cli, var, not_lynching): def on_chk_decision_abstain(evt, var, not_lynching):
_kill_entranced_voters(var, evt.params.votelist, not_lynching, None) _kill_entranced_voters(var, evt.params.votelist, not_lynching, None)
# entranced logic should run after team wins have already been determined (aka run last) # entranced logic should run after team wins have already been determined (aka run last)

View File

@ -5,11 +5,10 @@ import math
from collections import defaultdict from collections import defaultdict
import botconfig import botconfig
import src.settings as var
from src.utilities import * from src.utilities import *
from src import debuglog, errlog, plog, users, channels from src import debuglog, errlog, plog, users, channels
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -2,12 +2,11 @@ import re
import random import random
from collections import defaultdict from collections import defaultdict
import src.settings as var
from src.utilities import * from src.utilities import *
from src import channels, users, debuglog, errlog, plog from src import channels, users, debuglog, errlog, plog
from src.functions import get_players, get_target, get_main_role from src.functions import get_players, get_target, get_main_role
from src.decorators import command, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -2,12 +2,11 @@ import re
import random import random
from collections import defaultdict from collections import defaultdict
import src.settings as var
from src.utilities import * from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.functions import get_players, get_all_players, get_main_role, get_target from src.functions import get_players, get_all_players, get_main_role, get_target
from src.decorators import command, event_listener from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -1,9 +1,8 @@
import src.settings as var
from src.utilities import * from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.functions import get_players from src.functions import get_players
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

View File

@ -6,7 +6,7 @@ from src.utilities import *
from src import users, channels, debuglog, errlog, plog from src import users, channels, debuglog, errlog, plog
from src.functions import get_players, get_all_players, get_main_role from src.functions import get_players, get_all_players, get_main_role
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event
@ -148,10 +148,10 @@ def on_transition_night_end(evt, var):
child.send(messages[to_send]) child.send(messages[to_send])
@event_listener("revealroles_role") @event_listener("revealroles_role")
def on_revealroles_role(evt, var, wrapper, nick, role): def on_revealroles_role(evt, var, wrapper, user, role):
if role == "wild child": if role == "wild child":
if nick in IDOLS: if user.nick in IDOLS:
evt.data["special_case"].append("picked {0} as idol".format(IDOLS[nick])) evt.data["special_case"].append("picked {0} as idol".format(IDOLS[user.nick]))
else: else:
evt.data["special_case"].append("no idol picked yet") evt.data["special_case"].append("no idol picked yet")

View File

@ -7,7 +7,7 @@ from src.utilities import *
from src.functions import get_players, get_all_players, get_main_role, get_all_roles from src.functions import get_players, get_all_players, get_main_role, get_all_roles
from src import debuglog, errlog, plog, users, channels from src import debuglog, errlog, plog, users, channels
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event

102
src/roles/wolf_shaman.py Normal file
View File

@ -0,0 +1,102 @@
import re
import random
import itertools
from collections import defaultdict, deque
import botconfig
from src.utilities import *
from src import debuglog, errlog, plog, users, channels
from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target
from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.dispatcher import MessageDispatcher
from src.messages import messages
from src.events import Event
from src.roles._shaman_helper import setup_variables, get_totem_target, give_totem
def get_tags(var, totem):
tags = set()
if totem in var.BENEFICIAL_TOTEMS:
tags.add("beneficial")
return tags
TOTEMS, LASTGIVEN, SHAMANS = setup_variables("wolf shaman", knows_totem=True, get_tags=get_tags)
@command("give", "totem", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("wolf shaman",))
def wolf_shaman_totem(var, wrapper, message):
"""Give a totem to a player."""
target = get_totem_target(var, wrapper, message, LASTGIVEN)
if not target:
return
totem = TOTEMS[wrapper.source]
SHAMANS[wrapper.source] = give_totem(var, wrapper, target, prefix="You", tags=get_tags(var, totem), role="wolf shaman", msg=" of {0}".format(totem))
relay_wolfchat_command(wrapper.client, wrapper.source.nick, messages["shaman_wolfchat"].format(wrapper.source, target), ("wolf shaman",), is_wolf_command=True)
@event_listener("transition_day_begin", priority=4)
def on_transition_day_begin(evt, var):
# Select random totem recipients if shamans didn't act
pl = get_players()
for shaman in get_players(("wolf shaman",)):
if shaman not in SHAMANS and shaman.nick not in var.SILENCED:
ps = pl[:]
if shaman in LASTGIVEN:
if LASTGIVEN[shaman] in ps:
ps.remove(LASTGIVEN[shaman])
levt = Event("get_random_totem_targets", {"targets": ps})
levt.dispatch(var, shaman)
ps = levt.data["targets"]
if ps:
target = random.choice(ps)
dispatcher = MessageDispatcher(shaman, shaman)
tags = get_tags(var, TOTEMS[shaman])
SHAMANS[shaman] = give_totem(var, dispatcher, target, prefix=messages["random_totem_prefix"], tags=tags, role="wolf shaman", msg=" of {0}".format(TOTEMS[shaman]))
relay_wolfchat_command(shaman.client, shaman.nick, messages["shaman_wolfchat"].format(shaman, target), ("wolf shaman",), is_wolf_command=True)
else:
LASTGIVEN[shaman] = None
elif shaman not in SHAMANS:
LASTGIVEN[shaman] = None
@event_listener("transition_night_end", priority=2.01)
def on_transition_night_end(evt, var):
max_totems = 0
ps = get_players()
shamans = get_players(("wolf shaman",))
index = var.TOTEM_ORDER.index("wolf shaman")
for c in var.TOTEM_CHANCES.values():
max_totems += c[index]
for s in list(LASTGIVEN):
if s not in shamans:
del LASTGIVEN[s]
for shaman in shamans:
pl = ps[:]
random.shuffle(pl)
if LASTGIVEN.get(shaman):
if LASTGIVEN[shaman] in pl:
pl.remove(LASTGIVEN[shaman])
target = 0
rand = random.random() * max_totems
for t in var.TOTEM_CHANCES.keys():
target += var.TOTEM_CHANCES[t][index]
if rand <= target:
TOTEMS[shaman] = t
break
if shaman.prefers_simple():
# Message about role was sent with wolfchat
shaman.send(messages["totem_simple"].format(TOTEMS[shaman]))
else:
totem = TOTEMS[shaman]
tmsg = messages["shaman_totem"].format(totem)
tmsg += messages[totem + "_totem"]
shaman.send(tmsg)
# vim: set sw=4 expandtab:

View File

@ -2,12 +2,11 @@ import re
import random import random
from collections import defaultdict from collections import defaultdict
import src.settings as var
from src.utilities import * from src.utilities import *
from src.functions import get_players from src.functions import get_players
from src import debuglog, errlog, plog, users, channels from src import debuglog, errlog, plog, users, channels
from src.decorators import cmd, event_listener from src.decorators import cmd, event_listener
from src.containers import UserList, UserSet, UserDict from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages from src.messages import messages
from src.events import Event from src.events import Event
from src.roles import wolf from src.roles import wolf

View File

@ -1,7 +1,7 @@
import fnmatch import fnmatch
import re import re
import threading import threading
from collections import defaultdict, OrderedDict from collections import OrderedDict
LANGUAGE = 'en' LANGUAGE = 'en'

File diff suppressed because it is too large Load Diff