#! /usr/bin/perl -w
use strict;
use diagnostics;

#####################
## This script will test outputting and
##  running combat for the colloseum.
##
## Beau Holder - Dec. 4, 2004
#####################


################# COMBATANT INFORMATION ######################
### These Stats are the information I need to run a combat ###
### between two combatants. The format of this won't 	   ###
### change, but instead of static number, I will use       ###
### Class::DBI to pull this information from the database. ###
##############################################################
my %comb_a = (	attack		=> 14,
		defense		=> 14,
		accuracy	=> 25,
		evasion		=> 9,
		hit_points	=> 159,
		min_damage	=> 3,
		max_damage	=> 16,
		crit_chance	=> 1714,
		exp_bonus	=> 8,	#divide by 100 to get % bonus
		experience	=> 0,
		level		=> 10,
		 );

my %comb_b = (  attack          => 17,
                defense         => 4,
                accuracy        => 17,            
                evasion         => 14,
                hit_points      => 192,
                min_damage      => 4,
                max_damage      => 19,
                crit_chance     => 1428,
                exp_bonus       => 8,  #divide by 100 to get % bonus
                experience      => 0,
                level           => 10,
                 );
my $BASE_EXP_AWARD = 250;
my $round = 1;
my $owalika = 0; # "Owali ka" means "is it over" in japanese
my ($a_tohit, $a_todefend, $b_tohit, $b_todefend, $a_next, $b_next, $ave_level, $exp_award, $a_damage, $b_damage, $a_hp, $b_hp, $a_exp_mod, $b_exp_mod);

########################
### MAIN BLOCK CALLS ###
########################
init();
while (not $owalika) {
 fight();
 
 $owalika = endFight();
}
awardExp();
checkForLevelUp();
#updateDatabase();

##############################
### SUBROUTINE DEFINITIONS ###
##############################

#############
## init(); ##
#####################################
## usage : init();                 ##
## serves to initialize a bunch of ##
## global variables that can be    ##
## used by any other subroutine.   ##
#####################################
sub init {
 $a_tohit	= $comb_a{attack}  + $comb_a{accuracy};
 $a_todefend	= $comb_a{defense} + $comb_a{evasion};
 $b_tohit	= $comb_b{attack}  + $comb_b{accuracy};
 $b_todefend	= $comb_b{defense} + $comb_b{evasion};
 $a_next = ($comb_a{level}*$comb_a{level}) * 1000;
 $b_next = ($comb_b{level}*$comb_b{level}) * 1000;
 $a_exp_mod = 1 + ($comb_a{exp_bonus}/100);
 $b_exp_mod = 1 + ($comb_b{exp_bonus}/100);
 $ave_level = int(($comb_a{level} + $comb_b{level}) / 2);
 $exp_award = $BASE_EXP_AWARD * $ave_level;
 $a_hp = $comb_a{hit_points};
 $b_hp = $comb_b{hit_points};
}

################
## rolld20(); ##
## rolld10(); ##
#########################################
## usage : rolld20(); or rolld10();    ##
## generates a random number between   ##
## 1 and 20 for rolld20 or a random    ##
## number between 1 and 10 for rolld10 ##
#########################################
sub rolld20 { return (int(rand(20))+1); }
sub rolld10 { return (int(rand(10))+1); }

#################
## endFight(); ##
########################################
## usage : $foo = endFight();         ##
## checks each fighters' hit points   ##
## and if either or both of them have ##
## zero of less hit points, the fight ##
## is over (return 1;), otherwise the ##
## fight continues (return 0;)        ##
########################################
sub endFight {
 if ($round >= 50) { $a_hp = 0; $b_hp = 0; }
 if ($a_hp <= 0 || $b_hp <= 0) { return 1; }
 if ($a_hp <= 0 && $b_hp <= 0) { return 1; }
 else { return 0; }
}

#####################
## outputStatus(); ##
##############################################
## usage : outputStatus();                  ##
## simply prints the current round and      ##
## each fighter's name, current hit points, ##
## and maximum hit points.                  ##
##############################################
sub outputStatus {
 sleep 1;
 print "\nRound $round ...\n\n";
 sleep 1;
 print "Greg Hart    :  $a_hp  /  $comb_a{hit_points}\n";
 print "Marcus Lorai :  $b_hp  /  $comb_b{hit_points}\n";
 sleep 1;
}

