...

View Full Version : Battle System

Zangeel
04-19-2012, 02:24 AM
So I'm developing an interesting little game and I'm just puzzled as how to go about the next step of userA battling userB

My initial idea was to use a loop

while( <userA or userB has health > )

then let them hit eachother until one has 0 hp. But the problem I'm having is hits are based on three stats, ATTACK POWER (how hard a hit will be), DEFENSE power (ability to withstand attack) and SPEED (who hits first and how many times)

im specifically having trouble factoring the last part. Lets give some fake stats

UserA has... 1000 ATK, 5000 DEF, 10000 SPD with 1000HP
UserB has... 1000 ATK, 5000 DEF, 1000 SPD with 1000HP

Since userB has significantly less speed, uesrA would hit him 10 times before UserB even has a shot at UserA, so you can understand the model im trying to use for speed, x2 speed = 2 hits, x3 speed has 3 hits and so on.

But how can I get my php code to follow this?

shoookran

Zangeel
04-23-2012, 12:08 PM
Bump?

Maybe i can put it simpler. I neeed to tell my code to do A x amount of times and B x amount of times REPEATED until a variable hits 0.

Rowsdower!
04-23-2012, 01:42 PM
I'm envisioning something like this:

\$user_a=array(
'ATK' => 1000,
'DEF' => 5000,
'SPD' => 10000,
'HP' => 1000
);
\$user_b=array(
'ATK' => 1000,
'DEF' => 5000,
'SPD' => 1000,
'HP' => 1000
);

function resolve_battle(\$user_a,\$user_b){
\$in_progress=true;
\$time_increment_reached=0;
\$user_a_rof=1000/round(\$user_a['SPD']/1000);
\$user_b_rof=1000/round(\$user_b['SPD']/1000);
print "<h1>User A RoF: \$user_a_rof</h1>\n<h1>User B RoF: \$user_b_rof</h1>\n";
while(\$in_progress==true){
//as long as the battle is in progress, we loop through the process
\$time_increment_reached++;
if(\$time_increment_reached%\$user_a_rof==0){
//if \$user_a gets to make an attack in this milisecond, we process the attack...
#whatever attack calculations you have go here...
print "<p>User A attacks User B at \$time_increment_reached, User B HP goes from ".\$user_b['HP'];
\$user_b['HP']=round(\$user_b['HP']-\$user_a['ATK']/(\$user_b['DEF']/20),2);
print " to ".\$user_b['HP']."<p>\n";
if(\$user_b['HP']<=0){
//if this attack has brought the HP for \$user_b to zero (or less) then the battle will have ended after this round...
\$in_progress=false;
print "<p style=\"font-weight:bold;color:#c00;\">User B has fallen in this round...</p>\n";
}
}
if(\$time_increment_reached%\$user_b_rof==0){
//if \$user_b gets to make an attack in this milisecond, we process the attack...
#whatever attack calculations you have go here...
print "<p style=\"color:#579;font-weight:bold;\">User B attacks User A at \$time_increment_reached, User A HP goes from ".\$user_a['HP'];
\$user_a['HP']=round(\$user_a['HP']-\$user_b['ATK']/(\$user_a['DEF']/20),2);
print " to ".\$user_a['HP']."<p>\n";
if(\$user_a['HP']<=0){
//if this attack has brought the HP for \$user_a to zero (or less) then the battle will have ended after this round...
\$in_progress=false;
print "<p style=\"font-weight:bold;color:#c00;\">User A has fallen in this round...</p>\n";
}
}
}
//return the users as they are after the battle has ended...
return array(\$user_a,\$user_b);
}

\$result=resolve_battle(\$user_a,\$user_b);

print "<pre>";print_r(\$result);print "</pre>";

Apart from the actual battle damage calculations, is that about what you're looking for? Attack and defense stats could be degraded as HP declines and the beginning status of each combatant at the start of each loop could be cached for the current round so that damage could be dealt simultaneously so that the first fighter in the code won't win a match be that merit alone.

Zangeel
04-23-2012, 08:00 PM
Thanks! I really appreciate all of that, it gave me some great ideas.

it's exactly what I was looking for :)

Zangeel
05-21-2012, 09:06 PM
Just a friendly BUMP

I'm going to show my poor math skills now.. the integers used in the above code for the userspeed are fine, but what if just as an example we have one user with the speed of 50, and another with 50000000, it doesn't quite work then im guessing since the number the user speed is divided by is staticly at 1000

\$user_a_rof=1000/round(\$user_a['SPD']/1000);

how can we adjust this just so it doesn't matter what the speed of the two user is, whether 1 or 1 trillion?

