Example #1
0
    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))
Example #2
0
def on_player_win(evt, var, player, role, winner, survived):
    from src.roles.fool import VOTED
    if player in LOVERS:
        evt.data["special"].append("lover")
    if winner == "lovers" and player in LOVERS:
        evt.data["iwon"] = True

    elif player in LOVERS and survived and LOVERS[player].intersection(get_players()):
        for lvr in LOVERS[player]:
            if lvr not in get_players():
                # cannot win with dead lover (lover idled out)
                continue

            lover_role = get_main_role(lvr)

            if singular(winner) not in Win_Stealer:
                evt.data["iwon"] = True
                break
            elif winner == "fool":
                if lvr is VOTED:
                    evt.data["iwon"] = True
                    break
            elif singular(winner) in Win_Stealer and lover_role == singular(winner):
                evt.data["iwon"] = True
                break
Example #3
0
def on_transition_night_end(evt, var):
    chances = var.CURRENT_GAMEMODE.TOTEM_CHANCES
    max_totems = sum(x["wolf shaman"] for x in chances.values())
    ps = get_players()
    shamans = get_players(("wolf shaman",))
    for s in list(LASTGIVEN):
        if s not in shamans:
            del LASTGIVEN[s]

    for shaman in shamans:
        pl = ps[:]
        random.shuffle(pl)
        if LASTGIVEN.get(shaman):
            if LASTGIVEN[shaman] in pl:
                pl.remove(LASTGIVEN[shaman])

        target = 0
        rand = random.random() * max_totems
        for t in chances:
            target += chances[t]["wolf shaman"]
            if rand <= target:
                TOTEMS[shaman] = t
                break
        if shaman.prefers_simple():
            # Message about role was sent with wolfchat
            shaman.send(messages["totem_simple"].format(TOTEMS[shaman]))
        else:
            totem = TOTEMS[shaman]
            tmsg = messages["shaman_totem"].format(totem)
            tmsg += messages[totem + "_totem"]
            shaman.send(tmsg)
Example #4
0
def on_new_role(evt, var, player, old_role):
    wcroles = get_wolfchat_roles(var)

    if old_role is None:
        # initial role assignment; don't do all the logic below about notifying other wolves and such
        if evt.data["role"] in wcroles:
            evt.data["in_wolfchat"] = True
        return

    sayrole = evt.data["role"]
    if sayrole in Hidden:
        sayrole = var.HIDDEN_ROLE
    an = "n" if sayrole.startswith(("a", "e", "i", "o", "u")) else ""

    if player in KILLS:
        del KILLS[player]

    if old_role not in wcroles and evt.data["role"] in wcroles:
        # a new wofl has joined the party, give them tummy rubs and the wolf list
        # and let the other wolves know to break out the confetti and villager steaks
        wofls = get_players(wcroles)
        evt.data["in_wolfchat"] = True
        if wofls:
            new_wolves = []
            for wofl in wofls:
                wofl.queue_message(messages["wolfchat_new_member"].format(player, an, sayrole))
            wofl.send_messages()
        else:
            return # no other wolves, nothing else to do

        pl = get_players()
        if player in pl:
            pl.remove(player)
        random.shuffle(pl)
        pt = []
        wevt = Event("wolflist", {"tags": set()})
        for p in pl:
            prole = get_main_role(p)
            wevt.data["tags"].clear()
            wevt.dispatch(var, p, player)
            tags = " ".join(wevt.data["tags"])
            if prole in wcroles:
                if tags:
                    tags += " "
                pt.append("\u0002{0}\u0002 ({1}{2})".format(p, tags, prole))
            elif tags:
                pt.append("{0} ({1})".format(p, tags))
            else:
                pt.append(p.nick)

        evt.data["messages"].append(messages["players_list"].format(", ".join(pt)))

        if var.PHASE == "night" and evt.data["role"] in Wolf & Killer:
            # inform the new wolf that they can kill and stuff
            nevt = Event("wolf_numkills", {"numkills": 1, "message": ""})
            nevt.dispatch(var)
            if not nevt.data["numkills"] and nevt.data["message"]:
                evt.data["messages"].append(messages[nevt.data["message"]])
Example #5
0
def on_begin_day(evt, var):
    # Refund failed bites
    for alpha, target in BITTEN.items():
        if alpha in get_players() and target not in get_players(Wolf):
            alpha.send(messages["alpha_bite_failure"].format(target))
        else:
            alpha.send(messages["alpha_bite_success"].format(target))
            ALPHAS.add(alpha)
    BITTEN.clear()
Example #6
0
def on_swap_role_state(evt, var, actor, target, role):
    if role == "wild child":
        IDOLS[actor], IDOLS[target] = IDOLS[target], IDOLS[actor]
        if IDOLS[actor] in get_players():
            evt.data["actor_messages"].append(messages["wild_child_idol"].format(IDOLS[actor]))
        else: # The King is dead, long live the King!
            change_role(var, actor, "wild child", "wolf", message="wild_child_idol_died")
            var.ROLES["wild child"].add(actor)

        if IDOLS[target] in get_players():
            evt.data["target_messages"].append(messages["wild_child_idol"].format(IDOLS[target]))
        else:
            change_role(var, target, "wild child", "wolf", message="wild_child_idol_died")
            var.ROLES["wild child"].add(target)
Example #7
0
 def transition_day(self, evt, var):
     # 30% chance we kill a safe, otherwise kill at random
     # when killing safes, go after seer, then harlot, then shaman
     pl = get_players()
     tgt = None
     seer = None
     hlt = None
     hvst = None
     shmn = None
     if len(var.ROLES["seer"]) == 1:
         seer = list(var.ROLES["seer"])[0]
     if len(var.ROLES["harlot"]) == 1:
         hlt = list(var.ROLES["harlot"])[0]
         from src.roles import harlot
         hvst = harlot.VISITED.get(hlt)
         if hvst is not None:
             pl.remove(hlt)
     if len(var.ROLES["shaman"]) == 1:
         shmn = list(var.ROLES["shaman"])[0]
     if random.random() < 0.3:
         if seer:
             tgt = seer
         elif hvst:
             tgt = hvst
         elif shmn:
             tgt = shmn
         elif hlt and not hvst:
             tgt = hlt
     if not tgt:
         tgt = random.choice(pl)
     from src.roles.helper import wolves
     wolves.KILLS[users.Bot] = [tgt]
Example #8
0
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")
Example #9
0
def send_wolfchat_message(var, user, message, roles, *, role=None, command=None):
    if role not in Wolf & Killer and var.RESTRICT_WOLFCHAT & var.RW_NO_INTERACTION:
        return
    if command not in _kill_cmds and var.RESTRICT_WOLFCHAT & var.RW_ONLY_KILL_CMD:
        if var.PHASE == "night" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_NIGHT:
            return
        if var.PHASE == "day" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY:
            return
    if not is_known_wolf_ally(var, user, user):
        return

    wcroles = get_wolfchat_roles(var)
    if var.RESTRICT_WOLFCHAT & var.RW_ONLY_SAME_CMD:
        if var.PHASE == "night" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_NIGHT:
            wcroles = roles
        if var.PHASE == "day" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY:
            wcroles = roles

    wcwolves = get_players(wcroles)
    wcwolves.remove(user)

    player = None
    for player in wcwolves:
        player.queue_message(message)
    for player in var.SPECTATING_WOLFCHAT:
        player.queue_message("[wolfchat] " + message)
    if player is not None:
        player.send_messages()
Example #10
0
def on_gun_chances(evt, var, user, role):
    if user in get_players(get_wolfchat_roles(var)):
        hit, miss, headshot = var.WOLF_GUN_CHANCES
        evt.data["hit"] = hit
        evt.data["miss"] = miss
        evt.data["headshot"] = headshot
        evt.stop_processing = True
Example #11
0
def vg_kill(var, wrapper, message):
    """Take revenge on someone each night after you die."""
    if GHOSTS[wrapper.source][0] == "!":
        return

    target = get_target(var, wrapper, re.split(" +", message)[0])
    if not target:
        return

    if target is wrapper.source:
        wrapper.pm(messages["player_dead"])
        return

    wolves = get_players(var.WOLFTEAM_ROLES)
    if GHOSTS[wrapper.source] == "wolves" and target not in wolves:
        wrapper.pm(messages["vengeful_ghost_wolf"])
        return
    elif GHOSTS[wrapper.source] == "villagers" and target in wolves:
        wrapper.pm(messages["vengeful_ghost_villager"])
        return

    orig = target
    evt = Event("targeted_command", {"target": target.nick, "misdirection": True, "exchange": False})
    evt.dispatch(wrapper.source.client, var, "kill", wrapper.source.nick, target.nick, frozenset({"detrimental"}))
    if evt.prevent_default:
        return
    target = users._get(evt.data["target"]) # FIXME

    KILLS[wrapper.source] = target

    wrapper.pm(messages["player_kill"].format(orig))

    debuglog("{0} (vengeful ghost) KILL: {1} ({2})".format(wrapper.source.nick, target, get_main_role(target)))
    chk_nightdone(wrapper.source.client)
Example #12
0
def consecrate(var, wrapper, message):
    """Consecrates a corpse, putting its spirit to rest and preventing other unpleasant things from happening."""
    alive = get_players()
    targ = re.split(" +", message)[0]
    if not targ:
        wrapper.pm(messages["not_enough_parameters"])
        return

    dead = set(var.ALL_PLAYERS) - set(alive)
    target, _ = users.complete_match(targ, dead)
    if target is None:
        wrapper.pm(messages["consecrate_fail"].format(targ))
        return

    # we have a target, so mark them as consecrated, right now all this does is silence a VG for a night
    # but other roles that do stuff after death or impact dead players should have functionality here as well
    # (for example, if there was a role that could raise corpses as undead somethings, this would prevent that from working)
    # regardless if this has any actual effect or not, it still removes the priest from being able to vote

    evt = Event("consecrate", {})
    evt.dispatch(var, wrapper.source, target)

    wrapper.pm(messages["consecrate_success"].format(target))
    debuglog("{0} (priest) CONSECRATE: {1}".format(wrapper.source, target))
    add_absent(var, wrapper.source, "consecrating")
    from src.votes import chk_decision
    from src.wolfgame import chk_win
    if not chk_win():
        # game didn't immediately end due to marking as absent, see if we should force through a lynch
        chk_decision(var)
Example #13
0
def on_gun_chances(evt, var, user, role):
    if user in get_players(get_wolfchat_roles(var)):
        hit, miss, headshot = var.WOLF_GUN_CHANCES
        evt.data["hit"] = hit
        evt.data["miss"] = miss
        evt.data["headshot"] = headshot
        evt.stop_processing = True
Example #14
0
def send_wolfchat_message(var, user, message, roles, *, role=None, command=None):
    if role not in CAN_KILL and var.RESTRICT_WOLFCHAT & var.RW_NO_INTERACTION:
        return
    if command not in _kill_cmds and var.RESTRICT_WOLFCHAT & var.RW_ONLY_KILL_CMD:
        if var.PHASE == "night" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_NIGHT:
            return
        if var.PHASE == "day" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY:
            return
    if not is_known_wolf_ally(var, user, user):
        return

    wcroles = var.WOLFCHAT_ROLES
    if var.RESTRICT_WOLFCHAT & var.RW_ONLY_SAME_CMD:
        if var.PHASE == "night" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_NIGHT:
            wcroles = roles
        if var.PHASE == "day" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY:
            wcroles = roles
    elif var.RESTRICT_WOLFCHAT & var.RW_REM_NON_WOLVES:
        if var.RESTRICT_WOLFCHAT & var.RW_TRAITOR_NON_WOLF:
            wcroles = var.WOLF_ROLES
        else:
            wcroles = var.WOLF_ROLES | {"traitor"}

    wcwolves = get_players(wcroles)
    wcwolves.remove(user)

    player = None
    for player in wcwolves:
        player.queue_message(message)
    for player in var.SPECTATING_WOLFCHAT:
        player.queue_message("[wolfchat] " + message)
    if player is not None:
        player.send_messages()
Example #15
0
def on_transition_day_begin(evt, var):
    # Select random totem recipients if shamans didn't act
    pl = get_players()
    for shaman in get_all_players(("wolf shaman",)):
        if is_silent(var, shaman):
            continue

        ps = pl[:]
        for given in itertools.chain.from_iterable(LASTGIVEN[shaman].values()):
            if given in ps:
                ps.remove(given)
        for given in itertools.chain.from_iterable(SHAMANS[shaman].values()):
            if given in ps:
                ps.remove(given)
        for totem, count in TOTEMS[shaman].items():
            mustgive = count - len(SHAMANS[shaman][totem])
            for i in range(mustgive):
                if ps:
                    target = random.choice(ps)
                    ps.remove(target)
                    dispatcher = MessageDispatcher(shaman, shaman)
                    given = give_totem(var, dispatcher, target, totem, key="shaman_success_random_known", role="wolf shaman")
                    if given:
                        send_wolfchat_message(var, shaman, messages["shaman_wolfchat"].format(shaman, target), ("wolf shaman",), role="wolf shaman", command="totem")
                        SHAMANS[shaman][totem].append(given[0])
Example #16
0
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")
Example #17
0
def send_wolfchat_message(var,
                          user,
                          message,
                          roles,
                          *,
                          role=None,
                          command=None):
    if role not in Wolf & Killer and var.RESTRICT_WOLFCHAT & var.RW_NO_INTERACTION:
        return
    if command not in _kill_cmds and var.RESTRICT_WOLFCHAT & var.RW_ONLY_KILL_CMD:
        if var.PHASE == "night" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_NIGHT:
            return
        if var.PHASE == "day" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY:
            return
    if not is_known_wolf_ally(var, user, user):
        return

    wcroles = get_wolfchat_roles(var)
    if var.RESTRICT_WOLFCHAT & var.RW_ONLY_SAME_CMD:
        if var.PHASE == "night" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_NIGHT:
            wcroles = roles
        if var.PHASE == "day" and var.RESTRICT_WOLFCHAT & var.RW_DISABLE_DAY:
            wcroles = roles

    wcwolves = get_players(wcroles)
    wcwolves.remove(user)

    player = None
    for player in wcwolves:
        player.queue_message(message)
    for player in var.SPECTATING_WOLFCHAT:
        player.queue_message("[wolfchat] " + message)
    if player is not None:
        player.send_messages()
Example #18
0
def vg_kill(var, wrapper, message):
    """Take revenge on someone each night after you die."""
    if GHOSTS[wrapper.source][0] == "!":
        return

    target = get_target(var, wrapper, re.split(" +", message)[0])
    if not target:
        return

    if target is wrapper.source:
        wrapper.pm(messages["player_dead"])
        return

    wolves = get_players(Wolfteam)
    if GHOSTS[wrapper.source] == "wolf" and target not in wolves:
        wrapper.pm(messages["vengeful_ghost_wolf"])
        return
    elif GHOSTS[wrapper.source] == "villager" and target in wolves:
        wrapper.pm(messages["vengeful_ghost_villager"])
        return

    orig = target
    target = try_misdirection(var, wrapper.source, target)

    KILLS[wrapper.source] = target

    wrapper.pm(messages["player_kill"].format(orig))

    debuglog("{0} (vengeful ghost) KILL: {1} ({2})".format(
        wrapper.source, target, get_main_role(target)))