##################
## outputHit(); ##
######################################################################
## usage : outputHit(attack, defense, 'a' or 'b', damage, 0 or 1);  ##
## prints out the attack for each fighter on successful hits. Takes ##
## in a number for the attack roll, the defense roll, and take 'a'  ##
## or 'b' to represent which fighter is making the attack, and it   ##
## takes a number for damage dealt, and it takes either 0 or 1 for  ##
## hit was not/was a critical attack.                               ##
######################################################################
sub outputHit {
 my @inputs = @_;
 my $atk_roll = $inputs[0];
 my $def_roll = $inputs[1];
 my $atk_name = $inputs[2];
 my $def_name;
 my $dmg_roll = $inputs[3];
 my $crit_hit = $inputs[4];

 if ($atk_name eq 'a') {
  $atk_name = 'Greg Hart';
  $def_name = 'Marcus Lorai';
 } else {
  $atk_name = 'Marcus Lorai';
  $def_name = 'Greg Hart';
 }
 print "\n$atk_name\'s attack roll : $atk_roll\n";
 print "$def_name\'s defense roll: $def_roll\n";
 sleep 1;
 if ($crit_hit) {
 print "$atk_name scores a critical hit, dealing $dmg_roll points of damage!\n";
 } else {
 print "$atk_name\'s attack hits and deals $dmg_roll points of damage!\n";
 }
}

###################
## outputMiss(); ##
############################################################
## usage : outputMiss(attack, defense, 'a' or 'b');       ##
## simply prints out the attack for each fighter if the   ##
## attack missed. Takes a number for attack and defense   ##
## rolls and either 'a' or 'b' to represent which fighter ##
## made the attack that missed.                           ##
############################################################
sub outputMiss {
 my @inputs = @_;
 my $atk_roll = $inputs[0];
 my $def_roll = $inputs[1];
 my $atk_name = $inputs[2];
 my $def_name;
 if ($atk_name eq 'a') {
  $atk_name = 'Greg Hart';
  $def_name = 'Marcus Lorai';
 } else {
  $atk_name = 'Marcus Lorai';
  $def_name = 'Greg Hart';
 }
 print "\n$atk_name\'s attack roll : $atk_roll\n";
 print "$def_name\'s defense roll: $def_roll\n";
 sleep 1;
 print "$atk_name\'s attack missed!\n";
}

##############
## fight(); ##
######################################################
## usage : fight();				    ##
## this subroutine relies on global variables for   ##
## its input. it generates an attack and defense    ##
## roll for each fighter; compares a's attack to    ##
## b's defense and visa-versa; rolls damage for a   ##
## successful hit then doubles damage on a critical ##
## attack; subtracts damage from defender's current ##
## hit points; then increments's $rounds and exits. ##
######################################################
sub fight {
 my ($a_attack, $a_defend, $a_damage, $a_crit);
 my ($b_attack, $b_defend, $b_damage, $b_crit);

 outputStatus();

 $a_attack = rolld20() + $a_tohit;	#print "\n\n========a_attack = $a_attack=========\n";
 $b_defend = rolld20() + $b_todefend;	#print     "========b_defend = $b_defend=========\n";
 $b_attack = rolld20() + $b_tohit;	#print     "========b_attack = $b_attack=========\n";
 $a_defend = rolld20() + $a_todefend;	#print     "========a_defend = $a_defend=========\n\n";

 if ($a_attack > $b_defend) {
  if ($comb_a{max_damage} == 1) { $a_damage = 1; }
  else {
   $a_damage = int(rand($comb_a{max_damage})) + $comb_a{min_damage};
   if (isCrit('a')) { $a_damage = $a_damage*2; $a_crit = 1; } else { $a_crit = 0; }
  }
  $b_hp -= $a_damage;
  outputHit($a_attack, $b_defend, 'a', $a_damage, $a_crit);
 } else { outputMiss($a_attack, $b_defend, 'a'); }

 sleep 1;

 if ($b_attack > $a_defend) {
  if ($comb_b{max_damage} == 1) { $b_damage = 1; }
  else {
   $b_damage = int(rand($comb_b{max_damage})) + $comb_b{min_damage};
   if (isCrit('b')) { $b_damage = $b_damage*2; $b_crit = 1; } else { $b_crit = 0; }
  }
  $a_hp -= $b_damage;
  outputHit($b_attack, $a_defend, 'b', $b_damage, $b_crit);
 } else { outputMiss($b_attack, $a_defend, 'b'); }
 $round++;
}

###############
## isCrit(); ##
############################################################
## usage : if (isCrit('a' or 'b')) { ... }		  ##
## returns 1 or 0 (true/false) depending on the random    ##
## roll to determine a critical attack. the number in     ##
## the database is a number between 1 and 10000, so it    ##
## gets a random number between 1 and 10000, and if that  ##
## number is less than the fighter's crit_chance, then    ##
## the attack is a critical attack and will do x2 damage. ##
############################################################ 
sub isCrit {
 my $attacker = shift;
 my $roll = int(rand(10000))+1;
 if ($attacker eq 'a') {
  if ($roll < $comb_a{crit_chance}) { return 1; }
  else { return 0; }
 }
 if ($attacker eq 'b') {
  if ($roll < $comb_b{crit_chance}) { return 1; }
  else { return 0; }
 }
}

