def on_del_player(evt, var, player, all_roles, death_triggers): if not death_triggers or "mad scientist" not in all_roles: return target1, target2 = _get_targets(var, get_players(), player) prots1 = try_protection(var, target1, player, "mad scientist", "mad_scientist_fail") prots2 = try_protection(var, target2, player, "mad scientist", "mad_scientist_fail") if prots1: channels.Main.send(*prots1) if prots2: channels.Main.send(*prots2) kill1 = prots1 is None and add_dying( var, target1, killer_role="mad scientist", reason="mad_scientist") kill2 = prots2 is None and target1 is not target2 and add_dying( var, target2, killer_role="mad scientist", reason="mad_scientist") role1 = kill1 and get_reveal_role(target1) role2 = kill2 and get_reveal_role(target2) if kill1 and kill2: to_send = "mad_scientist_kill" debuglog( player.nick, "(mad scientist) KILL: {0} ({1}) - {2} ({3})".format( target1, get_main_role(target1), target2, get_main_role(target2))) elif kill1: to_send = "mad_scientist_kill_single" debuglog( player.nick, "(mad scientist) KILL: {0} ({1})".format(target1, get_main_role(target1))) elif kill2: to_send = "mad_scientist_kill_single" debuglog( player.nick, "(mad scientist) KILL: {0} ({1})".format(target2, get_main_role(target2))) # swap the targets around to show the proper target target1, target2 = target2, target1 role1, role2 = role2, role1 else: to_send = "mad_scientist_fail" debuglog(player.nick, "(mad scientist) KILL FAIL") if to_send != "mad_scientist_fail" and var.ROLE_REVEAL not in ("on", "team"): to_send += "_no_reveal" channels.Main.send(messages[to_send].format(player, target1, role1, target2, role2))
def on_del_player(evt, var, player, all_roles, death_triggers): if not death_triggers or "mad scientist" not in all_roles: return target1, target2 = _get_targets(var, get_players(), player) prots1 = try_protection(var, target1, player, "mad scientist", "mad_scientist_fail") prots2 = try_protection(var, target2, player, "mad scientist", "mad_scientist_fail") if prots1: channels.Main.send(*prots1) if prots2: channels.Main.send(*prots2) kill1 = prots1 is None and add_dying(var, target1, killer_role="mad scientist", reason="mad_scientist") kill2 = prots2 is None and target1 is not target2 and add_dying(var, target2, killer_role="mad scientist", reason="mad_scientist") if kill1: if kill2: if var.ROLE_REVEAL in ("on", "team"): r1 = get_reveal_role(target1) an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else "" r2 = get_reveal_role(target2) an2 = "n" if r2.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["mad_scientist_kill"].format(player, target1, an1, r1, target2, an2, r2) else: tmsg = messages["mad_scientist_kill_no_reveal"].format(player, target1, target2) channels.Main.send(tmsg) debuglog(player.nick, "(mad scientist) KILL: {0} ({1}) - {2} ({3})".format(target1, get_main_role(target1), target2, get_main_role(target2))) else: if var.ROLE_REVEAL in ("on", "team"): r1 = get_reveal_role(target1) an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["mad_scientist_kill_single"].format(player, target1, an1, r1) else: tmsg = messages["mad_scientist_kill_single_no_reveal"].format(player, target1) channels.Main.send(tmsg) debuglog(player.nick, "(mad scientist) KILL: {0} ({1})".format(target1, get_main_role(target1))) else: if kill2: if var.ROLE_REVEAL in ("on", "team"): r2 = get_reveal_role(target2) an2 = "n" if r2.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["mad_scientist_kill_single"].format(player, target2, an2, r2) else: tmsg = messages["mad_scientist_kill_single_no_reveal"].format(player, target2) channels.Main.send(tmsg) debuglog(player.nick, "(mad scientist) KILL: {0} ({1})".format(target2, get_main_role(target2))) else: tmsg = messages["mad_scientist_fail"].format(player) channels.Main.send(tmsg) debuglog(player.nick, "(mad scientist) KILL FAIL")
def on_del_player(evt, var, player, all_roles, death_triggers): if player in TARGETED.values(): for x, y in list(TARGETED.items()): if y is player: del TARGETED[x] PREV_ACTED.discard(x) if death_triggers and "assassin" in all_roles and player in TARGETED: target = TARGETED[player] del TARGETED[player] PREV_ACTED.discard(player) if target in get_players(): protected = try_protection(var, target, player, "assassin", "assassin_fail") if protected is not None: channels.Main.send(*protected) return if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(target) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" message = messages["assassin_success"].format(player, target, an, role) else: message = messages["assassin_success_no_reveal"].format(player, target) channels.Main.send(message) debuglog("{0} (assassin) ASSASSINATE: {1} ({2})".format(player, target, get_main_role(target))) add_dying(var, target, killer_role=evt.params.main_role, reason="assassin")
def on_del_player(evt, var, player, all_roles, death_triggers): for h, v in list(KILLS.items()): if v is player: h.send(messages["hunter_discard"]) del KILLS[h] elif h is player: del KILLS[h] if death_triggers and "dullahan" in all_roles: pl = get_players() with TARGETS[player].intersection(pl) as targets: if targets: target = random.choice(list(targets)) protected = try_protection(var, target, player, "dullahan", "dullahan_die") if protected is not None: channels.Main.send(*protected) return if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(target) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" channels.Main.send(messages["dullahan_die_success"].format(player, target, an, role)) else: channels.Main.send(messages["dullahan_die_success_noreveal"].format(player, target)) debuglog("{0} (dullahan) DULLAHAN ASSASSINATE: {1} ({2})".format(player, target, get_main_role(target))) add_dying(var, target, "dullahan", "dullahan_die")
def on_chk_decision_lynch5(evt, var, voters): votee = evt.data["votee"] if votee in DESPERATION: # Also kill the very last person to vote them, unless they voted themselves last in which case nobody else dies target = voters[-1] if target is not votee: protected = try_protection(var, target, attacker=votee, attacker_role="shaman", reason="totem_desperation") if protected is not None: channels.Main.send(*protected) return if var.ROLE_REVEAL in ("on", "team"): r1 = get_reveal_role(target) an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["totem_desperation"].format( votee, target, an1, r1) else: tmsg = messages["totem_desperation_no_reveal"].format( votee, target) channels.Main.send(tmsg) add_dying(var, target, killer_role="shaman", reason="totem_desperation")
def on_chk_decision_lynch5(evt, cli, var, voters): votee = evt.data["votee"] if votee in DESPERATION: # Also kill the very last person to vote them, unless they voted themselves last in which case nobody else dies target = voters[-1] if target != votee: prots = deque(var.ACTIVE_PROTECTIONS[target]) while len(prots) > 0: # an event can read the current active protection and cancel the totem # if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS # so that it cannot be used again (if the protection is meant to be usable once-only) desp_evt = Event("desperation_totem", {}) if not desp_evt.dispatch(cli, var, votee, target, prots[0]): return prots.popleft() if var.ROLE_REVEAL in ("on", "team"): r1 = get_reveal_role(users._get(target)) # FIXME an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["totem_desperation"].format( votee, target, an1, r1) else: tmsg = messages["totem_desperation_no_reveal"].format( votee, target) cli.msg(botconfig.CHANNEL, tmsg) # we lie to this function so it doesn't devoice the player yet. instead, we'll let the call further down do it evt.data["deadlist"].append(target) better_deadlist = [users._get(p) for p in evt.data["deadlist"]] # FIXME target_user = users._get(target) # FIXME evt.params.del_player(target_user, end_game=False, killer_role="shaman", deadlist=better_deadlist, ismain=False)
def kill_players(var, *, end_game: bool = True) -> bool: """ Kill all players marked as dying. This function is not re-entrant; do not call it inside of a del_player or kill_players event listener. This function does not print anything to the channel; code which calls add_dying should print things as appropriate. :param var: The game state :param end_game: Whether or not to check for win conditions and perform state transitions (temporary) :returns: True if the game is ending (temporary) """ t = time.time() with var.GRAVEYARD_LOCK: # FIXME if not var.GAME_ID or var.GAME_ID > t: # either game ended, or a new game has started return dead = set() while DYING: player, (killer_role, reason, death_triggers) = DYING.popitem() main_role = get_main_role(player) reveal_role = get_reveal_role(player) all_roles = get_all_roles(player) # kill them off del var.MAIN_ROLES[player] for role in all_roles: var.ROLES[role].remove(player) dead.add(player) # Don't track players that quit before the game started if var.PHASE != "join": var.DEAD.add(player) # notify listeners that the player died for possibility of chained deaths evt = Event("del_player", {}, killer_role=killer_role, main_role=main_role, reveal_role=reveal_role, reason=reason) evt_death_triggers = death_triggers and var.PHASE in var.GAME_PHASES evt.dispatch(var, player, all_roles, evt_death_triggers) # give roles/modes an opportunity to adjust !stats now that all deaths have resolved evt = Event("reconfigure_stats", {"new": []}) newstats = set() for rs in var.ROLE_STATS: d = Counter(dict(rs)) evt.data["new"] = [d] evt.dispatch(var, d, "del_player") for v in evt.data["new"]: if min(v.values()) >= 0: newstats.add(frozenset(v.items())) var.ROLE_STATS = frozenset(newstats) # notify listeners that all deaths have resolved # FIXME: end_game is a temporary hack until we move state transitions into the event loop # (priority 10 listener sets prevent_default if end_game=True and game is ending; that's another temporary hack) # Once hacks are removed, this function will not have any return value and the end_game kwarg will go away evt = Event("kill_players", {}, end_game=end_game) return not evt.dispatch(var, dead)
def on_del_player(evt, var, player, all_roles, death_triggers): for h, v in list(KILLS.items()): if v is player: h.send(messages["hunter_discard"]) del KILLS[h] elif h is player: del KILLS[h] if death_triggers and "dullahan" in all_roles: pl = get_players() with TARGETS[player].intersection(pl) as targets: if targets: target = random.choice(list(targets)) protected = try_protection(var, target, player, "dullahan", "dullahan_die") if protected is not None: channels.Main.send(*protected) return if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(target) channels.Main.send(messages["dullahan_die_success"].format( player, target, role)) else: channels.Main.send( messages["dullahan_die_success_noreveal"].format( player, target)) debuglog( "{0} (dullahan) DULLAHAN ASSASSINATE: {1} ({2})".format( player, target, get_main_role(target))) add_dying(var, target, "dullahan", "dullahan_die")
def on_del_player(evt, var, user, mainrole, allroles, death_triggers): for h, v in list(KILLS.items()): if v is user: h.send(messages["hunter_discard"]) del KILLS[h] elif h is user: del KILLS[h] if death_triggers and "dullahan" in allroles: pl = evt.data["pl"] targets = TARGETS[user].intersection(pl) if targets: target = random.choice(list(targets)) prots = deque(var.ACTIVE_PROTECTIONS[target.nick]) aevt = Event("assassinate", { "pl": evt.data["pl"], "target": target }, del_player=evt.params.del_player, deadlist=evt.params.deadlist, original=evt.params.original, refresh_pl=evt.params.refresh_pl, message_prefix="dullahan_die_", source="dullahan", killer=user, killer_mainrole=mainrole, killer_allroles=allroles, prots=prots) while len(prots) > 0: # an event can read the current active protection and cancel or redirect the assassination # if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS # so that it cannot be used again (if the protection is meant to be usable once-only) if not aevt.dispatch(var, user, target, prots[0]): evt.data["pl"] = aevt.data["pl"] if target is not aevt.data["target"]: target = aevt.data["target"] prots = deque(var.ACTIVE_PROTECTIONS[target.nick]) aevt.params.prots = prots continue return prots.popleft() if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(target) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" channels.Main.send(messages["dullahan_die_success"].format( user, target, an, role)) else: channels.Main.send( messages["dullahan_die_success_noreveal"].format( user, target)) debuglog("{0} (dullahan) DULLAHAN ASSASSINATE: {1} ({2})".format( user, target, get_main_role(target))) evt.params.del_player(target, end_game=False, killer_role="dullahan", deadlist=evt.params.deadlist, original=evt.params.original, ismain=False) evt.data["pl"] = evt.params.refresh_pl(pl)
def on_transition_day_resolve_end(evt, var, victims): for victim in list(evt.data["dead"]): if GUNNERS.get( victim) and "@wolves" in evt.data["killers"][victim]: if random.random() < var.GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE: # pick a random wolf to be shot wolves = [ wolf for wolf in get_players(Wolf & Killer) if wolf not in evt.data["dead"] ] if wolves: shot = random.choice(wolves) event = Event("gun_shoot", {"hit": True, "kill": True}) event.dispatch(var, victim, shot, rolename) GUNNERS[victim] -= 1 # deduct the used bullet if event.data["hit"] and event.data["kill"]: to_send = "gunner_killed_wolf_overnight_no_reveal" if var.ROLE_REVEAL in ("on", "team"): to_send = "gunner_killed_wolf_overnight" evt.data["message"][victim].append( messages[to_send].format( victim, shot, get_reveal_role(shot))) evt.data["dead"].append(shot) evt.data["killers"][shot].append(victim) elif event.data["hit"]: # shot hit, but didn't kill evt.data["message"][victim].append( messages["gunner_shoot_overnight_hit"].format( victim)) add_absent(var, shot, "wounded") else: # shot was fired and missed evt.data["message"][victim].append( messages["gunner_shoot_overnight_missed"]. format(victim)) # let wolf steal gun if the gunner has any bullets remaining # this gives the looter the "wolf gunner" secondary role # if the wolf gunner role isn't loaded, guns cannot be stolen regardless of var.WOLF_STEALS_GUN if var.WOLF_STEALS_GUN and GUNNERS[ victim] and "wolf gunner" in _rolestate: possible = get_players(Wolf & Killer) random.shuffle(possible) for looter in possible: if looter not in evt.data["dead"]: break else: return # no live wolf, nothing to do here _rolestate["wolf gunner"]["GUNNERS"][ looter] = _rolestate["wolf gunner"]["GUNNERS"].get( looter, 0) + 1 del GUNNERS[victim] var.ROLES["wolf gunner"].add(looter) looter.send(messages["wolf_gunner"].format(victim))
def on_del_player(evt, var, user, mainrole, allroles, death_triggers): global ALL_SUCC_IDLE if "succubus" not in allroles: return if user in VISITED: # if it's night, also unentrance the person they visited if var.PHASE == "night" and var.GAMEPHASE == "night": if VISITED[user] in ENTRANCED: ENTRANCED.discard(VISITED[user]) ENTRANCED_DYING.discard(VISITED[user]) VISITED[user].send(messages["entranced_revert_win"]) del VISITED[user] # if all succubi are dead, one of two things happen: # 1. if all succubi idled out (every last one of them), un-entrance people # 2. otherwise, kill all entranced people immediately, they still remain entranced (and therefore lose) # death_triggers is False for an idle-out, so we use that to determine which it is if death_triggers: ALL_SUCC_IDLE = False if not get_all_players(("succubus",)): entranced_alive = ENTRANCED.difference(evt.params.deadlist).intersection(evt.data["pl"]) if ALL_SUCC_IDLE: while ENTRANCED: e = ENTRANCED.pop() e.send(messages["entranced_revert_win"]) elif entranced_alive: msg = [] # Run in two loops so we can play the message for everyone dying at once before we actually # kill any of them off (if we killed off first, the message order would be wrong wrt death chains) comma = "" if var.ROLE_REVEAL in ("on", "team"): comma = "," for e in entranced_alive: if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(e) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" msg.append("\u0002{0}\u0002, a{1} \u0002{2}\u0002".format(e, an, role)) else: msg.append("\u0002{0}\u0002".format(e)) if len(msg) == 1: channels.Main.send(messages["succubus_die_kill"].format(msg[0] + comma)) elif len(msg) == 2: channels.Main.send(messages["succubus_die_kill"].format(msg[0] + comma + " and " + msg[1] + comma)) else: channels.Main.send(messages["succubus_die_kill"].format(", ".join(msg[:-1]) + ", and " + msg[-1] + comma)) for e in entranced_alive: # to ensure we do not double-kill someone, notify all child deaths that we'll be # killing off everyone else that is entranced so they don't need to bother dlc = list(evt.params.deadlist) dlc.extend(entranced_alive - {e}) debuglog("{0} (succubus) SUCCUBUS DEATH KILL: {1} ({2})".format(user, e, get_main_role(e))) evt.params.del_player(e, end_game=False, killer_role="succubus", deadlist=dlc, original=evt.params.original, ismain=False) evt.data["pl"] = evt.params.refresh_pl(evt.data["pl"]) ENTRANCED_DYING.clear()
def kill_off_dying_players(evt, var, victims): for victim in DYING: if victim not in evt.data["dead"]: evt.data["novictmsg"] = False evt.data["dead"].append(victim) to_send = "death_no_reveal" if var.ROLE_REVEAL in ("on", "team"): to_send = "death" evt.data["message"][victim].append(messages[to_send].format( victim, get_reveal_role(victim)))
def on_transition_day_resolve_end(evt, var, victims): for victim in victims: if victim in evt.data["dead"] and victim in VISITED.values() and "@wolves" in evt.data["killers"][victim]: for hlt in VISITED: if VISITED[hlt] is victim and hlt not in evt.data["dead"]: role = get_reveal_role(hlt) to_send = "visited_victim_noreveal" if var.ROLE_REVEAL in ("on", "team"): to_send = "visited_victim" evt.data["message"][hlt].append(messages[to_send].format(hlt, role)) evt.data["dead"].append(hlt) evt.data["killers"][hlt].append("@wolves")
def kill_off_dying_players(evt, var, victims): for victim in DYING: if victim not in evt.data["dead"]: evt.data["novictmsg"] = False evt.data["dead"].append(victim) if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(victim) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" evt.data["message"][victim].append(messages["death"].format(victim, an, role)) else: evt.data["message"][victim].append(messages["death_no_reveal"].format(victim))
def on_del_player(evt, var, player, mainrole, allroles, death_triggers): if player in TARGETED.values(): for x, y in list(TARGETED.items()): if y is player: del TARGETED[x] PREV_ACTED.discard(x) if death_triggers and "assassin" in allroles and player in TARGETED: target = TARGETED[player] del TARGETED[player] PREV_ACTED.discard(player) if target in evt.data["pl"]: prots = deque(var.ACTIVE_PROTECTIONS[target.nick]) aevt = Event("assassinate", {"pl": evt.data["pl"], "target": target}, del_player=evt.params.del_player, deadlist=evt.params.deadlist, original=evt.params.original, refresh_pl=evt.params.refresh_pl, message_prefix="assassin_fail_", source="assassin", killer=player, killer_mainrole=mainrole, killer_allroles=allroles, prots=prots) while len(prots) > 0: # an event can read the current active protection and cancel the assassination # if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS # so that it cannot be used again (if the protection is meant to be usable once-only) if not aevt.dispatch(var, player, target, prots[0]): pl = aevt.data["pl"] if target is not aevt.data["target"]: target = aevt.data["target"] prots = deque(var.ACTIVE_PROTECTIONS[target.nick]) aevt.params.prots = prots continue break prots.popleft() if not prots: if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(target) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" message = messages["assassin_success"].format(player, target, an, role) else: message = messages["assassin_success_no_reveal"].format(player, target) channels.Main.send(message) debuglog("{0} (assassin) ASSASSINATE: {1} ({2})".format(player, target, get_main_role(target))) evt.params.del_player(target, end_game=False, killer_role=mainrole, deadlist=evt.params.deadlist, original=evt.params.original, ismain=False) evt.data["pl"] = evt.params.refresh_pl(aevt.data["pl"])
def on_lynch(evt, var, votee, voters): if votee in DESPERATION: # Also kill the very last person to vote them, unless they voted themselves last in which case nobody else dies target = voters[-1] if target is not votee: protected = try_protection(var, target, attacker=votee, attacker_role="shaman", reason="totem_desperation") if protected is not None: channels.Main.send(*protected) return to_send = "totem_desperation_no_reveal" if var.ROLE_REVEAL in ("on", "team"): to_send = "totem_desperation" channels.Main.send(messages[to_send].format(votee, target, get_reveal_role(target))) status.add_dying(var, target, killer_role="shaman", reason="totem_desperation")
def on_transition_day_resolve6(evt, 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 evt.data["protected"].get(victim): return if victim in var.ROLES["lycan"] and victim in evt.data[ "onlybywolves"] and victim.nick not in var.IMMUNIZED: return # END checks to remove if victim.nick in RETRIBUTION: killers = list(evt.data["killers"].get(victim, [])) loser = None while killers: loser = random.choice(killers) if loser in evt.data["dead"] or victim is loser: killers.remove(loser) continue break if loser in evt.data["dead"] or victim is loser: loser = None ret_evt = Event("retribution_kill", {"target": loser, "message": []}) ret_evt.dispatch(var, victim, loser) loser = ret_evt.data["target"] evt.data["message"].extend(ret_evt.data["message"]) if loser in evt.data["dead"] or victim is loser: loser = None if loser is not None: prots = deque(var.ACTIVE_PROTECTIONS[loser.nick]) while len(prots) > 0: # an event can read the current active protection and cancel the totem # if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS # so that it cannot be used again (if the protection is meant to be usable once-only) ret_evt = Event("retribution_totem", {"message": []}) if not ret_evt.dispatch(var, victim, loser, prots[0]): evt.data["message"].extend(ret_evt.data["message"]) return prots.popleft() evt.data["dead"].append(loser) if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(loser) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" evt.data["message"].append(messages["totem_death"].format( victim, loser, an, role)) else: evt.data["message"].append( messages["totem_death_no_reveal"].format(victim, loser))
def on_del_player(evt, var, player, mainrole, allroles, death_triggers): if death_triggers and player in LOVERS: lovers = set(LOVERS[player]) for lover in lovers: if lover not in evt.data["pl"]: continue # already died somehow if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(lover) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" message = messages["lover_suicide"].format(lover, an, role) else: message = messages["lover_suicide_no_reveal"].format(lover) channels.Main.send(message) debuglog("{0} ({1}) LOVE SUICIDE: {2} ({3})".format(lover, get_main_role(lover), player, mainrole)) evt.params.del_player(lover, end_game=False, killer_role=evt.params.killer_role, deadlist=evt.params.deadlist, original=evt.params.original, ismain=False) evt.data["pl"] = evt.params.refresh_pl(evt.data["pl"])
def on_del_player(evt, var, player, all_roles, death_triggers): MATCHMAKERS.discard(player) if death_triggers and player in LOVERS: lovers = set(LOVERS[player]) for lover in lovers: if lover not in get_players(): continue # already died somehow if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(lover) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" message = messages["lover_suicide"].format(lover, an, role) else: message = messages["lover_suicide_no_reveal"].format(lover) channels.Main.send(message) debuglog("{0} ({1}) LOVE SUICIDE: {2} ({3})".format(lover, get_main_role(lover), player, evt.params.main_role)) add_dying(var, lover, killer_role=evt.params.killer_role, reason="lover_suicide")
def on_transition_day_resolve_end(evt, var, victims): for victim in victims: if victim in evt.data["dead"] and victim in VISITED.values( ) and "@wolves" in evt.data["killers"][victim]: for succubus in VISITED: if VISITED[succubus] is victim and succubus not in evt.data[ "dead"]: if var.ROLE_REVEAL in ("on", "team"): evt.data["message"][succubus].append( messages["visited_victim"].format( succubus, get_reveal_role(succubus))) else: evt.data["message"][succubus].append( messages["visited_victim_noreveal"].format( succubus)) evt.data["dead"].append(succubus)
def kill_players(var, *, end_game: bool = True) -> bool: """ Kill all players marked as dying. This function is not re-entrant; do not call it inside of a del_player or kill_players event listener. This function does not print anything to the channel; code which calls add_dying should print things as appropriate. :param var: The game state :param end_game: Whether or not to check for win conditions and perform state transitions (temporary) :returns: True if the game is ending (temporary) """ t = time.time() with var.GRAVEYARD_LOCK: # FIXME if not var.GAME_ID or var.GAME_ID > t: # either game ended, or a new game has started return dead = set() while DYING: player, (killer_role, reason, death_triggers) = DYING.popitem() main_role = get_main_role(player) reveal_role = get_reveal_role(player) all_roles = get_all_roles(player) # kill them off del var.MAIN_ROLES[player] for role in all_roles: var.ROLES[role].remove(player) dead.add(player) var.DEAD.add(player) # notify listeners that the player died for possibility of chained deaths evt = Event("del_player", {}, killer_role=killer_role, main_role=main_role, reveal_role=reveal_role, reason=reason) evt_death_triggers = death_triggers and var.PHASE in var.GAME_PHASES evt.dispatch(var, player, all_roles, evt_death_triggers) # notify listeners that all deaths have resolved # FIXME: end_game is a temporary hack until we move state transitions into the event loop # (priority 10 listener sets prevent_default if end_game=True and game is ending; that's another temporary hack) # Once hacks are removed, this function will not have any return value and the end_game kwarg will go away evt = Event("kill_players", {}, end_game=end_game) return not evt.dispatch(var, dead)
def on_transition_day_resolve_end(evt, 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] is 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)
def on_del_player(evt, var, player, all_roles, death_triggers): MATCHMAKERS.discard(player) ACTED.discard(player) if death_triggers and player in LOVERS: lovers = set(LOVERS[player]) for lover in lovers: if lover not in get_players(): continue # already died somehow to_send = "lover_suicide_no_reveal" if var.ROLE_REVEAL in ("on", "team"): to_send = "lover_suicide" channels.Main.send(messages[to_send].format( lover, get_reveal_role(lover))) debuglog("{0} ({1}) LOVE SUICIDE: {2} ({3})".format( lover, get_main_role(lover), player, evt.params.main_role)) add_dying(var, lover, killer_role=evt.params.killer_role, reason="lover_suicide")
def on_transition_day_resolve6(evt, var, victims): for victim in victims: if victim in RETRIBUTION: killers = list(evt.data["killers"].get(victim, [])) loser = None while killers: loser = random.choice(killers) if loser in evt.data["dead"] or victim is loser: killers.remove(loser) continue break if loser in evt.data["dead"] or victim is loser: loser = None ret_evt = Event("retribution_kill", { "target": loser, "message": [] }) ret_evt.dispatch(var, victim, loser) loser = ret_evt.data["target"] evt.data["message"][loser].extend(ret_evt.data["message"]) if loser in evt.data["dead"] or victim is loser: loser = None if loser is not None: protected = try_protection(var, loser, victim, get_main_role(victim), "retribution_totem") if protected is not None: channels.Main.send(*protected) return evt.data["dead"].append(loser) if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(loser) an = "n" if role.startswith( ("a", "e", "i", "o", "u")) else "" evt.data["message"][loser].append( messages["totem_death"].format(victim, loser, an, role)) else: evt.data["message"][loser].append( messages["totem_death_no_reveal"].format( victim, loser))
def on_del_player(evt, var, player, all_roles, death_triggers): MATCHMAKERS.discard(player) if death_triggers and player in LOVERS: lovers = set(LOVERS[player]) for lover in lovers: if lover not in get_players(): continue # already died somehow if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(lover) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" message = messages["lover_suicide"].format(lover, an, role) else: message = messages["lover_suicide_no_reveal"].format(lover) channels.Main.send(message) debuglog("{0} ({1}) LOVE SUICIDE: {2} ({3})".format( lover, get_main_role(lover), player, evt.params.main_role)) add_dying(var, lover, killer_role=evt.params.killer_role, reason="lover_suicide")
def on_transition_day_resolve_end(evt, var, victims): for victim in list(evt.data["dead"]): if GUNNERS.get( victim) and "@wolves" in evt.data["killers"][victim]: if random.random() < var.GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE: # pick a random wolf to be shot wolfset = [ wolf for wolf in get_players(Wolf) if wolf not in evt.data["dead"] ] if wolfset: deadwolf = random.choice(wolfset) if var.ROLE_REVEAL in ("on", "team"): evt.data["message"][victim].append( messages["gunner_killed_wolf_overnight"]. format(victim, deadwolf, get_reveal_role(deadwolf))) else: evt.data["message"][victim].append(messages[ "gunner_killed_wolf_overnight_no_reveal"]. format( victim, deadwolf)) evt.data["dead"].append(deadwolf) evt.data["killers"][deadwolf].append(victim) GUNNERS[victim] -= 1 # deduct the used bullet if var.WOLF_STEALS_GUN and GUNNERS[ victim]: # might have used up the last bullet or something possible = get_players(Wolfchat) random.shuffle(possible) for looter in possible: if looter not in evt.data["dead"]: break else: return # no live wolf, nothing to do here GUNNERS[looter] = GUNNERS.get(looter, 0) + 1 del GUNNERS[victim] var.ROLES[rolename].add(looter) looter.send(messages["wolf_gunner"].format(victim))
def shoot(var, wrapper, message): """Use this to fire off a bullet at someone in the day if you have bullets.""" if not GUNNERS[wrapper.source]: wrapper.pm(messages["no_bullets"]) return target = get_target(var, wrapper, re.split(" +", message)[0], not_self_message="gunner_target_self") if not target: return target = try_misdirection(var, wrapper.source, target) if try_exchange(var, wrapper.source, target): return GUNNERS[wrapper.source] -= 1 gun_evt = Event("gun_chances", {"hit": 0, "miss": 0, "headshot": 0}) gun_evt.dispatch(var, wrapper.source, rolename) rand = random.random() # need to save it shoot_evt = Event("gun_shoot", {"hit": rand <= gun_evt.data["hit"], "kill": random.random() <= gun_evt.data["headshot"]}) shoot_evt.dispatch(var, wrapper.source, target) realrole = get_main_role(target) targrole = get_reveal_role(target) if shoot_evt.data["hit"]: wrapper.send(messages["shoot_success"].format(wrapper.source, target)) an = "n" if targrole.startswith(("a", "e", "i", "o", "u")) else "" if realrole in Wolf: if var.ROLE_REVEAL == "on": wrapper.send(messages["gunner_victim_wolf_death"].format(target, an, targrole)) else: # off and team wrapper.send(messages["gunner_victim_wolf_death_no_reveal"].format(target)) add_dying(var, target, killer_role=get_main_role(wrapper.source), reason="gunner_victim") if kill_players(var): return elif shoot_evt.data["kill"]: accident = "accidentally " if gun_evt.data["headshot"] == 1: # would always headshot accident = "" wrapper.send(messages["gunner_victim_villager_death"].format(target, accident)) if var.ROLE_REVEAL in ("on", "team"): wrapper.send(messages["gunner_victim_role"].format(an, targrole)) add_dying(var, target, killer_role=get_main_role(wrapper.source), reason="gunner_victim") if kill_players(var): return else: wrapper.send(messages["gunner_victim_injured"].format(target)) add_absent(var, target, "wounded") from src.wolfgame import chk_decision, chk_win chk_decision() chk_win() elif rand <= gun_evt.data["hit"] + gun_evt.data["miss"]: wrapper.send(messages["gunner_miss"].format(wrapper.source)) else: # BOOM! your gun explodes, you're dead if var.ROLE_REVEAL in ("on", "team"): wrapper.send(messages["gunner_suicide"].format(wrapper.source, get_reveal_role(wrapper.source))) else: wrapper.send(messages["gunner_suicide_no_reveal"].format(wrapper.source)) add_dying(var, wrapper.source, killer_role="villager", reason="gunner_suicide") # blame explosion on villager's shoddy gun construction or something kill_players(var)
def on_transition_day_resolve_end(evt, var, victims): for victim in list(evt.data["dead"]): if GUNNERS.get(victim) and "@wolves" in evt.data["killers"][victim]: if random.random() < var.GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE: # pick a random wolf to be shot wolfset = [wolf for wolf in get_players(Wolf) if wolf not in evt.data["dead"]] if wolfset: deadwolf = random.choice(wolfset) if var.ROLE_REVEAL in ("on", "team"): evt.data["message"][victim].append(messages["gunner_killed_wolf_overnight"].format(victim, deadwolf, get_reveal_role(deadwolf))) else: evt.data["message"][victim].append(messages["gunner_killed_wolf_overnight_no_reveal"].format(victim, deadwolf)) evt.data["dead"].append(deadwolf) evt.data["killers"][deadwolf].append(victim) GUNNERS[victim] -= 1 # deduct the used bullet if var.WOLF_STEALS_GUN and GUNNERS[victim]: # might have used up the last bullet or something possible = get_players(Wolfchat) random.shuffle(possible) for looter in possible: if looter not in evt.data["dead"]: break else: return # no live wolf, nothing to do here GUNNERS[looter] = GUNNERS.get(looter, 0) + 1 del GUNNERS[victim] var.ROLES[rolename].add(looter) looter.send(messages["wolf_gunner"].format(victim))
def chk_decision(var, *, timeout=False): with var.GRAVEYARD_LOCK: players = set(get_players()) - get_absent(var) avail = len(players) needed = avail // 2 + 1 to_vote = [] for votee, voters in VOTES.items(): votes = (set(voters) | get_forced_votes(var, votee)) - get_forced_abstains(var) if sum(get_vote_weight(var, x) for x in votes) >= needed: to_vote.append(votee) break behaviour_evt = Event("lynch_behaviour", { "num_lynches": 1, "kill_ties": False, "force": timeout }, votes=VOTES, players=avail) behaviour_evt.dispatch(var) num_lynches = behaviour_evt.data["num_lynches"] kill_ties = behaviour_evt.data["kill_ties"] force = behaviour_evt.data["force"] abstaining = False if not to_vote: if len(ABSTAINS | get_forced_abstains(var)) >= avail / 2: abstaining = True elif force: voting = [] if VOTES: plurality = [(x, len(y)) for x, y in VOTES.items()] plurality.sort(key=lambda x: x[1]) votee, value = plurality.pop() max_value = value # Fetch all of the highest ties, exit out if we find someone lower # If everyone is tied, then at some point plurality will be empty, # but the values will still be at the max. Everything's fine, just break while value == max_value: voting.append(votee) if not plurality: break votee, value = plurality.pop() if len(voting) == 1: to_vote.append(voting[0]) elif voting and kill_ties: if set(voting) == set( get_players() ): # killing everyone off? have you considered not doing that abstaining = True else: to_vote.extend(voting) else: abstaining = True if abstaining: for forced_abstainer in get_forced_abstains(var): if forced_abstainer not in ABSTAINS: # did not explicitly abstain channels.Main.send(messages["player_meek_abstain"].format( forced_abstainer)) abstain_evt = Event("abstain", {}) abstain_evt.dispatch(var, ABSTAINS | get_forced_abstains(var)) global ABSTAINED ABSTAINED = True channels.Main.send(messages["village_abstain"]) from src.wolfgame import transition_night transition_night() if to_vote: global LYNCHED LYNCHED += len( to_vote) # track how many people we've lynched today if timeout: channels.Main.send(messages["sunset_lynch"]) for votee in to_vote: voters = list(VOTES[votee]) for forced_voter in get_forced_votes(var, votee): if forced_voter not in voters: # did not explicitly vote channels.Main.send(messages["impatient_vote"].format( forced_voter, votee)) voters.append( forced_voter ) # they need to be counted as voting for them still if not try_lynch_immunity(var, votee): lynch_evt = Event("lynch", {}, players=avail) if lynch_evt.dispatch(var, votee, voters): if var.ROLE_REVEAL in ("on", "team"): rrole = get_reveal_role(votee) an = "n" if rrole.startswith( ("a", "e", "i", "o", "u")) else "" lmsg = random.choice( messages["lynch_reveal"]).format( votee, an, rrole) else: lmsg = random.choice( messages["lynch_no_reveal"]).format(votee) channels.Main.send(lmsg) add_dying(var, votee, "villager", "lynch") kill_players(var, end_game=False) # FIXME elif timeout: channels.Main.send(messages["sunset"]) from src.wolfgame import chk_win if chk_win(): return # game ended, just exit out if timeout or LYNCHED >= num_lynches: from src.wolfgame import transition_night transition_night()
def on_transition_day_resolve_end(evt, var, victims): for victim in victims: if victim in evt.data["dead"] and victim in VISITED.values() and "@wolves" in evt.data["killers"][victim]: for hlt in VISITED: if VISITED[hlt] is victim and hlt not in evt.data["dead"]: if var.ROLE_REVEAL in ("on", "team"): evt.data["message"][hlt].append(messages["visited_victim"].format(hlt, get_reveal_role(hlt))) else: evt.data["message"][hlt].append(messages["visited_victim_noreveal"].format(hlt)) evt.data["dead"].append(hlt)
def on_del_player(evt, var, user, mainrole, allroles, death_triggers): if not death_triggers or "mad scientist" not in allroles: return pl = evt.data["pl"] target1, target2 = _get_targets(var, pl, user) # apply protections (if applicable) prots1 = deque(var.ACTIVE_PROTECTIONS[target1.nick]) prots2 = deque(var.ACTIVE_PROTECTIONS[target2.nick]) # for this event, we don't tell the event that the other side is dying # this allows, e.g. a bodyguard and the person they are guarding to get splashed, # and the bodyguard to still sacrifice themselves to guard the other person aevt = Event("assassinate", { "pl": pl, "target": target1 }, del_player=evt.params.del_player, deadlist=evt.params.deadlist, original=evt.params.original, refresh_pl=evt.params.refresh_pl, message_prefix="mad_scientist_fail_", source="mad scientist", killer=user, killer_mainrole=mainrole, killer_allroles=allroles, prots=prots1) while len(prots1) > 0: # events may be able to cancel this kill if not aevt.dispatch(var, user, target1, prots1[0]): pl = aevt.data["pl"] if target1 is not aevt.data["target"]: target1 = aevt.data["target"] prots1 = deque(var.ACTIVE_PROTECTIONS[target1.nick]) aevt.params.prots = prots1 continue break prots1.popleft() aevt.data["target"] = target2 aevt.params.prots = prots2 while len(prots2) > 0: # events may be able to cancel this kill if not aevt.dispatch(var, user, target2, prots2[0]): pl = aevt.data["pl"] if target2 is not aevt.data["target"]: target2 = aevt.data["target"] prots2 = deque(var.ACTIVE_PROTECTIONS[target2.nick]) aevt.params.prots = prots2 continue break prots2.popleft() kill1 = target1 in pl and len(prots1) == 0 kill2 = target2 in pl and len(prots2) == 0 and target1 is not target2 if kill1: if kill2: if var.ROLE_REVEAL in ("on", "team"): r1 = get_reveal_role(target1) an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else "" r2 = get_reveal_role(target2) an2 = "n" if r2.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["mad_scientist_kill"].format( user, target1, an1, r1, target2, an2, r2) else: tmsg = messages["mad_scientist_kill_no_reveal"].format( user, target1, target2) channels.Main.send(tmsg) debuglog( user.nick, "(mad scientist) KILL: {0} ({1}) - {2} ({3})".format( target1, get_main_role(target1), target2, get_main_role(target2))) # here we DO want to tell that the other one is dying already so chained deaths don't mess things up deadlist1 = evt.params.deadlist[:] deadlist1.append(target2) deadlist2 = evt.params.deadlist[:] deadlist2.append(target1) evt.params.del_player(target1, end_game=False, killer_role="mad scientist", deadlist=deadlist1, original=evt.params.original, ismain=False) evt.params.del_player(target2, end_game=False, killer_role="mad scientist", deadlist=deadlist2, original=evt.params.original, ismain=False) pl = evt.params.refresh_pl(pl) else: if var.ROLE_REVEAL in ("on", "team"): r1 = get_reveal_role(target1) an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["mad_scientist_kill_single"].format( user, target1, an1, r1) else: tmsg = messages["mad_scientist_kill_single_no_reveal"].format( user, target1) channels.Main.send(tmsg) debuglog( user.nick, "(mad scientist) KILL: {0} ({1})".format( target1, get_main_role(target1))) evt.params.del_player(target1, end_game=False, killer_role="mad scientist", deadlist=evt.params.deadlist, original=evt.params.original, ismain=False) pl = evt.params.refresh_pl(pl) else: if kill2: if var.ROLE_REVEAL in ("on", "team"): r2 = get_reveal_role(target2) an2 = "n" if r2.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["mad_scientist_kill_single"].format( user, target2, an2, r2) else: tmsg = messages["mad_scientist_kill_single_no_reveal"].format( user, target2) channels.Main.send(tmsg) debuglog( user.nick, "(mad scientist) KILL: {0} ({1})".format( target2, get_main_role(target2))) evt.params.del_player(target2, end_game=False, killer_role="mad scientist", deadlist=evt.params.deadlist, original=evt.params.original, ismain=False) pl = evt.params.refresh_pl(pl) else: tmsg = messages["mad_scientist_fail"].format(user) channels.Main.send(tmsg) debuglog(user.nick, "(mad scientist) KILL FAIL") evt.data["pl"] = pl
def on_del_player(evt, var, player, all_roles, death_triggers): if not death_triggers or "mad scientist" not in all_roles: return target1, target2 = _get_targets(var, get_players(), player) prots1 = try_protection(var, target1, player, "mad scientist", "mad_scientist_fail") prots2 = try_protection(var, target2, player, "mad scientist", "mad_scientist_fail") if prots1: channels.Main.send(*prots1) if prots2: channels.Main.send(*prots2) kill1 = prots1 is None and add_dying( var, target1, killer_role="mad scientist", reason="mad_scientist") kill2 = prots2 is None and target1 is not target2 and add_dying( var, target2, killer_role="mad scientist", reason="mad_scientist") if kill1: if kill2: if var.ROLE_REVEAL in ("on", "team"): r1 = get_reveal_role(target1) an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else "" r2 = get_reveal_role(target2) an2 = "n" if r2.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["mad_scientist_kill"].format( player, target1, an1, r1, target2, an2, r2) else: tmsg = messages["mad_scientist_kill_no_reveal"].format( player, target1, target2) channels.Main.send(tmsg) debuglog( player.nick, "(mad scientist) KILL: {0} ({1}) - {2} ({3})".format( target1, get_main_role(target1), target2, get_main_role(target2))) else: if var.ROLE_REVEAL in ("on", "team"): r1 = get_reveal_role(target1) an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["mad_scientist_kill_single"].format( player, target1, an1, r1) else: tmsg = messages["mad_scientist_kill_single_no_reveal"].format( player, target1) channels.Main.send(tmsg) debuglog( player.nick, "(mad scientist) KILL: {0} ({1})".format( target1, get_main_role(target1))) else: if kill2: if var.ROLE_REVEAL in ("on", "team"): r2 = get_reveal_role(target2) an2 = "n" if r2.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["mad_scientist_kill_single"].format( player, target2, an2, r2) else: tmsg = messages["mad_scientist_kill_single_no_reveal"].format( player, target2) channels.Main.send(tmsg) debuglog( player.nick, "(mad scientist) KILL: {0} ({1})".format( target2, get_main_role(target2))) else: tmsg = messages["mad_scientist_fail"].format(player) channels.Main.send(tmsg) debuglog(player.nick, "(mad scientist) KILL FAIL")
def chk_decision(var, *, timeout=False): with var.GRAVEYARD_LOCK: players = set(get_players()) - get_absent(var) avail = len(players) needed = avail // 2 + 1 to_vote = [] for votee, voters in VOTES.items(): votes = (set(voters) | get_forced_votes(var, votee)) - get_forced_abstains(var) if sum(get_vote_weight(var, x) for x in votes) >= needed: to_vote.append(votee) break behaviour_evt = Event("lynch_behaviour", {"num_lynches": 1, "kill_ties": False, "force": timeout}, votes=VOTES, players=avail) behaviour_evt.dispatch(var) num_lynches = behaviour_evt.data["num_lynches"] kill_ties = behaviour_evt.data["kill_ties"] force = behaviour_evt.data["force"] abstaining = False if not to_vote: if len(ABSTAINS | get_forced_abstains(var)) >= avail / 2: abstaining = True elif force: voting = [] if VOTES: plurality = [(x, len(y)) for x, y in VOTES.items()] plurality.sort(key=lambda x: x[1]) votee, value = plurality.pop() max_value = value # Fetch all of the highest ties, exit out if we find someone lower # If everyone is tied, then at some point plurality will be empty, # but the values will still be at the max. Everything's fine, just break while value == max_value: voting.append(votee) if not plurality: break votee, value = plurality.pop() if len(voting) == 1: to_vote.append(voting[0]) elif voting and kill_ties: if set(voting) == set(get_players()): # killing everyone off? have you considered not doing that abstaining = True else: to_vote.extend(voting) else: abstaining = True if abstaining: for forced_abstainer in get_forced_abstains(var): if forced_abstainer not in ABSTAINS: # did not explicitly abstain channels.Main.send(messages["player_meek_abstain"].format(forced_abstainer)) abstain_evt = Event("abstain", {}) abstain_evt.dispatch(var, ABSTAINS | get_forced_abstains(var)) global ABSTAINED ABSTAINED = True channels.Main.send(messages["village_abstain"]) from src.wolfgame import transition_night transition_night() if to_vote: global LYNCHED LYNCHED += len(to_vote) # track how many people we've lynched today if timeout: channels.Main.send(messages["sunset_lynch"]) for votee in to_vote: voters = list(VOTES[votee]) for forced_voter in get_forced_votes(var, votee): if forced_voter not in voters: # did not explicitly vote channels.Main.send(messages["impatient_vote"].format(forced_voter, votee)) voters.append(forced_voter) # they need to be counted as voting for them still if not try_lynch_immunity(var, votee): lynch_evt = Event("lynch", {}, players=avail) if lynch_evt.dispatch(var, votee, voters): if var.ROLE_REVEAL in ("on", "team"): rrole = get_reveal_role(votee) an = "n" if rrole.startswith(("a", "e", "i", "o", "u")) else "" lmsg = random.choice(messages["lynch_reveal"]).format(votee, an, rrole) else: lmsg = random.choice(messages["lynch_no_reveal"]).format(votee) channels.Main.send(lmsg) add_dying(var, votee, "villager", "lynch") kill_players(var, end_game=False) # FIXME elif timeout: channels.Main.send(messages["sunset"]) from src.wolfgame import chk_win if chk_win(): return # game ended, just exit out if timeout or LYNCHED >= num_lynches: from src.wolfgame import transition_night transition_night()
def shoot(var, wrapper, message): """Use this to fire off a bullet at someone in the day if you have bullets.""" if not GUNNERS[wrapper.source]: wrapper.pm(messages["no_bullets"]) return target = get_target(var, wrapper, re.split(" +", message)[0], not_self_message="gunner_target_self") if not target: return target = try_misdirection(var, wrapper.source, target) if try_exchange(var, wrapper.source, target): return GUNNERS[wrapper.source] -= 1 gun_evt = Event("gun_chances", {"hit": 0, "miss": 0, "headshot": 0}) gun_evt.dispatch(var, wrapper.source, rolename) rand = random.random() # need to save it shoot_evt = Event("gun_shoot", {"hit": rand <= gun_evt.data["hit"], "kill": random.random() <= gun_evt.data["headshot"]}) shoot_evt.dispatch(var, wrapper.source, target) realrole = get_main_role(target) targrole = get_reveal_role(target) if shoot_evt.data["hit"]: wrapper.send(messages["shoot_success"].format(wrapper.source, target)) an = "n" if targrole.startswith(("a", "e", "i", "o", "u")) else "" if realrole in Wolf: if var.ROLE_REVEAL == "on": wrapper.send(messages["gunner_victim_wolf_death"].format(target, an, targrole)) else: # off and team wrapper.send(messages["gunner_victim_wolf_death_no_reveal"].format(target)) add_dying(var, target, killer_role=get_main_role(wrapper.source), reason="gunner_victim") if kill_players(var): return elif shoot_evt.data["kill"]: accident = "accidentally " if gun_evt.data["headshot"] == 1: # would always headshot accident = "" wrapper.send(messages["gunner_victim_villager_death"].format(target, accident)) if var.ROLE_REVEAL in ("on", "team"): wrapper.send(messages["gunner_victim_role"].format(an, targrole)) add_dying(var, target, killer_role=get_main_role(wrapper.source), reason="gunner_victim") if kill_players(var): return else: wrapper.send(messages["gunner_victim_injured"].format(target)) add_absent(var, target, "wounded") from src.votes import chk_decision from src.wolfgame import chk_win if not chk_win(): # game didn't immediately end due to injury, see if we should force through a vote chk_decision(var) elif rand <= gun_evt.data["hit"] + gun_evt.data["miss"]: wrapper.send(messages["gunner_miss"].format(wrapper.source)) else: # BOOM! your gun explodes, you're dead if var.ROLE_REVEAL in ("on", "team"): wrapper.send(messages["gunner_suicide"].format(wrapper.source, get_reveal_role(wrapper.source))) else: wrapper.send(messages["gunner_suicide_no_reveal"].format(wrapper.source)) add_dying(var, wrapper.source, killer_role="villager", reason="gunner_suicide") # blame explosion on villager's shoddy gun construction or something kill_players(var)