Most of succubus

This commit is contained in:
Vgr E. Barry 2015-10-27 22:48:24 -04:00
parent 2421485875
commit da81909162
3 changed files with 110 additions and 16 deletions

View File

@ -514,10 +514,12 @@ class RandomMode(GameMode):
lcubs = addroles["wolf cub"] lcubs = addroles["wolf cub"]
lrealwolves = sum(addroles[r] for r in var.WOLF_ROLES - {"wolf cub"}) lrealwolves = sum(addroles[r] for r in var.WOLF_ROLES - {"wolf cub"})
lmonsters = addroles["monster"] lmonsters = addroles["monster"]
ldemoniacs = addroles["demoniacs"]
ltraitors = addroles["traitor"] ltraitors = addroles["traitor"]
lpipers = addroles["piper"] lpipers = addroles["piper"]
lsuccubi = addroles["succubus"]
if chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ltraitors, lpipers, cli, end_game=False): if chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs, ltraitors, lpipers, lsuccubi, 0, cli, end_game=False):
return self.role_attribution(evt, cli, chk_win_conditions, var, villagers) return self.role_attribution(evt, cli, chk_win_conditions, var, villagers)
evt.prevent_default = True evt.prevent_default = True

View File

@ -260,6 +260,9 @@ SEEN_WOLF = WOLF_ROLES | {"monster", "mad scientist", "succubus"}
SEEN_DEFAULT = frozenset({"traitor", "hag", "sorcerer", "time lord", "villager", "cultist", "minion", "turncoat", "amnesiac", SEEN_DEFAULT = frozenset({"traitor", "hag", "sorcerer", "time lord", "villager", "cultist", "minion", "turncoat", "amnesiac",
"vengeful ghost", "lycan", "clone", "fool", "jester", "werekitten", "warlock", "piper", "doomsayer", "demoniac"}) "vengeful ghost", "lycan", "clone", "fool", "jester", "werekitten", "warlock", "piper", "doomsayer", "demoniac"})
# these totems are detrimental for the *receiving* person, but can be detrimental to someone else acting on the receiver!
DETRIMENTAL_TOTEMS = frozenset({"death", "silence", "impatience", "pacifism", "narcolepsy", "exchange", "lycanthropy", "misdirection"})
# 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 # 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 set. Templates not listed here are considered full roles instead # NB: if you want a template to apply to everyone, list it here but make the restrictions an empty set. Templates not listed here are considered full roles instead
TEMPLATE_RESTRICTIONS = OrderedDict([ TEMPLATE_RESTRICTIONS = OrderedDict([

View File

@ -2169,6 +2169,11 @@ def chk_decision(cli, force = ""):
aftermessage = None aftermessage = None
votelist = copy.deepcopy(var.VOTES) votelist = copy.deepcopy(var.VOTES)
for votee, voters in votelist.items(): for votee, voters in votelist.items():
if votee in var.ROLES["succubus"]:
for vtr in var.ENTRANCED:
if vtr in voters:
voters.remove(vtr)
impatient_voters = [] impatient_voters = []
numvotes = 0 numvotes = 0
random.shuffle(var.IMPATIENT) random.shuffle(var.IMPATIENT)
@ -2533,6 +2538,8 @@ def stop_game(cli, winner = "", abort = False, additional_winners = None):
won = True won = True
elif winner == "villagers": elif winner == "villagers":
won = True won = True
elif winner == "succubi" and rol == "succubus":
won = True
survived = var.list_players() survived = var.list_players()
if plr.startswith("(dced)"): if plr.startswith("(dced)"):
@ -2598,6 +2605,8 @@ def stop_game(cli, winner = "", abort = False, additional_winners = None):
iwon = False iwon = False
elif rol == "jester" and splr in var.JESTERS: elif rol == "jester" and splr in var.JESTERS:
iwon = True iwon = True
elif winner == "succubi" and plr in var.ROLES["succubi"] | var.ENTRANCED:
iwon = True
elif not iwon: elif not iwon:
iwon = won and splr in survived # survived, team won = individual win iwon = won and splr in survived # survived, team won = individual win
@ -2672,6 +2681,8 @@ def chk_win(cli, end_game=True, winner=None):
ldemoniacs = len(var.ROLES.get("demoniac", ())) ldemoniacs = len(var.ROLES.get("demoniac", ()))
ltraitors = len(var.ROLES.get("traitor", ())) ltraitors = len(var.ROLES.get("traitor", ()))
lpipers = len(var.ROLES.get("piper", ())) lpipers = len(var.ROLES.get("piper", ()))
lsuccubi = len(var.ROLES.get("succubus", ()))
lentranced = len(var.ENTRANCED)
if var.PHASE == "day": if var.PHASE == "day":
for p in var.WOUNDED | var.ASLEEP | var.CONSECRATING: for p in var.WOUNDED | var.ASLEEP | var.CONSECRATING:
try: try:
@ -2683,9 +2694,9 @@ def chk_win(cli, end_game=True, winner=None):
except KeyError: except KeyError:
pass pass
return chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs, ltraitors, lpipers, cli, end_game, winner) return chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs, ltraitors, lpipers, lsuccubi, lentranced, cli, end_game, winner)
def chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs, ltraitors, lpipers, cli, end_game=True, winner=None): def chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs, ltraitors, lpipers, lsuccubi, lentranced, cli, end_game=True, winner=None):
"""Internal handler for the chk_win function.""" """Internal handler for the chk_win function."""
chan = botconfig.CHANNEL chan = botconfig.CHANNEL
with var.GRAVEYARD_LOCK: with var.GRAVEYARD_LOCK:
@ -2696,6 +2707,11 @@ def chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs,
elif lpl < 1: elif lpl < 1:
message = "Game over! There are no players remaining. Nobody wins." message = "Game over! There are no players remaining. Nobody wins."
winner = "none" winner = "none"
elif var.PHASE == "day" and lpl - lsuccubi <= lentranced: # entranced people
winner = "succubi"
message = ("Game over! The succub{0} ha{1} won! The succub{0} then take{2} all of the "
"entranced players with them, and go{3} out of the village, never to return"
"...").format(*(("us", "s", "s", "es") if lsuccubi == 1 else ("i", "ve", "", "")))
elif var.PHASE == "day" and lpipers and len(var.list_players()) - lpipers == len(var.CHARMED - var.ROLES["piper"]): elif var.PHASE == "day" and lpipers and len(var.list_players()) - lpipers == len(var.CHARMED - var.ROLES["piper"]):
winner = "pipers" winner = "pipers"
message = ("Game over! Everyone has fallen victim to the charms of the " + message = ("Game over! Everyone has fallen victim to the charms of the " +
@ -2747,7 +2763,7 @@ def chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs,
lcubs = len(var.ROLES.get("wolf cub", ())) lcubs = len(var.ROLES.get("wolf cub", ()))
lrealwolves = len(var.list_players(var.WOLF_ROLES - {"wolf cub"})) lrealwolves = len(var.list_players(var.WOLF_ROLES - {"wolf cub"}))
ltraitors = len(var.ROLES.get("traitor", ())) ltraitors = len(var.ROLES.get("traitor", ()))
return chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs, ltraitors, lpipers, cli, end_game) return chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs, ltraitors, lpipers, lsuccubi, lentranced, cli, end_game)
event = Event("chk_win", {"winner": winner, "message": message, "additional_winners": None}) event = Event("chk_win", {"winner": winner, "message": message, "additional_winners": None})
event.dispatch(var, lpl, lwolves, lrealwolves) event.dispatch(var, lpl, lwolves, lrealwolves)
@ -2778,6 +2794,9 @@ def chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs,
elif winner == "pipers": elif winner == "pipers":
for plr in var.ROLES["piper"]: for plr in var.ROLES["piper"]:
players.append("{0} ({1})".format(plr, var.get_role(plr))) players.append("{0} ({1})".format(plr, var.get_role(plr)))
elif winner == "succubi":
for plr in var.ROLES["succubus"] | var.ENTRANCED:
players.append("{0} ({1})".format(plr, var.get_role(plr)))
debuglog("WIN:", winner) debuglog("WIN:", winner)
debuglog("PLAYERS:", ", ".join(players)) debuglog("PLAYERS:", ", ".join(players))
cli.msg(chan, message) cli.msg(chan, message)
@ -3621,6 +3640,9 @@ def rename_player(cli, prefix, nick):
if prefix in var.CONSECRATING: if prefix in var.CONSECRATING:
var.CONSECRATING.remove(prefix) var.CONSECRATING.remove(prefix)
var.CONSECRATING.add(nick) var.CONSECRATING.add(nick)
if prefix in var.ENTRANCED:
var.ENTRANCED.remove(prefix)
var.ENTRANCED.add(nick)
with var.GRAVEYARD_LOCK: # to be safe with var.GRAVEYARD_LOCK: # to be safe
if prefix in var.LAST_SAID_TIME.keys(): if prefix in var.LAST_SAID_TIME.keys():
var.LAST_SAID_TIME[nick] = var.LAST_SAID_TIME.pop(prefix) var.LAST_SAID_TIME[nick] = var.LAST_SAID_TIME.pop(prefix)
@ -4677,10 +4699,11 @@ def chk_nightdone(cli):
var.OTHER_KILLS, var.PASSED, var.OBSERVED, var.OTHER_KILLS, var.PASSED, var.OBSERVED,
var.HEXED, var.SHAMANS, var.CURSED, var.CHARMERS))) var.HEXED, var.SHAMANS, var.CURSED, var.CHARMERS)))
nightroles = get_roles("seer", "oracle", "harlot", "bodyguard", "guardian angel", nightroles = get_roles("seer", "oracle", "harlot", "succubus", "bodyguard",
"wolf", "werecrow", "alpha wolf", "sorcerer", "hunter", "guardian angel", "wolf", "werecrow", "alpha wolf",
"hag", "shaman", "crazed shaman", "augur", "werekitten", "sorcerer", "hunter", "hag", "shaman", "crazed shaman",
"warlock", "piper", "wolf mystic", "fallen angel") "augur", "werekitten", "warlock", "piper", "wolf mystic",
"fallen angel")
for ghost, against in var.VENGEFUL_GHOSTS.items(): for ghost, against in var.VENGEFUL_GHOSTS.items():
if not against.startswith("!"): if not against.startswith("!"):
@ -5335,7 +5358,8 @@ def shoot(cli, nick, chan, rest):
if not del_player(cli, nick, killer_role = "villager"): # blame explosion on villager's shoddy gun construction or something if not del_player(cli, nick, killer_role = "villager"): # blame explosion on villager's shoddy gun construction or something
return # Someone won. return # Someone won.
def is_safe(nick, victim): # helper function
return nick in var.ENTRANCED and victim in var.ROLES["succubus"]
@cmd("kill", chan=False, pm=True, phases=("night",)) @cmd("kill", chan=False, pm=True, phases=("night",))
def kill(cli, nick, chan, rest): def kill(cli, nick, chan, rest):
@ -5388,10 +5412,16 @@ def kill(cli, nick, chan, rest):
victim = get_victim(cli, nick, victim, False) victim = get_victim(cli, nick, victim, False)
if not victim: if not victim:
return return
if is_safe(nick, victim):
pm(cli, nick, "You may not target a succubus.")
return
if victim2 != None: if victim2 != None:
victim2 = get_victim(cli, nick, victim2, False) victim2 = get_victim(cli, nick, victim2, False)
if not victim2: if not victim2:
return return
if is_safe(nick, victim):
pm(cli, nick, "You may not target a succubus.")
return
if victim == nick or victim2 == nick: if victim == nick or victim2 == nick:
if nick in var.VENGEFUL_GHOSTS.keys(): if nick in var.VENGEFUL_GHOSTS.keys():
@ -5760,9 +5790,11 @@ def pray(cli, nick, chan, rest):
chk_nightdone(cli) chk_nightdone(cli)
@cmd("visit", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("harlot",)) @cmd("visit", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("harlot", "succubus"))
def hvisit(cli, nick, chan, rest): def hvisit(cli, nick, chan, rest):
"""Visit a player. You will die if you visit a wolf or a target of the wolves.""" """Visit a player. You will die if you visit a wolf or a target of the wolves."""
role = var.get_role(nick)
if var.HVISITED.get(nick): if var.HVISITED.get(nick):
pm(cli, nick, ("You are already spending the night "+ pm(cli, nick, ("You are already spending the night "+
"with \u0002{0}\u0002.").format(var.HVISITED[nick])) "with \u0002{0}\u0002.").format(var.HVISITED[nick]))
@ -5779,12 +5811,45 @@ def hvisit(cli, nick, chan, rest):
if check_exchange(cli, nick, victim): if check_exchange(cli, nick, victim):
return return
var.HVISITED[nick] = victim var.HVISITED[nick] = victim
pm(cli, nick, ("You are spending the night with \u0002{0}\u0002. "+ if role == "harlot":
"Have a good time!").format(victim)) pm(cli, nick, ("You are spending the night with \u0002{0}\u0002. "+
if nick != victim: #prevent luck/misdirection totem weirdness "Have a good time!").format(victim))
pm(cli, victim, ("You are spending the night with \u0002{0}"+ if nick != victim: #prevent luck/misdirection totem weirdness
"\u0002. Have a good time!").format(nick)) pm(cli, victim, ("You are spending the night with \u0002{0}"+
debuglog("{0} ({1}) VISIT: {2} ({3})".format(nick, var.get_role(nick), victim, var.get_role(victim))) "\u0002. Have a good time!").format(nick))
else:
var.ENTRANCED.add(victim)
pm(cli, nick, "You are entrancing \u0002{0}\u0002 tonight.".format(victim))
if nick != victim:
pm(cli, victim, ("You are being entranced by \u0002{0}\u0002. From this point on, "
"you must vote along with them or risk dying. For as long as they "
"are alive, you \u0002cannot win with your own team\u0002, but you "
"will win along them if they are alive.").format(nick))
if var.OTHER_KILLS.get(victim) == nick:
pm(cli, victim, "You have retracted your kill on \u0002{0}\u0002.".format(nick)) # placeholder
del var.OTHER_KILLS[victim]
if var.TARGETED.get(victim) == nick:
pm(cli, victim, "You have retracted your targetting on \u0002{0}\u0002.".format(nick))
del var.TARGETED[victim]
if var.SHAMANS.get(victim) == nick and var.TOTEMS[victim] in var.DETRIMENTAL_TOTEMS:
pm(cli, victim, "You have retracted your totem on \u0002{0}\u0002.".format(nick))
del var.SHAMANS[victim]
if victim in var.HEXED and var.LASTHEXED[victim] == nick:
pm(cli, victim, "You have retracted your hex on \u0002{0}\u0002.".format(nick))
var.TOBESILENCED.remove(nick)
var.HEXED.remove(victim)
del var.LASTHEXED[victim]
if nick in var.KILLS.get(victim, ()):
pm(cli, victim, "You have retracted your kill on \u0002{0}\u0002.".format(nick))
var.KILLS[victim].remove(nick)
if not var.KILLS[victim]:
del var.KILLS[victim]
if var.BITE_PREFERENCES.get(victim) == nick:
pm(cli, victim, "You are retracted your bite on \u0002{0}\u0002.".format(nick))
del var.BITE_PREFERENCES[victim]
debuglog("{0} ({1}) VISIT: {2} ({3})".format(nick, role, victim, var.get_role(victim)))
chk_nightdone(cli) chk_nightdone(cli)
def is_fake_nick(who): def is_fake_nick(who):
@ -5870,6 +5935,9 @@ def totem(cli, nick, chan, rest, prefix="You"):
role = var.get_role(nick) role = var.get_role(nick)
if role != "crazed shaman": if role != "crazed shaman":
totem = " of " + var.TOTEMS[nick] totem = " of " + var.TOTEMS[nick]
if is_safe(nick, victim) and var.TOTEMS[nick] in var.DETRIMENTAL_TOTEMS:
pm(cli, nick, "You may not give a totem{0} to a succubus.".format(totem))
return
pm(cli, nick, ("{0} have given a totem{1} to \u0002{2}\u0002.").format(prefix, totem, original_victim)) pm(cli, nick, ("{0} have given a totem{1} to \u0002{2}\u0002.").format(prefix, totem, original_victim))
var.SHAMANS[nick] = (victim, original_victim) var.SHAMANS[nick] = (victim, original_victim)
debuglog("{0} ({1}) TOTEM: {2} ({3})".format(nick, role, victim, totem)) debuglog("{0} ({1}) TOTEM: {2} ({3})".format(nick, role, victim, totem))
@ -5976,6 +6044,9 @@ def bite_cmd(cli, nick, chan, rest):
if not victim: if not victim:
pm(cli, nick, "Please choose who to bite by specifying their nick.") pm(cli, nick, "Please choose who to bite by specifying their nick.")
return return
if is_safe(nick, victim):
pm(cli, nick, "You may not bite a succubus.")
return
vrole = var.get_role(victim) vrole = var.get_role(victim)
actual = choose_target(nick, victim) actual = choose_target(nick, victim)
@ -6179,6 +6250,10 @@ def target(cli, nick, chan, rest):
pm(cli, nick, "You may not target yourself.") pm(cli, nick, "You may not target yourself.")
return return
if is_safe(nick, victim):
pm(cli, nick, "You may not target a succubus.")
return
victim = choose_target(nick, victim) victim = choose_target(nick, victim)
# assassin is a template so it will never get swapped, so don't check for exchanges with it # assassin is a template so it will never get swapped, so don't check for exchanges with it
var.TARGETED[nick] = victim var.TARGETED[nick] = victim
@ -6205,6 +6280,10 @@ def hex_target(cli, nick, chan, rest):
"You cannot hex the same person two nights in a row.").format(victim)) "You cannot hex the same person two nights in a row.").format(victim))
return return
if is_safe(nick, victim):
pm(cli, nick, "You may not hex a succubus.")
return
victim = choose_target(nick, victim) victim = choose_target(nick, victim)
if check_exchange(cli, nick, victim): if check_exchange(cli, nick, victim):
return return
@ -6245,6 +6324,9 @@ def curse(cli, nick, chan, rest):
victim = get_victim(cli, nick, re.split(" +",rest)[0], False) victim = get_victim(cli, nick, re.split(" +",rest)[0], False)
if not victim: if not victim:
return return
if is_safe(nick, victim):
pm(cli, nick, "You may not curse a succubus.")
return
# There may actually be valid strategy in cursing other wolfteam members, # There may actually be valid strategy in cursing other wolfteam members,
# but for now it is not allowed. If someone seems suspicious and shows as # but for now it is not allowed. If someone seems suspicious and shows as
@ -6323,10 +6405,15 @@ def charm(cli, nick, chan, rest):
victim = get_victim(cli, nick, victim, False, True) victim = get_victim(cli, nick, victim, False, True)
if not victim: if not victim:
return return
if is_safe(nick, victim):
pm(cli, nick, "You may not charm a succubus.")
return
if victim2 is not None: if victim2 is not None:
victim2 = get_victim(cli, nick, victim2, False, True) victim2 = get_victim(cli, nick, victim2, False, True)
if not victim2: if not victim2:
return return
if is_safe(nick, victim):
pm(cli, nick, "You may not charm a succubus.")
if victim == victim2: if victim == victim2:
pm(cli, nick, "You must choose two different people.") pm(cli, nick, "You must choose two different people.")
@ -7479,6 +7566,8 @@ def start(cli, nick, chan, forced = False, restart = ""):
var.BLESSED = set() var.BLESSED = set()
var.PRIESTS = set() var.PRIESTS = set()
var.CONSECRATING = set() var.CONSECRATING = set()
var.ENTRANCED = set()
var.ENTRANCED_DYING = set()
var.DEADCHAT_PLAYERS = set() var.DEADCHAT_PLAYERS = set()