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." \"".rc_tok('RC_CATAPULT')."\" ".rc_bld($tbgid, $can_destroy)." ".rc_tok('RC_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.", ".rc_bld($tbgid, $can_destroy)." ".rc_tok('RC_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." \"".rc_tok('RC_CATAPULT')."\" ".rc_bld($tbgid, $can_destroy)." ".rc_tok('RC_NOT_DAMAGED').""; } } else { $info_cat = "".$catp_pic.",".rc_bld($tbgid, $can_destroy)." ".rc_tok('RC_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 = " ".rc_tok('RC_DAMAGED_FROM_TO', $tblevel, $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." \"".rc_tok('RC_CATAPULT')."\" ".rc_bld($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 . "," . rc_bld($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.", ".rc_tok('RC_NO_BUILDINGS'); }else if(strpos($info_cat, rc_tok('RC_NO_BUILDINGS')) === false){ $info_cat .= "".INFORMATION." \"".rc_tok('RC_CATAPULT')."\" ".rc_tok('RC_NO_BUILDINGS')."."; } } } 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.", ".rc_tok('RC_VILLAGE_ALREADY_DESTROYED'); else $info_cat = "".$catp_pic.", ".rc_tok('RC_VILLAGE_CANT_DESTROY'); } 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 . ',' . rc_tok('RC_NO_REDUCE_CP_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; elseif ($palacelevel['f' . $i . 't'] == 44) $plevel = $i; // Command Center (Huni) } // Command Center: sloturi de expansiune ca la Palat (10 -> 1, 15 -> 2, 20 -> 3) if ($palacelevel['f' . $plevel . 't'] == 44) { 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'] == 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 . ',' . rc_tok('RC_NOT_ENOUGH_CP'); 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 . ',' . rc_tok('RC_CANT_TAKEOVER'); 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 . ',' . rc_tok('RC_CANT_TAKEOVER'); return ['info_chief' => $info_chief, 'chiefing_village' => $chiefing_village, 'village_destroyed' => $village_destroyed]; } if ($database->getFieldLevelInVillage($data['to'], '25, 26, 44')) { $info_chief = $chief_pic . ',' . rc_tok('RC_RESIDENCE_NOT_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']; // Bug fix: Brewery (35) is capital-only but empire-wide — its effect // must be checked on the attacker's CAPITAL, not on $data['from'] (the // launching village, which may not be the capital at all), and only // while a Mead-Festival is actually active there, not just because // the Brewery has been built (it has no permanent effect). if ($owntribe == 2) { $attackerCapital = $database->getVillage($from['owner'], 3); if ($attackerCapital && (int)$attackerCapital['festival'] > $time && $this->getTypeLevel(35, $attackerCapital['wref']) > 0) { $reducedLoyalty /= 2; } } $reducedLoyaltyTotal += $reducedLoyalty; } if (($toF['loyalty'] - $reducedLoyaltyTotal) > 0) { $info_chief = $chief_pic . ',' . rc_tok('RC_LOYALTY_LOWERED', floor($toF['loyalty']), 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 . ',' . rc_tok('RC_INHABITANTS_JOIN', $villname); 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')]); // Milestones: first WW village ever conquered, and — separately — // first village ever conquered FROM ANOTHER PLAYER (not from // Natars). $to is this function's own parameter (not re-fetched), // so $to['natar']/$to['owner'] still reflect the village's state // from BEFORE this conquest, which is exactly what we need here. // natar==1 marks one of the 13 pre-built WW conquest targets (see // Artifacts::createWWVillages()) — Natars' capital and artifact/ // plan villages are natar=0, so this check cannot misfire on those. if (defined('NEW_FUNCTIONS_MILESTONES') && NEW_FUNCTIONS_MILESTONES) { $newOwner = $database->getVillageField($data['from'], 'owner'); if ((int)($to['natar'] ?? 0) === 1) { $database->recordMilestoneIfFirst('first_ww', $newOwner, $data['to']); } elseif ((int)($to['owner'] ?? 0) !== 3) { $database->recordMilestoneIfFirst('first_pvp_conquest', $newOwner, $data['to']); } } $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 <= 90; $u++) $units2reset[] = 'u' . $u . ' = 0'; // cucerirea goleste spitalul si coada de vindecare $database->clearHospital($data['to']); $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) { // Main Building (gid 15) nu poate cobori sub nivel 1: satul trebuie // sa ramana locuibil dupa cucerire (altfel pop 0 + nicio cladire). $minLevel = ($buildlevel['f' . $i . 't'] == 15) ? 1 : 0; $leveldown = $buildlevel['f' . $i] - 1; if ($leveldown < $minLevel) $leveldown = $minLevel; $newLevels_fieldNames[] = 'f' . $i; $newLevels_fieldValues[] = $leveldown; // sterge tipul cladirii DOAR daca a ajuns efectiv la 0 // (bug vechi: "!$leveldown > 0" era mereu adevarat pt leveldown==0, // deci stergea tipul si cand cladirea cobora la nivel 1) 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); $database->getMInfo($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)."
\"".rc_tok('CARRY')."\"".rc_tok('RC_TOTAL_RESOURCES')." ".round($totwood+$totclay+$totiron+$totcrop)."
"; }else if($data['spy'] == 2){ if ($isoasis == 0){ $walllevel = $database->getFieldLevelInVillage($data['to'], '31, 32, 33, 42, 43, 47, 50'); $residencelevel = $database->getFieldLevelInVillage($data['to'], 25); $palacelevel = $database->getFieldLevelInVillage($data['to'], 26); $residenceimg = "\"".RESIDENCE."\""; $palaceimg = "\"".PALACE."\""; $crannyimg = "\"".CRANNY."\""; $wallgids = array(1 => 31, 2 => 32, 3 => 33, 6 => 43, 7 => 42, 8 => 47, 9 => 50); $wallgid = isset($wallgids[$targettribe]) ? $wallgids[$targettribe] : 31; $wallimg = "\"".rc_tok('RC_WALL')."\""; $info_spy = "".$spy_pic.","; if($residencelevel > 0) $info_spy .= $residenceimg." ".rc_tok('RC_RESIDENCE_LEVEL')."".$residencelevel."
"; elseif($palacelevel > 0) $info_spy .= $palaceimg." ".rc_tok('RC_PALACE_LEVEL')." ".$palacelevel."
"; if($walllevel > 0) $info_spy .= $wallimg." ".rc_tok('RC_WALL_LEVEL')." ".$walllevel."
"; $info_spy .= $crannyimg." ".rc_tok('RC_CRANNY_CAPACITY')." ".$crannySpy.""; } else $info_spy = "".$spy_pic.", ".rc_tok('RC_NO_INFO'); } 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 ? " ".rc_tok('RC_OF_WHICH_SAVED',$ownAlive) : ""; $anotherDeadsText = ($anotherAlive = $anothertroops - $anotherDeads) > 0 ? " ".rc_tok('RC_OF_WHICH_SAVED',$anotherAlive) : ""; $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 = "\"".rc_tok('RC_TRAP')."\""; $p_username = $database->getUserField($from['owner'], "username", 0); if ($mytroops > 0 && $anothertroops > 0) { return $trapper_pic." ".$p_username." ".rc_tok('RC_FREED_FROM_HIS_TROOPS', $mytroops).$ownDeadsText." ".rc_tok('RC_AND_FRIENDLY_TROOPS', $anothertroops).$anotherDeadsText."."; } elseif ($mytroops > 0) { return $trapper_pic." ".$p_username." ".rc_tok('RC_FREED_FROM_HIS_TROOPS', $mytroops).$ownDeadsText."."; } elseif ($anothertroops > 0) { return $trapper_pic." ".$p_username." ".rc_tok('RC_FREED_FRIENDLY_TROOPS', $anothertroops).$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, $hun, $egy, $spa, $vik, $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] .','.$hun.','.$unitssend_def[6].','.$unitsdead_def[6] .','.$egy.','.$unitssend_def[7].','.$unitsdead_def[7] .','.$spa.','.$unitssend_def[8].','.$unitsdead_def[8] .','.$vik.','.$unitssend_def[9].','.$unitsdead_def[9] .','.$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, rc_tok('RC_VILLAGE_DESTROYED')) === false) { $info_cat .= "".INFORMATION."" . "\"".rc_tok('RC_CATAPULT')."\"" . " ".rc_tok('RC_VILLAGE_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] .','.$hun.','.$unitssend_def[6].','.$unitsdead_def[6] .','.$egy.','.$unitssend_def[7].','.$unitsdead_def[7] .','.$spa.','.$unitssend_def[8].','.$unitsdead_def[8] .','.$vik.','.$unitssend_def[9].','.$unitsdead_def[9] .','.$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] .','.$hun.','.$unitssend_deff[6].','.$unitsdead_deff[6] .','.$egy.','.$unitssend_deff[7].','.$unitsdead_deff[7] .','.$spa.','.$unitssend_deff[8].','.$unitsdead_deff[8] .','.$vik.','.$unitssend_deff[9].','.$unitsdead_deff[9] .','.$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); // Spital: o parte din mortii proprii (aparare) devin raniti - doar unitatile de lupta X1-X8 $woundedMap = []; for($wi = $start; $wi <= $start + 7; $wi++) { if(!empty($owndead[$wi])) $woundedMap[$wi] = $owndead[$wi]; } $this->applyHospitalWounded($data['to'], $woundedMap); 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 = $hun = $egy = $spa = $vik = 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 6: $hun = 1; break; case 7: $egy = 1; break; case 8: $spa = 1; break; case 9: $vik = 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, 'hun' => $hun, 'egy' => $egy, 'spa' => $spa, 'vik' => $vik]; } /** * 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 <= 90; $i++) { $DefenderEnf['u'.$i] = 0; if ($i <= 9) { $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 <= 90; $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 <= 9; $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.",".rc_tok('RC_NO_WALL'); 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.",".rc_tok('RC_WALL_DESTROYED'); $database->setVillageLevel($data['to'], ["f".$wallid, "f".$wallid."t"], [0, 0]); $this->recountPop($data['to']); }elseif ($newLevel == $walllevel){ $info_ram = "".$ram_pic.",".rc_tok('RC_WALL_NOT_DAMAGED'); }else{ $info_ram = "".$ram_pic.",".rc_tok('RC_WALL_DAMAGED_FROM_TO', $walllevel, $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, 58, 68, 78, 88]; $ram = [7, 17, 27, 47, 57, 67, 77, 87]; $chief = [9, 19, 29, 49, 59, 69, 79, 89]; } else { $catapult = [8, 18, 28, 38, 48, 58, 68, 78, 88]; $ram = [7, 17, 27, 37, 47, 57, 67, 77, 87]; $chief = [9, 19, 29, 39, 49, 59, 69, 79, 89]; } $spys = [4, 14, 23, 44, 52, 64, 74, 82]; $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 <= 90; $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 <= 90; $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 <= 90; $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.",".rc_tok('RC_HERO_NO_KILL'); } else { $xp = " ".rc_tok('RC_HERO_AND_GAINED_XP_BATTLE', $heroxp); $info_hero = $hero_pic.",".rc_tok('RC_HERO_GAINED_XP', $heroxp); } 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.",".rc_tok('RC_HERO_CONQUERED_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.",".rc_tok('RC_HERO_REDUCED_OASIS_LOYALTY', $Oloyaltynow, $Oloyaltybefore).$xp; } else $info_hero = $hero_pic.",".rc_tok('RC_NO_REDUCE_LOYALTY_RAID').$xp; } } } } else { if ($heroxp == 0) $xp=" ".rc_tok('RC_HERO_NO_XP_BATTLE'); else $xp=" ".rc_tok('RC_HERO_GAINED_XP_BATTLE', $heroxp); $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.",".rc_tok('RC_HERO_CARRYING_ARTIFACT', $artifact['name']).$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, rc_tok('RC_VILLAGE_DESTROYED')) === false) $info_hero .= " ".rc_tok('RC_VILLAGE_DESTROYED'); } } else $info_hero = $hero_pic.",".$artifactError.$xp; } else $info_hero = $hero_pic.",".rc_tok('RC_HERO_NO_ARTIFACT_RAID').$xp; } } }elseif($data['t11'] > 0) { if ($heroxp == 0) $xp = ""; else $xp = " ".rc_tok('RC_HERO_BUT_GAINED_XP_BATTLE', $heroxp); if ($traped11 > 0) $info_hero = $hero_pic.",".rc_tok('RC_HERO_TRAPPED').$xp; else $info_hero = $hero_pic.",".rc_tok('RC_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; // Spital: o parte din trupele proprii moarte IN ATAC devin ranite acasa. // $dead e indexat 1..11 pe sloturile tribului; doar unitatile de lupta (1-8). if($owntribe >= 1 && $owntribe <= 9) { $woundedAtt = []; for($wj = 1; $wj <= 8; $wj++) { if(!empty($dead[$wj])) $woundedAtt[($owntribe - 1) * 10 + $wj] = $dead[$wj]; } if(!empty($woundedAtt)) $this->applyHospitalWounded($from['wref'], $woundedAtt); } // 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]; // T4 hero port (Phase 6): bandages revive part of the // losses when the hero comes home alive; revived units // are written back to the attacks row (the source // returnunitsComplete reads on arrival). No-op when off. $returningTroops = HeroBattleBonus::applyBandages($from['owner'], $returningTroops, $dead, (int) $data['ref']); $troopsTime = $units->getWalkingTroopsTime($from['wref'], $to['wref'], $from['owner'], $owntribe, $returningTroops, 1, 't'); $endtime = $database->getArtifactsValueInfluence($from['owner'], $from['wref'], 2, $troopsTime); $endtime += $AttackArrivalTime; // T4 hero port (Phase 5): the map shortens the return // leg when the SURVIVING hero travels home. No-op when off. $endtime = HeroBattleBonus::adjustReturnEndtime($from['owner'], $returningTroops['t11'], $AttackArrivalTime, $endtime); 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); } } } /** * Destroy the target village when the battle flagged it for destruction: * reassign the capital to the player's most populated remaining village if * needed, delete the village and reassign its hero. * Pure behaviour-preserving extraction (refactor for issue #155). * * @param int $village_destroyed Village-destroyed flag. * @param int $can_destroy Village-destruction allowed flag. * @param array $data Current attack row (uses 'to'). * @param array $to Defender village info (uses 'capital'). * @param array $varray The defender owner's villages. * @return void */ private function handleVillageDestruction($village_destroyed, $can_destroy, $data, $to, $varray) { global $database; if (!($village_destroyed == 1 && $can_destroy == 1)) { return; } 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']); } private function sendunitsComplete() { // COMPLETE ATTACK PROCESSING - critical function, kept 100% compatible // This function handles all attacks that reach their destination. // Includes: battles, traps, hero evasion, building destruction, and village conquest. global $bid23, $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); // tiles whose village was razed earlier in this same batch (issue #298) $razedTargets = []; // 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']; // Issue #298: the target village no longer exists — it was razed // either earlier in this same batch ($razedTargets), or in an // earlier tick whose still-in-flight follow-up waves DelVillage() // failed to bounce. getMInfo() then returns NULL vdata columns // ($to['wref'] is NULL), so resolving a battle here would fight a // phantom village and compute the return trip from NULL coordinates // — a bogus arrival time that strands the troops in an endless loop // (report against "[?]"). Bounce the whole army straight home // instead, exactly like DelVillage() does for in-flight attacks, // and mark the movement processed so it stops being re-fetched. if (isset($razedTargets[$data['to']]) || empty($to['wref'])) { // only own the bounce if DelVillage() hasn't already handled it // (setMovementProc() returns true only when it flips proc 0->1), // so we never create a duplicate return movement if ($this->setMovementProc($data['moveid'])) { $bounceTime = $units->getWalkingTroopsTime($from['wref'], $data['to'], $from['owner'], $owntribe, $data, 1, 't'); $bounceEnd = $database->getArtifactsValueInfluence($from['owner'], $from['wref'], 2, $bounceTime) + $AttackArrivalTime; $database->addMovement(4, $data['to'], $from['wref'], $data['ref'], $AttackArrivalTime, $bounceEnd); } $data_num++; continue; } $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] $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] $defUnits = $this->buildDefenderUnits($data['to']); $Defender = $defUnits['Defender']; $enforDefender = $defUnits['enforDefender']; $enforcementarray = $defUnits['enforcementarray']; // T4 hero port (Phase 8): cages capture animals on an // UNOCCUPIED oasis before the fight - captured animals do // not defend and are stationed at home as reinforcements. // No-op when the flag is off, no hero rides along, the // oasis is owned, or the attacker has no cages. if ($isoasis != 0 && (int) $conqureby === 0 && (int) $data['t11'] > 0) { $Defender = HeroBattleBonus::applyCages($from['owner'], (int) $data['from'], (int) $data['to'], $Defender); } // 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 <= 90; $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){ // 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 $AttackerHeroID = 0; 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']; //reinforcement report (pre-casualty defender data) — extracted to collectReinforcementReport() [#155] $reinfReport = $this->collectReinforcementReport($data, $targettribe, $Defender); $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']; 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 <= 90; $i++) $alldead[$i] = 0; //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']; $hun = $tribesPresent['hun']; $egy = $tribesPresent['egy']; $spa = $tribesPresent['spa']; $vik = $tribesPresent['vik']; $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; // sursa autoritativa: rezultatul luptei (owndead['hero'] se poate pierde // pe unele cai de executie, iar raportul afisa eroul aparator ca viu) $deadhero = (int)(isset($battlepart['deadherodef']) && $battlepart['deadherodef'] > 0 ? $battlepart['deadherodef'] : $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 <= 9; $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; //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]) // T4 hero port (Phase 5): thief sacks raise the carry // bounty when the attacker's living hero joined the strike. $heroBounty = (int) round($battlepart['bounty'] * HeroBattleBonus::raidMultiplier($from['owner'], $data['t11'])); $steal = $this->applyBounty([$avwood, $avclay, $aviron, $avcrop], $heroBounty); //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) ? rc_tok('RC_NONE_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, $hun, $egy, $spa, $vik, $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; // delete the target village if it was destroyed — extracted to handleVillageDestruction() [#155] $this->handleVillageDestruction($village_destroyed, $can_destroy, $data, $to, $varray); // remember the razed tile so any follow-up wave still queued in // this same batch bounces home instead of fighting a phantom // (now-deleted) village. Needed on top of the $to['wref'] check // because getMInfo() is cached: a same-batch wave would still see // the stale "alive" village row — see the guard right after // resolveVillageTarget() (issue #298). if ($village_destroyed == 1 && $can_destroy == 1) { $razedTargets[$data['to']] = true; } }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; // T4 hero port (Phase 5): map return-speed bonus, same as // the combat return leg above. No-op when the flag is off. $endtime = HeroBattleBonus::adjustReturnEndtime($from['owner'], $data['t11'], $AttackArrivalTime, $endtime); $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']); $data_num++; unset( $Attacker ,$Defender ,$DefenderHeroesTotArray ,$DefenderHeroesDeadArray ,$DefenderHeroesTot ,$DefenderHeroesDead ,$unitssend_att ,$unitssend_def ,$battlepart ,$unitsdead_def ,$dead ,$steal ,$from ,$data ,$data2 ,$to ,$data_fail ,$owntribe ,$unitsdead_att ,$herosend_def ,$deadhero ,$heroxp ,$AttackerID ,$DefenderID ,$totalsend_att ,$totalsend_alldef ,$totaldead_att ,$totaltraped_att ,$totaldead_def ,$totalattackdead ,$defheroxp ,$AttackerWref ,$DefenderWref ,$troopsdead1 ,$troopsdead2 ,$troopsdead3 ,$troopsdead4 ,$troopsdead5 ,$troopsdead6 ,$troopsdead7 ,$troopsdead8 ,$troopsdead9 ,$troopsdead10 ,$troopsdead11 ,$DefenderUnit ,$info_trap ,$report); } } } 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 <= 90; $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 <= 90; $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 <= 90; $i++){ $ownunit['u'.$i] += $enforce['u'.$i]; } } } $movement = $database->getVillageMovement($base); if(!empty($movement)){ for($i = 1; $i <= 90; $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; } // Production of a single resource (1=wood, 2=clay, 3=iron, 4=crop) for the // given village field layout, used by the bounty/production catch-up. The // four resources only differ by: the production-building data global, the // booster building field-type(s) whose 'attri' adds a % bonus, and the oasis // bonus slot (resourceType - 1). Crop additionally has two boosters // (grain mill type 8, then bakery type 9 applied on the running total) and // the gold-club +25% crop bonus (b4) keyed on the village owner. private function bountyGetResourceProd($resArray, $oasisNumber, $resourceType) { global $bid1, $bid2, $bid3, $bid4, $bid5, $bid6, $bid7, $bid8, $bid9, $database; $prodBid = [1 => $bid1, 2 => $bid2, 3 => $bid3, 4 => $bid4][$resourceType]; $boosterBid = [5 => $bid5, 6 => $bid6, 7 => $bid7, 8 => $bid8, 9 => $bid9]; // Booster field-types per resource, in application order (crop: mill then bakery). $boosterTypes = [1 => [5], 2 => [6], 3 => [7], 4 => [8, 9]][$resourceType]; $prod = 0; $holders = []; $boosterLevels = array_fill_keys($boosterTypes, 0); for($i = 1; $i <= 38; $i++) { if($resArray['f'.$i.'t'] == $resourceType) $holders[] = 'f'.$i; if(isset($boosterLevels[$resArray['f'.$i.'t']])) $boosterLevels[$resArray['f'.$i.'t']] = $resArray['f'.$i]; } foreach($holders as $holder) $prod += $prodBid[$resArray[$holder]]['prod']; foreach($boosterTypes as $bt) { $level = $boosterLevels[$bt]; if($level >= 1) $prod += $prod / 100 * (isset($boosterBid[$bt][$level]['attri']) ? $boosterBid[$bt][$level]['attri'] : 0); } $oasisIndex = $resourceType - 1; if($oasisNumber[$oasisIndex] > 0) $prod += $prod * 0.25 * $oasisNumber[$oasisIndex]; if($resourceType == 4 && !empty($resArray['vref']) && is_numeric($resArray['vref'])) { $who = $database->getVillageField($resArray['vref'], "owner"); $croptrue = $database->getUserField($who, "b4", 0); if($croptrue > time()) $prod *= 1.25; } return round($prod * SPEED); } }