60 ) { @unlink( AUTOMATION_LOCK_FILE_NAME ); } else { // automation file exists and is valid, don't run another automation exit; } } else { // create automation lock file file_put_contents( AUTOMATION_LOCK_FILE_NAME, '' ); } } } include_once("Database.php"); include_once("Data/buidata.php"); include_once("Data/unitdata.php"); include_once("Data/hero_full.php"); include_once("Units.php"); include_once("Battle.php"); include_once("Technology.php"); include_once("Ranking.php"); include_once("Generator.php"); include_once("Multisort.php"); include_once("Building.php"); include_once("Artifacts.php"); class Automation { /** * @var object The artifacts class, used to create Natars, artifacts and obtaining info about them */ private $artifacts; /** * Cache pentru utilizatori pentru a reduce query-urile duplicate * @var array */ private $userCache = []; public function __construct() { //Classes initialization $this->artifacts = new Artifacts(); $autoprefix = ""; for ($i = 0; $i < 5; $i++) { $autoprefix = str_repeat('../', $i); if (file_exists($autoprefix.'autoloader.php')) { // we have our path, let's leave break; } } $this->procNewClimbers(); $this->ClearUser(); $this->ClearInactive(); $this->pruneResource(); $this->pruneOResource(); $this->checkWWAttacks(); $this->delTradeRoute(); $this->TradeRoute(); $methodsArrays = ["culturePoints", "updateHero", "clearDeleting", "buildComplete", "demolitionComplete", "marketComplete", "researchComplete", "trainingComplete", "starvation", "celebrationComplete", "sendUnitsComplete", "loyaltyRegeneration", "sendreinfunitsComplete", "returnunitsComplete", "sendSettlersComplete", "spawnNatars", "spawnWWVillages", "spawnWWBuildingPlans", "activateArtifacts"]; foreach($methodsArrays as $method){ $file = fopen($autoprefix."GameEngine/Prevention/".$method.".txt", "w"); if(flock($file, LOCK_EX)) { call_user_func(array($this, $method)); flock($file, LOCK_UN); } fclose($file); } $this->MasterBuilder(); $this->updateGeneralAttack(); $this->checkInvitedPlayes(); $this->updateStore(); $this->CheckBan(); $this->regenerateOasisTroops(); $this->medals(); $this->artefactOfTheFool(); } /** * Returneaza datele utilizatorului cu cache local */ private function getCachedUser($uid, $mode = 1) { global $database; $uid = (int)$uid; if (!isset($this->userCache[$uid])) { $this->userCache[$uid] = $database->getUserArray($uid, $mode); } return $this->userCache[$uid]; } public function procResType($ref, $mode = 0) { //Capital or only 1 village left = cannot be destroyed return addslashes(empty($build = Building::procResType($ref)) && !$mode ? "Village can't be" : $build); } function recountPop($vid, $use_cache = true){ global $database; $vid = (int) $vid; $fdata = $database->getResourceLevel($vid, $use_cache); $popTot = 0; for ($i = 1; $i <= 40; $i++) { $lvl = $fdata["f".$i]; $building = $fdata["f".$i."t"]; if($building) $popTot += $this->buildingPOP($building, $lvl); } Building::recountCP($database, $vid); $q = "UPDATE ".TB_PREFIX."vdata set pop = $popTot where wref = $vid"; mysqli_query($database->dblink, $q); $owner = $database->getVillageField($vid, "owner"); $this->procClimbers($owner); return $popTot; } function buildingPOP($f, $lvl){ $name = "bid".$f; global $$name; $popT = 0; $dataarray = $$name; for ($i = 0; $i <= $lvl; $i++) { $popT += ((isset($dataarray[$i]) && isset($dataarray[$i]['pop'])) ? $dataarray[$i]['pop'] : 0); } return $popT; } private function loyaltyRegeneration() { global $database; $array = []; $array = $database->getProfileVillages(0, 6); if(!empty($array)) { foreach($array as $loyalty) { if (($t25_level = $this->getTypeLevel(25, $loyalty['wref'])) >= 1) { $value = $t25_level; }elseif(($t26_level = $this->getTypeLevel(26, $loyalty['wref'])) >= 1){ $value = $t26_level; } else $value = 0; if($value > 0){ $newloyalty = min(100, $loyalty['loyalty'] + $value * (time() - $loyalty['lastupdate2']) / 3600); $q = "UPDATE ".TB_PREFIX."vdata SET loyalty = $newloyalty, lastupdate2=".time()." WHERE wref = '".$loyalty['wref']."'"; $database->query($q); } } } $array = []; $q = "SELECT conqured, loyalty, lastupdated, wref FROM ".TB_PREFIX."odata WHERE loyalty < 100"; $array = $database->query_return($q); if(!empty($array)) { foreach($array as $loyalty) { $value = $this->getTypeLevel(37, $loyalty['conqured']); if($value > 0){ $newloyalty = min(100, $loyalty['loyalty'] + $value * (time() - $loyalty['lastupdated']) / 3600); $q = "UPDATE ".TB_PREFIX."odata SET loyalty = $newloyalty, lastupdated=".time()." WHERE wref = '".$loyalty['wref']."'"; $database->query($q); } } } } public function getTypeLevel($tid, $vid) { global $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 clearDeleting() { global $database; $needDelete = $database->getNeedDelete(); if(count($needDelete) > 0) { //Remove the time limit, otherwise deleting players with 80 or more villages couldn't be deleted in one run @set_time_limit(0); foreach($needDelete as $need) { $need['uid'] = (int) $need['uid']; //Get the villages which have to be deleted $needVillages = $database->getVillagesID($need['uid']); //Delete all villages $database->DelVillage($needVillages); for($i = 0;$i < 20; $i++){ $q = "SELECT id FROM ".TB_PREFIX."users where friend".$i." = ".$need['uid']." or friend".$i."wait = ".$need['uid'].""; $array = $database->query_return($q); foreach($array as $friend){ $database->deleteFriend($friend['id'],"friend".$i); $database->deleteFriend($friend['id'],"friend".$i."wait"); } } $database->updateUserField($need['uid'], 'alliance', 0, 1); if($database->isAllianceOwner($need['uid'])){ $alliance = $database->getUserAllianceID($need['uid']); $newowner = $database->getAllMember2($alliance); $newleader = $newowner['id']; $q = "UPDATE " . TB_PREFIX . "alidata set leader = ".(int) $newleader." where id = ".(int) $alliance.""; $database->query($q); $database->updateAlliPermissions($newleader, $alliance, "Leader", 1, 1, 1, 1, 1, 1, 1); Automation::updateMax($newleader); } if (isset($alliance)) $database->deleteAlliance($alliance); $q = "DELETE FROM ".TB_PREFIX."hero where uid = ".$need['uid']; $database->query($q); $q = "DELETE FROM ".TB_PREFIX."mdata where target = ".$need['uid']." or owner = ".$need['uid']; $database->query($q); $q = "DELETE FROM ".TB_PREFIX."ndata where uid = ".$need['uid']; $database->query($q); $q = "DELETE FROM ".TB_PREFIX."users where id = ".$need['uid']; $database->query($q); $q = "DELETE FROM ".TB_PREFIX."deleting where uid = ".$need['uid']; $database->query($q); } } } private function ClearUser() { global $database; if(AUTO_DEL_INACTIVE) { $time = time() - UN_ACT_TIME; $q = "INSERT INTO ".TB_PREFIX."deleting SELECT id, UNIX_TIMESTAMP() FROM ".TB_PREFIX."users WHERE timestamp < $time AND tribe IN(1, 2, 3)"; $database->query($q); } } private function ClearInactive() { global $database; if(TRACK_USR) { $timeout = time()-USER_TIMEOUT * 60; $q = "DELETE FROM ".TB_PREFIX."active WHERE timestamp < $timeout"; $database->query($q); } } private function pruneOResource() { global $database; if(!ALLOW_BURST) { $database->query("UPDATE ".TB_PREFIX."odata SET wood = IF(wood < 0, 0, wood), clay = IF(clay < 0, 0, clay), iron = IF(iron < 0, 0, iron), crop = IF(crop < 0, 0, crop), maxstore = IF(maxstore < ".STORAGE_BASE.", ".STORAGE_BASE.", maxstore), maxcrop = IF(maxcrop < ".STORAGE_BASE.", ".STORAGE_BASE.", maxcrop) WHERE maxstore < ".STORAGE_BASE." OR maxcrop < ".STORAGE_BASE." OR wood < 0 OR clay < 0 OR iron < 0 OR crop < 0"); } } private function pruneResource() { global $database; if(!ALLOW_BURST) { $database->query("UPDATE ".TB_PREFIX."vdata SET wood = IF(wood < 0, 0, wood), clay = IF(clay < 0, 0, clay), iron = IF(iron < 0, 0, iron), crop = IF(crop < 0, 0, crop), maxstore = IF(maxstore < ".STORAGE_BASE.", ".STORAGE_BASE.", maxstore), maxcrop = IF(maxcrop < ".STORAGE_BASE.", ".STORAGE_BASE.", maxcrop) WHERE maxstore < ".STORAGE_BASE." OR maxcrop < ".STORAGE_BASE." OR wood < 0 OR clay < 0 OR iron < 0 OR crop < 0"); $database->query("UPDATE ".TB_PREFIX."vdata SET wood = IF(wood > maxstore, maxstore, wood), clay = IF(clay > maxstore, maxstore, clay), iron = IF(iron > maxstore, maxstore, iron), crop = IF(crop > maxcrop, maxcrop, crop) WHERE wood > maxstore OR clay > maxstore OR iron > maxstore OR crop > maxcrop"); } } private function culturePoints() { global $database; $database->updateVSumField('cp'); } private function buildComplete() { global $database, $technology, $bid18, $bid10, $bid11, $bid38, $bid39; $time = time(); // IDs of villages that were affected by this building completion update, // used to calculate statistical data at the end $villagesAffected = []; // holds additional conditions when updating loopcon records in the bdata table $loopconUpdates = []; // this will hold IDs of bdata table records to delete $dbIdsToDelete = []; // get all pending builds that should be complete by now $res = $database->query_return( "SELECT id, wid, field, level, type, timestamp FROM ".TB_PREFIX."bdata WHERE timestamp < $time and master = 0" ); // preload village data $vilIDs = []; foreach($res as $indi) { $vilIDs[$indi['wid']] = true; } $vilIDs = array_keys($vilIDs); $database->getProfileVillages($vilIDs, 5); $database->getEnforceVillage($vilIDs, 0); // complete buildings foreach($res as $indi) { // store village ID for later for statistical updates $villageData = $database->getVillageFields($indi['wid'],'owner, maxcrop, maxstore, starv, pop'); $villageOwner = $villageData['owner']; $villagesAffected[] = (int) $indi['wid']; $fieldsToSet = []; $q = "UPDATE ".TB_PREFIX."fdata SET f".$indi['field']." = ".$indi['level'].", f".$indi['field']."t = ".$indi['type']." WHERE vref = ".(int) $indi['wid']; if($database->query($q)) { // this will be the level we brought the building to now $level = $indi['level']; // TODO: magic numbers into constants (for building types below) // update capacity if we updated a warehouse or a granary if (in_array($indi['type'], [10, 11, 38, 39])) { $fieldDbName = (in_array($indi['type'], [10, 38]) ? 'maxstore' : 'maxcrop'); $max = $villageData[$fieldDbName]; if($level == 1 && $max == STORAGE_BASE) $max = STORAGE_BASE; if ($level != 1) $max -= ${'bid'.$indi['type']}[$level - 1]['attri'] * STORAGE_MULTIPLIER; $max += ${'bid'.$indi['type']}[$level]['attri'] * STORAGE_MULTIPLIER; $fieldsToSet[$fieldDbName] = $max; } // if we updated Embassy, update maximum members that the alliance can take if($indi['type'] == 18) Automation::updateMax($villageOwner); // by SlimShady95 aka Manuel Mannhardt < manuel_mannhardt@web.de > if ($indi['type'] == 40 && ($indi['level'] % 5 == 0 || $indi['level'] > 95) && $indi['level'] != 100) { $this->startNatarAttack($indi['level'], $indi['wid'], $indi['timestamp']); } //now can't be more than one winner if ww to level 100 is build by 2 users or more on same time if ($indi['type'] == 40 && $indi['level'] == 100) { mysqli_query($database->dblink,"TRUNCATE ".TB_PREFIX."bdata"); } // TODO: find out what exactly these conditions are for // no special military conditioning for Teutons and Gauls if ($database->getUserField($villageOwner, "tribe", 0) != 1) $loopconUpdates[$indi['wid']] = ''; else { // special condition for Roman military buildings if ($indi['field'] > 18) $loopconUpdates[$indi['wid']] = ' AND field > 18'; else $loopconUpdates[$indi['wid']] = ' AND field < 19'; } // Update ww last finish upgrade if ($indi['type'] == 40) { $qW = "UPDATE ".TB_PREFIX."fdata set ww_lastupdate = ".time()." where vref = ".(int) $indi['wid']; $database->query($qW); } $dbIdsToDelete[] = (int) $indi['id']; } //Update starvation data $database->addStarvationData($indi['wid']); // update the requested fields, all at once $database->setVillageFields($indi['wid'], array_keys($fieldsToSet), array_values($fieldsToSet)); } // update statistical data for affected villages foreach ($villagesAffected as $affected_id) $this->recountPop($affected_id, false); // update data that can be done in one swoop instead of using multiple update queries // no special checks for Romans foreach ($loopconUpdates as $villageId => $updateCondition) { $database->query( "UPDATE ".TB_PREFIX."bdata SET loopcon = 0 WHERE loopcon = 1 AND master = 0 AND wid = ".$villageId.$updateCondition); } // delete all processed entries if (count($dbIdsToDelete)) { $database->query( "DELETE FROM " . TB_PREFIX . "bdata WHERE id IN(" . implode( ',', $dbIdsToDelete ) . ")" ); } } // by SlimShady95 aka Manuel Mannhardt < manuel_mannhardt@web.de > private function startNatarAttack($level, $vid, $time) { global $database; // bad, but should work :D // I took the data from my first ww (first .org world) // TODO: get the algo from the real travian with the 100 biggest offs $troops = [5 => [[3412, 2814, 4156, 3553, 9, 0], [35, 0, 77, 33, 17, 10]], 10 => [[4314, 3688, 5265, 4621, 13, 0], [65, 0, 175, 77, 28, 17]], 15 => [[4645, 4267, 5659, 5272, 15, 0], [99, 0, 305, 134, 40, 25]], 20 => [[6207, 5881, 7625, 7225, 22, 0], [144, 0, 456, 201, 56, 36]], 25 => [[6004, 5977, 7400, 7277, 23, 0], [152, 0, 499, 220, 58, 37]], 30 => [[7073, 7181, 8730, 8713, 27, 0], [183, 0, 607, 268, 69, 45]], 35 => [[7090, 7320, 8762, 8856, 28, 0], [186, 0, 620, 278, 70, 45]], 40 => [[7852, 6967, 9606, 8667, 25, 0], [146, 0, 431, 190, 60, 37]], 45 => [[8480, 8883, 10490, 10719, 35, 0], [223, 0, 750, 331, 83, 54]], 50 => [[8522, 9038, 10551, 10883, 35, 0], [224, 0, 757, 335, 83, 54]], 55 => [[8931, 8690, 10992, 10624, 32, 0], [219, 0, 707, 312, 84, 54]], 60 => [[12138, 13013, 15040, 15642, 51, 0], [318, 0, 1079, 477, 118, 76]], 65 => [[13397, 14619, 16622, 17521, 58, 0], [345, 0, 1182, 522, 127, 83]], 70 => [[16323, 17665, 20240, 21201, 70, 0], [424, 0, 1447, 640, 157, 102]], 75 => [[20739, 22796, 25746, 27288, 91, 0], [529, 0, 1816, 803, 194, 127]], 80 => [[21857, 24180, 27147, 28914, 97, 0], [551, 0, 1898, 839, 202, 132]], 85 => [[22476, 25007, 27928, 29876, 100, 0], [560, 0, 1933, 855, 205, 134]], 90 => [[31345, 35053, 38963, 41843, 141, 0], [771, 0, 2668, 1180, 281, 184]], 95 => [[31720, 35635, 39443, 42506, 144, 0], [771, 0, 2671, 1181, 281, 184]], 96 => [[32885, 37007, 40897, 44130, 150, 0], [795, 0, 2757, 1219, 289, 190]], 97 => [[32940, 37099, 40968, 44235, 150, 0], [794, 0, 2755, 1219, 289, 190]], 98 => [[33521, 37691, 41686, 44953, 152, 0], [812, 0, 2816, 1246, 296, 194]], 99 => [[36251, 40861, 45089, 48714, 165, 0], [872, 0, 3025, 1338, 317, 208]]]; // select the troops^^ if (isset($troops[$level])) $units = $troops[$level]; else return false; // get the capital village from the natars $query = mysqli_query($database->dblink,'SELECT `wref` FROM `' . TB_PREFIX . 'vdata` WHERE `owner` = 3 and `capital` = 1 LIMIT 1') or die(mysqli_error($database->dblink)); $row = mysqli_fetch_assoc($query); // start the attacks $endtime = $time + round(86400 / INCREASE_SPEED); // -.- $vid = (int) $vid; mysqli_query($database->dblink,'INSERT INTO `' . TB_PREFIX . 'ww_attacks` (`vid`, `attack_time`) VALUES (' . $vid . ', ' . $endtime . ')'); mysqli_query($database->dblink,'INSERT INTO `' . TB_PREFIX . 'ww_attacks` (`vid`, `attack_time`) VALUES (' . $vid . ', ' . ($endtime + 1) . ')'); // wave 1 $ref = $database->addAttack($row['wref'], 0, $units[0][0], $units[0][1], 0, $units[0][2], $units[0][3], $units[0][4], $units[0][5], 0, 0, 0, 3, 0, 0, 0, 0, 20, 20, 0, 20, 20, 20, 20); $database->addMovement(3, $row['wref'], $vid, $ref, $time, $endtime); // wave 2 $ref2 = $database->addAttack($row['wref'], 0, $units[1][0], $units[1][1], 0, $units[1][2], $units[1][3], $units[1][4], $units[1][5], 0, 0, 0, 3, 40, 0, 0, 0, 20, 20, 0, 20, 20, 20, 20, ['vid' => $vid, 'endtime' => ($endtime + 1)]); $database->addMovement(3, $row['wref'], $vid, $ref2, $time, $endtime + 1); } private function checkWWAttacks() { global $database; $query = mysqli_query($database->dblink,'SELECT vid, attack_time FROM `' . TB_PREFIX . 'ww_attacks` WHERE `attack_time` <= ' . time()); while ($row = mysqli_fetch_assoc($query)) { // delete the attack $query3 = mysqli_query($database->dblink,'DELETE FROM `' . TB_PREFIX . 'ww_attacks` WHERE `vid` = ' . (int) $row['vid'] . ' AND `attack_time` = ' . (int) $row['attack_time']); } } private function getPop($tid, $level) { $name = "bid".$tid; global $$name; $dataarray = $$name; $pop = $dataarray[($level + 1)]['pop']; $cp = $dataarray[($level + 1)]['cp']; return [$pop, $cp]; } private function delTradeRoute() { global $database; $database->delTradeRoute(); } private function TradeRoute() { global $database; $time = time(); $q = "SELECT `from`, wood, clay, iron, crop, wid, deliveries, id FROM ".TB_PREFIX."route where timestamp < $time"; $dataarray = $database->query_return($q); $vilIDs = []; foreach($dataarray as $data) { $vilIDs[$data['wid']] = true; $vilIDs[$data['from']] = true; } $vilIDs = array_keys($vilIDs); $database->getVillageByWorldID($vilIDs); foreach($dataarray as $data) { $targettribe = $database->getUserField($database->getVillageField($data['from'], "owner"), "tribe", 0); $this->sendResource2($data['wood'], $data['clay'], $data['iron'], $data['crop'], $data['from'], $data['wid'], $targettribe, $data['deliveries']); $database->editTradeRoute($data['id'], "timestamp", 86400, 1); } } private function marketComplete() { global $database, $units; $time = microtime(true); $q = "SELECT s.wood, s.clay, s.iron, s.crop, `to`, `from`, endtime, merchant, send, moveid FROM ".TB_PREFIX."movement m, ".TB_PREFIX."send s WHERE m.ref = s.id AND m.proc = 0 AND sort_type = 0 AND endtime < $time"; $dataarray = $database->query_return($q); foreach($dataarray as $data) { $userData_from = $database->getUserFields($database->getVillageField($data['from'], "owner"), "alliance, tribe", 0); $userData_to = $database->getUserFields($database->getVillageField($data['to'], "owner"), "alliance, tribe", 0); if($data['wood'] >= $data['clay'] && $data['wood'] >= $data['iron'] && $data['wood'] >= $data['crop']) $sort_type = 10; elseif($data['clay'] >= $data['wood'] && $data['clay'] >= $data['iron'] && $data['clay'] >= $data['crop']) $sort_type = 11; elseif($data['iron'] >= $data['wood'] && $data['iron'] >= $data['clay'] && $data['iron'] >= $data['crop']) $sort_type = 12; elseif($data['crop'] >= $data['wood'] && $data['crop'] >= $data['clay'] && $data['crop'] >= $data['iron']) $sort_type = 13; $to = $database->getMInfo($data['to']); $from = $database->getMInfo($data['from']); $ownally = $userData_from['alliance']; $targetally = $userData_to['alliance']; // Report filter preferences (#198): skip merchant-transfer notices // according to the saved checkboxes of the involved players. // v4 -> recipient: no report for transfers to own villages // v6 -> recipient: no report for transfers from foreign villages // v5 -> sender: no report for transfers to foreign villages $ownTransfer = ($from['owner'] == $to['owner']); $skipRecipient = $ownTransfer ? !empty($userData_to['v4']) : !empty($userData_to['v6']); if(!$skipRecipient) { $database->addNotice($to['owner'],$to['wref'],$targetally,$sort_type,''.addslashes($from['name']).' send resources to '.addslashes($to['name']).'',''.$from['owner'].','.$from['wref'].','.$data['wood'].','.$data['clay'].','.$data['iron'].','.$data['crop'].'',$data['endtime']); } if(!$ownTransfer && empty($userData_from['v5'])) { $database->addNotice($from['owner'],$to['wref'],$ownally,$sort_type,''.addslashes($from['name']).' send resources to '.addslashes($to['name']).'',''.$from['owner'].','.$from['wref'].','.$data['wood'].','.$data['clay'].','.$data['iron'].','.$data['crop'].'',$data['endtime']); } $database->modifyResource($data['to'],$data['wood'],$data['clay'],$data['iron'],$data['crop'],1); $targettribe = $userData_to["tribe"]; $endtime = $units->getWalkingTroopsTime($data['from'], $data['to'], 0, 0, [$targettribe], 0) + $data['endtime']; $database->addMovement(2, $data['to'], $data['from'], $data['merchant'], time(), $endtime, $data['send'], $data['wood'], $data['clay'], $data['iron'], $data['crop']); $database->setMovementProc($data['moveid']); } $q1 = "SELECT send, moveid, `to`, wood, clay, iron, crop, `from` FROM ".TB_PREFIX."movement WHERE proc = 0 and sort_type = 2 and endtime < $time"; $dataarray1 = $database->query_return($q1); $vilIDs = []; foreach($dataarray1 as $data1) { $vilIDs[$data1['to']] = true; $vilIDs[$data1['from']] = true; } $vilIDs = array_keys($vilIDs); $database->getVillageByWorldID($vilIDs); foreach($dataarray1 as $data1) { $database->setMovementProc($data1['moveid']); if($data1['send'] > 1){ $targettribe1 = $database->getUserFields($database->getVillageField($data1['to'],"owner"),"alliance, tribe",0)['tribe']; $send = $data1['send']-1; $this->sendResource2($data1['wood'],$data1['clay'],$data1['iron'],$data1['crop'],$data1['to'],$data1['from'],$targettribe1,$send); } } } private function sendResource2($wtrans, $ctrans, $itrans, $crtrans, $from, $to, $tribe, $send) { global $bid17, $bid28, $database, $units; $availableWood = $database->getWoodAvailable($from); $availableClay = $database->getClayAvailable($from); $availableIron = $database->getIronAvailable($from); $availableCrop = $database->getCropAvailable($from); if($availableWood + $availableClay + $availableIron + $availableCrop > 0) { if($availableWood < $wtrans) $wtrans = $availableWood; if($availableClay < $ctrans) $ctrans = $availableClay; if($availableIron < $itrans) $itrans = $availableIron; if($availableCrop < $crtrans) $crtrans = $availableCrop; $merchant2 = ($this->getTypeLevel(17, $from) > 0)? $this->getTypeLevel(17, $from) : 0; $used2 = $database->totalMerchantUsed($from, false); $merchantAvail2 = $merchant2 - $used2; $maxcarry2 = ($tribe == 1)? 500 : (($tribe == 2)? 1000 : 750); $maxcarry2 *= TRADER_CAPACITY; if($this->getTypeLevel(28, $from) != 0) { $maxcarry2 *= $bid28[$this->getTypeLevel(28, $from)]['attri'] / 100; } $resource = [$wtrans, $ctrans, $itrans, $crtrans]; $reqMerc = ceil((array_sum($resource) - 0.1) / $maxcarry2); if($merchantAvail2 > 0 && $reqMerc <= $merchantAvail2) { if($database->getVillageState($to)) { $timetaken = $units->getWalkingTroopsTime($from, $to, 0, 0, [$tribe], 0); $res = $resource[0] + $resource[1] + $resource[2] + $resource[3]; if($res > 0){ $reference = $database->sendResource($resource[0], $resource[1], $resource[2], $resource[3], $reqMerc, 0); $database->modifyResource($from, $resource[0], $resource[1], $resource[2], $resource[3], 0); $database->addMovement(0, $from, $to, $reference, microtime(true), microtime(true) + $timetaken, $send); } } } } } private function resolveCatapultsDestruction(&$bdo, &$battlepart, &$info_cat, &$data, $catapultTarget, $twoRowsCatapultSetup, $isSecondRow, $catp_pic, $can_destroy, $isoasis, &$village_destroyed, $tribe) { global $battle, $database, $bid34; if(isset($catapultTarget)) { //Currently targeted building/field level $tblevel = (int) $bdo['f'.$catapultTarget]; //Currently targetet building/field GID (ID of the building/field type - woodcutter, cropland, embassy...) $tbgid = (int) $bdo['f'.$catapultTarget.'t']; //Currently targeted building/field ID in the database (fdata, the fID field, e.g. f1, f2, f3...) $tbid = (int) $catapultTarget; //If we're targeting the WW if($catapultTarget == 40){ $battlepart['catapults']['strongerBuildings'] = 1; $battlepart['catapults']['moraleBonus'] = 1; } $catapultsDamage = $battle->calculateCatapultsDamage($data['t8'], $battlepart['catapults']['upgrades'], $battlepart['catapults']['durability'], $battlepart['catapults']['attackDefenseRatio'], $battlepart['catapults']['strongerBuildings'], $battlepart['catapults']['moraleBonus']); $newLevel = $battle->calculateNewBuildingLevel($tblevel, $catapultsDamage / ($twoRowsCatapultSetup ? 2 : 1)); //fix: Only modify build queue if actual damage was dealt if ($newLevel < $tblevel) { $database->modifyBData($data['to'], $tbid, [$newLevel, $tblevel], $tribe); } // building/field destroyed if ($newLevel == 0){ // prepare data to be updated $fieldsToSet = ["f".$tbid]; $fieldValuesToSet = [0]; // update $bdo, so we don't have to reselect later $bdo['f'.$catapultTarget] = 0; if ($tbid >= 19 && $tbid != 99) { $fieldsToSet[] = "f".$tbid."t"; $fieldValuesToSet[] = 0; $bdo['f'.$catapultTarget."t"] = 0; } // update all that needs updating $database->setVillageLevel($data['to'], $fieldsToSet, $fieldValuesToSet); $buildarray = $GLOBALS["bid".$tbgid]; if ( isset( $buildarray[$newLevel] ) ) { // (great) warehouse level was changed if ($tbgid == 10 || $tbgid == 38) { $database->setMaxStoreForVillage($data['to'], $buildarray[$newLevel]['attri']); } // (great) granary level was changed if ($tbgid == 11 || $tbgid == 39) { $database->setMaxCropForVillage($data['to'], $buildarray[$newLevel]['attri']); } } // oasis cannot be destroyed $pop = $this->recountPop($data['to'], false); if ($isoasis == 0 && $pop == 0 && $can_destroy == 1) $village_destroyed = 1; if ($isSecondRow) { if ($tbid > 0) { $info_cat .= "Information \"Catapult\" ".$this->procResType($tbgid, $can_destroy)." destroyed."; } // embassy level was changed if ($tbgid == 18){ $info_cat .= $database->checkEmbassiesAfterBattle($data['to'], $bdo['f'.$catapultTarget], false); } $info_cat .= ""; } else { $info_cat = "".$catp_pic.", ".$this->procResType($tbgid, $can_destroy)." destroyed."; // embassy level was changed if ($tbgid == 18){ $info_cat .= $database->checkEmbassiesAfterBattle($data['to'], $bdo['f'.$catapultTarget], false); } } } // building/field not damaged elseif($newLevel == $tblevel){ if($isSecondRow) { if ($tbid > 0) { $info_cat .= "Information \"Catapult\" ".$this->procResType($tbgid, $can_destroy)." was not damaged."; } } else { $info_cat = "".$catp_pic.",".$this->procResType($tbgid, $can_destroy)." was not damaged."; } } // building/field was damaged, let's calculate the actual damage else { // update $bdo, so we don't have to reselect later $bdo['f'.$catapultTarget] = $newLevel; // building was damaged to a lower level $info_cata = " damaged from level ".$tblevel." to level ".$newLevel."."; $buildarray = $GLOBALS["bid".$tbgid]; // (great) warehouse level was changed if ($tbgid == 10 || $tbgid == 38) { $database->setMaxStoreForVillage($data['to'], $buildarray[$newLevel]['attri']); } // (great) granary level was changed if ($tbgid == 11 || $tbgid == 39) { $database->setMaxCropForVillage($data['to'], $buildarray[$newLevel]['attri']); } $fieldsToSet = ["f".$tbid]; $fieldValuesToSet = [$newLevel]; $database->setVillageLevel($data['to'], $fieldsToSet, $fieldValuesToSet); // recalculate population and check if the village shouldn't be destroyed at this point $pop = $this->recountPop($data['to'], false); if ($isoasis == 0) { if($pop == 0 && $can_destroy == 1) $village_destroyed = 1; } if ($isSecondRow) { $info_cat .= "Information \"Catapult\" ".$this->procResType($tbgid, $can_destroy).$info_cata; // embassy level was changed if ($tbgid == 18) { $info_cat .= $database->checkEmbassiesAfterBattle($data['to'], $bdo['f'.$catapultTarget], false); } $info_cat .= ""; } else { $info_cat = "" . $catp_pic . "," . $this->procResType($tbgid, $can_destroy).$info_cata; // embassy level was changed if ($tbgid == 18) { $info_cat .= $database->checkEmbassiesAfterBattle($data['to'], $bdo['f'.$catapultTarget], false); } } } }else{ if(!isset($info_cat) || empty($info_cat) || $info_cat == ","){ $info_cat = "".$catp_pic.", There are no buildings left to destroy"; }else if(strpos($info_cat, "There are no buildings left") === false){ $info_cat .= "Information \"Catapult\" There are no buildings left to destroy."; } } } private function applyCatapults($data, $battlepart, $catp_pic, $can_destroy, $isoasis, $targettribe, $info_cat) { global $database; // village_destroyed stays unset in the pop<=0 path of the original; // downstream treats unset as 0, so initialising to 0 here is equivalent. $village_destroyed = 0; $pop = $this->recountPop($data['to']); // village has been destroyed if ($pop <= 0) { if ($can_destroy == 1) $info_cat = "".$catp_pic.", Village already destroyed."; else $info_cat = "".$catp_pic.", Village can\'t be destroyed."; } else { // village stands, let's do the damage /** * FIRST CATAPULTS ROW */ $basearray = $data['to']; $bdo = $database->getResourceLevel($basearray, false); $catapultTarget = $data['ctar1']; $catapultTarget2 = (isset($data['ctar2']) ? $data['ctar2'] : 0); $catapults1TargetRandom = ($catapultTarget == 0); $catapults2WillNotShoot = ($catapultTarget2 == 0); $catapults2TargetRandom = ($catapults2WillNotShoot || $catapultTarget2 == 99); // we're manually targetting 1st and/or 2nd row of catapults if (!$catapults1TargetRandom) { $_catapultsTarget1Levels = []; $__catapultsTarget1AltTargets = []; // calculate targets for 1st rows of catapults $j = 0; for ($i = 1; $i <= 41; $i++) { if ($i == 41) $i = 99; // 1st row of catapults pre-selected target calculations, if needed if (!$catapults1TargetRandom && $bdo['f'.$i.'t'] == $catapultTarget && $bdo['f'.$i] > 0 && $i != 40) { $j++; $_catapultsTarget1Levels[$j]=$bdo['f'.$i]; $__catapultsTarget1AltTargets[$j]=$i; } } // if we couldn't find a suitable target for 1st row of catapults, // select a random target instead if (!$catapults1TargetRandom) { if ( count( $_catapultsTarget1Levels ) > 0 ) { if ( max( $_catapultsTarget1Levels ) <= 0 ) { $catapultTarget = 0; } else { $catapultTarget = $__catapultsTarget1AltTargets[rand( 1, $j )]; } } else { $catapultTarget = 0; $catapults1TargetRandom = true; } } } // 1st row of catapults set to target randomly if ($catapults1TargetRandom) { $list = []; for ($i = 1; $i <= 41; $i++) { if ($i == 41) $i = 99; if ($bdo['f'.$i] > 0 && $i != 40) $list[] = $i; } $catapultTarget = $list[rand(0, count($list) - 1)]; } /** * resolve 1st row of catapults */ $village_destroyed = 0; $this->resolveCatapultsDestruction($bdo, $battlepart, $info_cat, $data, $catapultTarget, !$catapults2WillNotShoot, false, $catp_pic, $can_destroy, $isoasis, $village_destroyed, $targettribe); /** * SECOND CATAPULTS ROW */ // we're manually targetting 2nd row of catapults if (!$catapults2TargetRandom) { $_catapultsTarget2Levels = []; $__catapultsTarget2AltTargets = []; // calculate targets for 2nd rows of catapults $j = 0; for ($i = 1; $i <= 41; $i++) { if ($i == 41) $i = 99; // 2nd row of catapults pre-selected target calculations, if needed if (!$catapults2TargetRandom && !$catapults2WillNotShoot && $bdo['f'.$i.'t'] == $catapultTarget2 && $bdo['f'.$i] > 0 && $i != 40) { $j++; $_catapultsTarget2Levels[$j] = $bdo['f'.$i]; $__catapultsTarget2AltTargets[$j] = $i; } } // if we couldn't find a suitable target for 2nd row of catapults, // select a random target instead if (!$catapults2TargetRandom) { if (count($_catapultsTarget2Levels) > 0 ) { if (max($_catapultsTarget2Levels) <= 0 ) { $catapultTarget2 = 99; } else $catapultTarget2 = $__catapultsTarget2AltTargets[rand( 1, $j )]; } else { $catapultTarget2 = 99; $catapults2TargetRandom = true; } } } // 2nd row of catapults set to target randomly if ($catapults2TargetRandom && !$catapults2WillNotShoot) { $list = []; for ($i = 1; $i <= 41; $i++) { if ($i == 41) $i = 99; if ($bdo['f'.$i] > 0 && $i != 40) $list[] = $i; } $catapultTarget2 = $list[ rand(0, count($list) - 1) ]; } /** * resolve 2nd row of catapults */ if (!$catapults2WillNotShoot) { $this->resolveCatapultsDestruction($bdo, $battlepart, $info_cat, $data, $catapultTarget2, true, true, $catp_pic, $can_destroy, $isoasis, $village_destroyed, $targettribe); } // clear resource levels cache, since we might have destroyed buildings/fields by now call_user_func(get_class($database).'::clearResourseLevelsCache'); } return ['battlepart' => $battlepart, 'info_cat' => $info_cat, 'village_destroyed' => $village_destroyed]; } private function claimMovementRecord($moveid) { global $database; $moveid = (int)$moveid; if ($moveid <= 0) { return false; } $q = "UPDATE ".TB_PREFIX."movement SET proc = 1 WHERE moveid = $moveid AND proc = 0"; mysqli_query($database->dblink, $q); return (mysqli_affected_rows($database->dblink) === 1); } /** * Handle hero evasion: if the defender has evasion active and can afford it, * send all defender units back to base and charge 2 gold + 1 evasion charge. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current movement row (needs 'to'). * @param int $DefenderID Defender's user ID. * @param array $DefenderUnit Unit counts for the defending village. * @param int $targettribe Defender's tribe (1=Roman, 2=Teuton, 3=Gaul…). * @param int $evasion Whether evasion is enabled (1) or not (0). * @param int $maxevasion Remaining evasion charges. * @param int $gold Defender's current gold. * @param bool $cannotsend True when troops are already returning and can't be re-sent. * @param int $attackType Type of incoming attack (must be > 2 to trigger evasion). */ private function handleEvasion( array $data, int $DefenderID, array $DefenderUnit, int $targettribe, int $evasion, int $maxevasion, int $gold, bool $cannotsend, int $attackType ): void { global $database; if (!($evasion == 1 && $maxevasion > 0 && $gold > 1 && !$cannotsend && $attackType > 2)) { return; } $playerunit = ($targettribe - 1) * 10; $totaltroops = 0; $evasionUnitModifications_units = []; $evasionUnitModifications_amounts = []; $evasionUnitModifications_modes = []; for ($i = 1; $i <= 10; $i++) { $playerunit += $i; $data['u' . $i] = $DefenderUnit['u' . $playerunit]; $evasionUnitModifications_units[] = $playerunit; $evasionUnitModifications_amounts[] = $DefenderUnit['u' . $playerunit]; $evasionUnitModifications_modes[] = 0; $playerunit -= $i; $totaltroops += $data['u' . $i]; } $data['u11'] = $DefenderUnit['hero']; $totaltroops += $data['u11']; if ($totaltroops > 0) { $evasionUnitModifications_units[] = 'hero'; $evasionUnitModifications_amounts[] = $DefenderUnit['hero']; $evasionUnitModifications_modes[] = 0; $attackid = $database->addAttack($data['to'], $data['u1'], $data['u2'], $data['u3'], $data['u4'], $data['u5'], $data['u6'], $data['u7'], $data['u8'], $data['u9'], $data['u10'], $data['u11'], 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); $database->addMovement(4, 0, $data['to'], $attackid, microtime(true), microtime(true) + (180 / EVASION_SPEED)); $database->updateUserField($DefenderID, ["gold", "maxevasion"], [$gold - 2, $maxevasion - 1], 1); } $database->modifyUnit($data['to'], $evasionUnitModifications_units, $evasionUnitModifications_amounts, $evasionUnitModifications_modes); } /** * Process senator/chief attacks: reduce loyalty and, if it hits 0, conquer the village. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current movement row. * @param int $type Attack type (must be 3 for chiefing). * @param int $dead9 Chiefs killed in battle. * @param int $traped9 Chiefs caught in traps. * @param array $from Attacker's village info row. * @param array $to Defender's village info row. * @param array $toF Defender's village field row (has loyalty). * @param int $owntribe Attacker's tribe. * @param int $targettribe Defender's tribe. * @param array $varray All villages of the defender. * @param array $varray1 All villages of the attacker. * @param array $battlepart Battle result. * @param int $isoasis 0 = village, non-zero = oasis. * @param int $village_destroyed Whether the village has already been destroyed. * @param int $chief_pic Chief unit sprite ID for report strings. * @return array{info_chief:string, chiefing_village:int, village_destroyed:int} */ private function handleConquest( array $data, int $type, int $dead9, int $traped9, int $targettribe, array $from, array $to, array $toF, int $owntribe, array $varray, array $varray1, array $battlepart, int $isoasis, int $village_destroyed, int $chief_pic ): array { global $database, $units; $info_chief = ','; $chiefing_village = 0; if (!(($data['t9'] - $dead9 - $traped9) > 0 && $isoasis == 0)) { return ['info_chief' => $info_chief, 'chiefing_village' => $chiefing_village, 'village_destroyed' => $village_destroyed]; } if ($type != 3) { $info_chief = $chief_pic . ',Could not reduce cultural points during raid'; return ['info_chief' => $info_chief, 'chiefing_village' => $chiefing_village, 'village_destroyed' => $village_destroyed]; } $palacelevel = $database->getResourceLevel($from['wref']); $plevel = 0; for ($i = 1; $i <= 40; $i++) { if ($palacelevel['f' . $i . 't'] == 26) $plevel = $i; elseif ($palacelevel['f' . $i . 't'] == 25) $plevel = $i; } if ($palacelevel['f' . $plevel . 't'] == 26) { if ($palacelevel['f' . $plevel] < 10) $canconquer = 0; elseif ($palacelevel['f' . $plevel] < 15) $canconquer = 1; elseif ($palacelevel['f' . $plevel] < 20) $canconquer = 2; else $canconquer = 3; } elseif ($palacelevel['f' . $plevel . 't'] == 25) { if ($palacelevel['f' . $plevel] < 10) $canconquer = 0; elseif ($palacelevel['f' . $plevel] < 20) $canconquer = 1; else $canconquer = 2; } else { $canconquer = 0; } $expArray = $database->getVillageFields($from['wref'], 'exp1, exp2, exp3'); $villexp = ($expArray['exp1'] == 0) ? 0 : (($expArray['exp2'] == 0) ? 1 : (($expArray['exp3'] == 0) ? 2 : 3)); $mode = CP; $cp_mode = $GLOBALS['cp' . $mode]; $need_cps = $cp_mode[count($varray1) + 1]; $user_cps = $database->getUserArray($from['owner'], 1)['cp']; if ($user_cps < $need_cps) { $info_chief = $chief_pic . ',Not enough culture points.'; return ['info_chief' => $info_chief, 'chiefing_village' => $chiefing_village, 'village_destroyed' => $village_destroyed]; } if (count($varray) <= 1 || $to['capital'] == 1 || $villexp >= $canconquer) { $info_chief = $chief_pic . ',You cant take over this village.'; return ['info_chief' => $info_chief, 'chiefing_village' => $chiefing_village, 'village_destroyed' => $village_destroyed]; } if ($to['owner'] == 3 && $to['name'] == 'WW Buildingplan') { $info_chief = $chief_pic . ',You cant take over this village.'; return ['info_chief' => $info_chief, 'chiefing_village' => $chiefing_village, 'village_destroyed' => $village_destroyed]; } if ($database->getFieldLevelInVillage($data['to'], '25, 26')) { $info_chief = $chief_pic . ',The Palace/Residence isn\'t destroyed!'; return ['info_chief' => $info_chief, 'chiefing_village' => $chiefing_village, 'village_destroyed' => $village_destroyed]; } // --- Reduce loyalty --- $time = time(); $reducedLoyaltyTotal = 0; for ($i = 0; $i < ($data['t9'] - $dead9 - $traped9); $i++) { $reducedLoyalty = ($owntribe == 1) ? rand(20, 30) : rand(20, 25); if ($from['celebration'] > $time && $from['type'] == 2) $reducedLoyalty += 5; if ($to['celebration'] > $time && $to['type'] == 2) $reducedLoyalty -= 5; $reducedLoyalty /= $battlepart['moralBonus']; if ($owntribe == 2 && $this->getTypeLevel(35, $data['from']) > 0) $reducedLoyalty /= 2; $reducedLoyaltyTotal += $reducedLoyalty; } if (($toF['loyalty'] - $reducedLoyaltyTotal) > 0) { $info_chief = $chief_pic . ',The loyalty was lowered from ' . floor($toF['loyalty']) . ' to ' . floor($toF['loyalty'] - $reducedLoyaltyTotal) . '.'; $database->setVillageField($data['to'], 'loyalty', ($toF['loyalty'] - $reducedLoyaltyTotal)); return ['info_chief' => $info_chief, 'chiefing_village' => $chiefing_village, 'village_destroyed' => $village_destroyed]; } if ($village_destroyed) { return ['info_chief' => $info_chief, 'chiefing_village' => $chiefing_village, 'village_destroyed' => $village_destroyed]; } // --- Village conquered --- $villname = addslashes($database->getVillageField($data['to'], 'name')); $artifact = reset($database->getOwnArtefactInfo($data['to'])); $info_chief = $chief_pic . ',Inhabitants of ' . $villname . ' village decided to join your empire.'; if ($artifact['vref'] == $data['to']) { $database->claimArtefact($data['to'], $data['to'], $database->getVillageField($data['from'], 'owner')); } $database->setVillageFields($data['to'], ['loyalty', 'owner'], [0, $database->getVillageField($data['from'], 'owner')]); $database->query("DELETE FROM " . TB_PREFIX . "abdata WHERE vref = " . (int)$data['to']); $database->addABTech($data['to']); $database->query("DELETE FROM " . TB_PREFIX . "tdata WHERE vref = " . (int)$data['to']); $database->addTech($data['to']); $database->query("DELETE FROM " . TB_PREFIX . "enforcement WHERE `from` = " . (int)$data['to']); $database->query("DELETE FROM " . TB_PREFIX . "route where wid = " . (int)$data['to'] . " OR `from` = " . (int)$data['to']); $units2reset = []; for ($u = 1; $u <= 50; $u++) $units2reset[] = 'u' . $u . ' = 0'; $units2reset[] = 'u99 = 0'; $units2reset[] = 'u99o = 0'; $units2reset[] = 'hero = 0'; $database->query("UPDATE " . TB_PREFIX . "units SET " . implode(',', $units2reset) . " WHERE vref = " . (int)$data['to']); $newLevels_fieldNames = []; $newLevels_fieldValues = []; $AttackerID = $database->getVillageField($data['from'], 'owner'); $DefenderID = $database->getVillageField($data['to'], 'owner'); $poparray = $database->getVSumField([$AttackerID, $DefenderID], 'pop'); $pop1 = $poparray[0]['Total']; $pop2 = $poparray[1]['Total']; if ($pop1 > $pop2 && $targettribe != 5) { $buildlevel = $database->getResourceLevel($data['to']); for ($i = 1; $i <= 39; $i++) { if ($buildlevel['f' . $i] != 0) { if ($buildlevel['f' . $i . 't'] != 35 && $buildlevel['f' . $i . 't'] != 36 && $buildlevel['f' . $i . 't'] != 41) { $leveldown = $buildlevel['f' . $i] - 1; $newLevels_fieldNames[] = 'f' . $i; $newLevels_fieldValues[] = $leveldown; if (!$leveldown > 0) { $newLevels_fieldNames[] = 'f' . $i . 't'; $newLevels_fieldValues[] = 0; } } else { $newLevels_fieldNames[] = 'f' . $i; $newLevels_fieldValues[] = 0; $newLevels_fieldNames[] = 'f' . $i . 't'; $newLevels_fieldValues[] = 0; } } } if ($buildlevel['f99'] != 0) { $newLevels_fieldNames[] = 'f99'; $newLevels_fieldValues[] = $buildlevel['f99'] - 1; } } // destroy wall $newLevels_fieldNames[] = 'f40'; $newLevels_fieldValues[] = 0; $newLevels_fieldNames[] = 'f40t'; $newLevels_fieldValues[] = 0; $database->clearExpansionSlot($data['to'], 1); $expArray2 = $database->getVillageFields($data['from'], 'exp1, exp2, exp3'); if ($expArray2['exp1'] == 0) { $exp = 'exp1'; } elseif ($expArray2['exp2'] == 0) { $exp = 'exp2'; } else { $exp = 'exp3'; } $database->setVillageField($data['from'], $exp, $data['to']); $database->deleteTradeRoutesByVillage($data['to']); $database->setVillageLevel($data['to'], $newLevels_fieldNames, $newLevels_fieldValues); $units->returnTroops($data['to'], 1); $chiefing_village = 1; $database->reassignHero($data['to']); $this->recountPop($data['to'], false); return ['info_chief' => $info_chief, 'chiefing_village' => $chiefing_village, 'village_destroyed' => $village_destroyed]; } /** * Fetch all attacks (sort_type 3, not reinforcement) that have arrived by * $time, joined with their attack rows, ordered by arrival. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param int $time Current timestamp. * @return array Rows of pending/completed attacks. */ private function fetchCompletedAttacks($time) { global $database; $time = (int) $time; $q = " SELECT `from`, `to`, endtime, ref, ctar1, ctar2, spy, moveid, attack_type, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, (SELECT oasistype FROM ".TB_PREFIX."wdata WHERE id = `to`) as oasistype FROM ".TB_PREFIX."movement, ".TB_PREFIX."attacks WHERE ".TB_PREFIX."movement.ref = ".TB_PREFIX."attacks.id AND ".TB_PREFIX."movement.proc = 0 AND ".TB_PREFIX."movement.sort_type = 3 AND ".TB_PREFIX."attacks.attack_type != 2 AND endtime < $time ORDER BY endtime ASC"; return $database->query_return($q); } /** * Batch-preload the village / unit / tech data used by sendunitsComplete() * so the per-attack loop hits the in-request cache instead of querying row * by row. Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $dataarray Completed attacks (each with 'from' and 'to'). */ private function preloadBattleData(array $dataarray) { global $database; $vilIDs = []; foreach ($dataarray as $data) { $vilIDs[$data['from']] = true; $vilIDs[$data['to']] = true; } $vilIDs = array_keys($vilIDs); $database->getProfileVillages($vilIDs, 5); $database->getUnit($vilIDs); $database->getEnforceVillage($vilIDs, 0); $database->getMovement(34, $vilIDs, 1); $database->getABTech($vilIDs); } private function buildScoutReport($data, $spy_pic, $isoasis, $targettribe, $crannySpy, $totwood, $totclay, $totiron, $totcrop) { global $database; $info_spy = ""; if ($data['spy'] == 1){ $info_spy = "".$spy_pic.",
\"Lumber\"".round($totwood)." | \"Clay\"".round($totclay)." | \"Iron\"".round($totiron)." | \"Crop\"".round($totcrop)."
\"carry\"Total Resources: ".round($totwood+$totclay+$totiron+$totcrop)."
"; }else if($data['spy'] == 2){ if ($isoasis == 0){ $walllevel = $database->getFieldLevelInVillage($data['to'], '31, 32, 33'); $residencelevel = $database->getFieldLevelInVillage($data['to'], 25); $palacelevel = $database->getFieldLevelInVillage($data['to'], 26); $residenceimg = "\"Residence\""; $palaceimg = "\"Palace\""; $crannyimg = "\"Cranny\""; $wallimg = "\"Wall\""; $info_spy = "".$spy_pic.","; if($residencelevel > 0) $info_spy .= $residenceimg." Residence level:".$residencelevel."
"; elseif($palacelevel > 0) $info_spy .= $palaceimg." Palace level: ".$palacelevel."
"; if($walllevel > 0) $info_spy .= $wallimg." Wall level: ".$walllevel."
"; $info_spy .= $crannyimg." Total crannies capacity: ".$crannySpy.""; } else $info_spy = "".$spy_pic.", There are no informations to show"; } return $info_spy; } /** * Release prisoners trapped in the target village during a conquest attack, * send them home (with 25% casualties), repair destroyed traps, and return * the HTML fragment for the battle-report trap line. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current attack row. * @param array $from Attacker village info (getMInfo). * @param array $to Defender village info (getMInfo). * @param int $ownally Attacker's alliance id. * @param int $type Attack type (3 = normal with rams/catas). * @param int $totalsend_att Total attacking troops sent. * @param int $totaldead_att Total attacking troops killed. * @param int $totaltraped_att Total attacking troops trapped. * @return string HTML fragment for the trap report line; empty if no prisoners freed. */ private function handlePrisoners($data, $from, $to, $ownally, $type, $totalsend_att, $totaldead_att, $totaltraped_att) { global $database, $units, $bid19, $u99; if ($type != 3 || $totalsend_att - ($totaldead_att + $totaltraped_att) <= 0) { return ''; } $prisoners = $database->getPrisoners([$to['wref']], 0, false)[$to['wref'].'0']; if (count($prisoners) == 0) { return ''; } $anothertroops = $mytroops = $ownDeads = $anotherDeads = 0; $prisoners2delete = $movementType = $movementFrom = $movementTo = $movementRef = $movementTime = $movementEndtime = []; $utime = microtime(true); foreach ($prisoners as $prisoner) { $p_owner = $database->getVillageField($prisoner['from'], "owner"); if ($prisoner['from'] == $from['wref']) { for ($i = 1; $i <= 11; $i++) { $deadPrisoners = round($prisoner['t'.$i] / 4); $mytroops += $prisoner['t'.$i]; $ownDeads += $deadPrisoners; $prisoner['t'.$i] -= $deadPrisoners; } $database->modifyAttack2( $data['ref'], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [$prisoner['t1'], $prisoner['t2'], $prisoner['t3'], $prisoner['t4'], $prisoner['t5'], $prisoner['t6'], $prisoner['t7'], $prisoner['t8'], $prisoner['t9'], $prisoner['t10'], $prisoner['t11']] ); $prisoners2delete[] = $prisoner['id']; } else { $p_alliance = $database->getUserField($p_owner, "alliance", 0); $friendarray = $database->getAllianceAlly($p_alliance, 1); $neutralarray = $database->getAllianceAlly($p_alliance, 2); $friend = ($friendarray[0]['alli1'] > 0 && $friendarray[0]['alli2'] > 0 && $p_alliance > 0) && ($friendarray[0]['alli1'] == $ownally || $friendarray[0]['alli2'] == $ownally) && ($ownally != $p_alliance && $ownally && $p_alliance); $neutral = ($neutralarray[0]['alli1'] > 0 && $neutralarray[0]['alli2'] > 0 && $p_alliance > 0) && ($neutralarray[0]['alli1'] == $ownally || $neutralarray[0]['alli2'] == $ownally) && ($ownally != $p_alliance && $ownally && $p_alliance); if ($p_alliance == $ownally || $friend || $neutral) { $p_tribe = $database->getUserField($p_owner, "tribe", 0); for ($i = 1; $i <= 11; $i++) { $deadPrisoners = round($prisoner['t'.$i] / 4); if ($p_owner == $from['owner']) { $mytroops += $prisoner['t'.$i]; $ownDeads += $deadPrisoners; } else { $anothertroops += $prisoner['t'.$i]; $anotherDeads += $deadPrisoners; } $prisoner['t'.$i] -= $deadPrisoners; } $troopsTime = $units->getWalkingTroopsTime($prisoner['from'], $prisoner['wref'], $p_owner, $p_tribe, $prisoner, 1, 't'); $p_time = $database->getArtifactsValueInfluence($p_owner, $prisoner['from'], 2, $troopsTime); $p_reference = $database->addAttack( $prisoner['from'], $prisoner['t1'], $prisoner['t2'], $prisoner['t3'], $prisoner['t4'], $prisoner['t5'], $prisoner['t6'], $prisoner['t7'], $prisoner['t8'], $prisoner['t9'], $prisoner['t10'], $prisoner['t11'], 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); $movementType[] = 4; $movementFrom[] = $prisoner['wref']; $movementTo[] = $prisoner['from']; $movementRef[] = $p_reference; $movementTime[] = $utime; $movementEndtime[] = $p_time + $utime; $prisoners2delete[] = $prisoner['id']; } } } if (count($movementType)) { $database->addMovement($movementType, $movementFrom, $movementTo, $movementRef, $movementTime, $movementEndtime); } $database->deletePrisoners($prisoners2delete); $ownDeadsText = ($ownAlive = $mytroops - $ownDeads) > 0 ? " of which ".$ownAlive." have been saved" : ""; $anotherDeadsText = ($anotherAlive = $anothertroops - $anotherDeads) > 0 ? " of which ".$anotherAlive." have been saved" : ""; $database->addGeneralAttack($ownDeads + $anotherDeads); $newtraps = round(($mytroops + $anothertroops) / 3); $database->modifyUnit($data['to'], ['99', '99o'], [$mytroops + $anothertroops, $mytroops + $anothertroops], [0, 0]); if ($newtraps > 0) { $repairDuration = $database->getArtifactsValueInfluence( $to['owner'], $to['wref'], 5, round(($bid19[max($this->getTypeLevel(36, $to['wref']), 1)]['attri'] / 100) * $u99['time'] / SPEED) ); $database->trainUnit($to['wref'], 99, $newtraps, $u99['pop'], $repairDuration, 0); } $trapper_pic = "\"Trap\""; $p_username = $database->getUserField($from['owner'], "username", 0); if ($mytroops > 0 && $anothertroops > 0) { return $trapper_pic." ".$p_username." freed ".$mytroops." from his troops".$ownDeadsText." and ".$anothertroops." friendly troops".$anotherDeadsText."."; } elseif ($mytroops > 0) { return $trapper_pic." ".$p_username." freed ".$mytroops." from his troops".$ownDeadsText."."; } elseif ($anothertroops > 0) { return $trapper_pic." ".$p_username." freed ".$anothertroops." friendly troops".$anotherDeadsText."."; } return ''; } /** * Assemble the battle-report data string ($data2) and the all-attacker-dead * fallback ($data_fail) from pre-computed battle results. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param bool $scout True when this is a scouting mission. * @param array $from Attacker village info (getMInfo). * @param array $to Defender village info (getMInfo). * @param int $owntribe Attacker tribe id. * @param int $targettribe Defender tribe id. * @param string $unitssend_att CSV of units sent by attacker. * @param string $unitsdead_att CSV of attacker units killed. * @param array $steal [wood, clay, iron, crop] looted amounts. * @param array $battlepart calculateBattle() result. * @param array $unitssend_def CSV of defender units sent (indexed 0–5). * @param array $unitsdead_def CSV of defender units dead (indexed 0–5). * @param array $unitssend_deff Masked defender units for data_fail (indexed 0–5). * @param array $unitsdead_deff Masked dead units for data_fail (indexed 0–5). * @param int $rom,$ger,$gal,$nat,$natar Tribe presence flags. * @param string $DefenderHeroesTot CSV of defender heroes totals by tribe. * @param string $DefenderHeroesDead CSV of defender heroes dead by tribe. * @param string $info_ram Ram result fragment. * @param string $info_cat Catapult result fragment (internally extended if village destroyed). * @param string $info_chief Chief/senator result fragment. * @param string $info_spy Scout report fragment (scout missions only). * @param string $info_trap Prisoner-release fragment (from handlePrisoners). * @param string $info_hero Hero result fragment. * @param string $info_troop "None of your soldiers returned" fragment. * @param array $data Current attack row (for t11). * @param int $dead11 Hero casualties. * @param int $herosend_def Defender hero count. * @param int $deadhero Defender hero casualties. * @param string $unitstraped_att CSV of attacker units trapped. * @param int $village_destroyed 1 if the village was destroyed. * @param int $can_destroy 1 if the village can be destroyed. * @param int $catp_pic Catapult unit-pic id for the HTML fragment. * @return array ['data2' => string, 'data_fail' => string] */ private function buildCombatReport( $scout, $from, $to, $owntribe, $targettribe, $unitssend_att, $unitsdead_att, $steal, $battlepart, $unitssend_def, $unitsdead_def, $unitssend_deff, $unitsdead_deff, $rom, $ger, $gal, $nat, $natar, $DefenderHeroesTot, $DefenderHeroesDead, $info_ram, $info_cat, $info_chief, $info_spy, $info_trap, $info_hero, $info_troop, $data, $dead11, $herosend_def, $deadhero, $unitstraped_att, $village_destroyed, $can_destroy, $catp_pic ) { if (!empty($scout)) { $data2 = ''.$from['owner'].','.$from['wref'].','.$owntribe.','.$unitssend_att.','.$unitsdead_att .',0,0,0,0,0,'.$to['owner'].','.$to['wref'].','.addslashes($to['name']) .',,,,'.$targettribe .','.$unitssend_def[0].','.$unitsdead_def[0].','.$rom .','.$unitssend_def[1].','.$unitsdead_def[1].','.$ger .','.$unitssend_def[2].','.$unitsdead_def[2].','.$gal .','.$unitssend_def[3].','.$unitsdead_def[3].','.$nat .','.$unitssend_def[4].','.$unitsdead_def[4].','.$natar .','.$unitssend_def[5].','.$unitsdead_def[5] .','.$DefenderHeroesTot.','.$DefenderHeroesDead .','.$info_ram.','.$info_cat.','.$info_chief.','.$info_spy .','.$data['t11'].','.$dead11.','.$herosend_def.','.$deadhero .',,'.$unitstraped_att; } else { if (isset($village_destroyed) && $village_destroyed == 1 && $can_destroy == 1) { if (strpos($info_cat, "The village has") === false) { $info_cat .= "Information" . "\"Catapult\"" . " The village has been destroyed."; } } $data2 = ''.$from['owner'].','.$from['wref'].','.$owntribe.','.$unitssend_att.','.$unitsdead_att .','.$steal[0].','.$steal[1].','.$steal[2].','.$steal[3].','.$battlepart['bounty'] .','.$to['owner'].','.$to['wref'].','.addslashes($to['name']) .',,,,'.$targettribe .','.$unitssend_def[0].','.$unitsdead_def[0].','.$rom .','.$unitssend_def[1].','.$unitsdead_def[1].','.$ger .','.$unitssend_def[2].','.$unitsdead_def[2].','.$gal .','.$unitssend_def[3].','.$unitsdead_def[3].','.$nat .','.$unitssend_def[4].','.$unitsdead_def[4].','.$natar .','.$unitssend_def[5].','.$unitsdead_def[5] .','.$DefenderHeroesTot.','.$DefenderHeroesDead .','.$info_ram.','.$info_cat.','.$info_chief.','.(isset($info_spy) ? $info_spy : '') .',,'.$data['t11'].','.$dead11.','.$herosend_def.','.$deadhero .','.$unitstraped_att; $data2 .= ','.($info_trap !== '' ? addslashes($info_trap) : '').',,'.$info_troop.','.$info_hero; } $data_fail = ''.$from['owner'].','.$from['wref'].','.$owntribe.','.$unitssend_att.','.$unitsdead_att .','.$steal[0].','.$steal[1].','.$steal[2].','.$steal[3].','.$battlepart['bounty'] .','.$to['owner'].','.$to['wref'].','.addslashes($to['name']) .',,,,'.$targettribe .','.$unitssend_deff[0].','.$unitsdead_deff[0].','.$rom .','.$unitssend_deff[1].','.$unitsdead_deff[1].','.$ger .','.$unitssend_deff[2].','.$unitsdead_deff[2].','.$gal .','.$unitssend_deff[3].','.$unitsdead_deff[3].','.$nat .','.$unitssend_deff[4].','.$unitsdead_deff[4].','.$natar .','.$unitssend_deff[5].','.$unitsdead_deff[5] .','.$DefenderHeroesTot.','.$DefenderHeroesDead .',,,'.$data['t11'].','.$dead11.','.$unitstraped_att .',,'.$info_ram.','.$info_cat.','.$info_chief.','.$info_troop.','.$info_hero; return ['data2' => $data2, 'data_fail' => $data_fail]; } /** * Distribute the battle bounty across the resources actually available in * the target (after cranny protection) and return how much of each is taken. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $available [wood, clay, iron, crop] lootable amounts. * @param int $bounty Total carry capacity / bounty from the battle. * @return array [wood, clay, iron, crop] amounts actually stolen. */ private function applyBounty(array $available, $bounty) { $avtotal = $available; $steal = [0, 0, 0, 0]; $btotal = $bounty; $bmod = 0; for ($i = 0; $i < 5; $i++) { for ($j = 0; $j < 4; $j++) { if (isset($avtotal[$j])) { if ($avtotal[$j] < 1) unset($avtotal[$j]); } } // No resources left to take if (empty($avtotal) || ($btotal < 1 && $bmod < 1)) break; if ($btotal < 1) { while ($bmod) { // random select $rs = array_rand($avtotal); if (isset($avtotal[$rs])) { $avtotal[$rs] -= 1; $steal[$rs] += 1; $bmod -= 1; } } } // handle unbalanced amounts. $btotal += $bmod; $bmod = $btotal % count($avtotal); $btotal -= $bmod; $bsplit = $btotal / count($avtotal); $max_steal = (min($avtotal) < $bsplit) ? min($avtotal) : $bsplit; for ($j = 0; $j < 4; $j++) { if (isset($avtotal[$j])) { $avtotal[$j] -= $max_steal; $steal[$j] += $max_steal; $btotal -= $max_steal; } } } return $steal; } /** * Apply battle casualties to the defender's own (in-village) troops, persist * the losses, and return the per-unit dead map used later for points/reports. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current attack row (uses 'to'). * @param int $targettribe Defender tribe (1-5). * @param array $battlepart Battle result (index 2 = defender kill ratio, * 'deadherodef' = defender hero losses). * @return array Map of dead own troops: keys are the unit index (int) plus 'hero'. */ private function applyOwnDefenceCasualties($data, $targettribe, $battlepart) { global $database; $owndead = []; $unitlist = $database->getUnit($data['to'], false); $start = ($targettribe - 1) * 10 + 1; $end = ($targettribe * 10); $unitModifications_units = []; $unitModifications_amounts = []; $unitModifications_modes = []; for ($i = $start; $i <= $end; $i++) { if ($unitlist) { $owndead[$i] = round($battlepart[2] * $unitlist['u'.$i]); $unitModifications_units[] = $i; $unitModifications_amounts[] = $owndead[$i]; $unitModifications_modes[] = 0; } } $owndead['hero'] = 0; if ($unitlist) { $owndead['hero'] = (isset($battlepart['deadherodef']) ? $battlepart['deadherodef'] : ''); $unitModifications_units[] = 'hero'; $unitModifications_amounts[] = $owndead['hero']; $unitModifications_modes[] = 0; } // modify units in DB $database->modifyUnit($data['to'], $unitModifications_units, $unitModifications_amounts, $unitModifications_modes); return $owndead; } /** * Apply battle casualties to the reinforcement (foreign) troops stationed in * the defender's village: compute each reinforcement's dead units/hero, persist * them (modifyEnforce), notify the reinforcing players, and delete fully-wiped * reinforcements. Accumulates the dead counts into $alldead and the dead heroes * into $DefenderHeroesDeadArray (both passed by reference), and reports which * tribes are present among the reinforcements. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current attack row (uses 'to'). * @param array $battlepart Battle result (index 2 = defender kill ratio, * 'deadheroref' = dead hero per enforce id). * @param array $from Attacker village info (uses 'wref'). * @param array $to Defender village info (uses 'name','wref'). * @param int $ownally Attacker's alliance id (for notices). * @param int $AttackArrivalTime Timestamp stamped on the reinforcement notices. * @param mixed $scout Scout marker; notices/deletion only fire when empty. * @param array $alldead [in/out] Per-unit dead reinforcements (+ 'hero'), accumulated. * @param array $DefenderHeroesDeadArray [in/out] Dead reinforcement heroes per tribe, accumulated. * @return array Tribe-presence flags among the reinforcements: keys rom,ger,gal,nat,natar (0/1). */ private function applyReinforcementCasualties($data, $battlepart, $from, $to, $ownally, $AttackArrivalTime, $scout, array &$alldead, array &$DefenderHeroesDeadArray) { global $database; $rom = $ger = $gal = $nat = $natar = 0; //kill other defence in village // ... once again, units could have changed, so we need to reselect $enforcementarray3 = $database->getEnforceVillage($data['to'], 0); foreach ($enforcementarray3 as $enforce) { $life = ''; $notlife = ''; $wrong = false; if ($enforce['from'] != 0) { $tribe = $database->getUserArray($database->getVillageField($enforce['from'], "owner"), 1)["tribe"]; } else { $tribe = 4; } $start = ($tribe - 1) * 10 + 1; $end = ($tribe * 10); unset($dead); switch ($tribe) { case 1: $rom = 1; break; case 2: $ger = 1; break; case 3: $gal = 1; break; case 4: $nat = 1; break; case 5: default: $natar = 1; break; } $enforceModificationsById = []; for ($i = $start; $i <= $end; $i++) { if ($enforce['u'.$i] > '0') { if (!isset($enforceModificationsById[$enforce['id']])) { $enforceModificationsById[$enforce['id']] = [ 'units' => [], 'amounts' => [], 'modes' => [] ]; } $enforceModificationsById[$enforce['id']]['units'][] = $i; $enforceModificationsById[$enforce['id']]['amounts'][] = (isset($battlepart[2]) ? round($battlepart[2]*$enforce['u'.$i]) : 0); $enforceModificationsById[$enforce['id']]['modes'][] = 0; $dead[$i] = (isset($battlepart[2]) ? round($battlepart[2]*$enforce['u'.$i]) : 0); $checkpoint = (isset($battlepart[2]) ? round($battlepart[2]*$enforce['u'.$i]) : 0); if (!isset($enforce['u'.$i])) $enforce['u'.$i] = 0; $alldead[$i] += $dead[$i]; $wrong = $checkpoint != $enforce['u'.$i]; } else { $dead[$i] = 0; } } if ($enforce['hero'] > 0) { $enforceModificationsById[$enforce['id']]['units'][] = 'hero'; $enforceModificationsById[$enforce['id']]['amounts'][] = $battlepart['deadheroref'][$enforce['id']]; $enforceModificationsById[$enforce['id']]['modes'][] = 0; $dead['hero'] = $battlepart['deadheroref'][$enforce['id']]; $alldead['hero'] += $dead['hero']; $wrong = $dead['hero'] != $enforce['hero']; //Collecting information for the report $reinfTribe = ($enforce['from'] == 0) ? 4 : $database->getUserField($database->getVillageField($enforce['from'], "owner"), "tribe", 0); $DefenderHeroesDeadArray[$reinfTribe] += $dead['hero']; } // modify enforce in DB foreach ($enforceModificationsById as $enforceId => $enforceArray) { $database->modifyEnforce($enforceId, $enforceArray['units'], $enforceArray['amounts'], $enforceArray['modes']); } $notlife = ''.$dead[$start].','.$dead[$start+1].','.$dead[$start+2].','.$dead[$start+3].','.$dead[$start+4].','.$dead[$start+5].','.$dead[$start+6].','.$dead[$start+7].','.$dead[$start+8].','.$dead[$start+9].''; $notlife1 = $dead[$start]+$dead[$start+1]+$dead[$start+2]+$dead[$start+3]+$dead[$start+4]+$dead[$start+5]+$dead[$start+6]+$dead[$start+7]+$dead[$start+8]+$dead[$start+9]; $life = ''.$enforce['u'.$start.''].','.$enforce['u'.($start+1).''].','.$enforce['u'.($start+2).''].','.$enforce['u'.($start+3).''].','.$enforce['u'.($start+4).''].','.$enforce['u'.($start+5).''].','.$enforce['u'.($start+6).''].','.$enforce['u'.($start+7).''].','.$enforce['u'.($start+8).''].','.$enforce['u'.($start+9).''].''; $life1 = $enforce['u'.$start.'']+$enforce['u'.($start+1).'']+$enforce['u'.($start+2).'']+$enforce['u'.($start+3).'']+$enforce['u'.($start+4).'']+$enforce['u'.($start+5).'']+$enforce['u'.($start+6).'']+$enforce['u'.($start+7).'']+$enforce['u'.($start+8).'']+$enforce['u'.($start+9).'']; $lifehero = (isset($enforce['hero']) ? $enforce['hero'] : 0); $notlifehero = (isset($dead['hero']) ? $dead['hero'] : 0); $totallife = (isset($enforce['hero']) ? $enforce['hero'] : 0)+$life1; $totalnotlife = (isset($dead['hero']) ? $dead['hero'] : 0)+$notlife1; //NEED TO SEND A RAPPORTAGE!!! $data2 = ''.$database->getVillageField($enforce['from'], "owner").','.$to['wref'].','.addslashes($to['name']).','.$tribe.','.$life.','.$notlife.','.$lifehero.','.$notlifehero.','.$enforce['from'].''; if (empty($scout)) { if ($totalnotlife == 0) { $database->addNotice($database->getVillageField($enforce['from'], "owner"), $from['wref'], $ownally, 15, 'Reinforcement in '.addslashes($to['name']).' was attacked', $data2, $AttackArrivalTime); } else if ($totallife > $totalnotlife) { $database->addNotice($database->getVillageField($enforce['from'], "owner"), $from['wref'], $ownally, 16, 'Reinforcement in '.addslashes($to['name']).' was attacked', $data2, $AttackArrivalTime); } else { $database->addNotice($database->getVillageField($enforce['from'], "owner"), $from['wref'], $ownally, 17, 'Reinforcement in '.addslashes($to['name']).' was attacked', $data2, $AttackArrivalTime); } //delete reinf sting when its killed all. if (!$wrong) $database->deleteReinf($enforce['id']); } } return ['rom' => $rom, 'ger' => $ger, 'gal' => $gal, 'nat' => $nat, 'natar' => $natar]; } /** * Collect the defender-side report data BEFORE casualties are applied: reset and * rebuild the reinforcement unit totals ($DefenderEnf) and the per-tribe hero * totals from a fresh re-select, fold reinforcement heroes into the defender's * hero count, build the "units sent" report rows (own troops + reinforcements, * plus their masked variants), and (re)initialise the per-tribe dead-hero * accumulator to zero. Returns everything the caller needs downstream. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current attack row (uses 'to'). * @param int $targettribe Defender tribe (1-5). * @param array $Defender Defender unit array (own troops + 'hero'); passed by value, * its 'hero' is folded with reinforcement heroes and returned * via the 'defenderHero' key. * @return array Report bundle: DefenderEnf, DefenderHeroesTotArray, * DefenderHeroesDeadArray, unitssend_def, unitssend_deff, * totalsend_alldef, defenderHero. */ private function collectReinforcementReport($data, $targettribe, $Defender) { global $database; $DefenderEnf = []; $DefenderHeroesTotArray = []; $DefenderHeroesDeadArray = []; $unitssend_def = []; $unitssend_deff = []; //Resetting the enforcement arrays for ($i = 1; $i <= 50; $i++) { $DefenderEnf['u'.$i] = 0; if ($i <= 5) { $DefenderHeroesTotArray[$i] = 0; $DefenderHeroesDeadArray[$i] = 0; } } // our reinforcements count could have changed at this point, thus the re-select $enforcementarray2 = $database->getEnforceVillage($data['to'], 0); if (count($enforcementarray2) > 0) { foreach ($enforcementarray2 as $enforce2) { for ($i = 1; $i <= 50; $i++) { $DefenderEnf['u'.$i] += $enforce2['u'.$i]; } //Divide heroes by tribe if ($enforce2['hero'] > 0) { $reinfTribe = ($enforce2['from'] == 0) ? 4 : $database->getUserField($database->getVillageField($enforce2['from'], "owner"), "tribe", 0); $DefenderHeroesTotArray[$reinfTribe] += $enforce2['hero']; $Defender['hero'] += $enforce2['hero']; } } } $totalsend_alldef = 0; //Own troops $ownTroops = array_slice($Defender, ($targettribe - 1) * 10 + 1, 10); $totalsend_alldef = array_sum($ownTroops); //Collecting informations for the report $unitssend_def[0] = implode(",", $ownTroops); $unitssend_deff[0] = '?,?,?,?,?,?,?,?,?,?,'; for ($i = 1; $i <= 5; $i++) { //Reinforcements $reinfTroops = array_slice($DefenderEnf, ($i - 1) * 10, 10); $totalsend_alldef += array_sum($reinfTroops); //Collecting informations for the report $unitssend_def[$i] = implode(",", $reinfTroops); $unitssend_deff[$i] = $unitssend_deff[0]; } $totalsend_alldef += $Defender['hero']; return [ 'DefenderEnf' => $DefenderEnf, 'DefenderHeroesTotArray' => $DefenderHeroesTotArray, 'DefenderHeroesDeadArray' => $DefenderHeroesDeadArray, 'unitssend_def' => $unitssend_def, 'unitssend_deff' => $unitssend_deff, 'totalsend_alldef' => $totalsend_alldef, 'defenderHero' => $Defender['hero'], ]; } /** * Compute attacker/defender total populations and fetch their village lists. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $to Defender village info (getMInfo/getOMInfo). * @param array $from Attacker village info (getMInfo). * @param int $isoasis Whether the target is an oasis (defender pop counts only for villages). * @param int $defpop Initial defender population (accumulated onto). * @param int $attpop Initial attacker population (accumulated onto). * @return array{varray:array,varray1:array,defpop:int,attpop:int} */ private function calculatePopulations($to, $from, $isoasis, $defpop, $attpop) { global $database; $varray = $database->getProfileVillages($to['owner'], 0, false); if ($to['owner'] == $from['owner']) $varray1 = $varray; else $varray1 = $database->getProfileVillages($from['owner'], 0, false); // total population of the defender if($isoasis == 0){ foreach($varray as $defenderVillage) $defpop += $defenderVillage['pop']; } // total population of the attacker foreach($varray1 as $attackerVillage) $attpop += $attackerVillage['pop']; return [ 'varray' => $varray, 'varray1' => $varray1, 'defpop' => $defpop, 'attpop' => $attpop, ]; } /** * Resolve how many incoming attacker units get caught in the defender's * traps (Gaul trapper / Natar capital), update the trap counters and the * prisoners table, and subtract the trapped troops from the attacking army. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current attack row. * @param array $Defender Defender unit counts (reads u99 / u99o). * @param array $Attacker Attacker units (u + uhero). * @param bool $NatarCapital True when the target is a Natar capital (all troops trapped). * @param int $scout 1 for a scouting attack (no trapping). * @param int $start First unit index of the attacker tribe. * @param int $end Last unit index of the attacker tribe. * @return array { * traped: int[] trapped count per unit slot (1..11), * totaltraped_att: int total trapped troops, * Attacker: array attacker units with trapped troops removed * } */ private function calculateTrappedUnits($data, $Defender, $Attacker, $NatarCapital, $scout, $start, $end) { global $database; $traped = []; for($i = 1; $i <= 11; $i++) $traped[$i] = 0; $totaltraped_att = 0; //impossible to attack or scout NATAR Capital Village if ($NatarCapital){ for($i = 1; $i <= 11; $i++) $traped[$i] = $data['t'.$i]; } elseif(empty($scout)) { $traps = max($Defender['u99'] - $Defender['u99o'], 0); $totalTroops = 0; for($i = 1; $i <= 11; $i++) $totalTroops += $data['t'.$i]; if($traps >= $totalTroops){ for($i = 1; $i <= 11; $i++) $traped[$i] = $data['t'.$i]; } else if($totalTroops > 0) { $multiplier = $traps / $totalTroops; //The hero is excluded, because it can be only trapped if traps > totalTroops for($i = 1; $i <= 10; $i++){ $trappedUnits = intval($data['t'.$i] * $multiplier); $traped[$i] = $trappedUnits; $traps -= $trappedUnits; } while($traps > 0){ //There are some traps left, let's distribute them for($i = 1; $i <= 10 && $traps > 0; $i++){ if($data['t'.$i] != 0){ $traped[$i]++; $traps--; } } } } else { for($i = 1; $i <= 11; $i++) $traped[$i] = 0; } } if(empty($scout) || $NatarCapital){ for ($i = 1; $i <= 11; $i++){ $totaltraped_att += $traped[$i]; } $database->modifyUnit($data['to'], ["99o"], [$totaltraped_att], [1]); for($i = $start; $i <= $end; $i++){ $j = $i-$start+1; $Attacker['u'.$i] -= $traped[$j]; } $Attacker['uhero'] -= $traped[11]; if($totaltraped_att > 0){ $prisoners2 = $database->getPrisoners2($data['to'], $data['from'], false); if(empty($prisoners2)){ $database->addPrisoners($data['to'],$data['from'],$traped[1],$traped[2],$traped[3],$traped[4],$traped[5],$traped[6],$traped[7],$traped[8],$traped[9],$traped[10],$traped[11]); }else{ $database->updatePrisoners($data['to'],$data['from'],$traped[1],$traped[2],$traped[3],$traped[4],$traped[5],$traped[6],$traped[7],$traped[8],$traped[9],$traped[10],$traped[11]); } } } return [ 'traped' => $traped, 'totaltraped_att' => $totaltraped_att, 'Attacker' => $Attacker, ]; } /** * Apply ram damage to the defender's wall during a normal attack (type 3): * compute the new wall level, update it in the database (recounting the * village population if the wall is destroyed), build the report fragment, * and recalculate the battle when the wall level changed. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current attack row (uses t7, to). * @param int $walllevel Current wall level. * @param int $wallid Wall building field id (40). * @param int $ram_pic Ram unit id (for the report fragment). * @param array $battlepart Battle result (provides the ram damage factors). * @param array $ctx Battle context for the post-damage recalculation * (Attacker, Defender, tribes, populations, AB tech, ...). * @return array { battlepart: array (possibly recalculated), info_ram: string } */ private function applyRamDamage($data, $walllevel, $wallid, $ram_pic, $battlepart, array $ctx) { global $database, $battle; $info_ram = "".$ram_pic.",There is no wall to destroy."; if($walllevel > 0){ $ramsDamage = $battle->calculateCatapultsDamage($data['t7'], $battlepart['rams']['upgrades'], $battlepart['rams']['durability'], $battlepart['rams']['attackDefenseRatio'], $battlepart['rams']['strongerBuildings'], $battlepart['rams']['moraleBonus']); $newLevel = $battle->calculateNewBuildingLevel($walllevel, $ramsDamage); if ($newLevel == 0){ $info_ram = "".$ram_pic.",Wall destroyed."; $database->setVillageLevel($data['to'], ["f".$wallid, "f".$wallid."t"], [0, 0]); $this->recountPop($data['to']); }elseif ($newLevel == $walllevel){ $info_ram = "".$ram_pic.",Wall was not damaged."; }else{ $info_ram = "".$ram_pic.",Wall damaged from level ".$walllevel." to level ".$newLevel."."; $database->setVillageLevel($data['to'],"f".$wallid."",$newLevel); } //If the wall got damaged/destroyed during the attack //we need to recalculate the whole battle if($newLevel != $walllevel){ $battlepart = $battle->calculateBattle($ctx['Attacker'], $ctx['Defender'], $newLevel, $ctx['att_tribe'], $ctx['def_tribe'], $ctx['residence'], $ctx['attpop'], $ctx['defpop'], $ctx['type'], $ctx['def_ab'], $ctx['att_ab1'], $ctx['att_ab2'], $ctx['att_ab3'], $ctx['att_ab4'], $ctx['att_ab5'], $ctx['att_ab6'], $ctx['att_ab7'], $ctx['att_ab8'], $ctx['tblevel'], $ctx['stonemason'], $newLevel, 0, 0, 0, $ctx['AttackerID'], $ctx['DefenderID'], $ctx['AttackerWref'], $ctx['DefenderWref'], $ctx['conqureby'], $ctx['enforcementarray']); } } return ['battlepart' => $battlepart, 'info_ram' => $info_ram]; } /** * Build the attacking army for the current attack: per-slot unit counts * (u + uhero) plus the catapult / ram / chief / scout unit ids * used in the battle report. Oasis attacks include the Nature siege/chief * slots (37/38/39). Pure behaviour-preserving extraction (issue #155). * * @param array $attackRow The attack row (t1..t11, tribe-relative). * @param int $owntribe Attacker tribe (1..5). * @param int $isoasis 0 for a village target, otherwise an oasis. * @return array { Attacker, start, end, u, catp_pic, ram_pic, chief_pic, spy_pic, hero_pic } */ private function buildAttackerUnits(array $attackRow, $owntribe, $isoasis) { $Attacker = []; $start = ($owntribe - 1) * 10 + 1; $end = $owntribe * 10; $u = ($owntribe - 1) * 10; if ($isoasis == 0) { $catapult = [8, 18, 28, 48]; $ram = [7, 17, 27, 47]; $chief = [9, 19, 29, 49]; } else { $catapult = [8, 18, 28, 38, 48]; $ram = [7, 17, 27, 37, 47]; $chief = [9, 19, 29, 39, 49]; } $spys = [4, 14, 23, 44]; $catp_pic = $ram_pic = $chief_pic = $spy_pic = null; for ($i = $start; $i <= $end; $i++) { $y = $i - $u; $Attacker['u'.$i] = $attackRow['t'.$y]; //there are catas if (in_array($i, $catapult)) $catp_pic = $i; if (in_array($i, $ram)) $ram_pic = $i; if (in_array($i, $chief)) $chief_pic = $i; if (in_array($i, $spys)) $spy_pic = $i; } $Attacker['uhero'] = $attackRow['t11']; return [ 'Attacker' => $Attacker, 'start' => $start, 'end' => $end, 'u' => $u, 'catp_pic' => $catp_pic, 'ram_pic' => $ram_pic, 'chief_pic' => $chief_pic, 'spy_pic' => $spy_pic, 'hero_pic' => 'hero', ]; } /** * Gather the defender's units for the current attack: the village's own * troops (normalised to non-negative ints) plus the aggregated reinforcement * totals, and the raw reinforcement rows. Used for both village and oasis * targets. Pure behaviour-preserving extraction (issue #155). * * @param int $wref Defending village/oasis wref. * @return array { Defender, enforDefender, enforcementarray } */ private function buildDefenderUnits($wref) { global $database; $enforDefender = []; $Defender = $database->getUnit($wref, false); $enforcementarray = $database->getEnforceVillage($wref, 0); if (count($enforcementarray) > 0) { foreach ($enforcementarray as $enforce) { for ($i = 1; $i <= 50; $i++) { if (!isset($enforDefender['u'.$i])) { $enforDefender['u'.$i] = 0; } $enforDefender['u'.$i] += $enforce['u'.$i]; } if (!isset($enforDefender['hero'])) { $enforDefender['hero'] = 0; } $enforDefender['hero'] += $enforce['hero']; } } for ($i = 1; $i <= 50; $i++) { if (!isset($Defender['u'.$i]) || empty($Defender['u'.$i]) || $Defender['u'.$i] < 0) { $Defender['u'.$i] = 0; } } if (!isset($Defender['hero']) || empty($Defender['hero']) || $Defender['hero'] < 0) { $Defender['hero'] = 0; } return [ 'Defender' => $Defender, 'enforDefender' => $enforDefender, 'enforcementarray' => $enforcementarray, ]; } /** * Resolve the per-attack context that does not depend on the target type: * the attacker village/owner data (tribe, alliance), the war references and * a few base flags. Read-only — no DB writes. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current attack row. * @return array {isoasis, AttackArrivalTime, AttackerWref, DefenderWref, * NatarCapital, AttackerID, owntribe, ownally, from, fromF} */ private function resolveAttackContext($data) { global $database; $owner = $this->getCachedUser($database->getVillageField($data['from'], "owner"), 1); return [ 'isoasis' => $data['oasistype'], 'AttackArrivalTime' => $data['endtime'], 'AttackerWref' => $data['from'], 'DefenderWref' => $data['to'], 'NatarCapital' => false, 'AttackerID' => $owner['id'], 'owntribe' => $owner['tribe'], 'ownally' => $owner['alliance'], 'from' => $database->getMInfo($data['from']), 'fromF' => $database->getVillage($data['from']), ]; } /** * Resolve the defender target context for a VILLAGE attack: target owner * (tribe/alliance), map info, conquest flag, evasion inputs and the battle * environment (wall, armory/blacksmith tech, residence, siege masonry). * Read-only — no DB writes. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current attack row. * @param array $dataarray Full batch of completed attacks. * @param int $data_num Index of $data within $dataarray. * @param int $owntribe Attacker tribe. * @return array Target + battle-environment context. */ private function resolveVillageTarget($data, $dataarray, $data_num, $owntribe) { global $database, $units; $DefenderUserData = $this->getCachedUser($database->getVillageField($data['to'],"owner"),1); $DefenderID = $DefenderUserData["id"]; $targettribe = $DefenderUserData["tribe"]; $targetally = $DefenderUserData["alliance"]; $to = $database->getMInfo($data['to']); $toF = $database->getVillage($data['to']); $conqureby = 0; $NatarCapital = ($toF['owner'] == 3 && $toF['capital'] == 1); if(!isset($to['name']) || empty($to['name'])) $to['name'] = "[?]"; $DefenderUnit = $database->getUnit($data['to']); $evasion = $toF["evasion"]; $maxevasion = $DefenderUserData["maxevasion"]; $gold = $DefenderUserData["gold"]; $cannotsend = false; $movements = $database->getMovement(34, $data['to'], 1); for($y = 0; $y < count($movements); $y++){ if(property_exists($units, $y)){ $returntime = $units->$y['endtime'] - time(); if($units->$y['sort_type'] == 4 && $units->$y['from'] != 0 && $returntime <= 10){ $cannotsend = true; } } } //need to set these variables. $def_wall = $database->getFieldLevel($data['to'], 40, false); $att_tribe = $owntribe; $def_tribe = $targettribe; $attpop = $defpop = $residence = 0; $def_ab = []; //get level of palace or residence $residence = $database->getFieldLevelInVillage($data['to'], '25, 26', false); //type of attack $type = $dataarray[$data_num]['attack_type']; $scout = ($type == 1) ? 1 : 0; $ud = ($def_tribe - 1) * 10; $att_ab = $database->getABTech($data['from']); // Blacksmith level $att_ab1 = $att_ab['b1']; $att_ab2 = $att_ab['b2']; $att_ab3 = $att_ab['b3']; $att_ab4 = $att_ab['b4']; $att_ab5 = $att_ab['b5']; $att_ab6 = $att_ab['b6']; $att_ab7 = $att_ab['b7']; $att_ab8 = $att_ab['b8']; $armory = $database->getABTech($data['to']); // Armory level $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']; //rams attack $walllevel = 0; $wallid = 0; if (($data['t7']) > 0 && $type == 3) { $basearraywall = $to; if (($walllevel = $database->getFieldLevel($basearraywall['wref'], 40, false)) > 0){ $wallid = 40; } } $tblevel = 1; $stonemason = $database->getFieldLevelInVillage($data['to'], 34); return [ 'DefenderID' => $DefenderID, 'targettribe' => $targettribe, 'targetally' => $targetally, 'to' => $to, 'toF' => $toF, 'conqureby' => $conqureby, 'NatarCapital' => $NatarCapital, 'DefenderUnit' => $DefenderUnit, 'evasion' => $evasion, 'maxevasion' => $maxevasion, 'gold' => $gold, 'cannotsend' => $cannotsend, 'def_wall' => $def_wall, 'att_tribe' => $att_tribe, 'def_tribe' => $def_tribe, 'attpop' => $attpop, 'defpop' => $defpop, 'residence' => $residence, 'def_ab' => $def_ab, 'type' => $type, 'scout' => $scout, 'att_ab1' => $att_ab1, 'att_ab2' => $att_ab2, 'att_ab3' => $att_ab3, 'att_ab4' => $att_ab4, 'att_ab5' => $att_ab5, 'att_ab6' => $att_ab6, 'att_ab7' => $att_ab7, 'att_ab8' => $att_ab8, 'walllevel' => $walllevel, 'wallid' => $wallid, 'tblevel' => $tblevel, 'stonemason' => $stonemason, ]; } /** * Resolve the defender target context for an OASIS attack: target owner * (tribe/alliance), oasis map info, conquest flag and the (mostly fixed) * battle environment. Read-only — no DB writes. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current attack row. * @param array $dataarray Full batch of completed attacks. * @param int $data_num Index of $data within $dataarray. * @param int $owntribe Attacker tribe. * @return array Target + battle-environment context. */ private function resolveOasisTarget($data, $dataarray, $data_num, $owntribe) { global $database; $DefenderUserData = $this->getCachedUser($database->getOasisField($data['to'], "owner"),1); $DefenderID = $DefenderUserData["id"]; $targettribe = $DefenderUserData["tribe"]; $targetally = $DefenderUserData["alliance"]; $to = $database->getOMInfo($data['to']); $toF = $database->getOasisV($data['to']); $conqureby = $toF['conqured']; //need to set these variables. $def_wall = $residence = $attpop = 0; $att_tribe = $owntribe; $def_tribe = $targettribe; $defpop = 500; //type of attack $type = $dataarray[$data_num]['attack_type']; $scout = ($type == 1) ? 1 : 0; $att_ab1 = $att_ab2 = $att_ab3 = $att_ab4 = $att_ab5 = $att_ab6 = $att_ab7 = $att_ab8 = 0; $def_ab = []; $def_ab[31] = $def_ab[32] = $def_ab[33] = $def_ab[34] = $def_ab[35] = $def_ab[36] = $def_ab[37] = $def_ab[38] = 0; $walllevel = $tblevel = $stonemason = 0; return [ 'DefenderID' => $DefenderID, 'targettribe' => $targettribe, 'targetally' => $targetally, 'to' => $to, 'toF' => $toF, 'conqureby' => $conqureby, 'def_wall' => $def_wall, 'att_tribe' => $att_tribe, 'def_tribe' => $def_tribe, 'attpop' => $attpop, 'defpop' => $defpop, 'residence' => $residence, 'def_ab' => $def_ab, 'type' => $type, 'scout' => $scout, 'att_ab1' => $att_ab1, 'att_ab2' => $att_ab2, 'att_ab3' => $att_ab3, 'att_ab4' => $att_ab4, 'att_ab5' => $att_ab5, 'att_ab6' => $att_ab6, 'att_ab7' => $att_ab7, 'att_ab8' => $att_ab8, 'walllevel' => $walllevel, 'tblevel' => $tblevel, 'stonemason' => $stonemason, ]; } /** * Compute attacker/defender hero XP and victory points from the battle * casualties, persist them (hero XP, player points, alliance points) and * return the total defender losses. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $alldead Reinforcement casualties by global unit id (+ 'hero'). * @param array $owndead Defender own casualties by global unit id (+ 'hero'). * @param array $attackerDead Attacker casualties, 0-indexed t1..t11 (index 10 = hero). * @param int $targettribe Defender tribe id. * @param int $att_tribe Attacker tribe id. * @param array $Attacker Attacker troop row (uses 'uhero'). * @param mixed $AttackerHeroID Attacker hero id (for the XP write). * @param array $Defender Defender troop row (uses 'hero'). * @param array $DefendersHeroID Defender hero ids (for the XP writes). * @param array $toF Defender village (uses 'owner'). * @param array $from Attacker village info (uses 'owner'). * @param int $targetally Defender alliance id. * @param int $ownally Attacker alliance id. * @param mixed $heroxp OUT (by ref): attacker hero XP, set only if the attacker has a hero. * @param mixed $defheroxp OUT (by ref): per-defender hero XP, set only if the defender has a hero. * @return int Total defender losses (reinforcements + own troops). */ private function calculateHeroXpAndPoints( array $alldead, array $owndead, array $attackerDead, $targettribe, $att_tribe, $Attacker, $AttackerHeroID, $Defender, $DefendersHeroID, $toF, $from, $targetally, $ownally, &$heroxp, &$defheroxp ) { global $database; $totaldead_def = 0; $totalpoint_att = 0; for($i = 1 ;$i <= 50; $i++) { $unitarray = $GLOBALS["u".$i]; //Reinforcements dead troops $totaldead_def += $alldead[$i]; $totalpoint_att += ($alldead[$i] * $unitarray['pop']); //Own dead troops if($i >= ($targettribe - 1) * 10 + 1 && $i <= $targettribe * 10){ $totaldead_def += $owndead[$i]; $totalpoint_att += ($owndead[$i] * $unitarray['pop']); } } $totalpoint_att += ((isset($alldead['hero']) ? $alldead['hero'] : 0) * 6); $totalpoint_att += ((isset($owndead['hero']) ? $owndead['hero'] : 0) * 6); if ($Attacker['uhero'] > 0){ $heroxp = $totalpoint_att; $database->modifyHeroXp("experience", $heroxp, $AttackerHeroID); } for($i = 1; $i <= 10; $i++){ $unitarray = $GLOBALS["u".(($att_tribe - 1) * 10 + $i)]; if ( !isset($totalpoint_def) ) { $totalpoint_def = 0; } $totalpoint_def += ($attackerDead[$i - 1] * $unitarray['pop']); } $totalpoint_def += $attackerDead[10] * 6; if($Defender['hero'] > 0){ //counting heroxp $defheroxp = intval($totalpoint_def / count($DefendersHeroID)); foreach($DefendersHeroID as $HeroID){ $database->modifyHeroXp("experience",$defheroxp,$HeroID); } } // we don't need these two variables anymore unset($AttackerHeroID, $DefendersHeroID); $database->modifyPoints( $toF['owner'], ['dpall', 'dp'], [$totalpoint_def, $totalpoint_def] ); $database->modifyPoints( $from['owner'], ['apall', 'ap'], [$totalpoint_att, $totalpoint_att] ); $database->modifyPointsAlly( $targetally, ['Adp', 'dp'], [$totalpoint_def, $totalpoint_def] ); $database->modifyPointsAlly( $ownally, ['Aap', 'ap'], [$totalpoint_att, $totalpoint_att] ); return $totaldead_def; } /** * Resolve the resources lootable from the battle target after cranny * protection: compute the cranny efficiency, pull the current (capped) * stocks of the target village/oasis and derive the lootable amounts. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current attack row (uses 'to'). * @param int $isoasis Oasis flag (0 = village). * @param int $conqureby Owner village of a conquered oasis (0 = none). * @param int $owntribe Attacker tribe id (Teuton cranny bonus). * @param int $targettribe Defender tribe id (Gaul cranny bonus). * @param mixed $crannySpy OUT (by ref): cranny value seen by a scout; set only for village targets (isoasis == 0). * @return array ['totclay','totiron','totwood','totcrop','avclay','aviron','avwood','avcrop']. */ private function resolveResourcesAfterBattle($data, $isoasis, $conqureby, $owntribe, $targettribe, &$crannySpy) { global $database, $bid23; if ($isoasis == 0){ // get total cranny value: $buildarray = $database->getResourceLevel($data['to']); $cranny = 0; for($i = 19; $i < 39;$i++){ if($buildarray['f'.$i.'t'] == 23){ $cranny += $bid23[$buildarray['f'.$i]]['attri'] * CRANNY_CAPACITY; } } //cranny efficiency $atk_bonus = ($owntribe == 2) ? (4 / 5) : 1; $def_bonus = ($targettribe == 3) ? 2 : 1; $to_owner = $database->getVillageField($data['to'], "owner"); $crannySpy = $database->getArtifactsValueInfluence($to_owner, $data['to'], 7, $cranny * $def_bonus); $cranny_eff = $crannySpy * $atk_bonus; // work out available resources. $this->updateRes($data['to']); $this->pruneResource(); $villageData = $database->getVillageFields($data['to'], 'clay, iron, wood, crop', false); $totclay = $villageData['clay']; $totiron = $villageData['iron']; $totwood = $villageData['wood']; $totcrop = $villageData['crop']; }else{ $cranny_eff = 0; if ($conqureby > 0) { //10% from owner proc village owner - fix by ronix - exploit fixed by iopietro $this->updateRes($conqureby); $this->pruneResource(); $villageData = $database->getVillageFields($conqureby, 'clay, iron, wood, crop', false); $totclay = intval($villageData['clay'] / 10); $totiron = intval($villageData['iron'] / 10); $totwood = intval($villageData['wood'] / 10); $totcrop = intval($villageData['crop'] / 10); }else{ // work out available resources. $this->updateORes($data['to']); $this->pruneOResource(); $oasisData = $database->getOasisFields($data['to'], false); $totclay = $oasisData['clay']; $totiron = $oasisData['iron']; $totwood = $oasisData['wood']; $totcrop = $oasisData['crop']; } } $avclay = floor($totclay - $cranny_eff); $aviron = floor($totiron - $cranny_eff); $avwood = floor($totwood - $cranny_eff); $avcrop = floor($totcrop - $cranny_eff); $avclay = ($avclay < 0) ? 0 : $avclay; $aviron = ($aviron < 0) ? 0 : $aviron; $avwood = ($avwood < 0) ? 0 : $avwood; $avcrop = ($avcrop < 0) ? 0 : $avcrop; return [ 'totclay' => $totclay, 'totiron' => $totiron, 'totwood' => $totwood, 'totcrop' => $totcrop, 'avclay' => $avclay, 'aviron' => $aviron, 'avwood' => $avwood, 'avcrop' => $avcrop, ]; } /** * Resolve the attacker hero's outcome after the battle: build the hero * report line ($info_hero), and on a surviving hero handle oasis conquest / * loyalty reduction (oasis targets) or artifact claiming with possible * village destruction (village targets). * Pure behaviour-preserving extraction (refactor for issue #155). * * @param array $data Current attack row (uses 't11', 'from', 'to'). * @param array $from Attacker village info (uses 'owner'). * @param array $to Defender village/oasis info (uses 'owner', 'loyalty'). * @param int $dead11 Attacker hero casualties. * @param int $traped11 Attacker hero trapped count. * @param mixed $heroxp Attacker hero XP gained this battle. * @param string $hero_pic Hero unit picture id (report markup). * @param int $isoasis Oasis flag (0 = village). * @param int $type Attack type (3 = normal with rams/catas). * @param int $targettribe Defender tribe id. * @param string $info_cat Catapult report fragment (read for the destruction notice). * @param string $info_hero IN/OUT (by ref): hero report line. * @param int $can_destroy IN/OUT (by ref): village-destruction allowed flag. * @param int $village_destroyed IN/OUT (by ref): village-destroyed flag. * @return void */ private function handleHeroPostBattle($data, $from, $to, $dead11, $traped11, $heroxp, $hero_pic, $isoasis, $type, $targettribe, $info_cat, &$info_hero, &$can_destroy, &$village_destroyed) { global $database; if(($data['t11'] - $dead11 - $traped11) > 0){ //hero if ($heroxp == 0) { $xp = ""; $info_hero = $hero_pic.",Your hero had nothing to kill therefore gains no XP at all."; } else { $xp = " and gained ".$heroxp." XP from the battle."; $info_hero = $hero_pic.",Your hero gained ".$heroxp." XP."; } if ($isoasis != 0) { //oasis fix by ronix if ($to['owner'] != $from['owner']) { $troopcount = $database->countOasisTroops($data['to'], false); $canqured = $database->canConquerOasis($data['from'], $data['to'], false); if ($canqured == 1 && $troopcount == 0) { $database->conquerOasis($data['from'], $data['to']); $info_hero = $hero_pic.",Your hero has conquered this oasis".$xp; }else{ if ($canqured == 3 && $troopcount == 0) { if ($type == 3) { $Oloyaltybefore = intval($to['loyalty']); //$database->modifyOasisLoyalty($data['to']); //$OasisInfo = $database->getOasisInfo($data['to']); $Oloyaltynow = intval($database->modifyOasisLoyalty($data['to']));//intval($OasisInfo['loyalty']); $info_hero = $hero_pic.",Your hero has reduced oasis loyalty to ".$Oloyaltynow." from ".$Oloyaltybefore.$xp; } else $info_hero = $hero_pic.",Could not reduce loyalty during raid".$xp; } } } } else { if ($heroxp == 0) $xp=" no XP from the battle."; else $xp=" gained ".$heroxp." XP from the battle."; $artifact = reset($database->getOwnArtefactInfo($data['to'])); if (!empty($artifact)) { if ($type == 3) { if (empty($artifactError = $database->canClaimArtifact($data['from'], $artifact['vref'], $artifact['size'], $artifact['type']))) { $database->claimArtefact($data['from'], $data['to'], $database->getVillageField($data['from'], "owner")); $info_hero = $hero_pic.",Your hero is carrying home the artifact ".$artifact['name']." and".$xp; // if the defender pop is 0 with no artefact, then destroy the village if($database->getVillageField($data['to'], "pop") == 0 || $targettribe == 5){ $can_destroy = $village_destroyed = 1; if(strpos($info_cat, "The village has") === false) $info_hero .= " The village has been destroyed."; } } else $info_hero = $hero_pic.",".$artifactError.$xp; } else $info_hero = $hero_pic.",Your hero could not claim an artifact during raid".$xp; } } }elseif($data['t11'] > 0) { if ($heroxp == 0) $xp = ""; else $xp = " but gained ".$heroxp." XP from the battle."; if ($traped11 > 0) $info_hero = $hero_pic.",Your hero was trapped".$xp; else $info_hero = $hero_pic.",Your hero died".$xp; } } /** * Send the battle/scout report notification to the defender. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param mixed $scout Truthy when the attack is a scouting run. * @param array $battlepart Battle result (uses 'casualties_attacker'). * @param array $from Attacker village info (uses 'owner', 'name'). * @param array $to Defender village/oasis info (uses 'owner', 'wref', 'name'). * @param int $targetally Defender alliance id. * @param string $data2 Combat-report payload. * @param int $AttackArrivalTime Battle resolution timestamp. * @param string $unitsdead_att Attacker dead units string. * @param string $unitssend_att Attacker sent units string. * @param bool $defspy Whether the defence has scouts. * @param string $info_troop Troop-return report fragment. * @param int $totalsend_alldef Total defending troops (incl. reinforcements). * @param int $totalsend_att Total attacking troops sent. * @param int $totaldead_att Total attacking troops killed. * @param int $totaltraped_att Total attacking troops trapped. * @param int $totaldead_alldef Total defending troops killed. * @return void */ private function sendBattleNotifications($scout, $battlepart, $from, $to, $targetally, $data2, $AttackArrivalTime, $unitsdead_att, $unitssend_att, $defspy, $info_troop, $totalsend_alldef, $totalsend_att, $totaldead_att, $totaltraped_att, $totaldead_alldef) { global $database; //Undetected and detected in here. if(!empty($scout)){ for($i = 1; $i <= 10; $i++){ if($battlepart['casualties_attacker'][$i]){ if($from['owner'] == 3){ $database->addNotice($to['owner'],$to['wref'],$targetally,20,''.addslashes($from['name']).' scouts '.addslashes($to['name']).'',$data2,$AttackArrivalTime); break; }else if($unitsdead_att == $unitssend_att && $defspy){ //fix by ronix $database->addNotice($to['owner'],$to['wref'],$targetally,20,''.addslashes($from['name']).' scouts '.addslashes($to['name']).'',$data2.',,'.$info_troop,$AttackArrivalTime); break; }else if($defspy){ //fix by ronix $database->addNotice($to['owner'],$to['wref'],$targetally,21,''.addslashes($from['name']).' scouts '.addslashes($to['name']).'',$data2,$AttackArrivalTime); break; } } } }else{ if($totalsend_alldef == 0 && $totalsend_att - ($totaldead_att + (isset($totaltraped_att) ? $totaltraped_att : 0)) > 0){ $database->addNotice($to['owner'],$to['wref'],$targetally,7,''.addslashes($from['name']).' attacks '.addslashes($to['name']).'',$data2,$AttackArrivalTime); }else if($totaldead_alldef == 0){ $database->addNotice($to['owner'],$to['wref'],$targetally,4,''.addslashes($from['name']).' attacks '.addslashes($to['name']).'',$data2,$AttackArrivalTime); }else if($totalsend_alldef > $totaldead_alldef){ $database->addNotice($to['owner'],$to['wref'],$targetally,5,''.addslashes($from['name']).' attacks '.addslashes($to['name']).'',$data2,$AttackArrivalTime); }else if($totalsend_alldef == $totaldead_alldef){ $database->addNotice($to['owner'],$to['wref'],$targetally,6,''.addslashes($from['name']).' attacks '.addslashes($to['name']).'',$data2,$AttackArrivalTime); } } } /** * Finalize the fate of the attacking troops: if any survived they return * home (attacker report + return movement + loot transfer / RR points / * leftover-chief enforcement), otherwise they die without returning (only * the all-dead report is sent). Pure behaviour-preserving extraction * (refactor for issue #155). * * @param array $data Current attack row (uses 't1..t11', 'moveid', 'ref'). * @param array $from Attacker village info (uses 'wref', 'owner', 'name'). * @param array $to Defender village/oasis info (uses 'wref', 'owner', 'name'). * @param int $owntribe Attacker tribe id. * @param int $type Attack type (1 = scout). * @param int $isoasis Oasis flag (0 = village). * @param int $conqureby Owner village of a conquered oasis (0 = none). * @param int $AttackArrivalTime Battle resolution timestamp. * @param int $targetally Defender alliance id. * @param int $ownally Attacker alliance id. * @param string $data2 Combat-report payload (survivors). * @param string $data_fail Combat-report payload (all dead). * @param int $chiefing_village Whether this attack chiefed the village. * @param int $DefenderWref Defender world ref. * @param int $AttackerWref Attacker world ref. * @param array $steal [wood, clay, iron, crop] looted. * @param array $dead Attacker casualties, 1-indexed t1..t11. * @param array $traped Attacker trapped, 1-indexed t1..t11. * @param array $troopsdead Attacker dead counts for enforcement, 1-indexed t1..t11. * @param int $totalsend_att Total attacking troops sent. * @param int $totaldead_att Total attacking troops killed. * @param int $totaltraped_att Total attacking troops trapped. * @param int $totalstolentaken IN/OUT (by ref): running RR loss accumulator for the defender. * @return void */ private function finalizeReturnOrDeath($data, $from, $to, $owntribe, $type, $isoasis, $conqureby, $AttackArrivalTime, $targetally, $ownally, $data2, $data_fail, $chiefing_village, $DefenderWref, $AttackerWref, $steal, $dead, $traped, $troopsdead, $totalsend_att, $totaldead_att, $totaltraped_att, &$totalstolentaken) { global $database, $units; // If the dead units not equal the ammount sent they will return and report if($totalsend_att - ($totaldead_att + (isset($totaltraped_att) ? $totaltraped_att : 0)) > 0) { $returningTroops = []; for($i = 1; $i <= 11; $i++) $returningTroops['t'.$i] = $data['t'.$i] - $traped[$i] - $dead[$i]; $troopsTime = $units->getWalkingTroopsTime($from['wref'], $to['wref'], $from['owner'], $owntribe, $returningTroops, 1, 't'); $endtime = $database->getArtifactsValueInfluence($from['owner'], $from['wref'], 2, $troopsTime); $endtime += $AttackArrivalTime; if($type == 1){ if($from['owner'] == 3){ // fix natar report by ronix $database->addNotice($to['owner'], $to['wref'], $targetally, 20, '' . addslashes($from['name']) . ' scouts ' . addslashes($to['name']) . '', $data2, $AttackArrivalTime); }elseif($totaldead_att == 0 && $totaltraped_att == 0){ $database->addNotice($from['owner'], $to['wref'], $ownally, 18, '' . addslashes($from['name']) . ' scouts ' . addslashes($to['name']) . '', $data2, $AttackArrivalTime); }else{ $database->addNotice($from['owner'], $to['wref'], $ownally, 21, '' . addslashes($from['name']) . ' scouts ' . addslashes($to['name']) . '', $data2, $AttackArrivalTime); } }else{ if((empty($totaldead_att) || $totaldead_att == 0) && (empty($totaltraped_att) || $totaltraped_att == 0)){ $database->addNotice($from['owner'], $to['wref'], $ownally, 1, '' . addslashes($from['name']) . ' attacks ' . addslashes($to['name']) . '', $data2, $AttackArrivalTime); }else{ $database->addNotice($from['owner'], $to['wref'], $ownally, 2, '' . addslashes($from['name']) . ' attacks ' . addslashes($to['name']) . '', $data2, $AttackArrivalTime); } } $database->setMovementProc($data['moveid']); if (!isset($chiefing_village)) $chiefing_village = 0; if($chiefing_village != 1){ $database->addMovement(4, $DefenderWref, $AttackerWref, $data['ref'], $AttackArrivalTime, $endtime, 1, $steal[0], $steal[1], $steal[2], $steal[3]); if($type !== 1){ if ($isoasis == 0) $database->modifyResource($DefenderWref, $steal[0], $steal[1], $steal[2], $steal[3], 0); else { //if it's an oasis but it's conquered by someone, resources must be modified in the owner's village if($conqureby > 0) $database->modifyResource($conqureby, $steal[0], $steal[1], $steal[2], $steal[3], 0); else $database->modifyOasisResource($DefenderWref, $steal[0], $steal[1], $steal[2], $steal[3], 0); } $totalstolengain = $steal[0] + $steal[1] + $steal[2] + $steal[3]; $totalstolentaken = ((isset($totalstolentaken) ? $totalstolentaken : 0) - ($steal[0] + $steal[1] + $steal[2] + $steal[3])); $database->modifyPoints($from['owner'], 'RR', $totalstolengain); $database->modifyPoints($to['owner'], 'RR', $totalstolentaken); $database->modifyPointsAlly($targetally, 'RR', $totalstolentaken); $database->modifyPointsAlly($ownally, 'RR', $totalstolengain); } }else{ //fix by ronix if only 1 chief left to conqured - don't add with zero enforces if($totalsend_att - ($totaldead_att + (isset($totaltraped_att) ? $totaltraped_att : 0)) > 1){ $database->addEnforce2($data, $owntribe, $troopsdead[1], $troopsdead[2], $troopsdead[3], $troopsdead[4], $troopsdead[5], $troopsdead[6], $troopsdead[7], $troopsdead[8], $troopsdead[9], $troopsdead[10], $troopsdead[11]); } } } else //else they die and don't return or report. { $database->setMovementProc($data['moveid']); if($type == 1){ $database->addNotice($from['owner'], $to['wref'], $ownally, 19, addslashes($from['name']) . ' scouts ' . addslashes($to['name']) . '', $data_fail, $AttackArrivalTime); }else{ $database->addNotice($from['owner'], $to['wref'], $ownally, 3, '' . addslashes($from['name']) . ' attacks ' . addslashes($to['name']) . '', $data_fail, $AttackArrivalTime); } } } private function sendunitsComplete() { // PROCESARE ATACURI COMPLETE - functie critica, pastrata 100% compatibila // Aceasta functie gestioneaza toate atacurile care ajung la destinatie // Include: batalii, capcane, evaziune erou, distrugere cladiri, cuceriri global $bid19, $bid23, $bid34, $u99, $database, $battle, $technology, $units; $time = time(); $dataarray = $this->fetchCompletedAttacks($time); $totalattackdead = $data_num = 0; if ($dataarray && count($dataarray)) { // preload village data (batched) so the per-attack loop hits the // cache instead of querying row by row $this->preloadBattleData($dataarray); // calculate battles foreach($dataarray as $data) { //set base things $totaltraped_att = 0; for($i = 1; $i <= 11; $i++) ${'traped'.$i} = 0; // per-attack context (attacker village/owner) — extracted to resolveAttackContext() [#155] $ctx = $this->resolveAttackContext($data); $isoasis = $ctx['isoasis']; $AttackArrivalTime = $ctx['AttackArrivalTime']; $AttackerWref = $ctx['AttackerWref']; $DefenderWref = $ctx['DefenderWref']; $NatarCapital = $ctx['NatarCapital']; $AttackerID = $ctx['AttackerID']; $Attacker['id'] = $ctx['AttackerID']; $owntribe = $ctx['owntribe']; $ownally = $ctx['ownally']; $from = $ctx['from']; $fromF = $ctx['fromF']; //It's a village if ($isoasis == 0){ // target + battle environment — extracted to resolveVillageTarget() [#155] $vt = $this->resolveVillageTarget($data, $dataarray, $data_num, $owntribe); $DefenderID = $vt['DefenderID']; $targettribe = $vt['targettribe']; $targetally = $vt['targetally']; $to = $vt['to']; $toF = $vt['toF']; $conqureby = $vt['conqureby']; $NatarCapital = $vt['NatarCapital']; $DefenderUnit = $vt['DefenderUnit']; $def_wall = $vt['def_wall']; $att_tribe = $vt['att_tribe']; $def_tribe = $vt['def_tribe']; $attpop = $vt['attpop']; $defpop = $vt['defpop']; $residence = $vt['residence']; $def_ab = $vt['def_ab']; $type = $vt['type']; $scout = $vt['scout']; $att_ab1 = $vt['att_ab1']; $att_ab2 = $vt['att_ab2']; $att_ab3 = $vt['att_ab3']; $att_ab4 = $vt['att_ab4']; $att_ab5 = $vt['att_ab5']; $att_ab6 = $vt['att_ab6']; $att_ab7 = $vt['att_ab7']; $att_ab8 = $vt['att_ab8']; $walllevel = $vt['walllevel']; $wallid = $vt['wallid']; $tblevel = $vt['tblevel']; $stonemason = $vt['stonemason']; $this->handleEvasion($data, $DefenderID, $DefenderUnit, $targettribe, $vt['evasion'], $vt['maxevasion'], $vt['gold'], $vt['cannotsend'], $dataarray[$data_num]['attack_type']); // defence units gathered — extracted to buildDefenderUnits() [#155] $rom = $ger = $gal = $nat = $natar = 0; $defUnits = $this->buildDefenderUnits($data['to']); $Defender = $defUnits['Defender']; $enforDefender = $defUnits['enforDefender']; $enforcementarray = $defUnits['enforcementarray']; // attacker army built — extracted to buildAttackerUnits() [#155] $atkUnits = $this->buildAttackerUnits($dataarray[$data_num], $owntribe, $isoasis); $Attacker = $atkUnits['Attacker']; $start = $atkUnits['start']; $end = $atkUnits['end']; $catp_pic = $atkUnits['catp_pic']; $ram_pic = $atkUnits['ram_pic']; $chief_pic = $atkUnits['chief_pic']; $spy_pic = $atkUnits['spy_pic']; $hero_pic = $atkUnits['hero_pic']; }else{ //It's an oasis // target + battle environment — extracted to resolveOasisTarget() [#155] $ot = $this->resolveOasisTarget($data, $dataarray, $data_num, $owntribe); $DefenderID = $ot['DefenderID']; $targettribe = $ot['targettribe']; $targetally = $ot['targetally']; $to = $ot['to']; $toF = $ot['toF']; $conqureby = $ot['conqureby']; $def_wall = $ot['def_wall']; $att_tribe = $ot['att_tribe']; $def_tribe = $ot['def_tribe']; $attpop = $ot['attpop']; $defpop = $ot['defpop']; $residence = $ot['residence']; $def_ab = $ot['def_ab']; $type = $ot['type']; $scout = $ot['scout']; $att_ab1 = $ot['att_ab1']; $att_ab2 = $ot['att_ab2']; $att_ab3 = $ot['att_ab3']; $att_ab4 = $ot['att_ab4']; $att_ab5 = $ot['att_ab5']; $att_ab6 = $ot['att_ab6']; $att_ab7 = $ot['att_ab7']; $att_ab8 = $ot['att_ab8']; $walllevel = $ot['walllevel']; $tblevel = $ot['tblevel']; $stonemason = $ot['stonemason']; // defence units gathered — extracted to buildDefenderUnits() [#155] $rom = $ger = $gal = $nat = $natar = 0; $defUnits = $this->buildDefenderUnits($data['to']); $Defender = $defUnits['Defender']; $enforDefender = $defUnits['enforDefender']; $enforcementarray = $defUnits['enforcementarray']; // attacker army built — extracted to buildAttackerUnits() [#155] $atkUnits = $this->buildAttackerUnits($dataarray[$data_num], $owntribe, $isoasis); $Attacker = $atkUnits['Attacker']; $start = $atkUnits['start']; $end = $atkUnits['end']; $catp_pic = $atkUnits['catp_pic']; $ram_pic = $atkUnits['ram_pic']; $chief_pic = $atkUnits['chief_pic']; $spy_pic = $atkUnits['spy_pic']; $hero_pic = $atkUnits['hero_pic']; } // attacker/defender populations + village lists — extracted to calculatePopulations() [#155] $popData = $this->calculatePopulations($to, $from, $isoasis, $defpop, $attpop); $varray = $popData['varray']; $varray1 = $popData['varray1']; $defpop = $popData['defpop']; $attpop = $popData['attpop']; //fix by ronix for ($i = 1; $i <= 50; $i++) { if (!isset($enforDefender['u'.$i])) { $enforDefender['u'.$i] = 0; } $enforDefender['u'.$i] += (isset($Defender['u'.$i]) ? $Defender['u'.$i] : 0); } $defspy = $enforDefender['u4'] > 0 || $enforDefender['u14'] > 0 || $enforDefender['u23'] > 0 || $enforDefender['u44'] > 0; if(PEACE == 0 || $targettribe == 4 || $targettribe == 5 || $scout){ if($targettribe == 1) $def_spy = $enforDefender['u4']; elseif($targettribe == 2) $def_spy = $enforDefender['u14']; elseif($targettribe == 3) $def_spy = $enforDefender['u23']; elseif($targettribe == 5) $def_spy = $enforDefender['u44']; // trapper resolution + prisoners — extracted to calculateTrappedUnits() [#155] $trapResult = $this->calculateTrappedUnits($data, $Defender, $Attacker, $NatarCapital, $scout, $start, $end); for($i = 1; $i <= 11; $i++) ${'traped'.$i} = $trapResult['traped'][$i]; $totaltraped_att = $trapResult['totaltraped_att']; $Attacker = $trapResult['Attacker']; // we need to save the attacker heroid before the battle if(isset($Attacker['uhero']) && $Attacker['uhero'] > 0){ $AttackerHeroID = $database->getHeroField($from['owner'], "heroid"); } // and the defender(s) heroid $DefendersHeroID = []; $herosend_def = 0; // check if our hero is defending the village, if so, add it to the list if (isset($Defender['hero']) && $Defender['hero'] > 0) { $DefendersHeroID[] = $database->getHeroField($DefenderID, "heroid"); // collecting information for the battle report $herosend_def += $Defender['hero']; } // check if there are other heroes defending the village if(count($enforcementarray) > 0){ foreach($enforcementarray as $enforcement){ if($enforcement['hero'] > 0){ $heroOwner = $database->getVillageField($enforcement['from'], "owner"); $DefendersHeroID[] = $database->getHeroField($heroOwner, "heroid"); } } } //fix by ronix if (!isset($walllevel)) $walllevel = 0; $battlepart = $battle->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, 0, 0, 0, $AttackerID, $DefenderID, $AttackerWref, $DefenderWref, $conqureby, $enforcementarray); //Data for when troops return. //catapults look :D $info_cat = $info_chief = $info_ram = $info_hero = ","; //check to see if can destroy village if (count($varray) > 1 && !$database->villageHasArtefact($DefenderWref) && empty($to['natar'])) { $can_destroy = 1; } else $can_destroy = 0; //Catapults and rams management //TODO: Move this in Battle.php if($isoasis == 0){ if ($type == 3){ if (($data['t7'] - $traped7) > 0){ // ram damage on the wall (+ battle recalc) — extracted to applyRamDamage() [#155] $ramResult = $this->applyRamDamage($data, $walllevel, $wallid, $ram_pic, $battlepart, [ 'Attacker' => $Attacker, 'Defender' => $Defender, 'att_tribe' => $att_tribe, 'def_tribe' => $def_tribe, 'residence' => $residence, 'attpop' => $attpop, 'defpop' => $defpop, 'type' => $type, 'def_ab' => $def_ab, 'att_ab1' => $att_ab1, 'att_ab2' => $att_ab2, 'att_ab3' => $att_ab3, 'att_ab4' => $att_ab4, 'att_ab5' => $att_ab5, 'att_ab6' => $att_ab6, 'att_ab7' => $att_ab7, 'att_ab8' => $att_ab8, 'tblevel' => $tblevel, 'stonemason' => $stonemason, 'AttackerID' => $AttackerID, 'DefenderID' => $DefenderID, 'AttackerWref' => $AttackerWref, 'DefenderWref' => $DefenderWref, 'conqureby' => $conqureby, 'enforcementarray' => $enforcementarray, ]); $battlepart = $ramResult['battlepart']; $info_ram = $ramResult['info_ram']; } if (($data['t8'] - $traped8) > 0) { $catResult = $this->applyCatapults($data, $battlepart, $catp_pic, $can_destroy, $isoasis, $targettribe, $info_cat); $battlepart = $catResult['battlepart']; $info_cat = $catResult['info_cat']; $village_destroyed = $catResult['village_destroyed']; } } elseif (($data['t7'] - $traped7) > 0) { $info_ram = "".$ram_pic.",Hint: The ram does not work during a raid."; } } else $can_destroy = 0; //units attack string for battleraport $unitssend_att = ''.$data['t1'].','.$data['t2'].','.$data['t3'].','.$data['t4'].','.$data['t5'].','.$data['t6'].','.$data['t7'].','.$data['t8'].','.$data['t9'].','.$data['t10'].''; $herosend_att = $data['t11']; if ($herosend_att > 0) $unitssend_att_check = $unitssend_att.','.$data['t11']; else $unitssend_att_check = $unitssend_att; //reinforcement report (pre-casualty defender data) — extracted to collectReinforcementReport() [#155] $reinfReport = $this->collectReinforcementReport($data, $targettribe, $Defender); $DefenderEnf = $reinfReport['DefenderEnf']; $DefenderHeroesTotArray = $reinfReport['DefenderHeroesTotArray']; $DefenderHeroesDeadArray = $reinfReport['DefenderHeroesDeadArray']; $unitssend_def = $reinfReport['unitssend_def']; $unitssend_deff = $reinfReport['unitssend_deff']; $totalsend_alldef = $reinfReport['totalsend_alldef']; $Defender['hero'] = $reinfReport['defenderHero']; ################################################# ################FIXED BY SONGER################ ################################################# for($i = 1; $i <= 11; $i++){ //MUST TO BE FIX : This is only for defender and still not properly coded if (isset($battlepart['casualties_attacker']) && isset($battlepart['casualties_attacker'][$i]) && $battlepart['casualties_attacker'][$i] <= 0) { ${'dead'.$i} = 0; } else if (isset($data['t'.$i]) && isset($battlepart['casualties_attacker']) && isset($battlepart['casualties_attacker'][$i]) && $battlepart['casualties_attacker'][$i] > $data['t'.$i]) { ${'dead'.$i} = $data['t'.$i]; } else { ${'dead'.$i} = (isset($battlepart['casualties_attacker']) && isset($battlepart['casualties_attacker'][$i]) ? $battlepart['casualties_attacker'][$i] : 0); } } ################################################# $dead = []; $owndead = []; $alldead = []; for($i = 1; $i <= 50; $i++) $alldead[$i] = 0; $heroAttackDead = $dead11; //kill own defence — extracted to applyOwnDefenceCasualties() [#155] $owndead = $this->applyOwnDefenceCasualties($data, $targettribe, $battlepart); //kill other defence in village (reinforcements) — extracted to applyReinforcementCasualties() [#155] $tribesPresent = $this->applyReinforcementCasualties($data, $battlepart, $from, $to, $ownally, $AttackArrivalTime, $scout, $alldead, $DefenderHeroesDeadArray); $rom = $tribesPresent['rom']; $ger = $tribesPresent['ger']; $gal = $tribesPresent['gal']; $nat = $tribesPresent['nat']; $natar = $tribesPresent['natar']; $totalsend_att = $data['t1']+$data['t2']+$data['t3']+$data['t4']+$data['t5']+$data['t6']+$data['t7']+$data['t8']+$data['t9']+$data['t10']+$data['t11']; $DefenderHeroesTot = implode(",", $DefenderHeroesTotArray); $DefenderHeroesDead = implode(",", $DefenderHeroesDeadArray); if (empty($alldead['hero'])) $alldead['hero'] = 0; if (empty($owndead['hero'])) $owndead['hero'] = 0; $deadhero = $owndead['hero']; //Counting own total dead troops $ownDeadTroops = array_slice($owndead, 0, 10); $totaldead_alldef = array_sum($ownDeadTroops); //Collecting informations for the report $unitsdead_def[0] = implode(",", $ownDeadTroops); $unitsdead_deff[0] = '?,?,?,?,?,?,?,?,?,?,'; for($i = 1; $i <= 5; $i++){ //Counting reinforcements total dead troops $deadTroops = array_slice($alldead, ($i - 1) * 10, 10); $totaldead_alldef += array_sum($deadTroops); //Collecting informations for the report $unitsdead_def[$i] = implode(",", $deadTroops); $unitsdead_deff[$i] = $unitsdead_deff[0]; } $totaldead_alldef += ($deadhero + $alldead['hero']); if (!isset($totalattackdead)) $totalattackdead = 0; $totalattackdead += $totaldead_alldef; // Set units returning from attack $p_units = []; for ($i = 1; $i <= 11; $i++) { if (!isset(${'dead'.$i})) ${'dead'.$i} = 0; if (!isset(${'traped'.$i})) ${'traped'.$i} = 0; $p_units[] = "t".$i." = t".$i." - ".(${'dead'.$i} + ${'traped'.$i}); } $database->modifyAttack3($data['ref'],implode(', ', $p_units)); $unitsdead_att = $dead1.','.$dead2.','.$dead3.','.$dead4.','.$dead5.','.$dead6.','.$dead7.','.$dead8.','.$dead9.','.$dead10; $unitstraped_att = $traped1.','.$traped2.','.$traped3.','.$traped4.','.$traped5.','.$traped6.','.$traped7.','.$traped8.','.$traped9.','.$traped10.','.$traped11; if($herosend_att > 0) $unitsdead_att_check = $unitsdead_att.','.$dead11; else $unitsdead_att_check = $unitsdead_att; //top 10 attack and defence update $totaldead_att = $dead1 + $dead2 + $dead3 + $dead4 + $dead5 + $dead6 + $dead7 + $dead8 + $dead9 + $dead10 + $dead11; $totalattackdead += $totaldead_att; $troopsdead1 = $dead1; $troopsdead2 = $dead2; $troopsdead3 = $dead3; $troopsdead4 = $dead4; $troopsdead5 = $dead5; $troopsdead6 = $dead6; $troopsdead7 = $dead7; $troopsdead8 = $dead8; $troopsdead9 = $dead9 + 1; $troopsdead10 = $dead10; $troopsdead11 = $dead11; // hero XP, player points and alliance points — extracted to calculateHeroXpAndPoints() [#155] $totaldead_def = $this->calculateHeroXpAndPoints( $alldead, $owndead, [$dead1, $dead2, $dead3, $dead4, $dead5, $dead6, $dead7, $dead8, $dead9, $dead10, $dead11], $targettribe, $att_tribe, $Attacker, $AttackerHeroID, $Defender, $DefendersHeroID, $toF, $from, $targetally, $ownally, $heroxp, $defheroxp ); // resources lootable after cranny protection — extracted to resolveResourcesAfterBattle() [#155] $resAfter = $this->resolveResourcesAfterBattle($data, $isoasis, $conqureby, $owntribe, $targettribe, $crannySpy); $totclay = $resAfter['totclay']; $totiron = $resAfter['totiron']; $totwood = $resAfter['totwood']; $totcrop = $resAfter['totcrop']; $avclay = $resAfter['avclay']; $aviron = $resAfter['aviron']; $avwood = $resAfter['avwood']; $avcrop = $resAfter['avcrop']; // bounty distributed across the resources available after cranny protection (extracted to applyBounty() [#155]) $steal = $this->applyBounty([$avwood, $avclay, $aviron, $avcrop], $battlepart['bounty']); //chiefing village — extracted to handleConquest() [#155] if (!isset($village_destroyed)) $village_destroyed = 0; $chiefResult = $this->handleConquest($data, $type, $dead9, $traped9, $targettribe, $from, $to, $toF, $owntribe, $varray, $varray1, $battlepart, $isoasis, $village_destroyed, $chief_pic); $info_chief = $chiefResult['info_chief']; $chiefing_village = $chiefResult['chiefing_village']; $village_destroyed = $chiefResult['village_destroyed']; // attacker hero outcome (oasis conquest/loyalty, artifact claim, report line) — extracted to handleHeroPostBattle() [#155] $this->handleHeroPostBattle($data, $from, $to, $dead11, $traped11, $heroxp, $hero_pic, $isoasis, $type, $targettribe, $info_cat, $info_hero, $can_destroy, $village_destroyed); if ($DefenderID == 0) $natar = 0; if (!empty($scout)) { $info_spy = $this->buildScoutReport($data, $spy_pic, $isoasis, $targettribe, $crannySpy, $totwood, $totclay, $totiron, $totcrop); } // prisoners freed during conquest attacks — extracted to handlePrisoners() [#155] $info_trap = empty($scout) ? $this->handlePrisoners($data, $from, $to, $ownally, $type, $totalsend_att, $totaldead_att, $totaltraped_att) : ''; $info_troop = ($totalsend_att - ($totaldead_att + (isset($totaltraped_att) ? $totaltraped_att : 0)) <= 0) ? "None of your soldiers returned." : ""; // assemble data2 + data_fail — extracted to buildCombatReport() [#155] $report = $this->buildCombatReport( $scout, $from, $to, $owntribe, $targettribe, $unitssend_att, $unitsdead_att, $steal, $battlepart, $unitssend_def, $unitsdead_def, $unitssend_deff, $unitsdead_deff, $rom, $ger, $gal, $nat, $natar, $DefenderHeroesTot, $DefenderHeroesDead, $info_ram, $info_cat, $info_chief, isset($info_spy) ? $info_spy : '', $info_trap, $info_hero, $info_troop, $data, $dead11, $herosend_def, $deadhero, $unitstraped_att, $village_destroyed, $can_destroy, $catp_pic ); $data2 = $report['data2']; $data_fail = $report['data_fail']; // notify the defender of the incoming battle/scout — extracted to sendBattleNotifications() [#155] $this->sendBattleNotifications($scout, $battlepart, $from, $to, $targetally, $data2, $AttackArrivalTime, $unitsdead_att, $unitssend_att, $defspy, $info_troop, $totalsend_alldef, $totalsend_att, $totaldead_att, $totaltraped_att, $totaldead_alldef); // surviving attacker troops return (report + movement + loot/RR/enforce) or die — extracted to finalizeReturnOrDeath() [#155] $retDead = $retTraped = $retTroopsdead = []; for ($i = 1; $i <= 11; $i++) { $retDead[$i] = ${'dead'.$i}; $retTraped[$i] = ${'traped'.$i}; $retTroopsdead[$i] = ${'troopsdead'.$i}; } $this->finalizeReturnOrDeath($data, $from, $to, $owntribe, $type, $isoasis, $conqureby, $AttackArrivalTime, $targetally, $ownally, $data2, $data_fail, $chiefing_village, $DefenderWref, $AttackerWref, $steal, $retDead, $retTraped, $retTroopsdead, $totalsend_att, $totaldead_att, $totaltraped_att, $totalstolentaken); if($type == 3 || $type == 4) $database->addGeneralAttack($totalattackdead); if (!isset($village_destroyed)) $village_destroyed = 0; if ($village_destroyed == 1 && $can_destroy == 1) { if($to['capital'] == 1){ $mostPopulatedVillage = []; //Search for the most populated village foreach($varray as $village){ if($village['wref'] != $data['to'] && (empty($mostPopulatedVillage) || $mostPopulatedVillage['pop'] < $village['pop'])){ $mostPopulatedVillage = $village; } } //Set the new capital $database->changeCapital($mostPopulatedVillage['wref']); } //Delete the village $database->DelVillage($data['to']); //Reassign the hero, if dead and assigned to the deleted village $database->reassignHero($data['to']); } }else{ //units attack string for battleraport $unitssend_att1 = ''.$data['t1'].','.$data['t2'].','.$data['t3'].','.$data['t4'].','.$data['t5'].','.$data['t6'].','.$data['t7'].','.$data['t8'].','.$data['t9'].','.$data['t10'].''; $herosend_att = $data['t11']; $unitssend_att= $unitssend_att1.','.$herosend_att; $troopsTime = $units->getWalkingTroopsTime($from['wref'], $to['wref'], $from['owner'], $owntribe, $data, 1, 't'); $endtime = $database->getArtifactsValueInfluence($from['owner'], $from['wref'], 2, $troopsTime); $endtime += $AttackArrivalTime; $database->setMovementProc($data['moveid']); $database->addMovement(4, $to['wref'], $from['wref'], $data['ref'], $AttackArrivalTime, $endtime); $peace = PEACE; $data2 = $from['owner'].','.$from['wref'].','.$to['owner'].','.$owntribe.','.$unitssend_att.','.$peace; $time = time(); $database->addNotice($from['owner'], $to['wref'], $ownally, 22,''.addslashes($from['name']).' attacks '.addslashes($to['name']).'', $data2, $time); $database->addNotice($to['owner'], $to['wref'], $targetally, 23,''.addslashes($from['name']).' attacks '.addslashes($to['name']).'', $data2, $time); } //Update starvation data $database->addStarvationData($to['wref']); //Returning units back to village is not necessary because it will be taken care when processing movement // Fix by AL-Kateb // if evasion was active, return units back to base /* if (isset($evaded)) { foreach ($evasionUnitModifications_modes as $index => $mode) { $evasionUnitModifications_modes[$index] = 1; } $database->modifyUnit($data['to'], $evasionUnitModifications_units, $evasionUnitModifications_amounts, $evasionUnitModifications_modes); } */ ################################################# ################FIXED BY SONGER################ ################################################# ################################################################################ ##############ISUE: Lag, fixed3#################################################### #### PHP.NET manual: unset() destroy more than one variable unset($foo1, $foo2, $foo3);###### ################################################################################ $data_num++; unset( $Attacker ,$Defender ,$DefenderEnf ,$DefenderHeroesTotArray ,$DefenderHeroesDeadArray ,$DefenderHeroesTot ,$DefenderHeroesDead ,$enforce ,$unitssend_att ,$unitssend_def ,$battlepart ,$unitlist ,$unitsdead_def ,$dead ,$steal ,$from ,$data ,$data2 ,$to ,$artifact ,$artifactBig ,$canclaim ,$data_fail ,$owntribe ,$unitsdead_att ,$herosend_def ,$deadhero ,$heroxp ,$AttackerID ,$DefenderID ,$totalsend_att ,$totalsend_alldef ,$totaldead_att ,$totaltraped_att ,$totaldead_def ,$unitsdead_att_check ,$totalattackdead ,$enforce1 ,$defheroowner ,$enforceowner ,$defheroxp ,$reinfheroxp ,$AttackerWref ,$DefenderWref ,$troopsdead1 ,$troopsdead2 ,$troopsdead3 ,$troopsdead4 ,$troopsdead5 ,$troopsdead6 ,$troopsdead7 ,$troopsdead8 ,$troopsdead9 ,$troopsdead10 ,$troopsdead11 ,$DefenderUnit ,$info_trap ,$report); ################################################# } } } private function sendreinfunitsComplete() { global $bid23, $database, $technology, $battle; $time = time(); $q = " SELECT `to`, `from`, moveid, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11 FROM ".TB_PREFIX."movement, ".TB_PREFIX."attacks WHERE ".TB_PREFIX."movement.ref = ".TB_PREFIX."attacks.id AND ".TB_PREFIX."movement.proc = 0 AND ".TB_PREFIX."movement.sort_type = 3 AND ".TB_PREFIX."attacks.attack_type = 2 AND endtime < $time"; $dataarray = $database->query_return($q); if ($dataarray && count($dataarray)) { // preload village data $vilIDs = []; $tos = []; $froms = []; foreach($dataarray as $data) { $vilIDs[$data['from']] = true; $vilIDs[$data['to']] = true; $tos[$data['to']] = true; $froms[$data['from']] = true; } $vilIDs = array_keys($vilIDs); $database->getProfileVillages($vilIDs, 5); $database->getUnit($vilIDs); $database->getEnforce(array_keys($tos), array_keys($froms)); $database->getVillageByWorldID($vilIDs); // calculate reinforcements data foreach($dataarray as $data) { if (!$this->claimMovementRecord($data['moveid'])) { continue; } $isoasis = $database->isVillageOases($data['to']); if($isoasis == 0){ $to = $database->getMInfo($data['to']); $toF = $database->getVillage($data['to']); $DefenderID = $to['owner']; $targettribe = $database->getUserField($DefenderID, "tribe", 0); $conqureby = 0; }else{ $to = $database->getOMInfo($data['to']); $toF = $database->getOasisV($data['to']); $DefenderID = $to['owner']; $targettribe = $database->getUserField($DefenderID, "tribe", 0); $conqureby = $toF['conqured']; } if($data['from'] == 0){ $DefenderID = $database->getVillageField($data['to'], "owner"); $database->addEnforce(['from' => $data['from'], 'to' => $data['to'], 't1' => 0, 't2' => 0, 't3' => 0, 't4' => 0, 't5' => 0, 't6' => 0, 't7' => 0, 't8' => 0, 't9' => 0, 't10' => 0, 't11' => 0]); $reinf = $database->getEnforce($data['to'], $data['from']); $database->modifyEnforce($reinf['id'], 31, 1, 1); $data_fail = '0,0,4,1,0,0,0,0,0,0,0,0,0,0'; $database->addNotice($to['owner'], $to['wref'], (isset($targetally) ? $targetally : 0), 8, 'village of the elders reinforcement ' . addslashes($to['name']), $data_fail, $AttackArrivalTime); }else{ //set base things $from = $database->getMInfo($data['from']); $fromF = $database->getVillage($data['from']); $AttackerID = $from['owner']; $owntribe = $database->getUserField($AttackerID,"tribe",0); $HeroTransfer = $troopsPresent = 0; for($i = 1;$i <= 10; $i++) { if($data['t'.$i] > 0) { $troopsPresent = 1; break; } } //check if the hero is present and we're not sending him to an occupied oasis //only add hero if we're sending him alone if($data['t11'] > 0 && !$isoasis && !$troopsPresent) { //check if we're sending a hero between own villages if($AttackerID == $DefenderID) { //check if there's a Mansion at target village if($this->getTypeLevel(37, $data['to']) > 0){ //don't reinforce, addunit instead $database->modifyUnit($data['to'], ["hero"], [1], [1]); $heroid = $database->getHeroField($DefenderID, 'heroid'); $database->modifyHero("wref", $data['to'], $heroid); $HeroTransfer = 1; } } } if($data['t11'] > 0 || $troopsPresent) { $temphero = $data['t11']; if ($HeroTransfer) $data['t11'] = 0; //check if there is defence from town in to town $check = $database->getEnforce($data['to'], $data['from']); if (!isset($check['id'])) $database->addEnforce($data); else { //yes $start = ($owntribe - 1) * 10 + 1; $end = ($owntribe * 10); //add unit. $t_units = ''; for($i = $start, $j = 1; $i <= $end; $i++, $j++) { $t_units .= "u".$i." = u".$i." + ".$data['t'.$j].(($j > 9) ? '' : ', '); } $q = "UPDATE ".TB_PREFIX."enforcement set $t_units where id =".(int) $check['id']; $database->query($q); $database->modifyEnforce($check['id'], 'hero', $data['t11'], 1); } $data['t11'] = $temphero; } //send rapport $unitssend_att = ''.$data['t1'].','.$data['t2'].','.$data['t3'].','.$data['t4'].','.$data['t5'].','.$data['t6'].','.$data['t7'].','.$data['t8'].','.$data['t9'].','.$data['t10'].','.$data['t11'].''; $data_fail = ''.$from['wref'].','.$from['owner'].','.$owntribe.','.$unitssend_att.''; if($isoasis == 0) $to_name = $to['name']; else $to_name = "Oasis ".$database->getVillageField($to['conqured'],"name"); $database->addNotice($from['owner'],$from['wref'],(isset($ownally) ? $ownally : 0),8,''.addslashes($from['name']).' reinforcement '.addslashes($to_name).'',$data_fail,(isset($AttackArrivalTime) ? $AttackArrivalTime : time())); if($from['owner'] != $to['owner']) { $database->addNotice($to['owner'],$to['wref'],(isset($targetally) ? $targetally : 0),8,''.addslashes($from['name']).' reinforcement '.addslashes($to_name).'',$data_fail,(isset($AttackArrivalTime) ? $AttackArrivalTime : time())); } } //Update starvation data $database->addStarvationData($data['to']); //check empty reinforcement in rally point $e_units = ''; for ($i = 1; $i <= 50; $i++) $e_units.= 'u'.$i.'= 0 AND '; $e_units.= 'hero = 0'; $q = "DELETE FROM ".TB_PREFIX."enforcement WHERE ".$e_units." AND (vref=".(int) $data['to']." OR `from`=".(int) $data['to'].")"; $database->query($q); } } } private function returnunitsComplete() { global $database, $technology; $time = time(); $q = " SELECT `to`, `from`, moveid, starttime, endtime, wood, clay, iron, crop, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11 FROM ".TB_PREFIX."movement, ".TB_PREFIX."attacks WHERE ".TB_PREFIX."movement.ref = ".TB_PREFIX."attacks.id AND ".TB_PREFIX."movement.proc = 0 AND ".TB_PREFIX."movement.sort_type = 4 AND endtime < $time"; $dataarray = $database->query_return($q); if ($dataarray && count($dataarray)) { // preload village data $vilIDs = []; foreach($dataarray as $data) { $vilIDs[$data['from']] = true; $vilIDs[$data['to']] = true; } $database->getProfileVillages(array_keys($vilIDs), 5); $database->getOasisEnforce($vilIDs, 0); $database->getOasisEnforce($vilIDs, 1); foreach($dataarray as $data) { if (!$this->claimMovementRecord($data['moveid'])) { continue; } $tribe = $database->getUserField($database->getVillageField($data['to'], "owner"), "tribe", 0); $u = $tribe == 1 ? "" : $tribe - 1; $database->modifyUnit( $data['to'], [$u."1", $u."2", $u."3", $u."4", $u."5", $u."6", $u."7", $u."8", $u."9", $tribe."0", "hero"], [$data['t1'], $data['t2'], $data['t3'], $data['t4'], $data['t5'], $data['t6'], $data['t7'], $data['t8'], $data['t9'], $data['t10'], $data['t11']], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] ); //If there's at least 1 resource, add it to the village if($data['wood'] + $data['clay'] + $data['iron'] + $data['crop'] > 0){ $database->modifyResource($data['to'], $data['wood'], $data['clay'], $data['iron'], $data['crop'], 1); } //Update starvation data $database->addStarvationData($data['to']); } $this->pruneResource(); } // Settlers $q = "SELECT `to`, moveid FROM ".TB_PREFIX."movement where ref = 0 and proc = '0' and sort_type = '4' and endtime < $time"; $dataarray = $database->query_return($q); if ($dataarray && count($dataarray)) { foreach($dataarray as $data) { if (!$this->claimMovementRecord($data['moveid'])) { continue; } $tribe = $database->getUserField($database->getVillageField($data['to'], "owner"), "tribe", 0); $database->modifyUnit($data['to'], [$tribe."0"], [3], [1]); //If a settling is canceled, add 750 for each resource type $database->modifyResource($data['to'], 750, 750, 750, 750, 1); } } } private function sendSettlersComplete() { global $database; $time = microtime(true); $q = "SELECT `to`, `from`, moveid, starttime, ref FROM ".TB_PREFIX."movement where proc = 0 and sort_type = 5 and endtime < $time"; $dataarray = $database->query_return($q); $fieldIDs = []; $addUnitsWrefs = []; $addTechWrefs = []; $addABTechWrefs = []; $time = microtime(true); $types = []; $froms = []; $tos = []; $refs = []; $times = []; $endtimes = []; // preload village data $vilIDs = []; foreach($dataarray as $data) { $vilIDs[$data['from']] = true; $vilIDs[$data['to']] = true; } $vilIDs = array_keys($vilIDs); $database->getProfileVillages($vilIDs, 5); $database->getVillageByWorldID($vilIDs); foreach($dataarray as $data) { if (!$this->claimMovementRecord($data['moveid'])) { continue; } $ownerID = $database->getUserField($database->getVillageField($data['from'], "owner"), "id", 0); $to = $database->getMInfo($data['from']); $user = addslashes($database->getUserField($to['owner'], 'username', 0)); $taken = $database->getVillageState($data['to']); if($taken != 1){ $fieldIDs[] = $data['to']; $database->addVillage($data['to'], $to['owner'], $user, '0'); $database->addResourceFields($data['to'], $database->getVillageType($data['to'])); $addUnitsWrefs[] = $data['to']; $addTechWrefs[] = $data['to']; $addABTechWrefs[] = $data['to']; $exp1 = $database->getVillageField($data['from'], 'exp1'); $exp2 = $database->getVillageField($data['from'], 'exp2'); $exp3 = $database->getVillageField($data['from'], 'exp3'); if($exp1 == 0){ $exp = 'exp1'; $value = $data['to']; }elseif($exp2 == 0){ $exp = 'exp2'; $value = $data['to']; }else{ $exp = 'exp3'; $value = $data['to']; } $database->setVillageField($data['from'], $exp, $value); // Report: new village founded (issue #178) $ncoor = $database->getCoor($data['to']); $database->addNotice($to['owner'], $data['to'], 0, 24, 'New village founded', ($ncoor['x'] ?? 0) . ',' . ($ncoor['y'] ?? 0), time()); }else{ // here must come movement from returning settlers $types[] = 4; $froms[] = $data['to']; $tos[] = $data['from']; $refs[] = $data['ref']; $times[] = $time; $endtimes[] = $time + ($time - $data['starttime']); // Report: valley already occupied, settlers returning (issue #178) $fcoor = $database->getCoor($data['to']); $database->addNotice($to['owner'], $data['to'], 0, 25, 'Settlers returned - valley occupied', ($fcoor['x'] ?? 0) . ',' . ($fcoor['y'] ?? 0), time()); } } $database->addMovement($types, $froms, $tos, $refs, $times, $endtimes); $database->setFieldTaken($fieldIDs); $database->addUnits($addUnitsWrefs); $database->addTech($addTechWrefs); $database->addABTech($addABTechWrefs); } /** * Create the Natars account and spawn artifacts * */ private function spawnNatars(){ global $database; //Check if Natars account is already created and if the time //is come and we have to create Natars and spawn their artifacts if($database->areArtifactsSpawned() || strtotime(START_DATE) + (NATARS_SPAWN_TIME * 86400) > time()) return; //Create the Natars account and his capital $this->artifacts->createNatars(); //Write the system message $database->displaySystemMessage(ARTEFACT); } /** * Spawn WW Villages * */ private function spawnWWVillages(){ global $database; //Check if Natars account has already been created, if WW villages have already been spawned //and if it's the time to spawn them or not if(!$database->areArtifactsSpawned() || $database->areWWVillagesSpawned() || strtotime(START_DATE) + (NATARS_WW_SPAWN_TIME * 86400) > time()) return; //Create WW villages $this->artifacts->createWWVillages(); //Write the system message $database->displaySystemMessage(WWVILLAGEMSG); } /** * Spawn WW Building plans * */ private function spawnWWBuildingPlans(){ global $database; //Check if Natars account is already spawned, if WW building plans have already been spawned //and if it's the time to spawn them or not if(!$database->areArtifactsSpawned() || $database->areArtifactsSpawned(true) || strtotime(START_DATE) + (NATARS_WW_BUILDING_PLAN_SPAWN_TIME * 86400) > time()) return; //Create WW building plans $this->artifacts->createWWBuildingPlans(); //Set the system message to contain the infos of the WW building plans $database->displaySystemMessage(PLAN_INFO); } /** * Automatically activate all artifacts that need to be activated * */ private function activateArtifacts() { global $database; //Check if there's at least one artifact, if not, return if(!$database->areArtifactsSpawned()) return; //Activate the artifacts that need to be activated $this->artifacts->activateArtifacts(); } private function researchComplete() { global $database; $time = time(); $deleteIDs = []; $tdata = []; $abdata = []; $q = "SELECT tech, vref, id FROM ".TB_PREFIX."research where timestamp < $time"; $dataarray = $database->query_return($q); foreach($dataarray as $data) { $sort_type = substr($data['tech'],0,1); switch($sort_type) { case "t": if (!isset($tdata[$data['vref']])) $tdata[$data['vref']] = []; $tdata[$data['vref']][] = $data['tech'].' = 1'; break; case "a": case "b": if (!isset($abdata[$data['vref']])) $abdata[$data['vref']] = []; $abdata[$data['vref']][] = $data['tech']." = ".$data['tech']." + 1"; break; } $deleteIDs[] = (int) $data['id']; } // execute queries with consolidated research data if (count($tdata)) { foreach ( $tdata as $vid => $preparedData ) { $q = "UPDATE ".TB_PREFIX."tdata SET ".implode(', ', $preparedData)." WHERE vref = ".$vid; $database->query($q); } } if (count($abdata)) { foreach ( $abdata as $vid => $preparedData ) { $q = "UPDATE ".TB_PREFIX."abdata SET ".implode(', ', $preparedData)." WHERE vref = ".$vid; $database->query($q); } } if (count($deleteIDs)) { $q = "DELETE FROM " . TB_PREFIX . "research where id IN(" . implode( ', ', $deleteIDs ) . ")"; $database->query( $q ); } } private function updateORes($bountywid) { global $database; $oasisInfoArray = $database->getOasisV($bountywid); $timepast = time() - $oasisInfoArray['lastupdated']; $nwood = (OASIS_WOOD_PRODUCTION / 3600) * $timepast; $nclay = (OASIS_CLAY_PRODUCTION / 3600) * $timepast; $niron = (OASIS_IRON_PRODUCTION / 3600) * $timepast; $ncrop = (OASIS_CROP_PRODUCTION / 3600) * $timepast; $database->modifyOasisResource($bountywid, $nwood, $nclay, $niron, $ncrop, 1); $database->updateOasis($bountywid); } private function updateRes($bountywid) { global $database, $technology; //Get village infos $villageInfoArray = $database->getVillage($bountywid); //Get building and resource fields array $resArray = $database->getResourceLevel($bountywid, false); //Get oasis array $oasisArray = $database->getOasis($bountywid); //Get an array with the numbers of the oasis $numberOfOasis = $this->bountysortOasis($oasisArray); //Set the village population (if WW Villages, it's halved) $villagePopulation = !$villageInfoArray['natar'] ? $villageInfoArray['pop'] : round($villageInfoArray['pop'] / 2); //Get the upkeep of the village $upkeep = $technology->getUpkeep($this->getAllUnits($bountywid), 0, $bountywid); //Calculate the produced resources $timepast = time() - $villageInfoArray['lastupdate']; $nwood = ($this->bountyGetWoodProd($resArray, $numberOfOasis) / 3600) * $timepast; $nclay = ($this->bountyGetClayProd($resArray, $numberOfOasis) / 3600) * $timepast; $niron = ($this->bountyGetIronProd($resArray, $numberOfOasis) / 3600) * $timepast; $ncrop = (($this->bountyGetCropProd($resArray, $numberOfOasis) - $villagePopulation - $upkeep) / 3600) * $timepast; $database->modifyResource($bountywid, $nwood, $nclay, $niron, $ncrop, 1); $database->updateVillage($bountywid); } private function bountysortOasis($oasisArray) { $crop = $clay = $wood = $iron = 0; foreach ($oasisArray as $oasis) { switch($oasis['type']) { case 1: case 2: $wood++; break; case 3: $wood++; $crop++; break; case 4: case 5: $clay++; break; case 6: $clay++; $crop++; break; case 7: case 8: $iron++; break; case 9: $iron++; $crop++; break; case 10: case 11: $crop++; break; case 12: $crop += 2; break; } } return [$wood, $clay, $iron, $crop]; } function getAllUnits($base, $use_cache = true) { global $database; $ownunit = $database->getUnit($base, $use_cache); $enforcementarray = $database->getEnforceVillage($base, 0); if(count($enforcementarray) > 0){ foreach($enforcementarray as $enforce){ for($i = 1; $i <= 50; $i++){ $ownunit['u'.$i] += $enforce['u'.$i]; } } } $enforceoasis = $database->getOasisEnforce($base, 0, $use_cache); if(count($enforceoasis) > 0){ foreach($enforceoasis as $enforce){ for($i = 1; $i <= 50; $i++){ $ownunit['u'.$i] += $enforce['u'.$i]; } } } $enforceoasis1 = $database->getOasisEnforce($base, 1, $use_cache); if(count($enforceoasis1) > 0){ foreach($enforceoasis1 as $enforce){ for($i = 1; $i <= 50; $i++){ $ownunit['u'.$i] += $enforce['u'.$i]; } } } $movement = $database->getVillageMovement($base); if(!empty($movement)){ for($i = 1; $i <= 50; $i++){ if(!isset($ownunit['u' . $i])){ $ownunit['u'.$i] = 0; } $ownunit['u'.$i] += (isset($movement['u'.$i]) ? $movement['u'.$i] : 0); } } $prisoners = $database->getPrisoners($base, 1); if(!empty($prisoners)){ foreach($prisoners as $prisoner){ $owner = $database->getVillageField($base, "owner"); $ownertribe = $database->getUserField($owner, "tribe", 0); $start = ($ownertribe - 1) * 10 + 1; $end = ($ownertribe * 10); for($i = $start; $i <= $end; $i++){ $j = $i - $start + 1; $ownunit['u'.$i] += $prisoner['t'.$j]; } $ownunit['hero'] += $prisoner['t11']; } } return $ownunit; } private function bountyGetWoodProd($resArray, $oasisNumber) { global $bid1, $bid5; $wood = $sawmill = 0; $woodholder = []; for($i = 1; $i <= 38; $i++) { if($resArray['f'.$i.'t'] == 1) array_push($woodholder,'f'.$i); if($resArray['f'.$i.'t'] == 5) $sawmill = $resArray['f'.$i]; } for($i = 0; $i <= count($woodholder) - 1; $i++) $wood += $bid1[$resArray[$woodholder[$i]]]['prod']; if($sawmill >= 1) $wood += $wood / 100 * $bid5[$sawmill]['attri']; if($oasisNumber[0] > 0) $wood += $wood * 0.25 * $oasisNumber[0]; return round($wood * SPEED); } private function bountyGetClayProd($resArray, $oasisNumber) { global $bid2, $bid6; $clay = $brick = 0; $clayholder = []; for($i = 1; $i <= 38; $i++) { if($resArray['f'.$i.'t'] == 2) array_push($clayholder, 'f'.$i); if($resArray['f'.$i.'t'] == 6) $brick = $resArray['f'.$i]; } for($i = 0; $i <= count($clayholder) - 1; $i++) $clay += $bid2[$resArray[$clayholder[$i]]]['prod']; if($brick >= 1) $clay += $clay / 100 * $bid6[$brick]['attri']; if($oasisNumber[1] > 0) $clay += $clay * 0.25 * $oasisNumber[1]; return round($clay * SPEED); } private function bountyGetIronProd($resArray, $oasisNumber) { global $bid3, $bid7; $iron = $foundry = 0; $ironholder = []; for($i = 1; $i <= 38; $i++) { if($resArray['f'.$i.'t'] == 3) array_push($ironholder, 'f'.$i); if($resArray['f'.$i.'t'] == 7) $foundry = $resArray['f'.$i]; } for($i = 0; $i <= count($ironholder) - 1; $i++) $iron += $bid3[$resArray[$ironholder[$i]]]['prod']; if($foundry >= 1) $iron += $iron / 100 * $bid7[$foundry]['attri']; if($oasisNumber[2] > 0) $iron += $iron * 0.25 * $oasisNumber[2]; return round($iron * SPEED); } private function bountyGetCropProd($resArray, $oasisNumber) { global $bid4, $bid8, $bid9, $database; $crop = $grainmill = $bakery = 0; $cropholder = []; for($i = 1; $i <= 38;$i++) { if($resArray['f'.$i.'t'] == 4) array_push($cropholder, 'f'.$i); if($resArray['f'.$i.'t'] == 8) $grainmill = $resArray['f'.$i]; if($resArray['f'.$i.'t'] == 9) $bakery = $resArray['f'.$i]; } for($i = 0; $i <= count($cropholder) - 1; $i++) $crop += $bid4[$resArray[$cropholder[$i]]]['prod']; if($grainmill >= 1) $crop += $crop / 100 * (isset($bid8[$grainmill]['attri']) ? $bid8[$grainmill]['attri'] : 0); if($bakery >= 1) $crop += $crop / 100 * (isset($bid9[$bakery]['attri']) ? $bid9[$bakery]['attri'] : 0); if($oasisNumber[3] > 0) $crop += $crop * 0.25 * $oasisNumber[3]; if(!empty($resArray['vref']) && is_numeric($resArray['vref'])){ $who = $database->getVillageField($resArray['vref'], "owner"); $croptrue = $database->getUserField($who, "b4", 0); if($croptrue > time()) $crop *= 1.25; } return round($crop * SPEED); } private function trainingComplete() { global $database, $technology; $time = time(); $trainlist = $database->getTrainingList(); if(count($trainlist) > 0){ // preload village data $vilIDs = []; foreach($trainlist as $train){ $vilIDs[$train['vref']] = true; } $vilIDs = array_keys($vilIDs); $database->getProfileVillages($vilIDs, 5); $database->cacheResourceLevels($vilIDs); $database->getUnit($vilIDs); $database->getEnforceVillage($vilIDs, 0 ); $database->getMovement(3, $vilIDs, 0); $database->getMovement(4, $vilIDs, 1); $database->getMovement(5, $vilIDs, 0); $database->getOasisEnforce($vilIDs, 0); $database->getOasisEnforce($vilIDs, 1); $database->getPrisoners($vilIDs, 1); // calculate training updates foreach($trainlist as $train){ $timepast = $train['timestamp2'] - $time; $pop = $train['pop']; $valuesUpdated = false; if($timepast <= 0 && $train['amt'] > 0) { $valuesUpdated = true; if($train['eachtime'] > 0){ $timepast2 = $time - $train['timestamp2']; $trained = 1; while($timepast2 >= $train['eachtime']){ $timepast2 -= $train['eachtime']; $trained += 1; } if($trained > $train['amt']) $trained = $train['amt']; } else $trained = $train['amt']; if($train['unit'] > 60 && $train['unit'] != 99){ $database->modifyUnit($train['vref'], [$train['unit'] - 60], [$trained], [1]); } else $database->modifyUnit($train['vref'], [$train['unit']], [$trained], [1]); $database->updateTraining($train['id'], $trained, $trained * $train['eachtime']); if($train['amt'] - $trained <= 0) $database->trainUnit($train['id'], 0, 0, 0, 0, 1); } if ($valuesUpdated) call_user_func(get_class($database).'::clearUnitsCache'); //Update starvation data $database->addStarvationData($train['vref']); } } } private function getsort_typeLevel($tid, $resarray) { $keyholder = []; foreach(array_keys($resarray, $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, $resarray['f'.$keyholder[$i]]); } foreach ($temparray as $key => $val) { if ($val == max($temparray)) $target = $key; } } } else if($element == 1) $target = 0; else return 0; if(!empty($keyholder[$target])) return $resarray['f'.$keyholder[$target]]; else return 0; } private function celebrationComplete() { global $database; $varray = $database->getCel(); foreach($varray as $vil){ $id = $vil['wref']; $type = $vil['type']; $user = $vil['owner']; $cp = ($type == 1) ? 500 : 2000; $database->clearCel($id); $database->setCelCp($user, $cp); } } private function demolitionComplete() { global $database; $varray = $database->getDemolition(); foreach($varray as $vil) { if ($vil['lvl'] < 0) { $database->delDemolition($vil['vref'], true); continue; } if ($vil['timetofinish'] <= time()) { $type = $database->getFieldType($vil['vref'],$vil['buildnumber']); $level = $database->getFieldLevel($vil['vref'],$vil['buildnumber']); $newLevel = max(0, $level - 1); $buildarray = $GLOBALS["bid".$type]; if ($type == 10 || $type == 38) { $database->query(" UPDATE ".TB_PREFIX."vdata SET `maxstore` = IF(`maxstore` - ".$buildarray[$level]['attri']." <= ".STORAGE_BASE.", ".STORAGE_BASE.", `maxstore` - ".$buildarray[$level]['attri'].") WHERE wref=".(int) $vil['vref']); } if ($type == 11 || $type == 39) { $database->query(" UPDATE ".TB_PREFIX."vdata SET `maxcrop` = IF(`maxcrop` - ".$buildarray[$level]['attri']." <= ".STORAGE_BASE.", ".STORAGE_BASE.", `maxcrop` - ".$buildarray[$level]['attri'].") WHERE wref=".(int) $vil['vref']); } if ($level == 1) $clear = ",f".$vil['buildnumber']."t=0"; else $clear = ""; if ($database->getVillageField($vil['vref'], 'natar') == 1 && $type == 40) $clear = ""; //fix by ronix - fixed by iopietro $q = " UPDATE ".TB_PREFIX."fdata SET f".$vil['buildnumber']."=".$newLevel." ".$clear." WHERE vref=".(int)$vil['vref']; $database->query($q); $pop = $this->getPop($type, $newLevel); $database->modifyPop($vil['vref'], $pop[0], 1); $this->procClimbers($database->getVillageField($vil['vref'], 'owner')); $database->delDemolition($vil['vref'], true); if ($type == 18) Automation::updateMax($database->getVillageField($vil['vref'], 'owner')); } } } private function updateHero() { global $database, $hero_levels; $harray = $database->getHero(); if(!empty($harray)){ // first of all, prepare all unit data at once for these heroes $heroVillageIDs = []; foreach($harray as $hdata) { $heroVillageIDs[] = $hdata['wref']; } // load data for those prepared IDs $unitData = $database->getUnit($heroVillageIDs); // now do the math $lastUpdateIDs = []; $timeNow = time(); foreach($harray as $hdata){ $columns = []; $columnValues = []; $modes = []; $lastUpdateTime = $timeNow; $newHealth = -1; if((time()-$hdata['lastupdate']) >= 1){ if($hdata['health'] < 100 and $hdata['health'] > 0){ if(SPEED <= 10) $speed = SPEED; else if(SPEED <= 100) $speed = ceil(SPEED / 10); else $speed = ceil(SPEED / 100); $reg = $hdata['health'] + $hdata['regeneration'] * 5 * $speed / 86400 * (time() - $hdata['lastupdate']); if($reg <= 100) $newHealth = $reg; else $newHealth = 100; } } $herolevel = $hdata['level']; $newLevel = - 1; $scorePoints = false; for ($i = $herolevel + 1; $i < 100; $i++){ if($hdata['experience'] >= $hero_levels[$i]){ $newLevel = $i; if ($i < 99) $scorePoints = true; } } // upgrade hero to a new level, if needed if ($newLevel > -1) { $columns[] = 'level'; $columnValues[] = $newLevel; $modes[] = null; } // add as many points as needed, if we're below level 100 if ($scorePoints) { $columns[] = 'points'; $columnValues[] = (5 * ($newLevel - $herolevel)); $modes[] = 1; } $villunits = $unitData[$hdata['wref']]; if($hdata['trainingtime'] < time() && $hdata['inrevive'] == 1){ mysqli_query($database->dblink,"UPDATE " . TB_PREFIX . "units SET hero = 1 WHERE vref = ".(int) $hdata['wref'].""); $columns[] = 'dead'; $columnValues[] = 0; $modes[] = null; $columns[] = 'inrevive'; $columnValues[] = 0; $modes[] = null; $columns[] = 'inrevive'; $columnValues[] = 0; $modes[] = null; $newHealth = 100; $lastUpdateTime = (int) $hdata['trainingtime']; } if($hdata['trainingtime'] < time() && $hdata['intraining'] == 1){ mysqli_query($database->dblink,"UPDATE " . TB_PREFIX . "units SET hero = 1 WHERE vref = ".(int) $hdata['wref']); $columns[] = 'dead'; $columnValues[] = 0; $modes[] = null; $columns[] = 'intraining'; $columnValues[] = 0; $modes[] = null; $lastUpdateTime = (int) $hdata['trainingtime']; } // update health, if needed if ($newHealth > -1) { $columns[] = 'health'; $columnValues[] = $newHealth; $modes[] = null; } if ($lastUpdateTime != $timeNow) { // last update timestamp $columns[] = 'lastupdate'; $columnValues[] = $lastUpdateTime; $modes[] = null; } else { // leave same last update values for multiple heroes to the end $lastUpdateIDs[] = $hdata['heroid']; } if (count($columns)) $database->modifyHero($columns, $columnValues, $hdata['heroid'], $modes); } if (count($lastUpdateIDs)) { mysqli_query($database->dblink,"UPDATE " . TB_PREFIX . "hero SET lastupdate = $timeNow WHERE heroid IN(".implode(', ', $lastUpdateIDs).")"); } } } // by SlimShady95, aka Manuel Mannhardt < manuel_mannhardt@web.de > UPDATED FROM songeriux < haroldas.snei@gmail.com > private function updateStore() { global $database, $bid10, $bid38, $bid11, $bid39; $result = mysqli_query($database->dblink, 'SELECT * FROM `' . TB_PREFIX . 'fdata`'); mysqli_begin_transaction($database->dblink); while ($row = mysqli_fetch_assoc($result)) { $ress = $crop = 0; for ($i = 19; $i < 40; ++$i) { //Warehouse if ($row['f' . $i . 't'] == 10) { $ress += ((isset($bid10[$row['f' . $i]]) && isset($bid10[$row['f' . $i]]['attri'])) ? $bid10[$row['f' . $i]]['attri'] * STORAGE_MULTIPLIER : 0); } //Great warehouse if ($row['f' . $i . 't'] == 38) { $ress += ((isset($bid38[$row['f' . $i]]) && isset($bid38[$row['f' . $i]]['attri'])) ? $bid38[$row['f' . $i]]['attri'] * STORAGE_MULTIPLIER : 0); } //Granary if ($row['f' . $i . 't'] == 11) { $crop += ((isset($bid11[$row['f' . $i]]) && isset($bid11[$row['f' . $i]]['attri'])) ? $bid11[$row['f' . $i]]['attri'] * STORAGE_MULTIPLIER : 0); } //Great granary if ($row['f' . $i . 't'] == 39) { $crop += ((isset($bid39[$row['f' . $i]]) && isset($bid39[$row['f' . $i]]['attri'])) ? $bid39[$row['f' . $i]]['attri'] * STORAGE_MULTIPLIER : 0); } } // no need for update, since we didn't find any warehouses or granaries // and maximums would have been set to correct values inside prune* functions already if ($ress == 0 && $crop == 0) continue; // maxstore nor maxcrop can go below the minimum threshold if ($ress < STORAGE_BASE) $ress = STORAGE_BASE; if ($crop < STORAGE_BASE) $crop = STORAGE_BASE; mysqli_query($database->dblink,'UPDATE `' . TB_PREFIX . 'vdata` SET `maxstore` = ' . (int) $ress . ', `maxcrop` = ' . (int) $crop . ' WHERE `wref` = ' . (int) $row['vref']); } mysqli_commit($database->dblink); } /*private function oasisResourcesProduce() { global $database; $speedMultiplier = (8 * (SPEED/3600)); $database->query(" UPDATE " . TB_PREFIX . "odata SET lastupdated = UNIX_TIMESTAMP(), wood = IF(wood + ($speedMultiplier * (UNIX_TIMESTAMP() - lastupdated)) > maxstore, maxstore, wood + ($speedMultiplier * (UNIX_TIMESTAMP() - lastupdated))), clay = IF(clay + ($speedMultiplier * (UNIX_TIMESTAMP() - lastupdated)) > maxstore, maxstore, clay + ($speedMultiplier * (UNIX_TIMESTAMP() - lastupdated))), iron = IF(iron + ($speedMultiplier * (UNIX_TIMESTAMP() - lastupdated)) > maxstore, maxstore, iron + ($speedMultiplier * (UNIX_TIMESTAMP() - lastupdated))), crop = IF(crop + ($speedMultiplier * (UNIX_TIMESTAMP() - lastupdated)) > maxcrop, maxcrop, crop + ($speedMultiplier * (UNIX_TIMESTAMP() - lastupdated))) WHERE wood < 800 OR clay < 800 OR iron < 800 OR crop < 800"); }*/ private function checkInvitedPlayes() { global $database; $q = "SELECT id, invited FROM ".TB_PREFIX."users WHERE invited > 0"; $array = $database->query_return($q); // preload villages data $userIDs = []; foreach($array as $user) { $userIDs[] = $user['id']; } $database->getProfileVillages($userIDs); // continue... foreach($array as $user) { $numusers = mysqli_fetch_array(mysqli_query($database->dblink,"SELECT Count(*) as Total FROM ".TB_PREFIX."users WHERE id = ".(int) $user['invited']), MYSQLI_ASSOC); if($numusers['Total'] > 0){ $varray = count($database->getProfileVillages($user['id'])); if($varray > 1){ $usergold = $database->getUserField($user['invited'],"gold",0); $gold = $usergold+50; $database->updateUserField($user['invited'],"gold",$gold,1); $database->updateUserField($user['id'],"invited",0,1); } } } } private function updateGeneralAttack() { global $database; mysqli_query($database->dblink, " UPDATE ".TB_PREFIX."general SET shown = 0 WHERE shown = 1 AND `time` < (UNIX_TIMESTAMP() - (86400 * 8))"); } private function MasterBuilder() { global $database; $q = "SELECT id, wid, type, level, field, timestamp FROM ".TB_PREFIX."bdata WHERE master = 1"; $array = $database->query_return($q); foreach($array as $master) { $owner = $database->getVillageField($master['wid'], 'owner'); $tribe = $database->getUserField($owner, 'tribe', 0); $villwood = $database->getVillageField($master['wid'], 'wood'); $villclay = $database->getVillageField($master['wid'], 'clay'); $villiron = $database->getVillageField($master['wid'], 'iron'); $villcrop = $database->getVillageField($master['wid'], 'crop'); $type = $master['type']; $level = $master['level']; $buildarray = $GLOBALS["bid".$type]; $buildwood = $buildarray[$level]['wood']; $buildclay = $buildarray[$level]['clay']; $buildiron = $buildarray[$level]['iron']; $buildcrop = $buildarray[$level]['crop']; $ww = count($database->getBuildingByType($master['wid'], 40)); if($tribe == 1){ if($master['field'] < 19){ $bdata = $database->getDorf1Building($master['wid']); $bdataTotal = count($bdata); $bbdata = count($database->getDorf2Building($master['wid'])); }else{ $bdata = $database->getDorf2Building($master['wid']); $bdataTotal = count($bdata); $bbdata = count($database->getDorf1Building($master['wid'])); } }else{ $bdata = array_merge($database->getDorf1Building($master['wid']), $database->getDorf2Building($master['wid'])); $bdataTotal = $bbdata = count($bdata); } if($database->getUserField($owner, 'plus', 0) > time() || $ww > 0){ if($bbdata < 2) $inbuild = 2; else $inbuild = 1; } else $inbuild = 1; $usergold = $database->getUserField($owner, 'gold', 0); if($bdataTotal < $inbuild && $buildwood <= $villwood && $buildclay <= $villclay && $buildiron <= $villiron && $buildcrop <= $villcrop && $usergold > 0){ $time = $master['timestamp'] + time(); if(!empty($bdata)){ foreach($bdata as $masterLoop) $time += ($masterLoop['timestamp'] - time()); } if($bdataTotal == 0) $database->updateBuildingWithMaster($master['id'], $time, 0); else $database->updateBuildingWithMaster($master['id'], $time, 1); $database->updateUserField($owner, 'gold', --$usergold, 1); $database->modifyResource($master['wid'], $buildwood, $buildclay, $buildiron, $buildcrop, 0); } } } /** * Function for starvation - by brainiacX and Shadow * Rework by ronix * Refactored by iopietro */ //TODO: This function needs to be splitted in many subfunctions (for TravianZ refactor) private function starvation() { global $database, $technology; // Starvation este dezactivat in perioada de pace (sarbatori) if(PEACE) return; $time = time(); // 1. Actualizeaza datele de starvation pentru toate satele $this->starvationUpdateAllVillages(); // 2. Incarca satele cu productie negativa $starvarray = $this->starvationGetVillagesWithDeficit(); if (empty($starvarray)) return; $vilIDs = array_column($starvarray, 'wref'); // 3. Pregateste cache-urile pentru a reduce query-urile $this->starvationPrepareCaches($vilIDs); foreach ($starvarray as $starv) { $this->starvationProcessVillage($starv, $time); } } /** * Actualizeaza tabela de starvation pentru toate satele */ private function starvationUpdateAllVillages() { global $database; $starvarray = $database->getProfileVillages(0, 7); foreach($starvarray as $starv) { $database->addStarvationData($starv['wref']); } } /** * Returneaza satele cu deficit de crop */ private function starvationGetVillagesWithDeficit() { global $database; return $database->getStarvation(); } /** * Pregateste cache-urile pentru trupe */ private function starvationPrepareCaches(array $vilIDs) { global $database; $database->getEnforceVillage($vilIDs, 0); $database->getOasisEnforce($vilIDs, 2); $database->getOasisEnforce($vilIDs, 3); $database->getPrisoners($vilIDs, 1); $database->getMovement(3, $vilIDs, 0); $database->getMovement(4, $vilIDs, 1); } /** * Proceseaza un singur sat pentru starvation */ private function starvationProcessVillage($starv, $time) { global $database, $technology; $unitarrays = $this->getAllUnits($starv['wref']); // Formula originala de upkeep - pastrata exact $upkeep = $starv['pop'] + $technology->getUpkeep($unitarrays, 0, $starv['wref']); $troopData = $this->starvationFindFirstTroopGroup($starv); if (empty($troopData['troops'])) { // Nu exista trupe, verifica doar resetarea $this->starvationCheckReset($starv, $upkeep); return; } $starvingTroops = $troopData['troops']; $type = $troopData['type']; $subtype = $troopData['subtype']; $timedif = $time - $starv['starvupdate']; $cropProd = $database->getCropProdstarv($starv['wref']) - $starv['starv']; if($cropProd < 0){ // Calcul deficit - formula originala pastrata $starvsec = (abs($cropProd) / 3600); $difcrop = ($timedif * $starvsec); $oldcrop = $database->getVillageField($starv['wref'], 'crop'); // Consuma mai intai crop-ul din hambar if ($oldcrop > 100) { $difcrop = $difcrop - $oldcrop; if($difcrop < 0){ $difcrop = 0; $newcrop = $oldcrop - $difcrop; $database->setVillageField($starv['wref'], 'crop', $newcrop); } } if($difcrop > 0 && $oldcrop <= 0){ $this->starvationKillTroops($starv, $starvingTroops, $type, $subtype, $difcrop, $upkeep, $time); } } $this->starvationCheckReset($starv, $upkeep); } /** * Gaseste primul grup de trupe care poate muri de foame * Ordinea: enforce oasis, enforce village, prisoners, unitati proprii, atacuri */ private function starvationFindFirstTroopGroup($starv) { global $database; $enforceArrays = [ $database->getOasisEnforce($starv['wref'], 2), $database->getOasisEnforce($starv['wref'], 3), $database->getEnforceVillage($starv['wref'], 2), $database->getEnforceVillage($starv['wref'], 3) ]; $prisonerArrays = [$database->getPrisoners($starv['wref'], 1)]; $unitArrays = ($database->getUnitsNumber($starv['wref'], 0) > 0) ? [[$database->getUnit($starv['wref'])]] : []; $attackArrays = [ $database->getMovement(3, $starv['wref'], 0), $database->getMovement(4, $starv['wref'], 1) ]; $allTroopsArray = [$enforceArrays, $prisonerArrays, $unitArrays, $attackArrays]; foreach($allTroopsArray as $type => $allTroops) { if(!empty($allTroops)){ foreach($allTroops as $subtype => $troops){ if(!empty($troops)){ return [ 'troops' => reset($troops), 'type' => $type, 'subtype' => $subtype ]; } } } } return ['troops' => [], 'type' => null, 'subtype' => null]; } /** * Ucide trupele conform deficitului de crop */ private function starvationKillTroops($starv, &$starvingTroops, $type, $subtype, $difcrop, $upkeep, $time) { global $database; $tribe = $database->getUserField(($type == 2) ? $starv['owner'] : $database->getVillageField($starvingTroops['from'], "owner"), "tribe", 0); $special = in_array($type, [1, 3]); $start = $special ? 1 : ($tribe - 1) * 10 + 1; $end = $special ? 10 : $tribe * 10; $utype = $special ? 't' : 'u'; $heroType = $special ? 't11' : 'hero'; $killedUnits = []; $totalUnits = 0; $counting = true; while($difcrop > 0) { $maxcount = $maxtype = 0; for($i = $start; $i <= $end; $i++) { $units = (isset($starvingTroops[$utype.$i]) ? $starvingTroops[$utype.$i] : 0); if($counting) $totalUnits += $units; if($units > $maxcount){ $maxcount = $units; $maxtype = $i; } } if($counting) $counting = false; if($maxtype > 0){ $starvingTroops[$utype.$maxtype]--; $killedUnits[$maxtype] = ($killedUnits[$maxtype] ?? 0) + 1; // Formula originala de consum crop per unitate $unitIndex = $special ? $maxtype + ($tribe - 1) * 10 : $maxtype; $difcrop -= $GLOBALS['u'.$unitIndex]['crop']; } else break; } $totalKilledUnits = array_sum($killedUnits); $newCrop = 0; // Verifica daca eroul moare if($starvingTroops[$heroType] > 0 && ($totalUnits == 0 || $totalUnits == $totalKilledUnits)){ $totalKilledUnits += $starvingTroops[$heroType]; $totalUnits += $starvingTroops[$heroType]; $heroOwner = ($type == 2) ? $starv['owner'] : $database->getVillageField(($type == 3 && $subtype == 1) ? $starvingTroops['to'] : $starvingTroops['from'], "owner"); $heroInfo = $database->getHero($heroOwner)[0]; $database->modifyHero("dead", 1, $heroInfo['heroid']); $database->modifyHero("health", 0, $heroInfo['heroid']); $newCrop = $GLOBALS['h'.$heroInfo['unit'].'_full'][min($heroInfo['level'], 60)]['crop'] + $difcrop; } else if($maxtype > 0) { $newCrop = $GLOBALS['u'.$maxtype]['crop']; } if($totalKilledUnits > 0) { $this->starvationApplyDatabaseChanges($starv, $starvingTroops, $type, $killedUnits, $totalUnits, $totalKilledUnits, $newCrop, $upkeep, $time); } } /** * Aplica modificarile in baza de date */ private function starvationApplyDatabaseChanges($starv, $starvingTroops, $type, $killedUnits, $totalUnits, $totalKilledUnits, $newCrop, $upkeep, $time) { global $database; switch($type){ case 0: // enforce if($totalKilledUnits < $totalUnits){ $database->modifyEnforce($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0); } else { $database->deleteReinf($starvingTroops['id']); } break; case 1: // prisoners if($totalKilledUnits < $totalUnits){ $database->modifyPrisoners($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0); $database->modifyUnit($starvingTroops['wref'], ["99o"], [$totalKilledUnits], [0]); } else { $database->deletePrisoners($starvingTroops['id']); $database->modifyUnit($starvingTroops['wref'], ["99o"], [$totalUnits], [0]); } break; case 2: // unitati proprii $database->modifyUnit($starv['wref'], array_keys($killedUnits), array_values($killedUnits), [0]); break; case 3: // atacuri in miscare if($totalKilledUnits < $totalUnits){ $database->modifyAttack2($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0); } else { $database->setMovementProc($starvingTroops['moveid']); } break; } $database->modifyResource($starv['wref'], 0, 0, 0, max($newCrop, 0), 1); $database->setVillageField($starv['wref'], ['starv', 'starvupdate'], [$upkeep, $time]); } /** * Verifica daca starvation poate fi resetat */ private function starvationCheckReset($starv, $upkeep) { global $database; $crop = $database->getCropProdstarv($starv['wref'], false); if ($crop > $upkeep) { $database->setVillageFields($starv['wref'], ['starv', 'starvupdate'], [0, 0]); } } private function procNewClimbers() { global $database, $ranking; $ranking->procRankArray(); $climbers = $ranking->getRank(); if(count($climbers) > 0){ $q = "SELECT week FROM ".TB_PREFIX."medal order by week DESC LIMIT 0, 1"; $result = mysqli_query($database->dblink,$q); if(mysqli_num_rows($result)) { $row = mysqli_fetch_assoc($result); $week = $row['week'] + 1; } else $week = 1; $q = "SELECT id FROM ".TB_PREFIX."users where oldrank = 0 and id > 5"; $array = $database->query_return($q); foreach($array as $user){ $newrank = $ranking->getUserRank($user['id']); if($week > 1){ for($i = $newrank + 1; $i < count($climbers); $i++) { if(isset($climbers[$i]['userid'])){ $oldrank = $ranking->getUserRank($climbers[$i]['userid']); $totalpoints = $oldrank - $climbers[$i]['oldrank']; $database->removeclimberrankpop($climbers[$i]['userid'], $totalpoints); $database->updateoldrank($climbers[$i]['userid'], $oldrank); } } $database->updateoldrank($user['id'], $newrank); }else{ $totalpoints = count($climbers) - $newrank; $database->setclimberrankpop($user['id'], $totalpoints); $database->updateoldrank($user['id'], $newrank); for($i = 1; $i < $newrank; $i++){ if(isset($climbers[$i]['userid'])){ $oldrank = $ranking->getUserRank($climbers[$i]['userid']); $totalpoints = count($climbers) - $oldrank; $database->setclimberrankpop($climbers[$i]['userid'], $totalpoints); $database->updateoldrank($climbers[$i]['userid'], $oldrank); } } for($i = $newrank + 1; $i < count($climbers); $i++){ if(isset($climbers[$i]['userid'])){ $oldrank = $ranking->getUserRank($climbers[$i]['userid']); $totalpoints = count($climbers) - $oldrank; $database->setclimberrankpop($climbers[$i]['userid'], $totalpoints); $database->updateoldrank($climbers[$i]['userid'], $oldrank); } } } } } } private function procClimbers($uid) { global $database, $ranking; $ranking->procRankArray(); $climbers = $ranking->getRank(); if(count($ranking->getRank()) > 0){ $q = "SELECT week FROM ".TB_PREFIX."medal order by week DESC LIMIT 0, 1"; $result = mysqli_query($database->dblink,$q); if(mysqli_num_rows($result)) { $row = mysqli_fetch_assoc($result); $week = $row['week'] + 1; } else $week = 1; $myrank = $ranking->getUserRank($uid); if(isset($climbers[$myrank]['oldrank']) && $climbers[$myrank]['oldrank'] > $myrank){ for($i = $myrank + 1; $i <= $climbers[$myrank]['oldrank']; $i++) { if(isset($climbers[$i]['oldrank'])){ $oldrank = $ranking->getUserRank($climbers[$i]['userid']); if($week > 1){ $totalpoints = $oldrank - $climbers[$i]['oldrank']; $database->removeclimberrankpop($climbers[$i]['userid'], $totalpoints); $database->updateoldrank($climbers[$i]['userid'], $oldrank); }else{ $totalpoints = count($ranking->getRank()) - $oldrank; $database->setclimberrankpop($climbers[$i]['userid'], $totalpoints); $database->updateoldrank($climbers[$i]['userid'], $oldrank); } } } if(isset($climbers[$myrank]['oldrank'])){ if($week > 1){ $totalpoints = $climbers[$myrank]['oldrank'] - $myrank; $database->addclimberrankpop($climbers[$myrank]['userid'], $totalpoints); $database->updateoldrank($climbers[$myrank]['userid'], $myrank); }else{ $totalpoints = count($ranking->getRank()) - $myrank; $database->setclimberrankpop($climbers[$myrank]['userid'], $totalpoints); $database->updateoldrank($climbers[$myrank]['userid'], $myrank); } } }else if(isset($climbers[$myrank]['oldrank']) && $climbers[$myrank]['oldrank'] < $myrank){ for($i = $climbers[$myrank]['oldrank']; $i < $myrank; $i++) { if(isset($climbers[$i]['oldrank'])){ $oldrank = $ranking->getUserRank($climbers[$i]['userid']); if($week > 1){ $totalpoints = $climbers[$i]['oldrank'] - $oldrank; $database->addclimberrankpop($climbers[$i]['userid'], $totalpoints); $database->updateoldrank($climbers[$i]['userid'], $oldrank); }else{ $totalpoints = count($ranking->getRank()) - $oldrank; $database->setclimberrankpop($climbers[$i]['userid'], $totalpoints); $database->updateoldrank($climbers[$i]['userid'], $oldrank); } } } if(isset($climbers[$myrank-1]['oldrank'])){ if($week > 1){ $totalpoints = $myrank - $climbers[$myrank-1]['oldrank']; $database->removeclimberrankpop($climbers[$myrank-1]['userid'], $totalpoints); $database->updateoldrank($climbers[$myrank-1]['userid'], $myrank); }else{ $totalpoints = count($ranking->getRank()) - $myrank; $database->setclimberrankpop($climbers[$myrank-1]['userid'], $totalpoints); $database->updateoldrank($climbers[$myrank-1]['userid'], $myrank); } } } } $ranking->procARankArray(); $aid = $database->getUserField($uid,"alliance",0); if(count($ranking->getRank()) > 0 && $aid != 0){ $ally = $database->getAlliance($aid); $memberlist = $database->getAllMember($ally['id']); $oldrank = 0; $memberIDs = []; foreach($memberlist as $member) { $memberIDs[] = $member['id']; } $data = $database->getVSumField($memberIDs,"pop"); if (count($data)) { foreach ($data as $row) { $oldrank += $row['Total']; } } if($ally['oldrank'] != $oldrank){ if($ally['oldrank'] < $oldrank) { $totalpoints = $oldrank - $ally['oldrank']; $database->addclimberrankpopAlly($ally['id'], $totalpoints); $database->updateoldrankAlly($ally['id'], $oldrank); } else if($ally['oldrank'] > $oldrank) { $totalpoints = $ally['oldrank'] - $oldrank; $database->removeclimberrankpopAlly($ally['id'], $totalpoints); $database->updateoldrankAlly($ally['id'], $oldrank); } } } } private function checkBan() { global $database; mysqli_query($database->dblink, " UPDATE ".TB_PREFIX."banlist as b JOIN ".TB_PREFIX."users as u ON b.uid = u.id SET b.active = 0, u.access = 2 WHERE b.active = 1 AND b.`end` < UNIX_TIMESTAMP() AND b.`end` > 0"); } private function regenerateOasisTroops() { global $database; $timeFinal = time() - NATURE_REGTIME; $q = "SELECT wref FROM " . TB_PREFIX . "odata where conqured = 0 and lastupdated2 < $timeFinal"; $array = $database->query_return($q); if (count($array)) { $ids = []; foreach($array as $oasis) $ids[] = $oasis['wref']; $database->regenerateOasisUnits($ids, true); } } public static function updateMax($leader) { global $bid18, $database; $q = mysqli_fetch_array(mysqli_query($database->dblink,"SELECT Count(*) as Total FROM " . TB_PREFIX . "alidata where leader = ". (int) $leader), MYSQLI_ASSOC); if ($q['Total'] > 0) { $villages = $database->getVillagesID2($leader); $max = 0; // cache resource levels $vilIDs = []; foreach($villages as $village){ $vilIDs[$village['wref']] = true; } $database->cacheResourceLevels(array_keys($vilIDs)); foreach($villages as $village){ $field = $database->getResourceLevel($village['wref'], false); for($i = 19; $i <= 40; $i++){ if($field['f'.$i.'t'] == 18){ $level = $field['f'.$i]; $attri = $bid18[$level]['attri']; } } if($attri > $max){ $max = $attri; } } $q = "UPDATE ".TB_PREFIX."alidata set max = ".(int) $max." where leader = ".(int) $leader; $database->query($q); } } /** * Function for automate medals - by yi12345 and Shadow * */ function medals() { global $ranking, $database; // --- Configuration --- $minUid = 5; // Exclude BANNED (0), MH (8), ADMIN (9) $userFilter = "id > $minUid AND access NOT IN (0,8,9)"; // Helper to insert a user medal $insertMedal = function($userid, $category, $place, $week, $points, $img) use ($database) { $q = "INSERT INTO ".TB_PREFIX."medal (userid, categorie, plaats, week, points, img) VALUES (". (int)$userid.", ".(int)$category.", ".(int)$place.", ".(int)$week.", '".mysqli_real_escape_string($database->dblink, $points)."', '".mysqli_real_escape_string($database->dblink, $img)."')"; mysqli_query($database->dblink, $q); }; // --- Check if we should award medals now --- $giveMedal = false; $q = "SELECT lastgavemedal FROM ".TB_PREFIX."config"; $result = mysqli_query($database->dblink, $q); if ($result) { $row = mysqli_fetch_assoc($result); $stime = strtotime(START_DATE) - strtotime(date('d.m.Y')) + strtotime(START_TIME); if ($row['lastgavemedal'] == 0 && $stime < time()) { // First run after server start - schedule next run $setDays = round(MEDALINTERVAL / 86400); $newtime = $setDays < 7? strtotime(($setDays + 1).' day midnight') : strtotime('next monday'); $database->query("UPDATE ".TB_PREFIX."config SET lastgavemedal = ".(int)$newtime); } elseif ($row['lastgavemedal']!= 0) { $time = $row['lastgavemedal'] + MEDALINTERVAL; $giveMedal = $row['lastgavemedal'] < time(); } } if (!($giveMedal && MEDALINTERVAL > 0)) { return; } // --- Determine current week numbers --- $getNextWeek = function($table) use ($database) { $q = "SELECT week FROM ".TB_PREFIX."$table ORDER BY week DESC LIMIT 1"; $res = mysqli_query($database->dblink, $q); return $res && mysqli_num_rows($res)? (mysqli_fetch_assoc($res)['week'] + 1) : 1; }; $week = $getNextWeek('medal'); $allyweek = $getNextWeek('allimedal'); // --- Award top 10 for each category --- $awardTop = function($field, $category, $imgPrefix) use ($database, $userFilter, $week, $insertMedal) { $q = "SELECT id, $field FROM ".TB_PREFIX."users WHERE $userFilter ORDER BY $field DESC, id DESC LIMIT 10"; $res = mysqli_query($database->dblink, $q); $i = 0; while ($row = mysqli_fetch_array($res)) { $i++; $insertMedal($row['id'], $category, $i, $week, $row[$field], $imgPrefix.$i); } }; // Attackers of the week (cat 1) $awardTop('ap', 1, 't2_'); // Defenders of the week (cat 2) $awardTop('dp', 2, 't3_'); // Climbers of the week (cat 3) $awardTop('Rc', 3, 't1_'); // Rank climbers of the week (cat 10) $awardTop('clp', 10, 't6_'); // Robbers of the week (cat 4) $awardTop('RR', 4, 't4_'); // --- Bonus: player in both top10 attack AND defense (cat 5) --- $topAttackers = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."users WHERE $userFilter ORDER BY ap DESC, id DESC LIMIT 10"); while ($a = mysqli_fetch_array($topAttackers)) { $topDefenders = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."users WHERE $userFilter ORDER BY dp DESC, id DESC LIMIT 10"); while ($d = mysqli_fetch_array($topDefenders)) { if ($a['id'] == $d['id']) { $cnt = mysqli_fetch_row(mysqli_query($database->dblink, "SELECT COUNT(*) FROM ".TB_PREFIX."medal WHERE userid=".(int)$a['id']." AND categorie=5"))[0]; if ($cnt <= 2) { $texts = [0 => '', 1 => 'twice ', 2 => 'three times ']; $insertMedal($a['id'], 5, 0, $week, $texts[$cnt], 't22'.$cnt.'_1'); } } } } // --- Milestone ribbons for 3/5/10 times in top3 or top10 --- $awardMilestone = function($field, $sourceCat, $topLimit, $targetCat, $imgBase) use ($database, $userFilter, $week, $insertMedal) { $res = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."users WHERE $userFilter ORDER BY $field DESC, id DESC LIMIT 10"); while ($u = mysqli_fetch_array($res)) { $cnt = mysqli_fetch_row(mysqli_query($database->dblink, "SELECT COUNT(*) FROM ".TB_PREFIX."medal WHERE userid=".(int)$u['id']." AND categorie=".(int)$sourceCat." AND plaats<=".(int)$topLimit))[0]; $map = ['3' => ['Three', $imgBase.'0_1'], '5' => ['Five', $imgBase.'1_1'], '10' => ['Ten', $imgBase.'2_1']]; if (isset($map[$cnt])) { $insertMedal($u['id'], $targetCat, 0, $week, $map[$cnt][0], $map[$cnt][1]); } } }; // Attackers milestones $awardMilestone('ap', 1, 3, 6, 't12'); // top3 attackers $awardMilestone('ap', 1, 10, 12, 't13'); // top10 attackers // Defenders milestones $awardMilestone('dp', 2, 3, 7, 't14'); $awardMilestone('dp', 2, 10, 13, 't15'); // Climbers milestones $awardMilestone('Rc', 3, 3, 8, 't10'); $awardMilestone('Rc', 3, 10, 14, 't11'); // Rank climbers milestones $awardMilestone('clp', 10, 3, 11, 't20'); $awardMilestone('clp', 10, 10, 16, 't21'); // Robbers milestones $awardMilestone('RR', 4, 3, 9, 't16'); $awardMilestone('RR', 4, 10, 15, 't17'); // --- Reset weekly stats for eligible users --- $ids = []; $res = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."users WHERE $userFilter"); while ($r = mysqli_fetch_row($res)) { $ids[] = (int)$r[0]; } if ($ids) { mysqli_query($database->dblink, "UPDATE ".TB_PREFIX."users SET ap=0, dp=0, Rc=0, clp=0, RR=0 WHERE id IN(".implode(',', $ids).")"); } // --- Alliance medals (no user filter needed) --- $insertAlly = function($allyid, $cat, $place, $week, $points, $img) use ($database) { $q = "INSERT INTO ".TB_PREFIX."allimedal (allyid, categorie, plaats, week, points, img) VALUES (".(int)$allyid.", '".(int)$cat."', ".(int)$place.", '".(int)$week."', '".mysqli_real_escape_string($database->dblink, $points)."', '".mysqli_real_escape_string($database->dblink, $img)."')"; mysqli_query($database->dblink, $q); }; $allyCats = [ ['ap', 1, 'a2_'], ['dp', 2, 'a3_'], ['RR', 4, 'a4_'], ['clp', 3, 'a1_'] ]; foreach ($allyCats as [$field, $cat, $img]) { $res = mysqli_query($database->dblink, "SELECT id, $field FROM ".TB_PREFIX."alidata ORDER BY $field DESC, id DESC LIMIT 10"); $i = 0; while ($r = mysqli_fetch_array($res)) { $i++; $insertAlly($r['id'], $cat, $i, $allyweek, $r[$field], $img.$i); } } // Alliance bonus for attack+defense $resA = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."alidata ORDER BY ap DESC, id DESC LIMIT 10"); while ($a = mysqli_fetch_array($resA)) { $resD = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."alidata ORDER BY dp DESC, id DESC LIMIT 10"); while ($d = mysqli_fetch_array($resD)) { if ($a['id'] == $d['id']) { $cnt = mysqli_fetch_row(mysqli_query($database->dblink, "SELECT COUNT(*) FROM ".TB_PREFIX."allimedal WHERE allyid=".(int)$a['id']." AND categorie=5"))[0]; if ($cnt <= 2) { $texts = [0 => '', 1 => 'twice ', 2 => 'three times ']; $insertAlly($a['id'], 5, 0, $allyweek, $texts[$cnt], 't22'.$cnt.'_1'); } } } } // Reset alliance stats $aliIds = []; $res = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."alidata"); while ($r = mysqli_fetch_row($res)) { $aliIds[] = (int)$r[0]; } if ($aliIds) { mysqli_query($database->dblink, "UPDATE ".TB_PREFIX."alidata SET ap=0, dp=0, RR=0, clp=0 WHERE id IN(".implode(',', $aliIds).")"); } // --- Update last awarded time --- $database->query("UPDATE ".TB_PREFIX."config SET lastgavemedal=".(int)$time); } private function artefactOfTheFool() { global $database; $time = time(); $q = "SELECT id, size FROM " . TB_PREFIX . "artefacts where type = 8 AND active = 1 AND del = 0 AND lastupdate <= ".($time - (86400 / (SPEED == 2 ? 1.5 : (SPEED == 3 ? 2 : SPEED)))); $array = $database->query_return($q); if ($array) { foreach($array as $artefact) { $kind = rand(1, 7); while($kind == 6) $kind = rand(1, 7); if($artefact['size'] != 3) $bad_effect = rand(0, 1); else $bad_effect = 0; switch($kind) { case 1: $effect = rand(1, 5); break; case 2: $effect = rand(1, 3); break; case 3: $effect = rand(3, 10); break; case 4: case 5: $effect = rand(2, 4); break; case 7: $effect = rand(1, 6); break; } mysqli_query($database->dblink,"UPDATE ".TB_PREFIX."artefacts SET kind = ". (int) $kind. ", bad_effect = $bad_effect, effect2 = $effect, lastupdate = $time WHERE id = ".(int) $artefact['id']); } } } } $automation = new Automation; // remove automation lock file @unlink( AUTOMATION_LOCK_FILE_NAME ); ?>