banned/src/roles/angel.py
Em Barry a2be87b85a Convert succubus (#311)
Convert succubus to new API along with (un)related fixes.
2018-01-10 07:51:45 -07:00

303 lines
13 KiB
Python

import re
import random
import itertools
import math
from collections import defaultdict
import botconfig
import src.settings as var
from src.utilities import *
from src import users, channels, debuglog, errlog, plog
from src.functions import get_players, get_all_players
from src.decorators import cmd, event_listener
from src.messages import messages
from src.events import Event
GUARDED = {} # type: Dict[str, str]
LASTGUARDED = {} # type: Dict[str, str]
PASSED = set() # type: Set[str]
@cmd("guard", "protect", "save", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("bodyguard", "guardian angel"))
def guard(cli, nick, chan, rest):
"""Guard a player, preventing them from being killed that night."""
if nick in GUARDED:
pm(cli, nick, messages["already_protecting"])
return
role = get_role(nick)
self_in_list = role == "guardian angel" and var.GUARDIAN_ANGEL_CAN_GUARD_SELF
victim = get_victim(cli, nick, re.split(" +",rest)[0], False, self_in_list)
if not victim:
return
if (role == "bodyguard" or not var.GUARDIAN_ANGEL_CAN_GUARD_SELF) and victim == nick:
pm(cli, nick, messages["cannot_guard_self"])
return
if role == "guardian angel" and LASTGUARDED.get(nick) == victim:
pm(cli, nick, messages["guardian_target_another"].format(victim))
return
angel = users._get(nick) # FIXME
target = users._get(victim) # FIXME
# self-guard ignores luck/misdirection/exchange totem
evt = Event("targeted_command", {"target": target, "misdirection": (angel is not target), "exchange": (angel is not target)})
if not evt.dispatch(var, "guard", angel, target, frozenset({"beneficial"})):
return
victim = evt.data["target"].nick
GUARDED[nick] = victim
LASTGUARDED[nick] = victim
if victim == nick:
pm(cli, nick, messages["guardian_guard_self"])
else:
pm(cli, nick, messages["protecting_target"].format(GUARDED[nick]))
pm(cli, victim, messages["target_protected"])
debuglog("{0} ({1}) GUARD: {2} ({3})".format(nick, role, victim, get_role(victim)))
@cmd("pass", chan=False, pm=True, playing=True, phases=("night",), roles=("bodyguard", "guardian angel"))
def pass_cmd(cli, nick, chan, rest):
"""Decline to use your special power for that night."""
if nick in GUARDED:
pm(cli, nick, messages["already_protecting"])
return
PASSED.add(nick)
pm(cli, nick, messages["guardian_no_protect"])
debuglog("{0} ({1}) PASS".format(nick, get_role(nick)))
@event_listener("rename_player")
def on_rename(evt, cli, var, prefix, nick):
for dictvar in (GUARDED, LASTGUARDED):
kvp = {}
for a,b in dictvar.items():
if a == prefix:
if b == prefix:
kvp[nick] = nick
else:
kvp[nick] = b
elif b == prefix:
kvp[a] = nick
dictvar.update(kvp)
if prefix in dictvar:
del dictvar[prefix]
if prefix in PASSED:
PASSED.discard(prefix)
PASSED.add(nick)
@event_listener("del_player")
def on_del_player(evt, var, user, mainrole, allroles, death_triggers):
if var.PHASE == "night" and user.nick in GUARDED:
pm(user.client, GUARDED[user.nick], messages["protector_disappeared"])
for dictvar in (GUARDED, LASTGUARDED):
for k,v in list(dictvar.items()):
if user.nick in (k, v):
del dictvar[k]
PASSED.discard(user.nick)
@event_listener("night_acted")
def on_acted(evt, var, user, actor):
if user.nick in GUARDED:
evt.data["acted"] = True
@event_listener("get_special")
def on_get_special(evt, var):
evt.data["special"].update(get_players(("guardian angel", "bodyguard")))
@event_listener("exchange_roles")
def on_exchange(evt, var, actor, target, actor_role, target_role):
if actor_role in ("bodyguard", "guardian angel"):
if actor.nick in GUARDED:
guarded = users._get(GUARDED.pop(actor.nick)) # FIXME
guarded.send(messages["protector disappeared"])
if actor.nick in LASTGUARDED:
del LASTGUARDED[actor.nick]
if target_role in ("bodyguard", "guardian angel"):
if target.nick in GUARDED:
guarded = users._get(GUARDED.pop(target.nick)) # FIXME
guarded.send(messages["protector disappeared"])
if target.nick in LASTGUARDED:
del LASTGUARDED[target.nick]
@event_listener("chk_nightdone")
def on_chk_nightdone(evt, var):
evt.data["actedcount"] += len(GUARDED) + len(PASSED)
evt.data["nightroles"].extend(get_players(("guardian angel", "bodyguard")))
@event_listener("transition_day", priority=4.2)
def on_transition_day(evt, var):
pl = get_players()
vs = set(evt.data["victims"])
for v in pl:
if v in vs:
if v in var.DYING:
continue
for g in get_all_players(("guardian angel",)):
if GUARDED.get(g.nick) == v.nick:
evt.data["numkills"][v] -= 1
if evt.data["numkills"][v] >= 0:
evt.data["killers"][v].pop(0)
if evt.data["numkills"][v] <= 0 and v not in evt.data["protected"]:
evt.data["protected"][v] = "angel"
elif evt.data["numkills"][v] <= 0:
var.ACTIVE_PROTECTIONS[v.nick].append("angel")
for g in get_all_players(("bodyguard",)):
if GUARDED.get(g.nick) == v.nick:
evt.data["numkills"][v] -= 1
if evt.data["numkills"][v] >= 0:
evt.data["killers"][v].pop(0)
if evt.data["numkills"][v] <= 0 and v not in evt.data["protected"]:
evt.data["protected"][v] = "bodyguard"
elif evt.data["numkills"][v] <= 0:
var.ACTIVE_PROTECTIONS[v.nick].append("bodyguard")
else:
for g in var.ROLES["guardian angel"]:
if GUARDED.get(g.nick) == v.nick:
var.ACTIVE_PROTECTIONS[v.nick].append("angel")
for g in var.ROLES["bodyguard"]:
if GUARDED.get(g.nick) == v.nick:
var.ACTIVE_PROTECTIONS[v.nick].append("bodyguard")
@event_listener("fallen_angel_guard_break")
def on_fagb(evt, var, user, killer):
for g in get_all_players(("guardian angel",)):
if GUARDED.get(g.nick) == user.nick:
if random.random() < var.FALLEN_ANGEL_KILLS_GUARDIAN_ANGEL_CHANCE:
if g in evt.data["protected"]:
del evt.data["protected"][g]
evt.data["bywolves"].add(g)
if g not in evt.data["victims"]:
evt.data["onlybywolves"].add(g)
evt.data["victims"].append(g)
evt.data["killers"][g].append(killer)
if g is not user:
g.send(messages["fallen_angel_success"].format(user))
for g in get_all_players(("bodyguard",)):
if GUARDED.get(g.nick) == user.nick:
if g in evt.data["protected"]:
del evt.data["protected"][g]
evt.data["bywolves"].add(g)
if g not in evt.data["victims"]:
evt.data["onlybywolves"].add(g)
evt.data["victims"].append(g)
evt.data["killers"][g].append(killer)
if g is not user:
g.send(messages["fallen_angel_success"].format(user))
@event_listener("transition_day_resolve", priority=2)
def on_transition_day_resolve(evt, var, victim):
if evt.data["protected"].get(victim) == "angel":
evt.data["message"].append(messages["angel_protection"].format(victim))
evt.data["novictmsg"] = False
evt.stop_processing = True
evt.prevent_default = True
elif evt.data["protected"].get(victim) == "bodyguard":
for bodyguard in get_all_players(("bodyguard",)):
if GUARDED.get(bodyguard.nick) == victim.nick:
evt.data["dead"].append(bodyguard)
evt.data["message"].append(messages["bodyguard_protection"].format(bodyguard))
evt.data["novictmsg"] = False
evt.stop_processing = True
evt.prevent_default = True
break
@event_listener("transition_day_resolve_end")
def on_transition_day_resolve_end(evt, var, victims):
for bodyguard in get_all_players(("bodyguard",)):
if GUARDED.get(bodyguard.nick) in list_players(var.WOLF_ROLES) and bodyguard not in evt.data["dead"] and bodyguard not in evt.data["bitten"]:
r = random.random()
if r < var.BODYGUARD_DIES_CHANCE:
evt.data["bywolves"].add(bodyguard)
evt.data["onlybywolves"].add(bodyguard)
if var.ROLE_REVEAL == "on":
evt.data["message"].append(messages["bodyguard_protected_wolf"].format(bodyguard))
else: # off and team
evt.data["message"].append(messages["bodyguard_protection"].format(bodyguard))
evt.data["dead"].append(bodyguard)
for gangel in get_all_players(("guardian angel",)):
if GUARDED.get(gangel.nick) in list_players(var.WOLF_ROLES) and gangel not in evt.data["dead"] and gangel not in evt.data["bitten"]:
r = random.random()
if r < var.GUARDIAN_ANGEL_DIES_CHANCE:
evt.data["bywolves"].add(gangel)
evt.data["onlybywolves"].add(gangel)
if var.ROLE_REVEAL == "on":
evt.data["message"].append(messages["guardian_angel_protected_wolf"].format(gangel))
else: # off and team
evt.data["message"].append(messages["guardian_angel_protected_wolf_no_reveal"].format(gangel))
evt.data["dead"].append(gangel)
@event_listener("transition_night_begin")
def on_transition_night_begin(evt, cli, 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()
@event_listener("transition_night_end", priority=2)
def on_transition_night_end(evt, var):
# the messages for angel and guardian angel are different enough to merit individual loops
ps = get_players()
for bg in get_all_players(("bodyguard",)):
pl = ps[:]
random.shuffle(pl)
pl.remove(bg)
chance = math.floor(var.BODYGUARD_DIES_CHANCE * 100)
warning = ""
if chance > 0:
warning = messages["bodyguard_death_chance"].format(chance)
to_send = "bodyguard_notify"
if bg.prefers_simple():
to_send = "bodyguard_simple"
bg.send(messages[to_send].format(warning), "Players: " + ", ".join(p.nick for p in pl), sep="\n")
for gangel in get_all_players(("guardian angel",)):
pl = ps[:]
random.shuffle(pl)
gself = messages["guardian_self_notification"]
if not var.GUARDIAN_ANGEL_CAN_GUARD_SELF:
pl.remove(gangel)
gself = ""
if gangel.nick in LASTGUARDED:
user = users._get(LASTGUARDED[gangel.nick]) # FIXME
if user in pl:
pl.remove(user)
chance = math.floor(var.GUARDIAN_ANGEL_DIES_CHANCE * 100)
warning = ""
if chance > 0:
warning = messages["bodyguard_death_chance"].format(chance)
to_send = "guardian_notify"
if gangel.prefers_simple():
to_send = "guardian_simple"
gangel.send(messages[to_send].format(warning, gself), "Players: " + ", ".join(p.nick for p in pl), sep="\n")
@event_listener("assassinate")
def on_assassinate(evt, var, killer, target, prot):
if prot == "angel" and var.GAMEPHASE == "night":
var.ACTIVE_PROTECTIONS[target.nick].remove("angel")
evt.prevent_default = True
evt.stop_processing = True
channels.Main.send(messages[evt.params.message_prefix + "angel"].format(killer, target))
elif prot == "bodyguard":
var.ACTIVE_PROTECTIONS[target.nick].remove("bodyguard")
evt.prevent_default = True
evt.stop_processing = True
for bg in var.ROLES["bodyguard"]:
if GUARDED.get(bg.nick) == target.nick:
channels.Main.send(messages[evt.params.message_prefix + "bodyguard"].format(killer, target, bg))
# redirect the assassination to the bodyguard
evt.data["target"] = bg
break
@event_listener("begin_day")
def on_begin_day(evt, var):
PASSED.clear()
# clear out LASTGUARDED for people that didn't guard last night
for g in list(LASTGUARDED.keys()):
if g not in GUARDED:
del LASTGUARDED[g]
@event_listener("reset")
def on_reset(evt, var):
GUARDED.clear()
LASTGUARDED.clear()
PASSED.clear()
# vim: set sw=4 expandtab: