Part 2 of 2

And as thus we sat in darkness,
Each one busy in his prayers,
“We are lost!” the captain shouted,
As he staggered down the stairs.

But his little daughter whispered,
As she took his icy hand,
“Isn’t God upon the ocean,
Just the same as on the land?”

Then we kissed the little maiden,
And we spoke in better cheer;
And we anchored safe in harbor
When the morn was shining clear.
This commit is contained in:
skizzerz 2016-02-03 22:04:29 -06:00
parent 69f72499ef
commit dbad8d1f09
4 changed files with 98 additions and 28 deletions

View File

@ -432,7 +432,7 @@
"day_lasted": "Day lasted \u0002{0:0>2}:{1:0>2}\u0002. ", "day_lasted": "Day lasted \u0002{0:0>2}:{1:0>2}\u0002. ",
"fallen_angel_turn": "As the moonlight filters through your window, you think back on the past few days. Your power has been growing, but the villagers you protect subconsciously detected your shift and have been keeping more distant from you. Grinning with wicked resolve, you vow to show them what fools they have been as you take to the skies once more with an unholy vengeance. Soon they will know true fear.", "fallen_angel_turn": "As the moonlight filters through your window, you think back on the past few days. Your power has been growing, but the villagers you protect subconsciously detected your shift and have been keeping more distant from you. Grinning with wicked resolve, you vow to show them what fools they have been as you take to the skies once more with an unholy vengeance. Soon they will know true fear.",
"bitten_turn": "As you prepare for bed, you watch in horror as your body starts growing a coat of fur! Sudden realization hits you as you grin with your now muzzled face; that mysterious bite earlier slowly changed you into a werewolf! You feel bigger, stronger, faster, and ready to seize the night as you stealthily exit your home and search for the rest of your pack...", "bitten_turn": "As you prepare for bed, you watch in horror as your body starts growing a coat of fur! Sudden realization hits you as you grin with your now muzzled face; that mysterious bite earlier slowly changed you into a werewolf! You feel bigger, stronger, faster, and ready to seize the night as you stealthily exit your home and search for the rest of your pack...",
"bitten_turn_wolfchat": "\u0002{0}\u0002 is now a \u0002{1}\u0002!", "wolfchat_new_member": "\u0002{0}\u0002 is now a \u0002{1}\u0002!",
"amnesia_clear": "Your amnesia clears and you now remember that you are a{0} \u0002{1}\u0002!", "amnesia_clear": "Your amnesia clears and you now remember that you are a{0} \u0002{1}\u0002!",
"amnesia_wolfchat": "\u0002{0}\u0002 is now a \u0002{1}\u0002!", "amnesia_wolfchat": "\u0002{0}\u0002 is now a \u0002{1}\u0002!",
"wolf_notify": "You are a \u0002wolf\u0002. It is your job to kill all the villagers. Use \"kill <nick>\" to kill a villager.", "wolf_notify": "You are a \u0002wolf\u0002. It is your job to kill all the villagers. Use \"kill <nick>\" to kill a villager.",

View File

@ -24,12 +24,12 @@ class Event:
self.name = name self.name = name
self.data = data self.data = data
def dispatch(self, *args): def dispatch(self, *args, **kwargs):
if self.name not in EVENT_CALLBACKS: if self.name not in EVENT_CALLBACKS:
return True return True
for item in list(EVENT_CALLBACKS[self.name]): for item in list(EVENT_CALLBACKS[self.name]):
item[1](self, *args) item[1](self, *args, **kwargs)
if self.stop_processing: if self.stop_processing:
break break

View File

