Separate bite from kill for alpha wolf.

Alpha now can choose to either bite or kill, but not both. If other
wolves are alive, they can still kill as normal allowing both to happen
that night. Additional interactions were added to deal with visiting
harlots and to not accidentally kill bodyguards/GAs in the event that
they are bitten and would die from wolves (if
BODYGUARD/GUARDIAN_ANGEL_DIES_AT_NIGHT_CHANCE is set).

Things to consider for the future but didn't have time to do yet:
- Should we play a message in channel if alpha wolf successfully bites
  someone at night?
- Avoid killing a bodyguard/GA if Fallen Angel is in play and said
  bodyguard/GA was bitten that night.
- Should wolves avoid automatically killing people bitten on previous
  nights (e.g. a bitten harlot wouldn't get nommed visiting wolves or
  victims, bitten bodyguards/GAs wouldn't die from fallen angel, etc.)?
This commit is contained in:
skizzerz 2015-07-26 23:29:40 -05:00
parent 017811b7db
commit 80f5f7d4e8

View File

@ -3811,23 +3811,15 @@ def transition_day(cli, gameid=0):
del var.ACTIVE_PROTECTIONS[p] del var.ACTIVE_PROTECTIONS[p]
if var.ALPHA_ENABLED: # check for bites if var.ALPHA_ENABLED: # check for bites
for (alpha, desired) in var.BITE_PREFERENCES.items(): for (alpha, target) in var.BITE_PREFERENCES.items():
if len(bywolves) == 0: # bite is now separate but some people may try to double up still, if bitten person is
# nobody to bite, so let them do it again in the future # also being killed by wolves, make the kill not apply
var.ALPHA_WOLVES.remove(alpha) # note that we cannot bite visiting harlots unless they are visiting a wolf,
continue # and lycans/immunized people turn/die instead of being bitten, so keep the kills valid on those
if len(bywolves) == 1: hvisit = var.HVISITED.get(target)
target = tuple(bywolves)[0] if (target in onlybywolves
elif desired in bywolves: and (target not in var.ROLES["harlot"] or not hvisit or var.get_role(hvisit) not in var.WOLFCHAT_ROLES
target = desired or (hvisit in bywolves and hvisit not in protected))
else:
target = random.choice(tuple(bywolves))
targetrole = var.get_role(target)
# do the usual checks; we can only bite those that would otherwise die
# (e.g. block it on visiting harlot, GA/bodyguard, and protection totem)
# also if a lycan is bitten, just turn them into a wolf immediately
if (target not in protected
and (target not in var.ROLES["harlot"] or not var.HVISITED.get(target))
and target not in var.ROLES["lycan"] and target not in var.ROLES["lycan"]
and target not in var.LYCANTHROPES and target not in var.LYCANTHROPES
and target not in var.IMMUNIZED): and target not in var.IMMUNIZED):
@ -3837,26 +3829,19 @@ def transition_day(cli, gameid=0):
killers[target].remove("@wolves") killers[target].remove("@wolves")
if target not in victims: if target not in victims:
victims_set.discard(target) victims_set.discard(target)
if target in victims_set:
# bite was unsuccessful
var.ALPHA_WOLVES.remove(alpha)
else:
var.BITTEN[target] = var.ALPHA_WOLF_NIGHTS var.BITTEN[target] = var.ALPHA_WOLF_NIGHTS
bitten.append(target) bitten.append(target)
else:
# someone else also killed them, mark bite unsuccessful
var.ALPHA_WOLVES.remove(alpha)
else:
# bite was unsuccessful, let them try again
var.ALPHA_WOLVES.remove(alpha)
if alpha in var.ALPHA_WOLVES: if alpha in var.ALPHA_WOLVES:
pm(cli, alpha, "You have bitten \u0002{0}\u0002.".format(target)) pm(cli, alpha, "You have bitten \u0002{0}\u0002.".format(target))
wolfchatwolves = var.list_players(var.WOLFCHAT_ROLES)
for wolf in wolfchatwolves:
if wolf != nick:
pm(cli, wolf, "\u0002{0}\u0002 has bitten \u0002{1}\u0002.".format(nick, victim))
else: else:
pm(cli, alpha, "You tried to bite \u0002{0}\u0002, but it didn't work. Better luck next time!".format(target)) pm(cli, alpha, "You tried to bite \u0002{0}\u0002, but it didn't work. Better luck next time!".format(target))
var.BITE_PREFERENCES = {} var.BITE_PREFERENCES = {}
victims = [] victims = []
# Ensures that special events play for bodyguard and harlot-visiting-victim so that kill can # Ensures that special events play for bodyguard and harlot-visiting-victim so that kill can
@ -3964,6 +3949,9 @@ def transition_day(cli, gameid=0):
for victim in vlist: for victim in vlist:
if victim in var.ROLES["harlot"] and var.HVISITED.get(victim) and victim not in dead and victim in onlybywolves: if victim in var.ROLES["harlot"] and var.HVISITED.get(victim) and victim not in dead and victim in 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 bitten:
message.append("The wolves' selected victim was a harlot, who was not at home last night.") message.append("The wolves' selected victim was a harlot, who was not at home last night.")
novictmsg = False novictmsg = False
elif protected.get(victim) == "totem": elif protected.get(victim) == "totem":
@ -4061,9 +4049,11 @@ def transition_day(cli, gameid=0):
message.append(out) message.append(out)
if victim in var.HVISITED.values() and victim in bywolves: # victim was visited by some harlot and victim was attacked by wolves # 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 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(): for hlt in var.HVISITED.keys():
if var.HVISITED[hlt] == victim: if var.HVISITED[hlt] == victim and hlt not in bitten and hlt not in dead:
message.append(("\u0002{0}\u0002, a \u0002harlot\u0002, made the unfortunate mistake of "+ message.append(("\u0002{0}\u0002, a \u0002harlot\u0002, made the unfortunate mistake of "+
"visiting the victim's house last night and is "+ "visiting the victim's house last night and is "+
"now dead.").format(hlt)) "now dead.").format(hlt))
@ -4075,7 +4065,7 @@ def transition_day(cli, gameid=0):
message.append(random.choice(var.NO_VICTIMS_MESSAGES) + " All villagers, however, have survived.") message.append(random.choice(var.NO_VICTIMS_MESSAGES) + " All villagers, however, have survived.")
for harlot in var.ROLES["harlot"]: for harlot in var.ROLES["harlot"]:
if var.HVISITED.get(harlot) in var.list_players(var.WOLF_ROLES) and harlot not in dead: if var.HVISITED.get(harlot) in var.list_players(var.WOLF_ROLES) and harlot not in dead and harlot not in bitten:
message.append(("\u0002{0}\u0002, a \u0002harlot\u0002, made the unfortunate mistake of "+ message.append(("\u0002{0}\u0002, a \u0002harlot\u0002, made the unfortunate mistake of "+
"visiting a wolf's house last night and is "+ "visiting a wolf's house last night and is "+
"now dead.").format(harlot)) "now dead.").format(harlot))
@ -4083,11 +4073,11 @@ def transition_day(cli, gameid=0):
onlybywolves.add(harlot) onlybywolves.add(harlot)
dead.append(harlot) dead.append(harlot)
for bodyguard in var.ROLES["bodyguard"]: for bodyguard in var.ROLES["bodyguard"]:
if var.GUARDED.get(bodyguard) in var.list_players(var.WOLF_ROLES) and bodyguard not in dead: if var.GUARDED.get(bodyguard) in var.list_players(var.WOLF_ROLES) and bodyguard not in dead and bodyguard not in bitten:
bywolves.add(bodyguard)
onlybywolves.add(bodyguard)
r = random.random() r = random.random()
if r < var.BODYGUARD_DIES_CHANCE: if r < var.BODYGUARD_DIES_CHANCE:
bywolves.add(bodyguard)
onlybywolves.add(bodyguard)
if var.ROLE_REVEAL == "on": if var.ROLE_REVEAL == "on":
message.append(("\u0002{0}\u0002, a \u0002bodyguard\u0002, "+ message.append(("\u0002{0}\u0002, a \u0002bodyguard\u0002, "+
"made the unfortunate mistake of guarding a wolf "+ "made the unfortunate mistake of guarding a wolf "+
@ -4098,11 +4088,11 @@ def transition_day(cli, gameid=0):
"last night, and is now dead.").format(bodyguard)) "last night, and is now dead.").format(bodyguard))
dead.append(bodyguard) dead.append(bodyguard)
for gangel in var.ROLES["guardian angel"]: for gangel in var.ROLES["guardian angel"]:
if var.GUARDED.get(gangel) in var.list_players(var.WOLF_ROLES) and gangel not in dead: if var.GUARDED.get(gangel) in var.list_players(var.WOLF_ROLES) and gangel not in dead and gangel not in bitten:
bywolves.add(gangel)
onlybywolves.add(gangel)
r = random.random() r = random.random()
if r < var.GUARDIAN_ANGEL_DIES_CHANCE: if r < var.GUARDIAN_ANGEL_DIES_CHANCE:
bywolves.add(gangel)
onlybywolves.add(gangel)
if var.ROLE_REVEAL == "on": if var.ROLE_REVEAL == "on":
message.append(("\u0002{0}\u0002, a \u0002guardian angel\u0002, "+ message.append(("\u0002{0}\u0002, a \u0002guardian angel\u0002, "+
"made the unfortunate mistake of guarding a wolf "+ "made the unfortunate mistake of guarding a wolf "+
@ -4167,6 +4157,9 @@ def transition_day(cli, gameid=0):
for chump in bitten: for chump in bitten:
if chump not in dead and chump not in var.WOLF_ROLES: if chump not in dead and chump not in var.WOLF_ROLES:
if chump in var.ROLES["harlot"] and var.HVISITED.get(chump):
pm(cli, chump, "While out visiting last night, you were overcome by a fierce-looking wolf and bitten on your neck...")
else:
pm(cli, chump, "You woke up today feeling light-headed, and you notice some odd bite marks on your neck...") pm(cli, chump, "You woke up today feeling light-headed, and you notice some odd bite marks on your neck...")
for deadperson in dead: # kill each player, but don't end the game if one group outnumbers another for deadperson in dead: # kill each player, but don't end the game if one group outnumbers another
@ -4215,6 +4208,9 @@ def chk_nightdone(cli):
if var.DISEASED_WOLVES: if var.DISEASED_WOLVES:
nightroles = [p for p in nightroles if p not in var.list_players(("wolf", "alpha wolf", "werekitten", "wolf mystic", "fallen angel"))] nightroles = [p for p in nightroles if p not in var.list_players(("wolf", "alpha wolf", "werekitten", "wolf mystic", "fallen angel"))]
elif var.ALPHA_ENABLED:
# don't re-add alphas here since they can only kill *or* bite, not both
actedcount += len([p for p in var.ALPHA_WOLVES if p in var.ROLES["alpha wolf"]])
for p in var.HUNTERS: for p in var.HUNTERS:
# only remove one instance of their name if they have used hunter ability, in case they have templates # only remove one instance of their name if they have used hunter ability, in case they have templates
@ -4257,7 +4253,7 @@ def chk_nightdone(cli):
kills.add(ls) kills.add(ls)
# check if wolves are actually agreeing # check if wolves are actually agreeing
# allow len(kills) == 0 through as that means that crow was dumb and observed instead # allow len(kills) == 0 through as that means that crow was dumb and observed instead
# of killing or something, or weird cases where there are no wolves at night # of killingor alpha wolf was alone and chose to bite instead of kill
if not var.ANGRY_WOLVES and len(kills) > 1: if not var.ANGRY_WOLVES and len(kills) > 1:
return return
elif var.ANGRY_WOLVES and (len(kills) == 1 or len(kills) > 2): elif var.ANGRY_WOLVES and (len(kills) == 1 or len(kills) > 2):
@ -4716,22 +4712,18 @@ def retract(cli, nick, chan, rest):
elif role == "hunter" and nick in var.HUNTERS and nick not in var.OTHER_KILLS.keys(): elif role == "hunter" and nick in var.HUNTERS and nick not in var.OTHER_KILLS.keys():
return return
what = re.split(" +",rest)[0].strip().lower() if role in var.WOLF_ROLES and nick in var.KILLS.keys():
if not what or what not in ("kill", "bite"):
what = "kill"
if what == "kill" and role in var.WOLF_ROLES and nick in var.KILLS.keys():
del var.KILLS[nick] del var.KILLS[nick]
pm(cli, nick, "You have retracted your kill.") pm(cli, nick, "You have retracted your kill.")
wolfchatwolves = var.list_players(var.WOLFCHAT_ROLES) wolfchatwolves = var.list_players(var.WOLFCHAT_ROLES)
for wolf in wolfchatwolves: for wolf in wolfchatwolves:
if wolf != nick: if wolf != nick:
pm(cli, wolf, "\u0002{0}\u0002 has retracted their kill.".format(nick)) pm(cli, wolf, "\u0002{0}\u0002 has retracted their kill.".format(nick))
elif what == "kill" and role not in var.WOLF_ROLES and nick in var.OTHER_KILLS.keys(): elif role not in var.WOLF_ROLES and nick in var.OTHER_KILLS.keys():
del var.OTHER_KILLS[nick] del var.OTHER_KILLS[nick]
var.HUNTERS.remove(nick) var.HUNTERS.remove(nick)
pm(cli, nick, "You have retracted your kill.") pm(cli, nick, "You have retracted your kill.")
elif what == "bite" and role == "alpha wolf" and nick in var.BITE_PREFERENCES.keys(): elif role == "alpha wolf" and nick in var.BITE_PREFERENCES.keys():
del var.BITE_PREFERENCES[nick] del var.BITE_PREFERENCES[nick]
var.ALPHA_WOLVES.remove(nick) var.ALPHA_WOLVES.remove(nick)
pm(cli, nick, "You have decided not to bite anyone tonight.") pm(cli, nick, "You have decided not to bite anyone tonight.")
@ -4739,8 +4731,10 @@ def retract(cli, nick, chan, rest):
for wolf in wolfchatwolves: for wolf in wolfchatwolves:
if wolf != nick: if wolf != nick:
pm(cli, wolf, "\u0002{0}\u0002 has decided not to bite anyone tonight.".format(nick)) pm(cli, wolf, "\u0002{0}\u0002 has decided not to bite anyone tonight.".format(nick))
elif role == "alpha wolf" and var.ALPHA_ENABLED:
pm(cli, nick, "You have not chosen to kill or bite anyone yet.")
else: else:
pm(cli, nick, "You have not chosen to {0} anyone yet.".format(what)) pm(cli, nick, "You have not chosen to kill anyone yet.")
return return
if var.PHASE != "day": if var.PHASE != "day":
@ -4890,6 +4884,9 @@ def kill(cli, nick, chan, rest):
if role in wolfroles and var.DISEASED_WOLVES: if role in wolfroles and var.DISEASED_WOLVES:
pm(cli, nick, "You are feeling ill, and are unable to kill anyone tonight.") pm(cli, nick, "You are feeling ill, and are unable to kill anyone tonight.")
return return
if role == "alpha wolf" and nick in var.BITE_PREFERENCES:
pm(cli, nick, 'You have chosen to bite someone tonight and cannot participate in the kill. Use "retract" if you want to not bite anyone tonight.')
return
pieces = re.split(" +",rest) pieces = re.split(" +",rest)
victim = pieces[0] victim = pieces[0]
victim2 = None victim2 = None
@ -5315,35 +5312,39 @@ def bite_cmd(cli, nick, chan, rest):
if not var.ALPHA_ENABLED: if not var.ALPHA_ENABLED:
pm(cli, nick, "You may only bite someone after another wolf has died during the day.") pm(cli, nick, "You may only bite someone after another wolf has died during the day.")
return return
if var.DISEASED_WOLVES:
pm(cli, nick, "You are feeling ill, and are unable to kill anyone tonight.")
return
victim = get_victim(cli, nick, re.split(" +",rest)[0], False, False) victim = get_victim(cli, nick, re.split(" +",rest)[0], False, False)
vrole = None
# also mark the victim as the kill target
if victim:
kill.caller(cli, nick, chan, rest)
if var.ANGRY_WOLVES:
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
vrole = var.get_role(victim) vrole = var.get_role(victim)
actual = choose_target(nick, victim)
if vrole in var.WOLFCHAT_ROLES: if vrole in var.WOLFCHAT_ROLES:
pm(cli, nick, "You may not bite other wolves.") pm(cli, nick, "You may not bite other wolves.")
return return
if nick not in var.ALPHA_WOLVES: if nick not in var.ALPHA_WOLVES:
var.ALPHA_WOLVES.append(nick) var.ALPHA_WOLVES.append(nick)
# this means that if victim is chosen by wolves, they will get bitten
# if victim isn't chosen, then a target is chosen at random from the victims
# (really only matters if wolves are angry; since there's only one target otherwise)
var.BITE_PREFERENCES[nick] = victim
if victim: var.BITE_PREFERENCES[nick] = actual
pm(cli, nick, "You have chosen to bite \u0002{0}\u0002. If that player is not selected to be killed, you will bite one of the wolf targets at random instead.".format(victim))
else: # biting someone makes them ineligible to participate in the kill
pm(cli, nick, "You have chosen to bite tonight. Whomever the wolves select to be killed tonight will be bitten instead.") if nick in var.KILLS:
debuglog("{0} ({1}) BITE: {2} ({3})".format(nick, var.get_role(nick), victim if victim else "wolves' target", vrole if vrole else "unknown")) del var.KILLS[nick]
pm(cli, nick, "You have chosen to bite \u0002{0}\u0002.".format(victim))
wolfchat = var.list_players(var.WOLFCHAT_ROLES)
for wolf in wolfchat:
if wolf != nick:
pm(cli, wolf, "\u0002{0}\u0002 has chosen to bite \u0002{1}\u0002.".format(nick, victim))
debuglog("{0} ({1}) BITE: {2} ({3})".format(nick, var.get_role(nick), actual, var.get_role(actual)))
@cmd("pass", chan=False, pm=True, playing=True, phases=("night",), roles=("hunter","harlot","bodyguard","guardian angel","turncoat","warlock","piper")) @cmd("pass", chan=False, pm=True, playing=True, phases=("night",), roles=("hunter","harlot","bodyguard","guardian angel","turncoat","warlock","piper"))
def pass_cmd(cli, nick, chan, rest): def pass_cmd(cli, nick, chan, rest):
@ -6066,7 +6067,7 @@ def transition_night(cli):
elif var.ANGRY_WOLVES and role in var.WOLF_ROLES and role != "wolf cub": elif var.ANGRY_WOLVES and role in var.WOLF_ROLES and role != "wolf cub":
pm(cli, wolf, 'You are \u0002angry\u0002 tonight, and may kill two targets by using "kill <nick1> and <nick2>".') pm(cli, wolf, 'You are \u0002angry\u0002 tonight, and may kill two targets by using "kill <nick1> and <nick2>".')
if var.ALPHA_ENABLED and role == "alpha wolf" and wolf not in var.ALPHA_WOLVES: if var.ALPHA_ENABLED and role == "alpha wolf" and wolf not in var.ALPHA_WOLVES:
pm(cli, wolf, ('You may use "bite <nick>" tonight in order to turn the wolves\' target into a wolf instead of killing them. ' + pm(cli, wolf, ('You may use "bite <nick>" tonight in order to turn your target into a wolf instead of participating in tonight\'s kill. ' +
'They will turn into a wolf in {0} night{1}.').format(var.ALPHA_WOLF_NIGHTS, 's' if var.ALPHA_WOLF_NIGHTS > 1 else '')) 'They will turn into a wolf in {0} night{1}.').format(var.ALPHA_WOLF_NIGHTS, 's' if var.ALPHA_WOLF_NIGHTS > 1 else ''))
for seer in var.list_players(("seer", "oracle", "augur")): for seer in var.list_players(("seer", "oracle", "augur")):