Example #19
0
def on_revealroles(evt, var, wrapper):
    # print out lovers
    pl = get_players()
    done = {}
    lovers = []
    for lover1, lset in LOVERS.items():
        if lover1 not in pl:
            continue
        for lover2 in lset:
            # check if already said the pairing
            if (lover1 in done and lover2 in done[lover1]) or (
                    lover2 in done and lover1 in done[lover2]):
                continue
            if lover2 not in pl:
                continue
            lovers.append("{0}/{1}".format(lover1, lover2))
            if lover1 in done:
                done[lover1].append(lover2)
            else:
                done[lover1] = [lover2]
    if len(lovers) == 1 or len(lovers) == 2:
        evt.data["output"].append("\u0002lovers\u0002: {0}".format(
            " and ".join(lovers)))
    elif len(lovers) > 2:
        evt.data["output"].append("\u0002lovers\u0002: {0}, and {1}".format(
            ", ".join(lovers[0:-1]), lovers[-1]))
Example #20
0
def on_transition_day(evt, var):
    # now that all protections are finished, add people back to onlybywolves
    # if they're down to 1 active kill and wolves were a valid killer
    victims = set(get_players()) & set(evt.data["victims"]) - var.DYING
    for v in victims:
        if evt.data["numkills"][v] == 1 and v in evt.data["bywolves"]:
            evt.data["onlybywolves"].add(v)

    if len(var.ROLES["fallen angel"]) > 0:
        for p, t in list(evt.data["protected"].items()):
            if p in evt.data["bywolves"]:
                if p in evt.data["protected"]:
                    p.send(messages["fallen_angel_deprotect"])

                # let other roles do special things when we bypass their guards
                killer = random.choice(
                    list(get_all_players(("fallen angel", ))))
                fevt = Event("fallen_angel_guard_break", evt.data)
                fevt.dispatch(var, p, killer)

                if p in evt.data["protected"]:
                    del evt.data["protected"][p]
                if p in var.ACTIVE_PROTECTIONS:
                    del var.ACTIVE_PROTECTIONS[p.nick]
                # mark kill as performed by a random FA
                # this is important as there may otherwise be no killers if every kill was blocked
                evt.data["killers"][p].append(killer)
Example #21
0
def dullahan_kill(var, wrapper, message):
    """Kill someone at night as a dullahan until everyone on your list is dead."""
    if not TARGETS[wrapper.source] & set(get_players()):
        wrapper.pm(messages["dullahan_targets_dead"])
        return

    target = get_target(var, wrapper, re.split(" +", message)[0])
    if not target:
        return

    if target is wrapper.source:
        wrapper.pm(messages["no_suicide"])
        return

    orig = target
    evt = Event("targeted_command", {"target": target.nick, "misdirection": True, "exchange": True})
    evt.dispatch(wrapper.client, var, "kill", wrapper.source.nick, target.nick, frozenset({"detrimental"}))
    if evt.prevent_default:
        return
    target = users._get(evt.data["target"]) # FIXME: Need to fix once targeted_command uses the new API

    KILLS[wrapper.source] = target

    wrapper.pm(messages["player_kill"].format(orig))

    debuglog("{0} (dullahan) KILL: {1} ({2})".format(wrapper.source, target, get_role(target.nick)))

    chk_nightdone(wrapper.client)
Example #22
0
    def happy_fun_times(self, evt, var, player, all_roles, death_triggers):
        if death_triggers and evt.params.main_role == "priest":
            channels.Main.send(messages["sleepy_priest_death"])

            turn_chance = 3 / 4
            mapping = {
                "seer": "doomsayer",
                "harlot": "succubus",
                "cultist": "demoniac"
            }
            for old, new in mapping.items():
                turn = [
                    p for p in get_players((old, ))
                    if random.random() < turn_chance
                ]
                for t in turn:
                    # messages: sleepy_doomsayer_turn, sleepy_succubus_turn, sleepy_demoniac_turn
                    change_role(var,
                                t,
                                old,
                                new,
                                message="sleepy_{0}_turn".format(new))

                newstats = set()
                for rs in var.ROLE_STATS:
                    d = Counter(dict(rs))
                    newstats.add(rs)
                    if old in d and d[old] >= 1:
                        d[old] -= 1
                        d[new] += 1
                        newstats.add(frozenset(d.items()))
                var.ROLE_STATS = frozenset(newstats)
Example #23
0
def on_transition_day(evt, var):
    pl = get_players()
    vs = set(evt.data["victims"])
    for v in pl:
        if v in vs:
            if v in var.DYING:
                continue
            for g in get_all_players(("guardian angel", )):
                if GUARDED.get(g.nick) == v.nick:
                    evt.data["numkills"][v] -= 1
                    if evt.data["numkills"][v] >= 0:
                        evt.data["killers"][v].pop(0)
                    if evt.data["numkills"][v] <= 0 and v not in evt.data[
                            "protected"]:
                        evt.data["protected"][v] = "angel"
                    elif evt.data["numkills"][v] <= 0:
                        var.ACTIVE_PROTECTIONS[v.nick].append("angel")
            for g in get_all_players(("bodyguard", )):
                if GUARDED.get(g.nick) == v.nick:
                    evt.data["numkills"][v] -= 1
                    if evt.data["numkills"][v] >= 0:
                        evt.data["killers"][v].pop(0)
                    if evt.data["numkills"][v] <= 0 and v not in evt.data[
                            "protected"]:
                        evt.data["protected"][v] = "bodyguard"
                    elif evt.data["numkills"][v] <= 0:
                        var.ACTIVE_PROTECTIONS[v.nick].append("bodyguard")
        else:
            for g in var.ROLES["guardian angel"]:
                if GUARDED.get(g.nick) == v.nick:
                    var.ACTIVE_PROTECTIONS[v.nick].append("angel")
            for g in var.ROLES["bodyguard"]:
                if GUARDED.get(g.nick) == v.nick:
                    var.ACTIVE_PROTECTIONS[v.nick].append("bodyguard")
Example #24
0
def get_lovers():
    lovers = []
    pl = get_players()
    for lover in LOVERS:
        done = None
        for i, lset in enumerate(lovers):
            if lover in pl and lover in lset:
                if done is not None:  # plot twist! two clusters turn out to be linked!
                    done.update(lset)
                    for lvr in LOVERS[lover]:
                        if lvr in pl:
                            done.add(lvr)

                    lset.clear()
                    continue

                for lvr in LOVERS[lover]:
                    if lvr in pl:
                        lset.add(lvr)
                done = lset

        if done is None and lover in pl:
            lovers.append(set())
            lovers[-1].add(lover)
            for lvr in LOVERS[lover]:
                if lvr in pl:
                    lovers[-1].add(lvr)

    while set() in lovers:
        lovers.remove(set())

    return lovers
Example #25
0
def consecrate(var, wrapper, message):
    """Consecrates a corpse, putting its spirit to rest and preventing other unpleasant things from happening."""
    alive = get_players()
    targ = re.split(" +", message)[0]
    if not targ:
        wrapper.pm(messages["not_enough_parameters"])
        return

    dead = set(var.ALL_PLAYERS) - set(alive)
    target, _ = users.complete_match(targ, dead)
    if target is None:
        wrapper.pm(messages["consecrate_fail"].format(targ))
        return

    # we have a target, so mark them as consecrated, right now all this does is silence a VG for a night
    # but other roles that do stuff after death or impact dead players should have functionality here as well
    # (for example, if there was a role that could raise corpses as undead somethings, this would prevent that from working)
    # regardless if this has any actual effect or not, it still removes the priest from being able to vote

    evt = Event("consecrate", {})
    evt.dispatch(var, wrapper.source, target)

    wrapper.pm(messages["consecrate_success"].format(target))
    debuglog("{0} (priest) CONSECRATE: {1}".format(wrapper.source, target))
    add_absent(var, wrapper.source, "consecrating")
    from src.votes import chk_decision
    from src.wolfgame import chk_win
    if not chk_win():
        # game didn't immediately end due to marking as absent, see if we should force through a lynch
        chk_decision(var)
Example #26
0
def add_dying(var, player: User, killer_role: str, reason: str, *, death_triggers: bool = True) -> bool:
    """
    Mark a player as dying.

    :param var: The game state
    :param player: The player to kill off
    :param killer_role: The role which is responsible for killing player; must be an actual role
    :param reason: The reason the player is being killed off, for stats tracking purposes
    :param death_triggers: Whether or not to run role logic that triggers on death; players who die due to quitting or idling out have this set to False
    :returns: True if the player was successfully marked as dying, or False if they are already dying or dead
    """
    t = time.time()

    # ensure that the reaper thread doesn't smash things against the gameplay thread when running this
    # (eventually the reaper thread will just pass messages to the main thread via the asyncio event loop and these locks would therefore be unnecessary)
    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 False

        if player in DYING or player not in get_players():
            return False

        DYING[player] = (killer_role, reason, death_triggers)
        return True
Example #27
0
def get_lovers():
    lovers = []
    pl = get_players()
    for lover in LOVERS:
        done = None
        for i, lset in enumerate(lovers):
            if lover in pl and lover in lset:
                if done is not None: # plot twist! two clusters turn out to be linked!
                    done.update(lset)
                    for lvr in LOVERS[lover]:
                        if lvr in pl:
                            done.add(lvr)

                    lset.clear()
                    continue

                for lvr in LOVERS[lover]:
                    if lvr in pl:
                        lset.add(lvr)
                done = lset

        if done is None and lover in pl:
            lovers.append(set())
            lovers[-1].add(lover)
            for lvr in LOVERS[lover]:
                if lvr in pl:
                    lovers[-1].add(lvr)

    while set() in lovers:
        lovers.remove(set())

    return lovers
Example #28
0
 def do_nightmare(self, var, target, night):
     if var.PHASE != "night" or var.NIGHT_COUNT != night:
         return
     if target not in get_players():
         return
     self.having_nightmare.clear()
     self.having_nightmare.append(target)
     target.send(messages["sleepy_nightmare_begin"])
     target.send(messages["sleepy_nightmare_navigate"])
     self.correct = [None, None, None]
     self.fake1 = [None, None, None]
     self.fake2 = [None, None, None]
     directions = ["n", "e", "s", "w"]
     self.step = 0
     self.prev_direction = None
     opposite = {"n": "s", "e": "w", "s": "n", "w": "e"}
     for i in range(3):
         corrdir = directions[:]
         f1dir = directions[:]
         f2dir = directions[:]
         if i > 0:
             corrdir.remove(opposite[self.correct[i - 1]])
             f1dir.remove(opposite[self.fake1[i - 1]])
             f2dir.remove(opposite[self.fake2[i - 1]])
         else:
             corrdir.remove("s")
             f1dir.remove("s")
             f2dir.remove("s")
         self.correct[i] = random.choice(corrdir)
         self.fake1[i] = random.choice(f1dir)
         self.fake2[i] = random.choice(f2dir)
     self.prev_direction = "n"
     self.start_direction = "n"
     self.on_path = set()
     self.nightmare_step()
