Split harlot

This commit is contained in:
skizzerz 2017-03-08 20:13:45 -06:00
parent e74079dae1
commit 63efb85bbb
7 changed files with 208 additions and 132 deletions

View File

@ -363,6 +363,7 @@
"investigator_reveal": "Someone accidentally drops a paper. The paper reveals that \u0002{0}\u0002 is the detective!",
"harlot_already_visited": "You are already spending the night with \u0002{0}\u0002.",
"harlot_success": "You are spending the night with \u0002{0}\u0002. Have a good time!",
"harlot_not_self": "You may not visit yourself. Use \"pass\" to choose to not visit anyone tonight.",
"seer_fail": "You may only have one vision per round.",
"no_see_self": "Seeing yourself would be a waste.",
"seer_success": "You have a vision; in this vision, you see that \u0002{0}\u0002 is a \u0002{1}\u0002!",

View File

@ -178,13 +178,6 @@ def on_fagb(evt, cli, var, victim, killer):
@event_listener("transition_day_resolve", priority=2)
def on_transition_day_resolve(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"] 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) == "angel":
evt.data["message"].append(messages["angel_protection"].format(victim))
evt.data["novictmsg"] = False

View File

@ -37,13 +37,6 @@ def on_transition_day(evt, cli, var):
@event_listener("transition_day_resolve", priority=2)
def on_transition_day_resolve(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"] 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) == "blessing":
# don't play any special message for a blessed target, this means in a game with priest and monster it's not really possible
# for wolves to tell which is which. May want to change that in the future to be more obvious to wolves since there's not really

165
src/roles/harlot.py Normal file
View File

@ -0,0 +1,165 @@
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 channels, users, debuglog, errlog, plog
from src.decorators import cmd, event_listener
from src.messages import messages
from src.events import Event
VISITED = {}
@cmd("visit", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("harlot",))
def hvisit(cli, nick, chan, rest):
"""Visit a player. You will die if you visit a wolf or a target of the wolves."""
if VISITED.get(nick):
pm(cli, nick, messages["harlot_already_visited"].format(VISITED[nick]))
return
victim = get_victim(cli, nick, re.split(" +",rest)[0], False, True)
if not victim:
return
if nick == victim:
pm(cli, nick, messages["harlot_not_self"])
return
evt = Event("targeted_command", {"target": victim, "misdirection": True, "exchange": True})
evt.dispatch(cli, var, "visit", nick, victim, frozenset({"immediate"}))
if evt.prevent_default:
return
victim = evt.data["target"]
vrole = get_role(victim)
VISITED[nick] = victim
pm(cli, nick, messages["harlot_success"].format(victim))
if nick != victim:
pm(cli, victim, messages["harlot_success"].format(nick))
revt = Event("harlot_visit", {})
revt.dispatch(cli, var, nick, victim)
debuglog("{0} ({1}) VISIT: {2} ({3})".format(nick, get_role(nick), victim, vrole))
chk_nightdone(cli)
@cmd("pass", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("harlot",))
def pass_cmd(cli, nick, chan, rest):
"""Do not visit someone tonight."""
if VISITED.get(nick):
pm(cli, nick, messages["harlot_already_visited"].format(VISITED[nick]))
return
VISITED[nick] = None
pm(cli, nick, messages["no_visit"])
debuglog("{0} ({1}) PASS".format(nick, get_role(nick)))
chk_nightdone(cli)
@event_listener("bite")
def on_bite(evt, cli, var, alpha, target):
if target not in var.ROLES["harlot"]:
return
hvisit = VISITED.get(target)
if hvisit and get_role(hvisit) not in var.WOLFCHAT_ROLES and (hvisit not in evt.param.bywolves or hvisit in evt.param.protected):
evt.data["can_bite"] = False
@event_listener("transition_day_resolve", priority=1)
def on_transition_day_resolve(evt, cli, var, victim):
if victim in var.ROLES["harlot"] and VISITED.get(victim) and victim not in evt.data["dead"] and victim in evt.data["onlybywolves"]:
if victim not in evt.data["bitten"]:
evt.data["message"].append(messages["target_not_home"])
evt.data["novictmsg"] = False
evt.stop_processing = True
evt.prevent_default = True
@event_listener("transition_day_resolve_end", priority=1)
def on_transition_day_resolve_end(evt, cli, var, victims):
for victim in victims + evt.data["bitten"]:
if victim in evt.data["dead"] and victim in VISITED.values() and (victim in evt.data["bywolves"] or victim in evt.data["bitten"]):
for hlt in VISITED:
if VISITED[hlt] == victim and hlt not in evt.data["bitten"] and hlt not in evt.data["dead"]:
if var.ROLE_REVEAL in ("on", "team"):
evt.data["message"].append(messages["visited_victim"].format(hlt, get_reveal_role(hlt)))
else:
evt.data["message"].append(messages["visited_victim_noreveal"].format(hlt))
evt.data["bywolves"].add(hlt)
evt.data["onlybywolves"].add(hlt)
evt.data["dead"].append(hlt)
@event_listener("transition_day_resolve_end", priority=3)
def on_transition_day_resolve_end3(evt, cli, var, victims):
for harlot in var.ROLES["harlot"]:
if VISITED.get(harlot) in list_players(var.WOLF_ROLES) and harlot not in evt.data["dead"] and harlot not in evt.data["bitten"]:
evt.data["message"].append(messages["harlot_visited_wolf"].format(harlot))
evt.data["bywolves"].add(harlot)
evt.data["onlybywolves"].add(harlot)
evt.data["dead"].append(harlot)
@event_listener("night_acted")
def on_night_acted(evt, cli, var, nick, sender):
if VISITED.get(nick):
evt.data["acted"] = True
@event_listener("chk_nightdone")
def on_chk_nightdone(evt, cli, var):
evt.data["actedcount"] += len(VISITED)
evt.data["nightroles"].extend(var.ROLES["harlot"])
@event_listener("exchange_roles")
def on_exchange_roles(evt, cli, var, actor, nick, actor_role, nick_role):
if actor_role == "harlot":
if actor in VISITED:
if VISITED[actor] is not None:
pm(cli, VISITED[actor], messages["harlot_disappeared"].format(actor))
del VISITED[actor]
if nick_role == "harlot":
if nick in VISITED:
if VISITED[nick] is not None:
pm(cli, VISITED[nick], messages["harlot_disappeared"].format(nick))
del VISITED[nick]
@event_listener("transition_night_end", priority=2)
def on_transition_night_end(evt, cli, var):
for harlot in var.ROLES["harlot"]:
pl = list_players()
random.shuffle(pl)
pl.remove(harlot)
if harlot in var.PLAYERS and not is_user_simple(harlot):
pm(cli, harlot, messages["harlot_info"])
else:
pm(cli, harlot, messages["harlot_simple"])
pm(cli, harlot, "Players: " + ", ".join(pl))
@event_listener("begin_day")
def on_begin_day(evt, cli, var):
VISITED.clear()
@event_listener("get_special")
def on_get_special(evt, cli, var):
evt.data["special"].update(var.ROLES["harlot"])
@event_listener("del_player")
def on_del_player(evt, cli, var, nick, nickrole, nicktpls, death_triggers):
if nickrole != "harlot":
return
if nick in VISITED:
del VISITED[nick]
@event_listener("rename_player")
def on_rename(evt, cli, var, prefix, nick):
kvp = {}
for a,b in VISITED.items():
s = nick if a == prefix else a
t = nick if b == prefix else b
kvp[s] = t
VISITED.update(kvp)
if prefix in VISITED:
del VISITED[prefix]
@event_listener("reset")
def on_reset(evt, var):
VISITED.clear()
# vim: set sw=4 expandtab:

View File

@ -434,13 +434,6 @@ def on_fagb(evt, cli, var, victim, killer):
@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"] 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
@ -452,8 +445,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"] 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:

View File

@ -71,17 +71,24 @@ def hvisit(cli, nick, chan, rest):
debuglog("{0} ({1}) VISIT: {2} ({3})".format(nick, get_role(nick), victim, vrole))
chk_nightdone(cli)
@cmd("pass", chan=False, pm=True, playing=True, phases=("night",), roles=("succubus",))
@cmd("pass", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("succubus",))
def pass_cmd(cli, nick, chan, rest):
"""Do not entrance someone tonight."""
if VISITED.get(nick):
pm(cli, nick, messages["succubus_already_visited"].format(VISITED[nick]))
return
VISITED[nick] = None
VISITED[nick] = None
pm(cli, nick, messages["succubus_pass"])
debuglog("{0} ({1}) PASS".format(nick, get_role(nick)))
chk_nightdone(cli)
@event_listener("harlot_visit")
def on_harlot_visit(evt, cli, var, nick, victim):
if get_role(victim) == "succubus":
pm(cli, nick, messages["notify_succubus_target"].format(victim))
pm(cli, victim, messages["succubus_harlot_success"].format(nick))
ENTRANCED.add(nick)
@event_listener("get_random_totem_targets")
def on_get_random_totem_targets(evt, cli, var, shaman):
if shaman in ENTRANCED:
@ -150,6 +157,12 @@ def on_chk_win(evt, cli, var, rolemap, lpl, lwolves, lrealwolves):
evt.data["winner"] = "succubi"
evt.data["message"] = messages["succubus_win"].format(plural("succubus", lsuccubi), plural("has", lsuccubi), plural("master's", lsuccubi))
@event_listener("can_exchange")
def on_can_exchange(evt, var, actor, nick):
if actor in var.ROLES["succubus"] or nick in var.ROLES["succubus"]:
evt.prevent_default = True
evt.stop_processing = True
@event_listener("del_player")
def on_del_player(evt, cli, var, nick, nickrole, nicktpls, death_triggers):
global ALL_SUCC_IDLE

View File

@ -3338,7 +3338,6 @@ def begin_day(cli):
var.KILLER = "" # nickname of who chose the victim
var.HEXED = set() # set of hags that have silenced others
var.OBSERVED = {} # those whom werecrows/sorcerers have observed
var.HVISITED = {} # those whom harlots have visited
var.PASSED = set() # set of certain roles that have opted not to act
var.STARTED_DAY_PLAYERS = len(list_players())
var.SILENCED = copy.copy(var.TOBESILENCED)
@ -3555,18 +3554,19 @@ def transition_day(cli, gameid=0):
# note that we cannot bite visiting harlots unless they are visiting a wolf,
# and lycans/immunized people turn/die instead of being bitten, so keep the kills valid on those
got_bit = False
hvisit = var.HVISITED.get(target)
bite_evt = Event("bite", {
"can_bite": True,
"kill": target in var.ROLES["lycan"] or target in var.LYCANTHROPES or target in var.IMMUNIZED
})
},
victims=victims,
killers=killers,
bywolves=bywolves,
onlybywolves=onlybywolves,
protected=protected,
bitten=bitten,
numkills=numkills)
bite_evt.dispatch(cli, var, alpha, target)
if ((target not in var.ROLES["harlot"]
or not hvisit
or get_role(hvisit) in var.WOLFCHAT_ROLES
or (hvisit in bywolves and hvisit not in protected))
and bite_evt.data["can_bite"]
and not bite_evt.data["kill"]):
if bite_evt.data["can_bite"] and not bite_evt.data["kill"]:
# mark them as bitten
got_bit = True
# if they were also being killed by wolves, undo that
@ -3617,13 +3617,14 @@ def transition_day(cli, gameid=0):
# that assumes they die en route to the wolves (and thus don't shoot/give out gun/etc.)
# TODO: this needs to be split off into angel.py, but all the stuff above it needs to be split off first
# so even though angel.py exists we can't exactly do this now
from src.roles import angel
# TODO: also needs to be split off into harlot.py
from src.roles import angel, harlot
for v in victims_set:
if v in var.DYING:
victims.append(v)
elif v in var.ROLES["bodyguard"] and angel.GUARDED.get(v) in victims_set:
vappend.append(v)
elif v in var.ROLES["harlot"] and var.HVISITED.get(v) in victims_set:
elif v in var.ROLES["harlot"] and harlot.VISITED.get(v) in victims_set:
vappend.append(v)
else:
victims.append(v)
@ -3641,7 +3642,7 @@ def transition_day(cli, gameid=0):
if v in var.ROLES["bodyguard"] and angel.GUARDED.get(v) not in vappend:
vappend.remove(v)
victims.append(v)
elif v in var.ROLES["harlot"] and var.HVISITED.get(v) not in vappend:
elif v in var.ROLES["harlot"] and harlot.VISITED.get(v) not in vappend:
vappend.remove(v)
victims.append(v)
@ -3694,13 +3695,7 @@ def transition_day(cli, gameid=0):
for victim in vlist:
if not revt.dispatch(cli, var, victim):
continue
if victim in var.ROLES["harlot"] and var.HVISITED.get(victim) and victim not in revt.data["dead"] and victim in revt.data["onlybywolves"]:
# alpha wolf can bite a harlot visiting another wolf, don't play a message in that case
# kept as a nested if so that the other victim logic does not run
if victim not in revt.data["bitten"]:
revt.data["message"].append(messages["target_not_home"])
revt.data["novictmsg"] = False
elif (victim in var.ROLES["lycan"] or victim in var.LYCANTHROPES) and victim in revt.data["onlybywolves"] and victim not in var.IMMUNIZED:
if (victim in var.ROLES["lycan"] or victim in var.LYCANTHROPES) and victim in revt.data["onlybywolves"] and victim not in var.IMMUNIZED:
vrole = get_role(victim)
if vrole not in var.WOLFCHAT_ROLES:
revt.data["message"].append(messages["new_wolf"])
@ -3785,28 +3780,6 @@ def transition_day(cli, gameid=0):
killers = revt2.data["killers"]
protected = revt2.data["protected"]
bitten = revt2.data["bitten"]
# handle separately so it always happens no matter how victim dies, and so that we can account for bitten victims as well
for victim in victims + bitten:
if victim in dead and victim in var.HVISITED.values() and (victim in bywolves or victim in bitten): # victim was visited by some harlot and victim was attacked by wolves
for hlt in var.HVISITED.keys():
if var.HVISITED[hlt] == victim and hlt not in bitten and hlt not in dead:
if var.ROLE_REVEAL in ("on", "team"):
message.append(messages["visited_victim"].format(hlt, get_role(hlt)))
else:
message.append(messages["visited_victim_noreveal"].format(hlt))
bywolves.add(hlt)
onlybywolves.add(hlt)
dead.append(hlt)
if novictmsg and len(dead) == 0:
message.append(random.choice(messages["no_victims"]) + messages["no_victims_append"])
for harlot in var.ROLES["harlot"]:
if var.HVISITED.get(harlot) in list_players(var.WOLF_ROLES) and harlot not in dead and harlot not in bitten:
message.append(messages["harlot_visited_wolf"].format(harlot))
bywolves.add(harlot)
onlybywolves.add(harlot)
dead.append(harlot)
for victim in list(dead):
if victim in var.GUNNERS.keys() and var.GUNNERS[victim] > 0 and victim in bywolves:
@ -3911,6 +3884,11 @@ def transition_day(cli, gameid=0):
event_end.data["begin_day"](cli)
@event_listener("transition_day_resolve_end", priority=2)
def on_transition_day_resolve_end(evt, cli, var, victims):
if evt.data["novictmsg"] and len(evt.data["dead"]) == 0:
evt.data["message"].append(random.choice(messages["no_victims"]) + messages["no_victims_append"])
@proxy.impl
def chk_nightdone(cli):
if var.PHASE != "night":
@ -3918,10 +3896,10 @@ def chk_nightdone(cli):
pl = list_players()
spl = set(pl)
actedcount = sum(map(len, (var.HVISITED, var.PASSED, var.OBSERVED,
actedcount = sum(map(len, (var.PASSED, var.OBSERVED,
var.HEXED, var.CURSED, var.CHARMERS)))
nightroles = get_roles("harlot", "sorcerer", "hag", "warlock", "werecrow", "piper", "prophet")
nightroles = get_roles("sorcerer", "hag", "warlock", "werecrow", "piper", "prophet")
for nick, info in var.PRAYED.items():
if info[0] > 0:
@ -4119,8 +4097,9 @@ def check_exchange(cli, actor, nick):
#some roles can act on themselves, ignore this
if actor == nick:
return False
if nick in var.ROLES["succubus"]:
return False # succubus cannot be affected by exchange totem, at least for now
event = Event("can_exchange", {})
if not event.dispatch(var, actor, nick):
return False # some roles such as succubus cannot be affected by exchange totem
if nick in var.EXCHANGED:
var.EXCHANGED.remove(nick)
actor_role = get_role(actor)
@ -4143,11 +4122,6 @@ def check_exchange(cli, actor, nick):
elif actor_role in ("werecrow", "sorcerer"):
if actor in var.OBSERVED:
del var.OBSERVED[actor]
elif actor_role == "harlot":
if actor in var.HVISITED:
if var.HVISITED[actor] is not None:
pm(cli, var.HVISITED[actor], messages["harlot_disappeared"].format(actor))
del var.HVISITED[actor]
elif actor_role == "hag":
if actor in var.LASTHEXED:
if var.LASTHEXED[actor] in var.TOBESILENCED and actor in var.HEXED:
@ -4183,11 +4157,6 @@ def check_exchange(cli, actor, nick):
elif nick_role in ("werecrow", "sorcerer"):
if nick in var.OBSERVED:
del var.OBSERVED[nick]
elif nick_role == "harlot":
if nick in var.HVISITED:
if var.HVISITED[nick] is not None:
pm(cli, var.HVISITED[nick], messages["harlot_disappeared"].format(nick))
del var.HVISITED[nick]
elif nick_role == "hag":
if nick in var.LASTHEXED:
if var.LASTHEXED[nick] in var.TOBESILENCED and nick in var.HEXED:
@ -4645,39 +4614,6 @@ def pray(cli, nick, chan, rest):
chk_nightdone(cli)
@cmd("visit", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("harlot",))
def hvisit(cli, nick, chan, rest):
"""Visit a player. You will die if you visit a wolf or a target of the wolves."""
role = get_role(nick)
if var.HVISITED.get(nick):
pm(cli, nick, messages["harlot_already_visited"].format(var.HVISITED[nick]))
return
victim = get_victim(cli, nick, re.split(" +",rest)[0], False, True)
if not victim:
return
if nick == victim: # Staying home (same as calling pass, so call pass)
pass_cmd.func(cli, nick, chan, "") # XXX: Old API
return
else:
victim = choose_target(nick, victim)
if check_exchange(cli, nick, victim):
return
var.HVISITED[nick] = victim
pm(cli, nick, messages["harlot_success"].format(victim))
if nick != victim: #prevent luck/misdirection totem weirdness
pm(cli, victim, messages["harlot_success"].format(nick))
# TODO: turn this into an event once harlot is split off
if get_role(victim) == "succubus":
pm(cli, nick, messages["notify_succubus_target"].format(victim))
pm(cli, victim, messages["succubus_harlot_success"].format(nick))
from src.roles import succubus
succubus.ENTRANCED.add(nick)
debuglog("{0} ({1}) VISIT: {2} ({3})".format(nick, role, victim, get_role(victim)))
chk_nightdone(cli)
@cmd("give", chan=False, pm=True, playing=True, silenced=True, phases=("day",), roles=("doctor",))
@cmd("immunize", "immunise", chan=False, pm=True, playing=True, silenced=True, phases=("day",), roles=("doctor",))
def immunize(cli, nick, chan, rest):
@ -4752,8 +4688,8 @@ def bite_cmd(cli, nick, chan, rest):
chk_nightdone(cli)
@cmd("pass", chan=False, pm=True, playing=True, phases=("night",),
roles=("harlot", "turncoat", "warlock"))
def pass_cmd(cli, nick, chan, rest): # XXX: hvisit (3 functions above this one) also needs updating alongside this
roles=("turncoat", "warlock"))
def pass_cmd(cli, nick, chan, rest):
"""Decline to use your special power for that night."""
nickrole = get_role(nick)
@ -4765,13 +4701,7 @@ def pass_cmd(cli, nick, chan, rest): # XXX: hvisit (3 functions above this one)
cli.notice(nick, messages["silenced"])
return
if nickrole == "harlot":
if var.HVISITED.get(nick):
pm(cli, nick, (messages["harlot_already_visited"]).format(var.HVISITED[nick]))
return
var.HVISITED[nick] = None
pm(cli, nick, messages["no_visit"])
elif nickrole == "turncoat":
if nickrole == "turncoat":
if var.TURNCOATS[nick][1] == var.NIGHT_COUNT:
# theoretically passing would revert them to how they were before, but
# we aren't tracking that, so just tell them to change it back themselves.
@ -5348,16 +5278,6 @@ def transition_night(cli):
# send PMs
ps = list_players()
for harlot in var.ROLES["harlot"]:
pl = ps[:]
random.shuffle(pl)
pl.remove(harlot)
if harlot in var.PLAYERS and not is_user_simple(harlot):
pm(cli, harlot, messages["harlot_info"])
else:
pm(cli, harlot, messages["harlot_simple"]) # !simple
pm(cli, harlot, "Players: " + ", ".join(pl))
for pht in var.ROLES["prophet"]:
chance1 = math.floor(var.PROPHET_REVEALED_CHANCE[0] * 100)
chance2 = math.floor(var.PROPHET_REVEALED_CHANCE[1] * 100)