#################
## awardExp(); ##
##################################################
## usage : awardExp();				##
## figures out what ended the fight and calls   ##
## the correct subroutine to award experience.  ##
## expDraw on a draw, expAwins if fighter A won ##
## and expBwins if fighter B won.               ##
##################################################
sub awardExp {
 if ($a_hp <= 0 && $b_hp <= 0) { expDraw(); }
 if ($a_hp <= 0 && $b_hp > 0)  { expBwins(); }
 if ($a_hp > 0 && $b_hp <= 0)  { expAwins(); }
}

#################
## expAwins(); ##
#########################################################
## usage : expAwins();				       ##
## calculates and awards the designated amount of      ##
## experience to each fighter. the winner (A) gets     ##
## full experience, modified by his experience bonus   ##
## and the loser (B) gets 1/3 full experience modified ##
## by his experience bonus. then prints how much was   ##
## awarded to each fighter.			       ##
#########################################################
sub expAwins {
 $comb_a{experience} += ($exp_award * $a_exp_mod);
 $comb_b{experience} += ($exp_award * $b_exp_mod)/3; 
 print "\n\nCombatant A wins! A is awarded " . ($exp_award*$a_exp_mod) . " experience points!\n";
 print "Combatant B is awarded " . ($exp_award*$b_exp_mod)/3 . " experience points!\n";
}

#################
## expBwins(); ##
#########################################################
## usage : expBwins();                                 ##
## calculates and awards the designated amount of      ##
## experience to each fighter. the winner (B) gets     ##
## full experience, modified by his experience bonus   ##
## and the loser (A) gets 1/3 full experience modified ##
## by his experience bonus. then prints how much was   ##
## awarded to each fighter.                            ##
#########################################################
sub expBwins {
 $comb_b{experience} += ($exp_award * $b_exp_mod);
 $comb_a{experience} += ($exp_award * $a_exp_mod)/3;
 print "\n\nCombatant B wins!\nCombatant B is awarded " . ($exp_award*$b_exp_mod) . " experience points!\n";
 print "Combatant A is awarded " . ($exp_award*$a_exp_mod)/3 . " experience points!\n";
}

################
## expDraw(); ##
##############################################################
## usage : expDraw();					    ##
## calculates and awards the designated experience to each  ##
## fighter. each fighter get's half the full experience     ##
## modified by their individual experience bonuses. it then ##
## prints out how much was awarded to each fighter.         ##
##############################################################
sub expDraw {
 $comb_b{experience} += ($exp_award * $b_exp_mod)/2;
 $comb_a{experience} += ($exp_award * $a_exp_mod)/2;
 print "\n\nThe fight is a draw!\nCombatant A is awarded " . ($exp_award*$a_exp_mod)/2 . " experience points!\n";
 print "Combatant B is awarded " . ($exp_award*$b_exp_mod)/2 . " experience points!\n";
}

########################
## checkForLevelUp(); ##
##################################################################
## usage : checkForLevelUp();					##
## simple and clean. if a fighter's experience rises over       ##
## (his current level squared) x 1000, then he gains a level.   ##
## this increments the fighter's level then calls calcNewStats  ##
## which will improve the fighters's stats (which increase with ##
## each level gained). If the fighter doesn't have enough expe- ##
## rience to gain a level, nothing happens.			##
##################################################################
sub checkForLevelUp {
 if ($comb_a{experience} >= $a_next) { $comb_a{level}++; calcNewStatsA(); }
 if ($comb_b{experience} >= $b_next) { $comb_b{level}++; calcNewStatsB(); }
}