Example #29
0
def wait(var, wrapper, message):
    """Increase the wait time until !start can be used."""
    if wrapper.target is not channels.Main:
        return

    pl = get_players()

    with WAIT_LOCK:
        global WAIT_TOKENS, WAIT_LAST
        wait_check_time = time.time()
        WAIT_TOKENS += (wait_check_time - WAIT_LAST) / var.WAIT_TB_DELAY
        WAIT_LAST = wait_check_time

        WAIT_TOKENS = min(WAIT_TOKENS, var.WAIT_TB_BURST)

        now = datetime.now()
        if ((LAST_WAIT and wrapper.source in LAST_WAIT
             and LAST_WAIT[wrapper.source] +
             timedelta(seconds=var.WAIT_RATE_LIMIT) > now) or WAIT_TOKENS < 1):
            wrapper.pm(messages["command_ratelimited"])
            return

        LAST_WAIT[wrapper.source] = now
        WAIT_TOKENS -= 1
        if now > var.CAN_START_TIME:
            var.CAN_START_TIME = now + timedelta(seconds=var.EXTRA_WAIT)
        else:
            var.CAN_START_TIME += timedelta(seconds=var.EXTRA_WAIT)
        wrapper.send(messages["wait_time_increase"].format(
            wrapper.source, var.EXTRA_WAIT))
Example #30
0
    def on_transition_night_end(evt, var):
        pl = set()
        ctr = Counter()

        for t in types:
            cat = cats.get(t)
            players = get_players(cat)
            pl.update(players)
            ctr[t] += len(players)

        values = []
        plural = True
        for name in types:
            keyname = "mystic_" + name.lower().replace(" ", "_")
            l = ctr[name]
            if l:
                if not values and l == 1:
                    plural = False
            else:
                l = "no"
            values.append("\u0002{0}\u0002 {1}{2}".format(l, messages[keyname], "" if l == 1 else "s"))

        if len(values) > 2:
            value = " and ".join((", ".join(values[:-1]), values[-1]))
        else:
            value = " and ".join(values)
        msg = messages["mystic_info"].format("are" if plural else "is", value, " still", "")

        for mystic in get_all_players((rolename,)):
            LAST_COUNT[mystic] = (value, plural)
            if send_role:
                to_send = "{0}_{1}".format(role, ("simple" if mystic.prefers_simple() else "notify"))
                mystic.send(messages[to_send])
            mystic.send(msg)
Example #31
0
def add_lycanthropy(var, target, prefix="lycan"):
    """Effect the target with lycanthropy. Fire the add_lycanthropy event."""
    if target in LYCANTHROPES:
        return

    if target in get_players() and Event("add_lycanthropy", {}).dispatch(var, target):
        LYCANTHROPES[target] = prefix
Example #32
0
def add_disease(var, target):
    """Effect the target with disease. Fire the add_disease event."""
    if target in DISEASED or target not in get_players():
        return

    if Event("add_disease", {}).dispatch(var, target):
        DISEASED.add(target)
Example #33
0
def wait(var, wrapper, message):
    """Increase the wait time until !start can be used."""
    if wrapper.target is not channels.Main:
        return

    pl = get_players()

    with WAIT_LOCK:
        global WAIT_TOKENS, WAIT_LAST
        wait_check_time = time.time()
        WAIT_TOKENS += (wait_check_time - WAIT_LAST) / var.WAIT_TB_DELAY
        WAIT_LAST = wait_check_time

        WAIT_TOKENS = min(WAIT_TOKENS, var.WAIT_TB_BURST)

        now = datetime.now()
        if ((LAST_WAIT and wrapper.source in LAST_WAIT and LAST_WAIT[wrapper.source] +
                timedelta(seconds=var.WAIT_RATE_LIMIT) > now) or WAIT_TOKENS < 1):
            wrapper.pm(messages["command_ratelimited"])
            return

        LAST_WAIT[wrapper.source] = now
        WAIT_TOKENS -= 1
        if now > var.CAN_START_TIME:
            var.CAN_START_TIME = now + timedelta(seconds=var.EXTRA_WAIT)
        else:
            var.CAN_START_TIME += timedelta(seconds=var.EXTRA_WAIT)
        wrapper.send(messages["wait_time_increase"].format(wrapper.source, var.EXTRA_WAIT))
Example #34
0
def on_new_role(evt, var, user, old_role):
    if old_role == "sharpshooter":
        if evt.data["role"] != "sharpshooter":
            del GUNNERS[user]

    elif evt.data["role"] == "sharpshooter":
        GUNNERS[user] = math.ceil(var.SHARPSHOOTER_MULTIPLIER * len(get_players()))
Example #35
0
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")
Example #36
0
def on_revealroles(evt, var, user, role):
    if role == "mad scientist":
        pl = get_players()
        target1, target2 = _get_targets(var, pl, user)
        evt.data["special_case"].append(
            messages["mad_scientist_revealroles_targets"].format(
                target1, target2))
