From cea5d519520a3ed3ef9f46070c786f7d5f8dc5d8 Mon Sep 17 00:00:00 2001
From: ElTata <gennuso2015@perso.iiens.net>
Date: Fri, 4 Oct 2019 01:06:32 +0200
Subject: [PATCH] classes wip: - Fight module became Action - there is 3
 actions available : fight/mystic/steal - everywhere there was initially a
 fight, now one of these 3 is chosen - the choice is random, ponderated with
 stats of player - for each action, classes have one method for atk and one
 for defense - actions are atk of attacker vs defense of defenser (same base
 calcul, but with stats linked to the action) - fight action -> attacker
 reduce/increase TTL if win/lose (critic: defenser increase TTL) - mystic
 action -> attacker exchange TTL with defender (critic: lose even more TTL
 with no impact on defenser) - steal action -> steal an item if victory (may
 be a worst item than ours) or decrease item capacity (if lose) (critic: steal
 the best item of defender) - the atk/def of base classe use all stats in a
 balanced and cyclic way - itemsum is modified by str stat (used on every
 action) - level of item found is modified by wis stat - (almost) all TTL
 modifications are affected by cha stat - diverse info commands prints more
 info, errors fixed - other stuff, probably...

---
 Irpg/Action.pm          | 475 ++++++++++++++++++++++++++++++++++++++++
 Irpg/Admin.pm           |  11 +-
 Irpg/Classes/Farmer.pm  |  75 +++++--
 Irpg/Classes/Mage.pm    |  10 -
 Irpg/Classes/Thief.pm   |  10 -
 Irpg/Classes/Warrior.pm |  10 -
 Irpg/Event.pm           |  64 ++++--
 Irpg/Fight.pm           | 295 -------------------------
 Irpg/Main.pm            |  21 +-
 Irpg/Quest.pm           |   7 +-
 Irpg/Users.pm           |  17 +-
 Irpg/Utils.pm           |   8 +-
 12 files changed, 606 insertions(+), 397 deletions(-)
 create mode 100644 Irpg/Action.pm
 delete mode 100644 Irpg/Fight.pm

