Files
TravianZ/GameEngine/Battle.php
T
novgorodschi catalin e03b7c9037 Fix hero system after refactor
Fix hero system after refactor
2026-05-18 11:12:15 +03:00

1459 lines
41 KiB
PHP

<?php
#################################################################################
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
## --------------------------------------------------------------------------- ##
## Project: TravianZ ##
## Version: 08.05.2026 ##
## Filename: Battle.php ##
## Developed by: Dzoki & Dixie ##
## Refactored by: Shadow (cata7007@gmail.com) ##
## 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: https://travianz.org ##
## https://github.com/Shadowss/TravianZ ##
## ##
#################################################################################
class Battle {
/*****************************************
Function sigma
*****************************************/
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;
/******************************************************************
* 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'];
/******************************************************************
* TARGET BUILD
******************************************************************/
$target = [];
for ($i = 1; $i <= 5; $i++) {
if (!empty($post['a2_v'.$i])) {
$target[] = $i;
}
}
$_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;
}
$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;
}
}
}
/******************************************************************
* 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];
}
} else {
$defender['u'.$i] = 0;
$def_ab[$i] = 0;
}
}
/******************************************************************
* BASIC VALUES
******************************************************************/
$deftribe = (int)$post['tribe'];
$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(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'];
}
$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'];
}
} 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);
}
$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;
}
}
}
/******************************************************************
* ATTACKER UNIT CALCULATION
******************************************************************/
$start = ($att_tribe - 1) * 10 + 1;
$end = $att_tribe * 10;
if ($type == 1) {
$abcount = ($att_tribe == 3) ? 3 : 4;
$scoutAB = ${'att_ab'.$abcount};
for ($i = $start; $i <= $end; $i++) {
$unitAmount = (int)$Attacker['u'.$i];
if ($unitAmount <= 0) {
continue;
}
global ${'u'.$i};
if ($i == 4 || $i == 14 || $i == 23 || $i == 44) {
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;
}
}
$involve += $unitAmount;
$units['Att_unit'][$i] = $unitAmount;
}
$ap *= $attacker_artefact;
} else {
$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
];
$abcount = 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++;
}
/******************************************************************
* HERO OFFENSE
******************************************************************/
if (!empty($Attacker['uhero']) && !empty($atkhero)) {
$units['Att_unit']['hero'] = $Attacker['uhero'];
$ap *= $atkhero['ob'];
$cap *= $atkhero['ob'];
$ap += $atkhero['atk'];
}
if ($offhero > 0 || $hero_strenght > 0) {
$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) {
$result['casualties_attacker'][11] = 1;
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"
);
}
}
}
/******************************************************************
* HERO DAMAGE (DEFENDER)
******************************************************************/
if (!empty($units['Def_unit']['hero']) && !empty($defenderhero['heroid'])) {
$hero_id = (int)$defenderhero['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[2]);
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 = " . $hero_id . "
LIMIT 1"
);
} else {
$result['deadherodef'] = 0;
mysqli_query(
$database->dblink,
"UPDATE " . TB_PREFIX . "hero
SET health = health - " . (int)$damage_health . "
WHERE heroid = " . $hero_id . "
LIMIT 1"
);
}
}
}
/******************************************************************
* HERO DAMAGE (DEFENDER + REINFORCEMENTS)
******************************************************************/
if (!empty($DefendersAll)) {
$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"
);
}
}
}
/******************************************************************
* BOUNTY CALCULATION
******************************************************************/
$max_bounty = 0;
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;
}
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;
}
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;
}
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;
?>