######################
## calcNewStatsA(); ##
############################################################################
## usage : calcNewStatsA();						  ##
## this raises each of a fighter's stats according to their level and     ##
## stats. All increases are saved in the global hash which will be        ##
## used to update the database (when/if) the server starts working again. ##
## for now, it just does all the things that calc_combat.pl does, only    ##
## I'm containing it all in a subroutine.                                 ##
############################################################################
sub calcNewStatsA {
 ## will get stats from database in future... but for now, I will assign them.
 my %stats = (	power		=> 4,
		quickness	=> 2,
		toughness	=> 4,
		intellect	=> 6,
		will		=> 4,
		presense	=> 1,
		);
 my $BASE_HP             = 9;
 my $BASE_LEVEL          = $comb_a{level};
 my ($hit_points, $attack, $defense, $accuracy,
        $evasion, $min_damage, $max_damage, $crit, $exp_bonus);
 my $messy = (((($BASE_LEVEL*$BASE_LEVEL) / (sqrt($BASE_LEVEL)) / $BASE_LEVEL) + $BASE_LEVEL));
 $attack	 = int($stats{power} * $messy);
 $defense	 = int($stats{toughness} * $messy);
 $accuracy	 = int($stats{intellect} * $messy);
 $evasion	 = int($stats{quickness} * $messy);
 $hit_points  	 = int($stats{will} * (((($BASE_LEVEL*$BASE_LEVEL) / (sqrt($BASE_LEVEL)) / $BASE_LEVEL) + ($BASE_HP + $BASE_LEVEL))));
 $min_damage 	 = int(($stats{power} / 15) * $messy);
 $max_damage 	 = int(($stats{power} / 4) * $messy);
 if ($min_damage == 0) { $min_damage++; }
 if ($max_damage == 0) { $max_damage++; }
 $crit = ($stats{intellect} / 35);
 $exp_bonus	 = int(($stats{presense} * $messy) / 25);
 if ($exp_bonus > 3.99) { $exp_bonus = 4; }

}

######################
## calcNewStatsB(); ##
############################################################################
## usage : calcNewStatsB();                                               ##
## this raises each of a fighter's stats according to their level and     ##
## stats. All increases are saved in the global hash which will be        ##
## used to update the database (when/if) the server starts working again. ##
## for now, it just does all the things that calc_combat.pl does, only    ##
## I'm containing it all in a subroutine.                                 ##
############################################################################
sub calcNewStatsB {
 ## will get stats from database in future... but for now, I will assign them.
 my %stats = (  power           => 5,
                quickness       => 4,
                toughness       => 1,
                intellect       => 5,
                will            => 5,
                presense        => 1,
                );
 my $BASE_HP             = 9;
 my $BASE_LEVEL          = $comb_a{level};
 my ($hit_points, $attack, $defense, $accuracy,
        $evasion, $min_damage, $max_damage, $crit, $exp_bonus);
 my $messy = (((($BASE_LEVEL*$BASE_LEVEL) / (sqrt($BASE_LEVEL)) / $BASE_LEVEL) + $BASE_LEVEL));
 $attack         = int($stats{power} * $messy);
 $defense        = int($stats{toughness} * $messy);
 $accuracy       = int($stats{intellect} * $messy);
 $evasion        = int($stats{quickness} * $messy);
 $hit_points     = int($stats{will} * (((($BASE_LEVEL*$BASE_LEVEL) / (sqrt($BASE_LEVEL)) / $BASE_LEVEL) + ($BASE_HP + $BASE_LEVEL))));
 $min_damage     = int(($stats{power} / 15) * $messy);
 $max_damage     = int(($stats{power} / 4) * $messy);
 if ($min_damage == 0) { $min_damage++; }
 if ($max_damage == 0) { $max_damage++; }
 $crit = ($stats{intellect} / 35);
 $exp_bonus      = int(($stats{presense} * $messy) / 25);
 if ($exp_bonus > 3.99) { $exp_bonus = 4; }

}

####################################################
################## COMMENT BLOCK ###################
####################################################
## HOW THE COMBAT WILL WORK AND RUN!
##
## Attack vs. Defense - Representation of forcefulness vs. armor
## Accuracy vs. Evasion - Representation of aim vs. ability to get out of the way
##
## First, what happens in one round?
##	- Combatant A & B both make an attack
##	- Each attack is resolved simultaniously
##	- Damage from successful a successful attack is determined
##	- Combatant A & B's hit points are reduced accordingly
##	- Check combatants' hit points:
##		- if A's HP <= 0, B wins... end fight
##		- if B's HP <= 0, A wins... end fight
##		- if A & B's HP <= 0, fight is a draw
##	- If fight hasn't ended, process repeats until there's a winner
##
## Making/Resolving attacks:
##	A's accuracy is added to a random number(1-20) and compared to
##	B's evasion added to a random number(1-20). If A wins, he rolls
##	an attack (1-10)+attack vs. b's defense (1-10)+defense. If that
##	suceeds, roll damage and check for critical.
##	This process repeats for B attacking A (just switch all instances of
##	A and B from above).
##
## Checking for a critical attack:
##	Get random number(1-10000), if that number is lower than comb_a/b{crit_chance}
##	then damage roll is doubled.
##
## Once the fight has ended:
##	If there is a victor, he is awarded ((BASE_EXP_AWARD x ave_level) x 1.exp_bous)
##	and the loser is awarded ((BASE_EXP_AWARD x ave_level) x 1.exp_bonus)/3.
##	If the fight is a draw, both combatants are awarded ((BASE_EXP_AWARD x ave_level) x 1.exp_bonus)/2
##	
##
##
##
##
##
















