#!/usr/local/bin/perl -w # irpg bot v2.2.1 by jotun, fakeusername@fakedomainname.com # # Please mail bugs, etc. to me. There is no help available for this code; just # ask if you need an explanation of my sloppy code. Patches are welcome to # fix bugs or clean up the code, but please do not use a radically different # coding style. Thanks to everyone that's contributed! # # v2.2.1 # - fixed a bug in item finding; if unique item was better than helm, not # better than its class, you would get the item (emad) # v2.2 # - added 1/20000 chance of 'team battle' every 5 seconds. team battle is # 3 players versus 3 other players. if the first three players win, their # time is lowered by 20% of the lowest of the three's TTL. if they lose, # no time is removed from any players. there is no chance for critical # strike in a team battle (Asterax) # - max level of Jotun's Fury Colossal Sword changed to 175 # - fixed 'kick' bug; users that were kicked were not logged out # - kick added as a p250 # - b0t now only bans those non-logged in users that say 'http:' that've # been in the channel < 90 seconds # - b0t won't ban for #G7-type URLs # - b0t now shows nick of user when new account is registered # - forgot to close filehandle in loaddb(); fixed # - added a db backup every 6 hours # v2.1.3 # - fixed bug where users changing their nick would not be candidates for # auto-login on a bot restart # - changed some messages to make them more friendly to female # players (LapCat) # v2.1.2 # - HoG can now carry or displace a player 5 - 75% toward the next level # - fixed CTCP version bug # - battling was changed from all users within 7 levels of you to # all online users # - added "unique" items, or a chance starting at level 25 to roll # higher-than-normal items # v2.1.1 # - DIE, JUMP, RESTART, INFO, and PEVAL now send warnings to users that # don't have access to tell them so. they are still penalized # - b0t will now penalize users without the proper access that try to use an # admin command # - add commands CHCLASS, CHUSER, and PUSH to adjust class names, usernames, # and next time to level, respectively # - HoG could occur for offline users; this is no longer the case # - b0t now responds to CTCP version requests (drdink) # v2.1 # - b0t bans non-logged-in users that say 'http:' # - INFO did not check ha(); fixed # - b0t will automagically log you back in if you were logged in before # a b0t restart, and if you haven't changed your nick!user@host since then # - removed logging # - dropped functions relating to old database in favor of the new one # - changed level up report from seconds to duration() # - changed item/userinfo db's to one file; battles still in battles.txt # - changed challenge report from seconds to duration() # - changed penalty text to display duration() instead of seconds # - added critical strike, 1/35 chance upon winning battle to cause opponent # to lose time (dwyn) # - changed summon text for HoG (res0) # - changed access to base off of irpg username in lieu of host # - changed top player report to every 6 hours # - changed positive HoG text (res0) # - changed random HoG chance to 1/20000 every 5 seconds # v2.0.3 # - dropped top players back to 3 # - removed STATUS; TTL available through website. # - battle history added to website; added logging of battles to battles.txt # - peval did not next(); fixed. # - added HOG command, randomly chooses someone, then randomly raises/lowers # their TTL (20% raise, 80% lower). HOG is, of course, an abbreviation for # Hand of God # - added a 1/7500 random HoG into rpcheck() # v2.0.2 # - STATUS would log you out; fixed. # - could STATUS if not online; fixed. # - added DEL command to remove accounts # - added ALERT command to make channel alerts # - changed admin HELP command text to display website # v2.0.1 # - fixed self-battle bug # - changed chance to battle from 20% to 25% if level < 25, 100% if >= 25 # - setup companion website # - updated HELP command to reflect website # - changed battle gain to (max(7,opplevel/4)/100)*your_next_ttl # - added battle loss of (max(7,opplevel/7)/100)*your_next_ttl # v2.0 # - added item finding and battling # - added penalties for QUIT, PART, instead of resetting time to the # beginning of that level # v1.0 # - initial version use strict; use IO::Socket; use Data::Dumper; my $TEST_MODE = 0; # connect to another test server; see lower block my $version = "2.2.1"; my $server = "area51.slashnet.org:6667"; my $botnick = "b0t"; # nick my $botuser = "bot"; # username my $botrlnm = "http://www.slashnet.org/~b0t/"; # real name my $botchan = "#g7"; # game channel my $botidentify = "identify g7_rocks!!"; # identify to services my $botopcmd = "PRIVMSG #g7 :opz plzkthx!!1 :)))"; # op self in channel my $helpurl = "http://jotun.ultrazone.org/g7/"; # URL sent for help my $admincommurl = "http://jotun.ultrazone.org/g7/admincomms.txt"; # ^-- URL for admin help my @access = ('yawnwraith','jotun','drdink'); # ^-- usernames with admin access my $outbytes = 0; # sent bytes my $inbytes = 0; # received bytes my $debug = 0; # print debug yes/no my %onchan; # users on game channel my %rps; # role-players my $irpgdb = "irpg.db"; # database my $rpstep = 1.16; # NEXT_LEVEL=(RPBASE*(RPSTEP**CURRENT_LEVEL)) my $rpbase = 600; # NEXT_LEVEL=(RPBASE*(RPSTEP**CURRENT_LEVEL)) my $rppenstep = 1.14; # PENALTY_SECS=(PENALTY*(RPPENSTEP**CURRENT_LEVEL)) my $rpreport = 0; # constant for reporting top players my $alrmint = 5; # secs between database rewrites my @prev_online; # user@hosts online on restart, die my @auto_login; # users to automatically log back on $SIG{'HUP'} = 0; # ignore sighup if ($TEST_MODE) { $server = "user-33qt3ul.dialup.mindspring.com:7800"; $rpstep = 0; # 0-second levels $rpbase = 0; # 0-second levels } CONNECT: # cheese. loaddb(); my $sock = IO::Socket::INET->new(PeerAddr=>$server,PeerPort=>6667); die "Could not build socket; $!" unless $sock; sts("NICK $botnick"); sts("USER $botuser 0 0 :$botrlnm"); while (<$sock>) { $inbytes += length; s/[\r\n]//g; my @arg = split/ /; if (lc $arg[0] eq 'ping') { sts("PONG $arg[1]"); } if ($arg[1] eq '433') { $botnick .= 0; sts("NICK $botnick"); } if (lc $arg[1] eq 'join') { my $usernick = (split(/!/,$arg[0]))[0]; $usernick = substr($usernick,1); $onchan{$usernick}=time(); } if (lc $arg[1] eq 'quit') { my $usernick = (split(/!/,$arg[0]))[0]; $usernick = substr($usernick,1); for my $k (keys %rps) { if (exists $rps{$k}{nick} && $rps{$k}{nick} eq $usernick && $rps{$k}{online}) { $rps{$k}{online}=0; $rps{$k}{next}+=int(20 * ($rppenstep**$rps{$k}{level})); } } delete $onchan{$usernick}; } if (lc $arg[1] eq 'nick') { my $usernick = (split(/!/,$arg[0]))[0]; $usernick = substr($usernick,1); for my $k (keys %rps) { if ($rps{$k}{nick} eq $usernick && $rps{$k}{online}) { $rps{$k}{nick}=substr($arg[2],1); $rps{$k}{userhost}=substr($arg[0],1); $rps{$k}{next} += int(30 * ($rppenstep**$rps{$k}{level})); sts("NOTICE $rps{$k}{nick} :Penalty of ". duration(int(30 * ($rppenstep**$rps{$k}{level}))). " added to your timer for changing nicks."); } } $onchan{substr($arg[2],1)} = delete $onchan{$usernick}; } if (lc $arg[1] eq 'part') { my $usernick = (split(/!/,$arg[0]))[0]; $usernick = substr($usernick,1); for my $k (keys %rps) { if (exists $rps{$k}{nick} && $rps{$k}{nick} eq $usernick && $rps{$k}{online}) { $rps{$k}{online}=0; $rps{$k}{next}+=int(200 * ($rppenstep**$rps{$k}{level})); sts("NOTICE $rps{$k}{nick} :Penalty of ". duration(int(200 * ($rppenstep**$rps{$k}{level}))). " added to your timer for parting."); } } delete $onchan{$usernick}; } if (lc $arg[1] eq 'kick') { my $usernick = $arg[3]; for my $k (keys %rps) { if (exists $rps{$k}{nick} && $rps{$k}{nick} eq $usernick && $rps{$k}{online}) { $rps{$k}{online}=0; $rps{$k}{next}+=int(250 * ($rppenstep**$rps{$k}{level})); sts("NOTICE $rps{$k}{nick} :Penalty of ". duration(int(250 * ($rppenstep**$rps{$k}{level}))). " added to your timer for getting kicked."); } } delete $onchan{$usernick}; } if (lc $arg[1] eq '315') { if (@auto_login) { sts("PRIVMSG $botchan :".scalar @auto_login." users matching ". scalar @prev_online." hosts automatically logged in; accounts: ". join(", ",@auto_login)); } else { sts("PRIVMSG $botchan :0 users qualified for auto login."); } undef @prev_online; undef @auto_login; } if (lc $arg[1] eq '352') { $onchan{$arg[7]}=time(); for my $k (keys %rps) { for my $host (@prev_online) { if ($rps{$k}{userhost} eq $arg[7]."!".$arg[4]."\@".$arg[5] && $rps{$k}{userhost} eq $host) { $rps{$k}{online} = 1; $rps{$k}{lastlogin} = localtime(time()); if (!scalar(grep { $_ eq $k } @auto_login)) { push(@auto_login,$k); } } } } } if ($arg[1] eq '001') { sts($botidentify); sts("JOIN $botchan"); sts("MODE $botchan"); sts($botopcmd); sts("WHO $botchan"); $SIG{ALRM} = \&rpcheck; alarm(5); } if (lc $arg[1] eq 'notice') { my $usernick = (split(/!/,$arg[0]))[0]; $usernick = substr($usernick,1); for my $k (keys %rps) { if (exists $rps{$k}{nick} && $rps{$k}{nick} eq $usernick && $rps{$k}{online}) { $rps{$k}{next} += int((length("@arg[3..$#arg]")-1) * ($rppenstep**$rps{$k}{level})); sts("NOTICE $rps{$k}{nick} :Penalty of ". duration(int((length("@arg[3..$#arg]")-1) * ($rppenstep**$rps{$k}{level}))). " added to your timer for notice."); } } } if (lc $arg[1] eq 'privmsg') { $arg[0] = substr($arg[0],1); my $usernick = (split/!/,$arg[0])[0]; if (lc $arg[2] eq lc $botnick) { if (lc $arg[3] eq ":\1version\1") { sts("NOTICE $usernick :\1VERSION IRPG bot v$version by jotun; ". "$helpurl\1"); } if (lc $arg[3] eq ":calc" && ha($usernick)) { my $eq = "@arg[4..$#arg]"; $eq =~ s/[^\d\.\+\-\(\)\*\&\^\%\~\!\/]//g; sts("PRIVMSG $botchan :$_") for eval $eq; if ($@) { sts("PRIVMSG $usernick :EVAL ERROR : $@"); sts("PRIVMSG $usernick :In expression: $eq"); } next; } if (lc $arg[3] eq ":peval") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You don't have access to PEVAL."); } else { sts("PRIVMSG $usernick :$_") for eval "@arg[4..$#arg]"; sts("PRIVMSG $usernick :EVAL ERROR: $@") if $@; next; } } if (lc $arg[3] eq ":register") { if ($#arg < 6 || $arg[6] eq "") { sts("PRIVMSG $usernick :Try: REGISTER "); sts("PRIVMSG $usernick :IE : REGISTER Poseidon MyPassword ". "God of the Sea"); } elsif (exists $rps{$arg[4]}) { sts("PRIVMSG $usernick :Sorry, that charname is already in use."); } elsif (!exists $onchan{$usernick}) { sts("PRIVMSG $usernick :Sorry, you're not in $botchan."); } elsif (length $arg[4] > 16) { sts("PRIVMSG $usernick :Sorry, charnames must be < 17 chars long."); } elsif (length "@arg[6..$#arg]" > 30) { sts("PRIVMSG $usernick :Sorry, char classes must be < 31 chars long."); } else { $rps{$arg[4]}{next} = $rpbase; $rps{$arg[4]}{class} = "@arg[6..$#arg]"; $rps{$arg[4]}{level} = 0; $rps{$arg[4]}{online} = 1; $rps{$arg[4]}{nick} = $usernick; $rps{$arg[4]}{userhost} = $arg[0]; $rps{$arg[4]}{lastlogin} = localtime(time()); $rps{$arg[4]}{pass} = crypt($arg[5],"rp"); sts("PRIVMSG $botchan :Welcome $usernick"."'s new player $arg[4], ". "the @arg[6..$#arg]! Next level in ".duration($rpbase)."."); sts("PRIVMSG $usernick :Success! Account $arg[4] created. ". "You have $rpbase seconds idleness until you reach level 1. "); sts("PRIVMSG $usernick :NOTE: The point of the game is to see who ". "can idle the longest. As such, talking (to channel or the ". "bot), parting, quitting, and changing nicks penalize you."); next; } } if (lc $arg[3] eq ":del") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You don't have access to del."); } else { if (!defined $arg[4]) { sts("PRIVMSG $usernick :Try: DEL "); } else { if (exists $rps{$arg[4]}) { delete $rps{$arg[4]}; sts("PRIVMSG $botchan :Account $arg[4] removed by $arg[0]."); } else { sts("PRIVMSG $usernick :No such account $arg[4]."); } } next; } } if (lc $arg[3] eq ":alert") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You don't have access to ALERT."); } else { if (!defined $arg[4]) { sts("PRIVMSG $usernick :Try: ALERT "); } else { sts("PRIVMSG $botchan :ALERT from $usernick: @arg[4..$#arg]"); } next; } } if (lc $arg[3] eq ":hog") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You don't have access to HOG."); } else { sts("PRIVMSG $botchan :$usernick has summoned the Hand of God."); hog(); next; } } if (lc $arg[3] eq ":chpass") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You don't have access to CHPASS."); } else { if (!defined $arg[5]) { sts("PRIVMSG $usernick :Try: CHPASS "); } else { if (exists $rps{$arg[4]}) { $rps{$arg[4]}{pass} = crypt($arg[5],"rp"); sts("PRIVMSG $usernick :Password for $arg[4] changed."); } else { sts("PRIVMSG $usernick :No such username $arg[4]."); } } next; } } if (lc $arg[3] eq ":chuser") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You don't have access to CHUSER."); } else { if (!defined $arg[5]) { sts("PRIVMSG $usernick :Try: CHUSER "); } elsif (!exists $rps{$arg[4]}) { sts("PRIVMSG $usernick :No such username $arg[4]."); } elsif (exists $rps{$arg[5]}) { sts("PRIVMSG $usernick :Username $arg[5] is already taken."); } else { $rps{$arg[5]} = delete $rps{$arg[4]}; sts("PRIVMSG $usernick :Username for $arg[4] changed to $arg[5]."); } next; } } if (lc $arg[3] eq ":chclass") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You don't have access to CHCLASS."); } else { if (!defined $arg[5]) { sts("PRIVMSG $usernick :Try: CHCLASS "); } else { if (exists $rps{$arg[4]}) { $rps{$arg[4]}{class} = "@arg[5..$#arg]"; sts("PRIVMSG $usernick :Class for $arg[4] changed to @arg[5..$#arg]."); } else { sts("PRIVMSG $usernick :No such username $arg[4]."); } } next; } } if (lc $arg[3] eq ":push") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You don't have access to PUSH."); } else { if (!defined $arg[5]) { sts("PRIVMSG $usernick :Try: PUSH "); } else { if (exists $rps{$arg[4]}) { $rps{$arg[4]}{next} -= $arg[5]; sts("PRIVMSG $usernick :Time to level for $arg[4] pushed ahead ". "$arg[5] seconds. $arg[4] reaches next level in ". duration($rps{$arg[4]}{next})."."); sts("PRIVMSG $botchan :$usernick has pushed $arg[4] $arg[5] ". "seconds toward level ".($rps{$arg[4]}{level}+1). ". $arg[4] reaches next level in ". duration($rps{$arg[4]}{next})."."); } else { sts("PRIVMSG $usernick :No such username $arg[4]."); } } next; } } if (lc $arg[3] eq ":whoami") { my $f=0; for (keys %rps) { if (exists $rps{$_}{nick} && $rps{$_}{nick} eq $usernick && $rps{$_}{online}) { sts("PRIVMSG $usernick :You are logged in as $_."); $f=1; } } sts("PRIVMSG $usernick :You are not logged in.") if !$f; next; } if (lc $arg[3] eq ":help") { if (!ha($usernick)) { sts("PRIVMSG $usernick :To register a new account: ". "/msg $botnick REGISTER"); sts("PRIVMSG $usernick :To login to an account: ". "/msg $botnick LOGIN"); sts("PRIVMSG $usernick :If you forget your password, ask for help ". "in the channel."); sts("PRIVMSG $usernick :For more info, see $helpurl"); } else { sts("PRIVMSG $usernick :Help URL is $helpurl"); sts("PRIVMSG $usernick :Admin commands URL is $admincommurl"); } next; } if (lc $arg[3] eq ":die") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You do not have access to DIE."); } else { sts("QUIT :DIE from $arg[0]"); next; } } if (lc $arg[3] eq ":jump") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You do not have access to JUMP."); } elsif (!defined $arg[4]) { sts("PRIVMSG $usernick :Try JUMP "); } else { sts("QUIT :JUMP to $arg[4] from $arg[0]"); $server = $arg[4]; close $sock; sleep 3; goto CONNECT; } } if (lc $arg[3] eq ":restart") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You do not have access to RESTART."); } else { sts("QUIT :RESTART from $arg[0]"); exec("perl $0"); } } if (lc $arg[3] eq ":info") { if (!ha($usernick)) { sts("PRIVMSG $usernick :You do not have access to INFO."); } else { my $info = sprintf("%.2fkb sent, %.2fkb received in %s. ". "%d IRPG users online.", $outbytes/1024,$inbytes/1024,duration(time-$^T), scalar(grep { $rps{$_}{online} } keys %rps)); sts("PRIVMSG $usernick :$info"); next; } } if (lc $arg[3] eq ":login") { if ($#arg < 5 || $arg[5] eq "") { sts("PRIVMSG $usernick :Try: LOGIN "); } elsif (!exists $rps{$arg[4]}) { sts("PRIVMSG $usernick :Sorry, no such account name. Note ". "that account names are case sensitive."); } elsif (!exists $onchan{$usernick}) { sts("PRIVMSG $usernick :Sorry, you're not in $botchan."); } elsif ($rps{$arg[4]}{pass} ne crypt($arg[5],"rp")) { sts("PRIVMSG $usernick :Wrong password."); } else { $rps{$arg[4]}{online} = 1; $rps{$arg[4]}{nick} = $usernick; $rps{$arg[4]}{userhost} = $arg[0]; $rps{$arg[4]}{lastlogin} = localtime(time()); sts("PRIVMSG $botchan :$arg[4], the level $rps{$arg[4]}{level} ". "$rps{$arg[4]}{class}, is now online from nickname $usernick. ". "Next level in ".duration($rps{$arg[4]}{next})."."); sts("PRIVMSG $usernick :Logon successful. Next level in ". duration($rps{$arg[4]}{next})."."); } next; } } my $found = 0; for my $k (keys %rps) { if (exists $rps{$k}{nick} && $rps{$k}{nick} eq $usernick && $rps{$k}{online}) { $rps{$k}{next} += int((length("@arg[3..$#arg]")-1) * ($rppenstep**$rps{$k}{level})); sts("NOTICE $rps{$k}{nick} :Penalty of ". duration(int((length("@arg[3..$#arg]")-1) * ($rppenstep**$rps{$k}{level}))). " added to your timer for privmsg."); $found=1; } } if (!$found && "@arg" =~ /http:/i && (time()-$onchan{$usernick}) < 90 && "@arg" !~ /ultrazone/i) { sts("MODE $botchan +b $arg[0]"); } } print "in : $_\n" if $debug; } print "Disconnected.\r\n" if $debug; sub sts { # send to server my $text = shift; print $sock "$text\r\n"; print "out: $text\n" if $debug; $outbytes += length($text) + 2; } sub ha { # return 0/1 if username has access my $nick = shift; for my $k (keys %rps) { if ($rps{$k}{nick} eq $nick && $rps{$k}{online}) { for my $l (@access) { return 1 if $l eq $k; } } } return 0; } sub duration { # return human duration of seconds my $s = shift; return "NA ($s)" if $s !~ /^\d+$/; sprintf("%d days, %02d:%02d:%02d",$s/84600, ($s%84600)/3600,($s%3600)/60,$s%60); } sub ts { # timestamp my @ts = localtime(time); sprintf("[%02d:%02d:%02d] ",$ts[2],$ts[1],$ts[0]); } sub hog { # summon the hand of god my @players = grep { $rps{$_}{online} } keys %rps; my $player = $players[rand @players]; my $win = int(rand(5)); my $time = int(((5 + int(rand(70)))/100) * $rps{$player}{next}); if ($win) { sts("PRIVMSG $botchan :Verily I say unto thee, the Heavens have burst ". "forth, and the blessed hand of God carried $player ".duration($time). " toward level ".($rps{$player}{level}+1)."."); $rps{$player}{next} -= $time; } else { sts("PRIVMSG $botchan :Thereupon He stretched out His little finger ". "among them and consumed $player with fire, slowing the heathen ". duration($time)." from level ".($rps{$player}{level}+1)."."); $rps{$player}{next} += $time; } sts("PRIVMSG $botchan :$player reaches next level in ". duration($rps{$player}{next})."."); } sub rpcheck { # check levels, update database hog() if rand(20000) < 1; team_battle() if rand(20000) < 1; if ($rpreport%4320==0) { # 4320 = six hours, if $alrmint is 5 seconds my @u = sort { $rps{$b}{level} <=> $rps{$a}{level} || $rps{$a}{next} <=> $rps{$b}{next} } keys %rps; sts("PRIVMSG $botchan :#G7 Idle RPG Top Players:"); sts("PRIVMSG $botchan :$u[0], the level $rps{$u[0]}{level} ". "$rps{$u[0]}{class}, is #1! Next level in ". (duration($rps{$u[0]}{next}))."."); sts("PRIVMSG $botchan :$u[1], the level $rps{$u[1]}{level} ". "$rps{$u[1]}{class}, is #2! Next level in ". (duration($rps{$u[1]}{next}))."."); sts("PRIVMSG $botchan :$u[2], the level $rps{$u[2]}{level} ". "$rps{$u[2]}{class}, is #3! Next level in ". (duration($rps{$u[2]}{next}))."."); system("cp $irpgdb .dbbackup/$irpgdb".time()); } open(RPS,">$irpgdb") or die "FAILED WRITING $irpgdb: $!"; print RPS "# username\tpass\tlevel\tclass\tnext\tnick\tuserhost\tonline 0/1". "\tlast login time\tamulet\tcharm\thelm\tboots\tgloves\tring\t". "leggings\tshield\ttunic\tweapon\n"; for my $k (keys %rps) { if ($rps{$k}{online} && exists $rps{$k}{nick} && $rps{$k}{nick} && exists $onchan{$rps{$k}{nick}}) { $rps{$k}{next}-=$alrmint; if ($rps{$k}{next} < 1) { $rps{$k}{level}++; $rps{$k}{next} = int($rpbase*($rpstep**$rps{$k}{level})); sts("PRIVMSG $botchan :$k, the $rps{$k}{class}, has attained level ". "$rps{$k}{level}! Next level in ".duration($rps{$k}{next}). "."); find_item($k); challenge_opp($k); } } print RPS join("\t", $k, $rps{$k}{pass}, $rps{$k}{level}, $rps{$k}{class}, $rps{$k}{next}, $rps{$k}{nick}||"", $rps{$k}{userhost}||"", $rps{$k}{online}||0, $rps{$k}{lastlogin}, $rps{$k}{item}{amulet}||0, $rps{$k}{item}{charm}||0, $rps{$k}{item}{helm}||0, $rps{$k}{item}{"pair of boots"}||0, $rps{$k}{item}{"pair of gloves"}||0, $rps{$k}{item}{ring}||0, $rps{$k}{item}{"set of leggings"}||0, $rps{$k}{item}{shield}||0, $rps{$k}{item}{tunic}||0, $rps{$k}{item}{weapon}||0)."\n"; } close RPS; ++$rpreport; $SIG{ALRM} = \&rpcheck; alarm($alrmint); } sub challenge_opp { # pit argument player against random player my $u = shift; if ($rps{$u}{level} < 25) { return unless rand(4) < 1; } my @opps = grep { $rps{$_}{online} && $u ne $_ } keys %rps; return unless @opps; my $opp = $opps[int rand @opps]; my($mysum,$oppsum); $mysum += $rps{$u}{item}{$_} for keys %{$rps{$u}{item}}; $oppsum += $rps{$opp}{item}{$_} for keys %{$rps{$opp}{item}}; if (rand($mysum) > rand($oppsum)) { my $gain = int($rps{$opp}{level}/4); $gain = 7 if $gain < 7; $gain = int(($gain/100)*$rps{$u}{next}); sts("PRIVMSG $botchan :$u has challenged $opp in combat and won! ". duration($gain)." is removed from $u"."'s clock."); open(B,">>battles.txt"); print B "$u has challenged $opp in combat and won! ".duration($gain). " is removed from $u"."'s clock.\n"; close B; $rps{$u}{next} -= $gain; if (rand(35) < 1) { $gain = int(((5 + int(rand(20)))/100) * $rps{$opp}{next}); open(B,">>battles.txt"); print B "$u has dealt $opp a Critical Strike! ".duration($gain). " is added to $opp"."'s clock.\n"; close B; sts("PRIVMSG $botchan :$u has dealt $opp a Critical Strike! ". duration($gain)." is added to $opp"."'s clock."); $rps{$opp}{next} += $gain; } } else { my $gain = int($rps{$opp}{level}/7); $gain = 7 if $gain < 7; $gain = int(($gain/100)*$rps{$u}{next}); sts("PRIVMSG $botchan :$u has challenged $opp in combat and lost! ". duration($gain)." is added to $u"."'s clock."); open(B,">>battles.txt"); print B "$u has challenged $opp in combat and lost! ".duration($gain). " is added to $u"."'s clock.\n"; close B; $rps{$u}{next} += $gain; } } sub team_battle { # pit three players against three other players my @opp = grep { $rps{$_}{online} } keys %rps; return if @opp < 6; splice(@opp,int rand @opp,1) while @opp > 6; my($mysum,$oppsum); $mysum += $rps{$opp[0]}{item}{$_} for keys %{$rps{$opp[0]}{item}}; $mysum += $rps{$opp[1]}{item}{$_} for keys %{$rps{$opp[1]}{item}}; $mysum += $rps{$opp[2]}{item}{$_} for keys %{$rps{$opp[2]}{item}}; $oppsum += $rps{$opp[3]}{item}{$_} for keys %{$rps{$opp[3]}{item}}; $oppsum += $rps{$opp[4]}{item}{$_} for keys %{$rps{$opp[4]}{item}}; $oppsum += $rps{$opp[5]}{item}{$_} for keys %{$rps{$opp[5]}{item}}; if (rand($mysum) > rand($oppsum)) { my $gain = $rps{$opp[0]}{next}; for my $p (1,2) { $gain = $rps{$opp[$p]}{next} if $gain > $rps{$opp[$p]}{next}; } $gain = int($gain*.20); sts("PRIVMSG $botchan :$opp[0], $opp[1], and $opp[2] have team battled ". "$opp[3], $opp[4], and $opp[5] and won! ".duration($gain). " is removed from their clocks."); open(B,">>battles.txt"); print B "$opp[0], $opp[1], and $opp[2] have team battled $opp[3], ". "$opp[4], and $opp[5] and won! ".duration($gain)." is removed ". "from their clocks.\n"; close B; $rps{$opp[0]}{next} -= $gain; $rps{$opp[1]}{next} -= $gain; $rps{$opp[2]}{next} -= $gain; } else { sts("PRIVMSG $botchan :$opp[0], $opp[1], and $opp[2] have team battled ". "$opp[3], $opp[4], and $opp[5] and lost! No time is awarded."); open(B,">>battles.txt"); print B "$opp[0], $opp[1], and $opp[2] have team battled $opp[3], ". "$opp[4], and $opp[5] and lost! No time is awarded.\n"; close B; } } sub find_item { # find item for argument player my $u = shift; my @items = ("ring","amulet","charm","weapon","helm","tunic", "pair of gloves","set of leggings","shield","pair of boots"); my $type = $items[rand @items]; my $level = 1; for my $num (1 .. int($rps{$u}{level}*1.5)) { if (rand(1.4**($num/4)) < 1) { $level = $num; } } if ($rps{$u}{level} >= 25 && rand(40) < 1) { my $ulevel = 50+int(rand(25)); if ($ulevel >= $level && $ulevel > $rps{$u}{item}{helm}) { sts("NOTICE $rps{$u}{nick} :The light of the gods shines upon you! ". "You have found the level $ulevel Mattt's Omniscience Grand Crown! ". "Your enemies fall before you as you anticipate their every move."); $rps{$u}{item}{helm} = $ulevel; } } elsif ($rps{$u}{level} >= 30 && rand(40) < 1) { my $ulevel = 75+int(rand(25)); if ($ulevel >= $level && $ulevel > $rps{$u}{item}{tunic}) { sts("NOTICE $rps{$u}{nick} :The light of the gods shines upon you! ". "You have found the level $ulevel Res0's Protectorate Plate Mail! ". "Your enemies cower in fear as their attacks have no effect on ". "you."); $rps{$u}{item}{tunic} = $ulevel; } } elsif ($rps{$u}{level} >= 35 && rand(40) < 1) { my $ulevel = 100+int(rand(25)); if ($ulevel >= $level && $ulevel > $rps{$u}{item}{amulet}) { sts("NOTICE $rps{$u}{nick} :The light of the gods shines upon you! ". "You have found the level $ulevel Dwyn's Storm Magic Amulet! ". "Your enemies are swept away by an elemental fury before the war ". "has even begun."); $rps{$u}{item}{amulet} = $ulevel; } } elsif ($rps{$u}{level} >= 40 && rand(40) < 1) { my $ulevel = 150+int(rand(26)); if ($ulevel >= $level && $ulevel > $rps{$u}{item}{weapon}) { sts("NOTICE $rps{$u}{nick} :The light of the gods shines upon you! ". "You have found the level $ulevel Jotun's Fury Colossal Sword! ". "Your enemies' hatred is brought to a quick end as you arc ". "your wrist, dealing the crushing blow."); $rps{$u}{item}{weapon} = $ulevel; } } elsif ($level > $rps{$u}{item}{$type}) { sts("NOTICE $rps{$u}{nick} :You found a level $level $type! Your ". "current $type is only level ".(0+$rps{$u}{item}{$type})." so ". "it seems Luck is with you."); $rps{$u}{item}{$type} = $level; } else { sts("NOTICE $rps{$u}{nick} :You found a level $level $type. Your ". "current $type is level ".(0+$rps{$u}{item}{$type})." so ". "it seems Luck is against you. You toss the $type."); } } sub loaddb { # load the players database undef %rps; %rps = (); (open(RPS,"<$irpgdb") || ! -e $irpgdb) or die "loaddb() failed; $!"; while (chomp(my $l=)) { next if $l =~ /^#/; # skip comments my @i = split("\t",$l); print Dumper @i if @i != 19; die "Anomaly in loaddb(); line $. of $irpgdb has wrong fields (". scalar @i.")" if @i != 19; if ($i[7]) { push(@prev_online,$i[6]); } # log back in $rps{$i[0]}{pass} = $i[1]; $rps{$i[0]}{level} = $i[2]; $rps{$i[0]}{class} = $i[3]; $rps{$i[0]}{next} = $i[4]; $rps{$i[0]}{nick} = $i[5]; $rps{$i[0]}{userhost} = $i[6]; $rps{$i[0]}{online} = 0; $rps{$i[0]}{lastlogin} = $i[8]; $rps{$i[0]}{item}{amulet} = $i[9]; $rps{$i[0]}{item}{charm} = $i[10]; $rps{$i[0]}{item}{helm} = $i[11]; $rps{$i[0]}{item}{"pair of boots"} = $i[12]; $rps{$i[0]}{item}{"pair of gloves"} = $i[13]; $rps{$i[0]}{item}{ring} = $i[14]; $rps{$i[0]}{item}{"set of leggings"} = $i[15]; $rps{$i[0]}{item}{shield} = $i[16]; $rps{$i[0]}{item}{tunic} = $i[17]; $rps{$i[0]}{item}{weapon} = $i[18]; } close RPS; }