Example #37
0
def fwait(var, wrapper, message):
    """Force an increase (or decrease) in wait time. Can be used with a number of seconds to wait."""
    pl = get_players()

    msg = re.split(" +", message.strip(), 1)[0]

    if msg and (msg.isdigit() or (msg[0] == "-" and msg[1:].isdigit())):
        extra = int(msg)
    else:
        extra = var.EXTRA_WAIT

    now = datetime.now()
    extra = max(-900, min(900, extra))

    if now > var.CAN_START_TIME:
        var.CAN_START_TIME = now + timedelta(seconds=extra)
    else:
        var.CAN_START_TIME += timedelta(seconds=extra)

    if extra >= 0:
        wrapper.send(messages["forced_wait_time_increase"].format(
            wrapper.source, abs(extra), "s" if extra != 1 else ""))
    else:
        wrapper.send(messages["forced_wait_time_decrease"].format(
            wrapper.source, abs(extra), "s" if extra != -1 else ""))
Example #38
0
def dullahan_kill(var, wrapper, message):
    """Kill someone at night as a dullahan until everyone on your list is dead."""
    if not TARGETS[wrapper.source] & set(get_players()):
        wrapper.pm(messages["dullahan_targets_dead"])
        return

    target = get_target(var,
                        wrapper,
                        re.split(" +", message)[0],
                        not_self_message="no_suicide")
    if not target:
        return

    orig = target
    evt = Event("targeted_command", {
        "target": target,
        "misdirection": True,
        "exchange": True
    })
    evt.dispatch(var, wrapper.source, target)
    if evt.prevent_default:
        return
    target = evt.data["target"]

    KILLS[wrapper.source] = target

    wrapper.pm(messages["player_kill"].format(orig))

    debuglog("{0} (dullahan) KILL: {1} ({2})".format(wrapper.source, target,
                                                     get_main_role(target)))
Example #39
0
    def on_transition_day_begin(self, evt, var):
        ps = get_players()
        for p in ps:
            if get_main_role(p) in Wolfteam:
                continue # wolf shamans can't starve

            if self.totem_tracking[p] > 0:
                # if sustenance totem made it through, fully feed player
                self.hunger_levels[p] = 0
            elif self.totem_tracking[p] < 0:
                # if hunger totem made it through, fast-track player to starvation
                if self.hunger_levels[p] < 3:
                    self.hunger_levels[p] = 3

            # apply natural hunger
            self.hunger_levels[p] += 1

            if self.hunger_levels[p] >= 5:
                # if they hit 5, they die of starvation
                # if there are less VGs than alive wolf shamans, they become a wendigo as well
                self.maybe_make_wendigo(var, p)
                add_dying(var, p, killer_role="villager", reason="boreal_starvation")
            elif self.hunger_levels[p] >= 3:
                # if they are at 3 or 4, alert them that they are hungry
                p.send(messages["boreal_hungry"])

        self.totem_tracking.clear()
Example #40
0
    def on_transition_night_end(evt, var):
        for seer in get_all_players((rolename,)):
            pl = get_players()
            random.shuffle(pl)
            pl.remove(seer)  # remove self from list

            seer.send(messages["seer_info_general"].format(rolename), messages[rolename + "_info"])
            seer.send(messages["players_list"].format(pl))
Example #41
0
def on_transition_day_begin(evt, var):
    pl = get_players()
    for mm in get_all_players(("matchmaker", )):
        if mm not in MATCHMAKERS:
            lovers = random.sample(pl, 2)
            MATCHMAKERS.add(mm)
            _set_lovers(*lovers)
            mm.send(messages["random_matchmaker"])
Example #42
0
def on_transition_day_begin(evt, var):
    pl = get_players()
    for mm in get_all_players(("matchmaker",)):
        if mm not in MATCHMAKERS:
            lovers = random.sample(pl, 2)
            MATCHMAKERS.add(mm)
            _set_lovers(*lovers)
            mm.send(messages["random_matchmaker"])
Example #43
0
def on_transition_night_end(evt, var):
    for harlot in get_all_players(("harlot", )):
        pl = get_players()
        random.shuffle(pl)
        pl.remove(harlot)
        harlot.send(messages["harlot_notify"],
                    messages["players_list"].format(pl),
                    sep="\n")
Example #44
0
def on_transition_night_end(evt, var):
    ps = get_players()
    for doctor in get_all_players(("doctor",)):
        if DOCTORS[doctor]: # has immunizations remaining
            pl = ps[:]
            random.shuffle(pl)
            doctor.send(messages["doctor_notify"])
            doctor.send(messages["doctor_immunizations"].format(DOCTORS[doctor]))
Example #45
0
def on_new_role(evt, var, user, old_role):
    if old_role == "sharpshooter":
        if evt.data["role"] != "sharpshooter":
            del GUNNERS[user]

    elif evt.data["role"] == "sharpshooter":
        GUNNERS[user] = math.ceil(var.SHARPSHOOTER_MULTIPLIER *
                                  len(get_players()))
Example #46
0
def add_vote_weight(var, target : users.User, amount : int = 1) -> None:
    """Make the target's votes as having more weight."""
    if target not in get_players():
        return

    WEIGHT[target] = WEIGHT.get(target, 1) + amount
    if WEIGHT[target] == 1:
        del WEIGHT[target]
Example #47
0
def add_vote_weight(var, target: users.User, amount: int = 1) -> None:
    """Make the target's votes as having more weight."""
    if target not in get_players():
        return

    WEIGHT[target] = WEIGHT.get(target, 1) + amount
    if WEIGHT[target] == 1:
        del WEIGHT[target]