Rowsdower!
05-23-2012, 02:18 PM
My earlier suggestion was something that essentially divided the battle into milliseconds/milliminutes/milli-whatever and calculated the combat occurring in each milli-period (since the example speeds you gave were in units of 1,000). This would have limitations (as you are finding with large numbers). In order to support a wider range of numbers you could simply increase the base 1000 up to a million, trillion or whatever you like. But that would still put an arbitrary cap on the speed ratings and larger caps will still mean a lot of extra iterations through the loop.

So you could do the first approach below, which is extremely inefficient and will take a looooooong time to run with big numbers. but which will present no arbitrary cap at all (allowing any speed rating you wish):

<?php

\$user_a=array(
'ATK' => 1000,
'DEF' => 5000,
'SPD' => 10000,
'HP' => 1000
);
\$user_b=array(
'ATK' => 1000,
'DEF' => 5000,
'SPD' => 1000,
'HP' => 1000
);

function resolve_battle(\$user_a,\$user_b){
\$in_progress=true;
\$time_increment_reached=0;
\$user_a_rof=\$user_b['SPD'];
\$user_b_rof=\$user_a['SPD'];
print "<h1>User A RoF: \$user_a_rof</h1>\n<h1>User B RoF: \$user_b_rof</h1>\n";
while(\$in_progress==true){
//as long as the battle is in progress, we loop through the process
\$time_increment_reached++;
if(\$time_increment_reached%\$user_a_rof==0){
//if \$user_a gets to make an attack in this milisecond, we process the attack...
#whatever attack calculations you have go here...
print "<p>User A attacks User B at \$time_increment_reached, User B HP goes from ".\$user_b['HP'];
\$user_b['HP']=round(\$user_b['HP']-\$user_a['ATK']/(\$user_b['DEF']/20),2);
print " to ".\$user_b['HP']."<p>\n";
if(\$user_b['HP']<=0){
//if this attack has brought the HP for \$user_b to zero (or less) then the battle will have ended after this round...
\$in_progress=false;
print "<p style=\"font-weight:bold;color:#c00;\">User B has fallen in this round...</p>\n";
}
}
if(\$time_increment_reached%\$user_b_rof==0){
//if \$user_b gets to make an attack in this milisecond, we process the attack...
#whatever attack calculations you have go here...
print "<p style=\"color:#579;font-weight:bold;\">User B attacks User A at \$time_increment_reached, User A HP goes from ".\$user_a['HP'];
\$user_a['HP']=round(\$user_a['HP']-\$user_b['ATK']/(\$user_a['DEF']/20),2);
print " to ".\$user_a['HP']."<p>\n";
if(\$user_a['HP']<=0){
//if this attack has brought the HP for \$user_a to zero (or less) then the battle will have ended after this round...
\$in_progress=false;
print "<p style=\"font-weight:bold;color:#c00;\">User A has fallen in this round...</p>\n";
}
}
}
//return the users as they are after the battle has ended...
return array(\$user_a,\$user_b);
}

\$result=resolve_battle(\$user_a,\$user_b);

print "<pre>";print_r(\$result);print "</pre>";

?>

That's maximum accuracy for timing. But like I said, it's also extremely terrible performance.

Now, I'm no math wiz myself, but another alternative I have come up with is that you could set things up more modestly with a speed ratio (using speed "b" / speed "a" for user "a" and speed "a" / speed "b" for user "b") and round these to the nearest "x" level of accuracy (whole unit, 10th, 100th, 1000th place, however deep you care to go for the sake of accuracy), then simply run the battle calcuation with the relative attack frequencies. That might look like this:

<?php
\$user_a=array(
'ATK' => 1000,
'DEF' => 5000,
'SPD' => 49587,
'HP' => 1000
);
\$user_b=array(
'ATK' => 1000,
'DEF' => 5000,
'SPD' => 2384,
'HP' => 1000
);

