Add the new User containers (#316)
- Add the new User containers for easier handling of users throughout the codebase - Remove the swap_player event (replaced by User.swap, made possible thanks to the containers) - Remove the cli argument from several events - Remove !frole (a replacement will follow) - Remove the ALLOWED_NORMAL_MODE_COMMANDS config option Plus a couple of small fixes here and there.
This commit is contained in:
parent
c121c0f08f
commit
28f26e181b
@ -41,7 +41,6 @@ OWNERS_ACCOUNTS = ("1owner_acc",)
|
||||
|
||||
#RULES = "https://werewolf.chat/Freenode:Rules"
|
||||
|
||||
ALLOWED_NORMAL_MODE_COMMANDS = [] # Debug mode commands to be allowed in normal mode
|
||||
OWNERS_ONLY_COMMANDS = [] # Commands that should only be allowed for owners, regardless of their original permissions
|
||||
|
||||
DISABLE_DEBUG_MODE_REAPER = True
|
||||
|
@ -621,11 +621,6 @@
|
||||
"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.",
|
||||
"template_default_role": "Added default role ({0}) because only a template was specified for a new player.",
|
||||
"improper_template_mod": "Improper template modification.",
|
||||
"template_mod_syntax": "Please specify \u0002+{0}\u0002 or \u0002-{0}\u0002 to add/remove this template.",
|
||||
"invalid_role": "Not a valid role.",
|
||||
"stats_accurate": "{0}stats type changed to accurate due to use of {0}frole.",
|
||||
"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.",
|
||||
|
341
src/containers.py
Normal file
341
src/containers.py
Normal file
@ -0,0 +1,341 @@
|
||||
from src.users import User
|
||||
|
||||
__all__ = ["UserList", "UserSet", "UserDict"]
|
||||
|
||||
""" * Important *
|
||||
|
||||
The containers present here should always follow these rules:
|
||||
|
||||
- Once a global variable has been set to one of the containers, it *must not* be overwritten;
|
||||
|
||||
- The proper way to empty a container is with the 'container.clear()' method. The 'UserDict.clear' method
|
||||
also takes care of calling the '.clear()' method of nested containers (if any), so you needn't do that;
|
||||
|
||||
- If any local variable points to a container, the 'container.clear()' method
|
||||
*must* be called before the variable goes out of scope;
|
||||
|
||||
- Copying a container for mutation purpose in a local context should make use of context managers,
|
||||
e.g. 'with copy.deepcopy(var.ROLES) as rolelist:' instead of 'rolelist = copy.deepcopy(var.ROLES)',
|
||||
with all operations on 'rolelist' being done inside the block. Once the 'with' block is exited (be it
|
||||
through exceptions or normal execution), the copied contained ('rolelist' in this case) is automatically cleared.
|
||||
|
||||
- If fetching a container from a 'UserDict' with the intent to keep it around separate from the dictionary,
|
||||
a copy is advised, as 'UserDict.clear' is recursive and will clear all nested containers, even if they
|
||||
are being used outside (as the function has no way to know).
|
||||
|
||||
Role files should use User containers as their global variables without ever overwriting them. It is advised to
|
||||
pay close attention to where the variables get touched, to keep the above rules enforced. Refer to existing role
|
||||
files to get an idea of how those containers should be used.
|
||||
|
||||
"""
|
||||
|
||||
class UserList(list):
|
||||
def __init__(self, iterable=()):
|
||||
super().__init__()
|
||||
try:
|
||||
for item in iterable:
|
||||
self.append(item)
|
||||
except:
|
||||
self.clear()
|
||||
raise
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
self.clear()
|
||||
|
||||
def __add__(self, other):
|
||||
if not isinstance(other, list):
|
||||
return NotImplemented
|
||||
|
||||
self.extend(other)
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
if not isinstance(value, User):
|
||||
raise TypeError("UserList may only contain User instances")
|
||||
|
||||
item = self[index]
|
||||
super().__setitem__(index, value)
|
||||
if item not in self:
|
||||
item.lists.remove(self)
|
||||
|
||||
if self not in value.lists:
|
||||
value.lists.append(self)
|
||||
|
||||
def __delitem__(self, index):
|
||||
item = self[index]
|
||||
|
||||
super().__delitem__(index)
|
||||
|
||||
if item not in self: # there may have been multiple instances
|
||||
item.lists.remove(self)
|
||||
|
||||
def append(self, item):
|
||||
if not isinstance(item, User):
|
||||
raise TypeError("UserList may only contain User instances")
|
||||
|
||||
if self not in item.lists:
|
||||
item.lists.append(self)
|
||||
|
||||
super().append(item)
|
||||
|
||||
def clear(self):
|
||||
for item in self:
|
||||
if self in item.lists:
|
||||
item.lists.remove(self)
|
||||
|
||||
super().clear()
|
||||
|
||||
def copy(self):
|
||||
return type(self)(self)
|
||||
|
||||
def extend(self, iterable):
|
||||
for item in iterable:
|
||||
self.append(item)
|
||||
|
||||
def insert(self, index, item):
|
||||
if not isinstance(item, User):
|
||||
raise TypeError("UserList may only contain User instances")
|
||||
|
||||
super().insert(index, item)
|
||||
|
||||
# If it didn't work, we don't get here
|
||||
|
||||
if self not in item.lists:
|
||||
item.lists.append(self)
|
||||
|
||||
def pop(self, index=-1):
|
||||
item = super().pop(index)
|
||||
|
||||
if item not in self:
|
||||
item.lists.remove(self)
|
||||
|
||||
return item
|
||||
|
||||
def remove(self, item):
|
||||
super().remove(item)
|
||||
|
||||
if item not in self:
|
||||
item.lists.remove(self)
|
||||
|
||||
class UserSet(set):
|
||||
def __init__(self, iterable=()):
|
||||
super().__init__()
|
||||
try:
|
||||
for item in iterable:
|
||||
self.add(item)
|
||||
except:
|
||||
self.clear()
|
||||
raise
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
self.clear()
|
||||
|
||||
# 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
|
||||
|
||||
def __eq__(self, other):
|
||||
return self is other
|
||||
|
||||
# Operators are not overloaded - 'user_set & other_set' will return a regular set
|
||||
# This is a deliberate design decision. To get a UserSet out of them, use the named ones
|
||||
|
||||
# Augmented assignment method overrides
|
||||
|
||||
def __iand__(self, other):
|
||||
res = super().__iand__(other)
|
||||
if not isinstance(other, set):
|
||||
return NotImplemented
|
||||
|
||||
self.intersection_update(other)
|
||||
return self
|
||||
|
||||
def __ior__(self, other):
|
||||
if not isinstance(other, set):
|
||||
return NotImplemented
|
||||
|
||||
self.update(other)
|
||||
return self
|
||||
|
||||
def __isub__(self, other):
|
||||
if not isinstance(other, set):
|
||||
return NotImplemented
|
||||
|
||||
self.difference_update(other)
|
||||
return
|
||||
|
||||
def __ixor__(self, other):
|
||||
if not isinstance(other, set):
|
||||
return NotImplemented
|
||||
|
||||
self.symmetric_difference_update(other)
|
||||
return self
|
||||
|
||||
def add(self, item):
|
||||
if item not in self:
|
||||
if not isinstance(item, User):
|
||||
raise TypeError("UserSet may only contain User instances")
|
||||
|
||||
item.sets.append(self)
|
||||
super().add(item)
|
||||
|
||||
def clear(self):
|
||||
for item in self:
|
||||
item.sets.remove(self)
|
||||
|
||||
super().clear()
|
||||
|
||||
def copy(self):
|
||||
return type(self)(self)
|
||||
|
||||
def difference(self, iterable):
|
||||
return type(self)(super().difference(iterable))
|
||||
|
||||
def difference_update(self, iterable):
|
||||
for item in iterable:
|
||||
if item in self:
|
||||
self.remove(item)
|
||||
|
||||
def discard(self, item):
|
||||
if item in self:
|
||||
item.sets.remove(self)
|
||||
|
||||
super().discard(item)
|
||||
|
||||
def intersection(self, iterable):
|
||||
return type(self)(super().intersection(iterable))
|
||||
|
||||
def intersection_update(self, iterable):
|
||||
for item in set(self):
|
||||
if item not in iterable:
|
||||
self.remove(item)
|
||||
|
||||
def pop(self):
|
||||
item = super().pop()
|
||||
item.sets.remove(self)
|
||||
return item
|
||||
|
||||
def remove(self, item):
|
||||
super().remove(item)
|
||||
|
||||
item.sets.remove(self)
|
||||
|
||||
def symmetric_difference(self, iterable):
|
||||
return type(self)(super().symmetric_difference(iterable))
|
||||
|
||||
def symmetric_difference_update(self, iterable):
|
||||
for item in iterable:
|
||||
if item in self:
|
||||
self.remove(item)
|
||||
else:
|
||||
self.add(item)
|
||||
|
||||
def union(self, iterable):
|
||||
return type(self)(super().union(iterable))
|
||||
|
||||
def update(self, iterable):
|
||||
for item in iterable:
|
||||
if item not in self:
|
||||
self.add(item)
|
||||
|
||||
class UserDict(dict):
|
||||
def __init__(_self, _it=(), **kwargs):
|
||||
super().__init__()
|
||||
if hasattr(_it, "items"):
|
||||
_it = _it.items()
|
||||
try:
|
||||
for key, value in _it:
|
||||
self[key] = value
|
||||
for key, value in kwargs.items():
|
||||
self[key] = value
|
||||
except:
|
||||
while self:
|
||||
self.popitem() # don't clear, as it's recursive (we might not want that)
|
||||
raise
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
self.clear()
|
||||
|
||||
def __setitem__(self, item, value):
|
||||
old = self.get(item)
|
||||
super().__setitem__(item, value)
|
||||
if isinstance(old, User):
|
||||
if old not in self.values():
|
||||
old.dict_values.remove(self)
|
||||
|
||||
if isinstance(item, User):
|
||||
if self not in item.dict_keys:
|
||||
item.dict_keys.append(self)
|
||||
|
||||
if isinstance(value, User):
|
||||
if self not in value.dict_values:
|
||||
value.dict_values.append(self)
|
||||
|
||||
def __delitem__(self, item):
|
||||
value = self[item]
|
||||
super().__delitem__(item)
|
||||
if isinstance(item, User):
|
||||
item.dict_keys.remove(self)
|
||||
|
||||
if isinstance(value, User):
|
||||
if value not in self.values():
|
||||
value.dict_values.remove(self)
|
||||
|
||||
def clear(self):
|
||||
for key, value in self.items():
|
||||
if isinstance(key, User):
|
||||
key.dict_keys.remove(self)
|
||||
if isinstance(value, User):
|
||||
if self in value.dict_values:
|
||||
value.dict_values.remove(self)
|
||||
|
||||
if isinstance(key, (UserList, UserSet, UserDict)):
|
||||
key.clear()
|
||||
if isinstance(value, (UserList, UserSet, UserDict)):
|
||||
value.clear()
|
||||
|
||||
super().clear()
|
||||
|
||||
def copy(self):
|
||||
return type(self)(self.items())
|
||||
|
||||
@classmethod
|
||||
def fromkeys(cls, iterable, value=None):
|
||||
return cls(dict.fromkeys(iterable, value))
|
||||
|
||||
def pop(self, key, *default):
|
||||
value = super().pop(key, *default)
|
||||
if isinstance(key, User):
|
||||
if self in key.dict_keys:
|
||||
key.dict_keys.remove(self)
|
||||
if isinstance(value, User):
|
||||
if value not in self.values():
|
||||
value.dict_values.remove(self)
|
||||
return value
|
||||
|
||||
def popitem(self):
|
||||
key, value = super().popitem()
|
||||
if isinstance(key, User):
|
||||
key.dict_keys.remove(self)
|
||||
if isinstance(value, User):
|
||||
if value not in self.values():
|
||||
value.dict_values.remove(self)
|
||||
return key, value
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
if key not in self:
|
||||
self[key] = default
|
||||
return self[key]
|
||||
|
||||
def update(self, iterable):
|
||||
if hasattr(iterable, "items"):
|
||||
iterable = iterable.items()
|
||||
for key, value in iterable:
|
||||
self[key] = value
|
@ -12,6 +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 import events, channels, users
|
||||
|
||||
def game_mode(name, minp, maxp, likelihood = 0):
|
||||
@ -601,7 +602,7 @@ class RandomMode(GameMode):
|
||||
events.remove_listener("role_attribution", self.role_attribution)
|
||||
events.remove_listener("chk_win", self.lovers_chk_win)
|
||||
|
||||
def role_attribution(self, evt, cli, var, chk_win_conditions, villagers):
|
||||
def role_attribution(self, evt, var, chk_win_conditions, villagers):
|
||||
lpl = len(villagers) - 1
|
||||
addroles = evt.data["addroles"]
|
||||
for role in var.ROLE_GUIDE:
|
||||
@ -629,8 +630,8 @@ class RandomMode(GameMode):
|
||||
mainroles[u] = role
|
||||
i += count
|
||||
|
||||
if chk_win_conditions(cli, rolemap, mainroles, end_game=False):
|
||||
return self.role_attribution(evt, cli, var, chk_win_conditions, villagers)
|
||||
if chk_win_conditions(rolemap, mainroles, end_game=False):
|
||||
return self.role_attribution(evt, var, chk_win_conditions, villagers)
|
||||
|
||||
evt.prevent_default = True
|
||||
|
||||
@ -875,20 +876,19 @@ class SleepyMode(GameMode):
|
||||
# disable wolfchat
|
||||
#self.RESTRICT_WOLFCHAT = 0x0f
|
||||
|
||||
self.having_nightmare = None
|
||||
|
||||
def startup(self):
|
||||
events.add_listener("dullahan_targets", self.dullahan_targets)
|
||||
events.add_listener("transition_night_begin", self.setup_nightmares)
|
||||
events.add_listener("chk_nightdone", self.prolong_night)
|
||||
events.add_listener("transition_day_begin", self.nightmare_kill)
|
||||
events.add_listener("del_player", self.happy_fun_times)
|
||||
events.add_listener("rename_player", self.rename_player)
|
||||
self.north_cmd = command("north", "n", chan=False, pm=True, playing=True, phases=("night",))(functools.partial(self.move, "n"))
|
||||
self.east_cmd = command("east", "e", chan=False, pm=True, playing=True, phases=("night",))(functools.partial(self.move, "e"))
|
||||
self.south_cmd = command("south", "s", chan=False, pm=True, playing=True, phases=("night",))(functools.partial(self.move, "s"))
|
||||
self.west_cmd = command("west", "w", chan=False, pm=True, playing=True, phases=("night",))(functools.partial(self.move, "w"))
|
||||
|
||||
self.having_nightmare = UserList()
|
||||
|
||||
def teardown(self):
|
||||
from src import decorators
|
||||
events.remove_listener("dullahan_targets", self.dullahan_targets)
|
||||
@ -896,7 +896,6 @@ class SleepyMode(GameMode):
|
||||
events.remove_listener("chk_nightdone", self.prolong_night)
|
||||
events.remove_listener("transition_day_begin", self.nightmare_kill)
|
||||
events.remove_listener("del_player", self.happy_fun_times)
|
||||
events.remove_listener("rename_player", self.rename_player)
|
||||
def remove_command(name, command):
|
||||
if len(decorators.COMMANDS[name]) > 1:
|
||||
decorators.COMMANDS[name].remove(command)
|
||||
@ -911,23 +910,18 @@ class SleepyMode(GameMode):
|
||||
remove_command("west", self.west_cmd)
|
||||
remove_command("w", self.west_cmd)
|
||||
|
||||
def dullahan_targets(self, evt, cli, var, dullahans, max_targets):
|
||||
self.having_nightmare.clear()
|
||||
|
||||
def dullahan_targets(self, evt, var, dullahans, max_targets):
|
||||
for dull in dullahans:
|
||||
evt.data["targets"][dull] = set(var.ROLES["priest"])
|
||||
evt.data["targets"][dull] = UserSet(var.ROLES["priest"])
|
||||
|
||||
def setup_nightmares(self, evt, cli, var):
|
||||
if random.random() < 1/5:
|
||||
self.having_nightmare = True
|
||||
with var.WARNING_LOCK:
|
||||
t = threading.Timer(60, self.do_nightmare, (var, random.choice(get_players()), var.NIGHT_COUNT))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
else:
|
||||
self.having_nightmare = None
|
||||
|
||||
def rename_player(self, evt, cli, var, prefix, nick):
|
||||
if self.having_nightmare == prefix:
|
||||
self.having_nightmare = nick
|
||||
|
||||
@handle_error
|
||||
def do_nightmare(self, var, target, night):
|
||||
@ -935,7 +929,8 @@ class SleepyMode(GameMode):
|
||||
return
|
||||
if target not in get_players():
|
||||
return
|
||||
self.having_nightmare = target
|
||||
self.having_nightmare.clear()
|
||||
self.having_nightmare.append(target)
|
||||
target.send(messages["sleepy_nightmare_begin"])
|
||||
target.send(messages["sleepy_nightmare_navigate"])
|
||||
self.correct = [None, None, None]
|
||||
@ -976,30 +971,30 @@ class SleepyMode(GameMode):
|
||||
directions = "north, south, and west"
|
||||
|
||||
if self.step == 0:
|
||||
self.having_nightmare.send(messages["sleepy_nightmare_0"].format(directions))
|
||||
self.having_nightmare[0].send(messages["sleepy_nightmare_0"].format(directions))
|
||||
elif self.step == 1:
|
||||
self.having_nightmare.send(messages["sleepy_nightmare_1"].format(directions))
|
||||
self.having_nightmare[0].send(messages["sleepy_nightmare_1"].format(directions))
|
||||
elif self.step == 2:
|
||||
self.having_nightmare.send(messages["sleepy_nightmare_2"].format(directions))
|
||||
self.having_nightmare[0].send(messages["sleepy_nightmare_2"].format(directions))
|
||||
elif self.step == 3:
|
||||
if "correct" in self.on_path:
|
||||
self.having_nightmare.send(messages["sleepy_nightmare_wake"])
|
||||
self.having_nightmare = None
|
||||
self.having_nightmare[0].send(messages["sleepy_nightmare_wake"])
|
||||
del self.having_nightmare[0]
|
||||
elif "fake1" in self.on_path:
|
||||
self.having_nightmare.send(messages["sleepy_nightmare_fake_1"])
|
||||
self.having_nightmare[0].send(messages["sleepy_nightmare_fake_1"])
|
||||
self.step = 0
|
||||
self.on_path = set()
|
||||
self.prev_direction = self.start_direction
|
||||
self.nightmare_step()
|
||||
elif "fake2" in self.on_path:
|
||||
self.having_nightmare.send(messages["sleepy_nightmare_fake_2"])
|
||||
self.having_nightmare[0].send(messages["sleepy_nightmare_fake_2"])
|
||||
self.step = 0
|
||||
self.on_path = set()
|
||||
self.prev_direction = self.start_direction
|
||||
self.nightmare_step()
|
||||
|
||||
def move(self, direction, var, wrapper, message):
|
||||
if self.having_nightmare is not wrapper.source:
|
||||
if self.having_nightmare[0] is not wrapper.source:
|
||||
return
|
||||
opposite = {"n": "s", "e": "w", "s": "n", "w": "e"}
|
||||
if self.prev_direction == opposite[direction]:
|
||||
@ -1032,14 +1027,14 @@ class SleepyMode(GameMode):
|
||||
self.nightmare_step()
|
||||
|
||||
def prolong_night(self, evt, var):
|
||||
if self.having_nightmare is not None:
|
||||
if self.having_nightmare:
|
||||
evt.data["actedcount"] = -1
|
||||
|
||||
def nightmare_kill(self, evt, var):
|
||||
# if True, it means night ended before 1 minute
|
||||
if self.having_nightmare is not None and self.having_nightmare in get_players():
|
||||
var.DYING.add(self.having_nightmare)
|
||||
self.having_nightmare.send(messages["sleepy_nightmare_death"])
|
||||
if self.having_nightmare and self.having_nightmare[0] in get_players():
|
||||
var.DYING.add(self.having_nightmare[0])
|
||||
self.having_nightmare[0].send(messages["sleepy_nightmare_death"])
|
||||
del self.having_nightmare[0]
|
||||
|
||||
def happy_fun_times(self, evt, var, user, mainrole, allroles, death_triggers):
|
||||
if death_triggers:
|
||||
@ -1125,13 +1120,12 @@ class MaelstromMode(GameMode):
|
||||
def _on_join(self, var, wrapper):
|
||||
from src import hooks, channels
|
||||
role = random.choice(self.roles)
|
||||
rolemap = copy.deepcopy(var.ROLES)
|
||||
rolemap[role].add(wrapper.source)
|
||||
mainroles = copy.deepcopy(var.MAIN_ROLES)
|
||||
mainroles[wrapper.source] = role
|
||||
with copy.deepcopy(var.ROLES) as rolemap, copy.deepcopy(var.MAIN_ROLES) as mainroles:
|
||||
rolemap[role].add(wrapper.source)
|
||||
mainroles[wrapper.source] = role
|
||||
|
||||
if self.chk_win_conditions(wrapper.client, rolemap, mainroles, end_game=False):
|
||||
return self._on_join(var, wrapper)
|
||||
if self.chk_win_conditions(rolemap, mainroles, end_game=False):
|
||||
return self._on_join(var, wrapper)
|
||||
|
||||
if not wrapper.source.is_fake or not botconfig.DEBUG_MODE:
|
||||
cmodes = [("+v", wrapper.source)]
|
||||
@ -1173,22 +1167,23 @@ class MaelstromMode(GameMode):
|
||||
pl[i] = player.nick + " (cursed)"
|
||||
wrapper.pm("Players: " + ", ".join(pl))
|
||||
|
||||
def role_attribution(self, evt, cli, var, chk_win_conditions, villagers):
|
||||
def role_attribution(self, evt, var, chk_win_conditions, villagers):
|
||||
self.chk_win_conditions = chk_win_conditions
|
||||
evt.data["addroles"] = self._role_attribution(cli, var, villagers, True)
|
||||
evt.data["addroles"] = self._role_attribution(var, villagers, True)
|
||||
|
||||
def transition_night_begin(self, evt, cli, var):
|
||||
def transition_night_begin(self, evt, var):
|
||||
# don't do this n1
|
||||
if var.FIRST_NIGHT:
|
||||
return
|
||||
villagers = get_players()
|
||||
lpl = len(villagers)
|
||||
addroles = self._role_attribution(cli, var, villagers, False)
|
||||
addroles = self._role_attribution(var, villagers, False)
|
||||
|
||||
# shameless copy/paste of regular role attribution
|
||||
for role, count in addroles.items():
|
||||
selected = random.sample(villagers, count)
|
||||
var.ROLES[role] = set(selected)
|
||||
var.ROLES[role].clear()
|
||||
var.ROLES[role].update(selected)
|
||||
for x in selected:
|
||||
villagers.remove(x)
|
||||
|
||||
@ -1216,7 +1211,7 @@ class MaelstromMode(GameMode):
|
||||
var.FINAL_ROLES[p.nick] = role # FIXME
|
||||
var.MAIN_ROLES[p] = role
|
||||
|
||||
def _role_attribution(self, cli, var, villagers, do_templates):
|
||||
def _role_attribution(self, var, villagers, do_templates):
|
||||
lpl = len(villagers) - 1
|
||||
addroles = {}
|
||||
for role in var.ROLE_GUIDE:
|
||||
@ -1255,8 +1250,8 @@ class MaelstromMode(GameMode):
|
||||
mainroles[u] = role
|
||||
i += count
|
||||
|
||||
if self.chk_win_conditions(cli, rolemap, mainroles, end_game=False):
|
||||
return self._role_attribution(cli, var, villagers, do_templates)
|
||||
if self.chk_win_conditions(rolemap, mainroles, end_game=False):
|
||||
return self._role_attribution(var, villagers, do_templates)
|
||||
|
||||
return addroles
|
||||
|
||||
@ -1375,7 +1370,7 @@ class MudkipMode(GameMode):
|
||||
def daylight_warning(self, evt, var):
|
||||
evt.data["message"] = "daylight_warning_killtie"
|
||||
|
||||
def transition_night_begin(self, evt, cli, var):
|
||||
def transition_night_begin(self, evt, var):
|
||||
if var.FIRST_NIGHT:
|
||||
# ensure shaman gets death totem on the first night
|
||||
var.TOTEM_CHANCES["pestilence"] = (0, 1, 0)
|
||||
|
@ -10,6 +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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
@ -63,7 +64,7 @@ def pass_cmd(cli, nick, chan, rest):
|
||||
debuglog("{0} ({1}) PASS".format(nick, get_role(nick)))
|
||||
|
||||
@event_listener("rename_player")
|
||||
def on_rename(evt, cli, var, prefix, nick):
|
||||
def on_rename(evt, var, prefix, nick):
|
||||
for dictvar in (GUARDED, LASTGUARDED):
|
||||
kvp = {}
|
||||
for a,b in dictvar.items():
|
||||
@ -223,7 +224,7 @@ def on_transition_day_resolve_end(evt, var, victims):
|
||||
evt.data["dead"].append(gangel)
|
||||
|
||||
@event_listener("transition_night_begin")
|
||||
def on_transition_night_begin(evt, cli, var):
|
||||
def on_transition_night_begin(evt, var):
|
||||
# needs to be here in order to allow bodyguard protections to work during the daytime
|
||||
# (right now they don't due to other reasons, but that may change)
|
||||
GUARDED.clear()
|
||||
|
@ -10,6 +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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
@ -51,7 +52,7 @@ def on_transition_night_end(evt, var):
|
||||
blessed.send(messages[to_send])
|
||||
|
||||
@event_listener("desperation_totem")
|
||||
def on_desperation(evt, cli, var, votee, target, prot):
|
||||
def on_desperation(evt, var, votee, target, prot):
|
||||
if prot == "blessing":
|
||||
var.ACTIVE_PROTECTIONS[target].remove("blessing")
|
||||
evt.prevent_default = True
|
||||
|
@ -9,11 +9,12 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
@event_listener("see")
|
||||
def on_see(evt, cli, var, nick, victim):
|
||||
def on_see(evt, var, nick, victim):
|
||||
if users._get(victim) in var.ROLES["cursed villager"]: # FIXME
|
||||
evt.data["role"] = "wolf"
|
||||
|
||||
|
@ -7,6 +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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
@ -59,7 +60,7 @@ def investigate(cli, nick, chan, rest):
|
||||
debuglog("{0} ({1}) PAPER DROP".format(nick, get_role(nick)))
|
||||
|
||||
@event_listener("rename_player")
|
||||
def on_rename(evt, cli, var, prefix, nick):
|
||||
def on_rename(evt, var, prefix, nick):
|
||||
if prefix in INVESTIGATED:
|
||||
INVESTIGATED.remove(prefix)
|
||||
INVESTIGATED.add(nick)
|
||||
@ -96,7 +97,7 @@ def on_transition_night_end(evt, var):
|
||||
dttv.send(messages[to_send].format(warning), "Players: " + ", ".join(p.nick for p in pl), sep="\n")
|
||||
|
||||
@event_listener("transition_night_begin")
|
||||
def on_transition_night_begin(evt, cli, var):
|
||||
def on_transition_night_begin(evt, var):
|
||||
INVESTIGATED.clear()
|
||||
|
||||
@event_listener("reset")
|
||||
|
@ -6,6 +6,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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
@ -55,7 +56,7 @@ def see(cli, nick, chan, rest):
|
||||
SEEN.add(nick)
|
||||
|
||||
@event_listener("rename_player")
|
||||
def on_rename(evt, cli, var, prefix, nick):
|
||||
def on_rename(evt, var, prefix, nick):
|
||||
if prefix in SEEN:
|
||||
SEEN.remove(prefix)
|
||||
SEEN.add(nick)
|
||||
@ -97,7 +98,7 @@ def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
|
||||
del dictvar[k]
|
||||
|
||||
@event_listener("doctor_immunize")
|
||||
def on_doctor_immunize(evt, cli, var, doctor, target):
|
||||
def on_doctor_immunize(evt, var, doctor, target):
|
||||
if target in SICK.values():
|
||||
for n, v in list(SICK.items()):
|
||||
if v == target:
|
||||
@ -161,7 +162,7 @@ def on_begin_day(evt, var):
|
||||
LYCANS.clear()
|
||||
|
||||
@event_listener("transition_night_begin")
|
||||
def on_transition_night_begin(evt, cli, var):
|
||||
def on_transition_night_begin(evt, var):
|
||||
SICK.clear()
|
||||
|
||||
@event_listener("reset")
|
||||
|
@ -7,12 +7,13 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
import botconfig
|
||||
|
||||
KILLS = {} # type: Dict[users.User, users.User]
|
||||
TARGETS = {} # type: Dict[users.User, Set[users.User]]
|
||||
KILLS = UserDict() # type: Dict[users.User, users.User]
|
||||
TARGETS = UserDict() # type: Dict[users.User, Set[users.User]]
|
||||
|
||||
@command("kill", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("dullahan",))
|
||||
def dullahan_kill(var, wrapper, message):
|
||||
@ -62,66 +63,50 @@ def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
|
||||
del KILLS[h]
|
||||
if death_triggers and "dullahan" in allroles:
|
||||
pl = evt.data["pl"]
|
||||
targets = TARGETS[user].intersection(pl)
|
||||
if targets:
|
||||
target = random.choice(list(targets))
|
||||
prots = deque(var.ACTIVE_PROTECTIONS[target.nick])
|
||||
aevt = Event("assassinate", {"pl": evt.data["pl"], "target": target},
|
||||
del_player=evt.params.del_player,
|
||||
deadlist=evt.params.deadlist,
|
||||
original=evt.params.original,
|
||||
refresh_pl=evt.params.refresh_pl,
|
||||
message_prefix="dullahan_die_",
|
||||
source="dullahan",
|
||||
killer=user,
|
||||
killer_mainrole=mainrole,
|
||||
killer_allroles=allroles,
|
||||
prots=prots)
|
||||
while len(prots) > 0:
|
||||
# an event can read the current active protection and cancel or redirect the assassination
|
||||
# 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)
|
||||
if not aevt.dispatch(var, user, target, prots[0]):
|
||||
evt.data["pl"] = aevt.data["pl"]
|
||||
if target is not aevt.data["target"]:
|
||||
target = aevt.data["target"]
|
||||
prots = deque(var.ACTIVE_PROTECTIONS[target.nick])
|
||||
aevt.params.prots = prots
|
||||
continue
|
||||
return
|
||||
prots.popleft()
|
||||
with TARGETS[user].intersection(pl) as targets:
|
||||
if targets:
|
||||
target = random.choice(list(targets))
|
||||
prots = deque(var.ACTIVE_PROTECTIONS[target.nick])
|
||||
aevt = Event("assassinate", {"pl": evt.data["pl"], "target": target},
|
||||
del_player=evt.params.del_player,
|
||||
deadlist=evt.params.deadlist,
|
||||
original=evt.params.original,
|
||||
refresh_pl=evt.params.refresh_pl,
|
||||
message_prefix="dullahan_die_",
|
||||
source="dullahan",
|
||||
killer=user,
|
||||
killer_mainrole=mainrole,
|
||||
killer_allroles=allroles,
|
||||
prots=prots)
|
||||
while len(prots) > 0:
|
||||
# an event can read the current active protection and cancel or redirect the assassination
|
||||
# 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)
|
||||
if not aevt.dispatch(var, user, target, prots[0]):
|
||||
evt.data["pl"] = aevt.data["pl"]
|
||||
if target is not aevt.data["target"]:
|
||||
target = aevt.data["target"]
|
||||
prots = deque(var.ACTIVE_PROTECTIONS[target.nick])
|
||||
aevt.params.prots = prots
|
||||
continue
|
||||
return
|
||||
prots.popleft()
|
||||
|
||||
if var.ROLE_REVEAL in ("on", "team"):
|
||||
role = get_reveal_role(target)
|
||||
an = "n" if role.startswith(("a", "e", "i", "o", "u")) else ""
|
||||
channels.Main.send(messages["dullahan_die_success"].format(user, target, an, role))
|
||||
else:
|
||||
channels.Main.send(messages["dullahan_die_success_noreveal"].format(user, target))
|
||||
debuglog("{0} (dullahan) DULLAHAN ASSASSINATE: {1} ({2})".format(user, target, get_main_role(target)))
|
||||
evt.params.del_player(target, end_game=False, killer_role="dullahan", deadlist=evt.params.deadlist, original=evt.params.original, ismain=False)
|
||||
evt.data["pl"] = evt.params.refresh_pl(pl)
|
||||
if var.ROLE_REVEAL in ("on", "team"):
|
||||
role = get_reveal_role(target)
|
||||
an = "n" if role.startswith(("a", "e", "i", "o", "u")) else ""
|
||||
channels.Main.send(messages["dullahan_die_success"].format(user, target, an, role))
|
||||
else:
|
||||
channels.Main.send(messages["dullahan_die_success_noreveal"].format(user, target))
|
||||
debuglog("{0} (dullahan) DULLAHAN ASSASSINATE: {1} ({2})".format(user, target, get_main_role(target)))
|
||||
evt.params.del_player(target, end_game=False, killer_role="dullahan", deadlist=evt.params.deadlist, original=evt.params.original, ismain=False)
|
||||
evt.data["pl"] = evt.params.refresh_pl(pl)
|
||||
|
||||
@event_listener("night_acted")
|
||||
def on_acted(evt, var, user, actor):
|
||||
if user in KILLS:
|
||||
evt.data["acted"] = True
|
||||
|
||||
@event_listener("swap_player")
|
||||
def on_swap(evt, var, old_user, user):
|
||||
if old_user in KILLS:
|
||||
KILLS[user] = KILLS.pop(old_user)
|
||||
if old_user in TARGETS:
|
||||
TARGETS[user] = TARGETS.pop(old_user)
|
||||
|
||||
for dullahan, target in KILLS.items():
|
||||
if target is old_user:
|
||||
KILLS[dullahan] = user
|
||||
|
||||
for dullahan, targets in TARGETS.items():
|
||||
if old_user in targets:
|
||||
targets.remove(old_user)
|
||||
targets.add(user)
|
||||
|
||||
@event_listener("get_special")
|
||||
def on_get_special(evt, var):
|
||||
evt.data["special"].update(get_players(("dullahan",)))
|
||||
@ -172,19 +157,17 @@ def on_transition_night_end(evt, var):
|
||||
dullahan.send(messages[to_send], t + ", ".join(t.nick for t in targets), sep="\n")
|
||||
|
||||
@event_listener("role_assignment")
|
||||
def on_role_assignment(evt, cli, var, gamemode, pl, restart):
|
||||
def on_role_assignment(evt, var, gamemode, pl):
|
||||
# assign random targets to dullahan to kill
|
||||
if var.ROLES["dullahan"]:
|
||||
max_targets = math.ceil(8.1 * math.log(len(pl), 10) - 5)
|
||||
for dull in var.ROLES["dullahan"]:
|
||||
TARGETS[dull] = set()
|
||||
TARGETS[dull] = UserSet()
|
||||
dull_targets = Event("dullahan_targets", {"targets": TARGETS}) # support sleepy
|
||||
dull_targets.dispatch(cli, var, var.ROLES["dullahan"], max_targets)
|
||||
|
||||
players = [users._get(x) for x in pl] # FIXME
|
||||
dull_targets.dispatch(var, var.ROLES["dullahan"], max_targets)
|
||||
|
||||
for dull, ts in TARGETS.items():
|
||||
ps = players[:]
|
||||
ps = pl[:]
|
||||
ps.remove(dull)
|
||||
while len(ts) < max_targets:
|
||||
target = random.choice(ps)
|
||||
|
@ -9,6 +9,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.functions import get_players, get_all_players
|
||||
from src.messages import messages
|
||||
from src.events import Event
|
||||
|
@ -10,11 +10,12 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
VISITED = {} # type: Dict[users.User, users.User]
|
||||
PASSED = set() # type: Set[users.User]
|
||||
VISITED = UserDict() # type: Dict[users.User, users.User]
|
||||
PASSED = UserSet() # type: Set[users.User]
|
||||
|
||||
@command("visit", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("harlot",))
|
||||
def hvisit(var, wrapper, message):
|
||||
@ -134,18 +135,6 @@ def on_begin_day(evt, var):
|
||||
VISITED.clear()
|
||||
PASSED.clear()
|
||||
|
||||
@event_listener("swap_player")
|
||||
def on_swap(evt, var, old_user, user):
|
||||
for harlot, target in set(VISITED.items()):
|
||||
if target is old_user:
|
||||
VISITED[harlot] = user
|
||||
if harlot is old_user:
|
||||
VISITED[user] = VISITED.pop(harlot)
|
||||
|
||||
if old_user in PASSED:
|
||||
PASSED.remove(old_user)
|
||||
PASSED.add(user)
|
||||
|
||||
@event_listener("get_special")
|
||||
def on_get_special(evt, var):
|
||||
evt.data["special"].update(get_players(("harlot",)))
|
||||
|
@ -7,12 +7,13 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
KILLS = {} # type: Dict[users.User, users.User]
|
||||
HUNTERS = set()
|
||||
PASSED = set()
|
||||
KILLS = UserDict() # type: Dict[users.User, users.User]
|
||||
HUNTERS = UserSet()
|
||||
PASSED = UserSet()
|
||||
|
||||
@command("kill", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("hunter",))
|
||||
def hunter_kill(var, wrapper, message):
|
||||
@ -74,20 +75,6 @@ def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
|
||||
h.send(messages["hunter_discard"])
|
||||
del KILLS[h]
|
||||
|
||||
@event_listener("swap_player")
|
||||
def on_swap(evt, var, old_user, user):
|
||||
for a, b in list(KILLS.items()):
|
||||
if a is old_user:
|
||||
KILLS[user] = KILLS.pop(old_user)
|
||||
if b is old_user:
|
||||
KILLS[user] = KILLS.pop(old_user)
|
||||
if old_user in HUNTERS:
|
||||
HUNTERS.discard(old_user)
|
||||
HUNTERS.add(user)
|
||||
if old_user in PASSED:
|
||||
PASSED.discard(old_user)
|
||||
PASSED.add(user)
|
||||
|
||||
@event_listener("night_acted")
|
||||
def on_acted(evt, var, user, actor):
|
||||
if user in KILLS:
|
||||
|
@ -10,10 +10,11 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
INVESTIGATED = set()
|
||||
INVESTIGATED = UserSet()
|
||||
|
||||
@command("id", chan=False, pm=True, playing=True, silenced=True, phases=("day",), roles=("investigator",))
|
||||
def investigate(var, wrapper, message):
|
||||
@ -97,12 +98,6 @@ def investigate(var, wrapper, message):
|
||||
wrapper.source, target1, get_main_role(target1), target2, get_main_role(target2),
|
||||
"same" if same else "different"))
|
||||
|
||||
@event_listener("swap_player")
|
||||
def on_swap(evt, var, old_user, user):
|
||||
if old_user in INVESTIGATED:
|
||||
INVESTIGATED.discard(old_user)
|
||||
INVESTIGATED.add(user)
|
||||
|
||||
@event_listener("del_player")
|
||||
def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
|
||||
INVESTIGATED.discard(user)
|
||||
@ -116,7 +111,7 @@ def on_exchange(evt, var, actor, target, actor_role, target_role):
|
||||
if actor_role == "investigator" and target_role != "investigator":
|
||||
INVESTIGATED.discard(actor)
|
||||
elif target_role == "investigator" and actor_role != "investigator":
|
||||
INVESTIGATED.discard(targe)
|
||||
INVESTIGATED.discard(target)
|
||||
|
||||
@event_listener("transition_night_end", priority=2)
|
||||
def on_transition_night_end(evt, var):
|
||||
@ -131,7 +126,7 @@ def on_transition_night_end(evt, var):
|
||||
inv.send(messages[to_send], "Players: " + ", ".join(p.nick for p in pl), sep="\n")
|
||||
|
||||
@event_listener("transition_night_begin")
|
||||
def on_transition_night_begin(evt, cli, var):
|
||||
def on_transition_night_begin(evt, var):
|
||||
INVESTIGATED.clear()
|
||||
|
||||
@event_listener("reset")
|
||||
|
@ -10,6 +10,7 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
|
@ -9,13 +9,14 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
REVEALED_MAYORS = set()
|
||||
|
||||
@event_listener("rename_player")
|
||||
def on_rename_player(evt, cli, var, prefix, nick):
|
||||
def on_rename_player(evt, var, prefix, nick):
|
||||
if prefix in REVEALED_MAYORS:
|
||||
REVEALED_MAYORS.remove(prefix)
|
||||
REVEALED_MAYORS.add(nick)
|
||||
|
@ -6,6 +6,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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
|
@ -10,11 +10,12 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
TOBECHARMED = {} # type: Dict[users.User, Set[users.User]]
|
||||
CHARMED = set() # type: Set[users.User]
|
||||
TOBECHARMED = UserDict() # type: Dict[users.User, Set[users.User]]
|
||||
CHARMED = UserSet() # type: Set[users.User]
|
||||
|
||||
@command("charm", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("piper",))
|
||||
def charm(var, wrapper, message):
|
||||
@ -69,8 +70,12 @@ def charm(var, wrapper, message):
|
||||
wrapper.send(messages["target_already_charmed"].format(orig2))
|
||||
return
|
||||
|
||||
TOBECHARMED[wrapper.source] = {target1, target2}
|
||||
TOBECHARMED[wrapper.source].discard(None)
|
||||
if wrapper.source in TOBECHARMED:
|
||||
TOBECHARMED[wrapper.source].clear()
|
||||
else:
|
||||
TOBECHARMED[wrapper.source] = UserSet()
|
||||
|
||||
TOBECHARMED[wrapper.source].update({target1, target2} - {None})
|
||||
|
||||
if orig2:
|
||||
debuglog("{0} (piper) CHARM {1} ({2}) && {3} ({4})".format(wrapper.source,
|
||||
@ -109,7 +114,9 @@ def on_player_win(evt, var, player, mainrole, winner, survived):
|
||||
@event_listener("del_player")
|
||||
def on_del_player(evt, var, player, mainrole, allroles, death_triggers):
|
||||
CHARMED.discard(player)
|
||||
TOBECHARMED.pop(player, None)
|
||||
x = TOBECHARMED.pop(player, None)
|
||||
if x is not None:
|
||||
x.clear()
|
||||
|
||||
@event_listener("transition_day_begin")
|
||||
def on_transition_day_begin(evt, var):
|
||||
@ -196,18 +203,4 @@ def on_revealroles(evt, var, wrapper):
|
||||
if CHARMED:
|
||||
evt.data["output"].append("\u0002charmed players\u0002: {0}".format(", ".join(p.nick for p in CHARMED)))
|
||||
|
||||
@event_listener("swap_player")
|
||||
def on_swap_player(evt, var, old, new):
|
||||
if old in CHARMED:
|
||||
CHARMED.remove(old)
|
||||
CHARMED.add(new)
|
||||
|
||||
if old in TOBECHARMED:
|
||||
TOBECHARMED[new] = TOBECHARMED.pop(old)
|
||||
|
||||
for s in TOBECHARMED.values():
|
||||
if old in s:
|
||||
s.remove(old)
|
||||
s.add(new)
|
||||
|
||||
# vim: set sw=4 expandtab:
|
||||
|
@ -5,6 +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.functions import get_players, get_all_players, get_main_role
|
||||
from src.messages import messages
|
||||
from src.events import Event
|
||||
@ -47,7 +48,7 @@ def see(cli, nick, chan, rest):
|
||||
victimrole = "villager"
|
||||
|
||||
evt = Event("see", {"role": victimrole})
|
||||
evt.dispatch(cli, var, nick, victim)
|
||||
evt.dispatch(var, nick, victim)
|
||||
victimrole = evt.data["role"]
|
||||
else:
|
||||
if victimrole == "amnesiac":
|
||||
@ -78,7 +79,7 @@ def see(cli, nick, chan, rest):
|
||||
SEEN.add(nick)
|
||||
|
||||
@event_listener("rename_player")
|
||||
def on_rename(evt, cli, var, prefix, nick):
|
||||
def on_rename(evt, var, prefix, nick):
|
||||
if prefix in SEEN:
|
||||
SEEN.remove(prefix)
|
||||
SEEN.add(nick)
|
||||
|
@ -9,6 +9,7 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
@ -65,7 +66,7 @@ def totem(cli, nick, chan, rest, prefix="You"): # XXX: The transition_day_begin
|
||||
return
|
||||
|
||||
original_victim = victim
|
||||
role = get_role(nick) # FIXME: this is bad, check if nick is in var.ROLES[thingy] instead once split
|
||||
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]
|
||||
@ -92,7 +93,7 @@ def totem(cli, nick, chan, rest, prefix="You"): # XXX: The transition_day_begin
|
||||
debuglog("{0} ({1}) TOTEM: {2} ({3})".format(nick, role, victim, TOTEMS[nick]))
|
||||
|
||||
@event_listener("rename_player")
|
||||
def on_rename(evt, cli, var, prefix, nick):
|
||||
def on_rename(evt, var, prefix, nick):
|
||||
if prefix in TOTEMS:
|
||||
TOTEMS[nick] = TOTEMS.pop(prefix)
|
||||
|
||||
@ -130,7 +131,7 @@ def on_rename(evt, cli, var, prefix, nick):
|
||||
setvar.add(nick)
|
||||
|
||||
@event_listener("see", priority=10)
|
||||
def on_see(evt, cli, var, nick, victim):
|
||||
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"
|
||||
@ -282,7 +283,7 @@ def on_chk_decision_lynch5(evt, cli, var, voters):
|
||||
# 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(cli, var, votee, target, prots[0]):
|
||||
if not desp_evt.dispatch(var, votee, target, prots[0]):
|
||||
return
|
||||
prots.popleft()
|
||||
if var.ROLE_REVEAL in ("on", "team"):
|
||||
@ -627,26 +628,6 @@ def on_reset(evt, var):
|
||||
MISDIRECTION.clear()
|
||||
DECEIT.clear()
|
||||
|
||||
@event_listener("frole_role")
|
||||
def on_frole_role(evt, cli, var, who, role, oldrole, args):
|
||||
if role in var.TOTEM_ORDER:
|
||||
if len(args) == 2:
|
||||
TOTEMS[who] = args[1]
|
||||
else:
|
||||
max_totems = defaultdict(int)
|
||||
for ix in range(len(var.TOTEM_ORDER)):
|
||||
for c in var.TOTEM_CHANCES.values():
|
||||
max_totems[var.TOTEM_ORDER[ix]] += c[ix]
|
||||
for shaman in list_players(var.TOTEM_ORDER):
|
||||
indx = var.TOTEM_ORDER.index(role)
|
||||
target = 0
|
||||
rand = random.random() * max_totems[var.TOTEM_ORDER[indx]]
|
||||
for t in var.TOTEM_CHANCES.keys():
|
||||
target += var.TOTEM_CHANCES[t][indx]
|
||||
if rand <= target:
|
||||
TOTEMS[shaman] = t
|
||||
break
|
||||
|
||||
@event_listener("get_role_metadata")
|
||||
def on_get_role_metadata(evt, var, kind):
|
||||
if kind == "night_kills":
|
||||
|
@ -10,11 +10,15 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
# Skeleton file for new roles. Not all events are represented, only the most common ones.
|
||||
|
||||
# Instead of using list, set or dict, please use UserList, UserSet or UserDict respectively
|
||||
# Please refer to the notes in src/containers.py for proper use
|
||||
|
||||
# Add to evt.data["actedcount"] and evt.data["nightroles"] if this role can act during night
|
||||
# nightroles lists all Users who have this role and are capable of acting tonight
|
||||
# actedcount should be added to (via +=) with everyone who has this role and has already acted
|
||||
@ -60,11 +64,6 @@ def on_del_player(evt, var, player, mainrole, allroles, death_triggers):
|
||||
def on_reset(evt, var):
|
||||
pass
|
||||
|
||||
# Swap out a user with a different one. Update all game state to use the new User.
|
||||
@event_listener("swap_player")
|
||||
def on_swap_player(evt, var, old, new):
|
||||
pass
|
||||
|
||||
# Gets metadata about this role; kind will be a str with one of the following values:
|
||||
# night_kills: Add metadata about any deaths this role can cause at night which use the standard
|
||||
# death message (i.e. do not have a custom death message). Set the data as follows:
|
||||
|
@ -10,13 +10,14 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
ENTRANCED = set() # type: Set[users.User]
|
||||
ENTRANCED_DYING = set() # type: Set[users.User]
|
||||
VISITED = {} # type: Dict[users.User, users.User]
|
||||
PASSED = set() # type: Set[users.User]
|
||||
ENTRANCED = UserSet() # type: Set[users.User]
|
||||
ENTRANCED_DYING = UserSet() # type: Set[users.User]
|
||||
VISITED = UserDict() # type: Dict[users.User, users.User]
|
||||
PASSED = UserSet() # type: Set[users.User]
|
||||
ALL_SUCC_IDLE = True
|
||||
|
||||
@command("visit", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("succubus",))
|
||||
@ -319,25 +320,6 @@ def on_vg_kill(evt, var, ghost, target):
|
||||
if ghost in ENTRANCED:
|
||||
evt.data["pl"] -= get_all_players(("succubus",))
|
||||
|
||||
@event_listener("swap_player")
|
||||
def on_swap(evt, var, old_user, user):
|
||||
if old_user in ENTRANCED:
|
||||
ENTRANCED.remove(old_user)
|
||||
ENTRANCED.add(user)
|
||||
if old_user in ENTRANCED_DYING:
|
||||
ENTRANCED_DYING.remove(old_user)
|
||||
ENTRANCED_DYING.add(user)
|
||||
|
||||
for succubus, target in set(VISITED.items()):
|
||||
if old_user is succubus:
|
||||
VISITED[user] = VISITED.pop(succubus)
|
||||
if old_user is target:
|
||||
VISITED[succubus] = user
|
||||
|
||||
if old_user in PASSED:
|
||||
PASSED.remove(old_user)
|
||||
PASSED.add(user)
|
||||
|
||||
@event_listener("reset")
|
||||
def on_reset(evt, var):
|
||||
global ALL_SUCC_IDLE
|
||||
|
@ -9,6 +9,7 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
|
@ -7,14 +7,15 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
KILLS = {} # type: Dict[users.User, users.User]
|
||||
GHOSTS = {} # type: Dict[users.User, str]
|
||||
KILLS = UserDict() # type: Dict[users.User, users.User]
|
||||
GHOSTS = UserDict() # type: Dict[users.User, str]
|
||||
|
||||
# temporary holding variable, only non-empty during transition_day
|
||||
drivenoff = {} # type: Dict[users.User, str]
|
||||
drivenoff = UserDict() # type: Dict[users.User, str]
|
||||
|
||||
@command("kill", chan=False, pm=True, playing=False, silenced=True, phases=("night",), users=GHOSTS)
|
||||
def vg_kill(var, wrapper, message):
|
||||
|
@ -7,11 +7,12 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
KILLS = {} # type: Dict[users.User, users.User]
|
||||
PASSED = set() # type: Set[users.User]
|
||||
KILLS = UserDict() # type: Dict[users.User, users.User]
|
||||
PASSED = UserSet() # type: Set[users.User]
|
||||
|
||||
@command("kill", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("vigilante",))
|
||||
def vigilante_kill(var, wrapper, message):
|
||||
@ -61,18 +62,6 @@ def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
|
||||
vigilante.send(messages["hunter_discard"])
|
||||
del KILLS[vigilante]
|
||||
|
||||
@event_listener("swap_player")
|
||||
def on_swap(evt, var, old_user, user):
|
||||
for vigilante, target in set(KILLS.items()):
|
||||
if vigilante is old_user:
|
||||
KILLS[user] = KILLS.pop(vigilante)
|
||||
if target is old_user:
|
||||
KILLS[vigilante] = user
|
||||
|
||||
if old_user in PASSED:
|
||||
PASSED.remove(old_user)
|
||||
PASSED.add(user)
|
||||
|
||||
@event_listener("night_acted")
|
||||
def on_acted(evt, var, target, spy):
|
||||
if target in KILLS:
|
||||
|
@ -3,6 +3,7 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
|
@ -6,6 +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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
@ -35,12 +36,12 @@ def choose_idol(cli, nick, chan, rest):
|
||||
debuglog("{0} (wild child) IDOLIZE: {1} ({2})".format(nick, victim, get_role(victim)))
|
||||
|
||||
@event_listener("see")
|
||||
def on_see(evt, cli, var, seer, victim):
|
||||
def on_see(evt, var, seer, victim):
|
||||
if victim in WILD_CHILDREN:
|
||||
evt.data["role"] = "wild child"
|
||||
|
||||
@event_listener("rename_player")
|
||||
def on_rename(evt, cli, var, prefix, nick):
|
||||
def on_rename(evt, var, prefix, nick):
|
||||
if prefix in WILD_CHILDREN:
|
||||
WILD_CHILDREN.remove(prefix)
|
||||
WILD_CHILDREN.add(nick)
|
||||
|
@ -7,6 +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.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
@ -127,7 +128,7 @@ def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
|
||||
del KILLS[a]
|
||||
|
||||
@event_listener("rename_player")
|
||||
def on_rename(evt, cli, var, prefix, nick):
|
||||
def on_rename(evt, var, prefix, nick):
|
||||
kvp = []
|
||||
for a,b in KILLS.items():
|
||||
nl = []
|
||||
|
@ -7,6 +7,7 @@ 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.messages import messages
|
||||
from src.events import Event
|
||||
from src.roles import wolf
|
||||
|
@ -3,8 +3,6 @@ import re
|
||||
import threading
|
||||
from collections import defaultdict, OrderedDict
|
||||
|
||||
import botconfig
|
||||
|
||||
LANGUAGE = 'en'
|
||||
|
||||
MINIMUM_WAIT = 60
|
||||
|
42
src/users.py
42
src/users.py
@ -194,17 +194,10 @@ def _reset(evt, var):
|
||||
_users.discard(user)
|
||||
_ghosts.clear()
|
||||
|
||||
def _swap_player(evt, var, old_user, user):
|
||||
"""Mark the user as no longer being a ghost, if they are one."""
|
||||
_ghosts.discard(old_user)
|
||||
if not old_user.channels:
|
||||
_users.discard(old_user)
|
||||
|
||||
# Can't use @event_listener decorator since src/decorators.py imports us
|
||||
# (meaning decorator isn't defined at the point in time we are run)
|
||||
events.add_listener("cleanup_user", _cleanup_user)
|
||||
events.add_listener("reset", _reset)
|
||||
events.add_listener("swap_player", _swap_player, priority=1)
|
||||
|
||||
class User(IRCContext):
|
||||
|
||||
@ -220,6 +213,10 @@ class User(IRCContext):
|
||||
self.account = account
|
||||
self.channels = {}
|
||||
self.timestamp = time.time()
|
||||
self.sets = []
|
||||
self.lists = []
|
||||
self.dict_keys = []
|
||||
self.dict_values = []
|
||||
|
||||
if Bot is not None and Bot.nick == nick and {Bot.ident, Bot.host, Bot.realname, Bot.account} == {None}:
|
||||
self = Bot
|
||||
@ -314,6 +311,35 @@ class User(IRCContext):
|
||||
def __deepcopy__(self, memo):
|
||||
return self
|
||||
|
||||
def swap(self, new):
|
||||
"""Swap yourself out with the new user everywhere."""
|
||||
if self is new:
|
||||
return # as far as the caller is aware, we've swapped
|
||||
|
||||
_ghosts.discard(self)
|
||||
if not self.channels:
|
||||
_users.discard(self) # Goodbye, my old friend
|
||||
|
||||
for l in self.lists[:]:
|
||||
while self in l:
|
||||
l[l.index(self)] = new
|
||||
|
||||
for s in self.sets[:]:
|
||||
s.remove(self)
|
||||
s.add(new)
|
||||
|
||||
for dk in self.dict_keys[:]:
|
||||
dk[new] = dk.pop(self)
|
||||
|
||||
for dv in self.dict_values[:]:
|
||||
for key in dv:
|
||||
if dv[key] is self:
|
||||
dv[key] = new
|
||||
|
||||
# It is the containers' reponsibility to properly remove themself from the users
|
||||
# So if any list is non-empty, something went terribly wrong
|
||||
assert not self.lists + self.sets + self.dict_keys + self.dict_values
|
||||
|
||||
def lower(self):
|
||||
temp = type(self)(self.client, lower(self.nick), lower(self.ident), lower(self.host, casemapping="ascii"), lower(self.realname), lower(self.account))
|
||||
if temp is not self: # If everything is already lowercase, we'll get back the same instance
|
||||
@ -613,7 +639,7 @@ class FakeUser(User):
|
||||
|
||||
@rawnick.setter
|
||||
def rawnick(self, rawnick):
|
||||
self.nick = parse_rawnick_as_dict(rawnick)["nick"]
|
||||
raise ValueError("may not change the raw nick of a fake user")
|
||||
|
||||
class BotUser(User): # TODO: change all the 'if x is Bot' for 'if isinstance(x, BotUser)'
|
||||
|
||||
|
274
src/wolfgame.py
274
src/wolfgame.py
@ -49,6 +49,7 @@ import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import db, events, dispatcher, channels, users, hooks, logger, debuglog, errlog, plog
|
||||
from src.decorators import command, cmd, hook, handle_error, event_listener, COMMANDS
|
||||
from src.containers import UserList, UserSet, UserDict
|
||||
from src.functions import get_players, get_all_players, get_participants, get_main_role, get_all_roles, get_reveal_role
|
||||
from src.messages import messages
|
||||
from src.warnings import *
|
||||
@ -72,8 +73,8 @@ var.LAST_GOAT = {}
|
||||
var.USERS = {}
|
||||
|
||||
var.ADMIN_PINGING = False
|
||||
var.ORIGINAL_ROLES = {} # type: Dict[str, Set[users.User]]
|
||||
var.DCED_LOSERS = set() # type: Set[users.User]
|
||||
var.ORIGINAL_ROLES = UserDict() # type: Dict[str, Set[users.User]]
|
||||
var.DCED_LOSERS = UserSet() # type: Set[users.User]
|
||||
var.PLAYERS = {}
|
||||
var.DCED_PLAYERS = {}
|
||||
var.ADMIN_TO_PING = None
|
||||
@ -82,6 +83,16 @@ var.PINGING_IFS = False
|
||||
var.TIMERS = {}
|
||||
var.OLD_MODES = defaultdict(set)
|
||||
|
||||
var.ROLES = UserDict() # type: Dict[str, Set[users.User]]
|
||||
var.MAIN_ROLES = UserDict() # type: Dict[users.User, str]
|
||||
var.ALL_PLAYERS = UserList()
|
||||
|
||||
var.DYING = UserSet()
|
||||
var.DEADCHAT_PLAYERS = UserSet()
|
||||
|
||||
var.SPECTATING_WOLFCHAT = UserSet()
|
||||
var.SPECTATING_DEADCHAT = UserSet()
|
||||
|
||||
var.ORIGINAL_SETTINGS = {}
|
||||
var.CURRENT_GAMEMODE = var.GAME_MODES["default"][0]()
|
||||
|
||||
@ -297,11 +308,9 @@ def reset_modes_timers(var):
|
||||
def reset():
|
||||
var.PHASE = "none" # "join", "day", or "night"
|
||||
var.GAME_ID = 0
|
||||
var.ALL_PLAYERS.clear()
|
||||
var.RESTART_TRIES = 0
|
||||
var.DEAD = set()
|
||||
var.ROLES = {"person" : set()} # type: Dict[str, Set[users.User]]
|
||||
var.MAIN_ROLES = {} # type: Dict[users.User, str]
|
||||
var.ALL_PLAYERS = []
|
||||
var.JOINED_THIS_GAME = set() # keeps track of who already joined this game at least once (hostmasks)
|
||||
var.JOINED_THIS_GAME_ACCS = set() # same, except accounts
|
||||
var.PINGED_ALREADY = set()
|
||||
@ -321,8 +330,13 @@ def reset():
|
||||
var.DCED_PLAYERS.clear()
|
||||
var.DISCONNECTED.clear()
|
||||
var.DCED_LOSERS.clear()
|
||||
var.SPECTATING_WOLFCHAT = set()
|
||||
var.SPECTATING_DEADCHAT = set()
|
||||
var.SPECTATING_WOLFCHAT.clear()
|
||||
var.SPECTATING_DEADCHAT.clear()
|
||||
|
||||
var.ROLES.clear()
|
||||
var.ORIGINAL_ROLES.clear()
|
||||
var.ROLES["person"] = UserSet()
|
||||
var.MAIN_ROLES.clear()
|
||||
|
||||
evt = Event("reset", {})
|
||||
evt.dispatch(var)
|
||||
@ -582,9 +596,8 @@ def replace(var, wrapper, message):
|
||||
return
|
||||
|
||||
if users.equals(target.account, wrapper.source.account) and target is not wrapper.source:
|
||||
evt = Event("swap_player", {})
|
||||
evt.dispatch(var, target, wrapper.source)
|
||||
rename_player(var, wrapper.source, target.nick)
|
||||
target.swap(wrapper.source)
|
||||
if var.PHASE in var.GAME_PHASES:
|
||||
return_to_village(var, target, show_message=False)
|
||||
|
||||
@ -594,19 +607,6 @@ def replace(var, wrapper, message):
|
||||
channels.Main.send(messages["player_swap"].format(wrapper.source, target))
|
||||
myrole.caller(wrapper.source.client, wrapper.source.nick, wrapper.target.name, "") # FIXME: Old API
|
||||
|
||||
@event_listener("swap_player", priority=0)
|
||||
def swap_player(evt, var, old_user, user):
|
||||
var.ALL_PLAYERS[var.ALL_PLAYERS.index(old_user)] = user
|
||||
var.MAIN_ROLES[user] = var.MAIN_ROLES.pop(old_user)
|
||||
for role, players in var.ROLES.items():
|
||||
if old_user in players:
|
||||
players.remove(old_user)
|
||||
players.add(user)
|
||||
for role, players in var.ORIGINAL_ROLES.items():
|
||||
if old_user in players:
|
||||
players.remove(old_user)
|
||||
players.add(user)
|
||||
|
||||
|
||||
@command("pingif", "pingme", "pingat", "pingpref", pm=True)
|
||||
def altpinger(var, wrapper, message):
|
||||
@ -2006,41 +2006,41 @@ def stop_game(winner="", abort=False, additional_winners=None, log=True):
|
||||
roles_msg = []
|
||||
|
||||
origroles = {} # user-based list of original roles
|
||||
rolelist = copy.deepcopy(var.ORIGINAL_ROLES)
|
||||
for role, playerlist in var.ORIGINAL_ROLES.items():
|
||||
if role in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
continue
|
||||
for p in playerlist:
|
||||
final = var.FINAL_ROLES.get(p.nick, role)
|
||||
if role != final:
|
||||
origroles[p] = role
|
||||
rolelist[role].remove(p)
|
||||
rolelist[final].add(p)
|
||||
prev = False
|
||||
for role in role_order():
|
||||
if len(rolelist[role]) == 0:
|
||||
continue
|
||||
playersformatted = []
|
||||
for p in rolelist[role]:
|
||||
if p in origroles and role not in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
playersformatted.append("\u0002{0}\u0002 ({1}{2})".format(p,
|
||||
"" if prev else "was ", origroles[p]))
|
||||
prev = True
|
||||
elif role == "amnesiac":
|
||||
playersformatted.append("\u0002{0}\u0002 (would be {1})".format(p,
|
||||
var.AMNESIAC_ROLES[p.nick]))
|
||||
with copy.deepcopy(var.ORIGINAL_ROLES) as rolelist:
|
||||
for role, playerlist in var.ORIGINAL_ROLES.items():
|
||||
if role in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
continue
|
||||
for p in playerlist:
|
||||
final = var.FINAL_ROLES.get(p.nick, role)
|
||||
if role != final:
|
||||
origroles[p] = role
|
||||
rolelist[role].remove(p)
|
||||
rolelist[final].add(p)
|
||||
prev = False
|
||||
for role in role_order():
|
||||
if len(rolelist[role]) == 0:
|
||||
continue
|
||||
playersformatted = []
|
||||
for p in rolelist[role]:
|
||||
if p in origroles and role not in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
playersformatted.append("\u0002{0}\u0002 ({1}{2})".format(p,
|
||||
"" if prev else "was ", origroles[p]))
|
||||
prev = True
|
||||
elif role == "amnesiac":
|
||||
playersformatted.append("\u0002{0}\u0002 (would be {1})".format(p,
|
||||
var.AMNESIAC_ROLES[p.nick]))
|
||||
else:
|
||||
playersformatted.append("\u0002{0}\u0002".format(p))
|
||||
if len(rolelist[role]) == 2:
|
||||
msg = "The {1} were {0[0]} and {0[1]}."
|
||||
roles_msg.append(msg.format(playersformatted, plural(role)))
|
||||
elif len(rolelist[role]) == 1:
|
||||
roles_msg.append("The {1} was {0[0]}.".format(playersformatted, role))
|
||||
else:
|
||||
playersformatted.append("\u0002{0}\u0002".format(p))
|
||||
if len(rolelist[role]) == 2:
|
||||
msg = "The {1} were {0[0]} and {0[1]}."
|
||||
roles_msg.append(msg.format(playersformatted, plural(role)))
|
||||
elif len(rolelist[role]) == 1:
|
||||
roles_msg.append("The {1} was {0[0]}.".format(playersformatted, role))
|
||||
else:
|
||||
msg = "The {2} were {0}, and {1}."
|
||||
roles_msg.append(msg.format(", ".join(playersformatted[0:-1]),
|
||||
playersformatted[-1],
|
||||
plural(role)))
|
||||
msg = "The {2} were {0}, and {1}."
|
||||
roles_msg.append(msg.format(", ".join(playersformatted[0:-1]),
|
||||
playersformatted[-1],
|
||||
plural(role)))
|
||||
message = ""
|
||||
count = 0
|
||||
if not abort:
|
||||
@ -2919,8 +2919,7 @@ def return_to_village(var, target, *, show_message, new_user=None):
|
||||
|
||||
if new_user is not target:
|
||||
# different users, perform a swap. This will clean up disconnected users.
|
||||
evt = Event("swap_player", {})
|
||||
evt.dispatch(var, target, new_user)
|
||||
target.swap(new_user)
|
||||
|
||||
if target.nick != new_user.nick:
|
||||
# have a nickchange, update tracking vars
|
||||
@ -2950,7 +2949,7 @@ def rename_player(var, user, prefix):
|
||||
nick = user.nick
|
||||
|
||||
event = Event("rename_player", {})
|
||||
event.dispatch(user.client, var, prefix, nick) # FIXME: Need to update all the callbacks
|
||||
event.dispatch(var, prefix, nick)
|
||||
|
||||
if user in var.ALL_PLAYERS:
|
||||
if var.PHASE in var.GAME_PHASES:
|
||||
@ -3247,7 +3246,7 @@ def begin_day(cli):
|
||||
var.LUCKY = set()
|
||||
var.DISEASED = set()
|
||||
var.MISDIRECTED = set()
|
||||
var.DYING = set()
|
||||
var.DYING.clear()
|
||||
var.LAST_GOAT.clear()
|
||||
msg = messages["villagers_lynch"].format(botconfig.CMD_CHAR, len(list_players()) // 2 + 1)
|
||||
cli.msg(chan, msg)
|
||||
@ -4488,7 +4487,7 @@ def immunize(cli, nick, chan, rest):
|
||||
if check_exchange(cli, nick, victim):
|
||||
return
|
||||
evt = Event("doctor_immunize", {"success": True, "message": "villager_immunized"})
|
||||
if evt.dispatch(cli, var, nick, victim):
|
||||
if evt.dispatch(var, nick, victim):
|
||||
pm(cli, nick, messages["doctor_success"].format(victim))
|
||||
lycan = False
|
||||
if victim in var.DISEASED:
|
||||
@ -4544,8 +4543,7 @@ def bite_cmd(cli, nick, chan, rest):
|
||||
relay_wolfchat_command(cli, nick, messages["alpha_bite_wolfchat"].format(nick, victim), ("alpha wolf",), is_wolf_command=True)
|
||||
debuglog("{0} ({1}) BITE: {2} ({3})".format(nick, get_role(nick), actual, get_role(actual)))
|
||||
|
||||
@cmd("pass", chan=False, pm=True, playing=True, phases=("night",),
|
||||
roles=("turncoat", "warlock"))
|
||||
@cmd("pass", chan=False, pm=True, playing=True, phases=("night",), roles=("turncoat", "warlock"))
|
||||
def pass_cmd(cli, nick, chan, rest):
|
||||
"""Decline to use your special power for that night."""
|
||||
nickrole = get_role(nick)
|
||||
@ -4976,7 +4974,7 @@ def transition_night(cli):
|
||||
var.FIRST_NIGHT = (var.NIGHT_COUNT == 1)
|
||||
|
||||
event_begin = Event("transition_night_begin", {})
|
||||
event_begin.dispatch(cli, var)
|
||||
event_begin.dispatch(var)
|
||||
|
||||
if var.DEVOICE_DURING_NIGHT:
|
||||
modes = []
|
||||
@ -5381,7 +5379,7 @@ def start(cli, nick, chan, forced = False, restart = ""):
|
||||
addroles = {}
|
||||
|
||||
event = Event("role_attribution", {"addroles": addroles})
|
||||
if event.dispatch(cli, var, chk_win_conditions, villagers):
|
||||
if event.dispatch(var, chk_win_conditions, villagers):
|
||||
addroles = event.data["addroles"]
|
||||
for index in range(len(var.ROLE_INDEX) - 1, -1, -1):
|
||||
if var.ROLE_INDEX[index] <= len(villagers):
|
||||
@ -5438,7 +5436,9 @@ def start(cli, nick, chan, forced = False, restart = ""):
|
||||
for decor in (COMMANDS["join"] + COMMANDS["start"]):
|
||||
decor(_command_disabled)
|
||||
|
||||
var.ROLES = {var.DEFAULT_ROLE: set()} # type: Dict[str, Set[users.User]]
|
||||
var.ROLES.clear()
|
||||
var.ROLES[var.DEFAULT_ROLE] = UserSet()
|
||||
var.MAIN_ROLES.clear()
|
||||
var.GUNNERS = {}
|
||||
var.OBSERVED = {}
|
||||
var.CLONED = {}
|
||||
@ -5453,7 +5453,6 @@ def start(cli, nick, chan, forced = False, restart = ""):
|
||||
var.DAY_COUNT = 0
|
||||
var.DISEASED_WOLVES = False
|
||||
var.TRAITOR_TURNED = False
|
||||
var.MAIN_ROLES = {}
|
||||
var.FINAL_ROLES = {}
|
||||
var.ORIGINAL_LOVERS = {}
|
||||
var.LYCANTHROPES = set()
|
||||
@ -5478,22 +5477,23 @@ def start(cli, nick, chan, forced = False, restart = ""):
|
||||
var.EXTRA_WOLVES = 0
|
||||
var.PRIESTS = set()
|
||||
var.CONSECRATING = set()
|
||||
var.DYING = set()
|
||||
var.DYING.clear()
|
||||
var.PRAYED = {}
|
||||
|
||||
var.DEADCHAT_PLAYERS = set()
|
||||
var.SPECTATING_WOLFCHAT = set()
|
||||
var.SPECTATING_DEADCHAT = set()
|
||||
var.DEADCHAT_PLAYERS.clear()
|
||||
var.SPECTATING_WOLFCHAT.clear()
|
||||
var.SPECTATING_DEADCHAT.clear()
|
||||
|
||||
for role, count in addroles.items():
|
||||
if role in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
var.ROLES[role] = [None] * count
|
||||
continue # We deal with those later, see below
|
||||
|
||||
selected = random.sample(villagers, count)
|
||||
for x in selected:
|
||||
var.MAIN_ROLES[users._get(x)] = role # FIXME
|
||||
villagers.remove(x)
|
||||
var.ROLES[role] = set(users._get(x) for x in selected) # FIXME
|
||||
var.ROLES[role] = UserSet(users._get(x) for x in selected) # FIXME
|
||||
fixed_count = count - roleset_roles[role]
|
||||
if fixed_count > 0:
|
||||
for pr in possible_rolesets:
|
||||
@ -5523,17 +5523,18 @@ def start(cli, nick, chan, forced = False, restart = ""):
|
||||
if len(possible) < len(var.ROLES[template]):
|
||||
cli.msg(chan, messages["not_enough_targets"].format(template))
|
||||
if var.ORIGINAL_SETTINGS:
|
||||
var.ROLES = {"person": set(var.ALL_PLAYERS)}
|
||||
var.ROLES.clear()
|
||||
var.ROLES["person"] = UserSet(var.ALL_PLAYERS)
|
||||
reset_settings()
|
||||
cli.msg(chan, messages["default_reset"].format(botconfig.CMD_CHAR))
|
||||
var.PHASE = "join"
|
||||
return
|
||||
else:
|
||||
cli.msg(chan, messages["role_skipped"])
|
||||
var.ROLES[template] = set()
|
||||
var.ROLES[template] = UserSet()
|
||||
continue
|
||||
|
||||
var.ROLES[template] = set(users._get(x) for x in random.sample(possible, len(var.ROLES[template]))) # FIXME
|
||||
var.ROLES[template] = UserSet(users._get(x) for x in random.sample(possible, len(var.ROLES[template]))) # FIXME
|
||||
|
||||
# Handle gunner
|
||||
cannot_be_sharpshooter = get_players(var.TEMPLATE_RESTRICTIONS["sharpshooter"])
|
||||
@ -5550,8 +5551,7 @@ def start(cli, nick, chan, forced = False, restart = ""):
|
||||
else:
|
||||
var.GUNNERS[gunner.nick] = math.ceil(var.SHOTS_MULTIPLIER * len(pl))
|
||||
|
||||
var.ROLES["sharpshooter"] = set(var.ROLES["sharpshooter"])
|
||||
var.ROLES["sharpshooter"].discard(None)
|
||||
var.ROLES["sharpshooter"] = UserSet(p for p in var.ROLES["sharpshooter"] if p is not None)
|
||||
|
||||
with var.WARNING_LOCK: # cancel timers
|
||||
for name in ("join", "join_pinger", "start_votes"):
|
||||
@ -5564,7 +5564,7 @@ def start(cli, nick, chan, forced = False, restart = ""):
|
||||
var.LAST_VOTES = None
|
||||
|
||||
event = Event("role_assignment", {})
|
||||
event.dispatch(cli, var, var.CURRENT_GAMEMODE.name, pl[:], restart)
|
||||
event.dispatch(var, var.CURRENT_GAMEMODE.name, get_players())
|
||||
|
||||
if not restart:
|
||||
gamemode = var.CURRENT_GAMEMODE.name
|
||||
@ -5605,7 +5605,9 @@ def start(cli, nick, chan, forced = False, restart = ""):
|
||||
cli.msg(chan, messages["welcome"].format(", ".join(pl), gamemode, options))
|
||||
cli.mode(chan, "+m")
|
||||
|
||||
var.ORIGINAL_ROLES = copy.deepcopy(var.ROLES) # Make a copy
|
||||
var.ORIGINAL_ROLES.clear()
|
||||
for role, players in var.ROLES.items():
|
||||
var.ORIGINAL_ROLES[role] = players.copy()
|
||||
|
||||
# Handle amnesiac;
|
||||
# matchmaker is blacklisted if AMNESIAC_NIGHTS > 1 due to only being able to act night 1
|
||||
@ -6921,9 +6923,6 @@ if botconfig.DEBUG_MODE:
|
||||
except Exception as e:
|
||||
wrapper.send("{e.__class__.__name__}: {e}".format(e=e))
|
||||
|
||||
|
||||
if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
|
||||
|
||||
# DO NOT MAKE THIS A PMCOMMAND ALSO
|
||||
@cmd("force", flag="d")
|
||||
def force(cli, nick, chan, rest):
|
||||
@ -6987,7 +6986,7 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
|
||||
elif who == "gunner":
|
||||
tgt = {users._get(g) for g in var.GUNNERS.keys()} # FIXME
|
||||
else:
|
||||
tgt = var.ROLES[who].copy()
|
||||
tgt = set(var.ROLES[who])
|
||||
|
||||
comm = rst.pop(0).lower().replace(botconfig.CMD_CHAR, "", 1)
|
||||
if comm in COMMANDS and not COMMANDS[comm][0].owner_only:
|
||||
@ -7008,113 +7007,4 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
|
||||
else:
|
||||
cli.msg(chan, messages["command_not_found"])
|
||||
|
||||
|
||||
|
||||
@cmd("frole", flag="d", phases=("day", "night"))
|
||||
def frole(cli, nick, chan, rest):
|
||||
"""Change the role or template of a player."""
|
||||
rst = re.split(" +",rest)
|
||||
if len(rst) < 2:
|
||||
cli.msg(chan, messages["incorrect_syntax"])
|
||||
return
|
||||
who = rst.pop(0).strip()
|
||||
rol = " ".join(rst).strip()
|
||||
ul = list(var.USERS.keys())
|
||||
ull = [u.lower() for u in ul]
|
||||
if who.lower() not in ull:
|
||||
if not is_fake_nick(who):
|
||||
cli.msg(chan, messages["invalid_target"])
|
||||
return
|
||||
if not is_fake_nick(who):
|
||||
who = ul[ull.index(who.lower())]
|
||||
if who == users.Bot.nick or not who:
|
||||
cli.msg(chan, messages["invalid_target"])
|
||||
return
|
||||
pl = list_players()
|
||||
rolargs = re.split("\s*=\s*", rol, 1)
|
||||
rol = rolargs[0]
|
||||
if rol[0] in ("+", "-"):
|
||||
addrem = rol[0]
|
||||
rol = rol[1:]
|
||||
is_gunner = (rol == "gunner" or rol == "sharpshooter")
|
||||
if addrem == "+" and who not in get_roles(rol): # FIXME
|
||||
if is_gunner:
|
||||
if len(rolargs) == 2 and rolargs[1].isdigit():
|
||||
if len(rolargs[1]) < 7:
|
||||
var.GUNNERS[who] = int(rolargs[1])
|
||||
else:
|
||||
var.GUNNERS[who] = 999
|
||||
elif rol == "gunner":
|
||||
var.GUNNERS[who] = math.ceil(var.SHOTS_MULTIPLIER * len(pl))
|
||||
else:
|
||||
var.GUNNERS[who] = math.ceil(var.SHARPSHOOTER_MULTIPLIER * len(pl))
|
||||
if who not in pl:
|
||||
var.ROLES[var.DEFAULT_ROLE].add(users._get(who)) # FIXME
|
||||
var.MAIN_ROLES[users._get(who)] = var.DEFAULT_ROLE # FIXME
|
||||
var.ALL_PLAYERS.append(users._get(who)) # FIXME
|
||||
if not is_fake_nick(who):
|
||||
cli.mode(chan, "+v", who)
|
||||
cli.msg(chan, messages["template_default_role"].format(var.DEFAULT_ROLE))
|
||||
|
||||
var.ROLES[rol].add(users._get(who)) # FIXME
|
||||
evt = Event("frole_template", {})
|
||||
evt.dispatch(cli, var, addrem, who, rol, rolargs)
|
||||
elif addrem == "-" and who in get_roles(rol) and get_role(who) != rol:
|
||||
var.ROLES[rol].remove(users._get(who)) # FIXME
|
||||
evt = Event("frole_template", {})
|
||||
evt.dispatch(cli, var, addrem, who, rol, rolargs)
|
||||
if is_gunner and who in var.GUNNERS:
|
||||
del var.GUNNERS[who]
|
||||
else:
|
||||
cli.msg(chan, messages["improper_template_mod"])
|
||||
return
|
||||
elif rol in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
cli.msg(chan, messages["template_mod_syntax"].format(rol))
|
||||
return
|
||||
elif rol in var.ROLES:
|
||||
oldrole = None
|
||||
if who in pl:
|
||||
oldrole = get_role(who)
|
||||
change_role(users._get(who), oldrole, rol) # FIXME
|
||||
else:
|
||||
var.ALL_PLAYERS.append(users._get(who)) # FIXME
|
||||
var.ROLES[rol].add(users._get(who)) # FIXME
|
||||
var.MAIN_ROLES[users._get(who)] = rol # FIXME
|
||||
var.ORIGINAL_ROLES[rol].add(users._get(who)) # FIXME
|
||||
evt = Event("frole_role", {})
|
||||
evt.dispatch(cli, var, who, rol, oldrole, rolargs)
|
||||
if rol == "amnesiac":
|
||||
if len(rolargs) == 2 and rolargs[1] in var.ROLES:
|
||||
var.AMNESIAC_ROLES[who] = rolargs[1]
|
||||
else:
|
||||
# Pick amnesiac role like normal
|
||||
amnroles = var.ROLE_GUIDE.keys() - {var.DEFAULT_ROLE, "amnesiac", "clone", "traitor"}
|
||||
if var.AMNESIAC_NIGHTS > 1 and "matchmaker" in amnroles:
|
||||
amnroles.remove("matchmaker")
|
||||
for nope in var.AMNESIAC_BLACKLIST:
|
||||
amnroles.discard(nope)
|
||||
for nope in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
amnroles.discard(nope)
|
||||
var.AMNESIAC_ROLES[who] = random.choice(list(amnroles))
|
||||
if not is_fake_nick(who):
|
||||
cli.mode(chan, "+v", who)
|
||||
else:
|
||||
cli.msg(chan, messages["invalid_role"])
|
||||
return
|
||||
cli.msg(chan, messages["operation_successful"])
|
||||
# default stats determination does not work if we're mucking with !frole
|
||||
if var.STATS_TYPE == "default":
|
||||
var.ORIGINAL_SETTINGS["STATS_TYPE"] = var.STATS_TYPE
|
||||
var.STATS_TYPE = "accurate"
|
||||
|
||||
cli.msg(chan, messages["stats_accurate"].format(botconfig.CMD_CHAR))
|
||||
chk_win()
|
||||
|
||||
|
||||
if botconfig.ALLOWED_NORMAL_MODE_COMMANDS and not botconfig.DEBUG_MODE:
|
||||
for comd in list(COMMANDS.keys()):
|
||||
if (comd not in before_debug_mode_commands and
|
||||
comd not in botconfig.ALLOWED_NORMAL_MODE_COMMANDS):
|
||||
del COMMANDS[comd]
|
||||
|
||||
# vim: set sw=4 expandtab:
|
||||
|
Loading…
x
Reference in New Issue
Block a user