Example #48
0
 def setup_nightmares(self, evt, var):
     if random.random() < 1 / 5:
         with var.WARNING_LOCK:
             t = threading.Timer(
                 60, self.do_nightmare,
                 (var, random.choice(get_players()), var.NIGHT_COUNT))
             t.daemon = True
             t.start()
Example #49
0
def on_transition_night_end(evt, var):
    for harlot in get_all_players(("harlot",)):
        pl = get_players()
        random.shuffle(pl)
        pl.remove(harlot)
        to_send = "harlot_info"
        if harlot.prefers_simple():
            to_send = "harlot_simple"
        harlot.send(messages[to_send], messages["players_list"].format(", ".join(p.nick for p in pl)), sep="\n")
Example #50
0
def on_transition_night_end(evt, var):
    wolves = get_players(get_wolfchat_roles(var))
    for child in get_all_players(("wild child",)):
        if child in wolves:
            continue
        if child.prefers_simple():
            child.send(messages["wild_child_simple"])
        else:
            child.send(messages["wild_child_notify"])
Example #51
0
def on_transition_night_end(evt, var):
    for ms in get_all_players(("mad scientist",)):
        pl = get_players()
        target1, target2 = _get_targets(var, pl, ms)

        to_send = "mad_scientist_notify"
        if ms.prefers_simple():
            to_send = "mad_scientist_simple"
        ms.send(messages[to_send].format(target1, target2))
Example #52
0
def on_transition_day_begin(evt, var):
    # Select random totem recipients if shamans didn't act
    pl = get_players()
    for shaman in get_players(("shaman",)):
        if shaman not in SHAMANS and not is_silent(var, shaman):
            ps = pl[:]
            if shaman in LASTGIVEN:
                if LASTGIVEN[shaman] in ps:
                    ps.remove(LASTGIVEN[shaman])
            if ps:
                target = random.choice(ps)
                dispatcher = MessageDispatcher(shaman, shaman)

                SHAMANS[shaman] = give_totem(var, dispatcher, target, prefix=messages["random_totem_prefix"], role="shaman", msg=" of {0}".format(TOTEMS[shaman]))
            else:
                LASTGIVEN[shaman] = None
        elif shaman not in SHAMANS:
            LASTGIVEN[shaman] = None
Example #53
0
def _get_target(target):
    """Internal helper for try_misdirection. Return one target over."""
    pl = get_players()
    index = pl.index(target)
    if random.randint(0, 1):
        index -= 2
    if index == len(pl) - 1: # last item
        index = -1
    return pl[index+1]
Example #54
0
def on_transition_day_resolve_end(evt, var, victims):
    for gangel in get_all_players(("guardian angel",)):
        if GUARDED.get(gangel) in get_players(Wolf) and gangel not in evt.data["dead"]:
            r = random.random()
            if r < var.GUARDIAN_ANGEL_DIES_CHANCE:
                if var.ROLE_REVEAL == "on":
                    evt.data["message"][gangel].append(messages["guardian_angel_protected_wolf"].format(gangel))
                else: # off and team
                    evt.data["message"][gangel].append(messages["guardian_angel_protected_wolf_no_reveal"].format(gangel))
                evt.data["dead"].append(gangel)
Example #55
0
def on_transition_night_end(evt, var):
    ps = get_players()
    for vigilante in get_all_players(("vigilante",)):
        pl = ps[:]
        random.shuffle(pl)
        pl.remove(vigilante)
        to_send = "vigilante_notify"
        if vigilante.prefers_simple():
            to_send = "vigilante_simple"
        vigilante.send(messages[to_send], messages["players_list"].format(", ".join(p.nick for p in pl)), sep="\n")
Example #56
0
def add_protection(var, target, protector, protector_role, scope=All):
    """Add a protection to the target affecting the relevant scope."""
    if target not in get_players():
        return

    if target not in PROTECTIONS:
        PROTECTIONS[target] = DefaultUserDict(list)

    prot_entry = (scope, protector_role)
    PROTECTIONS[target][protector].append(prot_entry)
Example #57
0
def on_transition_night_end(evt, var):
    ps = get_players()
    for inv in var.ROLES["investigator"]:
        pl = ps[:]
        random.shuffle(pl)
        pl.remove(inv)
        to_send = "investigator_notify"
        if inv.prefers_simple():
            to_send = "investigator_simple"
        inv.send(messages[to_send], messages["players_list"].format(", ".join(p.nick for p in pl)), sep="\n")
Example #58
0
def on_transition_night_end(evt, var):
    ps = set(get_players()) - CHARMED
    for piper in get_all_players(("piper",)):
        pl = list(ps)
        random.shuffle(pl)
        pl.remove(piper)
        to_send = "piper_notify"
        if piper.prefers_simple():
            to_send = "piper_simple"
        piper.send(messages[to_send], messages["players_list"].format(", ".join(p.nick for p in pl)), sep="\n")
Example #59
0
def on_transition_night_end(evt, var):
    ps = get_players()
    for doctor in get_all_players(("doctor",)):
        if DOCTORS[doctor]: # has immunizations remaining
            pl = ps[:]
            random.shuffle(pl)
            if doctor.prefers_simple():
                doctor.send(messages["doctor_simple"])
            else:
                doctor.send(messages["doctor_notify"])
            doctor.send(messages["doctor_immunizations"].format(DOCTORS[doctor], "s" if DOCTORS[doctor] > 1 else ""))