Split shaman
This has a number of related changes in other files due to needing to re-prioritize events and fixing other bits of code to play more nicely with split-off totems. Alpha wolf handling was moved after protection handling because it should've been that way to begin with, and as part of splitting off protection totem it needed to be split at the same time. Same with fallen angel changes with how protection now adjusts the killers lists.
This commit is contained in:
parent
8bd98b9e5c
commit
0287b05bcd
@ -479,7 +479,7 @@
|
||||
"silence_totem": "The player who is given this totem will be unable to use any special powers during the day tomorrow and the night after.",
|
||||
"desperation_totem": "If the player who is given this totem is lynched, the last player to vote them will also die.",
|
||||
"impatience_totem": "The player who is given this totem is counted as voting for everyone except themselves, even if they do not !vote.",
|
||||
"pacificsm_totem": "Votes by the player who is given this totem do not count.",
|
||||
"pacifism_totem": "Votes by the player who is given this totem do not count. Furthermore, they are always counted as abstaining, even if they do not !abstain.",
|
||||
"influence_totem": "Votes by the player who is given this totem count twice.",
|
||||
"exchange_totem": "The first person to use a power on the player given this totem tomorrow night will have their role swapped with the recipient.",
|
||||
"lycanthropy_totem": "If the player who is given this totem is targeted by wolves tomorrow night, they will become a wolf.",
|
||||
|
@ -23,6 +23,8 @@ class Event:
|
||||
self.params = SimpleNamespace(**kwargs)
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
self.stop_processing = False
|
||||
self.prevent_default = False
|
||||
for item in list(EVENT_CALLBACKS[self.name]):
|
||||
item[1](self, *args, **kwargs)
|
||||
if self.stop_processing:
|
||||
|
@ -33,40 +33,32 @@ def see(cli, nick, chan, rest):
|
||||
victimrole = get_role(victim)
|
||||
|
||||
if role != "augur":
|
||||
evt = Event("see", {"role": victimrole})
|
||||
evt.dispatch(cli, var, nick, victim)
|
||||
victimrole = evt.data["role"]
|
||||
else:
|
||||
evt = Event("investigate", {"role": victimrole})
|
||||
evt.dispatch(cli, var, nick, victim)
|
||||
victimrole = evt.data["role"]
|
||||
if victimrole == "amnesiac":
|
||||
victimrole = var.AMNESIAC_ROLES[victim]
|
||||
vrole = victimrole # keep a copy for logging
|
||||
|
||||
if role == "seer":
|
||||
if (victimrole in var.SEEN_WOLF and victimrole not in var.SEEN_DEFAULT) or victim in var.ROLES["cursed villager"]:
|
||||
victimrole = "wolf"
|
||||
elif victimrole in var.SEEN_DEFAULT:
|
||||
victimrole = var.DEFAULT_ROLE
|
||||
if var.DEFAULT_SEEN_AS_VILL:
|
||||
victimrole = "villager"
|
||||
# TODO: split off into shaman.py
|
||||
if (victim in var.DECEIVED) ^ (nick in var.DECEIVED):
|
||||
if victimrole == "wolf":
|
||||
victimrole = "villager"
|
||||
else:
|
||||
victimrole = "wolf"
|
||||
|
||||
evt = Event("see", {"role": victimrole})
|
||||
evt.dispatch(cli, var, nick, victim)
|
||||
victimrole = evt.data["role"]
|
||||
else:
|
||||
if victimrole == "amnesiac":
|
||||
victimrole = var.AMNESIAC_ROLES[victim]
|
||||
|
||||
evt = Event("investigate", {"role": victimrole})
|
||||
evt.dispatch(cli, var, nick, victim)
|
||||
victimrole = evt.data["role"]
|
||||
vrole = victimrole # keep a copy for logging
|
||||
|
||||
if role == "seer":
|
||||
pm(cli, nick, (messages["seer_success"]).format(victim, victimrole))
|
||||
debuglog("{0} ({1}) SEE: {2} ({3}) as {4}".format(nick, role, victim, vrole, victimrole))
|
||||
elif role == "oracle":
|
||||
iswolf = False
|
||||
if (victimrole in var.SEEN_WOLF and victimrole not in var.SEEN_DEFAULT) or victim in var.ROLES["cursed villager"]:
|
||||
if (victimrole in var.SEEN_WOLF and victimrole not in var.SEEN_DEFAULT):
|
||||
iswolf = True
|
||||
# deceit totem acts on both target and actor, so if both have them, they cancel each other out
|
||||
if (victim in var.DECEIVED) ^ (nick in var.DECEIVED):
|
||||
iswolf = not iswolf
|
||||
pm(cli, nick, (messages["oracle_success"]).format(victim, "" if iswolf else "\u0002not\u0002 ", "\u0002" if iswolf else ""))
|
||||
debuglog("{0} ({1}) SEE: {2} ({3}) (Wolf: {4})".format(nick, role, victim, vrole, str(iswolf)))
|
||||
elif role == "augur":
|
||||
|
612
src/roles/shaman.py
Normal file
612
src/roles/shaman.py
Normal file
@ -0,0 +1,612 @@
|
||||
import re
|
||||
import random
|
||||
import itertools
|
||||
from collections import defaultdict
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
from src.utilities import *
|
||||
from src import debuglog, errlog, plog
|
||||
from src.decorators import cmd, event_listener
|
||||
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]
|
||||
|
||||
@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"):
|
||||
"""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)
|
||||
totem = ""
|
||||
if role != "crazed shaman":
|
||||
totem = " of " + TOTEMS[nick]
|
||||
|
||||
tags = set()
|
||||
if role != "crazed shaman" and TOTEMS[nick] in var.BENEFICIAL_TOTEMS:
|
||||
tags.add("beneficial")
|
||||
|
||||
evt = Event("targeted_command", {"target": victim, "misdirection": True, "exchange": True},
|
||||
action="give a totem{0} to".format(totem))
|
||||
evt.dispatch(cli, var, "totem", nick, victim, frozenset(tags))
|
||||
if evt.prevent_default:
|
||||
return
|
||||
victim = evt.data["target"]
|
||||
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]))
|
||||
chk_nightdone(cli)
|
||||
|
||||
@event_listener("rename_player")
|
||||
def on_rename(evt, cli, 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, cli, 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, cli, var, nick, nickrole, nicktpls, death_triggers):
|
||||
for a,(b,c) in list(SHAMANS.items()):
|
||||
if nick in (a, b, c):
|
||||
del SHAMANS[a]
|
||||
|
||||
@event_listener("night_acted")
|
||||
def on_acted(evt, cli, var, nick, sender):
|
||||
if nick in SHAMANS:
|
||||
evt.data["acted"] = True
|
||||
|
||||
@event_listener("get_special")
|
||||
def on_get_special(evt, cli, var):
|
||||
evt.data["special"].update(list_players(("shaman",)))
|
||||
|
||||
@event_listener("exchange_roles")
|
||||
def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role):
|
||||
actor_totem = None
|
||||
nick_totem = None
|
||||
if actor_role in var.TOTEM_ORDER:
|
||||
actor_totem = TOTEMS.pop(actor)
|
||||
if actor in SHAMANS:
|
||||
del SHAMANS[actor]
|
||||
if actor in LASTGIVEN:
|
||||
del LASTGIVEN[actor]
|
||||
if nick_role in var.TOTEM_ORDER:
|
||||
nick_totem = TOTEMS.pop(nick)
|
||||
if nick in SHAMANS:
|
||||
del SHAMANS[nick]
|
||||
if nick in LASTGIVEN:
|
||||
del LASTGIVEN[nick]
|
||||
if nick_totem:
|
||||
if nick_role != "crazed shaman":
|
||||
evt.data["actor_messages"].append(messages["shaman_totem"].format(nick_totem))
|
||||
TOTEMS[actor] = nick_totem
|
||||
if actor_totem:
|
||||
if actor_role != "crazed shaman":
|
||||
evt.data["nick_messages"].append(messages["shaman_totem"].format(actor_totem))
|
||||
TOTEMS[nick] = actor_totem
|
||||
|
||||
@event_listener("chk_nightdone")
|
||||
def on_chk_nightdone(evt, cli, var):
|
||||
evt.data["actedcount"] += len(SHAMANS)
|
||||
evt.data["nightroles"].extend(list_players(var.TOTEM_ORDER))
|
||||
|
||||
@event_listener("get_voters")
|
||||
def on_get_voters(evt, cli, 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", priority=1.1)
|
||||
def on_hurry_up(evt, cli, var, force):
|
||||
if evt.params.timeout:
|
||||
evt.stop_propagation = True
|
||||
|
||||
@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))
|
||||
|
||||
@event_listener("chk_decision_lynch", priority=3)
|
||||
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"]
|
||||
if role == "amnesiac":
|
||||
var.ROLES["amnesiac"].remove(votee)
|
||||
role = var.AMNESIAC_ROLES[votee]
|
||||
var.ROLES[role].add(votee)
|
||||
var.AMNESIACS.add(votee)
|
||||
var.FINAL_ROLES[votee] = role
|
||||
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_propagation = 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]
|
||||
# TODO: instead of desperation_totem event to accomodate blessed villager, base on var.ACTIVE_PROTECTIONS
|
||||
# this means that prot totem, GA, and bodyguard would have a shot to block this too
|
||||
# and so that blessed villager doesn't double-dip (like if they were targeted that past night)
|
||||
desp_evt = Event("desperation_totem", {})
|
||||
if target != votee and desp_evt.dispatch(cli, var, votee, target) and target not in var.ROLES["blessed villager"]:
|
||||
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)
|
||||
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)
|
||||
evt.params.del_player(cli, target, True, end_game=False, killer_role="shaman", deadlist=evt.data["deadlist"], original=target, ismain=False)
|
||||
|
||||
@event_listener("player_win")
|
||||
def on_player_win(evt, cli, var, splr, rol, winner, survived):
|
||||
if rol == "crazed shaman" and survived and not winner.startswith("@") and singular(winner) not in var.WIN_STEALER_ROLES:
|
||||
evt.data["iwon"] = True
|
||||
|
||||
@event_listener("transition_day_begin", priority=4)
|
||||
def on_transition_day_begin(evt, cli, var):
|
||||
# Select random totem recipients if shamans didn't act
|
||||
pl = list_players()
|
||||
for shaman in list_players(var.TOTEM_ORDER):
|
||||
if shaman not in SHAMANS and shaman not in var.SILENCED:
|
||||
ps = pl[:]
|
||||
if LASTGIVEN.get(shaman) in ps:
|
||||
ps.remove(LASTGIVEN.get(shaman))
|
||||
# TODO: somehow split this off into succubus.py,
|
||||
# probably via a new event
|
||||
if shaman in var.ENTRANCED:
|
||||
for succubus in var.ROLES["succubus"]:
|
||||
if succubus in ps:
|
||||
ps.remove(succubus)
|
||||
if ps:
|
||||
target = random.choice(ps)
|
||||
totem.func(cli, shaman, shaman, target, messages["random_totem_prefix"])
|
||||
else:
|
||||
LASTGIVEN[shaman] = None
|
||||
elif shaman not in SHAMANS:
|
||||
LASTGIVEN[shaman] = None
|
||||
|
||||
@event_listener("transition_day_begin", priority=6)
|
||||
def on_transition_day_begin2(evt, cli, 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:
|
||||
pm(cli, shaman, 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.
|
||||
havetotem.clear()
|
||||
havetotem.extend(sorted(filter(None, LASTGIVEN.values())))
|
||||
|
||||
@event_listener("transition_day", priority=2)
|
||||
def on_transition_day2(evt, cli, var):
|
||||
for k, d in DEATH.items():
|
||||
evt.data["victims"].append(d)
|
||||
evt.data["onlybywolves"].discard(d)
|
||||
evt.data["killers"][d].append(k)
|
||||
|
||||
@event_listener("transition_day", priority=4.1)
|
||||
def on_transition_day3(evt, cli, 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 = list_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].append("totem")
|
||||
evt.data["numkills"][v] = numkills
|
||||
else:
|
||||
for i in range(0, numtotems):
|
||||
var.ACTIVE_PROTECTIONS[v].append("totem")
|
||||
|
||||
@event_listener("transition_day_resolve", priority=2)
|
||||
def on_transition_day_resolve2(evt, cli, var, victim):
|
||||
# TODO: remove these checks once everything is split
|
||||
# right now they're needed because otherwise protection may fire off even if the person isn't home
|
||||
# that will not be an issue once everything is using the event
|
||||
if victim in var.ROLES["harlot"] | var.ROLES["succubus"] and var.HVISITED.get(victim) and victim not in evt.data["dead"] and victim in evt.data["onlybywolves"]:
|
||||
return
|
||||
# END checks to remove
|
||||
|
||||
if evt.data["protected"].get(victim) == "totem":
|
||||
evt.data["message"].append(messages["totem_protection"].format(victim))
|
||||
evt.data["novictmsg"] = False
|
||||
evt.stop_propagation = True
|
||||
evt.prevent_default = True
|
||||
|
||||
@event_listener("transition_day_resolve", priority=6)
|
||||
def on_transition_day_resolve6(evt, cli, 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 victim in var.ROLES["harlot"] | var.ROLES["succubus"] and var.HVISITED.get(victim) and victim not in evt.data["dead"] and victim in evt.data["onlybywolves"]:
|
||||
return
|
||||
if evt.data["protected"].get(victim):
|
||||
return
|
||||
if victim in var.ROLES["lycan"] and victim in evt.data["onlybywolves"] and victim 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 == loser:
|
||||
killers.remove(loser)
|
||||
continue
|
||||
break
|
||||
if loser in evt.data["dead"] or victim == loser:
|
||||
loser = None
|
||||
ret_evt = Event("retribution_kill", {"target": loser, "message": []})
|
||||
ret_evt.dispatch(cli, var, victim, loser)
|
||||
loser = ret_evt.data["target"]
|
||||
evt.data["message"].extend(ret_evt.data["message"])
|
||||
if loser in evt.data["dead"] or victim == loser:
|
||||
loser = None
|
||||
# TODO: when blessed is split off, roll that check into retribution_kill
|
||||
if loser is not None and loser not in var.ROLES["blessed villager"]:
|
||||
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, cli, 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 ""))
|
||||
cli.msg(botconfig.CHANNEL, "\n".join(message))
|
||||
|
||||
@event_listener("transition_night_end", priority=2.01)
|
||||
def on_transition_night_end(evt, cli, var):
|
||||
max_totems = defaultdict(int)
|
||||
ps = list_players()
|
||||
shamans = list_players(var.TOTEM_ORDER)
|
||||
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()):
|
||||
if s not in shamans:
|
||||
del LASTGIVEN[s]
|
||||
for shaman in list_players(var.TOTEM_ORDER):
|
||||
pl = ps[:]
|
||||
random.shuffle(pl)
|
||||
if shaman in LASTGIVEN and LASTGIVEN[shaman] in pl:
|
||||
pl.remove(LASTGIVEN[shaman])
|
||||
role = get_role(shaman)
|
||||
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
|
||||
if shaman in var.PLAYERS and not is_user_simple(shaman):
|
||||
if role not in var.WOLFCHAT_ROLES:
|
||||
pm(cli, shaman, messages["shaman_notify"].format(role, "random " if shaman in var.ROLES["crazed shaman"] else ""))
|
||||
if role != "crazed shaman":
|
||||
totem = TOTEMS[shaman]
|
||||
tmsg = messages["shaman_totem"].format(totem)
|
||||
try:
|
||||
tmsg += messages[totem + "_totem"]
|
||||
except KeyError:
|
||||
tmsg += messages["generic_bug_totem"]
|
||||
pm(cli, shaman, tmsg)
|
||||
else:
|
||||
if role not in var.WOLFCHAT_ROLES:
|
||||
pm(cli, shaman, messages["shaman_simple"].format(role))
|
||||
if role != "crazed shaman":
|
||||
pm(cli, shaman, messages["totem_simple"].format(TOTEMS[shaman]))
|
||||
if role not in var.WOLFCHAT_ROLES:
|
||||
pm(cli, shaman, "Players: " + ", ".join(pl))
|
||||
|
||||
@event_listener("begin_day")
|
||||
def on_begin_day(evt, cli, var):
|
||||
# Apply totem effects that need to begin on day proper
|
||||
var.EXCHANGED.update(EXCHANGE)
|
||||
var.SILENCED.update(SILENCE)
|
||||
var.LYCANTHROPES.update(LYCANTHROPY)
|
||||
var.DISEASED.update(PESTILENCE)
|
||||
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("myrole")
|
||||
def on_myrole(evt, cli, var, nick):
|
||||
role = evt.data["role"]
|
||||
if role in var.TOTEM_ORDER and role != "crazed shaman" and var.PHASE == "night" and nick not in SHAMANS:
|
||||
evt.data["messages"].append(messages["totem_simple"].format(TOTEMS[nick]))
|
||||
|
||||
@event_listener("revealroles_role")
|
||||
def on_revealroles(evt, cli, var, 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("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
|
||||
|
||||
# vim: set sw=4 expandtab:
|
@ -151,8 +151,15 @@ def on_transition_day(evt, cli, var):
|
||||
evt.data["onlybywolves"].discard(d)
|
||||
evt.data["killers"][d].append(k)
|
||||
|
||||
@event_listener("transition_day", priority=5.01)
|
||||
def on_transition_day2(evt, cli, var):
|
||||
@event_listener("transition_day", priority=3.01)
|
||||
def on_transition_day3(evt, cli, var):
|
||||
for k, d in list(KILLS.items()):
|
||||
if GHOSTS[k] == "villagers":
|
||||
evt.data["killers"][d].remove(k)
|
||||
evt.data["killers"][d].insert(0, k)
|
||||
|
||||
@event_listener("transition_day", priority=6.01)
|
||||
def on_transition_day6(evt, cli, var):
|
||||
for k, d in list(KILLS.items()):
|
||||
if GHOSTS[k] == "villagers":
|
||||
evt.data["killers"][d].remove(k)
|
||||
|
@ -178,8 +178,13 @@ def on_transition_day(evt, cli, var):
|
||||
evt.data["bywolves"].discard(monster)
|
||||
evt.data["onlybywolves"].discard(monster)
|
||||
|
||||
@event_listener("transition_day", priority=5)
|
||||
def on_transition_day2(evt, cli, var):
|
||||
@event_listener("transition_day", priority=3)
|
||||
def on_transition_day3(evt, cli, var):
|
||||
evt.data["numkills"] = {v: evt.data["victims"].count(v) for v in set(evt.data["victims"])}
|
||||
on_transition_day6(evt, cli, var)
|
||||
|
||||
@event_listener("transition_day", priority=6)
|
||||
def on_transition_day6(evt, cli, var):
|
||||
wolfteam = list_players(var.WOLFTEAM_ROLES)
|
||||
for victim, killers in list(evt.data["killers"].items()):
|
||||
k2 = []
|
||||
|
832
src/wolfgame.py
832
src/wolfgame.py
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user