Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gunner overhaul #431

Merged
merged 2 commits into from
Apr 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"wild child": ["wild child", "wild children"],
"wolf": ["wolf", "wolves"],
"wolf cub": ["wolf cub", "wolf cubs"],
"wolf gunner": ["wolf gunner", "wolf gunners"],
"wolf mystic": ["wolf mystic", "wolf mystics"],
"wolf shaman": ["wolf shaman", "wolf shamans"],
"**": "The following items are names for the 'team reveal' role reveal option",
Expand Down Expand Up @@ -728,9 +729,6 @@
"bodyguard_protected_wolf_no_reveal": "{0:@} made the unfortunate mistake of guarding a wolf last night, and is now dead.",
"guardian_angel_protected_wolf": "{0:@}, {=guardian angel!role:article} {=guardian angel!role:bold}, made the unfortunate mistake of guarding a wolf last night, and is now dead.",
"guardian_angel_protected_wolf_no_reveal": "{0:@} made the unfortunate mistake of guarding a wolf last night, and is now dead.",
"gunner_killed_wolf_overnight": "Fortunately, {0:@} had bullets and {1:@}, {2!role:article} {2!role:bold}, was shot dead.",
"gunner_killed_wolf_overnight_no_reveal": "Fortunately, {0:@} had bullets and {1:@} was shot dead.",
"wolf_gunner": "While searching {0}'s belongings, you found a gun loaded with 1 silver bullet! You may only use it during the day. If you shoot at a wolf, you will intentionally miss. If you shoot a villager, it is likely that they will be injured.",
"totem_possession_alive": "{0:@} seems to be in possession of {=a,{1:bold}:plural({1})} mysterious {=totem,totems:plural({1})}...",
"totem_possession_dead": "{0:@} seemed to be in possession of {=a,{1:bold}:plural({1})} mysterious {=totem,totems:plural({1})}...",
"totem_broken": "Broken totem pieces were found next to {0:@}'s body...",
Expand Down Expand Up @@ -791,9 +789,9 @@
"wolf_no_target_wolf": "You may only kill villagers, not other wolves.",
"wolf_must_target_multiple": "You should select two different players.",
"player_kill": "You have selected {0:@} to be killed.",
"player_kill_multiple": "You have selected {0:@} and {1:@} to be killed.",
"player_kill_multiple": "You have selected {0:join(@)} to be killed.",
"wolfchat_kill": "{0:@} has selected {1:@} to be killed.",
"wolfchat_kill_multiple": "{0:@} has selected {1:@} and {2:@} to be killed.",
"wolfchat_kill_multiple": "{0:@} has selected {1:join(@)} to be killed.",
"already_protecting": "You are already protecting someone tonight.",
"guardian_target_another": "You protected {0:@} last night. You cannot protect the same person two nights in a row.",
"cannot_guard_self": "You cannot guard yourself. Use {=pass!command} if you do not wish to guard anyone tonight.",
Expand Down Expand Up @@ -910,15 +908,15 @@
"werecrow_notify": "You are {=werecrow!role:article} {=werecrow!role:bold}. You are able to fly at night. Use \"{=observe!command} <nick>\" to check if someone is in bed or not. You may also use \"{=kill!command} <nick>\" to kill a villager.",
"hag_notify": "You are {=hag!role:article} {=hag!role:bold}. You can hex someone to prevent them from using any special powers they may have during the next day and night. Use \"{=hex!command} <nick>\" to hex them. Only detectives can reveal your true identity, seers will see you as a regular villager.",
"sorcerer_notify": "You are {=sorcerer!role:article} {=sorcerer!role:bold}. You can use \"{=observe!command} <nick>\" to observe someone and determine if they are {=Spy!cat:article} {=Spy!cat}. Only detectives can reveal your true identity, seers will see you as a regular villager.",
"wolf_cub_notify": "You are {=wolf cub!role:article} {=wolf cub!role:bold}. While you cannot kill anyone, the other wolves will become enraged if you die and will get two kills the following night.",
"wolf_cub_notify": "You are {=wolf cub!role:article} {=wolf cub!role:bold}. Should you die, the other wolves will become enraged and will get two kills the following night. When all of the other wolves are dead, you will grow up into a wolf.",
"alpha_wolf_notify": "You are {=alpha wolf!role:article} {=alpha wolf!role:bold}. Once per game following the death of another wolf, you can bite someone by using \"{=bite!command} <nick>\" to turn them into a wolf. You may also use \"{=kill!command} <nick>\" to kill a villager.",
"werekitten_notify": "You are {=werekitten!role:article} {=werekitten!role:bold}. Due to your overwhelming cuteness, the {=seer!role} always sees you as villager and the {=gunner!role} will always miss you. Detectives can still reveal your true identity, however. Use \"{=kill!command} <nick>\" to kill a villager.",
"warlock_notify": "You are {=warlock!role:article} {=warlock!role:bold}. Each night you can curse someone with \"{=curse!command} <nick>\" to turn them into {=cursed villager!role:article} {=cursed villager!role}, so the {=seer!role} sees them as {=wolf!role}. Act quickly, as your curse applies as soon as you cast it! Only detectives can reveal your true identity, seers will see you as a regular villager.",
"wolf_mystic_notify": "You are {=wolf mystic!role:article} {=wolf mystic!role:bold}. Each night you divine the number of alive good villagers who have a special role. You may also use \"{=kill!command} <nick>\" to kill a villager.",
"wolf_shaman_notify": "You may also use \"{=kill!command} <nick>\" to kill a villager.",
"fallen_angel_notify": "You are {=fallen angel!role:article} {=fallen angel!role:bold}. Your sharp claws will rend any protection the villagers may have, and will likely kill living guardians as well. Use \"{=kill!command} <nick>\" to kill a villager.",
"wolf_gunner_notify": "You are a {=wolf gunner!role:article} {=wolf gunner!role:bold} and hold a gun. You may only use it during the day by typing \"{=shoot!command:!} <nick>\" in channel. If you shoot at a wolf, you will intentionally miss. If you shoot a villager, it is likely that they will die. You may also use \"{=kill!command} <nick>\" during the night to kill a villager.",
"undefined_role_notify": "You are {0:article} {0:bold}. There would normally be instructions here, but someone forgot to add them in. Please report this to the admins, you can PM me \"{=admins!command}\" for a list of available ones.",
"wolfchat_notify_0": "Wolfchat is currently disabled.",
"wolfchat_notify_1": "Also, if you PM me, your message will be relayed to other wolves.",
"wolfchat_notify_2": "Also, if you PM me during the day, your message will be relayed to other wolves.",
"wolfchat_notify_3": "Also, if you PM me during the night, your message will be relayed to other wolves.",
Expand All @@ -929,7 +927,7 @@
"relay_message_wolfchat": "[[wolfchat]] {0:@} says: {1}",
"relay_message_deadchat": "[[deadchat]] {0:@} says: {1}",
"relay_command_wolfchat": "[[wolfchat]] {0}",
"cursed_notify": "You are [b]cursed[/b]. You will be seen by the {=seer!role} and {=oracle!role} as being {=wolf!role:article} {=wolf!role} rather than your normal role!",
"cursed_notify": "You are [b]{=cursed villager!role}[/b]. You will be seen by the {=seer!role} and {=oracle!role} as being {=wolf!role:article} {=wolf!role} rather than your normal role!",
"seer_info_general": "You are {0!role:article} {0!role:bold}. It is your job to detect the wolves, you may have a vision once per night.",
"seer_info": "Use \"{=see!command} <nick>\" to see the role of a player.",
"oracle_info": "Use \"{=see!command} <nick>\" to see whether or not a player is a wolf.",
Expand Down Expand Up @@ -1010,9 +1008,14 @@
"turncoat_no_team": "If you die before selecting a side, you will not win.",
"matchmaker_notify": "You are {=matchmaker!role:article} {=matchmaker!role:bold}. You can select two players to be lovers with \"{=match!command} <nick1> <nick2>\". If one lover dies, the other will as well. You may select yourself as one of the lovers. You may only select lovers during the first night. If you do not select lovers, they will be randomly selected and you will not be told who they are (unless you are one of them).",
"clone_notify": "You are {=clone!role:article} {=clone!role:bold}. You can select someone to clone with \"{=clone!command} <nick>\". If that player dies, you become their role. You may only clone someone during the first night.",
"gunner_notify": "You are {=gunner!role:article} {=gunner!role:bold} and hold a gun that shoots special silver bullets. You may only use it during the day by typing \"{=shoot!command:!} <nick>\" in channel. Wolves and the crow will die instantly when shot, but anyone else will likely survive. You have {0} {=bullet,bullets:plural({0})}.",
"gunner_notify": "You are {=gunner!role:article} {=gunner!role:bold} and hold a gun that shoots special silver bullets. You may only use it during the day by typing \"{=shoot!command:!} <nick>\" in channel. Wolves will die instantly when shot, but anyone else will likely survive. You have {0} {=bullet,bullets:plural({0})}.",
"gunner_myrole": "You are {0!role:article} {0!role:bold} and have {1} {=bullet,bullets:plural({1})}.",
"sharpshooter_notify": "You are {=sharpshooter!role:article} {=sharpshooter!role:bold} and hold a gun that shoots special silver bullets. You may only use it during the day by typing \"{=shoot!command:!} <nick>\" in channel. Wolves and the crow will die instantly when shot, and anyone else will likely die as well due to your skill with the gun. You have {0} {=bullet,bullets:plural({0})}.",
"sharpshooter_notify": "You are {=sharpshooter!role:article} {=sharpshooter!role:bold} and hold a gun that shoots special silver bullets. You may only use it during the day by typing \"{=shoot!command:!} <nick>\" in channel. Due to your skill with the gun, anyone you shoot is very likely to die. You have {0} {=bullet,bullets:plural({0})}.",
"gunner_bullets": "You have {0} {=bullet,bullets:plural({0})}.",
"wolf_gunner": "While searching {0}'s belongings, you found a gun loaded with 1 silver bullet! You may only use it during the day. If you shoot at a wolf, you will intentionally miss. If you shoot a villager, it is likely that they will die.",
"gunner_killed_wolf_overnight": "Fortunately, {0:@} had bullets and {1:@}, {2!role:article} {2!role:bold}, was shot dead.",
"gunner_killed_wolf_overnight_no_reveal": "Fortunately, {0:@} had bullets and {1:@} was shot dead.",
"gunner_shoot_overnight_no_death": "{0:@} had bullets, but their shot missed their assailant.",
"night_begin": "It is now nighttime. All players check for PMs from me for instructions.",
"first_night_begin": "If you did not receive one, simply sit back, relax, and wait patiently for morning.",
"game_mode_not_found": "Mode {0:bold} not found.",
Expand Down
6 changes: 3 additions & 3 deletions src/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,9 +405,9 @@ def __call__(self, *args, **kwargs):
func = func.func
if self.listener_id is None:
self.listener_id = func.__qualname__
# always prefix with module for disambiguation if possible
if func.__module__ is not None:
self.listener_id = func.__module__ + "." + self.listener_id
# always prefix with module for disambiguation if possible
if func.__module__ is not None:
self.listener_id = func.__module__ + "." + self.listener_id
self.func = handle_error(func)
self.listener = events.EventListener(self.func, priority=self.priority, listener_id=self.listener_id)
self.listener.install(self.event)
Expand Down
2 changes: 1 addition & 1 deletion src/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def id(self):
def id(self, value):
raise ValueError("Cannot modify id attribute")