@ -1055,14 +1055,63 @@ class MaelstromMode(GameMode):
self.LOVER_WINS_WITH_FOOL = True self.LOVER_WINS_WITH_FOOL = True
self.MAD_SCIENTIST_SKIPS_DEAD_PLAYERS = 0 # always make it happen self.MAD_SCIENTIST_SKIPS_DEAD_PLAYERS = 0 # always make it happen
self.ALWAYS_PM_ROLE = True self.ALWAYS_PM_ROLE = True
# clone is pointless in this mode
# dullahan doesn't really work in this mode either, if enabling anyway special logic to determine kill list
# needs to be added above for when dulls are added during the game
# matchmaker is conditionally enabled during night 1 only
self.roles = list(var.ROLE_GUIDE.keys() - var.TEMPLATE_RESTRICTIONS.keys() - {"amnesiac", "clone", "dullahan", "matchmaker"})
def startup(self): def startup(self):
events.add_listener("role_attribution", self.role_attribution) events.add_listener("role_attribution", self.role_attribution)
events.add_listener("transition_night_begin", self.transition_night_begin) events.add_listener("transition_night_begin", self.transition_night_begin)
events.add_listener("join", self.on_join)
def teardown(self): def teardown(self):
events.remove_listener("role_attribution", self.role_attribution) events.remove_listener("role_attribution", self.role_attribution)
events.remove_listener("transition_night_begin", self.transition_night_begin) events.remove_listener("transition_night_begin", self.transition_night_begin)
events.remove_listener("join", self.on_join)
def on_join(self, evt, cli, var, nick, chan, rest, forced=False):
if var.PHASE != "day" or (nick != chan and chan != botconfig.CHANNEL):
return
if not forced and evt.data["join_player"](cli, nick, botconfig.CHANNEL, sanity=False):
self._on_join(cli, var, nick, chan)
evt.prevent_default = True
elif forced:
# in fjoin, handle this differently
jp = evt.data["join_player"]
evt.data["join_player"] = lambda cli, nick, chan, who=None, forced=False: jp(cli, nick, chan, who=who, forced=forced, sanity=False) and self._on_join(cli, var, nick, chan)
def _on_join(self, cli, var, nick, chan):
role = random.choice(self.roles)
var.ROLES[role].add(nick)
var.ORIGINAL_ROLES[role].add(nick)
var.FINAL_ROLES[nick] = role
if role == "doctor":
lpl = len(var.list_players())
var.DOCTORS[nick] = math.ceil(var.DOCTOR_IMMUNIZATION_MULTIPLIER * lpl)
# let them know their role
# FIXME: this is fugly
from src.decorators import COMMANDS
COMMANDS["myrole"][0].caller(cli, nick, chan, "")
# if they're a wolfchat role, alert the other wolves
if role in var.WOLFCHAT_ROLES:
relay_wolfchat_command(cli, nick, messages["wolfchat_new_member"].format(nick, role), var.WOLFCHAT_ROLES, is_wolf_command=True, is_kill_command=True)
# TODO: make this part of !myrole instead, no reason we can't give out wofllist in that
wolves = var.list_players(var.WOLFCHAT_ROLES)
pl = var.list_players()
random.shuffle(pl)
pl.remove(nick)
for i, player in enumerate(pl):
prole = var.get_role(player)
if prole in var.WOLFCHAT_ROLES:
cursed = ""
if player in var.ROLES["cursed villager"]:
cursed = "cursed "
pl[i] = "\u0002{0}\u0002 ({1}{2})".format(player, cursed, prole)
elif player in var.ROLES["cursed villager"]:
pl[i] = player + " (cursed)"
pm(cli, nick, "Players: " + ", ".join(pl))
def role_attribution(self, evt, cli, chk_win_conditions, var, villagers): def role_attribution(self, evt, cli, chk_win_conditions, var, villagers):
self.chk_win_conditions = chk_win_conditions self.chk_win_conditions = chk_win_conditions
@ -1112,13 +1161,10 @@ class MaelstromMode(GameMode):
wolves = var.WOLF_ROLES - {"wolf cub"} wolves = var.WOLF_ROLES - {"wolf cub"}
addroles[random.choice(list(wolves))] += 1 # make sure there's at least one wolf role addroles[random.choice(list(wolves))] += 1 # make sure there's at least one wolf role
# clone is pointless in this mode roles = self.roles[:]
# dullahan doesn't really work in this mode either, if enabling anyway special logic to determine kill list if do_templates:
# needs to be added above for when dulls are added during the game
roles = list(var.ROLE_GUIDE.keys() - var.TEMPLATE_RESTRICTIONS.keys() - {"amnesiac", "clone", "dullahan"})
if not do_templates:
# mm only works night 1, do_templates is also only true n1 # mm only works night 1, do_templates is also only true n1
roles.remove("matchmaker") self.roles.append("matchmaker")
while lpl: while lpl:
addroles[random.choice(roles)] += 1 addroles[random.choice(roles)] += 1
lpl -= 1 lpl -= 1

View File

