From 521551cfe4104e3a464175fd063054bf38038b2c Mon Sep 17 00:00:00 2001 From: ElTata <eltata@firemail.cc> Date: Mon, 28 Oct 2019 11:45:49 +0100 Subject: [PATCH] actions delay fixed - before, you had to wait one hour for one action to be available, even if you use them simultaneously, now there is one countdown per action - new function in Utils exec_delayed, to execute function after a given time - actions delays are stored in a new file named in the config file - the main loop of Main (rpcheck subroutine) now checks for delayed functions to execute - other typo/style fixs --- .irpg.conf | 5 ++ Irpg/Action.pm | 56 +++++++++++++------- Irpg/Main.pm | 136 ++++++++++++++++++++++++++----------------------- Irpg/Utils.pm | 64 +++++++++++++++++++---- 4 files changed, 171 insertions(+), 90 deletions(-) diff --git a/.irpg.conf b/.irpg.conf index 76d5f50..ea9289a 100644 --- a/.irpg.conf +++ b/.irpg.conf @@ -84,6 +84,11 @@ rppenstep 1.14 # player database file dbfile irpg.db +# actions database file +# the file where the delays before the next actions of players +# are stored +actionsfile next_actions.db + # where quests/godsends/calamities are stored eventsfile events.txt diff --git a/Irpg/Action.pm b/Irpg/Action.pm index bf5f0d5..55993f0 100644 --- a/Irpg/Action.pm +++ b/Irpg/Action.pm @@ -7,7 +7,7 @@ use Irpg::Irc qw(:interaction); use Exporter qw(import); our @EXPORT = qw(&choose_action &itemsum &challenge_opp &collision_action &team_battle); -our @EXPORT_OK = qw(itemsum); +our @EXPORT_OK = qw(itemsum new_action); my $primnick_ref; my $opts; @@ -19,7 +19,13 @@ my $rps; =item SCALAR (ref) - reference to the options hash =item SCALAR (ref) - reference to the players hash =cut -sub init_pkg { ($opts, $rps, $primnick_ref) = @_; } +sub init_pkg { + ($opts, $rps, $primnick_ref) = @_; + foreach my $user (keys %$rps) { + map { Irpg::Utils::execute_delayed($_, \&new_action, $user) } + @{$rps->{$user}{next_a}}; + } +} sub fisher_yates_shuffle { @@ -130,7 +136,7 @@ sub mystic_result { push(@queue, "$p1 has cast a Critical Spell! ". duration($gain)." of ".pronoun(2, $rps->{$p1}{gender}). - " clock is sent to another plan. ". + " clock is sent to another plane. ". "$p1 reaches next level in ".duration($rps->{$p1}{next})."."); } } @@ -194,9 +200,9 @@ sub steal_result { my $tempitem = $rps->{$p1}{item}{$type}; $rps->{$p1}{item}{$type}=$rps->{$p2}{item}{$type}; $rps->{$p2}{item}{$type} = $tempitem; - $queue[$#queue] .= pronoun(1, $rps->{$p1}{gender})." has retrieved ". + $queue[$#queue] .= ucfirst(pronoun(1, $rps->{$p1}{gender}))." has retrieved ". "the finest of $p2\'s equipment. $p1 then proceeds to crawl ". - "back into the shadows ".pronoun(1, $rps->{$p1}{gender})." came". + "back into the shadows ".pronoun(1, $rps->{$p1}{gender})." came ". "from, ".pronoun(2, $rps->{$p1}{gender})." nefarious deed ". "unknown from any soul but ".pronoun(3, $rps->{$p1}{gender}). "self."; @@ -244,7 +250,7 @@ sub perform_action { if ($p1roll < 5 || $p2roll < 5 || $p1sum < 5 || $p2roll < 5) { $ret = {p1sum=>$p1sum, p2sum=>$p2sum, p1roll=>$p1roll, p2roll=>$p2roll, - p1atk=>$p1atk, p2def=>$p2def, + p1atk=>$p1atk, p2def=>$p2def, vict=>0}; } else { @@ -259,7 +265,7 @@ sub perform_action { my $align_mod = $rps->{$p1}{alignment} eq 'Good' ? 0.05 : $rps->{$p1}{alignment} eq 'Evil' ? -0.10 : 1; - if ($p1roll >= $p1sum * $p1atk * ($rps->{$p1}{class}->{CRIT_SK} + $align_mod)) { + if ($p1roll*$p1atk >= $p1sum*$p1atk*($rps->{$p1}{class}->{CRIT_SK} + $align_mod)) { # CRITICAL STRIKE $ret->{vict}++; } @@ -276,8 +282,8 @@ sub challenge_opp { # pit argument player against random player my $action_type = choose_action($p1); my $ret_action = perform_action($p1, $p2, $action_type); my $res_action = eval $action_type.'_result($p1, $p2, $ret_action->{vict})'; - my $p1_res = "[$ret_action->{p1roll}*$ret_action->{p1atk}/$ret_action->{p1sum}]"; - my $p2_res = "[$ret_action->{p2roll}*$ret_action->{p2def}/$ret_action->{p2sum}]"; + my $p1_res = "[$ret_action->{p1roll}/$ret_action->{p1sum}]*$ret_action->{p1atk}"; + my $p2_res = "[$ret_action->{p2roll}/$ret_action->{p2sum}]*$ret_action->{p2def}"; my $mesg = ''; if ($action_type eq 'fight') { @@ -304,8 +310,8 @@ sub collision_action { my $ret_action = perform_action($p1, $p2, $action_type); my $res_action = eval $action_type.'_result($p1, $p2, $ret_action->{vict})'; - my $p1_res = "[$ret_action->{p1roll}*$ret_action->{p1atk}/$ret_action->{p1sum}]"; - my $p2_res = "[$ret_action->{p2roll}*$ret_action->{p2def}/$ret_action->{p2sum}]"; + my $p1_res = "[$ret_action->{p1roll}/$ret_action->{p1sum}]*$ret_action->{p1atk}"; + my $p2_res = "[$ret_action->{p2roll}/$ret_action->{p2sum}]*$ret_action->{p2def}"; my $mesg = "$p1 $p1_res has come upon $p2 $p2_res and "; if ($action_type eq 'fight') { $mesg .= ($ret_action->{vict} ? @@ -353,12 +359,12 @@ sub team_battle { # pit three players against three other players $sum = int(itemsum($opp[$i],1)); if ($i < 3) { $mysum += $sum; - $mod = eval '$rps->{$p1}{class}->'.$action_type.'_atk()'; + $mod = eval '$rps->{$opp[$i]}{class}->'.$action_type.'_atk()'; $myroll += int(rand($sum) * $mod); } else { $oppsum += $sum; - $mod = eval '$rps->{$p1}{class}->'.$action_type.'_def()'; + $mod = eval '$rps->{$opp[$i]}{class}->'.$action_type.'_def()'; $opproll += int(rand($sum) * $mod); } } @@ -446,20 +452,20 @@ sub do_action { elsif (exists($arg[0])) { $p2 = $arg[0]; # choosing your adversary cost an extra action point - $rps->{$username}{actions}--; + consume_action($username); } else { $p2 = $opps[int(rand(@opps))]; } - $rps->{$username}{actions}--; + consume_action($username); $rps->{$username}{next_a} = 3600 unless ($rps->{$username}{next_a}); my $p1 = $username; my $action_type = choose_action($p1); my $ret_action = perform_action($p1, $p2, $action_type); my $res_action = eval $action_type.'_result($p1, $p2, $ret_action->{vict}, 1)'; - my $p1_res = "[$ret_action->{p1roll}*$ret_action->{p1atk}/$ret_action->{p1sum}]"; - my $p2_res = "[$ret_action->{p2roll}*$ret_action->{p2def}/$ret_action->{p2sum}]"; + my $p1_res = "[$ret_action->{p1roll}/$ret_action->{p1sum}]*$ret_action->{p1atk}"; + my $p2_res = "[$ret_action->{p2roll}/$ret_action->{p2sum}]*$ret_action->{p2def}"; my $mesg = ''; if ($action_type eq 'fight') { @@ -483,6 +489,22 @@ sub do_action { foreach (@$res_action) { Irpg::Irc::chanmsg(Irpg::Utils::clog($_)); } } +sub consume_action { + my $player = shift; + return unless (exists($rps->{$player})); + $rps->{$player}{actions}--; + push $rps->{$player}{next_a}, 3600; + Irpg::Utils::execute_delayed(3600, \&new_action, $player); +} + +sub new_action { + my $player = shift; + return unless (exists($rps->{$player})); + $rps->{$player}{actions}++; + Irpg::Irc::notice("You feel ready to perform a new deed !", + $rps->{$player}{nick}); +} + our $commands = { action => {ref => \&do_action, adm => 0, prv => 1, pub => 1, hlp => 'ACTION [<char name>]: perform an action (fight/mystic/steal) '. diff --git a/Irpg/Main.pm b/Irpg/Main.pm index c91c535..bc679cf 100644 --- a/Irpg/Main.pm +++ b/Irpg/Main.pm @@ -38,6 +38,7 @@ my $primnick; my $opts; my $rps; my $prev_online; +my $delayed_funcs; =head1 FUNCTION init_pkg This function sets the references to options and players hashes. @@ -45,9 +46,10 @@ my $prev_online; =item SCALAR (ref) - reference to the options hash =item SCALAR (ref) - reference to the players hash =item SCALAR (ref) - reference to the prev_online hash +=item SCALAR (ref) - reference to the delayed_funcs array =cut sub init_pkg { - ($opts, $rps, $lasttime_ref, $prev_online) = @_; + ($opts, $rps, $lasttime_ref, $prev_online, $delayed_funcs) = @_; $primnick = $opts->{botnick}; # for regain or register checks Irpg::Irc::init_pkg(\$silentmode); Irpg::Quest::init_pkg($opts, $rps); @@ -251,7 +253,6 @@ sub rpcheck { # check levels, update database # check splits hash to see if any split users have expired checksplits() if $opts->{detectsplits}; # send out $freemessages lines of text from the outgoing message queue - #fq(); Irpg::Irc::fq(); # clear registration limiting $lastreg = 0; @@ -262,11 +263,11 @@ sub rpcheck { # check levels, update database ### MOVING PLAYERS ### moveplayers(); - ### ALL MODULES CHEKS ### + ### ALL MODULES CHECKS ### # statements using $rpreport do not bother with scaling by the clock because # $rpreport is adjusted by the number of seconds since last rpcheck() foreach (qw(Quest Action Event)) { - eval 'Irpg::'.$_.'::rpcheck($rpreport, $online'; + eval 'Irpg::'.$_.'::rpcheck($rpreport, $online)'; } ### TOP PLAYERS REPORT ### @@ -298,67 +299,75 @@ sub rpcheck { # check levels, update database Irpg::Irc::chanmsg("WARNING: Cannot write database in PAUSE mode!"); } - ### UPDATE PLAYERS DATA ### + ### TIME RELATED STUFFS ### # (time related data, and here is the levelling) - # - # do not write in pause mode, and do not write if not yet connected. (would - # log everyone out if the bot failed to connect. $lasttime = time() on - # successful join to $opts->{botchan}, initial value is 1). if fails to open - # $opts->{dbfile}, will not update $lasttime and so should have correct values - # on next rpcheck(). - if ($$lasttime_ref != 1) { - my $curtime=time(); - for my $k (keys(%$rps)) { - if ($rps->{$k}{online} && exists $rps->{$k}{nick} && - $rps->{$k}{nick} && exists $onchan{$rps->{$k}{nick}}) { - $rps->{$k}{next} -= ($curtime - $$lasttime_ref); - $rps->{$k}{next_a} = map { $_ - ($curtime - $$lasttime_ref) } - $rps->{$k}{next_a}; - #$rps->{$k}{next_a} -= ($curtime - $$lasttime_ref); - #$rps->{$k}{next_a} = 0 if ($rps->{$k}{next_a} < 0); - $rps->{$k}{idled} += ($curtime - $$lasttime_ref); - if ($rps->{$k}{next} < 1) { - $rps->{$k}{level}++; - if (!($rps->{$k}{level} % 5)){ - $rps->{$k}{points}++; - Irpg::Irc::notice( - "Your hard training paid off, and you have ". - "one more point to invest.", - $rps->{$k}{nick}); - } - if ($rps->{$k}{level} > 60) { - $rps->{$k}{next} = int(($opts->{rpbase} * - ($opts->{rpstep}**60)) + - (86400*($rps->{$k}{level} - 60))); - } - else { - $rps->{$k}{next} = int($opts->{rpbase} * - ($opts->{rpstep}**$rps->{$k}{level})); - } - Irpg::Irc::chanmsg("$k, the $rps->{$k}{alignment} ". - "$rps->{$k}{title} $rps->{$k}{class}->{NAME}, ". - "has attained level $rps->{$k}{level}! ". - "Next level in ".duration($rps->{$k}{next})."."); - find_item($k); - challenge_opp($k); + + # $lasttime = time() on successful join to $opts->{botchan}, initial value is 1). + # do nothing if not connected + next if ($$lasttime_ref == 1); + my $curtime=time(); + + ## Delayed Funcs ## + Irpg::Utils::exec_stack_delayed($curtime); + + ## Update Players Data ## + for my $k (keys(%$rps)) { + if ($rps->{$k}{online} && exists $rps->{$k}{nick} && + $rps->{$k}{nick} && exists $onchan{$rps->{$k}{nick}}) { + $rps->{$k}{next} -= ($curtime - $$lasttime_ref); + @{$rps->{$k}{next_a}} = grep { $_ > 0 } + map { $_ - ($curtime - $$lasttime_ref) } + @{$rps->{$k}{next_a}}; + #$rps->{$k}{next_a} -= ($curtime - $$lasttime_ref); + #$rps->{$k}{next_a} = 0 if ($rps->{$k}{next_a} < 0); + $rps->{$k}{idled} += ($curtime - $$lasttime_ref); + if ($rps->{$k}{next} < 1) { + $rps->{$k}{level}++; + if (!($rps->{$k}{level} % 5)){ + $rps->{$k}{points}++; + Irpg::Irc::notice( + "Your hard training paid off, and you have ". + "one more point to invest.", + $rps->{$k}{nick}); + } + if ($rps->{$k}{level} > 60) { + $rps->{$k}{next} = int(($opts->{rpbase} * + ($opts->{rpstep}**60)) + + (86400*($rps->{$k}{level} - 60))); } - if ($rps->{$k}{next_a} < 1 - && $rps->{$k}{actions} < int($rps->{$k}{level}/10)) { - $rps->{$k}{actions}++; - if ($rps->{$k}{actions} < int($rps->{$k}{level}/10)) { - $rps->{$k}{next_a} = 3600 - } - Irpg::Irc::notice("You feel ready to perform a new deed !", - $rps->{$k}{nick}); + else { + $rps->{$k}{next} = int($opts->{rpbase} * + ($opts->{rpstep}**$rps->{$k}{level})); } + Irpg::Irc::chanmsg("$k, the $rps->{$k}{alignment} ". + "$rps->{$k}{title} $rps->{$k}{class}->{NAME}, ". + "has attained level $rps->{$k}{level}! ". + "Next level in ".duration($rps->{$k}{next})."."); + find_item($k); + challenge_opp($k); + } + #if ($rps->{$k}{next_a} < 1 + # && $rps->{$k}{actions} < int($rps->{$k}{level}/10)) { + # $rps->{$k}{actions}++; + # if ($rps->{$k}{actions} < int($rps->{$k}{level}/10)) { + # $rps->{$k}{next_a} = 3600 + # } + # Irpg::Irc::notice("You feel ready to perform a new deed !", + # $rps->{$k}{nick}); + #} + if ($rps->{$k}{actions} + @{$rps->{$k}{next_a}} < int($rps->{$k}{level}/10)) { + Irpg::Action::new_action($k); } - # attempt to make sure this is an actual user, and not just an - # artifact of a bad PEVAL } - if (!$pausemode && $rpreport%60==0) { writedb($rps); } - $rpreport += $opts->{self_clock}; - $$lasttime_ref = $curtime; - } + # attempt to make sure this is an actual user, and not just an + # artifact of a bad PEVAL + } + # do not write in pause mode (would log everyone out if the bot failed to connect) + # if fails to open $opts->{dbfile}, will not update $lasttime and + # so should have correct values on next rpcheck(). + if (!$pausemode && $rpreport%60==0) { writedb($rps); } + $rpreport += $opts->{self_clock}; + $$lasttime_ref = $curtime; } @@ -394,8 +403,9 @@ sub parse { Irpg::Irc::sts("NICK $opts->{botnick}"); } elsif ($arg[1] eq 'join') { + return if (! $opts->{botchan} eq substr($arg[2], 1)); # %onchan holds time user joined channel. used just to now who is there - $onchan{$usernick}=time() if ($opts->{botchan} eq substr($arg[2], 1)); + $onchan{$usernick}=time(); if (grep { $rps->{$_}{nick} eq $usernick } keys %$rps) { Irpg::Irc::sts("WHOIS $usernick"); @@ -404,7 +414,7 @@ sub parse { if ($opts->{'detectsplits'} && exists($split{substr($arg[0],1)})) { delete($split{substr($arg[0],1)}); } - elsif ($opts->{botnick} eq $usernick && $opts->{botchan} eq substr($arg[2], 1)) { + elsif ($opts->{botnick} eq $usernick) { Irpg::Irc::sts("WHO $opts->{botchan}"); (my $opcmd = $opts->{botopcmd}) =~ s/%botnick%/$opts->{botnick}/eg; Irpg::Irc::sts($opcmd); @@ -560,7 +570,7 @@ sub parse { $source = $arg[2]; } if ((!$token_need || $token_given) && - grep /\Q$arg[3]\E/, + grep { $arg[3] eq $_ } grep({ $commands{$_}->{$msgtype} } keys(%commands))) { # the message is a command valid in $msgtype message if (!$commands{$arg[3]}->{adm} || ha($usernick)) { diff --git a/Irpg/Utils.pm b/Irpg/Utils.pm index 6f77e69..8eea365 100644 --- a/Irpg/Utils.pm +++ b/Irpg/Utils.pm @@ -20,7 +20,7 @@ use warnings; use Data::Dumper; use Irpg::Irc qw(:interaction); use Exporter qw(import); -our @EXPORT = qw(&clog &debug); +our @EXPORT = qw(&clog &debug &pronoun); our @EXPORT_OK = qw(&set_debug_status &daemonize &ts &mksalt &checksplits &duration &pronoun @@ -37,9 +37,10 @@ foreach (<Irpg/Classes/*.pm>) { my $_configfile = '.irpg.conf'; +my $delayed_funcs = []; my $opts; -=head1 FUNCTION ts +=head1 FUNCTION mksalt This function returns a random salt for passwds =cut sub mksalt { # generate a random salt for passwds @@ -47,7 +48,7 @@ sub mksalt { # generate a random salt for passwds } -=head1 FUNCTION ts +=head1 FUNCTION pronoun This function returns a pronoun fitting the given gender. =over =item SCALAR (int) - type of pronoun (1->subject, 2->possessive, 3->object) @@ -69,7 +70,7 @@ sub pronoun { 'obj'=>'them'}->{$type}; } -=head1 FUNCTION ts +=head1 FUNCTION duration This function returns a human readable duration. =over =item SCALAR (int) - number of seconds @@ -245,8 +246,8 @@ sub loaddb { # load the players database chomp($l); next if $l =~ /^#/; # skip comments my @i = split("\t",$l); - print Dumper(@i) if @i != 46; - if (@i != 46) { + print Dumper(@i) if @i != 45; + if (@i != 45) { Irpg::Irc::sts("QUIT: Anomaly in loaddb(); line $. of $opts->{dbfile} has ". "wrong fields (".scalar(@i).")"); debug("Anomaly in loaddb(); line $. of $opts->{dbfile} has wrong ". @@ -297,17 +298,28 @@ sub loaddb { # load the players database $classname, $rps->{$i[0]}{points}, $rps->{$i[0]}{actions}, - $rps->{$i[0]}{next_a}, + #$rps->{$i[0]}{next_a}, $rps->{$i[0]}{alignment}, $rps->{$i[0]}{gender}) = (@i[1..7],($startup?0:$i[8]),@i[9..$#i]); - $classname =~ s/ /_/g; + $classname =~ s/ /_/g; $rps->{$i[0]}{class} = eval 'Irpg::Classes::'.$classname. '->new($rps->{$i[0]}{stats})'; + $rps->{$i[0]}{next_a} = (); } close(RPS); debug("loaddb(): loaded ".scalar(keys(%$rps))." accounts, ". scalar(keys(%prev_online))." previously online."); + + return unless (open(DELAYS, $opts->{actionsfile})); + while (<DELAYS>) { + chomp; + next if /^#/; # skip comments + my ($user, @delays) = split; + push @{$rps->{$user}{next_a}}, @delays; + } + close(DELAYS); + return \%prev_online; } @@ -322,6 +334,9 @@ sub writedb { Irpg::Irc::chanmsg("ERROR: Cannot write $opts->{dbfile}: $!"); return 0; }; + my $open_delays = open(DELAYS, ">$opts->{actionsfile}"); + debug("ERROR: Cannot write $opts->{actionsfile}: $!") unless ($open_delays); + print RPS join("\t","# username", "pass", "is admin", @@ -365,7 +380,7 @@ sub writedb { "class", "points", "actions", - "next_a", + #"next_a", "alignment", "gender")."\n"; my $k; @@ -415,12 +430,14 @@ sub writedb { $rps->{$k}{class}->{NAME}, $rps->{$k}{points}, $rps->{$k}{actions}, - $rps->{$k}{next_a}, + #$rps->{$k}{next_a}, $rps->{$k}{alignment}, $rps->{$k}{gender})."\n"; + print DELAYS "$k ".join(' ', @{$rps->{$k}{next_a}})."\n" if ($open_delays); } } close(RPS); + close(DELAYS) if ($open_delays); } =head1 FUNCTION createdb @@ -509,4 +526,31 @@ sub checksplits { # removed expired split hosts from the hash } } +=head1 FUNCTION execute_delayed + add an entry in the list of delayed functions + ordered by time to be called +=over +=item SCALAR - time when the function will be called +=item SCALAR - function to be called +=item ARRAY - arguments +=back +=cut +sub execute_delayed { + my ($delay, $func, @args) = @_; + return unless (defined($delay) && defined($func)); + my $time = $delay+time(); + push @$delayed_funcs, [$time, $func, @args]; + @$delayed_funcs = sort { $a->[0] <=> $b->[0] } @$delayed_funcs; + return $time; +} + +sub exec_stack_delayed { + my $curtime = shift or return; + while (@$delayed_funcs + && $delayed_funcs->[0][0] <= $curtime) { + my @func = @{shift @$delayed_funcs}; + $func[1]->(@func[2..$#func]); + } +} + 1; -- GitLab