From c121c0f08f412fc83ca5b76c611b1869db957b69 Mon Sep 17 00:00:00 2001 From: skizzerz Date: Wed, 4 Apr 2018 17:06:03 -0500 Subject: [PATCH] Mudkip balance fix and bugfix Fix balance in mudkip, it was skewed too much towards village winning. Also, if day ended in mudkip and nobody was being voted, it would error. Let's make that not error. --- messages/en.json | 11 ++- src/gamemodes.py | 4 +- src/roles/detective.py | 2 +- src/roles/investigator.py | 141 ++++++++++++++++++++++++++++++++++++++ src/settings.py | 1 + 5 files changed, 153 insertions(+), 6 deletions(-) create mode 100644 src/roles/investigator.py diff --git a/messages/en.json b/messages/en.json index 09d4bc7..780615e 100644 --- a/messages/en.json +++ b/messages/en.json @@ -366,10 +366,12 @@ "sorcerer_success": "After casting your ritual, you determine that \u0002{0}\u0002 is a{1} \u0002{2}\u0002!", "sorcerer_fail": "After casting your ritual, you determine that \u0002{0}\u0002 does not have paranormal senses.", "sorcerer_success_wolfchat": "\u0002{0}\u0002 is observing \u0002{1}\u0002.", - "already_investigated": "You may only investigate one person per round.", - "no_investigate_self": "Investigating yourself would be a waste.", + "already_investigated": "You may only investigate once per day.", + "no_investigate_self": "You may not investigate yourself.", "investigate_success": "The results of your investigation have returned. \u0002{0}\u0002 is a... \u0002{1}\u0002!", - "investigator_reveal": "Someone accidentally drops a paper. The paper reveals that \u0002{0}\u0002 is the detective!", + "detective_reveal": "Someone accidentally drops a paper. The paper reveals that \u0002{0}\u0002 is the detective!", + "investigator_results_same": "Your investigation has revealed that \u0002{0}\u0002 and \u0002{1}\u0002 are friends.", + "investigator_results_different": "Your investigation has revealed that \u0002{0}\u0002 and \u0002{1}\u0002 do not trust each other.", "harlot_already_visited": "You are already spending the night with \u0002{0}\u0002.", "harlot_success": "You are spending the night with \u0002{0}\u0002. Have a good time!", "harlot_not_self": "You may not visit yourself. Use \"pass\" to choose to not visit anyone tonight.", @@ -475,6 +477,9 @@ "detective_chance": " Each time you use your ability, you risk a {0}% chance of having your identity revealed to the wolves.", "detective_notify": "You are a \u0002detective\u0002. It is your job to determine all the wolves and traitors. During the day you can see the true identity of all players, even traitors, by using \"id \" in PM.{0}", "detective_simple": "You are a \u0002detective\u0002.", + "investigator_notify": "You are an \u0002investigator\u0002. During the day, you can see if two people are on the same side by using \"id and \" in PM.", + "investigator_simple": "You are an \u0002investigator\u0002.", + "investigator_help": "Investigate two different people by using \"id and \" in PM.", "drunk_notification": "You have been drinking too much! You are the \u0002village drunk\u0002.", "drunk_simple": "You are the \u0002village drunk\u0002.", "mystic_notify": "You are the \u0002mystic\u0002. Each night you divine the number of evil villagers (including wolves) that are still alive.", diff --git a/src/gamemodes.py b/src/gamemodes.py index 680bac1..4c67ba8 100644 --- a/src/gamemodes.py +++ b/src/gamemodes.py @@ -1290,7 +1290,7 @@ class MudkipMode(GameMode): self.ROLE_INDEX = ( 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ) self.ROLE_GUIDE = reset_roles(self.ROLE_INDEX) self.ROLE_GUIDE.update({# village roles - "detective" : ( 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), + "investigator" : ( 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), "guardian angel" : ( 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), "shaman" : ( 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), "vengeful ghost" : ( 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 ), @@ -1339,7 +1339,7 @@ class MudkipMode(GameMode): avail = len(evt.params.voters) voted = sum(map(len, evt.data["votelist"].values())) - if avail != voted and not evt.params.timeout: + if (avail != voted and not evt.params.timeout) or voted == 0: return majority = avail // 2 + 1 diff --git a/src/roles/detective.py b/src/roles/detective.py index 453d5f1..f34a713 100644 --- a/src/roles/detective.py +++ b/src/roles/detective.py @@ -55,7 +55,7 @@ def investigate(cli, nick, chan, rest): else: wcroles = var.WOLF_ROLES | {"traitor"} - mass_privmsg(cli, list_players(wcroles), messages["investigator_reveal"].format(nick)) + mass_privmsg(cli, list_players(wcroles), messages["detective_reveal"].format(nick)) debuglog("{0} ({1}) PAPER DROP".format(nick, get_role(nick))) @event_listener("rename_player") diff --git a/src/roles/investigator.py b/src/roles/investigator.py new file mode 100644 index 0000000..46c5558 --- /dev/null +++ b/src/roles/investigator.py @@ -0,0 +1,141 @@ +import re +import random +import itertools +import math +from collections import defaultdict + +import botconfig +import src.settings as var +from src.utilities import * +from src import channels, users, debuglog, errlog, plog +from src.functions import get_players, get_all_players, get_main_role, get_reveal_role, get_target +from src.decorators import command, event_listener +from src.messages import messages +from src.events import Event + +INVESTIGATED = set() + +@command("id", chan=False, pm=True, playing=True, silenced=True, phases=("day",), roles=("investigator",)) +def investigate(var, wrapper, message): + """Investigate two players to determine their relationship to each other.""" + if wrapper.source in INVESTIGATED: + wrapper.pm(messages["already_investigated"]) + return + pieces = re.split(" +", message) + if len(pieces) == 1: + wrapper.pm(messages["investigator_help"]) + return + target1 = pieces[0] + target2 = pieces[1] + if target2.lower() == "and" and len(pieces) > 2: + target2 = pieces[2] + target1 = get_target(var, wrapper, target1, not_self_message="no_investigate_self") + target2 = get_target(var, wrapper, target2, not_self_message="no_investigate_self") + if not target1 or not target2: + return + elif target1 is target2: + wrapper.pm(messages["investigator_help"]) + return + + evt = Event("targeted_command", {"target": target1, "misdirection": True, "exchange": True}) + evt.dispatch(var, "identify", wrapper.source, target1, frozenset({"info", "immediate"})) + if evt.prevent_default: + return + target1 = evt.data["target"] + + evt = Event("targeted_command", {"target": target2, "misdirection": True, "exchange": True}) + evt.dispatch(var, "identify", wrapper.source, target2, frozenset({"info", "immediate"})) + if evt.prevent_default: + return + target2 = evt.data["target"] + + t1role = get_main_role(target1) + t2role = get_main_role(target2) + # FIXME: split into amnesiac via investigate event once amnesiac is split + if t1role == "amnesiac": + t1role = var.AMNESIAC_ROLES[target1.nick] + if t2role == "amnesiac": + t2role = var.AMNESIAC_ROLES[target2.nick] + + evt = Event("investigate", {"role": t1role}) + evt.dispatch(wrapper.client, var, wrapper.source.nick, target1.nick) # FIXME + t1role = evt.data["role"] + + evt = Event("investigate", {"role": t2role}) + evt.dispatch(wrapper.client, var, wrapper.source.nick, target2.nick) # FIXME + t2role = evt.data["role"] + + # FIXME: make a standardized way of getting team affiliation, and make + # augur and investigator both use it (and make it events-aware so other + # teams can be added more easily) + if t1role in var.WOLFTEAM_ROLES: + t1role = "red" + elif t1role in var.TRUE_NEUTRAL_ROLES: + t1role = "grey" + else: + t1role = "blue" + + if t2role in var.WOLFTEAM_ROLES: + t2role = "red" + elif t2role in var.TRUE_NEUTRAL_ROLES: + t2role = "grey" + else: + t2role = "blue" + + same = t1role == t2role + # FIXME: split into matchmaker once that is split and make this an event + if target2.nick in var.LOVERS.get(target1.nick, set()): + same = True + + if same: + wrapper.pm(messages["investigator_results_same"].format(target1, target2)) + else: + wrapper.pm(messages["investigator_results_different"].format(target1, target2)) + + INVESTIGATED.add(wrapper.source) + debuglog("{0} (investigator) ID: {1} ({2}) and {3} ({4}) as {5}".format( + wrapper.source, target1, get_main_role(target1), target2, get_main_role(target2), + "same" if same else "different")) + +@event_listener("swap_player") +def on_swap(evt, var, old_user, user): + if old_user in INVESTIGATED: + INVESTIGATED.discard(old_user) + INVESTIGATED.add(user) + +@event_listener("del_player") +def on_del_player(evt, var, user, mainrole, allroles, death_triggers): + INVESTIGATED.discard(user) + +@event_listener("get_special") +def on_get_special(evt, var): + evt.data["special"].update(get_players(("investigator",))) + +@event_listener("exchange_roles") +def on_exchange(evt, var, actor, target, actor_role, target_role): + if actor_role == "investigator" and target_role != "investigator": + INVESTIGATED.discard(actor) + elif target_role == "investigator" and actor_role != "investigator": + INVESTIGATED.discard(targe) + +@event_listener("transition_night_end", priority=2) +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], "Players: " + ", ".join(p.nick for p in pl), sep="\n") + +@event_listener("transition_night_begin") +def on_transition_night_begin(evt, cli, var): + INVESTIGATED.clear() + +@event_listener("reset") +def on_reset(evt, var): + INVESTIGATED.clear() + +# vim: set sw=4 expandtab: diff --git a/src/settings.py b/src/settings.py index 47f47d5..585e2e2 100644 --- a/src/settings.py +++ b/src/settings.py @@ -262,6 +262,7 @@ ROLE_GUIDE = OrderedDict([ # This is order-sensitive - many parts of the code re ("vigilante" , ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )), ("augur" , ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 )), ("detective" , ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 )), + ("investigator" , ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )), ("prophet" , ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )), ("guardian angel" , ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )), ("bodyguard" , ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 )),