♪ come, children, come out to the forest ♫
This adds the new piper role, whose goal is to charm all the other players to win. The charmed players are told who else is charmed, every night, and must find out the piper and lynch him to win. The piper is a win stealer, and takes precedence over monster. They can select either one or two targets, but unless there is exactly one person left to be charmed, they must pick two targets.
This commit is contained in:
parent
c49c2647d8
commit
04c814ea16
@ -185,6 +185,7 @@ ROLE_GUIDE = {# village roles
|
||||
"jester" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
|
||||
"monster" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 ),
|
||||
"amnesiac" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 ),
|
||||
"piper" : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ),
|
||||
# templates
|
||||
"cursed villager" : ( 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 2 , 2 , 2 , 2 ),
|
||||
"gunner" : ( 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 2 , 2 , 2 ),
|
||||
@ -203,13 +204,14 @@ WOLFCHAT_ROLES = WOLF_ROLES + ["traitor", "hag", "sorcerer", "warlock"]
|
||||
# Wins with the wolves, even if the roles are not necessarily wolves themselves
|
||||
WOLFTEAM_ROLES = WOLFCHAT_ROLES + ["minion", "cultist"]
|
||||
# These roles never win as a team, only ever individually (either instead of or in addition to the regular winners)
|
||||
TRUE_NEUTRAL_ROLES = ["crazed shaman", "fool", "jester", "monster", "clone"]
|
||||
TRUE_NEUTRAL_ROLES = ["crazed shaman", "fool", "jester", "monster", "clone", "piper"]
|
||||
# These are the roles that will NOT be used for when amnesiac turns, everything else is fair game! (var.DEFAULT_ROLE is also appended if not in this list)
|
||||
AMNESIAC_BLACKLIST = ["monster", "minion", "matchmaker", "clone", "doctor", "villager", "cultist"]
|
||||
AMNESIAC_BLACKLIST = ["monster", "minion", "matchmaker", "clone", "doctor", "villager", "cultist", "piper"]
|
||||
# These roles are seen as wolf by the seer/oracle
|
||||
SEEN_WOLF = WOLF_ROLES + ["monster", "mad scientist"]
|
||||
# These are seen as the default role (or villager) when seen by seer (this overrides SEEN_WOLF)
|
||||
SEEN_DEFAULT = ["traitor", "hag", "sorcerer", "village elder", "time lord", "villager", "cultist", "minion", "vengeful ghost", "lycan", "clone", "fool", "jester", "werekitten", "warlock"]
|
||||
SEEN_DEFAULT = ["traitor", "hag", "sorcerer", "village elder", "time lord", "villager", "cultist", "minion",
|
||||
"vengeful ghost", "lycan", "clone", "fool", "jester", "werekitten", "warlock", "piper"]
|
||||
|
||||
# The roles in here are considered templates and will be applied on TOP of other roles. The restrictions are a list of roles that they CANNOT be applied to
|
||||
# NB: if you want a template to apply to everyone, list it here but make the restrictions an empty list. Templates not listed here are considered full roles instead
|
||||
@ -367,6 +369,8 @@ def del_player(pname):
|
||||
del BITTEN[pname]
|
||||
if pname in BITTEN_ROLES:
|
||||
del BITTEN_ROLES[pname]
|
||||
if pname in CHARMED:
|
||||
CHARMED.remove(pname)
|
||||
|
||||
def get_templates(nick):
|
||||
tpl = []
|
||||
|
136
src/wolfgame.py
136
src/wolfgame.py
@ -2090,9 +2090,11 @@ def stop_game(cli, winner = "", abort = False):
|
||||
if winner == "wolves":
|
||||
won = True
|
||||
elif rol in var.TRUE_NEUTRAL_ROLES:
|
||||
# true neutral roles never have a team win (with exception of monsters), only individual wins
|
||||
# true neutral roles never have a team win (with exception of monsters and pipers), only individual wins
|
||||
if winner == "monsters" and rol == "monster":
|
||||
won = True
|
||||
if winner == "pipers" and rol == "piper":
|
||||
won = True
|
||||
elif rol in ("amnesiac", "vengeful ghost") and splr not in var.VENGEFUL_GHOSTS:
|
||||
if var.DEFAULT_ROLE == "villager" and winner == "villagers":
|
||||
won = True
|
||||
@ -2227,6 +2229,7 @@ def chk_win(cli, end_game = True):
|
||||
lrealwolves = len(var.list_players(var.WOLF_ROLES)) - cubs
|
||||
monsters = len(var.ROLES["monster"]) if "monster" in var.ROLES else 0
|
||||
traitors = len(var.ROLES["traitor"]) if "traitor" in var.ROLES else 0
|
||||
lpipers = len(var.ROLES["piper"]) if "piper" in var.ROLES else 0
|
||||
if var.PHASE == "day":
|
||||
for p in var.WOUNDED:
|
||||
try:
|
||||
@ -2252,6 +2255,11 @@ def chk_win(cli, end_game = True):
|
||||
if lpl < 1:
|
||||
message = "Game over! There are no players remaining."
|
||||
winner = "none"
|
||||
elif lpipers and lpl - lpipers == len(var.CHARMED - set(var.ROLES["piper"])):
|
||||
winner = "pipers"
|
||||
message = ("Game over! Everyone has fallen victim to the charms of the " +
|
||||
"piper{0}. The piper{0} lead{1} the villagers away from the village, " +
|
||||
"never to return...").format("s" if lpipers > 1 else "", "s" if lpipers == 1 else "")
|
||||
elif lwolves == lpl / 2:
|
||||
if monsters > 0:
|
||||
plural = "s" if monsters > 1 else ""
|
||||
@ -2309,6 +2317,9 @@ def chk_win(cli, end_game = True):
|
||||
vroles = [role for role in var.ROLES.keys() if var.ROLES[role] and role not in (var.WOLFTEAM_ROLES + var.TRUE_NEUTRAL_ROLES + list(var.TEMPLATE_RESTRICTIONS.keys()))]
|
||||
for plr in var.list_players(vroles):
|
||||
players.append("{0} ({1})".format(plr, var.get_role(plr)))
|
||||
elif winner == "pipers":
|
||||
for plr in var.ROLES["piper"]:
|
||||
players.append("{0} ({1})".format(plr, var.get_role(plr)))
|
||||
debuglog("WIN:", winner)
|
||||
debuglog("PLAYERS:", ", ".join(players))
|
||||
cli.msg(chan, message)
|
||||
@ -3068,6 +3079,12 @@ def on_nick(cli, oldnick, nick):
|
||||
if prefix in var.CURSED:
|
||||
var.CURSED.remove(prefix)
|
||||
var.CURSED.append(nick)
|
||||
if prefix in var.CHARMERS:
|
||||
var.CHARMERS.remove(prefix)
|
||||
var.CHARMERS.add(nick)
|
||||
if prefix in var.CHARMED:
|
||||
var.CHARMED.remove(prefix)
|
||||
var.CHARMED.add(nick)
|
||||
with var.GRAVEYARD_LOCK: # to be safe
|
||||
if prefix in var.LAST_SAID_TIME.keys():
|
||||
var.LAST_SAID_TIME[nick] = var.LAST_SAID_TIME.pop(prefix)
|
||||
@ -3828,12 +3845,14 @@ def chk_nightdone(cli):
|
||||
# TODO: alphabetize and/or arrange sensibly
|
||||
actedcount = len(var.SEEN + list(var.HVISITED.keys()) + list(var.GUARDED.keys()) +
|
||||
list(var.KILLS.keys()) + list(var.OTHER_KILLS.keys()) +
|
||||
list(var.OBSERVED.keys()) + var.PASSED + var.HEXED + var.SHAMANS + var.CURSED)
|
||||
list(var.OBSERVED.keys()) + var.PASSED + var.HEXED + var.SHAMANS +
|
||||
var.CURSED + list(var.CHARMERS))
|
||||
nightroles = (var.ROLES["seer"] + var.ROLES["oracle"] + var.ROLES["harlot"] +
|
||||
var.ROLES["bodyguard"] + var.ROLES["guardian angel"] + var.ROLES["wolf"] +
|
||||
var.ROLES["werecrow"] + var.ROLES["alpha wolf"] + var.ROLES["sorcerer"] + var.ROLES["hunter"] +
|
||||
list(var.VENGEFUL_GHOSTS.keys()) + var.ROLES["hag"] + var.ROLES["shaman"] +
|
||||
var.ROLES["crazed shaman"] + var.ROLES["augur"] + var.ROLES["werekitten"] + var.ROLES["warlock"])
|
||||
var.ROLES["crazed shaman"] + var.ROLES["augur"] + var.ROLES["werekitten"] +
|
||||
var.ROLES["warlock"] + var.ROLES["piper"])
|
||||
if var.FIRST_NIGHT:
|
||||
actedcount += len(var.MATCHMAKERS + list(var.CLONED.keys()))
|
||||
nightroles += var.ROLES["matchmaker"] + var.ROLES["clone"]
|
||||
@ -5214,6 +5233,94 @@ def clone(cli, nick, chan, rest):
|
||||
debuglog("{0} ({1}) CLONE: {2} ({3})".format(nick, var.get_role(nick), victim, var.get_role(victim)))
|
||||
chk_nightdone(cli)
|
||||
|
||||
@cmd("charm", chan=False, pm=True, game=True, playing=True, roles=("piper",))
|
||||
def charm(cli, nick, chan, rest):
|
||||
"""Charm a player, slowly leading to your win!"""
|
||||
if var.PHASE != "night":
|
||||
pm(cli, nick, "You may only charm other players during the night.")
|
||||
return
|
||||
if nick in var.CHARMERS:
|
||||
pm(cli, nick, "You have already charmed players for tonight.")
|
||||
return
|
||||
if nick in var.SILENCED:
|
||||
pm(cli, nick, "You are silenced, and are unable to use any special powers.")
|
||||
return
|
||||
|
||||
pieces = re.split(" +",rest)
|
||||
victim = pieces[0]
|
||||
if len(pieces) > 1:
|
||||
if len(pieces) > 2 and pieces[1].lower() == "and":
|
||||
victim2 = pieces[2]
|
||||
else:
|
||||
victim2 = pieces[1]
|
||||
else:
|
||||
victim2 = None
|
||||
|
||||
victim = get_victim(cli, nick, victim, False, True)
|
||||
if not victim:
|
||||
return
|
||||
if victim2 is not None:
|
||||
victim2 = get_victim(cli, nick, victim2, False, True)
|
||||
|
||||
if victim == victim2:
|
||||
pm(cli, nick, "You must choose two different people.")
|
||||
return
|
||||
if nick in (victim, victim2):
|
||||
pm(cli, nick, "You may not charm yourself.")
|
||||
return
|
||||
if victim in var.CHARMED or victim2 in var.CHARMED:
|
||||
if victim in var.CHARMED and victim2 and victim2 in var.CHARMED:
|
||||
pm(cli, nick, "\u0002{0}\u0002 and \u0002{1}\u0002 are already charmed!".format(victim, victim2))
|
||||
return
|
||||
if (len(var.list_players()) - len(var.ROLES["piper"]) - len(var.CHARMED) - 2 >= 0 or
|
||||
victim in var.CHARMED and not victim2):
|
||||
pm(cli, nick, "\u0002{0}\u0002 is already charmed!".format(victim in var.CHARMED and victim or victim2))
|
||||
return
|
||||
|
||||
elif not victim2 and len(var.list_players()) - len(var.ROLES["piper"]) - len(var.CHARMED) - 2 >= 0:
|
||||
pm(cli, nick, "Not enough parameters.")
|
||||
return
|
||||
|
||||
var.CHARMERS.add(nick)
|
||||
|
||||
var.CHARMED.add(victim)
|
||||
if victim2:
|
||||
var.CHARMED.add(victim2)
|
||||
|
||||
message = ("You hear the sweet tones of a flute coming from outside your window... " +
|
||||
"You inexorably walk outside and find yourself stranded away from " +
|
||||
"the village. You find out that \u0002{0}\u0002 {1} also charmed!")
|
||||
|
||||
simple_message = "You are now charmed. Other charmed players: \u0002{0}\u0002"
|
||||
|
||||
pm(cli, nick, "You have charmed \u0002{0}\u0002{1}.".format(victim, victim2 and " and \u0002{0}\u0002".format(victim2) or ""))
|
||||
|
||||
for vict in (victim, victim2):
|
||||
if vict and vict in var.PLAYERS:
|
||||
msg = is_user_simple(vict) and simple_message or message
|
||||
|
||||
pm(cli, vict, msg.format("\u0002, \u0002".join(var.CHARMED - {vict}),
|
||||
"is" if len(var.CHARMED) == 2 else "are"))
|
||||
|
||||
for vict in var.CHARMED:
|
||||
if vict in (victim, victim2):
|
||||
continue
|
||||
message = victim2 and "\u0002{0}\u0002 and \u0002{1}\u0002 are" or "\u0002{0}\u0002 is"
|
||||
pm(cli, vict, (message + " now charmed! All charmed players: " +
|
||||
"\u0002{2}\u0002").format(victim, victim2,
|
||||
"\u0002, \u0002".join(var.CHARMED - {vict})))
|
||||
|
||||
if victim2:
|
||||
debuglog("{0} ({1}) CHARM {2} ({3}) && {4} ({5})".format(nick, var.get_role(nick),
|
||||
victim, var.get_role(victim),
|
||||
victim2, var.get_role(victim2)))
|
||||
|
||||
else:
|
||||
debuglog("{0} ({1}) CHARM {2} ({3})".format(nick, var.get_role(nick),
|
||||
victim, var.get_role(victim)))
|
||||
|
||||
chk_nightdone(cli)
|
||||
|
||||
@hook("featurelist") # For multiple targets with PRIVMSG
|
||||
def getfeatures(cli, nick, *rest):
|
||||
for r in rest:
|
||||
@ -5355,6 +5462,7 @@ def transition_night(cli):
|
||||
var.SHAMANS = []
|
||||
var.PASSED = [] # list of hunters that have chosen not to kill
|
||||
var.OBSERVED = {} # those whom werecrows have observed
|
||||
var.CHARMERS = set() # pipers who have charmed
|
||||
var.HVISITED = {}
|
||||
var.ASLEEP = []
|
||||
var.DYING = []
|
||||
@ -5873,6 +5981,22 @@ def transition_night(cli):
|
||||
pm(cli, ass, "You are an \u0002assassin\u0002.")
|
||||
pm(cli, ass, "Players: " + ", ".join(pl))
|
||||
|
||||
for piper in var.ROLES["piper"]:
|
||||
pl = ps[:]
|
||||
random.shuffle(pl)
|
||||
pl.remove(piper)
|
||||
for charmed in var.CHARMED:
|
||||
pl.remove(charmed)
|
||||
if piper in var.PLAYERS and not is_user_simple(piper):
|
||||
pm(cli, piper, ('You are a \u0002piper\u0002. You must select two players ' +
|
||||
'to charm each night. The charmed players will know each ' +
|
||||
'other, but not who charmed them. You win when all other ' +
|
||||
'players are charmed. Use "charm <nick1> and <nick2>" to ' +
|
||||
'select the players to charm.'))
|
||||
else:
|
||||
pm(cli, piper, "You are a \u0002piper\u0002.")
|
||||
pm(cli, piper, "Players: " + ", ".join(pl))
|
||||
|
||||
if var.FIRST_NIGHT:
|
||||
for mm in var.ROLES["matchmaker"]:
|
||||
pl = ps[:]
|
||||
@ -6168,6 +6292,8 @@ def start(cli, nick, chan, forced = False, restart = ""):
|
||||
var.BITTEN = {}
|
||||
var.BITE_PREFERENCES = {}
|
||||
var.BITTEN_ROLES = {}
|
||||
var.CHARMERS = set()
|
||||
var.CHARMED = set()
|
||||
|
||||
for role, count in addroles.items():
|
||||
if role in var.TEMPLATE_RESTRICTIONS.keys():
|
||||
@ -7565,6 +7691,10 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
|
||||
if var.IMMUNIZED:
|
||||
output.append("\u0002immunized:\u0002 {0}".format(', '.join(var.IMMUNIZED)))
|
||||
|
||||
# get charmed players
|
||||
if var.CHARMED:
|
||||
output.append("\u0002charmed players\u0002: {0}".format(', '.join(var.CHARMED)))
|
||||
|
||||
if chan == nick:
|
||||
pm(cli, nick, var.break_long_message(output, ' | '))
|
||||
else:
|
||||
|
Loading…
Reference in New Issue
Block a user