function resolve_battle(\$user_a,\$user_b){
\$depth=3; //change this to set the decimal point depth of the comparison for RoF
\$in_progress=true;
\$time_increment_reached=0;
\$user_a_rof=round(\$user_b['SPD']/\$user_a['SPD'],5);
\$user_b_rof=round(\$user_a['SPD']/\$user_b['SPD'],5);
\$user_a_attacks=0;
\$user_b_attacks=0;
\$dominant_user = (max(\$user_a_rof,\$user_b_rof) == \$user_a_rof) ? "b" : "a";
if(\$dominant_user == "a"){
//"round" to desired decimal place (really just multiply by 10^x where x is the desired decimal place) and then round the result to an integer...
//the "dominant" user will always have a RoF of 1x10^x, since theirs is the base by which all speed activities are calculated
\$user_a_rof=1*pow(10,\$depth);
\$user_b_rof=round(\$user_b_rof*pow(10,\$depth));
}
else{
\$user_b_rof=1*pow(10,\$depth);
\$user_a_rof=round(\$user_a_rof*pow(10,\$depth));
}
//see if we can reduce the fraction (reduce uneventful combat cycles and speed up the process) so we'll try all of the prime numbers between 1 and 20 (arbitrarily...feel free to add more prime numbers to the list and create a helper function to run it all) to try to slim things down a bit...
while( fmod(\$user_a_rof,2)==0 && fmod(\$user_b_rof,2)==0 ){
\$user_a_rof/=2;
\$user_b_rof/=2;
}
while( fmod(\$user_a_rof,3)==0 && fmod(\$user_b_rof,3)==0 ){
\$user_a_rof/=3;
\$user_b_rof/=3;
}
while( fmod(\$user_a_rof,5)==0 && fmod(\$user_b_rof,5)==0 ){
\$user_a_rof/=5;
\$user_b_rof/=5;
}
while( fmod(\$user_a_rof,7)==0 && fmod(\$user_b_rof,7)==0 ){
\$user_a_rof/=7;
\$user_b_rof/=7;
}
while( fmod(\$user_a_rof,11)==0 && fmod(\$user_b_rof,11)==0 ){
\$user_a_rof/=11;
\$user_b_rof/=11;
}
while( fmod(\$user_a_rof,13)==0 && fmod(\$user_b_rof,13)==0 ){
\$user_a_rof/=13;
\$user_b_rof/=13;
}
while( fmod(\$user_a_rof,17)==0 && fmod(\$user_b_rof,17)==0 ){
\$user_a_rof/=17;
\$user_b_rof/=17;
}
while( fmod(\$user_a_rof,19)==0 && fmod(\$user_b_rof,19)==0 ){
\$user_a_rof/=19;
\$user_b_rof/=19;
}
//now we should be reasonably reduced, so we go ahead with the battle calculations...
print "<style>*{padding:0;margin:0;}</style><h1 style=\"font-size:24px;\">User A attack frequency (in time units): \$user_a_rof</h1>\n<h1 style=\"font-size:24px;\">User B attack frequency (in time units): \$user_b_rof</h1>\n<div style=\"width:600px;margin-left:40px;\">\n";
while(\$in_progress==true){
//as long as the battle is in progress, we loop through the process
\$time_increment_reached++;
if(\$time_increment_reached%\$user_a_rof==0){
//if \$user_a gets to make an attack in this round, we process the attack...
#whatever attack calculations you have go here...
\$user_a_attacks++;
print "<p style=\"color:#fff;font-weight:bold;background-color:#555;margin:0;padding:2px 5px;\">[\$user_a_attacks] User A attacks User B at \$time_increment_reached, User B HP goes from ".\$user_b['HP'];
\$user_b['HP']=round(\$user_b['HP']-\$user_a['ATK']/(\$user_b['DEF']/20),2);
print " to ".\$user_b['HP']."</p>\n";
if(\$user_b['HP']<=0){
//if this attack has brought the HP for \$user_b to zero (or less) then the battle will have ended after this round...
\$in_progress=false;
print "<p style=\"font-weight:bold;background-color:#c00;color:#fff;padding:2px 5px;text-align:center;\">User B has fallen in this round...</p>\n";
}
}
if(\$time_increment_reached%\$user_b_rof==0){
//if \$user_b gets to make an attack in this round, we process the attack...
#whatever attack calculations you have go here...
\$user_b_attacks++;
print "<p style=\"color:#444;font-weight:bold;background-color:#ffc;margin:0;padding:2px 5px;\">[\$user_b_attacks] User B attacks User A at \$time_increment_reached, User A HP goes from ".\$user_a['HP'];
\$user_a['HP']=round(\$user_a['HP']-\$user_b['ATK']/(\$user_a['DEF']/20),2);
print " to ".\$user_a['HP']."</p>\n";
if(\$user_a['HP']<=0){
//if this attack has brought the HP for \$user_a to zero (or less) then the battle will have ended after this round...
\$in_progress=false;
print "<p style=\"font-weight:bold;background-color:#c00;color:#fff;padding:2px 5px;text-align:center;\">User A has fallen in this round...</p>\n";
}
}
}
//return the users as they are after the battle has ended...
return array(\$user_a,\$user_b);
}

\$result=resolve_battle(\$user_a,\$user_b);

print "</div>\n<pre style=\"background-color:#fff;position:fixed;right:20px;top:20px;height:330px;width:260px;padding:20px;border:1px solid #000;\"><h1 style=\"margin:0px 0px 20px 0px;font-size:20px;font-weight:bold;font-family:'verdana';\">Final:</h1>";print_r(\$result);print "</pre>";
?>

You sacrifice a bit of accuracy in the timing (because you are rounding, after all), but you can control how much of a sacrifice you make and still keep a reasonable handle on performance.

Like I said, I'm no mathematician so in all likelihood there is a better/cleaner way of doing this (as in "using a formula" rather than actually running iterations). But this is what my muddled thoughts could pull together on short notice. :D