/php/battle.php
PHP | 1407 lines | 1043 code | 188 blank | 176 comment | 335 complexity | 4df5b6593ec0955b02d2ca0c6cf574d0 MD5 | raw file
Possible License(s): AGPL-3.0, LGPL-2.1
- <?php
- /* battle.php - Back-end code implementing a battle
- *
- * Copyright (C) 2006, 2007, 2008 Kevin Read, Simone Schaefer
- *
- * This file is part of Selador, a browser-based fantasy strategy game
- *
- * This program is distributed under the terms of the GNU Affero General Public License.
- *
- *
- * Selador is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * any later version.
- *
- * Selador is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with Selador. If not, see <http://www.gnu.org/licenses/>.
- **/
- // Works well but is really simple.
- require_once ("functions.inc.php");
- define ("GROUP", 50);
- define ("UNITID", 51);
- define ("TYPE", 52);
- define ("SIDE", 53);
- define ("AMOUNT", 54);
- define ("NAME", 55); // FIXME: Might want to take this out later on...
- define ("HP", 60);
- define ("ATT", 61);
- define ("DAMAGE", 62);
- define ("INI", 63);
- define ("FLAGS", 70);
- define ("UID", 71);
- define ("DEBUG", 1);
- define ("SQUISH_NUM", 10);
- define ("FOOT_TARGETS", 2);
- define ("BOW_TARGETS", 3);
- define ("GFOOT", 1);
- define ("GHORSE", 2);
- define ("GBOW", 3);
- define ("GCOMMANDO", 4);
- define ("GSIEGE", 5);
- define ("GCHIVALRY", 6);
- define ("GWALL", 7); // Not a wall-hack
- define ("UNITS_PER_SIEGE_TOWER", 10);
- define ("HEALER", 256);
- define ("SIEGE", 4);
- $battle_flags = 0;
- $bonus = array ();
- /* There is the global groups array, containing all information about the assembled units
- except the combat sequence (who strikes when), which needs to be extracted from
- modified initiative, and the number of remaining troops, just called troops.
- The troops are stored in one big array indexed with the round number and the group id.
- When multiple units strike simultaneously, the troop changes are stored in a temp array and
- only applied when the simultaneous attacks are done. Tricky :) */
- // Pass the attacking group, and the type-array (referencing group ids) of candidates
- // Throws the dice, calculates damage, and distributes among the candidates
- // The same but for three target groups
- function do_attack_three ($lineno, &$owncount, $attacker, $group, $candidates, &$count, &$result, $attlimit = -1, $attmalus = 0)
- {
- global $battle_flags, $bonus;
- $totalcount = @array_sum ($count);
- // print_r ($count);
- if ($totalcount > 0)
- {
- $attgroup = $group[$attacker];
- $attuid = $attgroup[UID];
- if ($totalcount < $result[$attacker] / SQUISH_NUM) // small groups are just squashed FIXME: Add a static amount of attackers below instead
- $force = $result[$attacker];
- else
- {
- if ($attgroup[TYPE] == GBOW)
- $force = min ($result[$attacker], min ($result[$attacker], $totalcount * BOW_TARGETS) + ($result[$attacker] / 8));
- else
- $force = min ($result[$attacker], min ($result[$attacker], $totalcount * FOOT_TARGETS) + ($result[$attacker] / 8));
- }
- if ($attlimit != -1)
- $force = min ($attlimit, $force);
- if ($attmalus != 0)
- $attack = max (1, ($attgroup[ATT] - $attmalus));
- else
- $attack = ($attgroup[ATT] - $attmalus);
- $damage = $attack/100 * $force * $attgroup[DAMAGE];
- if ($battle_flags & DEBUG) echo "<u>".$lineno."</u>: ".$force." (".$result[$attacker]."), malus ".$attmalus." gives ".$attack.", limit ".$attlimit." attack for ".$damage." damage<br>";
- if (is_array ($candidates))
- {
- foreach ($candidates as $this_type => $this_cand)
- {
- foreach ($this_cand as $this_group)
- {
- if ($count[$this_type] > 0)
- {
- $defuid = $group[$this_group][UID];
- $partdam = ($damage * $result[$this_group] / $totalcount);
- // Check if the attacking group has any specific bonuses against this target
- // group
- if ($battle_flags & DEBUG) echo "Attacking group ".$this_group." which is of type ".$group[$this_group][TYPE].", has flags ".$group[$this_group][FLAGS].", my own type is ".$attgroup[TYPE].", my flags are ".$attgroup[FLAGS]." and my side is ".$attgroup[SIDE]."<br>\n";
- // Check if attacker has horse defense bonus, is defender and target is horse
- if (($group[$this_group][TYPE] == GHORSE) && ($attgroup[SIDE] == 1))
- {
- if ($bonus[$attuid][HERO3] > 1)
- {
- if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO3]." defense against large enemies<br>\n";
- $partdam *= $bonus[$attuid][HERO3];
- }
- if ($attgroup[FLAGS] & 2)
- {
- $partdam *= 1.333;
- if ($battle_flags & DEBUG) echo "Invoked special defense against large enemies<br>\n";
- }
- }
- // Check if attacker has offense bonus against archers and target is archer
- if ($group[$this_group][TYPE] == GBOW)
- {
- if ($bonus[$attuid][HERO1] > 1)
- {
- if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO1]." defense against large enemies<br>\n";
- $partdam *= $bonus[$attuid][HERO1];
- }
- if ($attgroup[FLAGS] & 128)
- {
- $partdam *= 1.333;
- if ($battle_flags & DEBUG) echo "Invoked special defense against archers<br>\n";
- }
- }
- // Check if attacker has universal bonus against siege units and target is siege
- if ($group[$this_group][FLAGS] & 4)
- {
- if ($bonus[$attuid][HERO4] > 1)
- {
- if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO4]." defense against large enemies<br>\n";
- $partdam *= $bonus[$attuid][HERO4];
- }
- if ($attgroup[FLAGS] & 512)
- {
- $partdam *= 1.333;
- if ($battle_flags & DEBUG) echo "Invoked special universal against siege<br>\n";
- }
- }
- // Check if target has resistance against archery and attacker is archer
- if ($attgroup[TYPE] == GBOW)
- {
- if ($bonus[$defuid][HERO2] > 1)
- {
- if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$defuid][HERO2]." resistance against archers<br>\n";
- $partdam /= $bonus[$defuid][HERO2];
- }
- if ($group[$this_group][FLAGS] & 1024)
- {
- $partdam *= 0.6667;
- if ($battle_flags & DEBUG) echo "Invoked special resistance against archers<br>\n";
- }
- }
- $cas = min ($partdam / $group[$this_group][HP], $result[$this_group]);
- if ($battle_flags & DEBUG) echo "Have ".$result[$this_group]." people, which is ".($result[$this_group] / $totalcount)." percent of total dam ".$damage.", gives ".$partdam.", HP is ".$group[$this_group][HP]."<br>\n";
- $result[$this_group] = round ($result[$this_group] - $cas, 3);
- if ($battle_flags & DEBUG) echo "Group ".$group[$this_group][NAME]." (".$this_group.") loose ".$cas."<br>";
- $count[$this_type] = round ($count[$this_type] - $cas, 3);
- }
- }
- }
- }
- else
- {
- if ($battle_flags == DEBUG)
- {
- echo "No 3nd array. Count:<br>\n";
- print_r ($count);
- }
- }
- }
- else
- {
- if ($battle_flags == DEBUG)
- {
- echo "<b>Attacking empty group: </b>";
- print_r ($candidates);
- }
- }
- }
- // And again for two target groups
- function do_attack_simul_three ($lineno, &$owncount, $attacker, $group, $candidates, $count, &$tempcount, &$owntempcount, $input, &$result, $attlimit = -1, $attmalus = 0)
- {
- global $battle_flags, $bonus;
- $totalcount = array_sum ($count);
- // print_r ($count);
- if ($totalcount > 0)
- {
- $attgroup = $group[$attacker];
- $attuid = $attgroup[UID];
- if ($totalcount < $input[$attacker] / SQUISH_NUM) // small groups are just squashed
- $force = $input[$attacker];
- else
- {
- if ($attgroup[TYPE] == GBOW)
- $force = min ($input[$attacker], min ($input[$attacker], $totalcount * BOW_TARGETS) + ($input[$attacker] / 8));
- else
- $force = min ($input[$attacker], min ($input[$attacker], $totalcount * FOOT_TARGETS) + ($input[$attacker] / 8));
- }
- if ($attlimit != -1)
- $force = min ($attlimit, $force);
- if ($attmalus != 0)
- $attack = max (1, ($attgroup[ATT] - $attmalus));
- else
- $attack = ($attgroup[ATT] - $attmalus);
- $damage = $attack/100 * $force * $attgroup[DAMAGE];
- if ($battle_flags == DEBUG) echo "<u>".$lineno."</u>: ".$force."(".$input[$attacker].", l:".$attlimit."), malus ".$attmalus." gives ".$attack."/".$attgroup[ATT].", limit ".$attlimit." attack for ".$damage." damage (sim)<br>";
- if (is_array ($candidates))
- {
- foreach ($candidates as $this_type=>$this_cand)
- {
- if ($count[$this_type] > 0)
- {
- foreach ($this_cand as $this_group)
- {
- $defuid = $group[$this_group][UID];
- $partdam = ($damage * $result[$this_group] / $totalcount);
- // Check if the attacking group has any specific bonuses against this target
- // group
- if ($battle_flags & DEBUG) echo "Attacking group ".$this_group." with pd ".$partdam." which is of type ".$group[$this_group][TYPE].", has flags ".$group[$this_group][FLAGS].", my own type is ".$attgroup[TYPE].", my flags are ".$attgroup[FLAGS]." and my side is ".$attgroup[SIDE]."<br>\n";
- // Check if attacker has horse defense bonus, is attacker and target is horse
- if (($group[$this_group][TYPE] == GHORSE) && ($attgroup[SIDE] == 1))
- {
- if ($bonus[$attuid][HERO3] > 1)
- {
- if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO3]." defense damage bonus against large enemies<br>\n";
- $partdam *= $bonus[$attuid][HERO3];
- }
- if ($attgroup[FLAGS] & 2)
- {
- $partdam *= 1.333;
- if ($battle_flags & DEBUG) echo "Invoked special defense damage bonus against large enemies<br>\n";
- }
- }
- // Check if attacker has offense bonus against archers and target is archer
- if ($group[$this_group][TYPE] == GBOW)
- {
- if ($bonus[$attuid][HERO1] > 1)
- {
- if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO1]." universal against archers<br>\n";
- $partdam *= $bonus[$attuid][HERO1];
- }
- if ($attgroup[FLAGS] & 128)
- {
- $partdam *= 1.333;
- if ($battle_flags & DEBUG) echo "Invoked special universal against archers<br>\n";
- }
- }
- // Check if attacker has universal bonus against siege units and target is siege
- if ($group[$this_group][FLAGS] & 4)
- {
- if ($bonus[$attuid][HERO4] > 1)
- {
- if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO4]." universal against siege<br>\n";
- $partdam *= $bonus[$attuid][HERO4];
- }
- if ($attgroup[FLAGS] & 512)
- {
- $partdam *= 1.333;
- if ($battle_flags & DEBUG) echo "Invoked special universal against siege<br>\n";
- }
- }
- // Check if target has resistance against archery and attacker is archer
- if ($attgroup[TYPE] == GBOW)
- {
- if ($bonus[$defuid][HERO2] > 1)
- {
- if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$defuid][HERO2]." resistance against archers<br>\n";
- $partdam /= $bonus[$defuid][HERO2];
- }
- if ($group[$this_group][FLAGS] & 1024)
- {
- $partdam *= 0.6667;
- if ($battle_flags & DEBUG) echo "Invoked special resistance against archers<br>\n";
- }
- }
- $cas = min ($partdam / $group[$this_group][HP], $result[$this_group]);
- if ($battle_flags & DEBUG) echo "Have ".$result[$this_group]." people, which is ".($result[$this_group] / $totalcount)." percent of total dam ".$damage.", gives ".$partdam.", HP is ".$group[$this_group][HP]."<br>\n";
- $result[$this_group] = round ($result[$this_group] - $cas, 3);
- if ($battle_flags == DEBUG) echo "Group ".$group[$this_group][NAME]." (".$this_group.") loose ".$cas."<br>";
- $tempcount[$this_type] = round ($tempcount[$this_type] - $cas, 3);
- }
- }
- }
- }
- }
- else
- {
- if ($battle_flags == DEBUG)
- {
- echo "<b>Attacking empty group: </b>";
- print_r ($candidates);
- }
- }
- }
- // Concoct a units array suitable for the do_battle function from an attackers
- // troop id and a target village id
- function prepare_battle ($attuid, $troopid, $villageid, $hero, &$armyinfo, $attacker_surprised, $type=ATTACK, $grattid=0)
- {
- global $debug;
- // If spies were surprised while they where spying, they get severe mali
- if ($attacker_surprised)
- {
- if ($debug)
- {
- echo "Attacker with UID ".$attuid." is seriously surprised!<br>\n";
- }
- $surprise_malus = .1;
- }
- else
- $surprise_malus = 1;
- // Prime the attack group pool - even a single attacker is a group here :)
- $groupmembers[$troopid] = $attuid;
- // First check if this attack involves an attacking group or a single player
- if (($type == GROUPATTACK) && ($grattid > 0))
- {
- $query = "select uid,troopid from groupatt_members where grattid=".$grattid;
- log_debug ("grattquery: ".$query);
- if (!($res = mysql_query ($query)))
- log_err ("Cannot fetch group members in prepare_battle. Query: ".$query.", ".mysql_error ());
- else
- {
- // Then add all members to the pool
- $groupmembers = array ();
- while ($row = mysql_fetch_array ($res))
- $groupmembers[$row['troopid']] = $row['uid'];
- }
- // And formulate the query
- $query = "select A.villageid, A.flag, A.location, A.troopid, A.uid, B.unitid, B.amount, C.aid, C.hero_health, C.class, D.hp, D.ini, D.att, D.damage, D.capacity, D.regen, D.fight1, D.fight2, D.fight3, D.fight4, D.fight5, D.build3, D.build4, D.knowledge1, D.knowledge2 from armies A, troops B, user C, hero D where A.troopid=B.troopid and ((A.location=".$villageid." and A.flag=0) or A.troopid in (".implode (",", array_keys ($groupmembers)).")) and A.uid=C.uid and A.uid=D.uid";
- }
- else
- $query = "select A.villageid, A.flag, A.location, A.troopid, A.uid, B.unitid, B.amount, C.aid, C.hero_health, C.class, D.hp, D.ini, D.att, D.damage, D.capacity, D.regen, D.fight1, D.fight2, D.fight3, D.fight4, D.fight5, D.build3, D.build4, D.knowledge1, D.knowledge2 from armies A, troops B, user C, hero D where A.troopid=B.troopid and ((A.location=".$villageid." and A.flag=0) or A.troopid=".$troopid.") and A.uid=C.uid and A.uid=D.uid";
- if ($debug)
- echo $query;
- $wallres_level = 0;
- log_debug ("prepquery: ".$query);
- if (!($result = mysql_query ($query)))
- {
- log_err ("prepare_battle: query ".$query." failed. Error: ".mysql_error ());
- return NULL;
- }
- // It might happen that there are no troops in the target village at all
- // In this case we won't learn the ally id here
- while ($row = mysql_fetch_array ($result))
- {
- $uid = $row['uid'];
- $troopid = $row['troopid'];
- $go_on = false;
- // Using the pool we can now sort troops at the location into attackers, defenders, and not-involved-guys
- if (isset ($groupmembers[$troopid]) && ($row['flag'] != 0))
- {
- $armyinfo[$uid][2000] = $units[$troopid][2000] = 0;
- $go_on = true;
- }
- if ((!in_array ($row['uid'], $groupmembers)) && ($row['flag'] == 0)) // Which might mean home troops or supporting troops
- {
- // This unit is in the village that is being attacked
- $armyinfo[$uid][2000] = $units[$troopid][2000] = 1;
- $go_on = true;
- // Now check if this is the uid of the defender
- if ($row['villageid'] == $villageid)
- $defuid = $row['uid'];
- }
- if ($go_on)
- {
- $units[$troopid][2001] = $uid;
- $units[$troopid][2002] = $row['villageid'];
- // Heroes also fight, eh?
- if (($row['unitid'] == 1000) && ($row['amount'] > 0))
- {
- // The defending hero might have healed
- if ($row['uid'] == $defuid)
- {
- // But first check if he is alive at all
- if ((round ($hero->health, 2) > 0) && ($hero->location == $villageid))
- $units[$troopid][1000] = $hero->health / $row['hp'];
- }
- else
- $units[$troopid][1000] = $row['hero_health'] / $row['hp'];
- if ($debug)
- {
- echo "Hero for UID ".$row['uid']." TID ".$row['troopid']." has ".$units[$troopid][1000]." HP";
- }
- }
- else // Otherwise these are normal units
- $units[$troopid][$row['unitid']] = $row['amount'];
- if (!isset ($armyinfo[$uid][2003]))
- {
- $armyinfo[$uid][2003] = $row['aid'];
- $armyinfo[$uid][3000] = $armyinfo[$uid][3001] = $armyinfo[$uid][3002] = $armyinfo[$uid][3003] = 0;
- $armyinfo[$uid][3500] = $row['hp'];
- $armyinfo[$uid][3501] = $row['att'];
- $armyinfo[$uid][3502] = $row['ini'];
- $armyinfo[$uid][3503] = $row['damage'];
- $armyinfo[$uid][3504] = $row['capacity'];
- $armyinfo[$uid][3505] = $row['regen'];
- $armyinfo[$uid][3506] = $row['fight1'] * RESEARCH_STEP; // + att vs archers
- $armyinfo[$uid][3507] = $row['fight2'] * RESEARCH_STEP; // + res vs archers
- $armyinfo[$uid][3508] = $row['fight3'] * RESEARCH_STEP; // + for def + res vs riders
- $armyinfo[$uid][3509] = $row['fight4'] * RESEARCH_STEP; // + att vs siege
- $armyinfo[$uid][3510] = $row['fight5'] * RESEARCH_STEP; // initiative +
- $armyinfo[$uid][3511] = $row['build3'] * RESEARCH_STEP; // other walls -
- $armyinfo[$uid][3512] = $row['build4'] * RESEARCH_STEP; // own walls +
- $armyinfo[$uid][3513] = $row['knowledge1'] * RESEARCH_STEP; // hide own gold better
- $armyinfo[$uid][3514] = $row['knowledge2'] * RESEARCH_STEP; // loot more gold
- $armyinfo[$uid][3520] = $row['class'];
- // Fighters gain three levels of attack research
- if ($row['class'] == 1)
- {
- log_debug ("UID ".$uid." gains fighter bonus in this battle");
- $armyinfo[$uid][3000] = 15;
- }
- }
- }
- }
- foreach ($armyinfo as $uid=>$this_unit)
- {
- $uids[] = $uid;
- }
- $query = "select A.uid, A.level, B.type from userresearch A join research B where A.resid=B.id and A.uid in (".implode (",", $uids).")";
- if (!($res = mysql_query ($query)))
- {
- log_err ("Cannot fetch combat participant research levels. Query: ".$query.", ".mysql_error());
- }
- else
- {
- while ($row = mysql_fetch_array ($res))
- {
- $uid = $row['uid'];
- switch ($row['type'])
- {
- case 7: // chivalry research
- $armyinfo[$uid][3006] = $row['level'] * RESEARCH_STEP;
- break;
- case 6: // siege research
- $armyinfo[$uid][3005] = $row['level'] * RESEARCH_STEP;
- break;
- case 5: // horse research
- $armyinfo[$uid][3004] = $row['level'] * RESEARCH_STEP;
- break;
- case 4: // commando research
- $armyinfo[$uid][3003] = $row['level'] * RESEARCH_STEP;
- break;
- case 3: // archery research
- $armyinfo[$uid][3002] = $row['level'] * RESEARCH_STEP;
- break;
- case 2: // foot research
- $armyinfo[$uid][3001] = $row['level'] * RESEARCH_STEP;
- break;
- case 8: // off training
- if ($armyinfo[$uid][2000] == 0) // Only for attackers
- $armyinfo[$uid][3000] += $row['level'] * RESEARCH_STEP;
- break;
- case 9: // def training
- if ($armyinfo[$uid][2000] == 1) // Only for defenders
- $armyinfo[$uid][3000] += $row['level'] * RESEARCH_STEP;
- break;
- case 17: // wall building
- if ($defuid == $row['uid']) // Only for the owner of the defender village
- $wallres_level = $row['level'];
- break;
- }
- }
- }
- $query = "select type,level from villbuild where type=11 and villageid=".$villageid;
- if (!($res = mysql_query ($query)))
- {
- log_err ("Cannot read wall level in combat prep. Query: ".$query.", ".mysql_error ());
- }
- else
- {
- if (($row = mysql_fetch_array ($res)))
- {
- $armyinfo[$defuid][4000] = $row['level'];
- $armyinfo[$defuid][4001] = $wallres_level;
- }
- }
- $batlog = "";
- foreach ($armyinfo as $this_uid=>$this_army)
- {
- $batlog .= "UID ".$this_uid.": Att+".$this_army[3000].", Dam+".$this_army[3001].", HP+".$this_army[3003].", Wall: ".$this_army[3004]."; ";
- }
- log_debug ($batlog);
- // print_r ($armyinfo);
- return ($units);
- }
- // Test if each side has more than zero units - if not, the battle is won automatically
- function checkup_battle ($parties)
- {
- $def_units = $att_units = 0;
- foreach ($parties as $this_party)
- {
- if ($this_party[2000] == 1)
- {
- foreach ($this_party as $key=>$amount)
- {
- if ($key <= 1000)
- $def_units += $amount;
- }
- }
- else
- {
- foreach ($this_party as $key=>$amount)
- {
- if ($key <= 1000)
- $att_units += $amount;
- }
- }
- }
- $num_units = array($att_units, $def_units);
- return ($num_units);
- }
- function check_wall_health (&$new_level, $wall_hp, $wall_hp_orig, $wall_res, &$wfoot, &$wbow)
- {
- global $debug;
- $old_level = $new_level;
- if ($wall_hp_orig != $wall_hp)
- {
- while ($new_level > 0)
- {
- $wall_hp_test = WallValues ($new_level, $wall_res, $wfoot, $wbow);
- if ($debug) echo "wall hp test for level ".$new_level.": ".$wall_hp_test." < ".$wall_hp."<br>\n";
- if ($wall_hp_test < $wall_hp)
- break;
- else
- $new_level--;
- }
- if ($debug) echo "Wall razed from ".$old_level." to ".$new_level."<br>\n";
- }
- $wfoot = $new_level * 4;
- $wbow = $new_level * 2;
- }
- define ("CMD_INIT", 1);
- define ("CMD_ATT_1", 2);
- define ("CMD_ATT_2", 3);
- define ("CMD_WATT", 10);
- define ("CMD_DONE", 100);
- /**
- The central battle function
- After precomputing a lot of arrays, we iterate through all unit groups sorted by initiative
- each round. Before each iteration, we decide about parameters for this round, like wall state
- and the targets for each unit type for each side. Then each unit group acts according to these decisions
- for this round. When a unit type has no more actions to do, we count this type as having finished
- When either all unit types have finished or the round limit is up, we end the loop.
- Each round statistics are saved about the remaining number of units for each group.
- In typical battles unit groups will have two target groups most of the time, i.e.
- footmen will fight against other footmen and enemy horses (that are trying to kill our
- archers). The enemy horse are fighting our bowmen and our foot.
- @param units -
- @param idmap - (call by reference)
- @param capacity - (call by reference)
- @param maxrounds - maximum number of round that will be fought
- @param armyinfo -
- @param num_units - array(att_units_num,def_units_num)
- @param flags - debug boolean.
- @param extra_results - gives extra results (call by reference).
- @param attacker_is_surprised - boolean set if attackers are spies.
- */
- function do_battle ($units, &$idmap, &$capacity, $maxrounds, $armyinfo, $num_units, $flags, &$extra_results, $attacker_is_surprised=false)
- {
- global $battle_flags, $unit_byrace, $bonus;
- $battle_flags = $flags;
- if ($battle_flags == DEBUG)
- {
- $cmd_names = array (1=>"INIT", 2=>"ATT1", 3=>"ATT2", 10=>"WATT", 100=>"DONE");
- }
- $players = count($armyinfo);
- $sides = 2;
- $fighting = true;
- $round = $idx = 0;
- $siege_towers = 0;
- $all_towers = array ();
- $horses_vuln = true;
- $std_targets[GFOOT] = array (GFOOT, GHORSE, GBOW);
- $std_targets[GHORSE] = array (GBOW, GFOOT, GHORSE);
- $std_targets[GBOW] = array (GFOOT, GHORSE, GBOW);
- $wall_std_targets[GFOOT] = array (GHORSE, GFOOT, GBOW); // Horse is all that matters here anyway
- $wall_std_targets[GBOW] = array (GFOOT, GBOW, GHORSE);
- $wall_std_targets[GHORSE] = array (GBOW, GHORSE, GFOOT);
- // Get all bonuses into a factor, so we can apply it more easily
- foreach ($armyinfo as $uid => $this_player)
- {
- // echo "Reading info for uid ".$uid."\n";
- $bonus[$uid][ATT] = $bonus[$uid][HP] = $bonus[$uid][DAMAGE] = $bonus[$uid][INI] = $bonus[$uid][GHORSE] = $bonus[$uid][GFOOT] = $bonus[$uid][GSIEGE] = $bonus[$uid][GBOW] = $bonus[$uid][GCOMMANDO] = $bonus[$uid][GCHIVALRY] = 1;
- if ($this_player[3000] > 0)
- $bonus[$uid][ATT] += ($this_player[3000] / 100);
- if ($this_player[3001] > 0)
- $bonus[$uid][GFOOT] += ($this_player[3001] / 100);
- if ($this_player[3002] > 0)
- $bonus[$uid][GBOW] += ($this_player[3002] / 100);
- if ($this_player[3003] > 0)
- $bonus[$uid][GCOMMANDO] += ($this_player[3003] / 100);
- if ($this_player[3004] > 0)
- $bonus[$uid][GHORSE] += ($this_player[3004] / 100);
- if ($this_player[3005] > 0)
- $bonus[$uid][GSIEGE] += ($this_player[3005] / 100);
- if ($this_player[3006] > 0)
- $bonus[$uid][GCHIVALRY] += ($this_player[3006] / 100);
- if ($this_player[3510] > 0)
- $bonus[$uid][INI] += ($this_player[3010] / 100);
- if ($this_player[3506] > 0)
- $bonus[$uid][HERO1] = 1 + ($this_player[3506] / 100);
- if ($this_player[3507] > 0)
- $bonus[$uid][HERO2] = 1 + ($this_player[3507] / 100);
- if ($this_player[3508] > 0)
- $bonus[$uid][HERO3] = 1 + ($this_player[3508] / 100);
- if ($this_player[3509] > 0)
- $bonus[$uid][HERO4] = 1 + ($this_player[3509] / 100);
- if (isset ($this_player[4000]))
- {
- if ($this_player[4000] > 0)
- {
- $wall = $this_player[4000];
- $wall_res = $this_player[4001];
- $wall_hero_bonus = 1 + ($this_player[3512] / 100);
- }
- }
- if (isset ($this_player[3511]) && ($this_player[3511] > 0))
- $bonus[$uid][GWALL] = $this_player[3511];
- }
- // We need a wall level
- if (!isset ($wall))
- $wall = 0;
- $wallroom = $wall * 72 * pow (1.2, $wall); // How many units can stand on a wall?
- if ($wall > 0)
- {
- $wall_hp = $wall_hp_orig = WallValues ($wall, $wall_res, $wfoot, $wbow);
- $wall_new = $wall;
- $whorse = $wall * 8 * $wall_hero_bonus;
- $wfoot = $wall * 4 * $wall_hero_bonus;
- $wbow = $wall * 2 * $wall_hero_bonus;
- $wall_breached = false;
- if ($battle_flags == DEBUG)
- {
- echo "Setting wall to ".$wall." (".$wall_hero_bonus." from hero), wfoot is ".$wfoot.", wbow is ".$wbow.", wallroom is ".$wallroom.", wall hp is ".$wall_hp;
- echo "<br>Comes from 1000 * pow (".WALL_HP_BASE." * (50 + ".$wall.") / 50, ".$wall_res.")";
- }
- }
- else
- {
- $wall_breached = true;
- $whorse = $wbow = $wfoot = $wall_hp = $wall_hp_orig = $wall_new = 0;
- }
- if ($flags == DEBUG)
- {
- echo "in_units:<br>\n";
- print_r ($units);
- echo "armyinfo:<br>\n";
- print_r ($armyinfo);
- echo "bonus:<br>\n";
- print_r ($bonus);
- }
- $sidecount[0] = $sidecount[1] = 0;
- // Initialize arrays for each unit type and side
- // Reference them from group arrays ordered by ini
- $query = "select unitid,type,ini,hp,att,damage,flags,name,capacity from units"; // FIXME: Might want to take name out later on
- if (!($res = mysql_query ($query)))
- log_err ("do_battle: Error with query: ".$query.": ".mysql_error());
- $unitcount = mysql_num_rows ($res);
- while ($row = mysql_fetch_array ($res))
- {
- foreach ($units as $troopid => $this_player)
- {
- if (isset ($this_player[$row['unitid']]))
- {
- if ($this_player[$row['unitid']] > 0)
- {
- $uid = $this_player[2001];
- $idmap[$troopid][$row['unitid']] = $idx;
- $group[$idx][UNITID] = $row['unitid'];
- $side = $group[$idx][SIDE] = $this_player[2000];
- $group[$idx][TYPE] = $row['type'];
- $group[$idx][NAME] = $row['name'];
- $group[$idx][ATT] = $row['att'] * $bonus[$uid][ATT];
- $group[$idx][FLAGS] = $row['flags'];
- $group[$idx][UID] = $uid;
- $this_research = 1;
- if (($row['flags'] & 16) || ($row['flags'] & 4))// siege?
- {
- $this_research = $bonus[$uid][GSIEGE];
- }
- else
- {
- if ($row['flags'] & 32) // commando?
- $this_research = $bonus[$uid][GCOMMANDO];
- else
- {
- if ($row['flags'] & 64) // chivalry?
- $this_research = $bonus[$uid][GCHIVALRY];
- else
- {
- // all other unit types can be selected according to type
- $this_research = $bonus[$uid][$row['type']];
- }
- }
- }
- if ($battle_flags == DEBUG)
- echo "Using bonus ".$this_research." (+".$bonus[$uid][ATT].") for unit ".$row['name']."<br>\n";
- $group[$idx][DAMAGE] = $row['damage'] * $bonus[$uid][DAMAGE] * $this_research;
- $group[$idx][INI] = round ($row['ini'] * $bonus[$uid][INI]);
- $group[$idx][HP] = $row['hp'] * $bonus[$uid][HP] * $this_research;
- $capacity[$row['unitid']] = $row['capacity'];
- if (($row['flags'] & 16) && ($side == 0)) // Test for siege tower
- {
- $siege_towers += $units[$uid][$row['unitid']]; // Add as many siege towers as this user has
- $all_towers[] = $idx;
- }
- else
- $all[$idx] = $group[$idx][INI];
- // Check all flags that apply to all targets group in battle, i.e. that are
- // not dependent on any attribute of a specific targets
- // This tests for defense bonus, only applies to defender of course
- if (($row['flags'] & 1) && ($side == 1))
- $group[$idx][ATT] *= 2.0;
- else
- {
- if (array_key_exists ($row['unitid'], $unit_byrace[1]) && ($side == 1))
- {
- if ($battle_flags == DEBUG)
- echo "Only human, bonus 20% for unit ".$row['name']."<br>\n";
- $group[$idx][ATT] *= 1.2;
- }
- }
- // Berserker units get battle rage bonus when pitched against large amounts of enemies
- if ($row['flags'] & 2048)
- {
- $ragebonus = min (15, max (0, ($num_units[1] / $num_units[0] - 0.5) * 0.5) + 1.0);
- $ragevuln = min (15, max (0, ($num_units[1] / $num_units[0] - 1) * 0.125) + 1.0);
- if ($battle_flags == DEBUG)
- echo "Using rage bonus ".$ragebonus.", HP vuln ".$ragevuln." for unit ".$row['name']."<br>\n";
- $group[$idx][ATT] *= $ragebonus;
- $group[$idx][HP] /= $ragevuln;
- }
- // Healer units can heal others after combat
- if ($row['flags'] & HEALER)
- {
- if ($battle_flags == DEBUG)
- echo "Set healer for unit ".$row['name']."<br>\n";
- $healers[$troopid][] = $idx;
- }
- $troops[1][$idx] = $this_player[$row['unitid']];
- // If the attacker was surprised, he starts out with losses
- if ($attacker_is_surprised && ($side == 0))
- {
- $instantkills = ceil ($troops[1][$idx] * (rand (5, 10) / 100));
- if ($battle_flags == DEBUG)
- echo "Instant-killing ".$instantkills." of ".$troops[1][$idx]." troops. Also dropping attack to ".($group[$idx][ATT] * .1)."<br>\n";
- $troops[1][$idx] -= $instantkills;
- $group[$idx][ATT] *= 0.1;
- // Also slow them, as they are surprised
- $group[$idx][INI] *= 4;
- }
- $command[$idx] = CMD_INIT;
- $initest[$group[$idx][INI]][] = $idx; // Check for units that have the same modified ini
- $bytypes[$side][$group[$idx][TYPE]][] = $idx; // Reference by type and side for easier targetting
- $bysides[$side][] = $idx; // Reference by side, in case we attack anything we see - FIXME: remove later
- if (!isset ($typecount[$side][$group[$idx][TYPE]]))
- $typecount[$side][$group[$idx][TYPE]] = $troops[1][$idx];
- else
- $typecount[$side][$group[$idx][TYPE]] += $troops[1][$idx];
- $sidecount[$side] += $troops[1][$idx];
- if ($group[$idx][TYPE] == GBOW)
- $missile[$idx] = $group[$idx][INI];
- $idx++;
- }
- }
- }
- }
- foreach ($units as $troopid => $this_player)
- {
- if (isset ($this_player[1000]))
- {
- if ($this_player[1000] > 0)
- {
- $uid = $this_player[2001];
- $idmap[$troopid][1000] = $idx;
- $group[$idx][UNITID] = 1000;
- $side = $group[$idx][SIDE] = $this_player[2000];
- $group[$idx][TYPE] = GFOOT;
- $group[$idx][NAME] = "Held";
- $group[$idx][HP] = $armyinfo[$uid][3500];
- $group[$idx][ATT] = $armyinfo[$uid][3501];
- $group[$idx][DAMAGE] = $armyinfo[$uid][3503];
- $group[$idx][INI] = $armyinfo[$uid][3502];
- $group[$idx][FLAGS] = 0;
- $group[$idx][UID] = $uid;
- $capacity[1000+$uid] = $armyinfo[$uid][3504];
- $troops[1][$idx] =$this_player[1000];
- $command[$idx] = CMD_INIT;
- $all[$idx] = $group[$idx][INI];
- $initest[$group[$idx][INI]][] = $idx; // Check for units that have the same modified ini
- $bytypes[$side][$group[$idx][TYPE]][] = $idx; // Reference by type and side for easier targetting
- $bysides[$side][] = $idx; // Reference by side, in case we attack anything we see - FIXME: remove later
- $typecount[$side][$group[$idx][TYPE]] += $troops[1][$idx];
- $sidecount[$side] += $troops[1][$idx];
- if ($group[$idx][TYPE] == GBOW)
- $missile[$idx] = $group[$idx][INI];
- if ($battle_flags == DEBUG)
- {
- echo "<pre>Hero is\n";
- print_r ($group[$idx]);
- echo "</pre>";
- }
- $idx++;
- }
- }
- }
- if ($battle_flags == DEBUG)
- {
- echo "<br>Sidecount: <br>";
- print_r ($sidecount);
- echo "<br>Typecount: <br>";
- print_r ($typecount);
- echo "<br>By types: <br>";
- print_r ($bytypes);
- echo "<br>Towers: <br>";
- print_r ($all_towers);
- }
- // Evil actually: Test each initiative value for the amount of units that have this value
- // If only one unit has this ini, we delete the initest. In all other cases we have the group IDs
- // of units that "sit" on this ini together
- foreach ($initest as $idx => $this_initest)
- {
- if (count ($this_initest) == 1)
- unset ($initest[$idx]);
- }
- arsort ($all, SORT_NUMERIC);
- if (isset ($missile))
- arsort ($missile, SORT_NUMERIC);
- $starttypecount = $typecount;
- // This is the main loop of battle. It ends when the battle ends - surprise
- while ($fighting)
- {
- // Count the active round, since combat ends after a few rounds
- $round++;
- if ($flags == DEBUG) echo "<p><b>Round ".$round."</b><br>\n";
- // More on temp state later on
- $tempgroupini = -1;
- // If we have archer units, they can fire in the first round before melee starts.
- // If this happens, we add another round to combat length.
- if (1 == $round)
- {
- if (isset ($missile))
- {
- $list = $missile; // In the first round of combat, only the bowmen attack
- $maxrounds++; // Reflect the fact that first round of combat wasn't "real"
- }
- else
- $list = $all; // Otherwise, we iterate through all groups later on
- }
- else
- $list = $all;
- if ($flags == DEBUG)
- {
- echo "List of attackers, sorted:<br>\n";
- print_r ($list);
- echo "<br>\n";
- }
- // Iterate through all groups
- foreach ($list as $attgroupid => $thisini)
- {
- // Delete killed groups from the list of attacking units
- if ($troops[$round][$attgroupid] == 0)
- unset ($list[$attgroupid]);
- else
- {
- // If this group is still alive, switch the active side to its side
- $att = (int)$group[$attgroupid][SIDE];
- // And the target to the other side
- $target = $att ^ 1;
- // Only go through the attack sequence if this unit has a damage rating, except
- // when there is a wall to bash!
- if ((($group[$attgroupid][DAMAGE] == 0) || ($troops[$round][$attgroupid] == 0)) && !$wall)
- continue;
- if ($flags == DEBUG)
- {
- echo "<p>Attacker is ".$att.", target is ".$target.", Attacking group: ".$group[$attgroupid][NAME]." (".$attgroupid.")<br>\n";
- echo "Targets type count is<br>\n";
- if (isset ($typecount[$target]))
- print_r ($typecount[$target]);
- echo "<br>";
- }
- $this_uid = $group[$attgroupid][UID];
- if ($flags == DEBUG) echo "Attacker with uid ".$this_uid." has wallhack ".$bonus[$this_uid][GWALL]."<br>";
- // Temp is a state, when some groups in the battle attack at the same time.
- // During this time casualties are stored in a copy of the troops array, but of this round only.
- // This copy is called temptroops. tempgroupini records the initiative value for the
- // simultaneous attacks, so we can see when the temp state ends.
- // First we check if we are in temp state
- if ($tempgroupini != -1)
- {
- if ($flags == DEBUG) echo "Temp group found<br>";
- // Then check if temp state hasn't ended
- if ($thisini != $tempgroupini)
- {
- // Yes, it has.
- if ($flags == DEBUG)
- {
- echo "Temp group ini ".$tempgroupini." != ".$thisini.", deleting.<br>";
- print_r ($troops[$round]);
- }
- // Copy the collected values back into the troops array for this round
- $troops[$round] = $temptroops;
- $typecount = $tempcount;
- if ($flags == DEBUG)
- {
- echo "<br>New: <br>";
- print_r ($troops[$round]);
- echo "<br>:New typecount: <br";
- print_r ($typecount);
- }
- // And delete the temp state infos
- $tempgroupini = -1;
- $temptroops = null;
- $tempcount = null;
- }
- }
- // Now we check if we have to start temp state, which happens
- // if this group attacks simultaneously with other groups
- if (isset ($initest[$thisini]))
- {
- if ($flags == DEBUG) echo "Attacker with ini ".$thisini." is in a sim group<br>";
- // We only need a new temp troop array if we don't already have one for this timeslice
- if ($tempgroupini == -1)
- {
- if ($flags == DEBUG) echo "No temp group found, creating<br>";
- // First we record this ini value, so we know when to copy back the temp troops
- $tempgroupini = $thisini;
- $temptroops = $troops[$round];
- $tempcount = $typecount;
- }
- }
- // Per default there are no siege users (units that use a siege tower to
- // overcome the wall)
- $siege_users = 0;
- // Save type for easy access
- $this_type = $group[$attgroupid][TYPE];
- $malus = 0;
- // $attack_already_done = false;
- // Now we calculate the malus for this particular group.
- // This depends primarily on the wall, the side and then the type.
- if ($wall)
- {
- // If this is the attacker, we dish out a malus if these are non-archery
- if ($att == 0)
- {
- // First we check for special units, for now only catapults and rams
- // They attack the wall if it stands, otherwise they attack units
- if (($group[$attgroupid][FLAGS] == 4) && ($wall_hp > -($wall_hp_orig/3)))
- {
- if ($flags == DEBUG) echo "<br>\nWallatt: ".$group[$attgroupid][DAMAGE]." attack wall with ".$wall_hp." HP";
- $wall_hp = $wall_hp - ($troops[$round][$attgroupid] * $group[$attgroupid][DAMAGE]);
- if ($flags == DEBUG) echo "<br>\nWallatt: ".$group[$attgroupid][DAMAGE]." wall now has ".$wall_hp." HP left<br>";
- // Next, see how the wall fares
- check_wall_health ($wall_new, $wall_hp, $wall_hp_orig, $wall_res, $wfoot, $wbow);
- // They don't fight people now
- continue;
- }
- // We might have the state here that only the wall is left standing, so we skip other units
- if ($sidecount[$target] == 0)
- continue;
- // If there is a wall, siege units will help the attacker overcome the wall
- // This only helps foot units of course
- if ($this_type == GFOOT)
- {
- // We first calculate the number of units that can use the siege towers
- $siege_towers = 0;
- // For this we run through the list of all siege towers (which we recorded
- // during unit setup, remember?).
- foreach ($all_towers as $this_idx)
- $siege_towers += $troops[$round][$this_idx];
- // echo "<br>\nNum towers: ".$siege_towers.", typecount ".$typecount[$att][GFOOT]."<br>\n";
- // If there are only siege units left, we can skip this
- if (($typecount[$att][GFOOT] - $siege_towers) > 0)
- $siege_users = min ($troops[$round][$attgroupid], ($siege_towers * UNITS_PER_SIEGE_TOWER) / ($typecount[$att][GFOOT] - $siege_towers) * $troops[$round][$attgroupid]);
- else
- $siege_users = 0;
- if ($flags == DEBUG)
- echo "min (".$troops[$round][$attgroupid].", min (".$siege_towers * UNITS_PER_SIEGE_TOWER.", ".($wallroom*2).") / (".$typecount[$att][GFOOT]." - ".$siege_towers.") * ".$troops[$round][$attgroupid].")";
- $nonsiege_users = $troops[$round][$attgroupid] - $siege_users;
- }
- if ($this_type != GBOW)
- $malus = $wfoot;
- else
- $malus = ($wbow / 2);
- // Attacking berserkers ignore the wall to 50%
- if ($group[$attgroupid][FLAGS] & 2048)
- $malus = $malus * 0.333;
- }
- else
- {
- // Defender archers get a bonus for attacking from the wall
- if (($this_type == GBOW) && !($group[$attgroupid][FLAGS] & 4))
- $malus = -$wbow;
- else
- $malus = 0;
- }
- }
- if ($flags == DEBUG)
- echo "Malus for type ".$this_type." is ".$malus." - ".$bonus[$this_uid][GWALL]."\n";
- $malus = max (0, $malus - $bonus[$this_uid][GWALL]);
- // Now lets fight. If we don't have any malus, just go ahead
- if ($malus == 0)
- {
- if ($temptroops)
- do_attack_simul_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $tempcount[$target], $tempcount[$att], $troops[$round], $temptroops);
- else
- do_attack_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $troops[$round]);
- }
- else
- {
- // If we have guys in siege towers, do them first
- if ($siege_users)
- {
- if ($temptroops)
- {
- do_attack_simul_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $tempcount[$target], $tempcount[$att], $troops[$round], $temptroops, $siege_users);
- if ($nonsiege_users)
- do_attack_simul_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $tempcount[$target], $tempcount[$att], $troops[$round], $temptroops, $nonsiege_users, $malus);
- }
- else
- {
- do_attack_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $troops[$round], $siege_users);
- if ($nonsiege_users)
- do_attack_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $troops[$round], $nonsiege_users, $malus);
- }
- }
- else
- {
- if ($temptroops)
- do_attack_simul_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $tempcount[$target], $tempcount[$att], $troops[$round], $temptroops, -1, $malus);
- else
- do_attack_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $troops[$round], -1, $malus);
- }
- }
- } // Check if this group is still alive
- // To know how many units are still alive for each side,
- // we just sum up all units that are stored in the (always up to
- // date) count by type
- $sidecount[$target] = @array_sum ($typecount[$target]);
- $sidecount[$att] = array_sum ($typecount[$att]);
- if ($flags == DEBUG)
- {
- echo "Summary:<br>";
- print_r ($sidecount);
- }
- // The first end state is when one side has no more units,
- // but only when temp state has ended (since in temp state
- // attacks happen at the same time, they are still hitting each other)
- if ((($sidecount[$target] == 0) && ($thisini != $tempgroupini)) && !$wall)
- {
- $fighting = false;
- break;
- }
- }
- // At the end of each round we also have to copy back temp state data, of course
- if ($tempgroupini != -1)
- {
- // Yes, it has.
- if ($flags == DEBUG)
- {
- echo "Temp group found at end of round, deleting.<br>";
- print_r ($troops[$round]);
- }
- $troops[$round] = $temptroops;
- $tempgroupini = -1;
- $temptroops = null;
- $typecount = $tempcount;
- $tempcount = null;
- if ($flags == DEBUG)
- {
- echo "<br>New: <br>";
- print_r ($troops[$round]);
- echo "<br>:New typecount: <br";
- print_r ($typecount);
- }
- }
- // Then we copy over the state of the troops as base data for the next round
- $troops[$round+1] = $troops[$round];
- if ($flags == DEBUG)
- {
- $outstr = "";
- for ($temp = 0; $temp < $sides; $temp++)
- {
- echo "<table class=\"inhalt\"><tr>\n";
- if (isset ($bysides[$temp]))
- {
- foreach ($bysides[$temp] as $this_group)
- {
- echo "<th><img src=\"gfx/u/".$group[$this_group][UNITID].".gif\">(".$this_group.")</th>\n";
- $outstr .= "<td>".$troops[$round][$this_group]."</td>\n";
- }
- }
- echo "</tr><tr>".$outstr."</tr></table>\n";
- $outstr = "";
- }
- echo "<pre>";
- print_r ($typecount);
- echo "</pre>";
- }
- // If we are over the round limit, combat ends. This is the second
- // end state.
- if ($round >= $maxrounds)
- $fighting = false;
- } // while ($fighting) = main loop
- // Check if wall was damaged, only then will we file it in the report
- if ($wall_hp != $wall_hp_orig)
- {
- $extra_results[1] = $wall;
- $extra_results[2] = $wall_new;
- // The wall can be repaired to at least 50%
- $wall_hp = max (0, min ($wall_hp_orig, $wall_hp + (float)($wall_hp_orig * 0.5)));
- $wall_new = $wall;
- if ($flags == DEBUG) echo "wall healed back to ".$wall_hp." of ".$wall_hp_orig." hp<br>\n";
- // After combat, check the poor wall
- check_wall_health ($wall_new, $wall_hp, $wall_hp_orig, $wall_res, $wfoot, $wbow);
- $extra_results[3] = $wall_new;
- if ($flags == DEBUG)
- {
- echo "extra results:<br>\n<pre>";
- print_r ($extra_results);
- echo "</pre>";
- }
- }
- // After combat, healers get a chance to look at other units
- if (isset ($healers))
- {
- if ($flags == DEBUG)
- {
- echo "<b>Healing</b><br/>\n";
- print_r ($healers);
- echo "<br/>\nHealers found, now iterating.<br/>\n";
- }
- // iterate over troops
- foreach ($healers as $this_troopid=>$healer_list)
- {
- if ($flags == DEBUG)
- echo "<br/>\nCalculating healing for troopid ".$this_troopid."<br/>\n";
- $heals = 0;
- // iterate over healers
- foreach ($healer_list as $healer_idx)
- {
- // Only alive healers can heal :)
- if ($troops[$round][$healer_idx] > 0)
- {
- // For each three healers one other can be healed
- $heals += round ($troops[$round+1][$healer_idx] / 3);
- }
- }
- if($heals > 0)
- {
- if ($flags == DEBUG)
- echo $heals." units can be healed<br/>\n";
- // count wounded units
- $wounded = array();
- foreach ($idmap[$this_troopid] as $idx)
- {
- if ($flags == DEBUG)
- echo $group[$idx][NAME].": ";
- // Siege units or healers can't be healed
- if (($group[$idx][FLAGS] & SIEGE) || ($group[$idx][FLAGS] & HEALER))
- {
- if($flags == DEBUG)
- echo "Siege units or healers can't be healed<br/>\n";
- continue;
- }
- // damaged units count as wounded too
- $wounded[$idx] = $units[$this_troopid][$group[$idx][UNITID]] - floor ($troops[$round+1][$idx]);
- if ($flags == DEBUG)
- echo "has ".$wounded[$idx]." wounded units<br/>\n";
- }
- if (array_sum($wounded) > 0)
- {
- // now we can heal
- foreach ($idmap[$this_troopid] as $idx)
- {
- if($flags == DEBUG)
- echo $group[$idx][NAME].": ";
- // Siege units can't be healed, healer units can't be healed
- if (($group[$idx][FLAGS] & SIEGE) || ($group[$idx][FLAGS] & HEALER))
- {
- if($flags == DEBUG)
- echo "Siege units or healers can't be healed<br/>\n";
- continue;
- }
- $to_heal = min($wounded[$idx], floor($heals *($wounded[$idx] / array_sum($wounded))));
- // heal!
- $troops[$round+1][$idx] = $units[$this_troopid][$group[$idx][UNITID]] - $wounded[$idx] + $to_heal;
- $heals -= $to_heal;
- $wounded[$idx] = 0;
- if ($flags == DEBUG)
- echo "has ".$troops[$round+1][$idx].", after healing ".$to_heal." units<br/>\n";
- }
- if($flags == DEBUG)
- echo "balance heals: ".$heals."<br>\n";
- } elseif($flags == DEBUG)
- echo "no wounded units<br/>\n";
- } elseif ($flags == DEBUG)
- echo "no healer survived<br/>\n";
- }
- }elseif($flags == DEBUG)
- echo "no healers <br/>\n";
- return $troops;
- }
- ?>