diff --git a/Irpg/Action.pm b/Irpg/Action.pm
new file mode 100644
index 0000000..e08ab17
--- /dev/null
+++ b/Irpg/Action.pm
@@ -0,0 +1,475 @@
+package Irpg::Action;
+
+use strict;
+use warnings;
+use Irpg::Utils qw(:text);
+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);
+
+my $primnick_ref;
+my $opts;
+my $rps;
+=head1 FUNCTION init_pkg
+	This function sets the references to
+	options and players hashes.
+=over
+=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 fisher_yates_shuffle {
+    my $array = shift;
+    my $i;
+    for ($i = @$array; --$i; ) {
+        my $j = int rand ($i+1);
+        next if $i == $j;
+        @$array[$i,$j] = @$array[$j,$i];
+    }
+}
+
+sub itemsum {
+    my $user = shift;
+    # is this for a battle? if so, good users get a 10% boost and evil users get
+    # a 10% detriment
+    my $battle = shift;
+    return -1 unless defined $user;
+    my $sum = 0;
+    if ($user eq $$primnick_ref) {
+        for my $u (keys(%$rps)) {
+            $sum = itemsum($u) if $sum < itemsum($u);
+        }
+        return $sum+1;
+    }
+    if (!exists($rps->{$user})) { return -1; }
+    $sum += int($rps->{$user}{item}{$_}) for keys(%{$rps->{$user}{item}});
+    if ($battle) {
+        $sum = $rps->{$user}{alignment} eq 'e' ? int($sum*.9) :
+               $rps->{$user}{alignment} eq 'g' ? int($sum*1.1) :
+               $sum;
+		return $rps->{$user}{class}->real_sum($sum);
+    }
+    return $sum;
+}
+
+sub choose_action {
+	my $user = shift;
+	my $phy_score = $rps->{$user}{class}->str() + $rps->{$user}{class}->con();
+	my $mys_score = $rps->{$user}{class}->wis() + $rps->{$user}{class}->int();
+	my $stl_score = $rps->{$user}{class}->cha() + $rps->{$user}{class}->dex();
+	my $score = int(rand($phy_score+$mys_score+$stl_score));
+	return 'fight' if ($score < $phy_score);
+	return 'mystic' if ($score < $mys_score);
+	return 'steal';
+}
+
+sub fight_result {
+	my ($p1, $p2, $win, $wanted) = @_;
+    return unless $p1 && $p2 && defined($win);
+	my @queue = ();
+	if ($win) {
+        my $gain = int($rps->{$p2}{level}/4);
+        $gain = 7 if $gain < 7;
+		$gain *= $rps->{$p1}{class}->{MOD_CRT} if ($win == 2);
+		$gain *= 1.2 if ($wanted);
+        $gain = int(($gain/100)*$rps->{$p1}{next});
+        $gain = -$rps->{$p1}{class}->real_gain(-$gain);
+        $rps->{$p1}{next} -= $gain;
+		push(@queue,
+			duration($gain)." is removed from $p1\'s clock.");
+		if ($win == 2) {
+			$gain = 5 + int(rand(20));
+            $gain = int(($gain/100)*$rps->{$p2}{next});
+			$gain = $rps->{$p2}{class}->real_gain($gain);
+            $rps->{$p2}{next} += $gain;
+			push(@queue,
+				"$p1 has dealt $p2 a Critical Strike! ".
+				duration($gain)." is added to $p2\'s clock. ".
+				"$p2 reaches next level in ".duration($rps->{$p2}{next}).".");
+		}
+	}
+	else {
+        my $gain = $rps->{$p2}{level}/7;
+        $gain = 7 if $gain < 7;
+		$gain *= 1.2 if ($wanted);
+        $gain = int(($gain/100)*$rps->{$p1}{next});
+		$gain = $rps->{$p1}{class}->real_gain($gain);
+        $rps->{$p1}{next} += $gain;
+		push(@queue,
+			duration($gain)." is removed to $p1\'s clock.");
+	}
+	$queue[0] .= " $p1 reaches next level in ".duration($rps->{$p1}{next}).".";
+	return \@queue;
+}
+
+sub mystic_result {
+	my ($p1, $p2, $win, $wanted) = @_;
+    return unless $p1 && $p2 && defined($win);
+	my @queue = ();
+	if ($win) {
+        my $gain = int($rps->{$p2}{level}/8);
+        $gain = 5 if $gain < 5;
+		$gain *= 1.2 if ($wanted);
+        $gain = int(($gain/100)*$rps->{$p1}{next});
+        $gain = -$rps->{$p1}{class}->real_gain(-$gain);
+        $rps->{$p1}{next} -= $gain;
+        $rps->{$p2}{next} += $gain;
+		push(@queue,
+			"$p1 transfers ".duration($gain)." from ".
+			pronoun(2, $rps->{$p1}{gender}). "clock to $p2\'s. ".
+			"$p1 reaches next level in ".duration($rps->{$p1}{next}).". ".
+			"$p2 reaches next level in ".duration($rps->{$p2}{next}).".");
+		if ($win == 2) {
+			$gain = 5 + int(rand(20));
+			$gain *= $rps->{$p1}{class}->{MOD_CRT};
+            $gain = int(($gain/100)*$rps->{$p1}{next});
+			$gain = -$rps->{$p2}{class}->real_gain(-$gain);
+            $rps->{$p1}{next} -= $gain;
+			push(@queue,
+				"$p1 has cast a Critical Spell! ".
+				duration($gain)." of ".pronoun(2, $rps->{$p1}{gender}).
+				" clock is sent to another plan. ".
+				"$p1 reaches next level in ".duration($rps->{$p1}{next}).".");
+		}
+	}
+	else {
+        my $gain = $rps->{$p2}{level}/10;
+        $gain = 5 if $gain < 5;
+		$gain *= 1.2 if ($wanted);
+        $gain = int(($gain/100)*$rps->{$p1}{next});
+		$gain = $rps->{$p1}{class}->real_gain($gain);
+        $rps->{$p1}{next} += $gain;
+        $rps->{$p2}{next} -= $gain;
+		push(@queue,
+			"$p1 transfers ".duration($gain)." from ".
+			pronoun(2, $rps->{$p1}{gender}). "clock to $p2\'s. ".
+			"$p1 reaches next level in ".duration($rps->{$p1}{next}).". ".
+			"$p2 reaches next level in ".duration($rps->{$p2}{next}).".");
+	}
+	return \@queue;
+}
+
+sub steal_result {
+	my ($p1, $p2, $win, $wanted) = @_;
+    return unless $p1 && $p2 && defined($win);
+	my @queue = ();
+	if ($win == 1) {
+	    my @items = ("ring","amulet","charm","weapon","helm","tunic",
+	                 "pair of gloves","set of leggings","shield",
+	                 "pair of boots");
+	    my $type = $items[rand(@items)];
+	    if (int($rps->{$p2}{item}{$type}) > int($rps->{$p1}{item}{$type})) {
+	        my $tempitem = $rps->{$p1}{item}{$type};
+	        $rps->{$p1}{item}{$type}=$rps->{$p2}{item}{$type};
+	        $rps->{$p2}{item}{$type} = $tempitem;
+			push(@queue,
+				"$p1 leaves, smiling at the thought of ".
+				pronoun(2, $rps->{$p1}{gender})." wicked deed, whilst $p2 ".
+				"remains unaware of ".pronoun(2, $rps->{$p2}{gender}).
+				" own demise.");
+			Irpg::Irc::notice(
+				"You (rightfully) acquired a new level ".
+				int($rps->{$p1}{item}{$type})." $type, which looks far better ".
+				"than you old level ".int($rps->{$p2}{item}{$type})." $type! ".
+				"You left your old one to whom you took the new from.");
+	    }
+		else {
+			push(@queue,
+				"Unfortunately, $p2\'s stolen equipement ".
+				"revealed itself to be useless to $p1, who returns it ".
+				"to its rightful owner.");
+		}
+	}
+	elsif ($win == 2) {
+		push(@queue, "$p1 has managed a Critical Theft! ");
+		my $type = "ring";
+		my $val = 0;
+		while( my ($k,$v) = each(%{$rps->{$p2}{item}}) ) {
+			($type, $val) = ($k, $v) if ($v > $val)
+		}
+		if ($val > $rps->{$p1}{item}{$type}) {
+	        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 ".
+				"the finest of $p2\'s equipment. $p1 then proceeds to crawl ".
+				"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.";
+			Irpg::Irc::notice(
+				"You (rightfully) acquired a new level ".
+				int($rps->{$p1}{item}{$type})." $type, which looks far better ".
+				"than you old level ".int($rps->{$p2}{item}{$type})." $type! ".
+				"You left your old one to whom you took the new from.");
+		}
+		else {
+			$queue[$#queue] .= "Alas, despite ".pronoun(2, $rps->{$p1}{gender}).
+				" unquestionnable efforts, $p2\'s stolen equipement ".
+				"revealed itself to be useless to $p1, who returns it ".
+				"to its rightful owner.";
+		}
+
+	}
+	else {
+	    my @items = ("ring","amulet","charm","weapon","helm","tunic",
+	                 "pair of gloves","set of leggings","shield",
+	                 "pair of boots");
+	    my $type = $items[rand(@items)];
+		my ($val, $suffix) = ($rps->{$p1}{item}{$type} =~ /(\d+)(\D?)$/);
+        $rps->{$p1}{item}{$type} = int($val * .9);
+        $rps->{$p1}{item}{$type} .= $suffix;
+		push(@queue,
+			"$p1 did a false move, and damaged ".
+			pronoun(2, $rps->{$p1}{gender})." $type, which lost ".
+			"10% of its efficiency.");
+	}
+	return \@queue;
+}
+
+sub perform_action {
+    my ($p1, $p2, $action_type) = @_;
+    return unless $p1 && $p2 && $action_type;
+	my ($p1atk, $p2def);
+	$p1atk = eval '$rps->{$p1}{class}->'.$action_type.'_atk()';
+	$p2def = eval '$rps->{$p2}{class}->'.$action_type.'_def()';
+    my $p1sum = itemsum($p1,1);
+    my $p2sum = itemsum($p2,1);
+    my $p1roll = int(rand($p1sum) * $p1atk);
+    my $p2roll = int(rand($p2sum) * $p2def);
+	my $ret = {
+			p1sum=>int($p1sum/5), p2sum=>int($p2sum/5),
+			p1roll=>int($p1roll/5), p2roll=>int($p2roll/5),
+			vict=>0};
+    if ($p1roll >= $p2roll) {
+		# VICTORY
+		$ret->{vict}++;
+        my $align_mod = $rps->{$p1}{alignment} eq "g" ? 0.05 :
+						$rps->{$p1}{alignment} eq "e" ? -0.10 :
+						1;
+		if ($p1roll >= $p1sum * $rps->{$p1}{class}->atk()
+					  * ($rps->{$p1}{class}->{CRIT_SK} + $align_mod)) {
+			# CRITICAL STRIKE
+			$ret->{vict}++;
+        }
+    }
+	return $ret;
+}
+
+sub challenge_opp { # pit argument player against random player
+    my $p1 = shift;
+    if ($rps->{$p1}{level} < 25) { return unless rand(4) < 1; }
+    my @opps = grep { $rps->{$_}{online} && $p1 ne $_ } keys(%$rps);
+    return unless @opps;
+    my $p2 = $opps[int(rand(@opps))];
+	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->{p1sum}]";
+	my $p2_res = "[$ret_action->{p2roll}/$ret_action->{p2sum}]";
+	my $mesg = '';
+
+	if ($action_type eq 'fight') {
+		$mesg .=	"$p1 $p1_res has challenged $p2 $p2_res in combat and ".
+				($ret_action->{vict} ? 'won' : 'lost')."! ";
+	}
+	elsif ($action_type eq 'mystic') {
+		$mesg .= "$p1 $p1_res has cast a spell on $p2 $p2_res and ".
+				($ret_action->{vict} ? 'succeed' : 'failed')."! ";
+	}
+	else { #($action_type eq 'steal')
+		$mesg .= "$p1 $p1_res has tried to steal from $p2 $p2_res and ".
+				($ret_action->{vict} ? 'succeed' : 'failed')."! ";
+	}
+	$mesg .= shift @$res_action;
+	Irpg::Irc::chanmsg(Irpg::Utils::clog($mesg));
+	foreach (@$res_action) { Irpg::Irc::chanmsg(Irpg::Utils::clog($_)); }
+}
+
+
+sub collision_action {
+    my($p1,$p2) = @_;
+	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->{p1sum}]";
+	my $p2_res = "[$ret_action->{p2roll}/$ret_action->{p2sum}]";
+	my $mesg = "$p1 $p1_res has come upon $p2 $p2_res and ";
+	if ($action_type eq 'fight') {
+		$mesg .= ($ret_action->{vict} ?
+					'taken'.pronoun(3, $rps->{$p2}{gender}):
+					'been defeated').
+				" in combat! ";
+	}
+	elsif ($action_type eq 'mystic') {
+		$mesg .= ($ret_action->{vict} ?
+					'made '.pronoun(3, $rps->{$p2}{gender}).
+						' lost '.pronoun(2, $rps->{$p2}{gender}):
+					'lost '.pronoun(2, $rps->{$p1}{gender})).
+				" mind !";
+	}
+	else { #($action_type eq 'steal')
+		$mesg .= ($ret_action->{vict} ?
+					'stolen ':
+					'been caught stealing ').
+				'from '.pronoun(3, $rps->{$p2}{gender}."!");
+	}
+	$mesg .= shift @$res_action;
+	Irpg::Irc::chanmsg(Irpg::Utils::clog($mesg));
+	foreach (@$res_action) { Irpg::Irc::chanmsg(Irpg::Utils::clog($_)); }
+}
+
+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;
+    fisher_yates_shuffle(\@opp);
+
+	# the gain is 20% of the lower TTL of defenders
+    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);
+
+	# the fight, every one picks an action, and roll with its modificator
+	my ($mysum, $oppsum) = (0, 0);
+	my ($myroll, $opproll) = (0, 0);
+	my ($action_type, $sum, $mod);
+	for (my $i = 0; $i <= 6 ; $i++) {
+		$action_type = choose_action($opp[$i]);
+		$sum = int(itemsum($opp[$i],1));
+		if ($i < 3) {
+			$mysum += $sum;
+			$mod = eval '$rps->{$p1}{class}->'.$action_type.'_atk()';
+			$myroll += int(rand($sum) * $mod);
+		}
+		else {
+			$oppsum += $sum;
+			$mod = eval '$rps->{$p1}{class}->'.$action_type.'_def()';
+			$opproll += int(rand($sum) * $mod);
+		}
+	}
+
+	my ($state, $moved);
+    if ($myroll >= $opproll) {
+		($state, $moved) = ('won', 'removed from');
+		for (my $i = 0; $i < 3; $i++) {
+			$rps->{$opp[$i]}{next} += $rps->{$opp[$i]}{class}->real_gain(-$gain);
+		}
+    }
+    else {
+		($state, $moved) = ('lost', 'added to');
+		for (my $i = 0; $i < 3; $i++) {
+			$rps->{$opp[$i]}{next} += $rps->{$opp[$i]}{class}->real_gain($gain);
+		}
+    }
+	Irpg::Irc::chanmsg(Irpg::Utils::clog(
+		"$opp[0], $opp[1], and $opp[2] ".
+		"[".($myroll/5)."/".($mysum/5)."] have team battled ".
+		"$opp[3], $opp[4], and $opp[5] ".
+		"[".($opproll/5)."/".($oppsum/5)."] and $state! ".
+		duration($gain)." is $moved their clocks."));
+}
+
+sub rpcheck {
+	my $rpreport = shift;
+    if ($rpreport%3600==0 && $rpreport) { # 1 hour
+        my @players = grep { $rps->{$_}{online} &&
+                             $rps->{$_}{level} > 44 } keys(%$rps);
+        # 20% of all players must be level 45+
+        if ((scalar(@players)/scalar(grep { $rps->{$_}{online} } keys(%$rps))) > .15) {
+            challenge_opp($players[int(rand(@players))]);
+        }
+    }
+}
+
+sub do_action {
+	my ($userhost, $usernick, $username, $source, @arg) = @_;
+	if (!defined($username)) {
+		Irpg::Irc::notice("You are not logged in.", $usernick);
+		return;
+	}
+	if (!$rps->{$username}{actions}) {
+		Irpg::Irc::notice("You feel too weak to do anything.", $usernick);
+		Irpg::Irc::chanmsg("$username seems to be trying to achieve ".
+			"an undefined deed, but collapses out of exhaustion.");
+		return;
+	}
+    my @opps = grep { $rps->{$_}{online} && $username ne $_ } keys(%$rps);
+	if (!@opps) {
+		Irpg::Irc::notice("Your call echoes in the ambient silence...",$usernick);
+		Irpg::Irc::chanmsg(
+			"$username is desperatly searching for someone, ".
+			"but ".pronoun(2, $rps->{$username}{gender})." call remains ".
+			"unheard in the silent desert of ".pronoun(2, $rps->{$username}).
+			" loneliness.");
+		return;
+	}
+	my $p2;
+	if (exists($arg[0]) && !(grep { $arg[0] } @opps)) {
+		if (exists($rps->{$arg[0]})) {
+			Irpg::Irc::notice(
+				"You searched every places you know, but $arg[0] ".
+				"is nowhere to be found...", $usernick);
+		}
+		else {
+			Irpg::Irc::notice(
+				"You asked everyone you met, but no one ever ".
+				"heard of $arg[0].", $usernick);
+		}
+		Irpg::Irc::chanmsg(
+			"$username calls $arg[0] for an honest interaction, but no one ".
+			"can answer ".pronoun(2, $rps->{$username}{gender})." call.");
+		return
+	}
+	elsif (exists($arg[0])) {
+		$p2 = $arg[0];
+	}
+	else {
+		$p2 = $opps[int(rand(@opps))];
+	}
+	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->{p1sum}]";
+	my $p2_res = "[$ret_action->{p2roll}/$ret_action->{p2sum}]";
+	#my $mesg = "$p1 $p1_res has come upon $p2 $p2_res and ";
+	my $mesg = '';
+
+	if ($action_type eq 'fight') {
+		$mesg .= "$p1 $p1_res has provoked $p2 $p2_res in a fight, and ".
+				($ret_action->{vict} ? 'won': 'lost')."!";
+	}
+	elsif ($action_type eq 'mystic') {
+		$mesg .= "$p1 $p1_res has performed mystical deeds on $p2 $p2_res, and ".
+				($ret_action->{vict} ?
+					'dazed '.pronoun(3, $rps->{$p2}{gender}):
+					'got confused').
+				"!";
+	}
+	else { #($action_type eq 'steal')
+		$mesg .= "$p1 $p1_res has sneaked on $p2 $p2_res in the shadows and ".
+				($ret_action->{vict} ? 'stolen ' : 'been caught stealing ').
+				'from '.pronoun(3, $rps->{$p2}{gender}."!");
+	}
+	$mesg .= shift @$res_action;
+	Irpg::Irc::chanmsg(Irpg::Utils::clog($mesg));
+	foreach (@$res_action) { Irpg::Irc::chanmsg(Irpg::Utils::clog($_)); }
+}
+
+our $commands = {
+	action => {ref => \&do_action, adm => 0, prv => 1, pub => 1,
+			   hlp => 'ACTION [<char name>]: perform an action (fight/mystic/steal) '.
+			  		  'with someone. The action is chosen at random, '.
+				  	  'with more chance for the one you are the better at.'}
+};
+
+1;
diff --git a/Irpg/Admin.pm b/Irpg/Admin.pm
index 1f5b9f2..edfa2da 100644
--- a/Irpg/Admin.pm
+++ b/Irpg/Admin.pm
@@ -116,12 +116,6 @@ sub rmadmin {
 	}
 }
 