@ -1118,6 +1118,14 @@ def deadchat_pref(cli, nick, chan, rest):
@cmd("join", "j", pm=True) @cmd("join", "j", pm=True)
def join(cli, nick, chan, rest): def join(cli, nick, chan, rest):
"""Either starts a new game of Werewolf or joins an existing game that has not started yet.""" """Either starts a new game of Werewolf or joins an existing game that has not started yet."""
# keep this and the event in fjoin() in sync
evt = Event("join", {
"join_player": join_player,
"join_deadchat": join_deadchat,
"vote_gamemode": vote_gamemode
})
if not evt.dispatch(cli, var, nick, chan, rest, forced=False):
return
if var.PHASE in ("none", "join"): if var.PHASE in ("none", "join"):
if chan == nick: if chan == nick:
return return
@ -1125,25 +1133,25 @@ def join(cli, nick, chan, rest):
if nick in var.USERS and (not var.USERS[nick]["account"] or var.USERS[nick]["account"] == "*"): if nick in var.USERS and (not var.USERS[nick]["account"] or var.USERS[nick]["account"] == "*"):
cli.notice(nick, messages["not_logged_in"]) cli.notice(nick, messages["not_logged_in"])
return return
if join_player(cli, nick, chan) and rest: if evt.data["join_player"](cli, nick, chan) and rest:
vote_gamemode(cli, nick, chan, rest.lower().split()[0], False) evt.data["vote_gamemode"](cli, nick, chan, rest.lower().split()[0], False)
else: # join deadchat else: # join deadchat
if chan == nick and nick != botconfig.NICK: if chan == nick and nick != botconfig.NICK:
join_deadchat(cli, nick) evt.data["join_deadchat"](cli, nick)
def join_player(cli, player, chan, who = None, forced = False): def join_player(cli, player, chan, who=None, forced=False, *, sanity=True):
if who is None: if who is None:
who = player who = player
pl = var.list_players() pl = var.list_players()
if chan != botconfig.CHANNEL: if chan != botconfig.CHANNEL:
return return False
if not var.OPPED: if not var.OPPED:
cli.notice(who, messages["bot_not_opped"].format(chan)) cli.notice(who, messages["bot_not_opped"].format(chan))
cli.msg("ChanServ", "op " + botconfig.CHANNEL) cli.msg("ChanServ", "op " + botconfig.CHANNEL)
return return False
if player in var.USERS: if player in var.USERS:
ident = var.USERS[player]["ident"] ident = var.USERS[player]["ident"]
@ -1155,7 +1163,7 @@ def join_player(cli, player, chan, who = None, forced = False):
host = None host = None
acc = None acc = None
else: else:
return # Not normal return False # Not normal
if not acc or acc == "*" or var.DISABLE_ACCOUNTS: if not acc or acc == "*" or var.DISABLE_ACCOUNTS:
acc = None acc = None
@ -1174,11 +1182,10 @@ def join_player(cli, player, chan, who = None, forced = False):
cli.notice(who, messages["stasis"].format( cli.notice(who, messages["stasis"].format(
"you are" if player == who else player + " is", stasis, "you are" if player == who else player + " is", stasis,
"s" if stasis != 1 else "")) "s" if stasis != 1 else ""))
return return False
cmodes = [("+v", player)] cmodes = [("+v", player)]
if var.PHASE == "none": if var.PHASE == "none":
if var.AUTO_TOGGLE_MODES and player in var.USERS and var.USERS[player]["modes"]: if var.AUTO_TOGGLE_MODES and player in var.USERS and var.USERS[player]["modes"]:
for mode in var.USERS[player]["modes"]: for mode in var.USERS[player]["modes"]:
cmodes.append(("-"+mode, player)) cmodes.append(("-"+mode, player))
@ -1210,13 +1217,16 @@ def join_player(cli, player, chan, who = None, forced = False):
elif player in pl: elif player in pl:
cli.notice(who, messages["already_playing"].format("You" if who == player else "They")) cli.notice(who, messages["already_playing"].format("You" if who == player else "They"))
return True # if we're not doing insane stuff, return True so that one can use !join to vote for a game mode
# even if they are already joined. If we ARE doing insane stuff, return False to indicate that
# the player was not successfully joined by this call.
return sanity
elif len(pl) >= var.MAX_PLAYERS: elif len(pl) >= var.MAX_PLAYERS:
cli.notice(who, messages["too_many_players"]) cli.notice(who, messages["too_many_players"])
return return False
elif var.PHASE != "join": elif sanity and var.PHASE != "join":
cli.notice(who, messages["game_already_running"]) cli.notice(who, messages["game_already_running"])
return return False
else: else:
if acc is not None and not botconfig.DEBUG_MODE: if acc is not None and not botconfig.DEBUG_MODE:
for user in pl: for user in pl:
@ -1228,7 +1238,6 @@ def join_player(cli, player, chan, who = None, forced = False):
cli.notice(who, msg.format(user, "their", "")) cli.notice(who, msg.format(user, "their", ""))
return return
var.ROLES["person"].add(player)
var.ALL_PLAYERS.append(player) var.ALL_PLAYERS.append(player)
if not is_fake_nick(player) or not botconfig.DEBUG_MODE: if not is_fake_nick(player) or not botconfig.DEBUG_MODE:
if var.AUTO_TOGGLE_MODES and var.USERS[player]["modes"]: if var.AUTO_TOGGLE_MODES and var.USERS[player]["modes"]:
@ -1238,6 +1247,13 @@ def join_player(cli, player, chan, who = None, forced = False):
var.USERS[player]["modes"] = set() var.USERS[player]["modes"] = set()
mass_mode(cli, cmodes, []) mass_mode(cli, cmodes, [])
cli.msg(chan, messages["player_joined"].format(player, len(pl) + 1)) cli.msg(chan, messages["player_joined"].format(player, len(pl) + 1))
if not sanity:
# Abandon Hope All Ye Who Enter Here
leave_deadchat(cli, player)
var.SPECTATING_DEADCHAT.discard(player)
var.SPECTATING_WOLFCHAT.discard(player)
return True
var.ROLES["person"].add(player)
if not is_fake_nick(player): if not is_fake_nick(player):
hostmask = ident + "@" + host hostmask = ident + "@" + host
if hostmask not in var.JOINED_THIS_GAME and (not acc or acc not in var.JOINED_THIS_GAME_ACCS): if hostmask not in var.JOINED_THIS_GAME and (not acc or acc not in var.JOINED_THIS_GAME_ACCS):
@ -1287,9 +1303,17 @@ def kill_join(cli, chan):
var.AFTER_FLASTGAME = None var.AFTER_FLASTGAME = None
@cmd("fjoin", admin_only=True, phases=("none", "join")) @cmd("fjoin", admin_only=True)
def fjoin(cli, nick, chan, rest): def fjoin(cli, nick, chan, rest):
"""Forces someone to join a game.""" """Forces someone to join a game."""
# keep this and the event in def join() in sync
evt = Event("join", {
"join_player": join_player,
"join_deadchat": join_deadchat,
"vote_gamemode": vote_gamemode
})
if not evt.dispatch(cli, var, nick, chan, rest, forced=True):
return
noticed = False noticed = False
fake = False fake = False
if not var.OPPED: if not var.OPPED:
@ -1297,7 +1321,7 @@ def fjoin(cli, nick, chan, rest):
cli.msg("ChanServ", "op " + botconfig.CHANNEL) cli.msg("ChanServ", "op " + botconfig.CHANNEL)
return return
if not rest.strip(): if not rest.strip():
join_player(cli, nick, chan, forced=True) evt.data["join_player"](cli, nick, chan, forced=True)
for tojoin in re.split(" +",rest): for tojoin in re.split(" +",rest):
tojoin = tojoin.strip() tojoin = tojoin.strip()
@ -1309,7 +1333,7 @@ def fjoin(cli, nick, chan, rest):
break break
fake = True fake = True
for i in range(int(first), int(last)+1): for i in range(int(first), int(last)+1):
join_player(cli, str(i), chan, forced=True, who=nick) evt.data["join_player"](cli, str(i), chan, forced=True, who=nick)
continue continue
if not tojoin: if not tojoin:
continue continue
@ -1330,7 +1354,7 @@ def fjoin(cli, nick, chan, rest):
elif botconfig.DEBUG_MODE: elif botconfig.DEBUG_MODE:
fake = True fake = True
if tojoin != botconfig.NICK: if tojoin != botconfig.NICK:
join_player(cli, tojoin, chan, forced=True, who=nick) evt.data["join_player"](cli, tojoin, chan, forced=True, who=nick)
else: else:
cli.notice(nick, messages["not_allowed"]) cli.notice(nick, messages["not_allowed"])
if fake: if fake:
@ -6636,7 +6660,7 @@ def transition_night(cli):
var.ROLES[chumprole].remove(chump) var.ROLES[chumprole].remove(chump)
var.ROLES[newrole].add(chump) var.ROLES[newrole].add(chump)
var.FINAL_ROLES[chump] = newrole var.FINAL_ROLES[chump] = newrole
relay_wolfchat_command(cli, chump, messages["bitten_turn_wolfchat"].format(chump, newrole), var.WOLF_ROLES, is_wolf_command=True, is_kill_command=True) relay_wolfchat_command(cli, chump, messages["wolfchat_new_member"].format(chump, newrole), var.WOLF_ROLES, is_wolf_command=True, is_kill_command=True)
# convert amnesiac # convert amnesiac
if var.NIGHT_COUNT == var.AMNESIAC_NIGHTS: if var.NIGHT_COUNT == var.AMNESIAC_NIGHTS: