Split and convert prophet
Also changes prophet from being able to pray twice to only being able to pray once, but they get the third of the player list (instead of half and then one).
This commit is contained in:
parent
e54a3f7672
commit
0479bab5e1
@ -651,12 +651,9 @@
|
||||
"already_prayed": "You are exhausted and unable to receive any more visions tonight.",
|
||||
"specific_invalid_role": "\u0002{0}\u0002 is not a valid role.",
|
||||
"vision_only_role_self": "You receive a vision that you are the only \u0002{0}\u0002.",
|
||||
"vision_no_more_role": "You receive a vision that there are no other \u0002{0}\u0002.",
|
||||
"vision_players": "You receive a vision that at least one of these people is a \u0002{0}\u0002: ",
|
||||
"vision_prophet": "You receive a vision that \u0002{0}\u0002 is the prophet!",
|
||||
"vision_role": "You receive a vision that \u0002{0}\u0002 is a \u0002{1}\u0002.",
|
||||
"vision_players": "You receive a vision that at least one of these people is a{1} \u0002{0}\u0002: {2}",
|
||||
"vision_role": "You receive a vision that \u0002{2}\u0002 is a{1} \u0002{0}\u0002.",
|
||||
"vision_none": "You receive a vision that there are no \u0002{0}\u0002.",
|
||||
"vision_recovering": "You are still recovering from your previous vision and are unable to receive any more visions tonight.",
|
||||
"succubus_already_visited": "You are already entrancing \u0002{0}\u0002 tonight.",
|
||||
"succubus_not_self": "You may not entrance yourself. Use \"pass\" to not entrance anyone tonight.",
|
||||
"notify_succubus_target": "You have become entranced by \u0002{0}\u0002. From this point on, you must vote along with them or risk dying. You \u0002cannot win with your own team\u0002, but you will win should all alive players become entranced.",
|
||||
@ -680,9 +677,7 @@
|
||||
"shaman_turn": "As you were out delivering your totem last night, a large werewolf overpowered and bit you. Shortly thereafter, you found yourself transforming into a wolf yourself! Your mind floods with new wicked ideas for totems.",
|
||||
"no_longer_entranced": "You are no longer entranced.",
|
||||
"doomsayer_notify": "You are a \u0002doomsayer\u0002. You can see how bad luck will befall someone at night by using \"see <nick>\" on them. You may also use \"kill <nick>\" to kill a villager.",
|
||||
"prophet_notify_both": "You are a \u0002prophet\u0002. Each night you may pray up to twice to learn one player who has a particular role. The first time, you are given a list of players and have a{0} {1}% chance of revealing yourself to someone with that role. If you did not reveal yourself, you may pray again to obtain the exact player name with a{2} {3}% chance of revealing yourself. Use \"pray <role>\" in PM to learn who has that role.",
|
||||
"prophet_notify_second": "You are a \u0002prophet\u0002. Each night you may pray up to twice to learn one player who has a particular role. The first time, you are given a list of players with that role. You may pray again to obtain the exact player name, however this has a{0} {1}% chance of revealing yourself to that player. Use \"pray <role>\" in PM to learn who has that role.",
|
||||
"prophet_notify_none": "You are a \u0002prophet\u0002. Each night you may pray to learn one player who has a particular role. Use \"pray <role>\" in PM to learn who has that role.",
|
||||
"prophet_notify": "You are a \u0002prophet\u0002. Each night you may pray to learn one player who has a particular role. Use \"pray <role>\" in PM to learn who has that role.",
|
||||
"prophet_simple": "You are a \u0002prophet\u0002.",
|
||||
"dullahan_targets_dead": "All your targets are already dead!",
|
||||
"dullahan_notify": "You are a \u0002dullahan\u0002. Every night, you may kill someone by using \"kill <nick>\". You win when all your targets are dead.",
|
||||
|
@ -21,7 +21,7 @@ def setup_variables(rolename, *, send_role, types):
|
||||
|
||||
@event_listener("transition_night_end")
|
||||
def on_transition_night_end(evt, var):
|
||||
villagers = set(get_players(("priest", "prophet", "doctor")))
|
||||
villagers = set(get_players(("priest", "doctor")))
|
||||
win_stealers = set(get_players(("fool", "monster", "demoniac")))
|
||||
neutrals = set(get_players(("turncoat", "clone", "jester")))
|
||||
|
||||
|
115
src/roles/prophet.py
Normal file
115
src/roles/prophet.py
Normal file
@ -0,0 +1,115 @@
|
||||
import re
|
||||
import random
|
||||
import itertools
|
||||
import math
|
||||
from collections import defaultdict
|
||||
|
||||
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, DefaultUserDict
|
||||
from src.messages import messages
|
||||
from src.events import Event
|
||||
|
||||
PRAYED = UserSet() # type: Set[users.User]
|
||||
|
||||
@command("pray", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("prophet",))
|
||||
def pray(var, wrapper, message):
|
||||
"""Receive divine visions of who has a role."""
|
||||
if wrapper.source in PRAYED:
|
||||
wrapper.pm(messages["already_prayed"])
|
||||
return
|
||||
|
||||
what = re.split(" +", message)[0]
|
||||
if not what:
|
||||
wrapper.pm(messages["not_enough_parameters"])
|
||||
return
|
||||
|
||||
# complete this as a match with other roles (so "cursed" can match "cursed villager" for instance)
|
||||
role = complete_one_match(what.lower(), {p for p in var.ROLE_GUIDE if p not in var.TEMPLATE_RESTRICTIONS})
|
||||
if role is None and what.lower() in var.ROLE_ALIASES:
|
||||
role = var.ROLE_ALIASES[what.lower()]
|
||||
if role in var.TEMPLATE_RESTRICTIONS: # allow only main roles
|
||||
role = None
|
||||
if role is None:
|
||||
# typo, let them fix it
|
||||
wrapper.pm(messages["specific_invalid_role"].format(what))
|
||||
return
|
||||
|
||||
# get a list of all roles actually in the game, including roles that amnesiacs will be turning into
|
||||
# (amnesiacs are special since they're also listed as amnesiac; that way a prophet can see both who the
|
||||
# amnesiacs themselves are as well as what they'll become)
|
||||
pl = get_players()
|
||||
from src.roles.amnesiac import ROLES as amn_roles
|
||||
valid_roles = {r for p, r in amn_roles.items() if p in pl}.union(var.MAIN_ROLES.values())
|
||||
|
||||
PRAYED.add(wrapper.source)
|
||||
|
||||
if role in valid_roles:
|
||||
# this sees through amnesiac, so the amnesiac's final role counts as their role
|
||||
# also, if we're the only person with that role, say so
|
||||
people = set(get_all_players((role,))) | {p for p, r in amn_roles.items() if p in pl and r == role}
|
||||
if len(people) == 1 and wrapper.source in people:
|
||||
wrapper.pm(messages["vision_only_role_self"].format(role))
|
||||
PRAYED.add(wrapper.source)
|
||||
debuglog("{0} (prophet) PRAY {1} - ONLY".format(wrapper.source, role))
|
||||
return
|
||||
|
||||
target = random.choice(list(people))
|
||||
part = random.sample([p for p in pl if p is not wrapper.source], len(pl) // 3)
|
||||
if target not in part:
|
||||
part[0] = target
|
||||
random.shuffle(part)
|
||||
part = [p.nick for p in part]
|
||||
|
||||
an = ""
|
||||
if role.startswith(("a", "e", "i", "o", "u")):
|
||||
an = "n"
|
||||
|
||||
key = "vision_players"
|
||||
if len(part) == 1:
|
||||
key = "vision_role"
|
||||
|
||||
if len(part) > 2:
|
||||
msg = "{0}, and {1}".format(", ".join(part[:-1]), part[-1])
|
||||
else:
|
||||
msg = " and ".join(part)
|
||||
|
||||
wrapper.pm(messages[key].format(role, an, msg))
|
||||
debuglog("{0} (prophet) PRAY {1} ({2})".format(wrapper.source, role, target))
|
||||
|
||||
else:
|
||||
# role is not in this game, this still counts as a successful activation of the power!
|
||||
wrapper.pm(messages["vision_none"].format(plural(role)))
|
||||
debuglog("{0} (prophet) PRAY {1} - NONE".format(wrapper.source, role))
|
||||
|
||||
@event_listener("transition_night_end")
|
||||
def on_transition_night_end(evt, var):
|
||||
for pht in get_all_players(("prophet",)):
|
||||
if pht.prefers_simple():
|
||||
pht.send(messages["prophet_simple"])
|
||||
else:
|
||||
pht.send(messages["prophet_notify"])
|
||||
|
||||
@event_listener("chk_nightdone")
|
||||
def on_chk_nightdone(evt, var):
|
||||
evt.data["nightroles"].extend(get_all_players(("prophet",)))
|
||||
evt.data["actedcount"] += len(PRAYED)
|
||||
|
||||
@event_listener("night_acted")
|
||||
def on_night_acted(evt, var, spy, user):
|
||||
if user in PRAYED:
|
||||
evt.data["acted"] = True
|
||||
|
||||
@event_listener("get_special")
|
||||
def on_get_special(evt, var):
|
||||
evt.data["villagers"].update(get_players(("prophet",)))
|
||||
|
||||
@event_listener("begin_day")
|
||||
def on_begin_day(evt, var):
|
||||
PRAYED.clear()
|
||||
|
||||
@event_listener("reset")
|
||||
def on_reset(evt, var):
|
||||
PRAYED.clear()
|
@ -158,9 +158,6 @@ DETECTIVE_REVEALED_CHANCE = 2/5
|
||||
SHARPSHOOTER_CHANCE = 1/5 # if sharpshooter is enabled, chance that a gunner will become a sharpshooter instead
|
||||
FALLEN_ANGEL_KILLS_GUARDIAN_ANGEL_CHANCE = 1/2
|
||||
|
||||
# HALF FULL
|
||||
PROPHET_REVEALED_CHANCE = ( 2/5 , 4/5 )
|
||||
|
||||
AMNESIAC_NIGHTS = 3 # amnesiac gets to know their actual role on this night
|
||||
|
||||
DOCTOR_IMMUNIZATION_MULTIPLIER = 0.135 # ceil(num_players * multiplier) = number of immunizations
|
||||
|
158
src/wolfgame.py
158
src/wolfgame.py
@ -614,7 +614,7 @@ def replace(var, wrapper, message):
|
||||
channels.Main.mode(("-v", target), ("+v", wrapper.source))
|
||||
|
||||
channels.Main.send(messages["player_swap"].format(wrapper.source, target))
|
||||
myrole.caller(wrapper.source.client, wrapper.source.nick, wrapper.target.name, "") # FIXME: Old API
|
||||
myrole.func(var, wrapper, "")
|
||||
|
||||
|
||||
@command("pingif", "pingme", "pingat", "pingpref", pm=True)
|
||||
@ -2793,24 +2793,6 @@ def rename_player(var, user, prefix):
|
||||
if prefix == k:
|
||||
var.PLAYERS[nick] = var.PLAYERS.pop(k)
|
||||
|
||||
kvp = []
|
||||
# Looks like {'nick': [_, 'nick1', _, {'nick2': [_]}]}
|
||||
for a,b in var.PRAYED.items():
|
||||
kvp2 = []
|
||||
if a == prefix:
|
||||
a = nick
|
||||
if b[1] == prefix:
|
||||
b[1] = nick
|
||||
for c,d in b[3].items():
|
||||
if c == prefix:
|
||||
c = nick
|
||||
kvp2.append((c,d))
|
||||
b[3].update(kvp2)
|
||||
kvp.append((a,b))
|
||||
var.PRAYED.update(kvp)
|
||||
if prefix in var.PRAYED.keys():
|
||||
del var.PRAYED[prefix]
|
||||
|
||||
for dictvar in (var.OBSERVED, var.CLONED, var.LASTHEXED, var.BITE_PREFERENCES):
|
||||
kvp = []
|
||||
for a,b in dictvar.items():
|
||||
@ -3147,8 +3129,7 @@ def transition_day(gameid=0):
|
||||
user = users._get(target) # FIXME
|
||||
evt = Event("night_acted", {"acted": False})
|
||||
evt.dispatch(var, user, actor)
|
||||
if ((target in var.PRAYED and var.PRAYED[target][0] > 0) or
|
||||
target in var.OBSERVED or target in var.HEXED or target in var.CURSED or evt.data["acted"]):
|
||||
if target in var.OBSERVED or target in var.HEXED or target in var.CURSED or evt.data["acted"]:
|
||||
actor.send(messages["werecrow_success"].format(user))
|
||||
else:
|
||||
actor.send(messages["werecrow_failure"].format(user))
|
||||
@ -3532,11 +3513,7 @@ def chk_nightdone():
|
||||
spl = set(pl)
|
||||
actedcount = sum(map(len, (var.PASSED, var.OBSERVED, var.HEXED, var.CURSED)))
|
||||
|
||||
nightroles = list(get_all_players(("sorcerer", "hag", "warlock", "werecrow", "prophet")))
|
||||
|
||||
for nick, info in var.PRAYED.items():
|
||||
if info[0] > 0:
|
||||
actedcount += 1
|
||||
nightroles = list(get_all_players(("sorcerer", "hag", "warlock", "werecrow")))
|
||||
|
||||
if var.FIRST_NIGHT:
|
||||
actedcount += len(var.CLONED.keys())
|
||||
@ -4090,109 +4067,6 @@ def observe(cli, nick, chan, rest):
|
||||
|
||||
debuglog("{0} ({1}) OBSERVE: {2} ({3})".format(nick, role, victim, get_role(victim)))
|
||||
|
||||
@cmd("pray", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("prophet",))
|
||||
def pray(cli, nick, chan, rest):
|
||||
"""Receive divine visions of who has a role."""
|
||||
# this command may be used multiple times in the course of the night, however it only needs
|
||||
# to be used once to count towards ending night (additional uses don't count extra)
|
||||
if nick in var.PRAYED and var.PRAYED[nick][0] == 2:
|
||||
pm(cli, nick, messages["already_prayed"])
|
||||
return
|
||||
elif nick not in var.PRAYED:
|
||||
# [number of times prayed tonight, current target, target role, {target: [roles]}]
|
||||
var.PRAYED[nick] = [0, None, None, defaultdict(set)]
|
||||
|
||||
if var.PRAYED[nick][0] == 0:
|
||||
what = re.split(" +", rest)[0]
|
||||
if not what:
|
||||
pm(cli, nick, messages["not_enough_parameters"])
|
||||
return
|
||||
# complete this as a match with other roles (so "cursed" can match "cursed villager" for instance)
|
||||
role = complete_one_match(what.lower(), var.ROLE_GUIDE.keys())
|
||||
if role is None:
|
||||
if what.lower() in var.ROLE_ALIASES:
|
||||
role = var.ROLE_ALIASES[what.lower()]
|
||||
else:
|
||||
# typo, let them fix it
|
||||
pm(cli, nick, messages["specific_invalid_role"].format(what))
|
||||
return
|
||||
|
||||
# get a list of all roles actually in the game, including roles that amnesiacs will be turning into
|
||||
# (amnesiacs are special since they're also listed as amnesiac; that way a prophet can see both who the
|
||||
# amnesiacs themselves are as well as what they'll become)
|
||||
pl = list_players()
|
||||
from src.roles.amnesiac import ROLES
|
||||
valid_roles = {r for r, p in var.ROLES.items() if p} | {r for p, r in ROLES.items() if p.nick in pl} # FIXME
|
||||
|
||||
if role in valid_roles:
|
||||
# this sees through amnesiac, so the amnesiac's final role counts as their role
|
||||
# also, if we're the only person with that role, say so and don't allow a second vision
|
||||
people = set(get_roles(role)) | {p.nick for p, r in ROLES.items() if p.nick in pl and r == role} # FIXME
|
||||
if len(people) == 1 and nick in people:
|
||||
pm(cli, nick, messages["vision_only_role_self"].format(role))
|
||||
var.PRAYED[nick][0] = 2
|
||||
debuglog("{0} ({1}) PRAY {2} - ONLY".format(nick, get_role(nick), role))
|
||||
return
|
||||
# select someone with the role that we haven't looked at before for this particular role
|
||||
prevlist = (p for p, rl in var.PRAYED[nick][3].items() if role in rl)
|
||||
for p in prevlist:
|
||||
people.discard(p)
|
||||
if len(people) == 0 or (len(people) == 1 and nick in people):
|
||||
pm(cli, nick, messages["vision_no_more_role"].format(plural(role)))
|
||||
var.PRAYED[nick][0] = 2
|
||||
debuglog("{0} ({1}) PRAY {2} - NO OTHER".format(nick, get_role(nick), role))
|
||||
return
|
||||
target = random.choice(list(people))
|
||||
var.PRAYED[nick][0] = 1
|
||||
var.PRAYED[nick][1] = target
|
||||
var.PRAYED[nick][2] = role
|
||||
var.PRAYED[nick][3][target].add(role)
|
||||
half = random.sample(pl, math.ceil(len(pl) / 2))
|
||||
if target not in half:
|
||||
half[0] = target
|
||||
random.shuffle(half)
|
||||
# if prophet never reveals, there is no point making them pray twice,
|
||||
# so just give them the player the first time around
|
||||
if len(half) > 1 and (var.PROPHET_REVEALED_CHANCE[0] > 0 or var.PROPHET_REVEALED_CHANCE[1] > 0):
|
||||
msg = messages["vision_players"].format(role)
|
||||
if len(half) > 2:
|
||||
msg += "{0}, and {1}.".format(", ".join(half[:-1]), half[-1])
|
||||
else:
|
||||
msg += "{0} and {1}.".format(half[0], half[1])
|
||||
pm(cli, nick, msg)
|
||||
debuglog("{0} ({1}) PRAY {2} ({3}) - HALF".format(nick, get_role(nick), role, target))
|
||||
if random.random() < var.PROPHET_REVEALED_CHANCE[0]:
|
||||
pm(cli, target, messages["vision_prophet"].format(nick))
|
||||
debuglog("{0} ({1}) PRAY REVEAL".format(nick, get_role(nick), role))
|
||||
var.PRAYED[nick][0] = 2
|
||||
else:
|
||||
# only one, go straight to second chance
|
||||
var.PRAYED[nick][0] = 2
|
||||
pm(cli, nick, messages["vision_role"].format(target, role))
|
||||
debuglog("{0} ({1}) PRAY {2} ({3}) - FULL".format(nick, get_role(nick), role, target))
|
||||
if random.random() < var.PROPHET_REVEALED_CHANCE[1]:
|
||||
pm(cli, target, messages["vision_prophet"].format(nick))
|
||||
debuglog("{0} ({1}) PRAY REVEAL".format(nick, get_role(nick)))
|
||||
else:
|
||||
# role is not in this game, this still counts as a successful activation of the power!
|
||||
pm(cli, nick, messages["vision_none"].format(plural(role)))
|
||||
debuglog("{0} ({1}) PRAY {2} - NONE".format(nick, get_role(nick), role))
|
||||
var.PRAYED[nick][0] = 2
|
||||
elif var.PRAYED[nick][1] is None:
|
||||
# the previous vision revealed the prophet, so they cannot receive any more visions tonight
|
||||
pm(cli, nick, messages["vision_recovering"])
|
||||
return
|
||||
else:
|
||||
# continuing a praying session from this night to obtain more information, give them the actual person
|
||||
var.PRAYED[nick][0] = 2
|
||||
target = var.PRAYED[nick][1]
|
||||
role = var.PRAYED[nick][2]
|
||||
pm(cli, nick, messages["vision_role"].format(target, role))
|
||||
debuglog("{0} ({1}) PRAY {2} ({3}) - FULL".format(nick, get_role(nick), role, target))
|
||||
if random.random() < var.PROPHET_REVEALED_CHANCE[1]:
|
||||
pm(cli, target, messages["vision_prophet"].format(nick))
|
||||
debuglog("{0} ({1}) PRAY REVEAL".format(nick, get_role(nick)))
|
||||
|
||||
@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):
|
||||
@ -4618,10 +4492,6 @@ def transition_night():
|
||||
var.OBSERVED = {} # those whom werecrows have observed
|
||||
var.TOBESILENCED = set()
|
||||
var.CONSECRATING.clear()
|
||||
for nick in var.PRAYED:
|
||||
var.PRAYED[nick][0] = 0
|
||||
var.PRAYED[nick][1] = None
|
||||
var.PRAYED[nick][2] = None
|
||||
|
||||
daydur_msg = ""
|
||||
|
||||
@ -4656,21 +4526,6 @@ def transition_night():
|
||||
# send PMs
|
||||
ps = get_players()
|
||||
|
||||
for pht in get_all_players(("prophet",)):
|
||||
chance1 = math.floor(var.PROPHET_REVEALED_CHANCE[0] * 100)
|
||||
chance2 = math.floor(var.PROPHET_REVEALED_CHANCE[1] * 100)
|
||||
an1 = "n" if chance1 >= 80 and chance1 < 90 else ""
|
||||
an2 = "n" if chance2 >= 80 and chance2 < 90 else ""
|
||||
if pht.prefers_simple():
|
||||
pht.send(messages["prophet_simple"])
|
||||
else:
|
||||
if chance1 > 0:
|
||||
pht.send(messages["prophet_notify_both"].format(an1, chance1, an2, chance2))
|
||||
elif chance2 > 0:
|
||||
pht.send(messages["prophet_notify_second"].format(an2, chance2))
|
||||
else:
|
||||
pht.send(messages["prophet_notify_none"])
|
||||
|
||||
for drunk in get_all_players(("village drunk",)):
|
||||
if drunk.prefers_simple():
|
||||
drunk.send(messages["drunk_simple"])
|
||||
@ -5024,7 +4879,6 @@ def start(cli, nick, chan, forced = False, restart = ""):
|
||||
var.PRIESTS = set()
|
||||
var.CONSECRATING.clear()
|
||||
var.DYING.clear()
|
||||
var.PRAYED = {}
|
||||
|
||||
var.DEADCHAT_PLAYERS.clear()
|
||||
var.SPECTATING_WOLFCHAT.clear()
|
||||
@ -5824,7 +5678,7 @@ def listroles(cli, nick, chan, rest):
|
||||
reply(cli, nick, chan, " ".join(msg))
|
||||
|
||||
@command("myrole", pm=True, phases=("day", "night"))
|
||||
def myrole(var, wrapper, message): # FIXME: Need to fix !swap once this gets converted
|
||||
def myrole(var, wrapper, message):
|
||||
"""Reminds you of your current role."""
|
||||
|
||||
ps = get_participants()
|
||||
@ -5863,10 +5717,6 @@ def myrole(var, wrapper, message): # FIXME: Need to fix !swap once this gets con
|
||||
role = "sharpshooter"
|
||||
wrapper.pm(messages["gunner_simple"].format(role, var.GUNNERS[wrapper.source], "" if var.GUNNERS[wrapper.source] == 1 else "s"))
|
||||
|
||||
# Remind prophet of their role, in sleepy mode only where it is hacked into a template instead of a role
|
||||
if "prophet" in var.TEMPLATE_RESTRICTIONS and wrapper.source in var.ROLES["prophet"]:
|
||||
wrapper.pm(messages["prophet_simple"])
|
||||
|
||||
@command("aftergame", "faftergame", flag="D", pm=True)
|
||||
def aftergame(var, wrapper, message):
|
||||
"""Schedule a command to be run after the current game."""
|
||||
|
Loading…
Reference in New Issue
Block a user