-sub do_hog {
-	my ($userhost, $usernick, $username, $source, @arg) = @_;
-	Irpg::Irc::chanmsg("$usernick has summoned the Hand of God.");
-	hog();
-}
-
 sub rehash {
 	my ($userhost, $usernick, $username, $source, @arg) = @_;
 	$opts = readconfig();
@@ -222,7 +216,7 @@ sub clearq {
 }
 
 
-our $commands = {
+our commands = {
 	go 		=> {ref => \&join_chans, adm => 1, prv => 1, pub => 0,
 				hlp => 'GO <chan>[ <chan> ...] : join the given chan(s)'},
 
@@ -248,9 +242,6 @@ our $commands = {
 				hlp => 'MKADMIN <username> : remove the isadmin flag '.
 					   'for a given <username>'},
 
-	hog		=> {ref => \&do_hog,	 adm => 1, prv => 1, pub => 1,
-				hlp => 'HOG : summon the Hand Of God spell.'},
-
 	rehash	=> {ref => \&rehash,	 adm => 1, prv => 1, pub => 0,
 				hlp => 'REHASH : reload configuration file.'},
 
diff --git a/Irpg/Classes/Farmer.pm b/Irpg/Classes/Farmer.pm
index 580e837..340d512 100644
--- a/Irpg/Classes/Farmer.pm
+++ b/Irpg/Classes/Farmer.pm
@@ -17,11 +17,6 @@ sub new {
 	$self->{MOD_INT} = 1; # intelligence
 	$self->{MOD_CHA} = 1; # charisma
 	$self->{MOD_DEX} = 1; # dexterity
-	$self->{MOD_WIN} = 1; # time modificator when winning a fight
-	$self->{MOD_DEF} = 1; # time modificator when losing a fight
-	$self->{MOD_POS} = 1; # positive events
-	$self->{MOD_NEG} = 1; # negative events
-	$self->{MOD_EQP} = 1; # equipement
 	$self->{CRIT_SK} = 0.95; # critical strike minimum range, the lower the largest acceptance
 	$self->{MOD_CRT} = 1; # criticak strike modificator
 	return $self;
@@ -29,41 +24,91 @@ sub new {
 
 sub str {
 	my $self = shift;
-	return $self->{STATS}->{str} * $self->{MOD_STR};
+	return $self->{STATS}{str} * $self->{MOD_STR};
 }
 
 sub con {
 	my $self = shift;
-	return $self->{STATS}->{con} * $self->{MOD_CON};
+	return $self->{STATS}{con} * $self->{MOD_CON};
 }
 
 sub wis {
 	my $self = shift;
-	return $self->{STATS}->{wis} * $self->{MOD_WIS};
+	return $self->{STATS}{wis} * $self->{MOD_WIS};
 }
 
 sub int {
 	my $self = shift;
-	return $self->{STATS}->{int} * $self->{MOD_INT};
+	return $self->{STATS}{int} * $self->{MOD_INT};
 }
 
 sub cha {
 	my $self = shift;
-	return $self->{STATS}->{cha} * $self->{MOD_CHA};
+	return $self->{STATS}{cha} * $self->{MOD_CHA};
 }
 
 sub dex {
 	my $self = shift;
-	return $self->{STATS}->{dex} * $self->{MOD_DEX};
+	return $self->{STATS}{dex} * $self->{MOD_DEX};
 }
 
-sub atk {
+sub fight_atk {
 	my $self = shift;
-	return ($self->str() + $self->int() + $self->dex())/3;
+	return ($self->str() + $self->dex())/2;
 }
-sub def {
+sub fight_def {
 	my $self = shift;
-	return ($self->con() + $self->wis() + $self->cha())/3;
+	return ($self->con() + $self->dex())/2;
 }
 
+sub mystic_atk {
+	my $self = shift;
+	return ($self->int() + $self->con())/2;
+}
+sub mystic_def {
+	my $self = shift;
+	return ($self->wis() + $self->con())/2;
+}
+
+sub steal_atk {
+	my $self = shift;
+	return ($self->dex() + $self->int())/2;
+}
+sub steal_def {
+	my $self = shift;
+	return ($self->cha() + $self->int())/2;
+}
+
+sub real_gain {
+	# to apply on TTL modificators
+	my ($self, $time) = @_;
+	return unless ($time =~ m/^-?\d$/);
+	$time = $time >= 0 ?
+		$time*(1-($self->cha()-1)/10):	# add less time
+		$time*(1+($self->cha()-1)/10);	# remove more time
+	return Core::int($time);
+}
+
+sub real_sum {
+	# to apply on itemsum
+	my ($self, $sum) = @_;
+	return unless ($sum =~ m/^-?\d$/);
+	$sum = $sum >= 0 ?
+		$sum*(1+($self->str()-1)/10):
+		$sum*(1-($self->str()-1)/10);
+	return Core::int($sum);
+}
+
+sub real_lvl {
+	# to apply on found items
+	my ($self, $lvl) = @_;
+	return unless ($lvl =~ m/^-?\d$/);
+	$lvl = $lvl >= 0 ?
+		$lvl*(1+($self->wis()-1)/10):
+		$lvl*(1-($self->wis()-1)/10);
+	return Core::int($lvl);
+}
+
+
+
 1;
diff --git a/Irpg/Classes/Mage.pm b/Irpg/Classes/Mage.pm
index 7ce1d67..1ee0538 100644
--- a/Irpg/Classes/Mage.pm
+++ b/Irpg/Classes/Mage.pm
@@ -15,14 +15,4 @@ sub new {
 	return $self;
 }
 
-sub atk {
-	my $self = shift;
-	return ($self->int() + $self->dex())/2;
-}
-
-sub def {
-	my $self = shift;
-	return ($self->wis() + $self->dex())/2;
-}
-
 1;
diff --git a/Irpg/Classes/Thief.pm b/Irpg/Classes/Thief.pm
index dff3d84..0d76763 100644
--- a/Irpg/Classes/Thief.pm
+++ b/Irpg/Classes/Thief.pm
@@ -15,14 +15,4 @@ sub new {
 	return $self;
 }
 
-sub atk {
-	my $self = shift;
-	return ($self->int() + $self->dex())/2;
-}
-
-sub def {
-	my $self = shift;
-	return ($self->con() + $self->dex())/2;
-}
-
 1;
diff --git a/Irpg/Classes/Warrior.pm b/Irpg/Classes/Warrior.pm
index 2d66da7..181dcee 100644
--- a/Irpg/Classes/Warrior.pm
+++ b/Irpg/Classes/Warrior.pm
@@ -15,14 +15,4 @@ sub new {
 	return $self;
 }
 
-sub atk {
-	my $self = shift;
-	return ($self->str() + $self->dex())/2;
-}
-
-sub def {
-	my $self = shift;
-	return ($self->con() + $self->dex())/2;
-}
-
 1;
diff --git a/Irpg/Event.pm b/Irpg/Event.pm
index 36ba06a..ca0f599 100644
--- a/Irpg/Event.pm
+++ b/Irpg/Event.pm
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 use Irpg::Utils qw(:text);
 use Irpg::Irc qw(:interaction);
-use Irpg::Fight;
+use Irpg::Action;
 use Exporter qw(import);
 our @EXPORT = qw(find_item);
 our @EXPORT_OK = qw(hog);
@@ -30,11 +30,11 @@ sub find_item { # find item for argument player
     my $ulevel;
     for my $num (1 .. int($rps->{$u}{level}*1.5)) {
         if (rand(1.4**($num/4)) < 1) {
-            $level = $num;
+            $level = $rps->{$u}{class}->real_lvl($num);
         }
     }
     if ($rps->{$u}{level} >= 25 && rand(40) < 1) {
-        $ulevel = 50+int(rand(25));
+        $ulevel = $rps->{$u}{class}->real_lvl(50+int(rand(25)));
         if ($ulevel >= $level && $ulevel > int($rps->{$u}{item}{helm})) {
             Irpg::Irc::notice("The light of the gods shines down upon you! You have ".
                    "found the level $ulevel Mattt's Omniscience Grand Crown! ".
@@ -45,7 +45,7 @@ sub find_item { # find item for argument player
         }
     }
     elsif ($rps->{$u}{level} >= 25 && rand(40) < 1) {
-        $ulevel = 50+int(rand(25));
+        $ulevel = $rps->{$u}{class}->real_lvl(50+int(rand(25)));
         if ($ulevel >= $level && $ulevel > int($rps->{$u}{item}{ring})) {
             Irpg::Irc::notice("The light of the gods shines down upon you! You have ".
                    "found the level $ulevel Juliet's Glorious Ring of ".
@@ -57,7 +57,7 @@ sub find_item { # find item for argument player
         }
     }
     elsif ($rps->{$u}{level} >= 30 && rand(40) < 1) {
-        $ulevel = 75+int(rand(25));
+        $ulevel = $rps->{$u}{class}->real_lvl(75+int(rand(25)));
         if ($ulevel >= $level && $ulevel > int($rps->{$u}{item}{tunic})) {
             Irpg::Irc::notice("The light of the gods shines down upon you! You have ".
                    "found the level $ulevel Res0's Protectorate Plate Mail! ".
@@ -68,7 +68,7 @@ sub find_item { # find item for argument player
         }
     }
     elsif ($rps->{$u}{level} >= 35 && rand(40) < 1) {
-        $ulevel = 100+int(rand(25));
+        $ulevel = $rps->{$u}{class}->real_lvl(100+int(rand(25)));
         if ($ulevel >= $level && $ulevel > int($rps->{$u}{item}{amulet})) {
             Irpg::Irc::notice("The light of the gods shines down upon you! You have ".
                    "found the level $ulevel Dwyn's Storm Magic Amulet! Your ".
@@ -79,7 +79,7 @@ sub find_item { # find item for argument player
         }
     }
     elsif ($rps->{$u}{level} >= 40 && rand(40) < 1) {
-        $ulevel = 150+int(rand(25));
+        $ulevel = $rps->{$u}{class}->real_lvl(150+int(rand(25)));
         if ($ulevel >= $level && $ulevel > int($rps->{$u}{item}{weapon})) {
             Irpg::Irc::notice("The light of the gods shines down upon you! You have ".
                    "found the level $ulevel Jotun's Fury Colossal Sword! Your ".
@@ -90,7 +90,7 @@ sub find_item { # find item for argument player
         }
     }
     elsif ($rps->{$u}{level} >= 45 && rand(40) < 1) {
-        $ulevel = 175+int(rand(26));
+        $ulevel = $rps->{$u}{class}->real_lvl(175+int(rand(26)));
         if ($ulevel >= $level && $ulevel > int($rps->{$u}{item}{weapon})) {
             Irpg::Irc::notice("The light of the gods shines down upon you! You have ".
                    "found the level $ulevel Drdink's Cane of Blind Rage! Your ".
@@ -101,7 +101,7 @@ sub find_item { # find item for argument player
         }
     }
     elsif ($rps->{$u}{level} >= 48 && rand(40) < 1) {
-        $ulevel = 250+int(rand(51));
+        $ulevel = $rps->{$u}{class}->real_lvl(250+int(rand(51)));
         if ($ulevel >= $level && $ulevel >
             int($rps->{$u}{item}{"pair of boots"})) {
             Irpg::Irc::notice("The light of the gods shines down upon you! You have ".
@@ -113,7 +113,7 @@ sub find_item { # find item for argument player
         }
     }
     elsif ($rps->{$u}{level} >= 52 && rand(40) < 1) {
-        $ulevel = 300+int(rand(51));
+        $ulevel = $rps->{$u}{class}->real_lvl(300+int(rand(51)));
         if ($ulevel >= $level && $ulevel > int($rps->{$u}{item}{weapon})) {
             Irpg::Irc::notice("The light of the gods shines down upon you! You have ".
                    "found the level $ulevel Jeff's Cluehammer of Doom! Your ".
@@ -148,7 +148,7 @@ sub hog { # summon the hand of god
                 "and the blessed hand of God carried $player ".
                 duration($time)." toward level ".($rps->{$player}{level}+1).
                 "."));
-        $rps->{$player}{next} -= $time;
+        $rps->{$player}{next} += $rps->{$player}{class}->real_gain(-$time);
     }
     else {
         Irpg::Irc::chanmsg(Irpg::Utils::clog(
@@ -156,7 +156,7 @@ sub hog { # summon the hand of god
                 "and consumed $player with fire, slowing the heathen ".
                 duration($time)." from level ".($rps->{$player}{level}+1).
                 "."));
-        $rps->{$player}{next} += $time;
+        $rps->{$player}{next} += $rps->{$player}{class}->real_lvl($time);
     }
     Irpg::Irc::chanmsg("$player reaches next level in ".duration($rps->{$player}{next}).".");
 }
@@ -203,13 +203,13 @@ sub calamity { # suffer a little one
                 "ironing them! $player\'s $type loses 10% of its ".
                 "effectiveness."));
         }
-        my $suffix="";
-        if ($rps->{$player}{item}{$type} =~ /(\D)$/) { $suffix=$1; }
-        $rps->{$player}{item}{$type} = int(int($rps->{$player}{item}{$type}) * .9);
+		my ($val, $suffix) = ($rps->{$player}{item}{$type} =~ /(\d+)(\D?)$/);
+        $rps->{$player}{item}{$type} = int($val * .9);
         $rps->{$player}{item}{$type}.=$suffix;
     }
     else {
         my $time = int(int(5 + rand(8)) / 100 * $rps->{$player}{next});
+		$time = $rps->{$player}{class}->real_gain($time);
         if (!open(Q,$opts->{eventsfile})) {
             return Irpg::Irc::chanmsg("ERROR: Failed to open $opts->{eventsfile}: $!");
         }
@@ -271,13 +271,13 @@ sub godsend { # bless the unworthy
 				"Spirit of Fortitude! $player\'s $type gains 10% ".
 				"effectiveness."));
         }
-        my $suffix="";
-        if ($rps->{$player}{item}{$type} =~ /(\D)$/) { $suffix=$1; }
-        $rps->{$player}{item}{$type} = int(int($rps->{$player}{item}{$type}) * 1.1);
+		my ($val, $suffix) = ($rps->{$player}{item}{$type} =~ /(\d+)(\D?)$/);
+        $rps->{$player}{item}{$type} = int($val * 1.1);
         $rps->{$player}{item}{$type}.=$suffix;
     }
     else {
         my $time = int(int(5 + rand(8)) / 100 * $rps->{$player}{next});
+		$time = -$rps->{$player}{class}->real_gain(-$time);
         if (!open(Q,$opts->{eventsfile})) {
             return Irpg::Irc::chanmsg("ERROR: Failed to open $opts->{eventsfile}: $!");
         }
@@ -336,11 +336,13 @@ sub evilness {
     }
     else { # being evil only pays about half of the time...
         my $gain = 1 + int(rand(5));
+		$gain = int(($gain/100)*$rps->{$me}{next});
+		$gain = $rps->{$me}{class}->real_gain($gain);
         Irpg::Irc::chanmsg(Irpg::Utils::clog(
-			"$me is forsaken by his evil god. ".
-			duration(int($rps->{$me}{next} * ($gain/100)))." is added ".
-			"to his clock."));
-        $rps->{$me}{next} = int($rps->{$me}{next} * (1 + ($gain/100)));
+			"$me is forsaken by ".pronoun(2, $rps->{$me}{gender}).
+			" evil god. ".duration($gain)." is added to ".
+			pronoun(2, $rps->{$me}{gender})." clock."));
+        $rps->{$me}{next} += $gain;
         Irpg::Irc::chanmsg("$me reaches next level in ".duration($rps->{$me}{next}).".");
     }
 }
@@ -356,8 +358,8 @@ sub goodness {
             "evil men poison them. Together have they prayed to their ".
             "god, and it is his light that now shines upon them. $gain\% ".
             "of their time is removed from their clocks."));
-    $rps->{$players[0]}{next} = int($rps->{$players[0]}{next}*(1 - ($gain/100)));
-    $rps->{$players[1]}{next} = int($rps->{$players[1]}{next}*(1 - ($gain/100)));
+    $rps->{$players[0]}{next} += int($rps->{$players[0]}{class}->real_gain(-$gain/100));
+    $rps->{$players[1]}{next} += int($rps->{$players[1]}{class}->real_gain(-$gain/100));
     Irpg::Irc::chanmsg("$players[0] reaches next level in ".
             duration($rps->{$players[0]}{next}).".");
     Irpg::Irc::chanmsg("$players[1] reaches next level in ".
@@ -387,4 +389,18 @@ sub rpcheck {
     if (rand((12*86400)/$opts->{self_clock}) < $onlinegood) { goodness(); }
 }
 
+
+sub do_hog {
+	my ($userhost, $usernick, $username, $source, @arg) = @_;
+	Irpg::Irc::chanmsg("$usernick has summoned the Hand of God.");
+	hog();
+}
+
+
+our $commands = {
+	hog	=> {ref => \&do_hog, adm => 1, prv => 1, pub => 1,
+			hlp => 'HOG : summon the Hand Of God spell.'}
+};
+
+
 1;
diff --git a/Irpg/Fight.pm b/Irpg/Fight.pm
deleted file mode 100644
index 930d9eb..0000000
--- a/Irpg/Fight.pm
+++ /dev/null
@@ -1,295 +0,0 @@
-package Irpg::Fight;
-
-use strict;
-use warnings;
-use Irpg::Utils qw(:text);
-use Irpg::Irc qw(:interaction);
-use Exporter qw(import);
-our @EXPORT = qw(&challenge_opp &collision_fight &team_battle &itemsum);
-our @EXPORT_OK = qw(itemsum);
-
-my $primnick_ref;
-my $opts;
-my $rps;
-=head1 FUNCTION init_pkg
-	This function sets the references to
-	options and players hashes.
-=over
-=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 fisher_yates_shuffle {
-    my $array = shift;
-    my $i;
-    for ($i = @$array; --$i; ) {
-        my $j = int rand ($i+1);
-        next if $i == $j;
-        @$array[$i,$j] = @$array[$j,$i];
-    }
-}
-
-sub itemsum {
-    my $user = shift;
-    # is this for a battle? if so, good users get a 10% boost and evil users get
-    # a 10% detriment
-    my $battle = shift;
-    return -1 unless defined $user;
-    my $sum = 0;
-    if ($user eq $$primnick_ref) {
-        for my $u (keys(%$rps)) {
-            $sum = itemsum($u) if $sum < itemsum($u);
-        }
-        return $sum+1;
-    }
-    if (!exists($rps->{$user})) { return -1; }
-    $sum += int($rps->{$user}{item}{$_}) for keys(%{$rps->{$user}{item}});
-    if ($battle) {
-        $sum = $rps->{$user}{alignment} eq 'e' ? int($sum*.9) :
-               $rps->{$user}{alignment} eq 'g' ? int($sum*1.1) :
-               $sum;
-		$sum *= $rps->{$user}{class}->{MOD_EQP};
-		return $sum;
-    }
-    return $sum;
-}
-
-sub fight {
-    my ($u, $opp, $wanted) = @_;
-    return unless $u && $opp;
-    my $mysum = itemsum($u,1);
-    my $oppsum = itemsum($opp,1);
-    my $myroll = int(rand($mysum) * $rps->{$u}{class}->atk());
-    my $opproll = int(rand($oppsum) * $rps->{$opp}{class}->def());
-	my $ret = {
-			usum=>int($mysum/5), oppsum=>int($oppsum/5),
-			uroll=>int($myroll/5), opproll=>int($opproll/5),
-			vict=>0, gain=>0, queue=>[]};
-    if ($myroll >= $opproll) {
-		# VICTORY
-        my $gain = int($rps->{$opp}{level}/4);
-        $gain = 7 if $gain < 7;
-		$gain *= $rps->{$u}{class}->{MOD_WIN};
-		$gain *= 1.2 if ($wanted);
-        $gain = int(($gain/100)*$rps->{$u}{next});
-        $rps->{$u}{next} -= $gain;
-		$ret->{vict} = 1;
-		$ret->{gain} = $gain;
-
-        my $align_mod = $rps->{$u}{alignment} eq "g" ? 0.05 :
-						$rps->{$u}{alignment} eq "e" ? -0.10 :
-						1;
-		if ($myroll >= $mysum * $rps->{$u}{class}->atk()
-					  * ($rps->{$u}{class}->{CRIT_SK} + $align_mod)) {
-			# CRITICAL STRIKE
-			$gain = 5 + int(rand(20));
-			$gain *= $rps->{$u}{class}->{MOD_CRT};
-            $gain = int(($gain/100)*$rps->{$opp}{next});
-            $rps->{$opp}{next} += $gain;
-			unshift($ret->{queue},
-				"$u has dealt $opp a Critical Strike! ".
-				duration($gain)." is added to $opp\'s clock."
-				,
-				"$opp reaches next level in ".duration($rps->{$opp}{next}).".");
-        }
-        elsif (rand(25) < 1 && $rps->{$u}{level} > 19) {
-            my @items = ("ring","amulet","charm","weapon","helm","tunic",
-                         "pair of gloves","set of leggings","shield",
-                         "pair of boots");
-            my $type = $items[rand(@items)];
-            if (int($rps->{$opp}{item}{$type}) > int($rps->{$u}{item}{$type})) {
-                my $tempitem = $rps->{$u}{item}{$type};
-                $rps->{$u}{item}{$type}=$rps->{$opp}{item}{$type};
-                $rps->{$opp}{item}{$type} = $tempitem;
-				unshift($ret->{queue},
-					"In the fierce battle, $opp dropped his level ".
-					int($rps->{$opp}{item}{$type})." $type! $u picks it up, ".
-					"tossing his old level ".int($rps->{$u}{item}{$type}).
-					" $type to $opp.");
-            }
-        }
-    }
-    else {
-		# DEFEAT
-        my $gain = $rps->{$opp}{level}/7;
-        $gain = 7 if $gain < 7;
-		$gain *= $rps->{$u}{class}->{MOD_DEF};
-		$gain *= 1.2 if ($wanted);
-        $gain = int(($gain/100)*$rps->{$u}{next});
-        $rps->{$u}{next} += $gain;
-		$ret->{gain} = $gain;
-    }
-	return $ret;
-}
-
-
-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 $res = fight($u, $opp);
-	my ($state, $moved);
-	if ($res->{vict}) {
-		($state, $moved) = ('won', 'removed from');
-	}
-    else {
-		($state, $moved) = ('lost', 'added to');
-    }
-	Irpg::Irc::chanmsg(Irpg::Utils::clog(
-		"$u [$res->{uroll}/$res->{usum}] has challenged $opp [$res->{opproll}/".
-		"$res->{oppsum}] in combat and $state! ".duration($res->{gain})." is ".
-		"$moved $u\'s clock."));
-	Irpg::Irc::chanmsg("$u reaches next level in ".duration($rps->{$u}{next}).".");
-	foreach (@{$res->{queue}}) { Irpg::Irc::chanmsg(Irpg::Utils::clog($_)); }
-}
-
-
-sub collision_fight {
-    my($u,$opp) = @_;
-	my $res = fight($u, $opp);
-	my ($state, $moved);
-	my @ret = ();
-	if ($res->{vict}) {
-		($state, $moved) = ('taken '.pronoun(3, $rps->{$opp}{gender}), 'removed from');
-	}
-    else {
-		($state, $moved) = ('been defeated', 'added to');
-    }
-	Irpg::Irc::chanmsg(Irpg::Utils::clog(
-		"$u [$res->{uroll}/$res->{usum}] has come upon $opp [$res->{opproll}/".
-		"$res->{oppsum}] and $state in combat! ".duration($res->{gain})." is ".
-		"$moved $u\'s clock."));
-	Irpg::Irc::chanmsg("$u reaches next level in ".duration($rps->{$u}{next}).".");
-	foreach (@{$res->{queue}}) { Irpg::Irc::chanmsg(Irpg::Utils::clog($_)); }
-}
-
-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;
-    fisher_yates_shuffle(\@opp);
-	my $mysum = itemsum($opp[0],1) + itemsum($opp[1],1) + itemsum($opp[2],1);
-	my $oppsum = itemsum($opp[3],1) + itemsum($opp[4],1) + itemsum($opp[5],1);
-    my $gain = $rps->{$opp[0]}{next};
-	my ($state, $moved);
-    for my $p (1,2) {
-        $gain = $rps->{$opp[$p]}{next} if $gain > $rps->{$opp[$p]}{next};
-    }
-    $gain = int($gain*.20);
-	my ($myroll, $opproll) = (0, 0);
-	for (my $i = 0; $i <= 6 ; $i++) {
-		if ($i < 3) {
-			$myroll += int(rand(itemsum($opp[$i],1)) * $rps->{$opp[$i]}{class}->atk());
-		}
-		else {
-			$opproll += int(rand(itemsum($opp[$i],1)) * $rps->{$opp[$i]}{class}->def());
-		}
-	}
-    if ($myroll >= $opproll) {
-		($state, $moved) = ('won', 'removed from');
-        $rps->{$opp[0]}{next} -= $gain;
-        $rps->{$opp[1]}{next} -= $gain;
-        $rps->{$opp[2]}{next} -= $gain;
-    }
-    else {
-		($state, $moved) = ('lost', 'added to');
-        $rps->{$opp[0]}{next} += $gain;
-        $rps->{$opp[1]}{next} += $gain;
-        $rps->{$opp[2]}{next} += $gain;
-    }
-	Irpg::Irc::chanmsg(Irpg::Utils::clog(
-		"$opp[0], $opp[1], and $opp[2] ".
-		"[".($myroll/5)."/".($mysum/5)."] have team battled ".
-		"$opp[3], $opp[4], and $opp[5] ".
-		"[".($opproll/5)."/".($oppsum/5)."] and $state! ".
-		duration($gain)." is $moved their clocks."));
-}
-
-sub rpcheck {
-	my $rpreport = shift;
-    if ($rpreport%3600==0 && $rpreport) { # 1 hour
-        my @players = grep { $rps->{$_}{online} &&
-                             $rps->{$_}{level} > 44 } keys(%$rps);
-        # 20% of all players must be level 45+
-        if ((scalar(@players)/scalar(grep { $rps->{$_}{online} } keys(%$rps))) > .15) {
-            challenge_opp($players[int(rand(@players))]);
-        }
-    }
-}
-
-sub do_fight {
-	my ($userhost, $usernick, $username, $source, @arg) = @_;
-	if (!defined($username)) {
-		Irpg::Irc::notice("You are not logged in.", $usernick);
-		return;
-	}
-	if (!$rps->{$username}{fights}) {
-		Irpg::Irc::notice("You feel too tired to handle your weapon.", $usernick);
-		Irpg::Irc::chanmsg("$username tries to grasp ".
-			pronoun(2, $rps->{$username}{gender}).
-			" weapon, but collapses out of exhaustion.");
-		return;
-	}
-    my @opps = grep { $rps->{$_}{online} && $username ne $_ } keys(%$rps);
-	if (!@opps) {
-		Irpg::Irc::notice("Your call echoes in the ambient silence...",$usernick);
-		Irpg::Irc::chanmsg(
-			"$username is desperatly searching for an adversary, ".
-			"but ".pronoun(2, $rps->{$username}{gender})." call remains ".
-			"unheard in the silent desert of ".pronoun(2, $rps->{$username}).
-			" loneliness.");
-		return;
-	}
-	my $opp;
-	if (exists($arg[0]) && !(grep { $arg[0] } @opps)) {
-		if (exists($rps->{$arg[0]})) {
-			Irpg::Irc::notice(
-				"You searched every places you know, but $arg[0] ".
-				"is nowhere to be found...", $usernick);
-		}
-		else {
-			Irpg::Irc::notice(
-				"You asked everyone you met, but no one ever ".
-				"heard of $arg[0].", $usernick);
-		}
-		Irpg::Irc::chanmsg(
-			"$username calls $arg[0] for a fight, but no one can answer ".
-			pronoun(2, $rps->{$username}{gender})." call.");
-		return
-	}
-	elsif (exists($arg[0])) {
-		$opp = $arg[0];
-	}
-	else {
-		$opp = $opps[int(rand(@opps))];
-	}
-	my $res = fight($username, $opp, 1);
-	$rps->{$username}{fights} -= 1;
-	my ($state, $moved);
-	my @ret = ();
-	if ($res->{vict}) {
-		($state, $moved) = ('won', 'removed from');
-	}
-    else {
-		($state, $moved) = ('lost', 'added to');
-    }
-	Irpg::Irc::chanmsg(Irpg::Utils::clog(
-		"$username [$res->{uroll}/$res->{usum}] has provoked $opp [$res->{opproll}/".
-		"$res->{oppsum}] in a fight and $state! ".duration($res->{gain})." is ".
-		"$moved $username\'s clock."));
-	Irpg::Irc::chanmsg(
-		"$username reaches next level in ".
-		duration($rps->{$username}{next}).".");
-	foreach (@{$res->{queue}}) { Irpg::Irc::chanmsg(Irpg::Utils::clog($_)); }
-}
-
-our $commands = {
-	fight => {ref => \&do_fight, adm => 0, prv => 1, pub => 1,
-			  hlp => 'FIGHT [<char name>]: fight someone.'}
-};
-
-1;
diff --git a/Irpg/Main.pm b/Irpg/Main.pm
index c9f87ed..932f225 100644
--- a/Irpg/Main.pm
+++ b/Irpg/Main.pm
@@ -17,7 +17,7 @@ use Exporter qw(import);
 our @EXPORT = qw(&init_hashes &rpcheck &parse &penalize &ha &finduser);
 our @EXPORT_OK = qw($pausemode $silentmode $primnick $lastreg);
 
-foreach (qw(Quest Fight Event Admin Users)) {
+foreach (qw(Quest Action Event Admin Users)) {
 	eval "use Irpg::$_";
 }
 
@@ -52,7 +52,7 @@ sub init_pkg {
 	$primnick = $opts->{botnick}; # for regain or register checks
 	Irpg::Irc::init_pkg(\$silentmode);
 	Irpg::Quest::init_pkg($opts, $rps);
-	Irpg::Fight::init_pkg($opts, $rps, \$primnick);
+	Irpg::Action::init_pkg($opts, $rps, \$primnick);
 	Irpg::Event::init_pkg($opts, $rps);
 	Irpg::Admin::init_pkg($opts, $rps);
 	Irpg::Users::init_pkg($opts, $rps, \%onchan, \$primnick,
@@ -221,7 +221,7 @@ sub moveplayers {
 				}
 				if (rand($onlinecount) < 1) {
 					$positions{$rps->{$player}{x}}{$rps->{$player}{y}}{battled}=1;
-					collision_fight($player,
+					collision_action($player,
 						$positions{$rps->{$player}{x}}{$rps->{$player}{y}}{user});
 				}
 			}
@@ -254,11 +254,11 @@ sub rpcheck { # check levels, update database
 	### ALL MODULES CHEKS ###
     # 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 Fight Event)) {
+	foreach (qw(Quest Action Event)) {
 		eval 'Irpg::'.$_.'::rpcheck($rpreport, $online';
 	}
 #	Irpg::Quest::rpcheck($rpreport, $online); ### QUEST BUSINESS ###
-#	Irpg::Fight::rpcheck($rpreport, $online); ### FIGHT BUSINESS ###
+#	Irpg::Action::rpcheck($rpreport, $online); ### FIGHT BUSINESS ###
 #	Irpg::Event::rpcheck($rpreport, $online); ### EVENT BUSINESS ###
 
 	### TOP PLAYERS REPORT ###
@@ -320,12 +320,15 @@ sub rpcheck { # check levels, update database
                     Irpg::Irc::chanmsg("$k, the $rps->{$k}{title}, has attained level ".
                             "$rps->{$k}{level}! Next level in ".
                             duration($rps->{$k}{next}).".");
+					print "item\n";
                     find_item($k);
+					print "chall\n";
                     challenge_opp($k);
+					print "bite\n";
                 }
 				if ($rps->{$k}{next_f} < 1
-					&& $rps->{$k}{fights} < int($rps->{$k}{level}/10)) {
-					$rps->{$k}{fights}++;
+					&& $rps->{$k}{actions} < int($rps->{$k}{level}/10)) {
+					$rps->{$k}{actions}++;
 					$rps->{$k}{next_f} = 3600; # 1 hour
 					Irpg::Irc::notice("You feel ready for a new fight !", $rps->{$k}{nick});
 				}
@@ -559,11 +562,11 @@ sub help {
 
 
 my ($k, $v);
-foreach (qw(Quest Fight Event Admin Users)) {
+foreach (qw(Quest Action Event Admin Users)) {
 	eval 'while (($k,$v) = each %$Irpg::'.$_.'::commands) {$commands{$k} = $v;}';
 }
 #while (($k,$v) = each %$Irpg::Quest::commands) {$commands{$k} = $v;}
-#while (($k,$v) = each %$Irpg::Fight::commands) {$commands{$k} = $v;}
+#while (($k,$v) = each %$Irpg::Action::commands) {$commands{$k} = $v;}
 #while (($k,$v) = each %$Irpg::Event::commands) {$commands{$k} = $v;}
 #while (($k,$v) = each %$Irpg::Admin::commands) {$commands{$k} = $v;}
 #while (($k,$v) = each %$Irpg::Users::commands) {$commands{$k} = $v;}
diff --git a/Irpg/Quest.pm b/Irpg/Quest.pm
index 1eb3515..46d7fda 100644
--- a/Irpg/Quest.pm
+++ b/Irpg/Quest.pm
@@ -135,6 +135,7 @@ sub questpencheck {
                          "yourselves 15 steps closer to that gaping maw."));
             for $player (grep { $rps->{$_}{online} } keys %$rps) {
                 my $gain = int(15 * ($opts->{rppenstep}**$rps->{$player}{level}));
+				$gain = $rps->{$player}{class}->real_gain($gain);
                 $rps->{$player}{pen_quest} += $gain;
                 $rps->{$player}{next} += $gain;
             }
@@ -182,7 +183,8 @@ sub movequesters {
 				 	 "The peple can rejoice, the realm is sure a safer ".
 				 	 "place now, thanks to these heroes."));
 		for (@{$quest{questers}}) {
-			$rps->{$_}{next} = int($rps->{$_}{next} * .75);
+			$rps->{$_}{next} = int($rps->{$_}{class}->real_gain(
+									$rps->{$_}{next} * .75));
 		}
 		# reset the %quest hash
 		undef(@{$quest{questers}});
@@ -236,7 +238,8 @@ sub rpcheck {
                          "completing their quest! 25% of their burden is ".
                          "eliminated."));
             for (@{$quest{questers}}) {
-                $rps->{$_}{next} = int($rps->{$_}{next} * .75);
+				$rps->{$_}{next} = int($rps->{$_}{class}->real_gain(
+										$rps->{$_}{next} * .75));
             }
             undef(@{$quest{questers}});
             $quest{qtime} = time() + 21600;
diff --git a/Irpg/Users.pm b/Irpg/Users.pm
index c7cdcce..55a0912 100644
--- a/Irpg/Users.pm
+++ b/Irpg/Users.pm
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 use Irpg::Utils qw(:data :text);
 use Irpg::Irc qw(:interaction @queue);
-use Irpg::Fight;
+use Irpg::Action;
 use Irpg::Main;
 
 my $opts;
@@ -98,7 +98,7 @@ sub register {
 			}
 			$rps->{$arg[0]}{class} = Irpg::Classes::Farmer->new($rps->{$arg[0]}{stats});
 			$rps->{$arg[0]}{points} = 1;
-			$rps->{$arg[0]}{fights} = 0;
+			$rps->{$arg[0]}{actions} = 0;
 			$rps->{$arg[0]}{next_f} = 0;
 			$rps->{$arg[0]}{online} = 1;
 			$rps->{$arg[0]}{nick} = $usernick;
@@ -122,7 +122,7 @@ sub register {
 				$rps->{$arg[0]}{$pen} = 0;
 			}
 			Irpg::Irc::chanmsg("Welcome $usernick\'s new player $arg[0], the ".
-					"@arg[6..$#arg]! Next level in ".
+					"@arg[6..$#arg] Farmer! Next level in ".
 					duration($opts->{rpbase}).".");
 			Irpg::Irc::privmsg("Success! Account $arg[0] created. You have ".
 					"$opts->{rpbase} seconds idleness until you ".
@@ -184,8 +184,8 @@ sub login {
 			$rps->{$arg[0]}{userhost} = $userhost;
 			$rps->{$arg[0]}{lastlogin} = time();
 			Irpg::Irc::chanmsg("$arg[0], the level $rps->{$arg[0]}{level} ".
-					"$rps->{$arg[0]}{title}, is now online from ".
-					"nickname $usernick. Next level in ".
+					"$rps->{$arg[0]}{title} $rps->{$arg[0]}{class}->{NAME}, ".
+					"is now online from nickname $usernick. Next level in ".
 					duration($rps->{$arg[0]}{next}).".");
 			Irpg::Irc::notice("Logon successful. Next level in ".
 				   duration($rps->{$arg[0]}{next}).".", $usernick);
@@ -208,6 +208,7 @@ sub status {
 	return unless ($opts->{statuscmd});
 	my ($userhost, $usernick, $username, $source, @arg) = @_;
 	my $asked = exists($arg[0]) ? $arg[0] : $username;
+	return unless ($asked);
 	my $asked_found = Irpg::Main::finduser($asked) unless (exists($rps->{$asked}));
 	$asked = $asked_found if ($asked_found);
 	$asked = Irpg::Main::finduser($asked, 1) unless ($asked_found);
@@ -234,10 +235,10 @@ sub whoami {
 	}
 	else {
 		Irpg::Irc::privmsg("You are $username, the level ".
-				$rps->{$username}{level}." $rps->{$username}{title}. ".
+				"$rps->{$username}{level} $rps->{$username}{title} ".
 				"$rps->{$username}{class}->{NAME}. You have ".
-				"$rps->{username}{points} point(s) available, and ".
-				"$rps->{username}{fights} fight(s) you can lead. ".
+				"$rps->{$username}{points} point(s) available, and ".
+				"$rps->{$username}{actions} action(s) you can trigger. ".
 				"Next level in ".duration($rps->{$username}{next}),
 				$source);
 	}
diff --git a/Irpg/Utils.pm b/Irpg/Utils.pm
index 96ecb0c..500c718 100644
--- a/Irpg/Utils.pm
+++ b/Irpg/Utils.pm
@@ -293,7 +293,7 @@ sub loaddb { # load the players database
         $rps{$i[0]}{stats}{dex},
 		$classname,
 		$rps{$i[0]}{points},
-		$rps{$i[0]}{fights},
+		$rps{$i[0]}{actions},
 		$rps{$i[0]}{next_f},
         $rps{$i[0]}{alignment},
 		$rps{$i[0]}{gender}) = (@i[1..7],($sock?$i[8]:0),@i[9..$#i]);
@@ -357,7 +357,7 @@ sub writedb {
 						"dex",
 						"class",
 						"points",
-						"fights",
+						"actions",
 						"next_f",
                         "alignment",
 						"gender")."\n";
@@ -405,7 +405,7 @@ sub writedb {
                                 $rps->{$k}{stats}{dex},
 								$rps->{$k}{class}->{NAME},
 								$rps->{$k}{points},
-								$rps->{$k}{fights},
+								$rps->{$k}{actions},
 								$rps->{$k}{next_f},
                                 $rps->{$k}{alignment},
                                 $rps->{$k}{gender})."\n";
@@ -453,7 +453,7 @@ sub createdb {
 	}
     $rps{$uname}{class} = Irpg::Classes::Farmer->new($rps{$uname}{stats});
 	$rps{$uname}{points} = 1;
-	$rps{$uname}{fights} = 0;
+	$rps{$uname}{actions} = 0;
 	$rps{$uname}{next_f} = 0;
     $rps{$uname}{online} = 0;
     $rps{$uname}{idled} = 0;
-- 
GitLab