PageRenderTime 60ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/php/battle.php

https://bitbucket.org/obsidian/selador
PHP | 1407 lines | 1043 code | 188 blank | 176 comment | 335 complexity | 4df5b6593ec0955b02d2ca0c6cf574d0 MD5 | raw file
Possible License(s): AGPL-3.0, LGPL-2.1
  1. <?php
  2. /* battle.php - Back-end code implementing a battle
  3. *
  4. * Copyright (C) 2006, 2007, 2008 Kevin Read, Simone Schaefer
  5. *
  6. * This file is part of Selador, a browser-based fantasy strategy game
  7. *
  8. * This program is distributed under the terms of the GNU Affero General Public License.
  9. *
  10. *
  11. * Selador is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Affero General Public License as published by
  13. * the Free Software Foundation, either version 3 of the License, or
  14. * any later version.
  15. *
  16. * Selador is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU Affero General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License
  22. * along with Selador. If not, see <http://www.gnu.org/licenses/>.
  23. **/
  24. // Works well but is really simple.
  25. require_once ("functions.inc.php");
  26. define ("GROUP", 50);
  27. define ("UNITID", 51);
  28. define ("TYPE", 52);
  29. define ("SIDE", 53);
  30. define ("AMOUNT", 54);
  31. define ("NAME", 55); // FIXME: Might want to take this out later on...
  32. define ("HP", 60);
  33. define ("ATT", 61);
  34. define ("DAMAGE", 62);
  35. define ("INI", 63);
  36. define ("FLAGS", 70);
  37. define ("UID", 71);
  38. define ("DEBUG", 1);
  39. define ("SQUISH_NUM", 10);
  40. define ("FOOT_TARGETS", 2);
  41. define ("BOW_TARGETS", 3);
  42. define ("GFOOT", 1);
  43. define ("GHORSE", 2);
  44. define ("GBOW", 3);
  45. define ("GCOMMANDO", 4);
  46. define ("GSIEGE", 5);
  47. define ("GCHIVALRY", 6);
  48. define ("GWALL", 7); // Not a wall-hack
  49. define ("UNITS_PER_SIEGE_TOWER", 10);
  50. define ("HEALER", 256);
  51. define ("SIEGE", 4);
  52. $battle_flags = 0;
  53. $bonus = array ();
  54. /* There is the global groups array, containing all information about the assembled units
  55. except the combat sequence (who strikes when), which needs to be extracted from
  56. modified initiative, and the number of remaining troops, just called troops.
  57. The troops are stored in one big array indexed with the round number and the group id.
  58. When multiple units strike simultaneously, the troop changes are stored in a temp array and
  59. only applied when the simultaneous attacks are done. Tricky :) */
  60. // Pass the attacking group, and the type-array (referencing group ids) of candidates
  61. // Throws the dice, calculates damage, and distributes among the candidates
  62. // The same but for three target groups
  63. function do_attack_three ($lineno, &$owncount, $attacker, $group, $candidates, &$count, &$result, $attlimit = -1, $attmalus = 0)
  64. {
  65. global $battle_flags, $bonus;
  66. $totalcount = @array_sum ($count);
  67. // print_r ($count);
  68. if ($totalcount > 0)
  69. {
  70. $attgroup = $group[$attacker];
  71. $attuid = $attgroup[UID];
  72. if ($totalcount < $result[$attacker] / SQUISH_NUM) // small groups are just squashed FIXME: Add a static amount of attackers below instead
  73. $force = $result[$attacker];
  74. else
  75. {
  76. if ($attgroup[TYPE] == GBOW)
  77. $force = min ($result[$attacker], min ($result[$attacker], $totalcount * BOW_TARGETS) + ($result[$attacker] / 8));
  78. else
  79. $force = min ($result[$attacker], min ($result[$attacker], $totalcount * FOOT_TARGETS) + ($result[$attacker] / 8));
  80. }
  81. if ($attlimit != -1)
  82. $force = min ($attlimit, $force);
  83. if ($attmalus != 0)
  84. $attack = max (1, ($attgroup[ATT] - $attmalus));
  85. else
  86. $attack = ($attgroup[ATT] - $attmalus);
  87. $damage = $attack/100 * $force * $attgroup[DAMAGE];
  88. if ($battle_flags & DEBUG) echo "<u>".$lineno."</u>: ".$force." (".$result[$attacker]."), malus ".$attmalus." gives ".$attack.", limit ".$attlimit." attack for ".$damage." damage<br>";
  89. if (is_array ($candidates))
  90. {
  91. foreach ($candidates as $this_type => $this_cand)
  92. {
  93. foreach ($this_cand as $this_group)
  94. {
  95. if ($count[$this_type] > 0)
  96. {
  97. $defuid = $group[$this_group][UID];
  98. $partdam = ($damage * $result[$this_group] / $totalcount);
  99. // Check if the attacking group has any specific bonuses against this target
  100. // group
  101. 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";
  102. // Check if attacker has horse defense bonus, is defender and target is horse
  103. if (($group[$this_group][TYPE] == GHORSE) && ($attgroup[SIDE] == 1))
  104. {
  105. if ($bonus[$attuid][HERO3] > 1)
  106. {
  107. if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO3]." defense against large enemies<br>\n";
  108. $partdam *= $bonus[$attuid][HERO3];
  109. }
  110. if ($attgroup[FLAGS] & 2)
  111. {
  112. $partdam *= 1.333;
  113. if ($battle_flags & DEBUG) echo "Invoked special defense against large enemies<br>\n";
  114. }
  115. }
  116. // Check if attacker has offense bonus against archers and target is archer
  117. if ($group[$this_group][TYPE] == GBOW)
  118. {
  119. if ($bonus[$attuid][HERO1] > 1)
  120. {
  121. if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO1]." defense against large enemies<br>\n";
  122. $partdam *= $bonus[$attuid][HERO1];
  123. }
  124. if ($attgroup[FLAGS] & 128)
  125. {
  126. $partdam *= 1.333;
  127. if ($battle_flags & DEBUG) echo "Invoked special defense against archers<br>\n";
  128. }
  129. }
  130. // Check if attacker has universal bonus against siege units and target is siege
  131. if ($group[$this_group][FLAGS] & 4)
  132. {
  133. if ($bonus[$attuid][HERO4] > 1)
  134. {
  135. if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO4]." defense against large enemies<br>\n";
  136. $partdam *= $bonus[$attuid][HERO4];
  137. }
  138. if ($attgroup[FLAGS] & 512)
  139. {
  140. $partdam *= 1.333;
  141. if ($battle_flags & DEBUG) echo "Invoked special universal against siege<br>\n";
  142. }
  143. }
  144. // Check if target has resistance against archery and attacker is archer
  145. if ($attgroup[TYPE] == GBOW)
  146. {
  147. if ($bonus[$defuid][HERO2] > 1)
  148. {
  149. if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$defuid][HERO2]." resistance against archers<br>\n";
  150. $partdam /= $bonus[$defuid][HERO2];
  151. }
  152. if ($group[$this_group][FLAGS] & 1024)
  153. {
  154. $partdam *= 0.6667;
  155. if ($battle_flags & DEBUG) echo "Invoked special resistance against archers<br>\n";
  156. }
  157. }
  158. $cas = min ($partdam / $group[$this_group][HP], $result[$this_group]);
  159. 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";
  160. $result[$this_group] = round ($result[$this_group] - $cas, 3);
  161. if ($battle_flags & DEBUG) echo "Group ".$group[$this_group][NAME]." (".$this_group.") loose ".$cas."<br>";
  162. $count[$this_type] = round ($count[$this_type] - $cas, 3);
  163. }
  164. }
  165. }
  166. }
  167. else
  168. {
  169. if ($battle_flags == DEBUG)
  170. {
  171. echo "No 3nd array. Count:<br>\n";
  172. print_r ($count);
  173. }
  174. }
  175. }
  176. else
  177. {
  178. if ($battle_flags == DEBUG)
  179. {
  180. echo "<b>Attacking empty group: </b>";
  181. print_r ($candidates);
  182. }
  183. }
  184. }
  185. // And again for two target groups
  186. function do_attack_simul_three ($lineno, &$owncount, $attacker, $group, $candidates, $count, &$tempcount, &$owntempcount, $input, &$result, $attlimit = -1, $attmalus = 0)
  187. {
  188. global $battle_flags, $bonus;
  189. $totalcount = array_sum ($count);
  190. // print_r ($count);
  191. if ($totalcount > 0)
  192. {
  193. $attgroup = $group[$attacker];
  194. $attuid = $attgroup[UID];
  195. if ($totalcount < $input[$attacker] / SQUISH_NUM) // small groups are just squashed
  196. $force = $input[$attacker];
  197. else
  198. {
  199. if ($attgroup[TYPE] == GBOW)
  200. $force = min ($input[$attacker], min ($input[$attacker], $totalcount * BOW_TARGETS) + ($input[$attacker] / 8));
  201. else
  202. $force = min ($input[$attacker], min ($input[$attacker], $totalcount * FOOT_TARGETS) + ($input[$attacker] / 8));
  203. }
  204. if ($attlimit != -1)
  205. $force = min ($attlimit, $force);
  206. if ($attmalus != 0)
  207. $attack = max (1, ($attgroup[ATT] - $attmalus));
  208. else
  209. $attack = ($attgroup[ATT] - $attmalus);
  210. $damage = $attack/100 * $force * $attgroup[DAMAGE];
  211. 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>";
  212. if (is_array ($candidates))
  213. {
  214. foreach ($candidates as $this_type=>$this_cand)
  215. {
  216. if ($count[$this_type] > 0)
  217. {
  218. foreach ($this_cand as $this_group)
  219. {
  220. $defuid = $group[$this_group][UID];
  221. $partdam = ($damage * $result[$this_group] / $totalcount);
  222. // Check if the attacking group has any specific bonuses against this target
  223. // group
  224. 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";
  225. // Check if attacker has horse defense bonus, is attacker and target is horse
  226. if (($group[$this_group][TYPE] == GHORSE) && ($attgroup[SIDE] == 1))
  227. {
  228. if ($bonus[$attuid][HERO3] > 1)
  229. {
  230. if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO3]." defense damage bonus against large enemies<br>\n";
  231. $partdam *= $bonus[$attuid][HERO3];
  232. }
  233. if ($attgroup[FLAGS] & 2)
  234. {
  235. $partdam *= 1.333;
  236. if ($battle_flags & DEBUG) echo "Invoked special defense damage bonus against large enemies<br>\n";
  237. }
  238. }
  239. // Check if attacker has offense bonus against archers and target is archer
  240. if ($group[$this_group][TYPE] == GBOW)
  241. {
  242. if ($bonus[$attuid][HERO1] > 1)
  243. {
  244. if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO1]." universal against archers<br>\n";
  245. $partdam *= $bonus[$attuid][HERO1];
  246. }
  247. if ($attgroup[FLAGS] & 128)
  248. {
  249. $partdam *= 1.333;
  250. if ($battle_flags & DEBUG) echo "Invoked special universal against archers<br>\n";
  251. }
  252. }
  253. // Check if attacker has universal bonus against siege units and target is siege
  254. if ($group[$this_group][FLAGS] & 4)
  255. {
  256. if ($bonus[$attuid][HERO4] > 1)
  257. {
  258. if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$attuid][HERO4]." universal against siege<br>\n";
  259. $partdam *= $bonus[$attuid][HERO4];
  260. }
  261. if ($attgroup[FLAGS] & 512)
  262. {
  263. $partdam *= 1.333;
  264. if ($battle_flags & DEBUG) echo "Invoked special universal against siege<br>\n";
  265. }
  266. }
  267. // Check if target has resistance against archery and attacker is archer
  268. if ($attgroup[TYPE] == GBOW)
  269. {
  270. if ($bonus[$defuid][HERO2] > 1)
  271. {
  272. if ($battle_flags & DEBUG) echo "Invoked ".$bonus[$defuid][HERO2]." resistance against archers<br>\n";
  273. $partdam /= $bonus[$defuid][HERO2];
  274. }
  275. if ($group[$this_group][FLAGS] & 1024)
  276. {
  277. $partdam *= 0.6667;
  278. if ($battle_flags & DEBUG) echo "Invoked special resistance against archers<br>\n";
  279. }
  280. }
  281. $cas = min ($partdam / $group[$this_group][HP], $result[$this_group]);
  282. 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";
  283. $result[$this_group] = round ($result[$this_group] - $cas, 3);
  284. if ($battle_flags == DEBUG) echo "Group ".$group[$this_group][NAME]." (".$this_group.") loose ".$cas."<br>";
  285. $tempcount[$this_type] = round ($tempcount[$this_type] - $cas, 3);
  286. }
  287. }
  288. }
  289. }
  290. }
  291. else
  292. {
  293. if ($battle_flags == DEBUG)
  294. {
  295. echo "<b>Attacking empty group: </b>";
  296. print_r ($candidates);
  297. }
  298. }
  299. }
  300. // Concoct a units array suitable for the do_battle function from an attackers
  301. // troop id and a target village id
  302. function prepare_battle ($attuid, $troopid, $villageid, $hero, &$armyinfo, $attacker_surprised, $type=ATTACK, $grattid=0)
  303. {
  304. global $debug;
  305. // If spies were surprised while they where spying, they get severe mali
  306. if ($attacker_surprised)
  307. {
  308. if ($debug)
  309. {
  310. echo "Attacker with UID ".$attuid." is seriously surprised!<br>\n";
  311. }
  312. $surprise_malus = .1;
  313. }
  314. else
  315. $surprise_malus = 1;
  316. // Prime the attack group pool - even a single attacker is a group here :)
  317. $groupmembers[$troopid] = $attuid;
  318. // First check if this attack involves an attacking group or a single player
  319. if (($type == GROUPATTACK) && ($grattid > 0))
  320. {
  321. $query = "select uid,troopid from groupatt_members where grattid=".$grattid;
  322. log_debug ("grattquery: ".$query);
  323. if (!($res = mysql_query ($query)))
  324. log_err ("Cannot fetch group members in prepare_battle. Query: ".$query.", ".mysql_error ());
  325. else
  326. {
  327. // Then add all members to the pool
  328. $groupmembers = array ();
  329. while ($row = mysql_fetch_array ($res))
  330. $groupmembers[$row['troopid']] = $row['uid'];
  331. }
  332. // And formulate the query
  333. $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";
  334. }
  335. else
  336. $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";
  337. if ($debug)
  338. echo $query;
  339. $wallres_level = 0;
  340. log_debug ("prepquery: ".$query);
  341. if (!($result = mysql_query ($query)))
  342. {
  343. log_err ("prepare_battle: query ".$query." failed. Error: ".mysql_error ());
  344. return NULL;
  345. }
  346. // It might happen that there are no troops in the target village at all
  347. // In this case we won't learn the ally id here
  348. while ($row = mysql_fetch_array ($result))
  349. {
  350. $uid = $row['uid'];
  351. $troopid = $row['troopid'];
  352. $go_on = false;
  353. // Using the pool we can now sort troops at the location into attackers, defenders, and not-involved-guys
  354. if (isset ($groupmembers[$troopid]) && ($row['flag'] != 0))
  355. {
  356. $armyinfo[$uid][2000] = $units[$troopid][2000] = 0;
  357. $go_on = true;
  358. }
  359. if ((!in_array ($row['uid'], $groupmembers)) && ($row['flag'] == 0)) // Which might mean home troops or supporting troops
  360. {
  361. // This unit is in the village that is being attacked
  362. $armyinfo[$uid][2000] = $units[$troopid][2000] = 1;
  363. $go_on = true;
  364. // Now check if this is the uid of the defender
  365. if ($row['villageid'] == $villageid)
  366. $defuid = $row['uid'];
  367. }
  368. if ($go_on)
  369. {
  370. $units[$troopid][2001] = $uid;
  371. $units[$troopid][2002] = $row['villageid'];
  372. // Heroes also fight, eh?
  373. if (($row['unitid'] == 1000) && ($row['amount'] > 0))
  374. {
  375. // The defending hero might have healed
  376. if ($row['uid'] == $defuid)
  377. {
  378. // But first check if he is alive at all
  379. if ((round ($hero->health, 2) > 0) && ($hero->location == $villageid))
  380. $units[$troopid][1000] = $hero->health / $row['hp'];
  381. }
  382. else
  383. $units[$troopid][1000] = $row['hero_health'] / $row['hp'];
  384. if ($debug)
  385. {
  386. echo "Hero for UID ".$row['uid']." TID ".$row['troopid']." has ".$units[$troopid][1000]." HP";
  387. }
  388. }
  389. else // Otherwise these are normal units
  390. $units[$troopid][$row['unitid']] = $row['amount'];
  391. if (!isset ($armyinfo[$uid][2003]))
  392. {
  393. $armyinfo[$uid][2003] = $row['aid'];
  394. $armyinfo[$uid][3000] = $armyinfo[$uid][3001] = $armyinfo[$uid][3002] = $armyinfo[$uid][3003] = 0;
  395. $armyinfo[$uid][3500] = $row['hp'];
  396. $armyinfo[$uid][3501] = $row['att'];
  397. $armyinfo[$uid][3502] = $row['ini'];
  398. $armyinfo[$uid][3503] = $row['damage'];
  399. $armyinfo[$uid][3504] = $row['capacity'];
  400. $armyinfo[$uid][3505] = $row['regen'];
  401. $armyinfo[$uid][3506] = $row['fight1'] * RESEARCH_STEP; // + att vs archers
  402. $armyinfo[$uid][3507] = $row['fight2'] * RESEARCH_STEP; // + res vs archers
  403. $armyinfo[$uid][3508] = $row['fight3'] * RESEARCH_STEP; // + for def + res vs riders
  404. $armyinfo[$uid][3509] = $row['fight4'] * RESEARCH_STEP; // + att vs siege
  405. $armyinfo[$uid][3510] = $row['fight5'] * RESEARCH_STEP; // initiative +
  406. $armyinfo[$uid][3511] = $row['build3'] * RESEARCH_STEP; // other walls -
  407. $armyinfo[$uid][3512] = $row['build4'] * RESEARCH_STEP; // own walls +
  408. $armyinfo[$uid][3513] = $row['knowledge1'] * RESEARCH_STEP; // hide own gold better
  409. $armyinfo[$uid][3514] = $row['knowledge2'] * RESEARCH_STEP; // loot more gold
  410. $armyinfo[$uid][3520] = $row['class'];
  411. // Fighters gain three levels of attack research
  412. if ($row['class'] == 1)
  413. {
  414. log_debug ("UID ".$uid." gains fighter bonus in this battle");
  415. $armyinfo[$uid][3000] = 15;
  416. }
  417. }
  418. }
  419. }
  420. foreach ($armyinfo as $uid=>$this_unit)
  421. {
  422. $uids[] = $uid;
  423. }
  424. $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).")";
  425. if (!($res = mysql_query ($query)))
  426. {
  427. log_err ("Cannot fetch combat participant research levels. Query: ".$query.", ".mysql_error());
  428. }
  429. else
  430. {
  431. while ($row = mysql_fetch_array ($res))
  432. {
  433. $uid = $row['uid'];
  434. switch ($row['type'])
  435. {
  436. case 7: // chivalry research
  437. $armyinfo[$uid][3006] = $row['level'] * RESEARCH_STEP;
  438. break;
  439. case 6: // siege research
  440. $armyinfo[$uid][3005] = $row['level'] * RESEARCH_STEP;
  441. break;
  442. case 5: // horse research
  443. $armyinfo[$uid][3004] = $row['level'] * RESEARCH_STEP;
  444. break;
  445. case 4: // commando research
  446. $armyinfo[$uid][3003] = $row['level'] * RESEARCH_STEP;
  447. break;
  448. case 3: // archery research
  449. $armyinfo[$uid][3002] = $row['level'] * RESEARCH_STEP;
  450. break;
  451. case 2: // foot research
  452. $armyinfo[$uid][3001] = $row['level'] * RESEARCH_STEP;
  453. break;
  454. case 8: // off training
  455. if ($armyinfo[$uid][2000] == 0) // Only for attackers
  456. $armyinfo[$uid][3000] += $row['level'] * RESEARCH_STEP;
  457. break;
  458. case 9: // def training
  459. if ($armyinfo[$uid][2000] == 1) // Only for defenders
  460. $armyinfo[$uid][3000] += $row['level'] * RESEARCH_STEP;
  461. break;
  462. case 17: // wall building
  463. if ($defuid == $row['uid']) // Only for the owner of the defender village
  464. $wallres_level = $row['level'];
  465. break;
  466. }
  467. }
  468. }
  469. $query = "select type,level from villbuild where type=11 and villageid=".$villageid;
  470. if (!($res = mysql_query ($query)))
  471. {
  472. log_err ("Cannot read wall level in combat prep. Query: ".$query.", ".mysql_error ());
  473. }
  474. else
  475. {
  476. if (($row = mysql_fetch_array ($res)))
  477. {
  478. $armyinfo[$defuid][4000] = $row['level'];
  479. $armyinfo[$defuid][4001] = $wallres_level;
  480. }
  481. }
  482. $batlog = "";
  483. foreach ($armyinfo as $this_uid=>$this_army)
  484. {
  485. $batlog .= "UID ".$this_uid.": Att+".$this_army[3000].", Dam+".$this_army[3001].", HP+".$this_army[3003].", Wall: ".$this_army[3004]."; ";
  486. }
  487. log_debug ($batlog);
  488. // print_r ($armyinfo);
  489. return ($units);
  490. }
  491. // Test if each side has more than zero units - if not, the battle is won automatically
  492. function checkup_battle ($parties)
  493. {
  494. $def_units = $att_units = 0;
  495. foreach ($parties as $this_party)
  496. {
  497. if ($this_party[2000] == 1)
  498. {
  499. foreach ($this_party as $key=>$amount)
  500. {
  501. if ($key <= 1000)
  502. $def_units += $amount;
  503. }
  504. }
  505. else
  506. {
  507. foreach ($this_party as $key=>$amount)
  508. {
  509. if ($key <= 1000)
  510. $att_units += $amount;
  511. }
  512. }
  513. }
  514. $num_units = array($att_units, $def_units);
  515. return ($num_units);
  516. }
  517. function check_wall_health (&$new_level, $wall_hp, $wall_hp_orig, $wall_res, &$wfoot, &$wbow)
  518. {
  519. global $debug;
  520. $old_level = $new_level;
  521. if ($wall_hp_orig != $wall_hp)
  522. {
  523. while ($new_level > 0)
  524. {
  525. $wall_hp_test = WallValues ($new_level, $wall_res, $wfoot, $wbow);
  526. if ($debug) echo "wall hp test for level ".$new_level.": ".$wall_hp_test." < ".$wall_hp."<br>\n";
  527. if ($wall_hp_test < $wall_hp)
  528. break;
  529. else
  530. $new_level--;
  531. }
  532. if ($debug) echo "Wall razed from ".$old_level." to ".$new_level."<br>\n";
  533. }
  534. $wfoot = $new_level * 4;
  535. $wbow = $new_level * 2;
  536. }
  537. define ("CMD_INIT", 1);
  538. define ("CMD_ATT_1", 2);
  539. define ("CMD_ATT_2", 3);
  540. define ("CMD_WATT", 10);
  541. define ("CMD_DONE", 100);
  542. /**
  543. The central battle function
  544. After precomputing a lot of arrays, we iterate through all unit groups sorted by initiative
  545. each round. Before each iteration, we decide about parameters for this round, like wall state
  546. and the targets for each unit type for each side. Then each unit group acts according to these decisions
  547. for this round. When a unit type has no more actions to do, we count this type as having finished
  548. When either all unit types have finished or the round limit is up, we end the loop.
  549. Each round statistics are saved about the remaining number of units for each group.
  550. In typical battles unit groups will have two target groups most of the time, i.e.
  551. footmen will fight against other footmen and enemy horses (that are trying to kill our
  552. archers). The enemy horse are fighting our bowmen and our foot.
  553. @param units -
  554. @param idmap - (call by reference)
  555. @param capacity - (call by reference)
  556. @param maxrounds - maximum number of round that will be fought
  557. @param armyinfo -
  558. @param num_units - array(att_units_num,def_units_num)
  559. @param flags - debug boolean.
  560. @param extra_results - gives extra results (call by reference).
  561. @param attacker_is_surprised - boolean set if attackers are spies.
  562. */
  563. function do_battle ($units, &$idmap, &$capacity, $maxrounds, $armyinfo, $num_units, $flags, &$extra_results, $attacker_is_surprised=false)
  564. {
  565. global $battle_flags, $unit_byrace, $bonus;
  566. $battle_flags = $flags;
  567. if ($battle_flags == DEBUG)
  568. {
  569. $cmd_names = array (1=>"INIT", 2=>"ATT1", 3=>"ATT2", 10=>"WATT", 100=>"DONE");
  570. }
  571. $players = count($armyinfo);
  572. $sides = 2;
  573. $fighting = true;
  574. $round = $idx = 0;
  575. $siege_towers = 0;
  576. $all_towers = array ();
  577. $horses_vuln = true;
  578. $std_targets[GFOOT] = array (GFOOT, GHORSE, GBOW);
  579. $std_targets[GHORSE] = array (GBOW, GFOOT, GHORSE);
  580. $std_targets[GBOW] = array (GFOOT, GHORSE, GBOW);
  581. $wall_std_targets[GFOOT] = array (GHORSE, GFOOT, GBOW); // Horse is all that matters here anyway
  582. $wall_std_targets[GBOW] = array (GFOOT, GBOW, GHORSE);
  583. $wall_std_targets[GHORSE] = array (GBOW, GHORSE, GFOOT);
  584. // Get all bonuses into a factor, so we can apply it more easily
  585. foreach ($armyinfo as $uid => $this_player)
  586. {
  587. // echo "Reading info for uid ".$uid."\n";
  588. $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;
  589. if ($this_player[3000] > 0)
  590. $bonus[$uid][ATT] += ($this_player[3000] / 100);
  591. if ($this_player[3001] > 0)
  592. $bonus[$uid][GFOOT] += ($this_player[3001] / 100);
  593. if ($this_player[3002] > 0)
  594. $bonus[$uid][GBOW] += ($this_player[3002] / 100);
  595. if ($this_player[3003] > 0)
  596. $bonus[$uid][GCOMMANDO] += ($this_player[3003] / 100);
  597. if ($this_player[3004] > 0)
  598. $bonus[$uid][GHORSE] += ($this_player[3004] / 100);
  599. if ($this_player[3005] > 0)
  600. $bonus[$uid][GSIEGE] += ($this_player[3005] / 100);
  601. if ($this_player[3006] > 0)
  602. $bonus[$uid][GCHIVALRY] += ($this_player[3006] / 100);
  603. if ($this_player[3510] > 0)
  604. $bonus[$uid][INI] += ($this_player[3010] / 100);
  605. if ($this_player[3506] > 0)
  606. $bonus[$uid][HERO1] = 1 + ($this_player[3506] / 100);
  607. if ($this_player[3507] > 0)
  608. $bonus[$uid][HERO2] = 1 + ($this_player[3507] / 100);
  609. if ($this_player[3508] > 0)
  610. $bonus[$uid][HERO3] = 1 + ($this_player[3508] / 100);
  611. if ($this_player[3509] > 0)
  612. $bonus[$uid][HERO4] = 1 + ($this_player[3509] / 100);
  613. if (isset ($this_player[4000]))
  614. {
  615. if ($this_player[4000] > 0)
  616. {
  617. $wall = $this_player[4000];
  618. $wall_res = $this_player[4001];
  619. $wall_hero_bonus = 1 + ($this_player[3512] / 100);
  620. }
  621. }
  622. if (isset ($this_player[3511]) && ($this_player[3511] > 0))
  623. $bonus[$uid][GWALL] = $this_player[3511];
  624. }
  625. // We need a wall level
  626. if (!isset ($wall))
  627. $wall = 0;
  628. $wallroom = $wall * 72 * pow (1.2, $wall); // How many units can stand on a wall?
  629. if ($wall > 0)
  630. {
  631. $wall_hp = $wall_hp_orig = WallValues ($wall, $wall_res, $wfoot, $wbow);
  632. $wall_new = $wall;
  633. $whorse = $wall * 8 * $wall_hero_bonus;
  634. $wfoot = $wall * 4 * $wall_hero_bonus;
  635. $wbow = $wall * 2 * $wall_hero_bonus;
  636. $wall_breached = false;
  637. if ($battle_flags == DEBUG)
  638. {
  639. echo "Setting wall to ".$wall." (".$wall_hero_bonus." from hero), wfoot is ".$wfoot.", wbow is ".$wbow.", wallroom is ".$wallroom.", wall hp is ".$wall_hp;
  640. echo "<br>Comes from 1000 * pow (".WALL_HP_BASE." * (50 + ".$wall.") / 50, ".$wall_res.")";
  641. }
  642. }
  643. else
  644. {
  645. $wall_breached = true;
  646. $whorse = $wbow = $wfoot = $wall_hp = $wall_hp_orig = $wall_new = 0;
  647. }
  648. if ($flags == DEBUG)
  649. {
  650. echo "in_units:<br>\n";
  651. print_r ($units);
  652. echo "armyinfo:<br>\n";
  653. print_r ($armyinfo);
  654. echo "bonus:<br>\n";
  655. print_r ($bonus);
  656. }
  657. $sidecount[0] = $sidecount[1] = 0;
  658. // Initialize arrays for each unit type and side
  659. // Reference them from group arrays ordered by ini
  660. $query = "select unitid,type,ini,hp,att,damage,flags,name,capacity from units"; // FIXME: Might want to take name out later on
  661. if (!($res = mysql_query ($query)))
  662. log_err ("do_battle: Error with query: ".$query.": ".mysql_error());
  663. $unitcount = mysql_num_rows ($res);
  664. while ($row = mysql_fetch_array ($res))
  665. {
  666. foreach ($units as $troopid => $this_player)
  667. {
  668. if (isset ($this_player[$row['unitid']]))
  669. {
  670. if ($this_player[$row['unitid']] > 0)
  671. {
  672. $uid = $this_player[2001];
  673. $idmap[$troopid][$row['unitid']] = $idx;
  674. $group[$idx][UNITID] = $row['unitid'];
  675. $side = $group[$idx][SIDE] = $this_player[2000];
  676. $group[$idx][TYPE] = $row['type'];
  677. $group[$idx][NAME] = $row['name'];
  678. $group[$idx][ATT] = $row['att'] * $bonus[$uid][ATT];
  679. $group[$idx][FLAGS] = $row['flags'];
  680. $group[$idx][UID] = $uid;
  681. $this_research = 1;
  682. if (($row['flags'] & 16) || ($row['flags'] & 4))// siege?
  683. {
  684. $this_research = $bonus[$uid][GSIEGE];
  685. }
  686. else
  687. {
  688. if ($row['flags'] & 32) // commando?
  689. $this_research = $bonus[$uid][GCOMMANDO];
  690. else
  691. {
  692. if ($row['flags'] & 64) // chivalry?
  693. $this_research = $bonus[$uid][GCHIVALRY];
  694. else
  695. {
  696. // all other unit types can be selected according to type
  697. $this_research = $bonus[$uid][$row['type']];
  698. }
  699. }
  700. }
  701. if ($battle_flags == DEBUG)
  702. echo "Using bonus ".$this_research." (+".$bonus[$uid][ATT].") for unit ".$row['name']."<br>\n";
  703. $group[$idx][DAMAGE] = $row['damage'] * $bonus[$uid][DAMAGE] * $this_research;
  704. $group[$idx][INI] = round ($row['ini'] * $bonus[$uid][INI]);
  705. $group[$idx][HP] = $row['hp'] * $bonus[$uid][HP] * $this_research;
  706. $capacity[$row['unitid']] = $row['capacity'];
  707. if (($row['flags'] & 16) && ($side == 0)) // Test for siege tower
  708. {
  709. $siege_towers += $units[$uid][$row['unitid']]; // Add as many siege towers as this user has
  710. $all_towers[] = $idx;
  711. }
  712. else
  713. $all[$idx] = $group[$idx][INI];
  714. // Check all flags that apply to all targets group in battle, i.e. that are
  715. // not dependent on any attribute of a specific targets
  716. // This tests for defense bonus, only applies to defender of course
  717. if (($row['flags'] & 1) && ($side == 1))
  718. $group[$idx][ATT] *= 2.0;
  719. else
  720. {
  721. if (array_key_exists ($row['unitid'], $unit_byrace[1]) && ($side == 1))
  722. {
  723. if ($battle_flags == DEBUG)
  724. echo "Only human, bonus 20% for unit ".$row['name']."<br>\n";
  725. $group[$idx][ATT] *= 1.2;
  726. }
  727. }
  728. // Berserker units get battle rage bonus when pitched against large amounts of enemies
  729. if ($row['flags'] & 2048)
  730. {
  731. $ragebonus = min (15, max (0, ($num_units[1] / $num_units[0] - 0.5) * 0.5) + 1.0);
  732. $ragevuln = min (15, max (0, ($num_units[1] / $num_units[0] - 1) * 0.125) + 1.0);
  733. if ($battle_flags == DEBUG)
  734. echo "Using rage bonus ".$ragebonus.", HP vuln ".$ragevuln." for unit ".$row['name']."<br>\n";
  735. $group[$idx][ATT] *= $ragebonus;
  736. $group[$idx][HP] /= $ragevuln;
  737. }
  738. // Healer units can heal others after combat
  739. if ($row['flags'] & HEALER)
  740. {
  741. if ($battle_flags == DEBUG)
  742. echo "Set healer for unit ".$row['name']."<br>\n";
  743. $healers[$troopid][] = $idx;
  744. }
  745. $troops[1][$idx] = $this_player[$row['unitid']];
  746. // If the attacker was surprised, he starts out with losses
  747. if ($attacker_is_surprised && ($side == 0))
  748. {
  749. $instantkills = ceil ($troops[1][$idx] * (rand (5, 10) / 100));
  750. if ($battle_flags == DEBUG)
  751. echo "Instant-killing ".$instantkills." of ".$troops[1][$idx]." troops. Also dropping attack to ".($group[$idx][ATT] * .1)."<br>\n";
  752. $troops[1][$idx] -= $instantkills;
  753. $group[$idx][ATT] *= 0.1;
  754. // Also slow them, as they are surprised
  755. $group[$idx][INI] *= 4;
  756. }
  757. $command[$idx] = CMD_INIT;
  758. $initest[$group[$idx][INI]][] = $idx; // Check for units that have the same modified ini
  759. $bytypes[$side][$group[$idx][TYPE]][] = $idx; // Reference by type and side for easier targetting
  760. $bysides[$side][] = $idx; // Reference by side, in case we attack anything we see - FIXME: remove later
  761. if (!isset ($typecount[$side][$group[$idx][TYPE]]))
  762. $typecount[$side][$group[$idx][TYPE]] = $troops[1][$idx];
  763. else
  764. $typecount[$side][$group[$idx][TYPE]] += $troops[1][$idx];
  765. $sidecount[$side] += $troops[1][$idx];
  766. if ($group[$idx][TYPE] == GBOW)
  767. $missile[$idx] = $group[$idx][INI];
  768. $idx++;
  769. }
  770. }
  771. }
  772. }
  773. foreach ($units as $troopid => $this_player)
  774. {
  775. if (isset ($this_player[1000]))
  776. {
  777. if ($this_player[1000] > 0)
  778. {
  779. $uid = $this_player[2001];
  780. $idmap[$troopid][1000] = $idx;
  781. $group[$idx][UNITID] = 1000;
  782. $side = $group[$idx][SIDE] = $this_player[2000];
  783. $group[$idx][TYPE] = GFOOT;
  784. $group[$idx][NAME] = "Held";
  785. $group[$idx][HP] = $armyinfo[$uid][3500];
  786. $group[$idx][ATT] = $armyinfo[$uid][3501];
  787. $group[$idx][DAMAGE] = $armyinfo[$uid][3503];
  788. $group[$idx][INI] = $armyinfo[$uid][3502];
  789. $group[$idx][FLAGS] = 0;
  790. $group[$idx][UID] = $uid;
  791. $capacity[1000+$uid] = $armyinfo[$uid][3504];
  792. $troops[1][$idx] =$this_player[1000];
  793. $command[$idx] = CMD_INIT;
  794. $all[$idx] = $group[$idx][INI];
  795. $initest[$group[$idx][INI]][] = $idx; // Check for units that have the same modified ini
  796. $bytypes[$side][$group[$idx][TYPE]][] = $idx; // Reference by type and side for easier targetting
  797. $bysides[$side][] = $idx; // Reference by side, in case we attack anything we see - FIXME: remove later
  798. $typecount[$side][$group[$idx][TYPE]] += $troops[1][$idx];
  799. $sidecount[$side] += $troops[1][$idx];
  800. if ($group[$idx][TYPE] == GBOW)
  801. $missile[$idx] = $group[$idx][INI];
  802. if ($battle_flags == DEBUG)
  803. {
  804. echo "<pre>Hero is\n";
  805. print_r ($group[$idx]);
  806. echo "</pre>";
  807. }
  808. $idx++;
  809. }
  810. }
  811. }
  812. if ($battle_flags == DEBUG)
  813. {
  814. echo "<br>Sidecount: <br>";
  815. print_r ($sidecount);
  816. echo "<br>Typecount: <br>";
  817. print_r ($typecount);
  818. echo "<br>By types: <br>";
  819. print_r ($bytypes);
  820. echo "<br>Towers: <br>";
  821. print_r ($all_towers);
  822. }
  823. // Evil actually: Test each initiative value for the amount of units that have this value
  824. // If only one unit has this ini, we delete the initest. In all other cases we have the group IDs
  825. // of units that "sit" on this ini together
  826. foreach ($initest as $idx => $this_initest)
  827. {
  828. if (count ($this_initest) == 1)
  829. unset ($initest[$idx]);
  830. }
  831. arsort ($all, SORT_NUMERIC);
  832. if (isset ($missile))
  833. arsort ($missile, SORT_NUMERIC);
  834. $starttypecount = $typecount;
  835. // This is the main loop of battle. It ends when the battle ends - surprise
  836. while ($fighting)
  837. {
  838. // Count the active round, since combat ends after a few rounds
  839. $round++;
  840. if ($flags == DEBUG) echo "<p><b>Round ".$round."</b><br>\n";
  841. // More on temp state later on
  842. $tempgroupini = -1;
  843. // If we have archer units, they can fire in the first round before melee starts.
  844. // If this happens, we add another round to combat length.
  845. if (1 == $round)
  846. {
  847. if (isset ($missile))
  848. {
  849. $list = $missile; // In the first round of combat, only the bowmen attack
  850. $maxrounds++; // Reflect the fact that first round of combat wasn't "real"
  851. }
  852. else
  853. $list = $all; // Otherwise, we iterate through all groups later on
  854. }
  855. else
  856. $list = $all;
  857. if ($flags == DEBUG)
  858. {
  859. echo "List of attackers, sorted:<br>\n";
  860. print_r ($list);
  861. echo "<br>\n";
  862. }
  863. // Iterate through all groups
  864. foreach ($list as $attgroupid => $thisini)
  865. {
  866. // Delete killed groups from the list of attacking units
  867. if ($troops[$round][$attgroupid] == 0)
  868. unset ($list[$attgroupid]);
  869. else
  870. {
  871. // If this group is still alive, switch the active side to its side
  872. $att = (int)$group[$attgroupid][SIDE];
  873. // And the target to the other side
  874. $target = $att ^ 1;
  875. // Only go through the attack sequence if this unit has a damage rating, except
  876. // when there is a wall to bash!
  877. if ((($group[$attgroupid][DAMAGE] == 0) || ($troops[$round][$attgroupid] == 0)) && !$wall)
  878. continue;
  879. if ($flags == DEBUG)
  880. {
  881. echo "<p>Attacker is ".$att.", target is ".$target.", Attacking group: ".$group[$attgroupid][NAME]." (".$attgroupid.")<br>\n";
  882. echo "Targets type count is<br>\n";
  883. if (isset ($typecount[$target]))
  884. print_r ($typecount[$target]);
  885. echo "<br>";
  886. }
  887. $this_uid = $group[$attgroupid][UID];
  888. if ($flags == DEBUG) echo "Attacker with uid ".$this_uid." has wallhack ".$bonus[$this_uid][GWALL]."<br>";
  889. // Temp is a state, when some groups in the battle attack at the same time.
  890. // During this time casualties are stored in a copy of the troops array, but of this round only.
  891. // This copy is called temptroops. tempgroupini records the initiative value for the
  892. // simultaneous attacks, so we can see when the temp state ends.
  893. // First we check if we are in temp state
  894. if ($tempgroupini != -1)
  895. {
  896. if ($flags == DEBUG) echo "Temp group found<br>";
  897. // Then check if temp state hasn't ended
  898. if ($thisini != $tempgroupini)
  899. {
  900. // Yes, it has.
  901. if ($flags == DEBUG)
  902. {
  903. echo "Temp group ini ".$tempgroupini." != ".$thisini.", deleting.<br>";
  904. print_r ($troops[$round]);
  905. }
  906. // Copy the collected values back into the troops array for this round
  907. $troops[$round] = $temptroops;
  908. $typecount = $tempcount;
  909. if ($flags == DEBUG)
  910. {
  911. echo "<br>New: <br>";
  912. print_r ($troops[$round]);
  913. echo "<br>:New typecount: <br";
  914. print_r ($typecount);
  915. }
  916. // And delete the temp state infos
  917. $tempgroupini = -1;
  918. $temptroops = null;
  919. $tempcount = null;
  920. }
  921. }
  922. // Now we check if we have to start temp state, which happens
  923. // if this group attacks simultaneously with other groups
  924. if (isset ($initest[$thisini]))
  925. {
  926. if ($flags == DEBUG) echo "Attacker with ini ".$thisini." is in a sim group<br>";
  927. // We only need a new temp troop array if we don't already have one for this timeslice
  928. if ($tempgroupini == -1)
  929. {
  930. if ($flags == DEBUG) echo "No temp group found, creating<br>";
  931. // First we record this ini value, so we know when to copy back the temp troops
  932. $tempgroupini = $thisini;
  933. $temptroops = $troops[$round];
  934. $tempcount = $typecount;
  935. }
  936. }
  937. // Per default there are no siege users (units that use a siege tower to
  938. // overcome the wall)
  939. $siege_users = 0;
  940. // Save type for easy access
  941. $this_type = $group[$attgroupid][TYPE];
  942. $malus = 0;
  943. // $attack_already_done = false;
  944. // Now we calculate the malus for this particular group.
  945. // This depends primarily on the wall, the side and then the type.
  946. if ($wall)
  947. {
  948. // If this is the attacker, we dish out a malus if these are non-archery
  949. if ($att == 0)
  950. {
  951. // First we check for special units, for now only catapults and rams
  952. // They attack the wall if it stands, otherwise they attack units
  953. if (($group[$attgroupid][FLAGS] == 4) && ($wall_hp > -($wall_hp_orig/3)))
  954. {
  955. if ($flags == DEBUG) echo "<br>\nWallatt: ".$group[$attgroupid][DAMAGE]." attack wall with ".$wall_hp." HP";
  956. $wall_hp = $wall_hp - ($troops[$round][$attgroupid] * $group[$attgroupid][DAMAGE]);
  957. if ($flags == DEBUG) echo "<br>\nWallatt: ".$group[$attgroupid][DAMAGE]." wall now has ".$wall_hp." HP left<br>";
  958. // Next, see how the wall fares
  959. check_wall_health ($wall_new, $wall_hp, $wall_hp_orig, $wall_res, $wfoot, $wbow);
  960. // They don't fight people now
  961. continue;
  962. }
  963. // We might have the state here that only the wall is left standing, so we skip other units
  964. if ($sidecount[$target] == 0)
  965. continue;
  966. // If there is a wall, siege units will help the attacker overcome the wall
  967. // This only helps foot units of course
  968. if ($this_type == GFOOT)
  969. {
  970. // We first calculate the number of units that can use the siege towers
  971. $siege_towers = 0;
  972. // For this we run through the list of all siege towers (which we recorded
  973. // during unit setup, remember?).
  974. foreach ($all_towers as $this_idx)
  975. $siege_towers += $troops[$round][$this_idx];
  976. // echo "<br>\nNum towers: ".$siege_towers.", typecount ".$typecount[$att][GFOOT]."<br>\n";
  977. // If there are only siege units left, we can skip this
  978. if (($typecount[$att][GFOOT] - $siege_towers) > 0)
  979. $siege_users = min ($troops[$round][$attgroupid], ($siege_towers * UNITS_PER_SIEGE_TOWER) / ($typecount[$att][GFOOT] - $siege_towers) * $troops[$round][$attgroupid]);
  980. else
  981. $siege_users = 0;
  982. if ($flags == DEBUG)
  983. echo "min (".$troops[$round][$attgroupid].", min (".$siege_towers * UNITS_PER_SIEGE_TOWER.", ".($wallroom*2).") / (".$typecount[$att][GFOOT]." - ".$siege_towers.") * ".$troops[$round][$attgroupid].")";
  984. $nonsiege_users = $troops[$round][$attgroupid] - $siege_users;
  985. }
  986. if ($this_type != GBOW)
  987. $malus = $wfoot;
  988. else
  989. $malus = ($wbow / 2);
  990. // Attacking berserkers ignore the wall to 50%
  991. if ($group[$attgroupid][FLAGS] & 2048)
  992. $malus = $malus * 0.333;
  993. }
  994. else
  995. {
  996. // Defender archers get a bonus for attacking from the wall
  997. if (($this_type == GBOW) && !($group[$attgroupid][FLAGS] & 4))
  998. $malus = -$wbow;
  999. else
  1000. $malus = 0;
  1001. }
  1002. }
  1003. if ($flags == DEBUG)
  1004. echo "Malus for type ".$this_type." is ".$malus." - ".$bonus[$this_uid][GWALL]."\n";
  1005. $malus = max (0, $malus - $bonus[$this_uid][GWALL]);
  1006. // Now lets fight. If we don't have any malus, just go ahead
  1007. if ($malus == 0)
  1008. {
  1009. if ($temptroops)
  1010. do_attack_simul_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $tempcount[$target], $tempcount[$att], $troops[$round], $temptroops);
  1011. else
  1012. do_attack_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $troops[$round]);
  1013. }
  1014. else
  1015. {
  1016. // If we have guys in siege towers, do them first
  1017. if ($siege_users)
  1018. {
  1019. if ($temptroops)
  1020. {
  1021. do_attack_simul_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $tempcount[$target], $tempcount[$att], $troops[$round], $temptroops, $siege_users);
  1022. if ($nonsiege_users)
  1023. 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);
  1024. }
  1025. else
  1026. {
  1027. do_attack_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $troops[$round], $siege_users);
  1028. if ($nonsiege_users)
  1029. do_attack_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $troops[$round], $nonsiege_users, $malus);
  1030. }
  1031. }
  1032. else
  1033. {
  1034. if ($temptroops)
  1035. do_attack_simul_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $tempcount[$target], $tempcount[$att], $troops[$round], $temptroops, -1, $malus);
  1036. else
  1037. do_attack_three (__LINE__, $typecount[$att][$this_type], $attgroupid, $group, $bytypes[$target], $typecount[$target], $troops[$round], -1, $malus);
  1038. }
  1039. }
  1040. } // Check if this group is still alive
  1041. // To know how many units are still alive for each side,
  1042. // we just sum up all units that are stored in the (always up to
  1043. // date) count by type
  1044. $sidecount[$target] = @array_sum ($typecount[$target]);
  1045. $sidecount[$att] = array_sum ($typecount[$att]);
  1046. if ($flags == DEBUG)
  1047. {
  1048. echo "Summary:<br>";
  1049. print_r ($sidecount);
  1050. }
  1051. // The first end state is when one side has no more units,
  1052. // but only when temp state has ended (since in temp state
  1053. // attacks happen at the same time, they are still hitting each other)
  1054. if ((($sidecount[$target] == 0) && ($thisini != $tempgroupini)) && !$wall)
  1055. {
  1056. $fighting = false;
  1057. break;
  1058. }
  1059. }
  1060. // At the end of each round we also have to copy back temp state data, of course
  1061. if ($tempgroupini != -1)
  1062. {
  1063. // Yes, it has.
  1064. if ($flags == DEBUG)
  1065. {
  1066. echo "Temp group found at end of round, deleting.<br>";
  1067. print_r ($troops[$round]);
  1068. }
  1069. $troops[$round] = $temptroops;
  1070. $tempgroupini = -1;
  1071. $temptroops = null;
  1072. $typecount = $tempcount;
  1073. $tempcount = null;
  1074. if ($flags == DEBUG)
  1075. {
  1076. echo "<br>New: <br>";
  1077. print_r ($troops[$round]);
  1078. echo "<br>:New typecount: <br";
  1079. print_r ($typecount);
  1080. }
  1081. }
  1082. // Then we copy over the state of the troops as base data for the next round
  1083. $troops[$round+1] = $troops[$round];
  1084. if ($flags == DEBUG)
  1085. {
  1086. $outstr = "";
  1087. for ($temp = 0; $temp < $sides; $temp++)
  1088. {
  1089. echo "<table class=\"inhalt\"><tr>\n";
  1090. if (isset ($bysides[$temp]))
  1091. {
  1092. foreach ($bysides[$temp] as $this_group)
  1093. {
  1094. echo "<th><img src=\"gfx/u/".$group[$this_group][UNITID].".gif\">(".$this_group.")</th>\n";
  1095. $outstr .= "<td>".$troops[$round][$this_group]."</td>\n";
  1096. }
  1097. }
  1098. echo "</tr><tr>".$outstr."</tr></table>\n";
  1099. $outstr = "";
  1100. }
  1101. echo "<pre>";
  1102. print_r ($typecount);
  1103. echo "</pre>";
  1104. }
  1105. // If we are over the round limit, combat ends. This is the second
  1106. // end state.
  1107. if ($round >= $maxrounds)
  1108. $fighting = false;
  1109. } // while ($fighting) = main loop
  1110. // Check if wall was damaged, only then will we file it in the report
  1111. if ($wall_hp != $wall_hp_orig)
  1112. {
  1113. $extra_results[1] = $wall;
  1114. $extra_results[2] = $wall_new;
  1115. // The wall can be repaired to at least 50%
  1116. $wall_hp = max (0, min ($wall_hp_orig, $wall_hp + (float)($wall_hp_orig * 0.5)));
  1117. $wall_new = $wall;
  1118. if ($flags == DEBUG) echo "wall healed back to ".$wall_hp." of ".$wall_hp_orig." hp<br>\n";
  1119. // After combat, check the poor wall
  1120. check_wall_health ($wall_new, $wall_hp, $wall_hp_orig, $wall_res, $wfoot, $wbow);
  1121. $extra_results[3] = $wall_new;
  1122. if ($flags == DEBUG)
  1123. {
  1124. echo "extra results:<br>\n<pre>";
  1125. print_r ($extra_results);
  1126. echo "</pre>";
  1127. }
  1128. }
  1129. // After combat, healers get a chance to look at other units
  1130. if (isset ($healers))
  1131. {
  1132. if ($flags == DEBUG)
  1133. {
  1134. echo "<b>Healing</b><br/>\n";
  1135. print_r ($healers);
  1136. echo "<br/>\nHealers found, now iterating.<br/>\n";
  1137. }
  1138. // iterate over troops
  1139. foreach ($healers as $this_troopid=>$healer_list)
  1140. {
  1141. if ($flags == DEBUG)
  1142. echo "<br/>\nCalculating healing for troopid ".$this_troopid."<br/>\n";
  1143. $heals = 0;
  1144. // iterate over healers
  1145. foreach ($healer_list as $healer_idx)
  1146. {
  1147. // Only alive healers can heal :)
  1148. if ($troops[$round][$healer_idx] > 0)
  1149. {
  1150. // For each three healers one other can be healed
  1151. $heals += round ($troops[$round+1][$healer_idx] / 3);
  1152. }
  1153. }
  1154. if($heals > 0)
  1155. {
  1156. if ($flags == DEBUG)
  1157. echo $heals." units can be healed<br/>\n";
  1158. // count wounded units
  1159. $wounded = array();
  1160. foreach ($idmap[$this_troopid] as $idx)
  1161. {
  1162. if ($flags == DEBUG)
  1163. echo $group[$idx][NAME].": ";
  1164. // Siege units or healers can't be healed
  1165. if (($group[$idx][FLAGS] & SIEGE) || ($group[$idx][FLAGS] & HEALER))
  1166. {
  1167. if($flags == DEBUG)
  1168. echo "Siege units or healers can't be healed<br/>\n";
  1169. continue;
  1170. }
  1171. // damaged units count as wounded too
  1172. $wounded[$idx] = $units[$this_troopid][$group[$idx][UNITID]] - floor ($troops[$round+1][$idx]);
  1173. if ($flags == DEBUG)
  1174. echo "has ".$wounded[$idx]." wounded units<br/>\n";
  1175. }
  1176. if (array_sum($wounded) > 0)
  1177. {
  1178. // now we can heal
  1179. foreach ($idmap[$this_troopid] as $idx)
  1180. {
  1181. if($flags == DEBUG)
  1182. echo $group[$idx][NAME].": ";
  1183. // Siege units can't be healed, healer units can't be healed
  1184. if (($group[$idx][FLAGS] & SIEGE) || ($group[$idx][FLAGS] & HEALER))
  1185. {
  1186. if($flags == DEBUG)
  1187. echo "Siege units or healers can't be healed<br/>\n";
  1188. continue;
  1189. }
  1190. $to_heal = min($wounded[$idx], floor($heals *($wounded[$idx] / array_sum($wounded))));
  1191. // heal!
  1192. $troops[$round+1][$idx] = $units[$this_troopid][$group[$idx][UNITID]] - $wounded[$idx] + $to_heal;
  1193. $heals -= $to_heal;
  1194. $wounded[$idx] = 0;
  1195. if ($flags == DEBUG)
  1196. echo "has ".$troops[$round+1][$idx].", after healing ".$to_heal." units<br/>\n";
  1197. }
  1198. if($flags == DEBUG)
  1199. echo "balance heals: ".$heals."<br>\n";
  1200. } elseif($flags == DEBUG)
  1201. echo "no wounded units<br/>\n";
  1202. } elseif ($flags == DEBUG)
  1203. echo "no healer survived<br/>\n";
  1204. }
  1205. }elseif($flags == DEBUG)
  1206. echo "no healers <br/>\n";
  1207. return $troops;
  1208. }
  1209. ?>