From 33b50a651a3df1bb20c961ce999107575c7b42dd Mon Sep 17 00:00:00 2001 From: Elijah Perrault Date: Thu, 21 Apr 2011 20:39:55 -0600 Subject: [PATCH] Added a WerewolfAdmin module for administering the Werewolf IRC game. --- doc/CHANGELOG | 1 + modules/WerewolfAdmin.pm | 372 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 modules/WerewolfAdmin.pm diff --git a/doc/CHANGELOG b/doc/CHANGELOG index c6ae33f..a8341bd 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -3,6 +3,7 @@ Auto IRC Bot 3.0: Change Log 3.0 Indev =============================================================================== + * Added a WerewolfAdmin module for administering the Werewolf IRC game. * Fixed significant state mismatched data bugs. * Added API::IRC::ison for checking if a user is on a channel. * Werewolf: Fixed a bug in harlot staying home. diff --git a/modules/WerewolfAdmin.pm b/modules/WerewolfAdmin.pm new file mode 100644 index 0000000..4440732 --- /dev/null +++ b/modules/WerewolfAdmin.pm @@ -0,0 +1,372 @@ +# Module: WerewolfAdmin. See below for documentation. +# Copyright (C) 2010-2011 Xelhua Development Group, et al. +# This program is free software; rights to this code are stated in doc/LICENSE. +package M::WerewolfAdmin; +use strict; +use warnings; +use feature qw(switch); +use API::Std qw(cmd_add cmd_del timer_add timer_del trans conf_get); +use API::IRC qw(privmsg notice cmode ison); + +# Initialization subroutine. +sub _init { + # Create the WOLFA command. + cmd_add('WOLFA', 0, 'werewolf.admin', \%M::WerewolfAdmin::HELP_WOLFA, \&M::WerewolfAdmin::cmd_wolfa) or return; + + # Success. + return 1; +} + +# Void subroutine. +sub _void { + # Delete the WOLFA command. + cmd_del('WOLFA') or return; + + # Success. + return 1; +} + +# Help hash for the WOLFA command. +our %HELP_WOLFA = ( + en => "This command allows you to perform various administrative actions in a game of Werewolf (A.K.A. Mafia). \2Syntax:\2 WOLFA (FJOIN|FWAIT|FSTART|KICK|FSTOP) [parameters]", +); + +# Callback for the WOLFA command. +sub cmd_wolfa { + my ($src, @argv) = @_; + + # We require at least one parameter. + if (!defined $argv[0]) { + notice($src->{svr}, $src->{nick}, trans('Not enough parameters').q{.}); + return; + } + + # Iterate the parameter. + given (uc $argv[0]) { + when (/^(JOIN|J)$/) { + # WOLFA JOIN + + # Requires an extra parameter. + if (!defined $argv[1]) { + notice($src->{svr}, $src->{nick}, trans('Not enough parameters').q{.}); + return; + } + + # Check if a game is running. + if (!$M::Werewolf::PGAME and !$M::Werewolf::GAME) { + notice($src->{svr}, $src->{nick}, 'No game is currently running.'); + } + elsif ($M::Werewolf::GAME) { + notice($src->{svr}, $src->{nick}, 'Sorry, but the game is already running. Try again next time.'); + } + else { + # Check if this is the game channel. + if ($src->{svr}.'/'.$src->{chan} ne $M::Werewolf::GAMECHAN) { + notice($src->{svr}, $src->{nick}, "Werewolf is currently running in \2$M::Werewolf::GAMECHAN\2."); + return; + } + + # Check if this user exists in channel. + if (!ison($src->{svr}, $src->{chan}, lc $argv[1])) { + notice($src->{svr}, $src->{nick}, "No such user \2$argv[1]\2 is on the channel."); + return; + } + + # Make sure they're not already playing. + if (exists $M::Werewolf::PLAYERS{lc $argv[1]}) { + notice($src->{svr}, $src->{nick}, 'They\'re already playing!'); + return; + } + + # Set variables. + $M::Werewolf::PLAYERS{lc $argv[1]} = 0; + my $nick = $M::Werewolf::NICKS{lc $argv[1]} = $Core::IRC::Users::users{$src->{svr}}{lc $argv[1]}; + + # Send message. + my ($gsvr, $gchan) = split '/', $M::Werewolf::GAMECHAN, 2; + cmode($gsvr, $gchan, "+v $nick"); + privmsg($gsvr, $gchan, "\2$nick\2 was forced to join the game by \2$src->{nick}\2."); + } + } + when (/^(WAIT|W)$/) { + # WOLFA WAIT + + # Check if a game is running. + if (!$M::Werewolf::PGAME) { + if (!$M::Werewolf::GAME) { + notice($src->{svr}, $src->{nick}, 'No game is currently running.'); + return; + } + else { + notice($src->{svr}, $src->{nick}, 'Werewolf is already in play.'); + return; + } + } + + # Check if this is the game channel. + if ($src->{svr}.'/'.$src->{chan} ne $M::Werewolf::GAMECHAN) { + notice($src->{svr}, $src->{nick}, "Werewolf is currently running in \2$M::Werewolf::GAMECHAN\2."); + return; + } + + # Increase WAIT. + $M::Werewolf::WAIT += 20; + # And WAITED. + $M::Werewolf::WAITED++; + privmsg($src->{svr}, $src->{chan}, "\2$src->{nick}\2 forcibly increased join wait time by 20 seconds."); + } + when ('START') { + # WOLFA START + + # Check if a game is running. + if (!$M::Werewolf::PGAME) { + if (!$M::Werewolf::GAME) { + notice($src->{svr}, $src->{nick}, 'No game is currently running.'); + return; + } + else { + notice($src->{svr}, $src->{nick}, 'Werewolf is already in play.'); + return; + } + } + + # Check if this is the game channel. + if ($src->{svr}.'/'.$src->{chan} ne $M::Werewolf::GAMECHAN) { + notice($src->{svr}, $src->{nick}, "Werewolf is currently running in \2$M::Werewolf::GAMECHAN\2."); + return; + } + + # Need four or more players. + if (keys %M::Werewolf::PLAYERS < 4) { + privmsg($src->{svr}, $src->{chan}, "$src->{nick}: Four or more players are required to play."); + return; + } + + # First, determine how many players to declare a wolf. + my $cwolves = POSIX::ceil(keys(%M::Werewolf::PLAYERS) * .14); + # Only one seer, harlot, guardian angel, traitor and detective. + my $cseers = 1; + my $charlots = my $cdrunks = my $cangels = my $ctraitors = my $cdetectives = 0; + if (keys %M::Werewolf::PLAYERS >= 6) { $charlots++ unless conf_get('werewolf:rated-g') } + if (keys %M::Werewolf::PLAYERS >= 7) { $cdrunks++ unless conf_get('werewolf:rated-g') } + if (keys %M::Werewolf::PLAYERS >= 9) { $cangels++ unless conf_get('werewolf:no-angels') } + if (keys %M::Werewolf::PLAYERS >= 12 and conf_get('werewolf:traitors')) { $ctraitors++ } + if (keys %M::Werewolf::PLAYERS >= 16 and conf_get('werewolf:detectives')) { $cdetectives++ } + + # Give all players a role. + foreach my $plyr (keys %M::Werewolf::PLAYERS) { $M::Werewolf::PLAYERS{$plyr} = 'v' } + # Push players into a temporary array. + my @plyrs = keys %M::Werewolf::PLAYERS; + + # Set wolves. + while ($cwolves > 0) { + my $rpi = $plyrs[int rand scalar @plyrs]; + if ($M::Werewolf::PLAYERS{$rpi} !~ m/^(w|s|g|h|d|t)$/xsm) { + $M::Werewolf::PLAYERS{$rpi} = 'w'; + $cwolves--; + $M::Werewolf::STATIC[0] .= ", \2$M::Werewolf::NICKS{$rpi}\2"; + } + } + $M::Werewolf::STATIC[0] = substr $M::Werewolf::STATIC[0], 2; + # Set seers. + while ($cseers > 0) { + my $rpi = $plyrs[int rand scalar @plyrs]; + if ($M::Werewolf::PLAYERS{$rpi} !~ m/^(w|g|h|d|t)$/xsm) { + $M::Werewolf::PLAYERS{$rpi} = 's'; + $cseers--; + $M::Werewolf::STATIC[1] = "\2$M::Werewolf::NICKS{$rpi}\2"; + } + } + # Set harlots. + while ($charlots > 0) { + my $rpi = $plyrs[int rand scalar @plyrs]; + if ($M::Werewolf::PLAYERS{$rpi} !~ m/^(w|g|s|d|t)$/xsm) { + $M::Werewolf::PLAYERS{$rpi} = 'h'; + $charlots--; + $M::Werewolf::STATIC[2] = "\2$M::Werewolf::NICKS{$rpi}\2"; + } + } + # Set drunks. + while ($cdrunks > 0) { + my $rpi = $plyrs[int rand scalar @plyrs]; + if ($M::Werewolf::PLAYERS{$rpi} =~ m/v/xsm) { + $M::Werewolf::PLAYERS{$rpi} = 'vi'; + $cdrunks--; + } + } + # Set guardian angels. + while ($cangels > 0) { + my $rpi = $plyrs[int rand scalar @plyrs]; + if ($M::Werewolf::PLAYERS{$rpi} !~ m/^(w|h|s|d|t)$/xsm) { + $M::Werewolf::PLAYERS{$rpi} = 'g'; + $cangels--; + $M::Werewolf::STATIC[3] = "\2$M::Werewolf::NICKS{$rpi}\2"; + } + } + # Set traitors. + while ($ctraitors > 0) { + my $rpi = $plyrs[int rand scalar @plyrs]; + if ($M::Werewolf::PLAYERS{$rpi} !~ m/^(w|h|s|d|g)$/xsm) { + $M::Werewolf::PLAYERS{$rpi} = 't'; + $ctraitors--; + $M::Werewolf::STATIC[4] = "\2$M::Werewolf::NICKS{$rpi}\2"; + } + } + # Set detectives. + while ($cdetectives > 0) { + my $rpi = $plyrs[int rand scalar @plyrs]; + if ($M::Werewolf::PLAYERS{$rpi} !~ m/^(w|h|s|g|t)$/xsm) { + $M::Werewolf::PLAYERS{$rpi} = 'd'; + $cdetectives--; + $M::Werewolf::STATIC[5] = "\2$M::Werewolf::NICKS{$rpi}\2"; + } + } + + # If there's 8 or more players, give one of them a gun. + if (keys %M::Werewolf::PLAYERS >= 8) { + my $rpi = $plyrs[int rand scalar @plyrs]; + while ($M::Werewolf::PLAYERS{$rpi} =~ m/w/xsm || $M::Werewolf::PLAYERS{$rpi} =~ m/t/xsm) { $rpi = $plyrs[int rand scalar @plyrs] } + $M::Werewolf::PLAYERS{$rpi} .= 'b'; + + # And give them PLAYER COUNT * .12 bullets rounded up. + $M::Werewolf::BULLETS = POSIX::ceil(keys(%M::Werewolf::PLAYERS) * .12); + + if ($M::Werewolf::PLAYERS{$rpi} =~ m/i/xsm) { $M::Werewolf::BULLETS = $M::Werewolf::BULLETS * 3 } + } + + # Set variables. + $M::Werewolf::GAME = 1; + $M::Werewolf::PGAME = 0; + + # Set spoke variables. + foreach (keys %M::Werewolf::PLAYERS) { $M::Werewolf::SPOKE{$_} = time } + + # All players have their role, so lets begin the game! + my ($gsvr, $gchan) = split '/', $M::Werewolf::GAMECHAN, 2; + privmsg($gsvr, $gchan, "Game is now starting. (forced start by \2$src->{nick}\2)"); + cmode($gsvr, $gchan, '+m'); + # Delete waiting timer. + timer_del('werewolf.joinwait'); + # Start timer for bed checking. + timer_add('werewolf.chkbed', 2, 5, \&M::Werewolf::_chkbed); + # Initialize the nighttime. + M::Werewolf::_init_night(); + } + when (/^(KICK|K)$/) { + # WOLFA KICK + + # Check if a game is running. + if (!$M::Werewolf::GAME and !$M::Werewolf::PGAME) { + notice($src->{svr}, $src->{nick}, 'No game is currently running.'); + return; + } + + # Check if this is the game channel. + if ($src->{svr}.'/'.$src->{chan} ne $M::Werewolf::GAMECHAN) { + notice($src->{svr}, $src->{nick}, "Werewolf is currently running in \2$M::Werewolf::GAMECHAN\2."); + return; + } + + # Requires an extra parameter. + if (!defined $argv[1]) { + notice($src->{svr}, $src->{nick}, trans('Not enough parameters').q{.}); + return; + } + + # Check if the target is playing. + if (!exists $M::Werewolf::PLAYERS{lc $argv[1]}) { + notice($src->{svr}, $src->{nick}, "\2$argv[1]\2 is not currently playing."); + return; + } + + # Kill the target. + privmsg($src->{svr}, $src->{chan}, "\2$argv[1]\2 died of an unknown disease. He/She was a \2"._getrole(lc $argv[1], 2)."\2."); + _player_del(lc $argv[1]); + } + when ('STOP') { + # WOLFA STOP + + # Check if a game is running. + if (!$M::Werewolf::GAME and !$M::Werewolf::PGAME) { + notice($src->{svr}, $src->{nick}, 'No game is currently running.'); + return; + } + + # Check if this is the game channel. + if ($src->{svr}.'/'.$src->{chan} ne $M::Werewolf::GAMECHAN) { + notice($src->{svr}, $src->{nick}, "Werewolf is currently running in \2$M::Werewolf::GAMECHAN\2."); + return; + } + + # End the game. + my ($gsvr, $gchan) = split '/', $M::Werewolf::GAMECHAN, 2; + privmsg($gsvr, $gchan, "\2$src->{nick}\2 is forcing the game to end..."); + M::Werewolf::_gameover('n'); + } + default { notice($src->{svr}, $src->{nick}, trans('Unknown action', $_).q{.}) } + } + + return 1; +} + +# Start initialization. +API::Std::mod_init('WerewolfAdmin', 'Xelhua', '1.00', '3.0.0a11'); +# build: perl=5.010000 + +__END__ + +=head1 NAME + +WerewolfAdmin - Administrative functions for Werewolf + +=head1 VERSION + + 1.00 + +=head1 SYNOPSIS + +None + +=head1 DESCRIPTION + +This module allows one to administer the Werewolf IRC game, provided by the +Werewolf module. + +It provides the following commands: + + WOLFA JOIN|J - Force join. + WOLFA START - Force start. + WOLFA KICK|K - Kick a player. + WOLFA STOP - Stop a game forcibly. + +And requires the werewolf.admin privilege. + +=head1 DEPENDENCIES + +This module depends on the following Auto module(s): + +=over + +=item Werewolf + +The Werewolf module provides the game that this module provides administrative +commands for. Using this module without Werewolf will likely cause a fatal +error. + +=back + +=head1 AUTHOR + +This module was written by Elijah Perrault. + +This module is maintained by Xelhua Development Group. + +=head1 LICENSE AND COPYRIGHT + +This module is Copyright (C) 2010-2011, Xelhua Development Group. + +This module is released under the same terms as Auto itself. + +=cut + +# vim: set ai et ts=4 sw=4: