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:
parent
42424e49df
commit
745a1dc68a
@ -502,7 +502,6 @@
|
||||
"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.",
|
||||
"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.",
|
||||
"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.",
|
||||
@ -562,6 +561,8 @@
|
||||
"need_one_wolf": "There has to be at least one wolf!",
|
||||
"too_many_wolves": "Too many wolves.",
|
||||
"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.",
|
||||
"command_disabled_admin": "This command has been disabled by an admin.",
|
||||
"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.",
|
||||
"admin_only_force": "Only full admins can force an admin-only command.",
|
||||
"operation_successful": "Operation successful.",
|
||||
"frole_incorrect": "Invalid arguments for {0}frole: {1}",
|
||||
"not_owner": "You are not the owner.",
|
||||
"invalid_permissions": "You do not have permission to use that command.",
|
||||
"player_joined_deadchat": "\u0002{0}\u0002 has joined the deadchat.",
|
||||
|
@ -1,6 +1,8 @@
|
||||
import copy
|
||||
|
||||
from src.users import User
|
||||
|
||||
__all__ = ["UserList", "UserSet", "UserDict"]
|
||||
__all__ = ["UserList", "UserSet", "UserDict", "DefaultUserDict"]
|
||||
|
||||
""" * Important *
|
||||
|
||||
@ -45,12 +47,27 @@ class UserList(list):
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
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):
|
||||
if not isinstance(other, list):
|
||||
return NotImplemented
|
||||
|
||||
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):
|
||||
if not isinstance(value, User):
|
||||
raise TypeError("UserList may only contain User instances")
|
||||
@ -135,6 +152,12 @@ class UserSet(set):
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
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
|
||||
# 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):
|
||||
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):
|
||||
old = self.get(item)
|
||||
super().__setitem__(item, value)
|
||||
@ -288,6 +323,9 @@ class UserDict(dict):
|
||||
if value not in self.values():
|
||||
value.dict_values.remove(self)
|
||||
|
||||
if isinstance(value, (UserSet, UserList, UserDict)):
|
||||
value.clear()
|
||||
|
||||
def clear(self):
|
||||
for key, value in self.items():
|
||||
if isinstance(key, User):
|
||||
@ -339,3 +377,12 @@ class UserDict(dict):
|
||||
iterable = iterable.items()
|
||||
for key, value in iterable:
|
||||
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]
|
||||
|
@ -248,6 +248,10 @@ class command:
|
||||
self.aliases.append(name)
|
||||
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):
|
||||
if isinstance(func, command):
|
||||
func = func.func
|
||||
@ -303,7 +307,7 @@ class command:
|
||||
# Role commands might end the night if it's nighttime
|
||||
if var.PHASE == "night":
|
||||
from src.wolfgame import chk_nightdone
|
||||
chk_nightdone(cli)
|
||||
chk_nightdone()
|
||||
return
|
||||
|
||||
if self.owner_only:
|
||||
@ -461,7 +465,7 @@ class cmd:
|
||||
# Role commands might end the night if it's nighttime
|
||||
if var.PHASE == "night":
|
||||
from src.wolfgame import chk_nightdone
|
||||
chk_nightdone(cli)
|
||||
chk_nightdone()
|
||||
return
|
||||
|
||||
forced_owner_only = False
|
||||
|
@ -6,7 +6,8 @@ from src import users
|
||||
__all__ = [
|
||||
"get_players", "get_all_players", "get_participants",
|
||||
"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):
|
||||
@ -104,5 +105,17 @@ def get_reveal_role(user):
|
||||
else:
|
||||
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:
|
||||
|
@ -12,7 +12,7 @@ from src.utilities import *
|
||||
from src.messages import messages
|
||||
from src.functions import get_players, get_all_players, get_main_role
|
||||
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
|
||||
|
||||
def game_mode(name, minp, maxp, likelihood = 0):
|
||||
@ -172,6 +172,23 @@ class DefaultMode(GameMode):
|
||||
self.ROLE_INDEX = role_index
|
||||
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)
|
||||
class VillagergameMode(GameMode):
|
||||
"""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("transition_day_begin", self.transition_day)
|
||||
events.add_listener("retribution_kill", self.on_retribution_kill, priority=4)
|
||||
events.add_listener("chk_decision", self.chk_decision, priority=20)
|
||||
|
||||
def teardown(self):
|
||||
events.remove_listener("chk_win", self.chk_win)
|
||||
events.remove_listener("chk_nightdone", self.chk_nightdone)
|
||||
events.remove_listener("transition_day_begin", self.transition_day)
|
||||
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):
|
||||
# village can only win via unanimous vote on the bot nick
|
||||
@ -214,15 +233,15 @@ class VillagergameMode(GameMode):
|
||||
|
||||
def chk_nightdone(self, evt, var):
|
||||
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")))
|
||||
rand = random.gauss(5, 1.5)
|
||||
if rand <= 0 and nspecials > 0:
|
||||
transition_day(cli, gameid=gameid)
|
||||
transition_day(gameid=gameid)
|
||||
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()
|
||||
|
||||
def transition_day(self, evt, var):
|
||||
@ -265,6 +284,16 @@ class VillagergameMode(GameMode):
|
||||
evt.data["target"] = None
|
||||
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)
|
||||
class FoolishMode(GameMode):
|
||||
"""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("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,
|
||||
# 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).
|
||||
@ -1323,13 +1352,15 @@ class MudkipMode(GameMode):
|
||||
# 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
|
||||
# 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:
|
||||
evt.data["votelist"] = {force: set()}
|
||||
evt.data["numvotes"] = {force: 0}
|
||||
evt.data["votelist"].clear()
|
||||
evt.data["votelist"][force] = set()
|
||||
evt.data["numvotes"].clear()
|
||||
evt.data["numvotes"][force] = 0
|
||||
else:
|
||||
evt.data["votelist"] = {}
|
||||
evt.data["numvotes"] = {}
|
||||
evt.data["votelist"].clear()
|
||||
evt.data["numvotes"].clear()
|
||||
return
|
||||
|
||||
avail = len(evt.params.voters)
|
||||
@ -1357,12 +1388,12 @@ class MudkipMode(GameMode):
|
||||
for p in tovote:
|
||||
deadlist = tovote[:]
|
||||
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
|
||||
# gameid changes if game stops due to us voting someone
|
||||
if var.GAME_ID == gameid:
|
||||
evt.data["transition_night"](cli)
|
||||
evt.data["transition_night"]()
|
||||
|
||||
# make original chk_decision that called us no-op
|
||||
evt.prevent_default = True
|
||||
|
@ -24,10 +24,12 @@ def on_privmsg(cli, rawnick, chan, msg, *, notice=False, force_role=None):
|
||||
|
||||
user = users._get(rawnick, allow_none=True) # FIXME
|
||||
|
||||
ch = chan.lstrip("".join(hooks.Features["PREFIX"]))
|
||||
|
||||
if users.equals(chan, users.Bot.nick): # PM
|
||||
target = users.Bot
|
||||
else:
|
||||
target = channels.get(chan, allow_none=True)
|
||||
target = channels.get(ch, allow_none=True)
|
||||
|
||||
if user is None or target is None:
|
||||
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
|
||||
for fn in decorators.COMMANDS[""]:
|
||||
fn.caller(cli, rawnick, chan, msg)
|
||||
fn.caller(cli, rawnick, ch, msg)
|
||||
|
||||
parts = msg.split(sep=" ", maxsplit=1)
|
||||
key = parts[0].lower()
|
||||
@ -120,7 +122,7 @@ def on_privmsg(cli, rawnick, chan, msg, *, notice=False, force_role=None):
|
||||
for fn in cmds:
|
||||
if phase == var.PHASE:
|
||||
# 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):
|
||||
for fn in decorators.HOOKS.get(cmd, []):
|
||||
|
@ -9,7 +9,7 @@ search = os.path.join(path, "*.py")
|
||||
for f in glob.iglob(search):
|
||||
f = os.path.basename(f)
|
||||
n, _ = os.path.splitext(f)
|
||||
if f == "__init__.py":
|
||||
if f.startswith("_"):
|
||||
continue
|
||||
importlib.import_module("." + n, package="src.roles")
|
||||
|
||||
|
537
src/roles/_shaman_helper.py
Normal file
537
src/roles/_shaman_helper.py
Normal 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:
|
@ -10,7 +10,7 @@ from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players
|
||||
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.events import Event
|
||||
|
||||
|
@ -5,12 +5,11 @@ import math
|
||||
from collections import defaultdict
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players
|
||||
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.events import Event
|
||||
|
||||
@ -54,7 +53,7 @@ def on_transition_night_end(evt, var):
|
||||
@event_listener("desperation_totem")
|
||||
def on_desperation(evt, var, votee, target, prot):
|
||||
if prot == "blessing":
|
||||
var.ACTIVE_PROTECTIONS[target].remove("blessing")
|
||||
var.ACTIVE_PROTECTIONS[target.nick].remove("blessing")
|
||||
evt.prevent_default = True
|
||||
evt.stop_processing = True
|
||||
|
||||
|
98
src/roles/crazed_shaman.py
Normal file
98
src/roles/crazed_shaman.py
Normal 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:
|
@ -5,11 +5,10 @@ import math
|
||||
from collections import defaultdict
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
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.events import Event
|
||||
|
||||
|
@ -7,7 +7,7 @@ from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players
|
||||
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.events import Event
|
||||
|
||||
|
@ -1,107 +1,84 @@
|
||||
import re
|
||||
import random
|
||||
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players
|
||||
from src.decorators import cmd, event_listener
|
||||
from src.containers import UserList, UserSet, UserDict
|
||||
from src.functions import get_players, get_all_players, get_main_role, get_target, is_known_wolf_ally
|
||||
from src.decorators import command, event_listener
|
||||
from src.containers import UserList, UserSet, UserDict, DefaultUserDict
|
||||
from src.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
SEEN = set()
|
||||
KILLS = {}
|
||||
SICK = {}
|
||||
LYCANS = {}
|
||||
SEEN = UserSet()
|
||||
KILLS = UserDict()
|
||||
SICK = UserDict()
|
||||
LYCANS = UserDict()
|
||||
|
||||
_mappings = ("death", KILLS), ("lycan", LYCANS), ("sick", SICK)
|
||||
|
||||
@cmd("see", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("doomsayer",))
|
||||
def see(cli, nick, chan, rest):
|
||||
@command("see", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("doomsayer",))
|
||||
def see(var, wrapper, message):
|
||||
"""Use your paranormal senses to determine a player's doom."""
|
||||
role = get_role(nick)
|
||||
if nick in SEEN:
|
||||
pm(cli, nick, messages["seer_fail"])
|
||||
if wrapper.source in SEEN:
|
||||
wrapper.send(messages["seer_fail"])
|
||||
return
|
||||
victim = get_victim(cli, nick, re.split(" +",rest)[0], False)
|
||||
if not victim:
|
||||
target = get_target(var, wrapper, re.split(" +", message)[0], not_self_message="no_see_self")
|
||||
if not target:
|
||||
return
|
||||
|
||||
if victim == nick:
|
||||
pm(cli, nick, messages["no_see_self"])
|
||||
if is_known_wolf_ally(wrapper.source, target):
|
||||
wrapper.send(messages["no_see_wolf"])
|
||||
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.dispatch(var, "see", doomsayer, target, frozenset({"detrimental", "immediate"}))
|
||||
evt.dispatch(var, "see", wrapper.source, target, frozenset({"detrimental", "immediate"}))
|
||||
if evt.prevent_default:
|
||||
return
|
||||
victim = evt.data["target"].nick
|
||||
victimrole = get_role(victim)
|
||||
|
||||
target = evt.data["target"]
|
||||
targrole = get_main_role(target)
|
||||
|
||||
mode, mapping = random.choice(_mappings)
|
||||
pm(cli, nick, messages["doomsayer_{0}".format(mode)].format(victim))
|
||||
if mode != "sick" or nick not in var.IMMUNIZED:
|
||||
mapping[nick] = victim
|
||||
wrapper.send(messages["doomsayer_{0}".format(mode)].format(target))
|
||||
if mode != "sick" or wrapper.source.nick not in var.IMMUNIZED:
|
||||
mapping[wrapper.source] = target
|
||||
|
||||
debuglog("{0} ({1}) SEE: {2} ({3}) - {4}".format(nick, role, victim, victimrole, mode.upper()))
|
||||
relay_wolfchat_command(cli, nick, messages["doomsayer_wolfchat"].format(nick, victim), ("doomsayer",), is_wolf_command=True)
|
||||
debuglog("{0} (doomsayer) SEE: {1} ({2}) - {3}".format(wrapper.source, target, targrole, mode.upper()))
|
||||
relay_wolfchat_command(wrapper.client, wrapper.source.nick, messages["doomsayer_wolfchat"].format(wrapper.source, target), ("doomsayer",), is_wolf_command=True)
|
||||
|
||||
SEEN.add(nick)
|
||||
|
||||
@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]
|
||||
SEEN.add(wrapper.source)
|
||||
|
||||
@event_listener("night_acted")
|
||||
def on_acted(evt, var, user, actor):
|
||||
if user.nick in SEEN:
|
||||
if user in SEEN:
|
||||
evt.data["acted"] = True
|
||||
|
||||
@event_listener("exchange_roles")
|
||||
def on_exchange(evt, var, actor, target, actor_role, target_role):
|
||||
if actor_role == "doomsayer" and target_role != "doomsayer":
|
||||
SEEN.discard(actor.nick)
|
||||
SEEN.discard(actor)
|
||||
for name, mapping in _mappings:
|
||||
mapping.pop(actor.nick, None)
|
||||
mapping.pop(actor, None)
|
||||
|
||||
elif target_role == "doomsayer" and actor_role != "doomsayer":
|
||||
SEEN.discard(target.nick)
|
||||
SEEN.discard(target)
|
||||
for name, mapping in _mappings:
|
||||
mapping.pop(target.nick, None)
|
||||
mapping.pop(target, None)
|
||||
|
||||
@event_listener("del_player")
|
||||
def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
|
||||
SEEN.discard(user.nick)
|
||||
SEEN.discard(user)
|
||||
for name, dictvar in _mappings:
|
||||
for k, v in list(dictvar.items()):
|
||||
if user.nick in (k, v):
|
||||
if user in (k, v):
|
||||
del dictvar[k]
|
||||
|
||||
@event_listener("doctor_immunize")
|
||||
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()):
|
||||
if v == target:
|
||||
if v is user:
|
||||
del SICK[n]
|
||||
evt.data["message"] = "not_sick"
|
||||
|
||||
@ -115,15 +92,15 @@ def on_chk_nightdone(evt, var):
|
||||
evt.data["nightroles"].extend(get_all_players(("doomsayer",)))
|
||||
|
||||
@event_listener("abstain")
|
||||
def on_abstain(evt, cli, var, nick):
|
||||
if nick in SICK.values():
|
||||
pm(cli, nick, messages["illness_no_vote"])
|
||||
def on_abstain(evt, var, user):
|
||||
if user in SICK.values():
|
||||
user.send(messages["illness_no_vote"])
|
||||
evt.prevent_default = True
|
||||
|
||||
@event_listener("lynch")
|
||||
def on_lynch(evt, cli, var, nick):
|
||||
if nick in SICK.values():
|
||||
pm(cli, nick, messages["illness_no_vote"])
|
||||
def on_lynch(evt, var, target):
|
||||
if target in SICK.values():
|
||||
target.send(messages["illness_no_vote"])
|
||||
evt.prevent_default = True
|
||||
|
||||
@event_listener("get_voters")
|
||||
@ -132,17 +109,14 @@ def on_get_voters(evt, var):
|
||||
|
||||
@event_listener("transition_day_begin")
|
||||
def on_transition_day_begin(evt, var):
|
||||
for victim in SICK.values():
|
||||
user = users._get(victim)
|
||||
user.queue_message(messages["player_sick"])
|
||||
for target in SICK.values():
|
||||
target.queue_message(messages["player_sick"])
|
||||
if SICK:
|
||||
user.send_messages()
|
||||
target.send_messages()
|
||||
|
||||
@event_listener("transition_day", priority=2)
|
||||
def on_transition_day(evt, var):
|
||||
for k, v in list(KILLS.items()):
|
||||
killer = users._get(k) # FIXME
|
||||
victim = users._get(v) # FIXME
|
||||
for killer, victim in list(KILLS.items()):
|
||||
evt.data["victims"].append(victim)
|
||||
# even though doomsayer is a wolf, remove from onlybywolves since
|
||||
# that particular item indicates that they were the target of a wolf !kill.
|
||||
|
@ -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 import users, channels, debuglog, errlog, plog
|
||||
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.events import Event
|
||||
import botconfig
|
||||
@ -199,9 +199,8 @@ def on_myrole(evt, var, user):
|
||||
evt.data["messages"].append(messages["dullahan_targets_dead"])
|
||||
|
||||
@event_listener("revealroles_role")
|
||||
def on_revealroles_role(evt, var, wrapper, nickname, role):
|
||||
user = users._get(nickname) # FIXME
|
||||
if role == "dullahan" and user in TARGETS: # FIXME
|
||||
def on_revealroles_role(evt, var, wrapper, user, role):
|
||||
if role == "dullahan" and user in TARGETS:
|
||||
targets = set(TARGETS[user])
|
||||
for target in TARGETS[user]:
|
||||
if target.nick in var.DEAD:
|
||||
|
@ -5,11 +5,10 @@ import math
|
||||
from collections import defaultdict
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
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.messages import messages
|
||||
from src.events import Event
|
||||
|
@ -5,12 +5,11 @@ import math
|
||||
from collections import defaultdict
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import channels, users, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target
|
||||
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.events import Event
|
||||
|
||||
|
@ -2,12 +2,11 @@ import re
|
||||
import random
|
||||
from collections import defaultdict
|
||||
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players, get_target, get_main_role
|
||||
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.events import Event
|
||||
|
||||
|
@ -5,12 +5,11 @@ import math
|
||||
from collections import defaultdict
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import channels, users, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target
|
||||
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.events import Event
|
||||
|
||||
|
@ -5,12 +5,11 @@ import math
|
||||
from collections import defaultdict, deque
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import channels, users, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players, get_main_role, get_reveal_role
|
||||
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.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))
|
||||
|
||||
@event_listener("revealroles_role")
|
||||
def on_revealroles(evt, var, wrapper, nickname, role):
|
||||
def on_revealroles(evt, var, wrapper, user, role):
|
||||
if role == "mad scientist":
|
||||
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))
|
||||
|
||||
|
||||
# vim: set sw=4 expandtab:
|
||||
|
@ -5,27 +5,20 @@ import math
|
||||
from collections import defaultdict
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
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.events import Event
|
||||
|
||||
REVEALED_MAYORS = set()
|
||||
|
||||
@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)
|
||||
REVEALED_MAYORS = UserSet()
|
||||
|
||||
@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"]
|
||||
if users._get(votee) in var.ROLES["mayor"] and votee not in REVEALED_MAYORS: # FIXME
|
||||
cli.msg(botconfig.CHANNEL, messages["mayor_reveal"].format(votee))
|
||||
if votee in var.ROLES["mayor"] and votee not in REVEALED_MAYORS:
|
||||
channels.Main.send(messages["mayor_reveal"].format(votee))
|
||||
REVEALED_MAYORS.add(votee)
|
||||
evt.data["votee"] = None
|
||||
evt.prevent_default = True
|
||||
|
@ -1,12 +1,11 @@
|
||||
import re
|
||||
import random
|
||||
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players
|
||||
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.events import Event
|
||||
|
||||
|
@ -5,12 +5,11 @@ import math
|
||||
from collections import defaultdict
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src.functions import get_players, get_all_players, get_target, get_main_role
|
||||
from src import channels, users, debuglog, errlog, plog
|
||||
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.events import Event
|
||||
|
||||
|
@ -5,7 +5,7 @@ import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
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.messages import messages
|
||||
from src.events import Event
|
||||
|
@ -4,637 +4,98 @@ import itertools
|
||||
from collections import defaultdict, deque
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
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
|
||||
from src.decorators import cmd, event_listener
|
||||
from src.containers import UserList, UserSet, UserDict
|
||||
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
|
||||
|
||||
# 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. 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]
|
||||
from src.roles._shaman_helper import setup_variables, get_totem_target, give_totem
|
||||
|
||||
def get_tags(var, totem):
|
||||
tags = set()
|
||||
if role != "crazed shaman" and TOTEMS[nick] in var.BENEFICIAL_TOTEMS:
|
||||
if totem in var.BENEFICIAL_TOTEMS:
|
||||
tags.add("beneficial")
|
||||
return tags
|
||||
|
||||
shaman = users._get(nick) # FIXME
|
||||
target = users._get(victim) # FIXME
|
||||
TOTEMS, LASTGIVEN, SHAMANS = setup_variables("shaman", knows_totem=True, get_tags=get_tags)
|
||||
|
||||
evt = Event("targeted_command", {"target": target, "misdirection": True, "exchange": True},
|
||||
action="give a totem{0} to".format(totem))
|
||||
evt.dispatch(var, "totem", shaman, target, frozenset(tags))
|
||||
if evt.prevent_default:
|
||||
@command("give", "totem", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("shaman",))
|
||||
def shaman_totem(var, wrapper, message):
|
||||
"""Give a totem to a player."""
|
||||
|
||||
target = get_totem_target(var, wrapper, message, LASTGIVEN)
|
||||
if not target:
|
||||
return
|
||||
victim = evt.data["target"].nick
|
||||
victimrole = get_role(victim)
|
||||
|
||||
pm(cli, nick, messages["shaman_success"].format(prefix, totem, original_victim))
|
||||
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]))
|
||||
totem = TOTEMS[wrapper.source]
|
||||
|
||||
@event_listener("rename_player")
|
||||
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
|
||||
SHAMANS[wrapper.source] = give_totem(var, wrapper, target, prefix="You", tags=get_tags(var, totem), role="shaman", msg=" of {0}".format(totem))
|
||||
|
||||
@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(var.TOTEM_ORDER):
|
||||
if shaman.nick not in SHAMANS and shaman.nick not in var.SILENCED:
|
||||
for shaman in get_players(("shaman",)):
|
||||
if shaman not in SHAMANS and shaman.nick not in var.SILENCED:
|
||||
ps = pl[:]
|
||||
if shaman.nick in LASTGIVEN:
|
||||
user = users._get(LASTGIVEN[shaman.nick]) # FIXME
|
||||
if user in ps:
|
||||
ps.remove(user)
|
||||
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)
|
||||
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:
|
||||
LASTGIVEN[shaman.nick] = None
|
||||
LASTGIVEN[shaman] = None
|
||||
elif shaman not in SHAMANS:
|
||||
LASTGIVEN[shaman.nick] = 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))
|
||||
LASTGIVEN[shaman] = None
|
||||
|
||||
@event_listener("transition_night_end", priority=2.01)
|
||||
def on_transition_night_end(evt, var):
|
||||
max_totems = defaultdict(int)
|
||||
max_totems = 0
|
||||
ps = get_players()
|
||||
shamans = list_players(var.TOTEM_ORDER) # FIXME: Need to convert alongside the entire role
|
||||
for ix in range(len(var.TOTEM_ORDER)):
|
||||
for c in var.TOTEM_CHANCES.values():
|
||||
max_totems[var.TOTEM_ORDER[ix]] += c[ix]
|
||||
for s in list(LASTGIVEN.keys()):
|
||||
shamans = get_players(("shaman",))
|
||||
index = var.TOTEM_ORDER.index("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 get_players(var.TOTEM_ORDER):
|
||||
|
||||
for shaman in shamans:
|
||||
pl = ps[:]
|
||||
random.shuffle(pl)
|
||||
if LASTGIVEN.get(shaman.nick):
|
||||
user = users._get(LASTGIVEN[shaman.nick]) # FIXME
|
||||
if user in pl:
|
||||
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)
|
||||
if LASTGIVEN.get(shaman):
|
||||
if LASTGIVEN[shaman] in pl:
|
||||
pl.remove(LASTGIVEN[shaman])
|
||||
|
||||
target = 0
|
||||
rand = random.random() * max_totems[var.TOTEM_ORDER[indx]]
|
||||
rand = random.random() * max_totems
|
||||
for t in var.TOTEM_CHANCES.keys():
|
||||
target += var.TOTEM_CHANCES[t][indx]
|
||||
target += var.TOTEM_CHANCES[t][index]
|
||||
if rand <= target:
|
||||
TOTEMS[shaman.nick] = t # FIXME: Fix once shaman is converted
|
||||
TOTEMS[shaman] = t
|
||||
break
|
||||
if shaman.prefers_simple():
|
||||
if role not in var.WOLFCHAT_ROLES:
|
||||
shaman.send(messages["shaman_simple"].format(role))
|
||||
if role != "crazed shaman":
|
||||
shaman.send(messages["totem_simple"].format(TOTEMS[shaman.nick])) # FIXME
|
||||
shaman.send(messages["shaman_simple"].format("shaman"))
|
||||
shaman.send(messages["totem_simple"].format(TOTEMS[shaman]))
|
||||
else:
|
||||
if role not in var.WOLFCHAT_ROLES:
|
||||
shaman.send(messages["shaman_notify"].format(role, "random " if shaman in var.ROLES["crazed shaman"] else ""))
|
||||
if role != "crazed shaman":
|
||||
totem = TOTEMS[shaman.nick] # FIXME
|
||||
tmsg = messages["shaman_totem"].format(totem)
|
||||
try:
|
||||
tmsg += messages[totem + "_totem"]
|
||||
except KeyError:
|
||||
tmsg += messages["generic_bug_totem"]
|
||||
shaman.send(tmsg)
|
||||
if role not in var.WOLFCHAT_ROLES:
|
||||
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")
|
||||
shaman.send(messages["shaman_notify"].format("shaman", ""))
|
||||
totem = TOTEMS[shaman]
|
||||
tmsg = messages["shaman_totem"].format(totem)
|
||||
tmsg += messages[totem + "_totem"]
|
||||
shaman.send(tmsg)
|
||||
shaman.send("Players: " + ", ".join(p.nick for p in pl))
|
||||
|
||||
# vim: set sw=4 expandtab:
|
||||
|
@ -5,12 +5,11 @@ import math
|
||||
from collections import defaultdict
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import channels, users, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target
|
||||
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.events import Event
|
||||
|
||||
|
@ -5,12 +5,11 @@ import math
|
||||
from collections import defaultdict
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import channels, users, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target
|
||||
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.events import Event
|
||||
|
||||
@ -103,16 +102,17 @@ def on_get_random_totem_targets(evt, var, shaman):
|
||||
evt.data["targets"].remove(succubus)
|
||||
|
||||
@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():
|
||||
if users._get(votee) in get_all_players(("succubus",)): # FIXME
|
||||
if votee in get_all_players(("succubus",)):
|
||||
for vtr in ENTRANCED:
|
||||
if vtr.nick in voters:
|
||||
evt.data["numvotes"][votee] -= evt.data["weights"][votee][vtr.nick]
|
||||
evt.data["weights"][votee][vtr.nick] = 0
|
||||
if vtr in voters:
|
||||
evt.data["numvotes"][votee] -= evt.data["weights"][votee][vtr]
|
||||
evt.data["weights"][votee][vtr] = 0
|
||||
|
||||
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
|
||||
return
|
||||
# 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)
|
||||
|
||||
for other_votee, other_voters in votelist.items():
|
||||
if {p.nick for p in get_all_players(("succubus",))} & set(other_voters): # FIXME
|
||||
if votee == other_votee:
|
||||
if get_all_players(("succubus",)) & set(other_voters):
|
||||
if votee is other_votee:
|
||||
ENTRANCED_DYING.clear()
|
||||
return
|
||||
|
||||
for x in set(ENTRANCED_DYING):
|
||||
if x.nick in other_voters:
|
||||
ENTRANCED_DYING.remove(x)
|
||||
ENTRANCED_DYING.difference_update(other_voters)
|
||||
|
||||
if {p.nick for p in get_all_players(("succubus",))} & not_lynching: # FIXME
|
||||
if get_all_players(("succubus",)) & not_lynching:
|
||||
if votee is None:
|
||||
ENTRANCED_DYING.clear()
|
||||
return
|
||||
|
||||
for x in set(ENTRANCED_DYING):
|
||||
if x.nick in not_lynching:
|
||||
ENTRANCED_DYING.remove(x)
|
||||
ENTRANCED_DYING.difference_update(not_lynching)
|
||||
|
||||
@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
|
||||
# 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)
|
||||
|
||||
@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)
|
||||
|
||||
# entranced logic should run after team wins have already been determined (aka run last)
|
||||
|
@ -5,11 +5,10 @@ import math
|
||||
from collections import defaultdict
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import debuglog, errlog, plog, users, channels
|
||||
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.events import Event
|
||||
|
||||
|
@ -2,12 +2,11 @@ import re
|
||||
import random
|
||||
from collections import defaultdict
|
||||
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import channels, users, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_target, get_main_role
|
||||
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.events import Event
|
||||
|
||||
|
@ -2,12 +2,11 @@ import re
|
||||
import random
|
||||
from collections import defaultdict
|
||||
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players, get_main_role, get_target
|
||||
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.events import Event
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
from src.functions import get_players
|
||||
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.events import Event
|
||||
|
||||
|
@ -6,7 +6,7 @@ from src.utilities import *
|
||||
from src import users, channels, debuglog, errlog, plog
|
||||
from src.functions import get_players, get_all_players, get_main_role
|
||||
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.events import Event
|
||||
|
||||
@ -148,10 +148,10 @@ def on_transition_night_end(evt, var):
|
||||
child.send(messages[to_send])
|
||||
|
||||
@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 nick in IDOLS:
|
||||
evt.data["special_case"].append("picked {0} as idol".format(IDOLS[nick]))
|
||||
if user.nick in IDOLS:
|
||||
evt.data["special_case"].append("picked {0} as idol".format(IDOLS[user.nick]))
|
||||
else:
|
||||
evt.data["special_case"].append("no idol picked yet")
|
||||
|
||||
|
@ -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 import debuglog, errlog, plog, users, channels
|
||||
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.events import Event
|
||||
|
||||
|
102
src/roles/wolf_shaman.py
Normal file
102
src/roles/wolf_shaman.py
Normal 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:
|
@ -2,12 +2,11 @@ import re
|
||||
import random
|
||||
from collections import defaultdict
|
||||
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src.functions import get_players
|
||||
from src import debuglog, errlog, plog, users, channels
|
||||
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.events import Event
|
||||
from src.roles import wolf
|
||||
|
@ -1,7 +1,7 @@
|
||||
import fnmatch
|
||||
import re
|
||||
import threading
|
||||
from collections import defaultdict, OrderedDict
|
||||
from collections import OrderedDict
|
||||
|
||||
LANGUAGE = 'en'
|
||||
|
||||
|
1040
src/wolfgame.py
1040
src/wolfgame.py
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user