def find_listener(event: str, listener_id: str) -> Optional[EventListener]:
def find_listener(event: str, listener_id: str) -> EventListener:
for evt in EVENT_CALLBACKS[event]:
if evt.id == listener_id:
return evt
Expand Down
15 changes: 9 additions & 6 deletions src/gamemodes/drunkfire.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ def __init__(self, arg=""):
self.SHORT_DAY_LIMIT = 240
self.SHORT_DAY_WARN = 180
self.NIGHT_TIME_LIMIT = 60
self.NIGHT_TIME_WARN = 40 # HIT MISS HEADSHOT
self.GUN_CHANCES = ( 3/7 , 3/7 , 4/5 )
self.WOLF_GUN_CHANCES = ( 4/7 , 3/7 , 1 )
self.NIGHT_TIME_WARN = 40
self.GUN_CHANCES = {
"gunner": (10/20, 8/20, 16/20), # 50% hit, 40% miss, 10% explode, 80% headshot
"wolf gunner": (12/20, 8/20, 1), # 60% hit, 40% miss, 0% explode, 100% headshot
"sharpshooter": (1, 0, 1) # 100% hit, 0% miss, 0% explode, 100% headshot
}
self.ROLE_GUIDE = {
8: ["wolf", "traitor", "seer", "village drunk", "village drunk(2)", "cursed villager", "gunner", "gunner(2)", "gunner(3)", "sharpshooter", "sharpshooter(2)"],
10: ["wolf(2)", "village drunk(3)", "gunner(4)"],
8: ["wolf gunner", "traitor", "seer", "village drunk", "village drunk(2)", "cursed villager", "gunner", "gunner(2)", "gunner(3)", "sharpshooter", "sharpshooter(2)"],
10: ["wolf gunner(2)", "village drunk(3)", "gunner(4)"],
12: ["hag", "village drunk(4)", "crazed shaman", "sharpshooter(3)"],
14: ["wolf(3)", "seer(2)", "gunner(5)", "assassin"],
14: ["wolf gunner(3)", "seer(2)", "gunner(5)", "assassin"],
16: ["traitor(2)", "village drunk(5)", "sharpshooter(4)"],
}
self.EVENTS = {
Expand Down
29 changes: 21 additions & 8 deletions src/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,19 +132,32 @@ def parse_and_dispatch(var,
common_roles = set(roles) # roles shared by every eligible role command
# A user can be a participant but not have a role, for example, dead vengeful ghost
has_roles = len(roles) != 0
if role_prefix is not None:
roles &= {role_prefix} # only fire off role commands for the user-specified role
else:
roles = set()
common_roles = set()
has_roles = False

for fn in decorators.COMMANDS.get(key, []):
if not fn.roles:
cmds.append(fn)
elif roles.intersection(fn.roles):
cmds.append(fn)
common_roles.intersection_update(fn.roles)
for i in range(2):
cmds.clear()
# if we execute this loop twice, it means we had an ambiguity the first time around
# only fire off role commands for the user-specified role in that event, if one was provided
# doing it this way ensures we only look at the role prefix if it's actually required,
# meaning that prefixing a role for public commands doesn't "prove" the user has that role
# in the vast majority of cases
if i == 1 and role_prefix is not None:
roles &= {role_prefix}

for fn in decorators.COMMANDS.get(key, []):
if not fn.roles:
cmds.append(fn)
elif roles.intersection(fn.roles):
cmds.append(fn)
common_roles.intersection_update(fn.roles)

# if this isn't a role command or the command is unambiguous, continue logic instead of
# making use of role_prefix
if not has_roles or common_roles:
break

if has_roles and not common_roles:
# getting here means that at least one of the role_cmds is disjoint
Expand Down
4 changes: 2 additions & 2 deletions src/roles/alphawolf.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from src.messages import messages
from src.status import try_misdirection, try_exchange, add_lycanthropy, add_lycanthropy_scope
from src.cats import Wolf, All
from src.roles.helper.wolves import is_known_wolf_ally, send_wolfchat_message, get_wolfchat_roles, register_killer
from src.roles.helper.wolves import is_known_wolf_ally, send_wolfchat_message, register_wolf

register_killer("alpha wolf")
register_wolf("alpha wolf")

ENABLED = False
ALPHAS = UserSet() # type: UserSet[users.User]
Expand Down
11 changes: 10 additions & 1 deletion src/roles/cursed.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
import math
from collections import defaultdict

from src.utilities import *
from src.functions import get_all_players, get_main_role
from src import users, channels, debuglog, errlog, plog
from src.decorators import command, event_listener
from src.containers import UserList, UserSet, UserDict, DefaultUserDict
from src.messages import messages
from src.status import try_misdirection, try_exchange
from src.cats import Wolfchat

@event_listener("see")
def on_see(evt, var, seer, target):
Expand All @@ -20,3 +21,11 @@ def on_see(evt, var, seer, target):
def on_get_role_metadata(evt, var, kind):
if kind == "role_categories":
evt.data["cursed villager"] = {"Village", "Cursed"}

@event_listener("transition_night_end", priority=3)
def on_transition_night_end(evt, var):
cursed = get_all_players(("cursed villager",))
wolves = get_all_players(Wolfchat)
for player in cursed:
if get_main_role(player) == "cursed villager" or cursed in wolves:
cursed.send(messages["cursed_notify"])
4 changes: 2 additions & 2 deletions src/roles/doomsayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from src.status import try_misdirection, try_exchange
from src.cats import All

from src.roles.helper.wolves import is_known_wolf_ally, register_killer, send_wolfchat_message
from src.roles.helper.wolves import is_known_wolf_ally, register_wolf, send_wolfchat_message

register_killer("doomsayer")
register_wolf("doomsayer")

SEEN = UserSet()
KILLS = UserDict()
Expand Down
4 changes: 2 additions & 2 deletions src/roles/fallenangel.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
from src.status import try_misdirection, try_exchange
from src.cats import Wolf

from src.roles.helper.wolves import register_killer
from src.roles.helper.wolves import register_wolf

register_killer("fallen angel")
register_wolf("fallen angel")

@event_listener("try_protection")
def on_try_protection(evt, var, target, attacker, attacker_role, reason):
Expand Down
19 changes: 0 additions & 19 deletions src/roles/gunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,6 @@

GUNNERS = setup_variables("gunner")

@event_listener("gun_chances")
def on_gun_chances(evt, var, user, role):
if role == "gunner":
hit, miss, headshot = var.GUN_CHANCES
evt.data["hit"] = hit
evt.data["miss"] = miss
evt.data["headshot"] = headshot

@event_listener("new_role")
def on_new_role(evt, var, user, old_role):
if old_role == "gunner":
if evt.data["role"] != "gunner":
del GUNNERS[user]

elif evt.data["role"] == "gunner":
GUNNERS[user] = math.ceil(var.SHOTS_MULTIPLIER * len(get_players()))
if user in get_all_players(("village drunk",)):
GUNNERS[user] *= var.DRUNK_SHOTS_MULTIPLIER

@event_listener("get_role_metadata")
def on_get_role_metadata(evt, var, kind):
if kind == "role_categories":
Expand Down
4 changes: 3 additions & 1 deletion src/roles/hag.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
from src.messages import messages
from src.status import try_misdirection, try_exchange, add_silent

from src.roles.helper.wolves import is_known_wolf_ally, send_wolfchat_message
from src.roles.helper.wolves import is_known_wolf_ally, send_wolfchat_message, register_wolf

register_wolf("hag")

HEXED = UserDict() # type: UserDict[users.User, users.User]
LASTHEXED = UserDict() # type: UserDict[users.User, users.User]
Expand Down
Loading