mirror of
https://github.com/Shadowss/TravianZ.git
synced 2026-06-30 17:44:22 +00:00
f849260bfa
When several catapult waves land in the same resolution tick, sendunitsComplete() fetches them all into one in-memory batch. The first wave can raze the target village: handleVillageDestruction() -> DelVillage() already bounces every still-in-flight attack straight home (symmetric return time) and deletes the vdata row, leaving the wdata tile as an empty valley. The loop then kept resolving the remaining waves from the in-memory batch against the now-deleted village. getMInfo() LEFT-JOINs wdata onto the missing vdata, so every vdata column (including wref) comes back NULL. The return trip in finalizeReturnOrDeath() is then computed from a NULL wref (NULL coordinates), producing a bogus arrival time that strands the troops, and a duplicate of the bounce DelVillage() had already issued. The attacker sees the waves "coming to nowhere and never coming back" (issue #298). Track the tiles razed during the current batch and skip any follow-up wave that targets one of them: those troops were already bounced home by DelVillage(). No change to the battle maths; for a normal (non-razed) target the set stays empty, so behaviour is preserved. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
5328 lines
246 KiB
PHP
5328 lines
246 KiB
PHP
<?php
|
||
|
||
#################################################################################
|
||
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
|
||
## --------------------------------------------------------------------------- ##
|
||
## Project: TravianZ ##
|
||
## Version: 22.06.2015 ##
|
||
## Filename Automation.php ##
|
||
## Developed by: Mr.php , Advocaite , brainiacX , yi12345 , Shadow , ronix ##
|
||
## Fixed by: Shadow - STARVATION , HERO FIXED COMPL. ##
|
||
## Fixed by: InCube - double troops ##
|
||
## Refactor by: Shadow ##
|
||
## License: TravianZ Project ##
|
||
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
|
||
## URLs: https://travianz.org ##
|
||
## https://github.com/Shadowss/TravianZ ##
|
||
## ##
|
||
#################################################################################
|
||
|
||
// make sure we only run the automation script once and wait until it's done,
|
||
// so concurrent AJAX calls from many different users won't overload the server
|
||
if ( !defined('AUTOMATION_MANUAL_RUN') ) {
|
||
if(defined('AUTOMATION_LOCK_FILE_NAME')){
|
||
if ( file_exists( AUTOMATION_LOCK_FILE_NAME ) ) {
|
||
// check that the file is not too old, in which case our PHP script hung
|
||
// and we need to remove the lock and run automation again
|
||
$fileTime = filemtime( AUTOMATION_LOCK_FILE_NAME );
|
||
|
||
// allow for 60 seconds of old automation script processing time, which is still way too plenty
|
||
if ( ! $fileTime || time() - $fileTime > 60 ) {
|
||
@unlink( AUTOMATION_LOCK_FILE_NAME );
|
||
} else {
|
||
// automation file exists and is valid, don't run another automation
|
||
exit;
|
||
}
|
||
} else {
|
||
// create automation lock file
|
||
file_put_contents( AUTOMATION_LOCK_FILE_NAME, '' );
|
||
}
|
||
}
|
||
}
|
||
|
||
include_once("Database.php");
|
||
include_once("Data/buidata.php");
|
||
include_once("Data/unitdata.php");
|
||
include_once("Data/hero_full.php");
|
||
include_once("Units.php");
|
||
include_once("Battle.php");
|
||
include_once("Technology.php");
|
||
include_once("Ranking.php");
|
||
include_once("Generator.php");
|
||
include_once("Multisort.php");
|
||
include_once("Building.php");
|
||
include_once("Artifacts.php");
|
||
|
||
class Automation {
|
||
|
||
/**
|
||
* @var object The artifacts class, used to create Natars, artifacts and obtaining info about them
|
||
*/
|
||
|
||
private $artifacts;
|
||
|
||
/**
|
||
* Cache pentru utilizatori pentru a reduce query-urile duplicate
|
||
* @var array
|
||
*/
|
||
private $userCache = [];
|
||
|
||
public function __construct() {
|
||
|
||
//Classes initialization
|
||
$this->artifacts = new Artifacts();
|
||
|
||
$autoprefix = "";
|
||
for ($i = 0; $i < 5; $i++) {
|
||
$autoprefix = str_repeat('../', $i);
|
||
if (file_exists($autoprefix.'autoloader.php')) {
|
||
// we have our path, let's leave
|
||
break;
|
||
}
|
||
}
|
||
|
||
$this->procNewClimbers();
|
||
$this->ClearUser();
|
||
$this->ClearInactive();
|
||
$this->pruneResource();
|
||
$this->pruneOResource();
|
||
$this->checkWWAttacks();
|
||
$this->delTradeRoute();
|
||
$this->TradeRoute();
|
||
|
||
$methodsArrays = ["culturePoints", "updateHero", "clearDeleting", "buildComplete",
|
||
"demolitionComplete", "marketComplete", "researchComplete",
|
||
"trainingComplete", "starvation", "celebrationComplete", "festivalComplete",
|
||
"sendUnitsComplete", "loyaltyRegeneration", "sendreinfunitsComplete",
|
||
"returnunitsComplete", "sendSettlersComplete", "spawnNatars",
|
||
"spawnWWVillages", "spawnWWBuildingPlans", "activateArtifacts"];
|
||
|
||
foreach($methodsArrays as $method){
|
||
$file = fopen($autoprefix."GameEngine/Prevention/".$method.".txt", "w");
|
||
if(flock($file, LOCK_EX)) {
|
||
call_user_func(array($this, $method));
|
||
flock($file, LOCK_UN);
|
||
}
|
||
fclose($file);
|
||
}
|
||
|
||
$this->MasterBuilder();
|
||
$this->updateGeneralAttack();
|
||
$this->checkInvitedPlayes();
|
||
$this->updateStore();
|
||
$this->CheckBan();
|
||
$this->regenerateOasisTroops();
|
||
$this->medals();
|
||
$this->artefactOfTheFool();
|
||
}
|
||
|
||
/**
|
||
* Retrieve user data using local cache.
|
||
**/
|
||
|
||
private function getCachedUser($uid, $mode = 1) {
|
||
global $database;
|
||
$uid = (int)$uid;
|
||
if (!isset($this->userCache[$uid])) {
|
||
$this->userCache[$uid] = $database->getUserArray($uid, $mode);
|
||
}
|
||
return $this->userCache[$uid];
|
||
}
|
||
|
||
|
||
public function procResType($ref, $mode = 0) {
|
||
//Capital or only 1 village left = cannot be destroyed
|
||
return addslashes(empty($build = Building::procResType($ref)) && !$mode ? RC_VILLAGE_CANT_BE : $build);
|
||
}
|
||
|
||
function recountPop($vid, $use_cache = true){
|
||
global $database;
|
||
|
||
$vid = (int) $vid;
|
||
$fdata = $database->getResourceLevel($vid, $use_cache);
|
||
$popTot = 0;
|
||
|
||
for ($i = 1; $i <= 40; $i++) {
|
||
$lvl = $fdata["f".$i];
|
||
$building = $fdata["f".$i."t"];
|
||
if($building) $popTot += $this->buildingPOP($building, $lvl);
|
||
}
|
||
|
||
Building::recountCP($database, $vid);
|
||
$q = "UPDATE ".TB_PREFIX."vdata set pop = $popTot where wref = $vid";
|
||
mysqli_query($database->dblink, $q);
|
||
$owner = $database->getVillageField($vid, "owner");
|
||
$this->procClimbers($owner);
|
||
|
||
return $popTot;
|
||
}
|
||
|
||
function buildingPOP($f, $lvl){
|
||
$name = "bid".$f;
|
||
global $$name;
|
||
|
||
$popT = 0;
|
||
$dataarray = $$name;
|
||
|
||
for ($i = 0; $i <= $lvl; $i++) {
|
||
$popT += ((isset($dataarray[$i]) && isset($dataarray[$i]['pop'])) ? $dataarray[$i]['pop'] : 0);
|
||
}
|
||
return $popT;
|
||
}
|
||
|
||
private function loyaltyRegeneration() {
|
||
global $database;
|
||
|
||
$array = [];
|
||
$array = $database->getProfileVillages(0, 6);
|
||
if(!empty($array)) {
|
||
foreach($array as $loyalty) {
|
||
if (($t25_level = $this->getTypeLevel(25, $loyalty['wref'])) >= 1) {
|
||
$value = $t25_level;
|
||
}elseif(($t26_level = $this->getTypeLevel(26, $loyalty['wref'])) >= 1){
|
||
$value = $t26_level;
|
||
}
|
||
else $value = 0;
|
||
|
||
if($value > 0){
|
||
$newloyalty = min(100, $loyalty['loyalty'] + $value * (time() - $loyalty['lastupdate2']) / 3600);
|
||
$q = "UPDATE ".TB_PREFIX."vdata SET loyalty = $newloyalty, lastupdate2=".time()." WHERE wref = '".$loyalty['wref']."'";
|
||
$database->query($q);
|
||
}
|
||
}
|
||
}
|
||
|
||
$array = [];
|
||
$q = "SELECT conqured, loyalty, lastupdated, wref FROM ".TB_PREFIX."odata WHERE loyalty < 100";
|
||
$array = $database->query_return($q);
|
||
if(!empty($array)) {
|
||
foreach($array as $loyalty) {
|
||
$value = $this->getTypeLevel(37, $loyalty['conqured']);
|
||
|
||
if($value > 0){
|
||
$newloyalty = min(100, $loyalty['loyalty'] + $value * (time() - $loyalty['lastupdated']) / 3600);
|
||
$q = "UPDATE ".TB_PREFIX."odata SET loyalty = $newloyalty, lastupdated=".time()." WHERE wref = '".$loyalty['wref']."'";
|
||
$database->query($q);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public function getTypeLevel($tid, $vid) {
|
||
global $database;
|
||
|
||
$keyholder = [];
|
||
|
||
$resourcearray = $database->getResourceLevel($vid);
|
||
foreach(array_keys($resourcearray, $tid) as $key) {
|
||
if(strpos($key,'t')) {
|
||
$key = preg_replace("/[^0-9]/", '', $key);
|
||
array_push($keyholder, $key);
|
||
}
|
||
}
|
||
|
||
$element = count($keyholder);
|
||
if($element >= 2) {
|
||
if($tid <= 4) {
|
||
$temparray = [];
|
||
for($i = 0; $i <= $element - 1; $i++) {
|
||
array_push($temparray,$resourcearray['f'.$keyholder[$i]]);
|
||
}
|
||
foreach ($temparray as $key => $val) {
|
||
if ($val == max($temparray)) $target = $key;
|
||
}
|
||
}
|
||
else {
|
||
$target = 0;
|
||
for($i = 1; $i <= $element - 1; $i++) {
|
||
if($resourcearray['f'.$keyholder[$i]] > $resourcearray['f'.$keyholder[$target]]) {
|
||
$target = $i;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if($element == 1) $target = 0;
|
||
else return 0;
|
||
|
||
if(!empty($keyholder[$target])) return $resourcearray['f'.$keyholder[$target]];
|
||
else return 0;
|
||
}
|
||
|
||
private function clearDeleting() {
|
||
global $database;
|
||
|
||
$needDelete = $database->getNeedDelete();
|
||
if(count($needDelete) > 0) {
|
||
|
||
//Remove the time limit, otherwise deleting players with 80 or more villages couldn't be deleted in one run
|
||
@set_time_limit(0);
|
||
|
||
foreach($needDelete as $need) {
|
||
$need['uid'] = (int) $need['uid'];
|
||
|
||
//Get the villages which have to be deleted
|
||
$needVillages = $database->getVillagesID($need['uid']);
|
||
|
||
//Delete all villages
|
||
$database->DelVillage($needVillages);
|
||
|
||
for($i = 0;$i < 20; $i++){
|
||
$q = "SELECT id FROM ".TB_PREFIX."users where friend".$i." = ".$need['uid']." or friend".$i."wait = ".$need['uid']."";
|
||
$array = $database->query_return($q);
|
||
foreach($array as $friend){
|
||
$database->deleteFriend($friend['id'],"friend".$i);
|
||
$database->deleteFriend($friend['id'],"friend".$i."wait");
|
||
}
|
||
}
|
||
|
||
$database->updateUserField($need['uid'], 'alliance', 0, 1);
|
||
|
||
if($database->isAllianceOwner($need['uid'])){
|
||
$alliance = $database->getUserAllianceID($need['uid']);
|
||
$newowner = $database->getAllMember2($alliance);
|
||
$newleader = $newowner['id'];
|
||
$q = "UPDATE " . TB_PREFIX . "alidata set leader = ".(int) $newleader." where id = ".(int) $alliance."";
|
||
$database->query($q);
|
||
$database->updateAlliPermissions($newleader, $alliance, "Leader", 1, 1, 1, 1, 1, 1, 1);
|
||
Automation::updateMax($newleader);
|
||
}
|
||
|
||
if (isset($alliance)) $database->deleteAlliance($alliance);
|
||
|
||
$q = "DELETE FROM ".TB_PREFIX."hero where uid = ".$need['uid'];
|
||
$database->query($q);
|
||
|
||
$q = "DELETE FROM ".TB_PREFIX."mdata where target = ".$need['uid']." or owner = ".$need['uid'];
|
||
$database->query($q);
|
||
|
||
$q = "DELETE FROM ".TB_PREFIX."ndata where uid = ".$need['uid'];
|
||
$database->query($q);
|
||
|
||
$q = "DELETE FROM ".TB_PREFIX."users where id = ".$need['uid'];
|
||
$database->query($q);
|
||
|
||
$q = "DELETE FROM ".TB_PREFIX."deleting where uid = ".$need['uid'];
|
||
$database->query($q);
|
||
}
|
||
}
|
||
}
|
||
|
||
private function ClearUser() {
|
||
global $database;
|
||
|
||
if(AUTO_DEL_INACTIVE) {
|
||
$time = time() - UN_ACT_TIME;
|
||
|
||
$q = "INSERT INTO ".TB_PREFIX."deleting SELECT id, UNIX_TIMESTAMP() FROM ".TB_PREFIX."users WHERE timestamp < $time AND tribe IN(1, 2, 3)";
|
||
$database->query($q);
|
||
}
|
||
}
|
||
|
||
private function ClearInactive() {
|
||
global $database;
|
||
|
||
if(TRACK_USR) {
|
||
$timeout = time()-USER_TIMEOUT * 60;
|
||
$q = "DELETE FROM ".TB_PREFIX."active WHERE timestamp < $timeout";
|
||
$database->query($q);
|
||
}
|
||
}
|
||
|
||
// Clamp resources/storage up to their minimums for the given table (vdata or
|
||
// odata): negative resources back to 0 and maxstore/maxcrop back to
|
||
// STORAGE_BASE. This floor pass is identical for both tables, so both
|
||
// pruneResource() (vdata) and pruneOResource() (odata) share it. The $table
|
||
// argument is an internal constant string, never user input.
|
||
private function pruneResourceMinimums($table) {
|
||
global $database;
|
||
|
||
$database->query("UPDATE
|
||
".TB_PREFIX.$table."
|
||
SET
|
||
wood = IF(wood < 0, 0, wood),
|
||
clay = IF(clay < 0, 0, clay),
|
||
iron = IF(iron < 0, 0, iron),
|
||
crop = IF(crop < 0, 0, crop),
|
||
maxstore = IF(maxstore < ".STORAGE_BASE.", ".STORAGE_BASE.", maxstore),
|
||
maxcrop = IF(maxcrop < ".STORAGE_BASE.", ".STORAGE_BASE.", maxcrop)
|
||
WHERE
|
||
maxstore < ".STORAGE_BASE." OR
|
||
maxcrop < ".STORAGE_BASE." OR
|
||
wood < 0 OR
|
||
clay < 0 OR
|
||
iron < 0 OR
|
||
crop < 0");
|
||
}
|
||
|
||
private function pruneOResource() {
|
||
if(!ALLOW_BURST) {
|
||
$this->pruneResourceMinimums('odata');
|
||
}
|
||
}
|
||
private function pruneResource() {
|
||
global $database;
|
||
|
||
if(!ALLOW_BURST) {
|
||
$this->pruneResourceMinimums('vdata');
|
||
|
||
$database->query("UPDATE
|
||
".TB_PREFIX."vdata
|
||
SET
|
||
wood = IF(wood > maxstore, maxstore, wood),
|
||
clay = IF(clay > maxstore, maxstore, clay),
|
||
iron = IF(iron > maxstore, maxstore, iron),
|
||
crop = IF(crop > maxcrop, maxcrop, crop)
|
||
WHERE
|
||
wood > maxstore OR
|
||
clay > maxstore OR
|
||
iron > maxstore OR
|
||
crop > maxcrop");
|
||
}
|
||
}
|
||
|
||
private function culturePoints() {
|
||
global $database;
|
||
|
||
$database->updateVSumField('cp');
|
||
}
|
||
|
||
private function buildComplete() {
|
||
global $database;
|
||
|
||
$time = time();
|
||
// IDs of villages that were affected by this building completion update,
|
||
// used to calculate statistical data at the end
|
||
$villagesAffected = [];
|
||
// holds additional conditions when updating loopcon records in the bdata table
|
||
$loopconUpdates = [];
|
||
// this will hold IDs of bdata table records to delete
|
||
$dbIdsToDelete = [];
|
||
|
||
// get all pending builds that should be complete by now
|
||
$res = $database->query_return(
|
||
"SELECT
|
||
id, wid, field, level, type, timestamp
|
||
FROM
|
||
".TB_PREFIX."bdata
|
||
WHERE
|
||
timestamp < $time and master = 0"
|
||
);
|
||
|
||
// preload village data
|
||
$vilIDs = [];
|
||
foreach($res as $indi) {
|
||
$vilIDs[$indi['wid']] = true;
|
||
}
|
||
$vilIDs = array_keys($vilIDs);
|
||
$database->getProfileVillages($vilIDs, 5);
|
||
$database->getEnforceVillage($vilIDs, 0);
|
||
|
||
// complete buildings
|
||
foreach($res as $indi) {
|
||
// store village ID for later for statistical updates
|
||
$villageData = $database->getVillageFields($indi['wid'],'owner, maxcrop, maxstore, starv, pop');
|
||
$villageOwner = $villageData['owner'];
|
||
$villagesAffected[] = (int) $indi['wid'];
|
||
$fieldsToSet = [];
|
||
|
||
$q = "UPDATE ".TB_PREFIX."fdata SET f".$indi['field']." = ".$indi['level'].", f".$indi['field']."t = ".$indi['type']." WHERE vref = ".(int) $indi['wid'];
|
||
|
||
if($database->query($q)) {
|
||
// this will be the level we brought the building to now
|
||
$level = $indi['level'];
|
||
|
||
// TODO: magic numbers into constants (for building types below)
|
||
|
||
// update capacity if we updated a warehouse or a granary
|
||
if (in_array($indi['type'], [10, 11, 38, 39])) {
|
||
[$fieldDbName, $max] = $this->updateStorageCapacity($indi['type'], $level, $villageData);
|
||
$fieldsToSet[$fieldDbName] = $max;
|
||
}
|
||
|
||
// if we updated Embassy, update maximum members that the alliance can take
|
||
if($indi['type'] == 18) Automation::updateMax($villageOwner);
|
||
|
||
// World Wonder completion handling (Natar attacks, winner lock, last-upgrade time)
|
||
if ($indi['type'] == 40) $this->completeWorldWonder($indi);
|
||
|
||
// TODO: find out what exactly these conditions are for
|
||
// no special military conditioning for Teutons and Gauls
|
||
if ($database->getUserField($villageOwner, "tribe", 0) != 1) $loopconUpdates[$indi['wid']] = '';
|
||
else
|
||
{
|
||
// special condition for Roman military buildings
|
||
if ($indi['field'] > 18) $loopconUpdates[$indi['wid']] = ' AND field > 18';
|
||
else $loopconUpdates[$indi['wid']] = ' AND field < 19';
|
||
}
|
||
|
||
$dbIdsToDelete[] = (int) $indi['id'];
|
||
}
|
||
|
||
//Update starvation data
|
||
$database->addStarvationData($indi['wid']);
|
||
|
||
// update the requested fields, all at once
|
||
$database->setVillageFields($indi['wid'], array_keys($fieldsToSet), array_values($fieldsToSet));
|
||
}
|
||
|
||
// update statistical data for affected villages
|
||
foreach ($villagesAffected as $affected_id) $this->recountPop($affected_id, false);
|
||
|
||
// update data that can be done in one swoop instead of using multiple update queries
|
||
// no special checks for Romans
|
||
foreach ($loopconUpdates as $villageId => $updateCondition) {
|
||
$database->query(
|
||
"UPDATE
|
||
".TB_PREFIX."bdata
|
||
SET
|
||
loopcon = 0
|
||
WHERE
|
||
loopcon = 1 AND
|
||
master = 0 AND
|
||
wid = ".$villageId.$updateCondition);
|
||
}
|
||
|
||
// delete all processed entries
|
||
if (count($dbIdsToDelete)) {
|
||
$database->query( "DELETE FROM " . TB_PREFIX . "bdata WHERE id IN(" . implode( ',', $dbIdsToDelete ) . ")" );
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Recompute a warehouse/granary capacity after a build completion.
|
||
* Returns [$fieldDbName, $max]: the vdata column to update and its new value.
|
||
*/
|
||
private function updateStorageCapacity($type, $level, $villageData) {
|
||
global $bid10, $bid11, $bid38, $bid39;
|
||
|
||
$fieldDbName = (in_array($type, [10, 38]) ? 'maxstore' : 'maxcrop');
|
||
$max = $villageData[$fieldDbName];
|
||
|
||
if($level == 1 && $max == STORAGE_BASE) $max = STORAGE_BASE;
|
||
|
||
if ($level != 1) $max -= ${'bid'.$type}[$level - 1]['attri'] * STORAGE_MULTIPLIER;
|
||
|
||
$max += ${'bid'.$type}[$level]['attri'] * STORAGE_MULTIPLIER;
|
||
|
||
return [$fieldDbName, $max];
|
||
}
|
||
|
||
/**
|
||
* Handle the side effects of completing a World Wonder (type 40) build:
|
||
* launch the Natar attack waves, lock out further winners at level 100,
|
||
* and record the last upgrade time.
|
||
*/
|
||
private function completeWorldWonder($indi) {
|
||
global $database;
|
||
|
||
if (($indi['level'] % 5 == 0 || $indi['level'] > 95) && $indi['level'] != 100) {
|
||
$this->startNatarAttack($indi['level'], $indi['wid'], $indi['timestamp']);
|
||
}
|
||
|
||
//now can't be more than one winner if ww to level 100 is build by 2 users or more on same time
|
||
if ($indi['level'] == 100) {
|
||
mysqli_query($database->dblink,"TRUNCATE ".TB_PREFIX."bdata");
|
||
}
|
||
|
||
// Update ww last finish upgrade
|
||
$qW = "UPDATE ".TB_PREFIX."fdata set ww_lastupdate = ".time()." where vref = ".(int) $indi['wid'];
|
||
$database->query($qW);
|
||
}
|
||
|
||
private function startNatarAttack($level, $vid, $time) {
|
||
global $database;
|
||
|
||
// bad, but should work :D
|
||
// I took the data from my first ww (first .org world)
|
||
// TODO: get the algo from the real travian with the 100 biggest offs
|
||
|
||
// select the troops^^
|
||
$troops = $this->getNatarTroopTable();
|
||
if (isset($troops[$level])) $units = $troops[$level];
|
||
else return false;
|
||
|
||
// get the capital village from the natars
|
||
$query = mysqli_query($database->dblink,'SELECT `wref` FROM `' . TB_PREFIX . 'vdata` WHERE `owner` = 3 and `capital` = 1 LIMIT 1') or die(mysqli_error($database->dblink));
|
||
$row = mysqli_fetch_assoc($query);
|
||
|
||
// start the attacks
|
||
$endtime = $time + round(86400 / INCREASE_SPEED);
|
||
|
||
// -.-
|
||
$vid = (int) $vid;
|
||
mysqli_query($database->dblink,'INSERT INTO `' . TB_PREFIX . 'ww_attacks` (`vid`, `attack_time`) VALUES (' . $vid . ', ' . $endtime . ')');
|
||
mysqli_query($database->dblink,'INSERT INTO `' . TB_PREFIX . 'ww_attacks` (`vid`, `attack_time`) VALUES (' . $vid . ', ' . ($endtime + 1) . ')');
|
||
|
||
// two waves: the second one targets the WW (catapult target 40) one second later
|
||
$this->launchNatarWave($row['wref'], $vid, $units[0], 0, $time, $endtime);
|
||
$this->launchNatarWave($row['wref'], $vid, $units[1], 40, $time, $endtime + 1);
|
||
}
|
||
|
||
/**
|
||
* Fire a single Natar attack wave at a World Wonder village.
|
||
* $unitRow is one troop row [t2, t3, t5, t6, t7, t8]; $ctar1 is the
|
||
* catapult target slot (0 for the first wave, 40 for the second).
|
||
*/
|
||
private function launchNatarWave($source, $vid, $unitRow, $ctar1, $time, $arrival) {
|
||
global $database;
|
||
|
||
$ref = $database->addAttack($source, 0, $unitRow[0], $unitRow[1], 0, $unitRow[2], $unitRow[3], $unitRow[4], $unitRow[5], 0, 0, 0, 3, $ctar1, 0, 0, 0, 20, 20, 0, 20, 20, 20, 20);
|
||
$database->addMovement(3, $source, $vid, $ref, $time, $arrival);
|
||
}
|
||
|
||
/**
|
||
* Hard-coded Natar offensive waves indexed by World Wonder level.
|
||
* Each level holds two waves, each a troop row [t2, t3, t5, t6, t7, t8].
|
||
*/
|
||
private function getNatarTroopTable() {
|
||
return [5 => [[3412, 2814, 4156, 3553, 9, 0], [35, 0, 77, 33, 17, 10]],
|
||
10 => [[4314, 3688, 5265, 4621, 13, 0], [65, 0, 175, 77, 28, 17]],
|
||
15 => [[4645, 4267, 5659, 5272, 15, 0], [99, 0, 305, 134, 40, 25]],
|
||
20 => [[6207, 5881, 7625, 7225, 22, 0], [144, 0, 456, 201, 56, 36]],
|
||
25 => [[6004, 5977, 7400, 7277, 23, 0], [152, 0, 499, 220, 58, 37]],
|
||
30 => [[7073, 7181, 8730, 8713, 27, 0], [183, 0, 607, 268, 69, 45]],
|
||
35 => [[7090, 7320, 8762, 8856, 28, 0], [186, 0, 620, 278, 70, 45]],
|
||
40 => [[7852, 6967, 9606, 8667, 25, 0], [146, 0, 431, 190, 60, 37]],
|
||
45 => [[8480, 8883, 10490, 10719, 35, 0], [223, 0, 750, 331, 83, 54]],
|
||
50 => [[8522, 9038, 10551, 10883, 35, 0], [224, 0, 757, 335, 83, 54]],
|
||
55 => [[8931, 8690, 10992, 10624, 32, 0], [219, 0, 707, 312, 84, 54]],
|
||
60 => [[12138, 13013, 15040, 15642, 51, 0], [318, 0, 1079, 477, 118, 76]],
|
||
65 => [[13397, 14619, 16622, 17521, 58, 0], [345, 0, 1182, 522, 127, 83]],
|
||
70 => [[16323, 17665, 20240, 21201, 70, 0], [424, 0, 1447, 640, 157, 102]],
|
||
75 => [[20739, 22796, 25746, 27288, 91, 0], [529, 0, 1816, 803, 194, 127]],
|
||
80 => [[21857, 24180, 27147, 28914, 97, 0], [551, 0, 1898, 839, 202, 132]],
|
||
85 => [[22476, 25007, 27928, 29876, 100, 0], [560, 0, 1933, 855, 205, 134]],
|
||
90 => [[31345, 35053, 38963, 41843, 141, 0], [771, 0, 2668, 1180, 281, 184]],
|
||
95 => [[31720, 35635, 39443, 42506, 144, 0], [771, 0, 2671, 1181, 281, 184]],
|
||
96 => [[32885, 37007, 40897, 44130, 150, 0], [795, 0, 2757, 1219, 289, 190]],
|
||
97 => [[32940, 37099, 40968, 44235, 150, 0], [794, 0, 2755, 1219, 289, 190]],
|
||
98 => [[33521, 37691, 41686, 44953, 152, 0], [812, 0, 2816, 1246, 296, 194]],
|
||
99 => [[36251, 40861, 45089, 48714, 165, 0], [872, 0, 3025, 1338, 317, 208]]];
|
||
}
|
||
|
||
private function checkWWAttacks() {
|
||
global $database;
|
||
|
||
$query = mysqli_query($database->dblink,'SELECT vid, attack_time FROM `' . TB_PREFIX . 'ww_attacks` WHERE `attack_time` <= ' . time());
|
||
while ($row = mysqli_fetch_assoc($query))
|
||
{
|
||
// delete the attack
|
||
$query3 = mysqli_query($database->dblink,'DELETE FROM `' . TB_PREFIX . 'ww_attacks` WHERE `vid` = ' . (int) $row['vid'] . ' AND `attack_time` = ' . (int) $row['attack_time']);
|
||
}
|
||
}
|
||
|
||
private function getPop($tid, $level) {
|
||
$name = "bid".$tid;
|
||
global $$name;
|
||
|
||
$dataarray = $$name;
|
||
$pop = $dataarray[($level + 1)]['pop'];
|
||
$cp = $dataarray[($level + 1)]['cp'];
|
||
return [$pop, $cp];
|
||
}
|
||
|
||
private function delTradeRoute() {
|
||
global $database;
|
||
|
||
$database->delTradeRoute();
|
||
}
|
||
|
||
private function TradeRoute() {
|
||
global $database;
|
||
$time = time();
|
||
$q = "SELECT `from`, wood, clay, iron, crop, wid, deliveries, id FROM ".TB_PREFIX."route where timestamp < $time";
|
||
$dataarray = $database->query_return($q);
|
||
|
||
$vilIDs = [];
|
||
foreach($dataarray as $data) {
|
||
$vilIDs[$data['wid']] = true;
|
||
$vilIDs[$data['from']] = true;
|
||
}
|
||
$vilIDs = array_keys($vilIDs);
|
||
$database->getVillageByWorldID($vilIDs);
|
||
|
||
foreach($dataarray as $data) {
|
||
$targettribe = $database->getUserField($database->getVillageField($data['from'], "owner"), "tribe", 0);
|
||
$this->sendResource2($data['wood'], $data['clay'], $data['iron'], $data['crop'], $data['from'], $data['wid'], $targettribe, $data['deliveries']);
|
||
$database->editTradeRoute($data['id'], "timestamp", 86400, 1);
|
||
}
|
||
}
|
||
|
||
private function marketComplete() {
|
||
// Two independent phases share a single cutoff timestamp: sort_type = 0
|
||
// new trade deliveries, then sort_type = 2 resending of resources.
|
||
$time = microtime(true);
|
||
$this->marketCompleteDeliveries($time);
|
||
$this->marketCompleteResends($time);
|
||
}
|
||
|
||
private function marketCompleteDeliveries($time) {
|
||
global $database, $units;
|
||
|
||
$q = "SELECT s.wood, s.clay, s.iron, s.crop, `to`, `from`, endtime, merchant, send, moveid FROM ".TB_PREFIX."movement m, ".TB_PREFIX."send s WHERE m.ref = s.id AND m.proc = 0 AND sort_type = 0 AND endtime < $time";
|
||
$dataarray = $database->query_return($q);
|
||
|
||
foreach($dataarray as $data) {
|
||
$userData_from = $database->getUserFields($database->getVillageField($data['from'], "owner"), "alliance, tribe", 0);
|
||
$userData_to = $database->getUserFields($database->getVillageField($data['to'], "owner"), "alliance, tribe", 0);
|
||
|
||
if($data['wood'] >= $data['clay'] && $data['wood'] >= $data['iron'] && $data['wood'] >= $data['crop']) $sort_type = 10;
|
||
elseif($data['clay'] >= $data['wood'] && $data['clay'] >= $data['iron'] && $data['clay'] >= $data['crop']) $sort_type = 11;
|
||
elseif($data['iron'] >= $data['wood'] && $data['iron'] >= $data['clay'] && $data['iron'] >= $data['crop']) $sort_type = 12;
|
||
elseif($data['crop'] >= $data['wood'] && $data['crop'] >= $data['clay'] && $data['crop'] >= $data['iron']) $sort_type = 13;
|
||
|
||
$to = $database->getMInfo($data['to']);
|
||
$from = $database->getMInfo($data['from']);
|
||
|
||
$ownally = $userData_from['alliance'];
|
||
$targetally = $userData_to['alliance'];
|
||
|
||
// Report filter preferences (#198): skip merchant-transfer notices
|
||
// according to the saved checkboxes of the involved players.
|
||
// v4 -> recipient: no report for transfers to own villages
|
||
// v6 -> recipient: no report for transfers from foreign villages
|
||
// v5 -> sender: no report for transfers to foreign villages
|
||
$ownTransfer = ($from['owner'] == $to['owner']);
|
||
$skipRecipient = $ownTransfer ? !empty($userData_to['v4']) : !empty($userData_to['v6']);
|
||
if(!$skipRecipient) {
|
||
$database->addNotice($to['owner'],$to['wref'],$targetally,$sort_type,''.addslashes($from['name']).' send resources to '.addslashes($to['name']).'',''.$from['owner'].','.$from['wref'].','.$data['wood'].','.$data['clay'].','.$data['iron'].','.$data['crop'].'',$data['endtime']);
|
||
}
|
||
if(!$ownTransfer && empty($userData_from['v5'])) {
|
||
$database->addNotice($from['owner'],$to['wref'],$ownally,$sort_type,''.addslashes($from['name']).' send resources to '.addslashes($to['name']).'',''.$from['owner'].','.$from['wref'].','.$data['wood'].','.$data['clay'].','.$data['iron'].','.$data['crop'].'',$data['endtime']);
|
||
}
|
||
$database->modifyResource($data['to'],$data['wood'],$data['clay'],$data['iron'],$data['crop'],1);
|
||
$targettribe = $userData_to["tribe"];
|
||
$endtime = $units->getWalkingTroopsTime($data['from'], $data['to'], 0, 0, [$targettribe], 0) + $data['endtime'];
|
||
$database->addMovement(2, $data['to'], $data['from'], $data['merchant'], time(), $endtime, $data['send'], $data['wood'], $data['clay'], $data['iron'], $data['crop']);
|
||
$database->setMovementProc($data['moveid']);
|
||
}
|
||
}
|
||
|
||
private function marketCompleteResends($time) {
|
||
global $database;
|
||
|
||
$q1 = "SELECT send, moveid, `to`, wood, clay, iron, crop, `from` FROM ".TB_PREFIX."movement WHERE proc = 0 and sort_type = 2 and endtime < $time";
|
||
$dataarray1 = $database->query_return($q1);
|
||
|
||
$vilIDs = [];
|
||
foreach($dataarray1 as $data1) {
|
||
$vilIDs[$data1['to']] = true;
|
||
$vilIDs[$data1['from']] = true;
|
||
}
|
||
$vilIDs = array_keys($vilIDs);
|
||
$database->getVillageByWorldID($vilIDs);
|
||
|
||
foreach($dataarray1 as $data1) {
|
||
$database->setMovementProc($data1['moveid']);
|
||
if($data1['send'] > 1){
|
||
$targettribe1 = $database->getUserFields($database->getVillageField($data1['to'],"owner"),"alliance, tribe",0)['tribe'];
|
||
$send = $data1['send']-1;
|
||
$this->sendResource2($data1['wood'],$data1['clay'],$data1['iron'],$data1['crop'],$data1['to'],$data1['from'],$targettribe1,$send);
|
||
}
|
||
}
|
||
}
|
||
|
||
private function sendResource2($wtrans, $ctrans, $itrans, $crtrans, $from, $to, $tribe, $send) {
|
||
global $bid17, $bid28, $database, $units;
|
||
|
||
$availableWood = $database->getWoodAvailable($from);
|
||
$availableClay = $database->getClayAvailable($from);
|
||
$availableIron = $database->getIronAvailable($from);
|
||
$availableCrop = $database->getCropAvailable($from);
|
||
|
||
if($availableWood + $availableClay + $availableIron + $availableCrop > 0)
|
||
{
|
||
if($availableWood < $wtrans) $wtrans = $availableWood;
|
||
if($availableClay < $ctrans) $ctrans = $availableClay;
|
||
if($availableIron < $itrans) $itrans = $availableIron;
|
||
if($availableCrop < $crtrans) $crtrans = $availableCrop;
|
||
|
||
$merchant2 = ($this->getTypeLevel(17, $from) > 0)? $this->getTypeLevel(17, $from) : 0;
|
||
$used2 = $database->totalMerchantUsed($from, false);
|
||
$merchantAvail2 = $merchant2 - $used2;
|
||
$maxcarry2 = ($tribe == 1)? 500 : (($tribe == 2)? 1000 : 750);
|
||
$maxcarry2 *= TRADER_CAPACITY;
|
||
|
||
if($this->getTypeLevel(28, $from) != 0) {
|
||
$maxcarry2 *= $bid28[$this->getTypeLevel(28, $from)]['attri'] / 100;
|
||
}
|
||
|
||
$resource = [$wtrans, $ctrans, $itrans, $crtrans];
|
||
$reqMerc = ceil((array_sum($resource) - 0.1) / $maxcarry2);
|
||
|
||
if($merchantAvail2 > 0 && $reqMerc <= $merchantAvail2) {
|
||
if($database->getVillageState($to)) {
|
||
$timetaken = $units->getWalkingTroopsTime($from, $to, 0, 0, [$tribe], 0);
|
||
$res = $resource[0] + $resource[1] + $resource[2] + $resource[3];
|
||
if($res > 0){
|
||
$reference = $database->sendResource($resource[0], $resource[1], $resource[2], $resource[3], $reqMerc, 0);
|
||
$database->modifyResource($from, $resource[0], $resource[1], $resource[2], $resource[3], 0);
|
||
$database->addMovement(0, $from, $to, $reference, microtime(true), microtime(true) + $timetaken, $send);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private function resolveCatapultsDestruction(&$bdo, &$battlepart, &$info_cat, &$data, $catapultTarget, $twoRowsCatapultSetup, $isSecondRow, $catp_pic, $can_destroy, $isoasis, &$village_destroyed, $tribe) {
|
||
global $battle, $database, $bid34;
|
||
|
||
if(isset($catapultTarget))
|
||
{
|
||
//Currently targeted building/field level
|
||
$tblevel = (int) $bdo['f'.$catapultTarget];
|
||
//Currently targetet building/field GID (ID of the building/field type - woodcutter, cropland, embassy...)
|
||
$tbgid = (int) $bdo['f'.$catapultTarget.'t'];
|
||
//Currently targeted building/field ID in the database (fdata, the fID field, e.g. f1, f2, f3...)
|
||
$tbid = (int) $catapultTarget;
|
||
|
||
//If we're targeting the WW
|
||
if($catapultTarget == 40){
|
||
$battlepart['catapults']['strongerBuildings'] = 1;
|
||
$battlepart['catapults']['moraleBonus'] = 1;
|
||
}
|
||
|
||
$catapultsDamage = $battle->calculateCatapultsDamage($data['t8'],
|
||
$battlepart['catapults']['upgrades'],
|
||
$battlepart['catapults']['durability'],
|
||
$battlepart['catapults']['attackDefenseRatio'],
|
||
$battlepart['catapults']['strongerBuildings'],
|
||
$battlepart['catapults']['moraleBonus']);
|
||
|
||
$newLevel = $battle->calculateNewBuildingLevel($tblevel, $catapultsDamage / ($twoRowsCatapultSetup ? 2 : 1));
|
||
|
||
//fix: Only modify build queue if actual damage was dealt
|
||
if ($newLevel < $tblevel) {
|
||
$database->modifyBData($data['to'], $tbid, [$newLevel, $tblevel], $tribe);
|
||
}
|
||
|
||
// building/field destroyed
|
||
if ($newLevel == 0){
|
||
// prepare data to be updated
|
||
$fieldsToSet = ["f".$tbid];
|
||
$fieldValuesToSet = [0];
|
||
|
||
// update $bdo, so we don't have to reselect later
|
||
$bdo['f'.$catapultTarget] = 0;
|
||
|
||
if ($tbid >= 19 && $tbid != 99) {
|
||
$fieldsToSet[] = "f".$tbid."t";
|
||
$fieldValuesToSet[] = 0;
|
||
$bdo['f'.$catapultTarget."t"] = 0;
|
||
}
|
||
|
||
// update all that needs updating
|
||
$database->setVillageLevel($data['to'], $fieldsToSet, $fieldValuesToSet);
|
||
|
||
$buildarray = $GLOBALS["bid".$tbgid];
|
||
|
||
if ( isset( $buildarray[$newLevel] ) ) {
|
||
// (great) warehouse level was changed
|
||
if ($tbgid == 10 || $tbgid == 38) {
|
||
$database->setMaxStoreForVillage($data['to'], $buildarray[$newLevel]['attri']);
|
||
}
|
||
|
||
// (great) granary level was changed
|
||
if ($tbgid == 11 || $tbgid == 39) {
|
||
$database->setMaxCropForVillage($data['to'], $buildarray[$newLevel]['attri']);
|
||
}
|
||
}
|
||
|
||
// oasis cannot be destroyed
|
||
$pop = $this->recountPop($data['to'], false);
|
||
if ($isoasis == 0 && $pop == 0 && $can_destroy == 1) $village_destroyed = 1;
|
||
|
||
if ($isSecondRow) {
|
||
if ($tbid > 0) {
|
||
$info_cat .= "<tbody class=\"goods\"><tr><th>".INFORMATION."</th><td colspan=\"11\">
|
||
<img class=\"unit u".$catp_pic."\" src=\"img/x.gif\" alt=\"".rc_tok('RC_CATAPULT')."\" title=\"".rc_tok('RC_CATAPULT')."\" /> ".rc_bld($tbgid, $can_destroy)." <b>".rc_tok('RC_DESTROYED')."</b>.";
|
||
}
|
||
|
||
// embassy level was changed
|
||
if ($tbgid == 18){
|
||
$info_cat .= $database->checkEmbassiesAfterBattle($data['to'], $bdo['f'.$catapultTarget], false);
|
||
}
|
||
|
||
$info_cat .= "</td></tr></tbody>";
|
||
} else {
|
||
$info_cat = "".$catp_pic.", ".rc_bld($tbgid, $can_destroy)." <b>".rc_tok('RC_DESTROYED')."</b>.";
|
||
|
||
// 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 .= "<tbody class=\"goods\"><tr><th>".INFORMATION."</th><td colspan=\"11\">
|
||
<img class=\"unit u".$catp_pic."\" src=\"img/x.gif\" alt=\"".rc_tok('RC_CATAPULT')."\" title=\"".rc_tok('RC_CATAPULT')."\" /> ".rc_bld($tbgid, $can_destroy)." ".rc_tok('RC_NOT_DAMAGED')."</td></tr></tbody>";
|
||
}
|
||
} 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 .= "<tbody class=\"goods\"><tr><th>".INFORMATION."</th><td colspan=\"11\">
|
||
<img class=\"unit u".$catp_pic."\" src=\"img/x.gif\" alt=\"".rc_tok('RC_CATAPULT')."\" title=\"".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 .= "</td></tr></tbody>";
|
||
} 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 .= "<tbody class=\"goods\"><tr><th>".INFORMATION."</th><td colspan=\"11\">
|
||
<img class=\"unit u".$catp_pic."\" src=\"img/x.gif\" alt=\"".rc_tok('RC_CATAPULT')."\" title=\"".rc_tok('RC_CATAPULT')."\" /> ".rc_tok('RC_NO_BUILDINGS').".</td></tr></tbody>";
|
||
}
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
if ($palacelevel['f' . $plevel . 't'] == 26) {
|
||
if ($palacelevel['f' . $plevel] < 10) $canconquer = 0;
|
||
elseif ($palacelevel['f' . $plevel] < 15) $canconquer = 1;
|
||
elseif ($palacelevel['f' . $plevel] < 20) $canconquer = 2;
|
||
else $canconquer = 3;
|
||
} elseif ($palacelevel['f' . $plevel . 't'] == 25) {
|
||
if ($palacelevel['f' . $plevel] < 10) $canconquer = 0;
|
||
elseif ($palacelevel['f' . $plevel] < 20) $canconquer = 1;
|
||
else $canconquer = 2;
|
||
} else {
|
||
$canconquer = 0;
|
||
}
|
||
|
||
$expArray = $database->getVillageFields($from['wref'], 'exp1, exp2, exp3');
|
||
$villexp = ($expArray['exp1'] == 0) ? 0 : (($expArray['exp2'] == 0) ? 1 : (($expArray['exp3'] == 0) ? 2 : 3));
|
||
|
||
$mode = CP;
|
||
$cp_mode = $GLOBALS['cp' . $mode];
|
||
$need_cps = $cp_mode[count($varray1) + 1];
|
||
$user_cps = $database->getUserArray($from['owner'], 1)['cp'];
|
||
|
||
if ($user_cps < $need_cps) {
|
||
$info_chief = $chief_pic . ',' . 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')) {
|
||
$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')]);
|
||
|
||
$database->query("DELETE FROM " . TB_PREFIX . "abdata WHERE vref = " . (int)$data['to']);
|
||
$database->addABTech($data['to']);
|
||
|
||
$database->query("DELETE FROM " . TB_PREFIX . "tdata WHERE vref = " . (int)$data['to']);
|
||
$database->addTech($data['to']);
|
||
|
||
$database->query("DELETE FROM " . TB_PREFIX . "enforcement WHERE `from` = " . (int)$data['to']);
|
||
$database->query("DELETE FROM " . TB_PREFIX . "route where wid = " . (int)$data['to'] . " OR `from` = " . (int)$data['to']);
|
||
|
||
$units2reset = [];
|
||
for ($u = 1; $u <= 50; $u++) $units2reset[] = 'u' . $u . ' = 0';
|
||
$units2reset[] = 'u99 = 0';
|
||
$units2reset[] = 'u99o = 0';
|
||
$units2reset[] = 'hero = 0';
|
||
$database->query("UPDATE " . TB_PREFIX . "units SET " . implode(',', $units2reset) . " WHERE vref = " . (int)$data['to']);
|
||
|
||
$newLevels_fieldNames = [];
|
||
$newLevels_fieldValues = [];
|
||
|
||
$AttackerID = $database->getVillageField($data['from'], 'owner');
|
||
$DefenderID = $database->getVillageField($data['to'], 'owner');
|
||
$poparray = $database->getVSumField([$AttackerID, $DefenderID], 'pop');
|
||
$pop1 = $poparray[0]['Total'];
|
||
$pop2 = $poparray[1]['Total'];
|
||
|
||
if ($pop1 > $pop2 && $targettribe != 5) {
|
||
$buildlevel = $database->getResourceLevel($data['to']);
|
||
for ($i = 1; $i <= 39; $i++) {
|
||
if ($buildlevel['f' . $i] != 0) {
|
||
if ($buildlevel['f' . $i . 't'] != 35 && $buildlevel['f' . $i . 't'] != 36 && $buildlevel['f' . $i . 't'] != 41) {
|
||
$leveldown = $buildlevel['f' . $i] - 1;
|
||
$newLevels_fieldNames[] = 'f' . $i;
|
||
$newLevels_fieldValues[] = $leveldown;
|
||
if (!$leveldown > 0) {
|
||
$newLevels_fieldNames[] = 'f' . $i . 't';
|
||
$newLevels_fieldValues[] = 0;
|
||
}
|
||
} else {
|
||
$newLevels_fieldNames[] = 'f' . $i;
|
||
$newLevels_fieldValues[] = 0;
|
||
$newLevels_fieldNames[] = 'f' . $i . 't';
|
||
$newLevels_fieldValues[] = 0;
|
||
}
|
||
}
|
||
}
|
||
if ($buildlevel['f99'] != 0) {
|
||
$newLevels_fieldNames[] = 'f99';
|
||
$newLevels_fieldValues[] = $buildlevel['f99'] - 1;
|
||
}
|
||
}
|
||
|
||
// destroy wall
|
||
$newLevels_fieldNames[] = 'f40';
|
||
$newLevels_fieldValues[] = 0;
|
||
$newLevels_fieldNames[] = 'f40t';
|
||
$newLevels_fieldValues[] = 0;
|
||
|
||
$database->clearExpansionSlot($data['to'], 1);
|
||
|
||
$expArray2 = $database->getVillageFields($data['from'], 'exp1, exp2, exp3');
|
||
if ($expArray2['exp1'] == 0) { $exp = 'exp1'; }
|
||
elseif ($expArray2['exp2'] == 0) { $exp = 'exp2'; }
|
||
else { $exp = 'exp3'; }
|
||
$database->setVillageField($data['from'], $exp, $data['to']);
|
||
|
||
$database->deleteTradeRoutesByVillage($data['to']);
|
||
$database->setVillageLevel($data['to'], $newLevels_fieldNames, $newLevels_fieldValues);
|
||
|
||
$units->returnTroops($data['to'], 1);
|
||
|
||
$chiefing_village = 1;
|
||
$database->reassignHero($data['to']);
|
||
$this->recountPop($data['to'], false);
|
||
|
||
return ['info_chief' => $info_chief, 'chiefing_village' => $chiefing_village, 'village_destroyed' => $village_destroyed];
|
||
}
|
||
|
||
/**
|
||
* Fetch all attacks (sort_type 3, not reinforcement) that have arrived by
|
||
* $time, joined with their attack rows, ordered by arrival.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param int $time Current timestamp.
|
||
* @return array Rows of pending/completed attacks.
|
||
*/
|
||
private function fetchCompletedAttacks($time) {
|
||
global $database;
|
||
|
||
$time = (int) $time;
|
||
$q = "
|
||
SELECT
|
||
`from`, `to`, endtime, ref, ctar1, ctar2, spy, moveid, attack_type,
|
||
t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, (SELECT oasistype FROM ".TB_PREFIX."wdata WHERE id = `to`) as oasistype
|
||
FROM
|
||
".TB_PREFIX."movement,
|
||
".TB_PREFIX."attacks
|
||
WHERE
|
||
".TB_PREFIX."movement.ref = ".TB_PREFIX."attacks.id
|
||
AND
|
||
".TB_PREFIX."movement.proc = 0
|
||
AND
|
||
".TB_PREFIX."movement.sort_type = 3
|
||
AND
|
||
".TB_PREFIX."attacks.attack_type != 2
|
||
AND
|
||
endtime < $time
|
||
ORDER BY endtime ASC";
|
||
return $database->query_return($q);
|
||
}
|
||
|
||
/**
|
||
* Batch-preload the village / unit / tech data used by sendunitsComplete()
|
||
* so the per-attack loop hits the in-request cache instead of querying row
|
||
* by row. Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $dataarray Completed attacks (each with 'from' and 'to').
|
||
*/
|
||
private function preloadBattleData(array $dataarray) {
|
||
global $database;
|
||
|
||
$vilIDs = [];
|
||
foreach ($dataarray as $data) {
|
||
$vilIDs[$data['from']] = true;
|
||
$vilIDs[$data['to']] = true;
|
||
}
|
||
$vilIDs = array_keys($vilIDs);
|
||
|
||
$database->getProfileVillages($vilIDs, 5);
|
||
$database->getUnit($vilIDs);
|
||
$database->getEnforceVillage($vilIDs, 0);
|
||
$database->getMovement(34, $vilIDs, 1);
|
||
$database->getABTech($vilIDs);
|
||
$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.",<div class=\"res\"><img class=\"r1\" src=\"img/x.gif\" alt=\"".LUMBER."\" title=\"".LUMBER."\" />".round($totwood)." |
|
||
<img class=\"r2\" src=\"img/x.gif\" alt=\"".CLAY."\" title=\"".CLAY."\" />".round($totclay)." |
|
||
<img class=\"r3\" src=\"img/x.gif\" alt=\"".IRON."\" title=\"".IRON."\" />".round($totiron)." |
|
||
<img class=\"r4\" src=\"img/x.gif\" alt=\"".CROP."\" title=\"".CROP."\" />".round($totcrop)."</div>
|
||
<div class=\"carry\"><img class=\"car\" src=\"img/x.gif\" alt=\"".rc_tok('CARRY')."\" title=\"".rc_tok('CARRY')."\" />".rc_tok('RC_TOTAL_RESOURCES')." ".round($totwood+$totclay+$totiron+$totcrop)."</div>
|
||
";
|
||
}else if($data['spy'] == 2){
|
||
if ($isoasis == 0){
|
||
$walllevel = $database->getFieldLevelInVillage($data['to'], '31, 32, 33');
|
||
$residencelevel = $database->getFieldLevelInVillage($data['to'], 25);
|
||
$palacelevel = $database->getFieldLevelInVillage($data['to'], 26);
|
||
$residenceimg = "<img src=\"".GP_LOCATE."img/g/g25.gif\" height=\"20\" width=\"15\" alt=\"".RESIDENCE."\" title=\"".RESIDENCE."\" />";
|
||
$palaceimg = "<img src=\"".GP_LOCATE."img/g/g26.gif\" height=\"20\" width=\"15\" alt=\"".PALACE."\" title=\"".PALACE."\" />";
|
||
$crannyimg = "<img src=\"".GP_LOCATE."img/g/g23.gif\" height=\"20\" width=\"15\" alt=\"".CRANNY."\" title=\"".CRANNY."\" />";
|
||
$wallimg = "<img src=\"".GP_LOCATE."img/g/g3".$targettribe."Icon.gif\" height=\"20\" width=\"15\" alt=\"".rc_tok('RC_WALL')."\" title=\"".rc_tok('RC_WALL')."\" />";
|
||
$info_spy = "".$spy_pic.",";
|
||
if($residencelevel > 0) $info_spy .= $residenceimg." ".rc_tok('RC_RESIDENCE_LEVEL')."<b>".$residencelevel."</b><br />";
|
||
elseif($palacelevel > 0) $info_spy .= $palaceimg." ".rc_tok('RC_PALACE_LEVEL')." <b>".$palacelevel."</b><br />";
|
||
|
||
if($walllevel > 0) $info_spy .= $wallimg." ".rc_tok('RC_WALL_LEVEL')." <b>".$walllevel."</b><br />";
|
||
$info_spy .= $crannyimg." ".rc_tok('RC_CRANNY_CAPACITY')." <b>".$crannySpy."</b>";
|
||
}
|
||
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 = "<img src=\"".GP_LOCATE."img/u/98.gif\" alt=\"".rc_tok('RC_TRAP')."\" title=\"".rc_tok('RC_TRAP')."\" />";
|
||
$p_username = $database->getUserField($from['owner'], "username", 0);
|
||
|
||
if ($mytroops > 0 && $anothertroops > 0) {
|
||
return $trapper_pic." <b>".$p_username."</b> ".rc_tok('RC_FREED_FROM_HIS_TROOPS', $mytroops).$ownDeadsText." ".rc_tok('RC_AND_FRIENDLY_TROOPS', $anothertroops).$anotherDeadsText.".";
|
||
} elseif ($mytroops > 0) {
|
||
return $trapper_pic." <b>".$p_username."</b> ".rc_tok('RC_FREED_FROM_HIS_TROOPS', $mytroops).$ownDeadsText.".";
|
||
} elseif ($anothertroops > 0) {
|
||
return $trapper_pic." <b>".$p_username."</b> ".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,
|
||
$DefenderHeroesTot, $DefenderHeroesDead,
|
||
$info_ram, $info_cat, $info_chief, $info_spy,
|
||
$info_trap, $info_hero, $info_troop,
|
||
$data, $dead11, $herosend_def, $deadhero, $unitstraped_att,
|
||
$village_destroyed, $can_destroy, $catp_pic
|
||
) {
|
||
if (!empty($scout)) {
|
||
$data2 = ''.$from['owner'].','.$from['wref'].','.$owntribe.','.$unitssend_att.','.$unitsdead_att
|
||
.',0,0,0,0,0,'.$to['owner'].','.$to['wref'].','.addslashes($to['name'])
|
||
.',,,,'.$targettribe
|
||
.','.$unitssend_def[0].','.$unitsdead_def[0].','.$rom
|
||
.','.$unitssend_def[1].','.$unitsdead_def[1].','.$ger
|
||
.','.$unitssend_def[2].','.$unitsdead_def[2].','.$gal
|
||
.','.$unitssend_def[3].','.$unitsdead_def[3].','.$nat
|
||
.','.$unitssend_def[4].','.$unitsdead_def[4].','.$natar
|
||
.','.$unitssend_def[5].','.$unitsdead_def[5]
|
||
.','.$DefenderHeroesTot.','.$DefenderHeroesDead
|
||
.','.$info_ram.','.$info_cat.','.$info_chief.','.$info_spy
|
||
.','.$data['t11'].','.$dead11.','.$herosend_def.','.$deadhero
|
||
.',,'.$unitstraped_att;
|
||
} else {
|
||
if (isset($village_destroyed) && $village_destroyed == 1 && $can_destroy == 1) {
|
||
if (strpos($info_cat, rc_tok('RC_VILLAGE_DESTROYED')) === false) {
|
||
$info_cat .= "<tbody class=\"goods\"><tr><th>".INFORMATION."</th><td colspan=\"11\">"
|
||
. "<img class=\"unit u".$catp_pic."\" src=\"img/x.gif\" alt=\"".rc_tok('RC_CATAPULT')."\" title=\"".rc_tok('RC_CATAPULT')."\" />"
|
||
. " ".rc_tok('RC_VILLAGE_DESTROYED')."</td></tr></tbody>";
|
||
}
|
||
}
|
||
$data2 = ''.$from['owner'].','.$from['wref'].','.$owntribe.','.$unitssend_att.','.$unitsdead_att
|
||
.','.$steal[0].','.$steal[1].','.$steal[2].','.$steal[3].','.$battlepart['bounty']
|
||
.','.$to['owner'].','.$to['wref'].','.addslashes($to['name'])
|
||
.',,,,'.$targettribe
|
||
.','.$unitssend_def[0].','.$unitsdead_def[0].','.$rom
|
||
.','.$unitssend_def[1].','.$unitsdead_def[1].','.$ger
|
||
.','.$unitssend_def[2].','.$unitsdead_def[2].','.$gal
|
||
.','.$unitssend_def[3].','.$unitsdead_def[3].','.$nat
|
||
.','.$unitssend_def[4].','.$unitsdead_def[4].','.$natar
|
||
.','.$unitssend_def[5].','.$unitsdead_def[5]
|
||
.','.$DefenderHeroesTot.','.$DefenderHeroesDead
|
||
.','.$info_ram.','.$info_cat.','.$info_chief.','.(isset($info_spy) ? $info_spy : '')
|
||
.',,'.$data['t11'].','.$dead11.','.$herosend_def.','.$deadhero
|
||
.','.$unitstraped_att;
|
||
|
||
$data2 .= ','.($info_trap !== '' ? addslashes($info_trap) : '').',,'.$info_troop.','.$info_hero;
|
||
}
|
||
|
||
$data_fail = ''.$from['owner'].','.$from['wref'].','.$owntribe.','.$unitssend_att.','.$unitsdead_att
|
||
.','.$steal[0].','.$steal[1].','.$steal[2].','.$steal[3].','.$battlepart['bounty']
|
||
.','.$to['owner'].','.$to['wref'].','.addslashes($to['name'])
|
||
.',,,,'.$targettribe
|
||
.','.$unitssend_deff[0].','.$unitsdead_deff[0].','.$rom
|
||
.','.$unitssend_deff[1].','.$unitsdead_deff[1].','.$ger
|
||
.','.$unitssend_deff[2].','.$unitsdead_deff[2].','.$gal
|
||
.','.$unitssend_deff[3].','.$unitsdead_deff[3].','.$nat
|
||
.','.$unitssend_deff[4].','.$unitsdead_deff[4].','.$natar
|
||
.','.$unitssend_deff[5].','.$unitsdead_deff[5]
|
||
.','.$DefenderHeroesTot.','.$DefenderHeroesDead
|
||
.',,,'.$data['t11'].','.$dead11.','.$unitstraped_att
|
||
.',,'.$info_ram.','.$info_cat.','.$info_chief.','.$info_troop.','.$info_hero;
|
||
|
||
return ['data2' => $data2, 'data_fail' => $data_fail];
|
||
}
|
||
|
||
/**
|
||
* Distribute the battle bounty across the resources actually available in
|
||
* the target (after cranny protection) and return how much of each is taken.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $available [wood, clay, iron, crop] lootable amounts.
|
||
* @param int $bounty Total carry capacity / bounty from the battle.
|
||
* @return array [wood, clay, iron, crop] amounts actually stolen.
|
||
*/
|
||
private function applyBounty(array $available, $bounty) {
|
||
$avtotal = $available;
|
||
$steal = [0, 0, 0, 0];
|
||
$btotal = $bounty;
|
||
$bmod = 0;
|
||
|
||
for ($i = 0; $i < 5; $i++) {
|
||
for ($j = 0; $j < 4; $j++) {
|
||
if (isset($avtotal[$j])) {
|
||
if ($avtotal[$j] < 1) unset($avtotal[$j]);
|
||
}
|
||
}
|
||
|
||
// No resources left to take
|
||
if (empty($avtotal) || ($btotal < 1 && $bmod < 1)) break;
|
||
|
||
if ($btotal < 1) {
|
||
while ($bmod) {
|
||
// random select
|
||
$rs = array_rand($avtotal);
|
||
if (isset($avtotal[$rs])) {
|
||
$avtotal[$rs] -= 1;
|
||
$steal[$rs] += 1;
|
||
$bmod -= 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
// handle unbalanced amounts.
|
||
$btotal += $bmod;
|
||
$bmod = $btotal % count($avtotal);
|
||
$btotal -= $bmod;
|
||
$bsplit = $btotal / count($avtotal);
|
||
|
||
$max_steal = (min($avtotal) < $bsplit) ? min($avtotal) : $bsplit;
|
||
|
||
for ($j = 0; $j < 4; $j++) {
|
||
if (isset($avtotal[$j])) {
|
||
$avtotal[$j] -= $max_steal;
|
||
$steal[$j] += $max_steal;
|
||
$btotal -= $max_steal;
|
||
}
|
||
}
|
||
}
|
||
|
||
return $steal;
|
||
}
|
||
|
||
/**
|
||
* Apply battle casualties to the defender's own (in-village) troops, persist
|
||
* the losses, and return the per-unit dead map used later for points/reports.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $data Current attack row (uses 'to').
|
||
* @param int $targettribe Defender tribe (1-5).
|
||
* @param array $battlepart Battle result (index 2 = defender kill ratio,
|
||
* 'deadherodef' = defender hero losses).
|
||
* @return array Map of dead own troops: keys are the unit index (int) plus 'hero'.
|
||
*/
|
||
private function applyOwnDefenceCasualties($data, $targettribe, $battlepart) {
|
||
global $database;
|
||
|
||
$owndead = [];
|
||
$unitlist = $database->getUnit($data['to'], false);
|
||
$start = ($targettribe - 1) * 10 + 1;
|
||
$end = ($targettribe * 10);
|
||
|
||
$unitModifications_units = [];
|
||
$unitModifications_amounts = [];
|
||
$unitModifications_modes = [];
|
||
for ($i = $start; $i <= $end; $i++) {
|
||
if ($unitlist) {
|
||
$owndead[$i] = round($battlepart[2] * $unitlist['u'.$i]);
|
||
$unitModifications_units[] = $i;
|
||
$unitModifications_amounts[] = $owndead[$i];
|
||
$unitModifications_modes[] = 0;
|
||
}
|
||
}
|
||
|
||
$owndead['hero'] = 0;
|
||
|
||
if ($unitlist) {
|
||
$owndead['hero'] = (isset($battlepart['deadherodef']) ? $battlepart['deadherodef'] : '');
|
||
|
||
$unitModifications_units[] = 'hero';
|
||
$unitModifications_amounts[] = $owndead['hero'];
|
||
$unitModifications_modes[] = 0;
|
||
}
|
||
|
||
// modify units in DB
|
||
$database->modifyUnit($data['to'], $unitModifications_units, $unitModifications_amounts, $unitModifications_modes);
|
||
|
||
return $owndead;
|
||
}
|
||
|
||
/**
|
||
* Apply battle casualties to the reinforcement (foreign) troops stationed in
|
||
* the defender's village: compute each reinforcement's dead units/hero, persist
|
||
* them (modifyEnforce), notify the reinforcing players, and delete fully-wiped
|
||
* reinforcements. Accumulates the dead counts into $alldead and the dead heroes
|
||
* into $DefenderHeroesDeadArray (both passed by reference), and reports which
|
||
* tribes are present among the reinforcements.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $data Current attack row (uses 'to').
|
||
* @param array $battlepart Battle result (index 2 = defender kill ratio,
|
||
* 'deadheroref' = dead hero per enforce id).
|
||
* @param array $from Attacker village info (uses 'wref').
|
||
* @param array $to Defender village info (uses 'name','wref').
|
||
* @param int $ownally Attacker's alliance id (for notices).
|
||
* @param int $AttackArrivalTime Timestamp stamped on the reinforcement notices.
|
||
* @param mixed $scout Scout marker; notices/deletion only fire when empty.
|
||
* @param array $alldead [in/out] Per-unit dead reinforcements (+ 'hero'), accumulated.
|
||
* @param array $DefenderHeroesDeadArray [in/out] Dead reinforcement heroes per tribe, accumulated.
|
||
* @return array Tribe-presence flags among the reinforcements: keys rom,ger,gal,nat,natar (0/1).
|
||
*/
|
||
private function applyReinforcementCasualties($data, $battlepart, $from, $to, $ownally, $AttackArrivalTime, $scout, array &$alldead, array &$DefenderHeroesDeadArray) {
|
||
global $database;
|
||
|
||
$rom = $ger = $gal = $nat = $natar = 0;
|
||
|
||
//kill other defence in village
|
||
// ... once again, units could have changed, so we need to reselect
|
||
$enforcementarray3 = $database->getEnforceVillage($data['to'], 0);
|
||
foreach ($enforcementarray3 as $enforce) {
|
||
$life = ''; $notlife = ''; $wrong = false;
|
||
if ($enforce['from'] != 0) {
|
||
$tribe = $database->getUserArray($database->getVillageField($enforce['from'], "owner"), 1)["tribe"];
|
||
} else {
|
||
$tribe = 4;
|
||
}
|
||
|
||
$start = ($tribe - 1) * 10 + 1;
|
||
$end = ($tribe * 10);
|
||
unset($dead);
|
||
|
||
switch ($tribe) {
|
||
case 1: $rom = 1; break;
|
||
case 2: $ger = 1; break;
|
||
case 3: $gal = 1; break;
|
||
case 4: $nat = 1; break;
|
||
case 5:
|
||
default: $natar = 1; break;
|
||
}
|
||
|
||
$enforceModificationsById = [];
|
||
for ($i = $start; $i <= $end; $i++) {
|
||
if ($enforce['u'.$i] > '0') {
|
||
if (!isset($enforceModificationsById[$enforce['id']])) {
|
||
$enforceModificationsById[$enforce['id']] = [
|
||
'units' => [],
|
||
'amounts' => [],
|
||
'modes' => []
|
||
];
|
||
}
|
||
$enforceModificationsById[$enforce['id']]['units'][] = $i;
|
||
$enforceModificationsById[$enforce['id']]['amounts'][] = (isset($battlepart[2]) ? round($battlepart[2]*$enforce['u'.$i]) : 0);
|
||
$enforceModificationsById[$enforce['id']]['modes'][] = 0;
|
||
$dead[$i] = (isset($battlepart[2]) ? round($battlepart[2]*$enforce['u'.$i]) : 0);
|
||
$checkpoint = (isset($battlepart[2]) ? round($battlepart[2]*$enforce['u'.$i]) : 0);
|
||
|
||
if (!isset($enforce['u'.$i])) $enforce['u'.$i] = 0;
|
||
|
||
$alldead[$i] += $dead[$i];
|
||
|
||
$wrong = $checkpoint != $enforce['u'.$i];
|
||
} else {
|
||
$dead[$i] = 0;
|
||
}
|
||
}
|
||
|
||
if ($enforce['hero'] > 0) {
|
||
$enforceModificationsById[$enforce['id']]['units'][] = 'hero';
|
||
$enforceModificationsById[$enforce['id']]['amounts'][] = $battlepart['deadheroref'][$enforce['id']];
|
||
$enforceModificationsById[$enforce['id']]['modes'][] = 0;
|
||
|
||
$dead['hero'] = $battlepart['deadheroref'][$enforce['id']];
|
||
$alldead['hero'] += $dead['hero'];
|
||
$wrong = $dead['hero'] != $enforce['hero'];
|
||
|
||
//Collecting information for the report
|
||
$reinfTribe = ($enforce['from'] == 0) ? 4 : $database->getUserField($database->getVillageField($enforce['from'], "owner"), "tribe", 0);
|
||
$DefenderHeroesDeadArray[$reinfTribe] += $dead['hero'];
|
||
}
|
||
|
||
// modify enforce in DB
|
||
foreach ($enforceModificationsById as $enforceId => $enforceArray) {
|
||
$database->modifyEnforce($enforceId, $enforceArray['units'], $enforceArray['amounts'], $enforceArray['modes']);
|
||
}
|
||
|
||
$notlife = ''.$dead[$start].','.$dead[$start+1].','.$dead[$start+2].','.$dead[$start+3].','.$dead[$start+4].','.$dead[$start+5].','.$dead[$start+6].','.$dead[$start+7].','.$dead[$start+8].','.$dead[$start+9].'';
|
||
$notlife1 = $dead[$start]+$dead[$start+1]+$dead[$start+2]+$dead[$start+3]+$dead[$start+4]+$dead[$start+5]+$dead[$start+6]+$dead[$start+7]+$dead[$start+8]+$dead[$start+9];
|
||
$life = ''.$enforce['u'.$start.''].','.$enforce['u'.($start+1).''].','.$enforce['u'.($start+2).''].','.$enforce['u'.($start+3).''].','.$enforce['u'.($start+4).''].','.$enforce['u'.($start+5).''].','.$enforce['u'.($start+6).''].','.$enforce['u'.($start+7).''].','.$enforce['u'.($start+8).''].','.$enforce['u'.($start+9).''].'';
|
||
$life1 = $enforce['u'.$start.'']+$enforce['u'.($start+1).'']+$enforce['u'.($start+2).'']+$enforce['u'.($start+3).'']+$enforce['u'.($start+4).'']+$enforce['u'.($start+5).'']+$enforce['u'.($start+6).'']+$enforce['u'.($start+7).'']+$enforce['u'.($start+8).'']+$enforce['u'.($start+9).''];
|
||
$lifehero = (isset($enforce['hero']) ? $enforce['hero'] : 0);
|
||
$notlifehero = (isset($dead['hero']) ? $dead['hero'] : 0);
|
||
$totallife = (isset($enforce['hero']) ? $enforce['hero'] : 0)+$life1;
|
||
$totalnotlife = (isset($dead['hero']) ? $dead['hero'] : 0)+$notlife1;
|
||
//NEED TO SEND A RAPPORTAGE!!!
|
||
$data2 = ''.$database->getVillageField($enforce['from'], "owner").','.$to['wref'].','.addslashes($to['name']).','.$tribe.','.$life.','.$notlife.','.$lifehero.','.$notlifehero.','.$enforce['from'].'';
|
||
if (empty($scout)) {
|
||
if ($totalnotlife == 0) {
|
||
$database->addNotice($database->getVillageField($enforce['from'], "owner"), $from['wref'], $ownally, 15, 'Reinforcement in '.addslashes($to['name']).' was attacked', $data2, $AttackArrivalTime);
|
||
} else if ($totallife > $totalnotlife) {
|
||
$database->addNotice($database->getVillageField($enforce['from'], "owner"), $from['wref'], $ownally, 16, 'Reinforcement in '.addslashes($to['name']).' was attacked', $data2, $AttackArrivalTime);
|
||
} else {
|
||
$database->addNotice($database->getVillageField($enforce['from'], "owner"), $from['wref'], $ownally, 17, 'Reinforcement in '.addslashes($to['name']).' was attacked', $data2, $AttackArrivalTime);
|
||
}
|
||
//delete reinf sting when its killed all.
|
||
if (!$wrong) $database->deleteReinf($enforce['id']);
|
||
}
|
||
}
|
||
|
||
return ['rom' => $rom, 'ger' => $ger, 'gal' => $gal, 'nat' => $nat, 'natar' => $natar];
|
||
}
|
||
|
||
/**
|
||
* Collect the defender-side report data BEFORE casualties are applied: reset and
|
||
* rebuild the reinforcement unit totals ($DefenderEnf) and the per-tribe hero
|
||
* totals from a fresh re-select, fold reinforcement heroes into the defender's
|
||
* hero count, build the "units sent" report rows (own troops + reinforcements,
|
||
* plus their masked variants), and (re)initialise the per-tribe dead-hero
|
||
* accumulator to zero. Returns everything the caller needs downstream.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $data Current attack row (uses 'to').
|
||
* @param int $targettribe Defender tribe (1-5).
|
||
* @param array $Defender Defender unit array (own troops + 'hero'); passed by value,
|
||
* its 'hero' is folded with reinforcement heroes and returned
|
||
* via the 'defenderHero' key.
|
||
* @return array Report bundle: DefenderEnf, DefenderHeroesTotArray,
|
||
* DefenderHeroesDeadArray, unitssend_def, unitssend_deff,
|
||
* totalsend_alldef, defenderHero.
|
||
*/
|
||
private function collectReinforcementReport($data, $targettribe, $Defender) {
|
||
global $database;
|
||
|
||
$DefenderEnf = [];
|
||
$DefenderHeroesTotArray = [];
|
||
$DefenderHeroesDeadArray = [];
|
||
$unitssend_def = [];
|
||
$unitssend_deff = [];
|
||
|
||
//Resetting the enforcement arrays
|
||
for ($i = 1; $i <= 50; $i++) {
|
||
$DefenderEnf['u'.$i] = 0;
|
||
if ($i <= 5) {
|
||
$DefenderHeroesTotArray[$i] = 0;
|
||
$DefenderHeroesDeadArray[$i] = 0;
|
||
}
|
||
}
|
||
|
||
// our reinforcements count could have changed at this point, thus the re-select
|
||
$enforcementarray2 = $database->getEnforceVillage($data['to'], 0);
|
||
if (count($enforcementarray2) > 0) {
|
||
foreach ($enforcementarray2 as $enforce2) {
|
||
for ($i = 1; $i <= 50; $i++) {
|
||
$DefenderEnf['u'.$i] += $enforce2['u'.$i];
|
||
}
|
||
|
||
//Divide heroes by tribe
|
||
if ($enforce2['hero'] > 0) {
|
||
$reinfTribe = ($enforce2['from'] == 0) ? 4 : $database->getUserField($database->getVillageField($enforce2['from'], "owner"), "tribe", 0);
|
||
$DefenderHeroesTotArray[$reinfTribe] += $enforce2['hero'];
|
||
$Defender['hero'] += $enforce2['hero'];
|
||
}
|
||
}
|
||
}
|
||
|
||
$totalsend_alldef = 0;
|
||
|
||
//Own troops
|
||
$ownTroops = array_slice($Defender, ($targettribe - 1) * 10 + 1, 10);
|
||
$totalsend_alldef = array_sum($ownTroops);
|
||
|
||
//Collecting informations for the report
|
||
$unitssend_def[0] = implode(",", $ownTroops);
|
||
$unitssend_deff[0] = '?,?,?,?,?,?,?,?,?,?,';
|
||
|
||
for ($i = 1; $i <= 5; $i++) {
|
||
//Reinforcements
|
||
$reinfTroops = array_slice($DefenderEnf, ($i - 1) * 10, 10);
|
||
$totalsend_alldef += array_sum($reinfTroops);
|
||
|
||
//Collecting informations for the report
|
||
$unitssend_def[$i] = implode(",", $reinfTroops);
|
||
$unitssend_deff[$i] = $unitssend_deff[0];
|
||
}
|
||
$totalsend_alldef += $Defender['hero'];
|
||
|
||
return [
|
||
'DefenderEnf' => $DefenderEnf,
|
||
'DefenderHeroesTotArray' => $DefenderHeroesTotArray,
|
||
'DefenderHeroesDeadArray' => $DefenderHeroesDeadArray,
|
||
'unitssend_def' => $unitssend_def,
|
||
'unitssend_deff' => $unitssend_deff,
|
||
'totalsend_alldef' => $totalsend_alldef,
|
||
'defenderHero' => $Defender['hero'],
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Compute attacker/defender total populations and fetch their village lists.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $to Defender village info (getMInfo/getOMInfo).
|
||
* @param array $from Attacker village info (getMInfo).
|
||
* @param int $isoasis Whether the target is an oasis (defender pop counts only for villages).
|
||
* @param int $defpop Initial defender population (accumulated onto).
|
||
* @param int $attpop Initial attacker population (accumulated onto).
|
||
* @return array{varray:array,varray1:array,defpop:int,attpop:int}
|
||
*/
|
||
private function calculatePopulations($to, $from, $isoasis, $defpop, $attpop) {
|
||
global $database;
|
||
|
||
$varray = $database->getProfileVillages($to['owner'], 0, false);
|
||
|
||
if ($to['owner'] == $from['owner']) $varray1 = $varray;
|
||
else $varray1 = $database->getProfileVillages($from['owner'], 0, false);
|
||
|
||
// total population of the defender
|
||
if($isoasis == 0){
|
||
foreach($varray as $defenderVillage) $defpop += $defenderVillage['pop'];
|
||
}
|
||
|
||
// total population of the attacker
|
||
foreach($varray1 as $attackerVillage) $attpop += $attackerVillage['pop'];
|
||
|
||
return [
|
||
'varray' => $varray,
|
||
'varray1' => $varray1,
|
||
'defpop' => $defpop,
|
||
'attpop' => $attpop,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Resolve how many incoming attacker units get caught in the defender's
|
||
* traps (Gaul trapper / Natar capital), update the trap counters and the
|
||
* prisoners table, and subtract the trapped troops from the attacking army.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $data Current attack row.
|
||
* @param array $Defender Defender unit counts (reads u99 / u99o).
|
||
* @param array $Attacker Attacker units (u<start..end> + 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<start..end> + uhero) plus the catapult / ram / chief / scout unit ids
|
||
* used in the battle report. Oasis attacks include the Nature siege/chief
|
||
* slots (37/38/39). Pure behaviour-preserving extraction (issue #155).
|
||
*
|
||
* @param array $attackRow The attack row (t1..t11, tribe-relative).
|
||
* @param int $owntribe Attacker tribe (1..5).
|
||
* @param int $isoasis 0 for a village target, otherwise an oasis.
|
||
* @return array { Attacker, start, end, u, catp_pic, ram_pic, chief_pic, spy_pic, hero_pic }
|
||
*/
|
||
private function buildAttackerUnits(array $attackRow, $owntribe, $isoasis) {
|
||
$Attacker = [];
|
||
$start = ($owntribe - 1) * 10 + 1;
|
||
$end = $owntribe * 10;
|
||
$u = ($owntribe - 1) * 10;
|
||
|
||
if ($isoasis == 0) {
|
||
$catapult = [8, 18, 28, 48];
|
||
$ram = [7, 17, 27, 47];
|
||
$chief = [9, 19, 29, 49];
|
||
} else {
|
||
$catapult = [8, 18, 28, 38, 48];
|
||
$ram = [7, 17, 27, 37, 47];
|
||
$chief = [9, 19, 29, 39, 49];
|
||
}
|
||
$spys = [4, 14, 23, 44];
|
||
|
||
$catp_pic = $ram_pic = $chief_pic = $spy_pic = null;
|
||
|
||
for ($i = $start; $i <= $end; $i++) {
|
||
$y = $i - $u;
|
||
$Attacker['u'.$i] = $attackRow['t'.$y];
|
||
//there are catas
|
||
if (in_array($i, $catapult)) $catp_pic = $i;
|
||
if (in_array($i, $ram)) $ram_pic = $i;
|
||
if (in_array($i, $chief)) $chief_pic = $i;
|
||
if (in_array($i, $spys)) $spy_pic = $i;
|
||
}
|
||
$Attacker['uhero'] = $attackRow['t11'];
|
||
|
||
return [
|
||
'Attacker' => $Attacker,
|
||
'start' => $start,
|
||
'end' => $end,
|
||
'u' => $u,
|
||
'catp_pic' => $catp_pic,
|
||
'ram_pic' => $ram_pic,
|
||
'chief_pic' => $chief_pic,
|
||
'spy_pic' => $spy_pic,
|
||
'hero_pic' => 'hero',
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Gather the defender's units for the current attack: the village's own
|
||
* troops (normalised to non-negative ints) plus the aggregated reinforcement
|
||
* totals, and the raw reinforcement rows. Used for both village and oasis
|
||
* targets. Pure behaviour-preserving extraction (issue #155).
|
||
*
|
||
* @param int $wref Defending village/oasis wref.
|
||
* @return array { Defender, enforDefender, enforcementarray }
|
||
*/
|
||
private function buildDefenderUnits($wref) {
|
||
global $database;
|
||
|
||
$enforDefender = [];
|
||
$Defender = $database->getUnit($wref, false);
|
||
$enforcementarray = $database->getEnforceVillage($wref, 0);
|
||
|
||
if (count($enforcementarray) > 0) {
|
||
foreach ($enforcementarray as $enforce) {
|
||
for ($i = 1; $i <= 50; $i++) {
|
||
if (!isset($enforDefender['u'.$i])) {
|
||
$enforDefender['u'.$i] = 0;
|
||
}
|
||
$enforDefender['u'.$i] += $enforce['u'.$i];
|
||
}
|
||
if (!isset($enforDefender['hero'])) {
|
||
$enforDefender['hero'] = 0;
|
||
}
|
||
$enforDefender['hero'] += $enforce['hero'];
|
||
}
|
||
}
|
||
|
||
for ($i = 1; $i <= 50; $i++) {
|
||
if (!isset($Defender['u'.$i]) || empty($Defender['u'.$i]) || $Defender['u'.$i] < 0) {
|
||
$Defender['u'.$i] = 0;
|
||
}
|
||
}
|
||
|
||
if (!isset($Defender['hero']) || empty($Defender['hero']) || $Defender['hero'] < 0) {
|
||
$Defender['hero'] = 0;
|
||
}
|
||
|
||
return [
|
||
'Defender' => $Defender,
|
||
'enforDefender' => $enforDefender,
|
||
'enforcementarray' => $enforcementarray,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Resolve the per-attack context that does not depend on the target type:
|
||
* the attacker village/owner data (tribe, alliance), the war references and
|
||
* a few base flags. Read-only — no DB writes.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $data Current attack row.
|
||
* @return array {isoasis, AttackArrivalTime, AttackerWref, DefenderWref,
|
||
* NatarCapital, AttackerID, owntribe, ownally, from, fromF}
|
||
*/
|
||
private function resolveAttackContext($data) {
|
||
global $database;
|
||
|
||
$owner = $this->getCachedUser($database->getVillageField($data['from'], "owner"), 1);
|
||
|
||
return [
|
||
'isoasis' => $data['oasistype'],
|
||
'AttackArrivalTime' => $data['endtime'],
|
||
'AttackerWref' => $data['from'],
|
||
'DefenderWref' => $data['to'],
|
||
'NatarCapital' => false,
|
||
'AttackerID' => $owner['id'],
|
||
'owntribe' => $owner['tribe'],
|
||
'ownally' => $owner['alliance'],
|
||
'from' => $database->getMInfo($data['from']),
|
||
'fromF' => $database->getVillage($data['from']),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Resolve the defender target context for a VILLAGE attack: target owner
|
||
* (tribe/alliance), map info, conquest flag, evasion inputs and the battle
|
||
* environment (wall, armory/blacksmith tech, residence, siege masonry).
|
||
* Read-only — no DB writes.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $data Current attack row.
|
||
* @param array $dataarray Full batch of completed attacks.
|
||
* @param int $data_num Index of $data within $dataarray.
|
||
* @param int $owntribe Attacker tribe.
|
||
* @return array Target + battle-environment context.
|
||
*/
|
||
private function resolveVillageTarget($data, $dataarray, $data_num, $owntribe) {
|
||
global $database, $units;
|
||
|
||
$DefenderUserData = $this->getCachedUser($database->getVillageField($data['to'],"owner"),1);
|
||
$DefenderID = $DefenderUserData["id"];
|
||
$targettribe = $DefenderUserData["tribe"];
|
||
$targetally = $DefenderUserData["alliance"];
|
||
$to = $database->getMInfo($data['to']);
|
||
$toF = $database->getVillage($data['to']);
|
||
$conqureby = 0;
|
||
$NatarCapital = ($toF['owner'] == 3 && $toF['capital'] == 1);
|
||
if(!isset($to['name']) || empty($to['name'])) $to['name'] = "[?]";
|
||
|
||
$DefenderUnit = $database->getUnit($data['to']);
|
||
$evasion = $toF["evasion"];
|
||
$maxevasion = $DefenderUserData["maxevasion"];
|
||
$gold = $DefenderUserData["gold"];
|
||
$cannotsend = false;
|
||
|
||
$movements = $database->getMovement(34, $data['to'], 1);
|
||
for($y = 0; $y < count($movements); $y++){
|
||
if(property_exists($units, $y)){
|
||
$returntime = $units->$y['endtime'] - time();
|
||
if($units->$y['sort_type'] == 4 && $units->$y['from'] != 0 && $returntime <= 10){
|
||
$cannotsend = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
//need to set these variables.
|
||
$def_wall = $database->getFieldLevel($data['to'], 40, false);
|
||
$att_tribe = $owntribe;
|
||
$def_tribe = $targettribe;
|
||
$attpop = $defpop = $residence = 0;
|
||
$def_ab = [];
|
||
|
||
//get level of palace or residence
|
||
$residence = $database->getFieldLevelInVillage($data['to'], '25, 26', false);
|
||
|
||
//type of attack
|
||
$type = $dataarray[$data_num]['attack_type'];
|
||
$scout = ($type == 1) ? 1 : 0;
|
||
|
||
$ud = ($def_tribe - 1) * 10;
|
||
$att_ab = $database->getABTech($data['from']); // Blacksmith level
|
||
$att_ab1 = $att_ab['b1'];
|
||
$att_ab2 = $att_ab['b2'];
|
||
$att_ab3 = $att_ab['b3'];
|
||
$att_ab4 = $att_ab['b4'];
|
||
$att_ab5 = $att_ab['b5'];
|
||
$att_ab6 = $att_ab['b6'];
|
||
$att_ab7 = $att_ab['b7'];
|
||
$att_ab8 = $att_ab['b8'];
|
||
$armory = $database->getABTech($data['to']); // Armory level
|
||
$def_ab[$ud + 1] = $armory['a1'];
|
||
$def_ab[$ud + 2] = $armory['a2'];
|
||
$def_ab[$ud + 3] = $armory['a3'];
|
||
$def_ab[$ud + 4] = $armory['a4'];
|
||
$def_ab[$ud + 5] = $armory['a5'];
|
||
$def_ab[$ud + 6] = $armory['a6'];
|
||
$def_ab[$ud + 7] = $armory['a7'];
|
||
$def_ab[$ud + 8] = $armory['a8'];
|
||
|
||
//rams attack
|
||
$walllevel = 0;
|
||
$wallid = 0;
|
||
if (($data['t7']) > 0 && $type == 3) {
|
||
$basearraywall = $to;
|
||
if (($walllevel = $database->getFieldLevel($basearraywall['wref'], 40, false)) > 0){
|
||
$wallid = 40;
|
||
}
|
||
}
|
||
|
||
$tblevel = 1;
|
||
$stonemason = $database->getFieldLevelInVillage($data['to'], 34);
|
||
|
||
return [
|
||
'DefenderID' => $DefenderID, 'targettribe' => $targettribe, 'targetally' => $targetally,
|
||
'to' => $to, 'toF' => $toF, 'conqureby' => $conqureby, 'NatarCapital' => $NatarCapital,
|
||
'DefenderUnit' => $DefenderUnit, 'evasion' => $evasion, 'maxevasion' => $maxevasion,
|
||
'gold' => $gold, 'cannotsend' => $cannotsend,
|
||
'def_wall' => $def_wall, 'att_tribe' => $att_tribe, 'def_tribe' => $def_tribe,
|
||
'attpop' => $attpop, 'defpop' => $defpop, 'residence' => $residence, 'def_ab' => $def_ab,
|
||
'type' => $type, 'scout' => $scout,
|
||
'att_ab1' => $att_ab1, 'att_ab2' => $att_ab2, 'att_ab3' => $att_ab3, 'att_ab4' => $att_ab4,
|
||
'att_ab5' => $att_ab5, 'att_ab6' => $att_ab6, 'att_ab7' => $att_ab7, 'att_ab8' => $att_ab8,
|
||
'walllevel' => $walllevel, 'wallid' => $wallid,
|
||
'tblevel' => $tblevel, 'stonemason' => $stonemason,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Resolve the defender target context for an OASIS attack: target owner
|
||
* (tribe/alliance), oasis map info, conquest flag and the (mostly fixed)
|
||
* battle environment. Read-only — no DB writes.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $data Current attack row.
|
||
* @param array $dataarray Full batch of completed attacks.
|
||
* @param int $data_num Index of $data within $dataarray.
|
||
* @param int $owntribe Attacker tribe.
|
||
* @return array Target + battle-environment context.
|
||
*/
|
||
private function resolveOasisTarget($data, $dataarray, $data_num, $owntribe) {
|
||
global $database;
|
||
|
||
$DefenderUserData = $this->getCachedUser($database->getOasisField($data['to'], "owner"),1);
|
||
$DefenderID = $DefenderUserData["id"];
|
||
$targettribe = $DefenderUserData["tribe"];
|
||
$targetally = $DefenderUserData["alliance"];
|
||
$to = $database->getOMInfo($data['to']);
|
||
$toF = $database->getOasisV($data['to']);
|
||
$conqureby = $toF['conqured'];
|
||
|
||
//need to set these variables.
|
||
$def_wall = $residence = $attpop = 0;
|
||
$att_tribe = $owntribe;
|
||
$def_tribe = $targettribe;
|
||
$defpop = 500;
|
||
|
||
//type of attack
|
||
$type = $dataarray[$data_num]['attack_type'];
|
||
$scout = ($type == 1) ? 1 : 0;
|
||
|
||
$att_ab1 = $att_ab2 = $att_ab3 = $att_ab4 = $att_ab5 = $att_ab6 = $att_ab7 = $att_ab8 = 0;
|
||
$def_ab = [];
|
||
$def_ab[31] = $def_ab[32] = $def_ab[33] = $def_ab[34] = $def_ab[35] = $def_ab[36] = $def_ab[37] = $def_ab[38] = 0;
|
||
|
||
$walllevel = $tblevel = $stonemason = 0;
|
||
|
||
return [
|
||
'DefenderID' => $DefenderID, 'targettribe' => $targettribe, 'targetally' => $targetally,
|
||
'to' => $to, 'toF' => $toF, 'conqureby' => $conqureby,
|
||
'def_wall' => $def_wall, 'att_tribe' => $att_tribe, 'def_tribe' => $def_tribe,
|
||
'attpop' => $attpop, 'defpop' => $defpop, 'residence' => $residence, 'def_ab' => $def_ab,
|
||
'type' => $type, 'scout' => $scout,
|
||
'att_ab1' => $att_ab1, 'att_ab2' => $att_ab2, 'att_ab3' => $att_ab3, 'att_ab4' => $att_ab4,
|
||
'att_ab5' => $att_ab5, 'att_ab6' => $att_ab6, 'att_ab7' => $att_ab7, 'att_ab8' => $att_ab8,
|
||
'walllevel' => $walllevel, 'tblevel' => $tblevel, 'stonemason' => $stonemason,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Compute attacker/defender hero XP and victory points from the battle
|
||
* casualties, persist them (hero XP, player points, alliance points) and
|
||
* return the total defender losses.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $alldead Reinforcement casualties by global unit id (+ 'hero').
|
||
* @param array $owndead Defender own casualties by global unit id (+ 'hero').
|
||
* @param array $attackerDead Attacker casualties, 0-indexed t1..t11 (index 10 = hero).
|
||
* @param int $targettribe Defender tribe id.
|
||
* @param int $att_tribe Attacker tribe id.
|
||
* @param array $Attacker Attacker troop row (uses 'uhero').
|
||
* @param mixed $AttackerHeroID Attacker hero id (for the XP write).
|
||
* @param array $Defender Defender troop row (uses 'hero').
|
||
* @param array $DefendersHeroID Defender hero ids (for the XP writes).
|
||
* @param array $toF Defender village (uses 'owner').
|
||
* @param array $from Attacker village info (uses 'owner').
|
||
* @param int $targetally Defender alliance id.
|
||
* @param int $ownally Attacker alliance id.
|
||
* @param mixed $heroxp OUT (by ref): attacker hero XP, set only if the attacker has a hero.
|
||
* @param mixed $defheroxp OUT (by ref): per-defender hero XP, set only if the defender has a hero.
|
||
* @return int Total defender losses (reinforcements + own troops).
|
||
*/
|
||
private function calculateHeroXpAndPoints(
|
||
array $alldead, array $owndead, array $attackerDead,
|
||
$targettribe, $att_tribe,
|
||
$Attacker, $AttackerHeroID, $Defender, $DefendersHeroID,
|
||
$toF, $from, $targetally, $ownally,
|
||
&$heroxp, &$defheroxp
|
||
) {
|
||
global $database;
|
||
|
||
$totaldead_def = 0;
|
||
$totalpoint_att = 0;
|
||
|
||
for($i = 1 ;$i <= 50; $i++) {
|
||
$unitarray = $GLOBALS["u".$i];
|
||
|
||
//Reinforcements dead troops
|
||
$totaldead_def += $alldead[$i];
|
||
$totalpoint_att += ($alldead[$i] * $unitarray['pop']);
|
||
|
||
//Own dead troops
|
||
if($i >= ($targettribe - 1) * 10 + 1 && $i <= $targettribe * 10){
|
||
$totaldead_def += $owndead[$i];
|
||
$totalpoint_att += ($owndead[$i] * $unitarray['pop']);
|
||
}
|
||
}
|
||
$totalpoint_att += ((isset($alldead['hero']) ? $alldead['hero'] : 0) * 6);
|
||
$totalpoint_att += ((isset($owndead['hero']) ? $owndead['hero'] : 0) * 6);
|
||
|
||
if ($Attacker['uhero'] > 0){
|
||
$heroxp = $totalpoint_att;
|
||
$database->modifyHeroXp("experience", $heroxp, $AttackerHeroID);
|
||
}
|
||
|
||
for($i = 1; $i <= 10; $i++){
|
||
$unitarray = $GLOBALS["u".(($att_tribe - 1) * 10 + $i)];
|
||
if ( !isset($totalpoint_def) ) {
|
||
$totalpoint_def = 0;
|
||
}
|
||
$totalpoint_def += ($attackerDead[$i - 1] * $unitarray['pop']);
|
||
}
|
||
|
||
$totalpoint_def += $attackerDead[10] * 6;
|
||
|
||
if($Defender['hero'] > 0){
|
||
//counting heroxp
|
||
$defheroxp = intval($totalpoint_def / count($DefendersHeroID));
|
||
foreach($DefendersHeroID as $HeroID){
|
||
$database->modifyHeroXp("experience",$defheroxp,$HeroID);
|
||
}
|
||
}
|
||
|
||
// we don't need these two variables anymore
|
||
unset($AttackerHeroID, $DefendersHeroID);
|
||
|
||
$database->modifyPoints(
|
||
$toF['owner'],
|
||
['dpall', 'dp'],
|
||
[$totalpoint_def, $totalpoint_def]
|
||
);
|
||
|
||
$database->modifyPoints(
|
||
$from['owner'],
|
||
['apall', 'ap'],
|
||
[$totalpoint_att, $totalpoint_att]
|
||
);
|
||
|
||
$database->modifyPointsAlly(
|
||
$targetally,
|
||
['Adp', 'dp'],
|
||
[$totalpoint_def, $totalpoint_def]
|
||
);
|
||
|
||
$database->modifyPointsAlly(
|
||
$ownally,
|
||
['Aap', 'ap'],
|
||
[$totalpoint_att, $totalpoint_att]
|
||
);
|
||
|
||
return $totaldead_def;
|
||
}
|
||
|
||
/**
|
||
* Resolve the resources lootable from the battle target after cranny
|
||
* protection: compute the cranny efficiency, pull the current (capped)
|
||
* stocks of the target village/oasis and derive the lootable amounts.
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $data Current attack row (uses 'to').
|
||
* @param int $isoasis Oasis flag (0 = village).
|
||
* @param int $conqureby Owner village of a conquered oasis (0 = none).
|
||
* @param int $owntribe Attacker tribe id (Teuton cranny bonus).
|
||
* @param int $targettribe Defender tribe id (Gaul cranny bonus).
|
||
* @param mixed $crannySpy OUT (by ref): cranny value seen by a scout; set only for village targets (isoasis == 0).
|
||
* @return array ['totclay','totiron','totwood','totcrop','avclay','aviron','avwood','avcrop'].
|
||
*/
|
||
private function resolveResourcesAfterBattle($data, $isoasis, $conqureby, $owntribe, $targettribe, &$crannySpy) {
|
||
global $database, $bid23;
|
||
|
||
if ($isoasis == 0){
|
||
// get total cranny value:
|
||
$buildarray = $database->getResourceLevel($data['to']);
|
||
$cranny = 0;
|
||
for($i = 19; $i < 39;$i++){
|
||
if($buildarray['f'.$i.'t'] == 23){
|
||
$cranny += $bid23[$buildarray['f'.$i]]['attri'] * CRANNY_CAPACITY;
|
||
}
|
||
}
|
||
|
||
//cranny efficiency
|
||
$atk_bonus = ($owntribe == 2) ? (4 / 5) : 1;
|
||
$def_bonus = ($targettribe == 3) ? 2 : 1;
|
||
$to_owner = $database->getVillageField($data['to'], "owner");
|
||
|
||
$crannySpy = $database->getArtifactsValueInfluence($to_owner, $data['to'], 7, $cranny * $def_bonus);
|
||
$cranny_eff = $crannySpy * $atk_bonus;
|
||
|
||
// work out available resources.
|
||
$this->updateRes($data['to']);
|
||
$this->pruneResource();
|
||
|
||
$villageData = $database->getVillageFields($data['to'], 'clay, iron, wood, crop', false);
|
||
$totclay = $villageData['clay'];
|
||
$totiron = $villageData['iron'];
|
||
$totwood = $villageData['wood'];
|
||
$totcrop = $villageData['crop'];
|
||
}else{
|
||
$cranny_eff = 0;
|
||
|
||
if ($conqureby > 0) { //10% from owner proc village owner - fix by ronix - exploit fixed by iopietro
|
||
$this->updateRes($conqureby);
|
||
$this->pruneResource();
|
||
|
||
$villageData = $database->getVillageFields($conqureby, 'clay, iron, wood, crop', false);
|
||
$totclay = intval($villageData['clay'] / 10);
|
||
$totiron = intval($villageData['iron'] / 10);
|
||
$totwood = intval($villageData['wood'] / 10);
|
||
$totcrop = intval($villageData['crop'] / 10);
|
||
}else{
|
||
// work out available resources.
|
||
$this->updateORes($data['to']);
|
||
$this->pruneOResource();
|
||
|
||
$oasisData = $database->getOasisFields($data['to'], false);
|
||
$totclay = $oasisData['clay'];
|
||
$totiron = $oasisData['iron'];
|
||
$totwood = $oasisData['wood'];
|
||
$totcrop = $oasisData['crop'];
|
||
}
|
||
}
|
||
|
||
$avclay = floor($totclay - $cranny_eff);
|
||
$aviron = floor($totiron - $cranny_eff);
|
||
$avwood = floor($totwood - $cranny_eff);
|
||
$avcrop = floor($totcrop - $cranny_eff);
|
||
|
||
$avclay = ($avclay < 0) ? 0 : $avclay;
|
||
$aviron = ($aviron < 0) ? 0 : $aviron;
|
||
$avwood = ($avwood < 0) ? 0 : $avwood;
|
||
$avcrop = ($avcrop < 0) ? 0 : $avcrop;
|
||
|
||
return [
|
||
'totclay' => $totclay, 'totiron' => $totiron, 'totwood' => $totwood, 'totcrop' => $totcrop,
|
||
'avclay' => $avclay, 'aviron' => $aviron, 'avwood' => $avwood, 'avcrop' => $avcrop,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Resolve the attacker hero's outcome after the battle: build the hero
|
||
* report line ($info_hero), and on a surviving hero handle oasis conquest /
|
||
* loyalty reduction (oasis targets) or artifact claiming with possible
|
||
* village destruction (village targets).
|
||
* Pure behaviour-preserving extraction (refactor for issue #155).
|
||
*
|
||
* @param array $data Current attack row (uses 't11', 'from', 'to').
|
||
* @param array $from Attacker village info (uses 'owner').
|
||
* @param array $to Defender village/oasis info (uses 'owner', 'loyalty').
|
||
* @param int $dead11 Attacker hero casualties.
|
||
* @param int $traped11 Attacker hero trapped count.
|
||
* @param mixed $heroxp Attacker hero XP gained this battle.
|
||
* @param string $hero_pic Hero unit picture id (report markup).
|
||
* @param int $isoasis Oasis flag (0 = village).
|
||
* @param int $type Attack type (3 = normal with rams/catas).
|
||
* @param int $targettribe Defender tribe id.
|
||
* @param string $info_cat Catapult report fragment (read for the destruction notice).
|
||
* @param string $info_hero IN/OUT (by ref): hero report line.
|
||
* @param int $can_destroy IN/OUT (by ref): village-destruction allowed flag.
|
||
* @param int $village_destroyed IN/OUT (by ref): village-destroyed flag.
|
||
* @return void
|
||
*/
|
||
private function handleHeroPostBattle($data, $from, $to, $dead11, $traped11, $heroxp, $hero_pic, $isoasis, $type, $targettribe, $info_cat, &$info_hero, &$can_destroy, &$village_destroyed) {
|
||
global $database;
|
||
|
||
if(($data['t11'] - $dead11 - $traped11) > 0){ //hero
|
||
if ($heroxp == 0) {
|
||
$xp = "";
|
||
$info_hero = $hero_pic.",".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;
|
||
|
||
// If the dead units not equal the ammount sent they will return and report
|
||
if($totalsend_att - ($totaldead_att + (isset($totaltraped_att) ? $totaltraped_att : 0)) > 0)
|
||
{
|
||
$returningTroops = [];
|
||
for($i = 1; $i <= 11; $i++) $returningTroops['t'.$i] = $data['t'.$i] - $traped[$i] - $dead[$i];
|
||
$troopsTime = $units->getWalkingTroopsTime($from['wref'], $to['wref'], $from['owner'], $owntribe, $returningTroops, 1, 't');
|
||
$endtime = $database->getArtifactsValueInfluence($from['owner'], $from['wref'], 2, $troopsTime);
|
||
$endtime += $AttackArrivalTime;
|
||
if($type == 1){
|
||
if($from['owner'] == 3){ // fix natar report by ronix
|
||
$database->addNotice($to['owner'], $to['wref'], $targetally, 20, '' . addslashes($from['name']) . ' scouts ' . addslashes($to['name']) . '', $data2, $AttackArrivalTime);
|
||
}elseif($totaldead_att == 0 && $totaltraped_att == 0){
|
||
$database->addNotice($from['owner'], $to['wref'], $ownally, 18, '' . addslashes($from['name']) . ' scouts ' . addslashes($to['name']) . '', $data2, $AttackArrivalTime);
|
||
}else{
|
||
$database->addNotice($from['owner'], $to['wref'], $ownally, 21, '' . addslashes($from['name']) . ' scouts ' . addslashes($to['name']) . '', $data2, $AttackArrivalTime);
|
||
}
|
||
}else{
|
||
if((empty($totaldead_att) || $totaldead_att == 0) && (empty($totaltraped_att) || $totaltraped_att == 0)){
|
||
$database->addNotice($from['owner'], $to['wref'], $ownally, 1, '' . addslashes($from['name']) . ' attacks ' . addslashes($to['name']) . '', $data2, $AttackArrivalTime);
|
||
}else{
|
||
$database->addNotice($from['owner'], $to['wref'], $ownally, 2, '' . addslashes($from['name']) . ' attacks ' . addslashes($to['name']) . '', $data2, $AttackArrivalTime);
|
||
}
|
||
}
|
||
|
||
$database->setMovementProc($data['moveid']);
|
||
|
||
if (!isset($chiefing_village)) $chiefing_village = 0;
|
||
|
||
if($chiefing_village != 1){
|
||
$database->addMovement(4, $DefenderWref, $AttackerWref, $data['ref'], $AttackArrivalTime, $endtime, 1, $steal[0], $steal[1], $steal[2], $steal[3]);
|
||
if($type !== 1){
|
||
if ($isoasis == 0) $database->modifyResource($DefenderWref, $steal[0], $steal[1], $steal[2], $steal[3], 0);
|
||
else
|
||
{
|
||
//if it's an oasis but it's conquered by someone, resources must be modified in the owner's village
|
||
if($conqureby > 0) $database->modifyResource($conqureby, $steal[0], $steal[1], $steal[2], $steal[3], 0);
|
||
else $database->modifyOasisResource($DefenderWref, $steal[0], $steal[1], $steal[2], $steal[3], 0);
|
||
}
|
||
$totalstolengain = $steal[0] + $steal[1] + $steal[2] + $steal[3];
|
||
$totalstolentaken = ((isset($totalstolentaken) ? $totalstolentaken : 0) - ($steal[0] + $steal[1] + $steal[2] + $steal[3]));
|
||
$database->modifyPoints($from['owner'], 'RR', $totalstolengain);
|
||
$database->modifyPoints($to['owner'], 'RR', $totalstolentaken);
|
||
$database->modifyPointsAlly($targetally, 'RR', $totalstolentaken);
|
||
$database->modifyPointsAlly($ownally, 'RR', $totalstolengain);
|
||
}
|
||
}else{ //fix by ronix if only 1 chief left to conqured - don't add with zero enforces
|
||
if($totalsend_att - ($totaldead_att + (isset($totaltraped_att) ? $totaltraped_att : 0)) > 1){
|
||
$database->addEnforce2($data, $owntribe, $troopsdead[1], $troopsdead[2], $troopsdead[3], $troopsdead[4], $troopsdead[5], $troopsdead[6], $troopsdead[7], $troopsdead[8], $troopsdead[9], $troopsdead[10], $troopsdead[11]);
|
||
}
|
||
}
|
||
}
|
||
else //else they die and don't return or report.
|
||
{
|
||
$database->setMovementProc($data['moveid']);
|
||
if($type == 1){
|
||
$database->addNotice($from['owner'], $to['wref'], $ownally, 19, addslashes($from['name']) . ' scouts ' . addslashes($to['name']) . '', $data_fail, $AttackArrivalTime);
|
||
}else{
|
||
$database->addNotice($from['owner'], $to['wref'], $ownally, 3, '' . addslashes($from['name']) . ' attacks ' . addslashes($to['name']) . '', $data_fail, $AttackArrivalTime);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 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() {
|
||
// PROCESARE ATACURI COMPLETE - functie critica, pastrata 100% compatibila
|
||
// Aceasta functie gestioneaza toate atacurile care ajung la destinatie
|
||
// Include: batalii, capcane, evaziune erou, distrugere cladiri, cuceriri
|
||
global $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) {
|
||
// If an earlier attack in this same batch razed the target village,
|
||
// its still-in-flight follow-up waves were already bounced straight
|
||
// home by DelVillage() (issue #298). Re-resolving them here would
|
||
// fight a now-deleted (phantom) village: getMInfo() returns NULL
|
||
// vdata columns, so the return trip is computed from a NULL wref
|
||
// (NULL coordinates) — yielding a bogus arrival time that strands
|
||
// the troops — and would also duplicate the bounce. Skip them.
|
||
if (isset($razedTargets[$data['to']])) {
|
||
$data_num++;
|
||
continue;
|
||
}
|
||
|
||
//set base things
|
||
$totaltraped_att = 0;
|
||
for($i = 1; $i <= 11; $i++) ${'traped'.$i} = 0;
|
||
// per-attack context (attacker village/owner) — extracted to resolveAttackContext() [#155]
|
||
$ctx = $this->resolveAttackContext($data);
|
||
$isoasis = $ctx['isoasis'];
|
||
$AttackArrivalTime = $ctx['AttackArrivalTime'];
|
||
$AttackerWref = $ctx['AttackerWref'];
|
||
$DefenderWref = $ctx['DefenderWref'];
|
||
$NatarCapital = $ctx['NatarCapital'];
|
||
$AttackerID = $ctx['AttackerID'];
|
||
$Attacker['id'] = $ctx['AttackerID'];
|
||
$owntribe = $ctx['owntribe'];
|
||
$ownally = $ctx['ownally'];
|
||
$from = $ctx['from'];
|
||
$fromF = $ctx['fromF'];
|
||
|
||
//It's a village
|
||
if ($isoasis == 0){
|
||
// target + battle environment — extracted to resolveVillageTarget() [#155]
|
||
$vt = $this->resolveVillageTarget($data, $dataarray, $data_num, $owntribe);
|
||
$DefenderID = $vt['DefenderID'];
|
||
$targettribe = $vt['targettribe'];
|
||
$targetally = $vt['targetally'];
|
||
$to = $vt['to'];
|
||
$toF = $vt['toF'];
|
||
$conqureby = $vt['conqureby'];
|
||
$NatarCapital = $vt['NatarCapital'];
|
||
$DefenderUnit = $vt['DefenderUnit'];
|
||
$def_wall = $vt['def_wall'];
|
||
$att_tribe = $vt['att_tribe'];
|
||
$def_tribe = $vt['def_tribe'];
|
||
$attpop = $vt['attpop'];
|
||
$defpop = $vt['defpop'];
|
||
$residence = $vt['residence'];
|
||
$def_ab = $vt['def_ab'];
|
||
$type = $vt['type'];
|
||
$scout = $vt['scout'];
|
||
$att_ab1 = $vt['att_ab1'];
|
||
$att_ab2 = $vt['att_ab2'];
|
||
$att_ab3 = $vt['att_ab3'];
|
||
$att_ab4 = $vt['att_ab4'];
|
||
$att_ab5 = $vt['att_ab5'];
|
||
$att_ab6 = $vt['att_ab6'];
|
||
$att_ab7 = $vt['att_ab7'];
|
||
$att_ab8 = $vt['att_ab8'];
|
||
$walllevel = $vt['walllevel'];
|
||
$wallid = $vt['wallid'];
|
||
$tblevel = $vt['tblevel'];
|
||
$stonemason = $vt['stonemason'];
|
||
|
||
$this->handleEvasion($data, $DefenderID, $DefenderUnit, $targettribe, $vt['evasion'], $vt['maxevasion'], $vt['gold'], $vt['cannotsend'], $dataarray[$data_num]['attack_type']);
|
||
|
||
// defence units gathered — extracted to buildDefenderUnits() [#155]
|
||
$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'];
|
||
|
||
// attacker army built — extracted to buildAttackerUnits() [#155]
|
||
$atkUnits = $this->buildAttackerUnits($dataarray[$data_num], $owntribe, $isoasis);
|
||
$Attacker = $atkUnits['Attacker'];
|
||
$start = $atkUnits['start'];
|
||
$end = $atkUnits['end'];
|
||
$catp_pic = $atkUnits['catp_pic'];
|
||
$ram_pic = $atkUnits['ram_pic'];
|
||
$chief_pic = $atkUnits['chief_pic'];
|
||
$spy_pic = $atkUnits['spy_pic'];
|
||
$hero_pic = $atkUnits['hero_pic'];
|
||
}
|
||
|
||
// attacker/defender populations + village lists — extracted to calculatePopulations() [#155]
|
||
$popData = $this->calculatePopulations($to, $from, $isoasis, $defpop, $attpop);
|
||
$varray = $popData['varray'];
|
||
$varray1 = $popData['varray1'];
|
||
$defpop = $popData['defpop'];
|
||
$attpop = $popData['attpop'];
|
||
|
||
//fix by ronix
|
||
for ($i = 1; $i <= 50; $i++) {
|
||
if (!isset($enforDefender['u'.$i])) {
|
||
$enforDefender['u'.$i] = 0;
|
||
}
|
||
$enforDefender['u'.$i] += (isset($Defender['u'.$i]) ? $Defender['u'.$i] : 0);
|
||
}
|
||
|
||
$defspy = $enforDefender['u4'] > 0 || $enforDefender['u14'] > 0 || $enforDefender['u23'] > 0 || $enforDefender['u44'] > 0;
|
||
|
||
if(PEACE == 0 || $targettribe == 4 || $targettribe == 5 || $scout){
|
||
// trapper resolution + prisoners — extracted to calculateTrappedUnits() [#155]
|
||
$trapResult = $this->calculateTrappedUnits($data, $Defender, $Attacker, $NatarCapital, $scout, $start, $end);
|
||
for($i = 1; $i <= 11; $i++) ${'traped'.$i} = $trapResult['traped'][$i];
|
||
$totaltraped_att = $trapResult['totaltraped_att'];
|
||
$Attacker = $trapResult['Attacker'];
|
||
|
||
// we need to save the attacker heroid before the battle
|
||
if(isset($Attacker['uhero']) && $Attacker['uhero'] > 0){
|
||
$AttackerHeroID = $database->getHeroField($from['owner'], "heroid");
|
||
}
|
||
|
||
// and the defender(s) heroid
|
||
$DefendersHeroID = [];
|
||
$herosend_def = 0;
|
||
|
||
// check if our hero is defending the village, if so, add it to the list
|
||
if (isset($Defender['hero']) && $Defender['hero'] > 0) {
|
||
$DefendersHeroID[] = $database->getHeroField($DefenderID, "heroid");
|
||
|
||
// collecting information for the battle report
|
||
$herosend_def += $Defender['hero'];
|
||
}
|
||
|
||
// check if there are other heroes defending the village
|
||
if(count($enforcementarray) > 0){
|
||
foreach($enforcementarray as $enforcement){
|
||
if($enforcement['hero'] > 0){
|
||
$heroOwner = $database->getVillageField($enforcement['from'], "owner");
|
||
$DefendersHeroID[] = $database->getHeroField($heroOwner, "heroid");
|
||
}
|
||
}
|
||
}
|
||
|
||
//fix by ronix
|
||
if (!isset($walllevel)) $walllevel = 0;
|
||
|
||
$battlepart = $battle->calculateBattle($Attacker, $Defender, $def_wall, $att_tribe, $def_tribe, $residence, $attpop, $defpop, $type, $def_ab, $att_ab1, $att_ab2, $att_ab3, $att_ab4, $att_ab5, $att_ab6, $att_ab7, $att_ab8, $tblevel, $stonemason, $walllevel, 0, 0, 0, $AttackerID, $DefenderID, $AttackerWref, $DefenderWref, $conqureby, $enforcementarray);
|
||
|
||
//Data for when troops return.
|
||
//catapults look :D
|
||
$info_cat = $info_chief = $info_ram = $info_hero = ",";
|
||
|
||
//check to see if can destroy village
|
||
if (count($varray) > 1 && !$database->villageHasArtefact($DefenderWref) && empty($to['natar'])) {
|
||
$can_destroy = 1;
|
||
}
|
||
else $can_destroy = 0;
|
||
|
||
//Catapults and rams management
|
||
//TODO: Move this in Battle.php
|
||
if($isoasis == 0){
|
||
if ($type == 3){
|
||
if (($data['t7'] - $traped7) > 0){
|
||
// ram damage on the wall (+ battle recalc) — extracted to applyRamDamage() [#155]
|
||
$ramResult = $this->applyRamDamage($data, $walllevel, $wallid, $ram_pic, $battlepart, [
|
||
'Attacker' => $Attacker, 'Defender' => $Defender,
|
||
'att_tribe' => $att_tribe, 'def_tribe' => $def_tribe,
|
||
'residence' => $residence, 'attpop' => $attpop, 'defpop' => $defpop,
|
||
'type' => $type, 'def_ab' => $def_ab,
|
||
'att_ab1' => $att_ab1, 'att_ab2' => $att_ab2, 'att_ab3' => $att_ab3, 'att_ab4' => $att_ab4,
|
||
'att_ab5' => $att_ab5, 'att_ab6' => $att_ab6, 'att_ab7' => $att_ab7, 'att_ab8' => $att_ab8,
|
||
'tblevel' => $tblevel, 'stonemason' => $stonemason,
|
||
'AttackerID' => $AttackerID, 'DefenderID' => $DefenderID,
|
||
'AttackerWref' => $AttackerWref, 'DefenderWref' => $DefenderWref,
|
||
'conqureby' => $conqureby, 'enforcementarray' => $enforcementarray,
|
||
]);
|
||
$battlepart = $ramResult['battlepart'];
|
||
$info_ram = $ramResult['info_ram'];
|
||
}
|
||
|
||
if (($data['t8'] - $traped8) > 0)
|
||
{
|
||
$catResult = $this->applyCatapults($data, $battlepart, $catp_pic, $can_destroy, $isoasis, $targettribe, $info_cat);
|
||
$battlepart = $catResult['battlepart'];
|
||
$info_cat = $catResult['info_cat'];
|
||
$village_destroyed = $catResult['village_destroyed'];
|
||
}
|
||
} elseif (($data['t7'] - $traped7) > 0) {
|
||
$info_ram = "".$ram_pic.",Hint: The ram does not work during a raid.";
|
||
}
|
||
}
|
||
else $can_destroy = 0;
|
||
|
||
//units attack string for battleraport
|
||
$unitssend_att = ''.$data['t1'].','.$data['t2'].','.$data['t3'].','.$data['t4'].','.$data['t5'].','.$data['t6'].','.$data['t7'].','.$data['t8'].','.$data['t9'].','.$data['t10'].'';
|
||
$herosend_att = $data['t11'];
|
||
|
||
//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 <= 50; $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'];
|
||
$totalsend_att = $data['t1']+$data['t2']+$data['t3']+$data['t4']+$data['t5']+$data['t6']+$data['t7']+$data['t8']+$data['t9']+$data['t10']+$data['t11'];
|
||
|
||
$DefenderHeroesTot = implode(",", $DefenderHeroesTotArray);
|
||
$DefenderHeroesDead = implode(",", $DefenderHeroesDeadArray);
|
||
|
||
if (empty($alldead['hero'])) $alldead['hero'] = 0;
|
||
if (empty($owndead['hero'])) $owndead['hero'] = 0;
|
||
$deadhero = $owndead['hero'];
|
||
|
||
//Counting own total dead troops
|
||
$ownDeadTroops = array_slice($owndead, 0, 10);
|
||
$totaldead_alldef = array_sum($ownDeadTroops);
|
||
|
||
//Collecting informations for the report
|
||
$unitsdead_def[0] = implode(",", $ownDeadTroops);
|
||
$unitsdead_deff[0] = '?,?,?,?,?,?,?,?,?,?,';
|
||
for($i = 1; $i <= 5; $i++){
|
||
//Counting reinforcements total dead troops
|
||
$deadTroops = array_slice($alldead, ($i - 1) * 10, 10);
|
||
$totaldead_alldef += array_sum($deadTroops);
|
||
//Collecting informations for the report
|
||
$unitsdead_def[$i] = implode(",", $deadTroops);
|
||
$unitsdead_deff[$i] = $unitsdead_deff[0];
|
||
}
|
||
$totaldead_alldef += ($deadhero + $alldead['hero']);
|
||
|
||
if (!isset($totalattackdead)) $totalattackdead = 0;
|
||
$totalattackdead += $totaldead_alldef;
|
||
|
||
// Set units returning from attack
|
||
|
||
$p_units = [];
|
||
for ($i = 1; $i <= 11; $i++) {
|
||
if (!isset(${'dead'.$i})) ${'dead'.$i} = 0;
|
||
if (!isset(${'traped'.$i})) ${'traped'.$i} = 0;
|
||
$p_units[] = "t".$i." = t".$i." - ".(${'dead'.$i} + ${'traped'.$i});
|
||
}
|
||
|
||
$database->modifyAttack3($data['ref'],implode(', ', $p_units));
|
||
|
||
$unitsdead_att = $dead1.','.$dead2.','.$dead3.','.$dead4.','.$dead5.','.$dead6.','.$dead7.','.$dead8.','.$dead9.','.$dead10;
|
||
$unitstraped_att = $traped1.','.$traped2.','.$traped3.','.$traped4.','.$traped5.','.$traped6.','.$traped7.','.$traped8.','.$traped9.','.$traped10.','.$traped11;
|
||
|
||
//top 10 attack and defence update
|
||
$totaldead_att = $dead1 + $dead2 + $dead3 + $dead4 + $dead5 + $dead6 + $dead7 + $dead8 + $dead9 + $dead10 + $dead11;
|
||
$totalattackdead += $totaldead_att;
|
||
$troopsdead1 = $dead1;
|
||
$troopsdead2 = $dead2;
|
||
$troopsdead3 = $dead3;
|
||
$troopsdead4 = $dead4;
|
||
$troopsdead5 = $dead5;
|
||
$troopsdead6 = $dead6;
|
||
$troopsdead7 = $dead7;
|
||
$troopsdead8 = $dead8;
|
||
$troopsdead9 = $dead9 + 1;
|
||
$troopsdead10 = $dead10;
|
||
$troopsdead11 = $dead11;
|
||
|
||
// hero XP, player points and alliance points — extracted to calculateHeroXpAndPoints() [#155]
|
||
$totaldead_def = $this->calculateHeroXpAndPoints(
|
||
$alldead, $owndead,
|
||
[$dead1, $dead2, $dead3, $dead4, $dead5, $dead6, $dead7, $dead8, $dead9, $dead10, $dead11],
|
||
$targettribe, $att_tribe,
|
||
$Attacker, $AttackerHeroID, $Defender, $DefendersHeroID,
|
||
$toF, $from, $targetally, $ownally,
|
||
$heroxp, $defheroxp
|
||
);
|
||
|
||
// resources lootable after cranny protection — extracted to resolveResourcesAfterBattle() [#155]
|
||
$resAfter = $this->resolveResourcesAfterBattle($data, $isoasis, $conqureby, $owntribe, $targettribe, $crannySpy);
|
||
$totclay = $resAfter['totclay'];
|
||
$totiron = $resAfter['totiron'];
|
||
$totwood = $resAfter['totwood'];
|
||
$totcrop = $resAfter['totcrop'];
|
||
$avclay = $resAfter['avclay'];
|
||
$aviron = $resAfter['aviron'];
|
||
$avwood = $resAfter['avwood'];
|
||
$avcrop = $resAfter['avcrop'];
|
||
|
||
// bounty distributed across the resources available after cranny protection (extracted to applyBounty() [#155])
|
||
$steal = $this->applyBounty([$avwood, $avclay, $aviron, $avcrop], $battlepart['bounty']);
|
||
|
||
//chiefing village — extracted to handleConquest() [#155]
|
||
if (!isset($village_destroyed)) $village_destroyed = 0;
|
||
$chiefResult = $this->handleConquest($data, $type, $dead9, $traped9, $targettribe, $from, $to, $toF, $owntribe, $varray, $varray1, $battlepart, $isoasis, $village_destroyed, $chief_pic);
|
||
$info_chief = $chiefResult['info_chief'];
|
||
$chiefing_village = $chiefResult['chiefing_village'];
|
||
$village_destroyed = $chiefResult['village_destroyed'];
|
||
|
||
// attacker hero outcome (oasis conquest/loyalty, artifact claim, report line) — extracted to handleHeroPostBattle() [#155]
|
||
$this->handleHeroPostBattle($data, $from, $to, $dead11, $traped11, $heroxp, $hero_pic, $isoasis, $type, $targettribe, $info_cat, $info_hero, $can_destroy, $village_destroyed);
|
||
|
||
if ($DefenderID == 0) $natar = 0;
|
||
|
||
if (!empty($scout)) {
|
||
$info_spy = $this->buildScoutReport($data, $spy_pic, $isoasis, $targettribe, $crannySpy, $totwood, $totclay, $totiron, $totcrop);
|
||
}
|
||
|
||
// prisoners freed during conquest attacks — extracted to handlePrisoners() [#155]
|
||
$info_trap = empty($scout) ? $this->handlePrisoners($data, $from, $to, $ownally, $type, $totalsend_att, $totaldead_att, $totaltraped_att) : '';
|
||
$info_troop = ($totalsend_att - ($totaldead_att + (isset($totaltraped_att) ? $totaltraped_att : 0)) <= 0) ? 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,
|
||
$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 is skipped instead of fighting a phantom
|
||
// (now-deleted) village — see the guard at the top of the loop (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;
|
||
|
||
$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 sendreinfunitsComplete() {
|
||
global $bid23, $database, $technology, $battle;
|
||
|
||
$time = time();
|
||
$q = "
|
||
SELECT
|
||
`to`, `from`, moveid,
|
||
t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11
|
||
FROM
|
||
".TB_PREFIX."movement,
|
||
".TB_PREFIX."attacks
|
||
WHERE
|
||
".TB_PREFIX."movement.ref = ".TB_PREFIX."attacks.id
|
||
AND
|
||
".TB_PREFIX."movement.proc = 0
|
||
AND
|
||
".TB_PREFIX."movement.sort_type = 3
|
||
AND
|
||
".TB_PREFIX."attacks.attack_type = 2
|
||
AND
|
||
endtime < $time";
|
||
$dataarray = $database->query_return($q);
|
||
|
||
if ($dataarray && count($dataarray)) {
|
||
// preload village data
|
||
$vilIDs = [];
|
||
$tos = [];
|
||
$froms = [];
|
||
foreach($dataarray as $data) {
|
||
$vilIDs[$data['from']] = true;
|
||
$vilIDs[$data['to']] = true;
|
||
$tos[$data['to']] = true;
|
||
$froms[$data['from']] = true;
|
||
}
|
||
$vilIDs = array_keys($vilIDs);
|
||
$database->getProfileVillages($vilIDs, 5);
|
||
$database->getUnit($vilIDs);
|
||
$database->getEnforce(array_keys($tos), array_keys($froms));
|
||
$database->getVillageByWorldID($vilIDs);
|
||
|
||
// calculate reinforcements data
|
||
foreach($dataarray as $data) {
|
||
if (!$this->claimMovementRecord($data['moveid'])) {
|
||
continue;
|
||
}
|
||
|
||
$isoasis = $database->isVillageOases($data['to']);
|
||
if($isoasis == 0){
|
||
$to = $database->getMInfo($data['to']);
|
||
$toF = $database->getVillage($data['to']);
|
||
$DefenderID = $to['owner'];
|
||
$targettribe = $database->getUserField($DefenderID, "tribe", 0);
|
||
$conqureby = 0;
|
||
}else{
|
||
$to = $database->getOMInfo($data['to']);
|
||
$toF = $database->getOasisV($data['to']);
|
||
$DefenderID = $to['owner'];
|
||
$targettribe = $database->getUserField($DefenderID, "tribe", 0);
|
||
$conqureby = $toF['conqured'];
|
||
}
|
||
|
||
if($data['from'] == 0){
|
||
// flow 1: a "village of the elders" reinforcement returning home
|
||
$this->completeReinforcementFromElders($data, $to);
|
||
}else{
|
||
// flow 2: a standard reinforcement delivery (hero transfer + enforce merge + report)
|
||
$this->completeReinforcementDelivery($data, $to, $isoasis, $DefenderID);
|
||
}
|
||
|
||
//Update starvation data
|
||
$database->addStarvationData($data['to']);
|
||
|
||
//check empty reinforcement in rally point
|
||
$e_units = '';
|
||
for ($i = 1; $i <= 50; $i++) $e_units.= 'u'.$i.'= 0 AND ';
|
||
|
||
$e_units.= 'hero = 0';
|
||
$q = "DELETE FROM ".TB_PREFIX."enforcement WHERE ".$e_units." AND (vref=".(int) $data['to']." OR `from`=".(int) $data['to'].")";
|
||
$database->query($q);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Flow 1 of sendreinfunitsComplete(): a "village of the elders" reinforcement
|
||
// (from = 0) returning home. $targetally / $AttackArrivalTime are intentionally
|
||
// left undefined here, exactly as in the original inline code.
|
||
private function completeReinforcementFromElders($data, $to) {
|
||
global $database;
|
||
|
||
$DefenderID = $database->getVillageField($data['to'], "owner");
|
||
$database->addEnforce(['from' => $data['from'], 'to' => $data['to'], 't1' => 0, 't2' => 0, 't3' => 0, 't4' => 0, 't5' => 0, 't6' => 0, 't7' => 0, 't8' => 0, 't9' => 0, 't10' => 0, 't11' => 0]);
|
||
$reinf = $database->getEnforce($data['to'], $data['from']);
|
||
$database->modifyEnforce($reinf['id'], 31, 1, 1);
|
||
$data_fail = '0,0,4,1,0,0,0,0,0,0,0,0,0,0';
|
||
$database->addNotice($to['owner'], $to['wref'], (isset($targetally) ? $targetally : 0), 8, 'village of the elders reinforcement ' . addslashes($to['name']), $data_fail, $AttackArrivalTime);
|
||
}
|
||
|
||
// Flow 2 of sendreinfunitsComplete(): a standard reinforcement delivery. Handles
|
||
// the lone-hero transfer between own villages, the enforcement merge-or-create,
|
||
// and the reinforcement notices. $ownally / $targetally / $AttackArrivalTime are
|
||
// intentionally left undefined (guarded by isset()), exactly as in the original.
|
||
private function completeReinforcementDelivery($data, $to, $isoasis, $DefenderID) {
|
||
global $database;
|
||
|
||
//set base things
|
||
$from = $database->getMInfo($data['from']);
|
||
$fromF = $database->getVillage($data['from']);
|
||
$AttackerID = $from['owner'];
|
||
$owntribe = $database->getUserField($AttackerID,"tribe",0);
|
||
|
||
$HeroTransfer = $troopsPresent = 0;
|
||
for($i = 1;$i <= 10; $i++) {
|
||
if($data['t'.$i] > 0) {
|
||
$troopsPresent = 1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//check if the hero is present and we're not sending him to an occupied oasis
|
||
//only add hero if we're sending him alone
|
||
if($data['t11'] > 0 && !$isoasis && !$troopsPresent) {
|
||
//check if we're sending a hero between own villages
|
||
if($AttackerID == $DefenderID) {
|
||
//check if there's a Mansion at target village
|
||
if($this->getTypeLevel(37, $data['to']) > 0){
|
||
//don't reinforce, addunit instead
|
||
$database->modifyUnit($data['to'], ["hero"], [1], [1]);
|
||
$heroid = $database->getHeroField($DefenderID, 'heroid');
|
||
$database->modifyHero("wref", $data['to'], $heroid);
|
||
$HeroTransfer = 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
if($data['t11'] > 0 || $troopsPresent) {
|
||
$this->mergeOrCreateEnforcement($data, $owntribe, $HeroTransfer);
|
||
}
|
||
|
||
//send rapport
|
||
$unitssend_att = ''.$data['t1'].','.$data['t2'].','.$data['t3'].','.$data['t4'].','.$data['t5'].','.$data['t6'].','.$data['t7'].','.$data['t8'].','.$data['t9'].','.$data['t10'].','.$data['t11'].'';
|
||
$data_fail = ''.$from['wref'].','.$from['owner'].','.$owntribe.','.$unitssend_att.'';
|
||
|
||
|
||
if($isoasis == 0) $to_name = $to['name'];
|
||
else $to_name = "Oasis ".$database->getVillageField($to['conqured'],"name");
|
||
|
||
$database->addNotice($from['owner'],$from['wref'],(isset($ownally) ? $ownally : 0),8,''.addslashes($from['name']).' reinforcement '.addslashes($to_name).'',$data_fail,(isset($AttackArrivalTime) ? $AttackArrivalTime : time()));
|
||
if($from['owner'] != $to['owner']) {
|
||
$database->addNotice($to['owner'],$to['wref'],(isset($targetally) ? $targetally : 0),8,''.addslashes($from['name']).' reinforcement '.addslashes($to_name).'',$data_fail,(isset($AttackArrivalTime) ? $AttackArrivalTime : time()));
|
||
}
|
||
}
|
||
|
||
// Flow 3 of sendreinfunitsComplete(): merge the incoming troops into an existing
|
||
// enforcement record at the target, or create a new one. When the hero was already
|
||
// transferred as a unit ($HeroTransfer), his t11 is temporarily zeroed for the
|
||
// merge and restored afterwards, exactly as in the original.
|
||
private function mergeOrCreateEnforcement($data, $owntribe, $HeroTransfer) {
|
||
global $database;
|
||
|
||
$temphero = $data['t11'];
|
||
if ($HeroTransfer) $data['t11'] = 0;
|
||
//check if there is defence from town in to town
|
||
$check = $database->getEnforce($data['to'], $data['from']);
|
||
if (!isset($check['id'])) $database->addEnforce($data);
|
||
else
|
||
{
|
||
//yes
|
||
$start = ($owntribe - 1) * 10 + 1;
|
||
$end = ($owntribe * 10);
|
||
|
||
//add unit.
|
||
$t_units = '';
|
||
for($i = $start, $j = 1; $i <= $end; $i++, $j++)
|
||
{
|
||
$t_units .= "u".$i." = u".$i." + ".$data['t'.$j].(($j > 9) ? '' : ', ');
|
||
}
|
||
|
||
$q = "UPDATE ".TB_PREFIX."enforcement set $t_units where id =".(int) $check['id'];
|
||
$database->query($q);
|
||
$database->modifyEnforce($check['id'], 'hero', $data['t11'], 1);
|
||
}
|
||
$data['t11'] = $temphero;
|
||
}
|
||
|
||
private function returnunitsComplete() {
|
||
global $database, $technology;
|
||
|
||
$time = time();
|
||
$q = "
|
||
SELECT
|
||
`to`, `from`, moveid, starttime, endtime, wood, clay, iron, crop,
|
||
t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11
|
||
FROM
|
||
".TB_PREFIX."movement,
|
||
".TB_PREFIX."attacks
|
||
WHERE
|
||
".TB_PREFIX."movement.ref = ".TB_PREFIX."attacks.id
|
||
AND
|
||
".TB_PREFIX."movement.proc = 0
|
||
AND
|
||
".TB_PREFIX."movement.sort_type = 4
|
||
AND
|
||
endtime < $time";
|
||
$dataarray = $database->query_return($q);
|
||
|
||
if ($dataarray && count($dataarray)) {
|
||
// preload village data
|
||
$vilIDs = [];
|
||
foreach($dataarray as $data) {
|
||
$vilIDs[$data['from']] = true;
|
||
$vilIDs[$data['to']] = true;
|
||
}
|
||
$database->getProfileVillages(array_keys($vilIDs), 5);
|
||
$database->getOasisEnforce($vilIDs, 0);
|
||
$database->getOasisEnforce($vilIDs, 1);
|
||
|
||
foreach($dataarray as $data) {
|
||
if (!$this->claimMovementRecord($data['moveid'])) {
|
||
continue;
|
||
}
|
||
|
||
$tribe = $database->getUserField($database->getVillageField($data['to'], "owner"), "tribe", 0);
|
||
$u = $tribe == 1 ? "" : $tribe - 1;
|
||
$database->modifyUnit(
|
||
$data['to'],
|
||
[$u."1", $u."2", $u."3", $u."4", $u."5", $u."6", $u."7", $u."8", $u."9", $tribe."0", "hero"],
|
||
[$data['t1'], $data['t2'], $data['t3'], $data['t4'], $data['t5'], $data['t6'], $data['t7'], $data['t8'], $data['t9'], $data['t10'], $data['t11']],
|
||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
|
||
);
|
||
|
||
//If there's at least 1 resource, add it to the village
|
||
if($data['wood'] + $data['clay'] + $data['iron'] + $data['crop'] > 0){
|
||
$database->modifyResource($data['to'], $data['wood'], $data['clay'], $data['iron'], $data['crop'], 1);
|
||
}
|
||
|
||
//Update starvation data
|
||
$database->addStarvationData($data['to']);
|
||
}
|
||
|
||
$this->pruneResource();
|
||
}
|
||
|
||
// Settlers
|
||
$q = "SELECT `to`, moveid FROM ".TB_PREFIX."movement where ref = 0 and proc = '0' and sort_type = '4' and endtime < $time";
|
||
$dataarray = $database->query_return($q);
|
||
if ($dataarray && count($dataarray)) {
|
||
foreach($dataarray as $data) {
|
||
if (!$this->claimMovementRecord($data['moveid'])) {
|
||
continue;
|
||
}
|
||
|
||
$tribe = $database->getUserField($database->getVillageField($data['to'], "owner"), "tribe", 0);
|
||
$database->modifyUnit($data['to'], [$tribe."0"], [3], [1]);
|
||
|
||
//If a settling is canceled, add 750 for each resource type
|
||
$database->modifyResource($data['to'], 750, 750, 750, 750, 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
private function sendSettlersComplete() {
|
||
global $database;
|
||
|
||
$time = microtime(true);
|
||
$q = "SELECT `to`, `from`, moveid, starttime, ref FROM ".TB_PREFIX."movement where proc = 0 and sort_type = 5 and endtime < $time";
|
||
|
||
$dataarray = $database->query_return($q);
|
||
$fieldIDs = [];
|
||
$addUnitsWrefs = [];
|
||
$addTechWrefs = [];
|
||
$addABTechWrefs = [];
|
||
$time = microtime(true);
|
||
$types = [];
|
||
$froms = [];
|
||
$tos = [];
|
||
$refs = [];
|
||
$times = [];
|
||
$endtimes = [];
|
||
|
||
// preload village data
|
||
$vilIDs = [];
|
||
foreach($dataarray as $data) {
|
||
$vilIDs[$data['from']] = true;
|
||
$vilIDs[$data['to']] = true;
|
||
}
|
||
$vilIDs = array_keys($vilIDs);
|
||
$database->getProfileVillages($vilIDs, 5);
|
||
$database->getVillageByWorldID($vilIDs);
|
||
|
||
foreach($dataarray as $data) {
|
||
if (!$this->claimMovementRecord($data['moveid'])) {
|
||
continue;
|
||
}
|
||
|
||
$ownerID = $database->getUserField($database->getVillageField($data['from'], "owner"), "id", 0);
|
||
$to = $database->getMInfo($data['from']);
|
||
$user = addslashes($database->getUserField($to['owner'], 'username', 0));
|
||
$taken = $database->getVillageState($data['to']);
|
||
if($taken != 1){
|
||
$fieldIDs[] = $data['to'];
|
||
$database->addVillage($data['to'], $to['owner'], $user, '0');
|
||
$database->addResourceFields($data['to'], $database->getVillageType($data['to']));
|
||
$addUnitsWrefs[] = $data['to'];
|
||
$addTechWrefs[] = $data['to'];
|
||
$addABTechWrefs[] = $data['to'];
|
||
|
||
$exp1 = $database->getVillageField($data['from'], 'exp1');
|
||
$exp2 = $database->getVillageField($data['from'], 'exp2');
|
||
$exp3 = $database->getVillageField($data['from'], 'exp3');
|
||
|
||
if($exp1 == 0){
|
||
$exp = 'exp1';
|
||
$value = $data['to'];
|
||
}elseif($exp2 == 0){
|
||
$exp = 'exp2';
|
||
$value = $data['to'];
|
||
}else{
|
||
$exp = 'exp3';
|
||
$value = $data['to'];
|
||
}
|
||
|
||
$database->setVillageField($data['from'], $exp, $value);
|
||
|
||
// Report: new village founded (issue #178)
|
||
$ncoor = $database->getCoor($data['to']);
|
||
$database->addNotice($to['owner'], $data['to'], 0, 24, 'New village founded', ($ncoor['x'] ?? 0) . ',' . ($ncoor['y'] ?? 0), time());
|
||
}else{
|
||
// here must come movement from returning settlers
|
||
$types[] = 4;
|
||
$froms[] = $data['to'];
|
||
$tos[] = $data['from'];
|
||
$refs[] = $data['ref'];
|
||
$times[] = $time;
|
||
$endtimes[] = $time + ($time - $data['starttime']);
|
||
|
||
// Report: valley already occupied, settlers returning (issue #178)
|
||
$fcoor = $database->getCoor($data['to']);
|
||
$database->addNotice($to['owner'], $data['to'], 0, 25, 'Settlers returned - valley occupied', ($fcoor['x'] ?? 0) . ',' . ($fcoor['y'] ?? 0), time());
|
||
}
|
||
}
|
||
|
||
$database->addMovement($types, $froms, $tos, $refs, $times, $endtimes);
|
||
$database->setFieldTaken($fieldIDs);
|
||
$database->addUnits($addUnitsWrefs);
|
||
$database->addTech($addTechWrefs);
|
||
$database->addABTech($addABTechWrefs);
|
||
|
||
}
|
||
|
||
/**
|
||
* Create the Natars account and spawn artifacts
|
||
*
|
||
*/
|
||
|
||
private function spawnNatars(){
|
||
global $database;
|
||
|
||
//Check if Natars account is already created and if the time
|
||
//is come and we have to create Natars and spawn their artifacts
|
||
if($database->areArtifactsSpawned() || strtotime(START_DATE) + (NATARS_SPAWN_TIME * 86400) > time()) return;
|
||
|
||
//Create the Natars account and his capital
|
||
$this->artifacts->createNatars();
|
||
|
||
//Write the system message
|
||
$database->displaySystemMessage(ARTEFACT);
|
||
}
|
||
|
||
/**
|
||
* Spawn WW Villages
|
||
*
|
||
*/
|
||
|
||
private function spawnWWVillages(){
|
||
global $database;
|
||
|
||
//Check if Natars account has already been created, if WW villages have already been spawned
|
||
//and if it's the time to spawn them or not
|
||
if(!$database->areArtifactsSpawned() || $database->areWWVillagesSpawned() || strtotime(START_DATE) + (NATARS_WW_SPAWN_TIME * 86400) > time()) return;
|
||
|
||
//Create WW villages
|
||
$this->artifacts->createWWVillages();
|
||
|
||
//Write the system message
|
||
$database->displaySystemMessage(WWVILLAGEMSG);
|
||
}
|
||
|
||
/**
|
||
* Spawn WW Building plans
|
||
*
|
||
*/
|
||
|
||
private function spawnWWBuildingPlans(){
|
||
global $database;
|
||
|
||
//Check if Natars account is already spawned, if WW building plans have already been spawned
|
||
//and if it's the time to spawn them or not
|
||
if(!$database->areArtifactsSpawned() || $database->areArtifactsSpawned(true) || strtotime(START_DATE) + (NATARS_WW_BUILDING_PLAN_SPAWN_TIME * 86400) > time()) return;
|
||
|
||
//Create WW building plans
|
||
$this->artifacts->createWWBuildingPlans();
|
||
|
||
//Set the system message to contain the infos of the WW building plans
|
||
$database->displaySystemMessage(PLAN_INFO);
|
||
}
|
||
|
||
/**
|
||
* Automatically activate all artifacts that need to be activated
|
||
*
|
||
*/
|
||
|
||
private function activateArtifacts() {
|
||
global $database;
|
||
|
||
//Check if there's at least one artifact, if not, return
|
||
if(!$database->areArtifactsSpawned()) return;
|
||
|
||
//Activate the artifacts that need to be activated
|
||
$this->artifacts->activateArtifacts();
|
||
}
|
||
|
||
private function researchComplete() {
|
||
global $database;
|
||
|
||
$time = time();
|
||
$deleteIDs = [];
|
||
$tdata = [];
|
||
$abdata = [];
|
||
|
||
$q = "SELECT tech, vref, id FROM ".TB_PREFIX."research where timestamp < $time";
|
||
$dataarray = $database->query_return($q);
|
||
|
||
foreach($dataarray as $data) {
|
||
$sort_type = substr($data['tech'],0,1);
|
||
switch($sort_type) {
|
||
case "t":
|
||
if (!isset($tdata[$data['vref']])) $tdata[$data['vref']] = [];
|
||
$tdata[$data['vref']][] = $data['tech'].' = 1';
|
||
break;
|
||
case "a":
|
||
case "b":
|
||
if (!isset($abdata[$data['vref']])) $abdata[$data['vref']] = [];
|
||
$abdata[$data['vref']][] = $data['tech']." = ".$data['tech']." + 1";
|
||
break;
|
||
}
|
||
$deleteIDs[] = (int) $data['id'];
|
||
}
|
||
|
||
// execute queries with consolidated research data
|
||
if (count($tdata)) {
|
||
foreach ( $tdata as $vid => $preparedData ) {
|
||
$q = "UPDATE ".TB_PREFIX."tdata SET ".implode(', ', $preparedData)." WHERE vref = ".$vid;
|
||
$database->query($q);
|
||
}
|
||
}
|
||
|
||
if (count($abdata)) {
|
||
foreach ( $abdata as $vid => $preparedData ) {
|
||
$q = "UPDATE ".TB_PREFIX."abdata SET ".implode(', ', $preparedData)." WHERE vref = ".$vid;
|
||
$database->query($q);
|
||
}
|
||
}
|
||
|
||
if (count($deleteIDs)) {
|
||
$q = "DELETE FROM " . TB_PREFIX . "research where id IN(" . implode( ', ', $deleteIDs ) . ")";
|
||
$database->query( $q );
|
||
}
|
||
}
|
||
|
||
private function updateORes($bountywid) {
|
||
global $database;
|
||
|
||
$oasisInfoArray = $database->getOasisV($bountywid);
|
||
$timepast = time() - $oasisInfoArray['lastupdated'];
|
||
$nwood = (OASIS_WOOD_PRODUCTION / 3600) * $timepast;
|
||
$nclay = (OASIS_CLAY_PRODUCTION / 3600) * $timepast;
|
||
$niron = (OASIS_IRON_PRODUCTION / 3600) * $timepast;
|
||
$ncrop = (OASIS_CROP_PRODUCTION / 3600) * $timepast;
|
||
$database->modifyOasisResource($bountywid, $nwood, $nclay, $niron, $ncrop, 1);
|
||
$database->updateOasis($bountywid);
|
||
}
|
||
|
||
private function updateRes($bountywid) {
|
||
global $database, $technology;
|
||
|
||
//Get village infos
|
||
$villageInfoArray = $database->getVillage($bountywid);
|
||
|
||
//Get building and resource fields array
|
||
$resArray = $database->getResourceLevel($bountywid, false);
|
||
|
||
//Get oasis array
|
||
$oasisArray = $database->getOasis($bountywid);
|
||
|
||
//Get an array with the numbers of the oasis
|
||
$numberOfOasis = $this->bountysortOasis($oasisArray);
|
||
|
||
//Set the village population (if WW Villages, it's halved)
|
||
$villagePopulation = !$villageInfoArray['natar'] ? $villageInfoArray['pop'] : round($villageInfoArray['pop'] / 2);
|
||
|
||
//Get the upkeep of the village
|
||
$upkeep = $technology->getUpkeep($this->getAllUnits($bountywid), 0, $bountywid);
|
||
|
||
//Calculate the produced resources
|
||
$timepast = time() - $villageInfoArray['lastupdate'];
|
||
$nwood = ($this->bountyGetResourceProd($resArray, $numberOfOasis, 1) / 3600) * $timepast;
|
||
$nclay = ($this->bountyGetResourceProd($resArray, $numberOfOasis, 2) / 3600) * $timepast;
|
||
$niron = ($this->bountyGetResourceProd($resArray, $numberOfOasis, 3) / 3600) * $timepast;
|
||
$ncrop = (($this->bountyGetResourceProd($resArray, $numberOfOasis, 4) - $villagePopulation - $upkeep) / 3600) * $timepast;
|
||
$database->modifyResource($bountywid, $nwood, $nclay, $niron, $ncrop, 1);
|
||
$database->updateVillage($bountywid);
|
||
}
|
||
|
||
private function bountysortOasis($oasisArray) {
|
||
$crop = $clay = $wood = $iron = 0;
|
||
foreach ($oasisArray as $oasis) {
|
||
switch($oasis['type']) {
|
||
case 1:
|
||
case 2:
|
||
$wood++;
|
||
break;
|
||
case 3:
|
||
$wood++;
|
||
$crop++;
|
||
break;
|
||
case 4:
|
||
case 5:
|
||
$clay++;
|
||
break;
|
||
case 6:
|
||
$clay++;
|
||
$crop++;
|
||
break;
|
||
case 7:
|
||
case 8:
|
||
$iron++;
|
||
break;
|
||
case 9:
|
||
$iron++;
|
||
$crop++;
|
||
break;
|
||
case 10:
|
||
case 11:
|
||
$crop++;
|
||
break;
|
||
case 12:
|
||
$crop += 2;
|
||
break;
|
||
}
|
||
}
|
||
return [$wood, $clay, $iron, $crop];
|
||
}
|
||
|
||
function getAllUnits($base, $use_cache = true) {
|
||
global $database;
|
||
|
||
$ownunit = $database->getUnit($base, $use_cache);
|
||
$enforcementarray = $database->getEnforceVillage($base, 0);
|
||
|
||
if(count($enforcementarray) > 0){
|
||
foreach($enforcementarray as $enforce){
|
||
for($i = 1; $i <= 50; $i++){
|
||
$ownunit['u'.$i] += $enforce['u'.$i];
|
||
}
|
||
}
|
||
}
|
||
|
||
$enforceoasis = $database->getOasisEnforce($base, 0, $use_cache);
|
||
if(count($enforceoasis) > 0){
|
||
foreach($enforceoasis as $enforce){
|
||
for($i = 1; $i <= 50; $i++){
|
||
$ownunit['u'.$i] += $enforce['u'.$i];
|
||
}
|
||
}
|
||
}
|
||
|
||
$enforceoasis1 = $database->getOasisEnforce($base, 1, $use_cache);
|
||
if(count($enforceoasis1) > 0){
|
||
foreach($enforceoasis1 as $enforce){
|
||
for($i = 1; $i <= 50; $i++){
|
||
$ownunit['u'.$i] += $enforce['u'.$i];
|
||
}
|
||
}
|
||
}
|
||
|
||
$movement = $database->getVillageMovement($base);
|
||
if(!empty($movement)){
|
||
for($i = 1; $i <= 50; $i++){
|
||
if(!isset($ownunit['u' . $i])){
|
||
$ownunit['u'.$i] = 0;
|
||
}
|
||
|
||
$ownunit['u'.$i] += (isset($movement['u'.$i]) ? $movement['u'.$i] : 0);
|
||
}
|
||
}
|
||
|
||
$prisoners = $database->getPrisoners($base, 1);
|
||
if(!empty($prisoners)){
|
||
foreach($prisoners as $prisoner){
|
||
$owner = $database->getVillageField($base, "owner");
|
||
$ownertribe = $database->getUserField($owner, "tribe", 0);
|
||
$start = ($ownertribe - 1) * 10 + 1;
|
||
$end = ($ownertribe * 10);
|
||
for($i = $start; $i <= $end; $i++){
|
||
$j = $i - $start + 1;
|
||
$ownunit['u'.$i] += $prisoner['t'.$j];
|
||
}
|
||
$ownunit['hero'] += $prisoner['t11'];
|
||
}
|
||
}
|
||
return $ownunit;
|
||
}
|
||
|
||
// 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);
|
||
}
|
||
|
||
private function trainingComplete() {
|
||
global $database, $technology;
|
||
|
||
$time = time();
|
||
$trainlist = $database->getTrainingList();
|
||
if(count($trainlist) > 0){
|
||
// preload village data
|
||
$vilIDs = [];
|
||
foreach($trainlist as $train){
|
||
$vilIDs[$train['vref']] = true;
|
||
}
|
||
$vilIDs = array_keys($vilIDs);
|
||
$database->getProfileVillages($vilIDs, 5);
|
||
$database->cacheResourceLevels($vilIDs);
|
||
$database->getUnit($vilIDs);
|
||
$database->getEnforceVillage($vilIDs, 0 );
|
||
$database->getMovement(3, $vilIDs, 0);
|
||
$database->getMovement(4, $vilIDs, 1);
|
||
$database->getMovement(5, $vilIDs, 0);
|
||
$database->getOasisEnforce($vilIDs, 0);
|
||
$database->getOasisEnforce($vilIDs, 1);
|
||
$database->getPrisoners($vilIDs, 1);
|
||
|
||
// calculate training updates
|
||
foreach($trainlist as $train){
|
||
$timepast = $train['timestamp2'] - $time;
|
||
$pop = $train['pop'];
|
||
$valuesUpdated = false;
|
||
if($timepast <= 0 && $train['amt'] > 0) {
|
||
$valuesUpdated = true;
|
||
if($train['eachtime'] > 0){
|
||
$timepast2 = $time - $train['timestamp2'];
|
||
$trained = 1;
|
||
while($timepast2 >= $train['eachtime']){
|
||
$timepast2 -= $train['eachtime'];
|
||
$trained += 1;
|
||
}
|
||
|
||
if($trained > $train['amt']) $trained = $train['amt'];
|
||
}
|
||
else $trained = $train['amt'];
|
||
|
||
if($train['unit'] > 60 && $train['unit'] != 99){
|
||
$database->modifyUnit($train['vref'], [$train['unit'] - 60], [$trained], [1]);
|
||
}
|
||
else $database->modifyUnit($train['vref'], [$train['unit']], [$trained], [1]);
|
||
|
||
$database->updateTraining($train['id'], $trained, $trained * $train['eachtime']);
|
||
|
||
if($train['amt'] - $trained <= 0) $database->trainUnit($train['id'], 0, 0, 0, 0, 1);
|
||
}
|
||
|
||
if ($valuesUpdated) call_user_func(get_class($database).'::clearUnitsCache');
|
||
|
||
//Update starvation data
|
||
$database->addStarvationData($train['vref']);
|
||
}
|
||
}
|
||
}
|
||
|
||
private function getsort_typeLevel($tid, $resarray) {
|
||
$keyholder = [];
|
||
|
||
foreach(array_keys($resarray, $tid) as $key) {
|
||
if(strpos($key, 't')) {
|
||
$key = preg_replace("/[^0-9]/", '', $key);
|
||
array_push($keyholder, $key);
|
||
}
|
||
}
|
||
|
||
$element = count($keyholder);
|
||
if($element >= 2) {
|
||
if($tid <= 4) {
|
||
$temparray = [];
|
||
|
||
for($i = 0; $i <= $element - 1; $i++) {
|
||
array_push($temparray, $resarray['f'.$keyholder[$i]]);
|
||
}
|
||
|
||
foreach ($temparray as $key => $val) {
|
||
if ($val == max($temparray)) $target = $key;
|
||
}
|
||
}
|
||
}
|
||
else if($element == 1) $target = 0;
|
||
else return 0;
|
||
|
||
if(!empty($keyholder[$target])) return $resarray['f'.$keyholder[$target]];
|
||
else return 0;
|
||
}
|
||
|
||
private function celebrationComplete() {
|
||
global $database;
|
||
|
||
$varray = $database->getCel();
|
||
foreach($varray as $vil){
|
||
$id = $vil['wref'];
|
||
$type = $vil['type'];
|
||
$user = $vil['owner'];
|
||
$cp = ($type == 1) ? 500 : 2000;
|
||
$database->clearCel($id);
|
||
$database->setCelCp($user, $cp);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Expires Mead-Festivals (Brewery, building 35). Unlike celebrationComplete()
|
||
* this grants no reward — the festival only gated the temporary combat
|
||
* bonus / chief penalty / catapult randomization while it was active.
|
||
*/
|
||
private function festivalComplete() {
|
||
global $database;
|
||
|
||
$varray = $database->getFestivals();
|
||
foreach($varray as $vil){
|
||
$database->clearFestival($vil['wref']);
|
||
}
|
||
}
|
||
|
||
private function demolitionComplete() {
|
||
global $database;
|
||
|
||
$varray = $database->getDemolition();
|
||
foreach($varray as $vil) {
|
||
if ($vil['lvl'] < 0) {
|
||
$database->delDemolition($vil['vref'], true);
|
||
continue;
|
||
}
|
||
if ($vil['timetofinish'] <= time()) {
|
||
$type = $database->getFieldType($vil['vref'],$vil['buildnumber']);
|
||
$level = $database->getFieldLevel($vil['vref'],$vil['buildnumber']);
|
||
|
||
$newLevel = max(0, $level - 1);
|
||
|
||
$buildarray = $GLOBALS["bid".$type];
|
||
|
||
if ($type == 10 || $type == 38) {
|
||
$database->query("
|
||
UPDATE ".TB_PREFIX."vdata
|
||
SET
|
||
`maxstore` = IF(`maxstore` - ".$buildarray[$level]['attri']." <= ".STORAGE_BASE.", ".STORAGE_BASE.", `maxstore` - ".$buildarray[$level]['attri'].")
|
||
WHERE
|
||
wref=".(int) $vil['vref']);
|
||
}
|
||
|
||
if ($type == 11 || $type == 39) {
|
||
$database->query("
|
||
UPDATE ".TB_PREFIX."vdata
|
||
SET
|
||
`maxcrop` = IF(`maxcrop` - ".$buildarray[$level]['attri']." <= ".STORAGE_BASE.", ".STORAGE_BASE.", `maxcrop` - ".$buildarray[$level]['attri'].")
|
||
WHERE
|
||
wref=".(int) $vil['vref']);
|
||
}
|
||
|
||
if ($level == 1) $clear = ",f".$vil['buildnumber']."t=0";
|
||
else $clear = "";
|
||
|
||
if ($database->getVillageField($vil['vref'], 'natar') == 1 && $type == 40) $clear = ""; //fix by ronix - fixed by iopietro
|
||
$q = "UPDATE ".TB_PREFIX."fdata SET f".$vil['buildnumber']."=".$newLevel." ".$clear." WHERE vref=".(int)$vil['vref'];
|
||
$database->query($q);
|
||
|
||
$pop = $this->getPop($type, $newLevel);
|
||
$database->modifyPop($vil['vref'], $pop[0], 1);
|
||
$this->procClimbers($database->getVillageField($vil['vref'], 'owner'));
|
||
$database->delDemolition($vil['vref'], true);
|
||
|
||
if ($type == 18) Automation::updateMax($database->getVillageField($vil['vref'], 'owner'));
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
private function updateHero() {
|
||
global $database, $hero_levels;
|
||
|
||
$harray = $database->getHero();
|
||
if(!empty($harray)){
|
||
// first of all, prepare all unit data at once for these heroes
|
||
$heroVillageIDs = [];
|
||
foreach($harray as $hdata) {
|
||
$heroVillageIDs[] = $hdata['wref'];
|
||
}
|
||
|
||
// load data for those prepared IDs
|
||
$unitData = $database->getUnit($heroVillageIDs);
|
||
|
||
// now do the math
|
||
$lastUpdateIDs = [];
|
||
$timeNow = time();
|
||
foreach($harray as $hdata){
|
||
$columns = [];
|
||
$columnValues = [];
|
||
$modes = [];
|
||
$lastUpdateTime = $timeNow;
|
||
|
||
// 1. passive HP regeneration
|
||
$newHealth = $this->calculateHealthRegen($hdata);
|
||
|
||
// 2. level-up + score points
|
||
$this->mergeHeroColumns($columns, $columnValues, $modes, $this->calculateLevelUp($hdata));
|
||
|
||
// 3. revive completion (forces health to 100 and stamps the update time)
|
||
$revive = $this->handleReviveCompletion($hdata);
|
||
$this->mergeHeroColumns($columns, $columnValues, $modes, $revive);
|
||
if ($revive['health'] !== null) $newHealth = $revive['health'];
|
||
if ($revive['lastUpdate'] !== null) $lastUpdateTime = $revive['lastUpdate'];
|
||
|
||
// 4. training completion (does not touch health)
|
||
$training = $this->handleTrainingCompletion($hdata);
|
||
$this->mergeHeroColumns($columns, $columnValues, $modes, $training);
|
||
if ($training['lastUpdate'] !== null) $lastUpdateTime = $training['lastUpdate'];
|
||
|
||
// update health, if needed
|
||
if ($newHealth > -1) {
|
||
$columns[] = 'health';
|
||
$columnValues[] = $newHealth;
|
||
$modes[] = null;
|
||
}
|
||
|
||
if ($lastUpdateTime != $timeNow) {
|
||
// last update timestamp
|
||
$columns[] = 'lastupdate';
|
||
$columnValues[] = $lastUpdateTime;
|
||
$modes[] = null;
|
||
} else {
|
||
// leave same last update values for multiple heroes to the end
|
||
$lastUpdateIDs[] = $hdata['heroid'];
|
||
}
|
||
|
||
if (count($columns)) $database->modifyHero($columns, $columnValues, $hdata['heroid'], $modes);
|
||
}
|
||
|
||
if (count($lastUpdateIDs)) {
|
||
mysqli_query($database->dblink,"UPDATE " . TB_PREFIX . "hero SET lastupdate = $timeNow WHERE heroid IN(".implode(', ', $lastUpdateIDs).")");
|
||
}
|
||
}
|
||
}
|
||
|
||
// Append a hero column fragment (['columns'=>[], 'values'=>[], 'modes'=>[]])
|
||
// onto the running modifyHero() arrays, preserving order.
|
||
private function mergeHeroColumns(&$columns, &$columnValues, &$modes, $fragment) {
|
||
foreach ($fragment['columns'] as $k => $col) {
|
||
$columns[] = $col;
|
||
$columnValues[] = $fragment['values'][$k];
|
||
$modes[] = $fragment['modes'][$k];
|
||
}
|
||
}
|
||
|
||
// Passive HP regeneration: returns the new health value, or -1 if unchanged.
|
||
private function calculateHealthRegen($hdata) {
|
||
if((time()-$hdata['lastupdate']) >= 1){
|
||
if($hdata['health'] < 100 and $hdata['health'] > 0){
|
||
if(SPEED <= 10) $speed = SPEED;
|
||
else if(SPEED <= 100) $speed = ceil(SPEED / 10);
|
||
else $speed = ceil(SPEED / 100);
|
||
|
||
$reg = $hdata['health'] + $hdata['regeneration'] * 5 * $speed / 86400 * (time() - $hdata['lastupdate']);
|
||
|
||
return ($reg <= 100) ? $reg : 100;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
// Level-up + score points. Returns a column fragment.
|
||
private function calculateLevelUp($hdata) {
|
||
global $hero_levels;
|
||
|
||
$columns = [];
|
||
$values = [];
|
||
$modes = [];
|
||
|
||
$herolevel = $hdata['level'];
|
||
$newLevel = - 1;
|
||
$scorePoints = false;
|
||
for ($i = $herolevel + 1; $i < 100; $i++){
|
||
if($hdata['experience'] >= $hero_levels[$i]){
|
||
$newLevel = $i;
|
||
if ($i < 99) $scorePoints = true;
|
||
}
|
||
}
|
||
|
||
// upgrade hero to a new level, if needed
|
||
if ($newLevel > -1) {
|
||
$columns[] = 'level';
|
||
$values[] = $newLevel;
|
||
$modes[] = null;
|
||
}
|
||
|
||
// add as many points as needed, if we're below level 100
|
||
if ($scorePoints) {
|
||
$columns[] = 'points';
|
||
$values[] = (5 * ($newLevel - $herolevel));
|
||
$modes[] = 1;
|
||
}
|
||
|
||
return ['columns' => $columns, 'values' => $values, 'modes' => $modes, 'health' => null, 'lastUpdate' => null];
|
||
}
|
||
|
||
// Revive completion: marks the hero unit alive and clears the revive flag.
|
||
// Returns a column fragment plus the health (100) and lastUpdate overrides.
|
||
private function handleReviveCompletion($hdata) {
|
||
global $database;
|
||
|
||
if($hdata['trainingtime'] < time() && $hdata['inrevive'] == 1){
|
||
mysqli_query($database->dblink,"UPDATE " . TB_PREFIX . "units SET hero = 1 WHERE vref = ".(int) $hdata['wref']."");
|
||
|
||
return [
|
||
'columns' => ['dead', 'inrevive', 'inrevive'],
|
||
'values' => [0, 0, 0],
|
||
'modes' => [null, null, null],
|
||
'health' => 100,
|
||
'lastUpdate' => (int) $hdata['trainingtime'],
|
||
];
|
||
}
|
||
|
||
return ['columns' => [], 'values' => [], 'modes' => [], 'health' => null, 'lastUpdate' => null];
|
||
}
|
||
|
||
// Training completion: marks the hero unit alive and clears the training flag.
|
||
// Returns a column fragment plus the lastUpdate override (health untouched).
|
||
private function handleTrainingCompletion($hdata) {
|
||
global $database;
|
||
|
||
if($hdata['trainingtime'] < time() && $hdata['intraining'] == 1){
|
||
mysqli_query($database->dblink,"UPDATE " . TB_PREFIX . "units SET hero = 1 WHERE vref = ".(int) $hdata['wref']);
|
||
|
||
return [
|
||
'columns' => ['dead', 'intraining'],
|
||
'values' => [0, 0],
|
||
'modes' => [null, null],
|
||
'health' => null,
|
||
'lastUpdate' => (int) $hdata['trainingtime'],
|
||
];
|
||
}
|
||
|
||
return ['columns' => [], 'values' => [], 'modes' => [], 'health' => null, 'lastUpdate' => null];
|
||
}
|
||
|
||
private function updateStore() {
|
||
global $database, $bid10, $bid38, $bid11, $bid39;
|
||
|
||
$result = mysqli_query($database->dblink, 'SELECT * FROM `' . TB_PREFIX . 'fdata`');
|
||
|
||
mysqli_begin_transaction($database->dblink);
|
||
while ($row = mysqli_fetch_assoc($result))
|
||
{
|
||
$ress = $crop = 0;
|
||
for ($i = 19; $i < 40; ++$i)
|
||
{
|
||
//Warehouse
|
||
if ($row['f' . $i . 't'] == 10)
|
||
{
|
||
$ress += ((isset($bid10[$row['f' . $i]]) && isset($bid10[$row['f' . $i]]['attri'])) ? $bid10[$row['f' . $i]]['attri'] * STORAGE_MULTIPLIER : 0);
|
||
}
|
||
|
||
//Great warehouse
|
||
if ($row['f' . $i . 't'] == 38)
|
||
{
|
||
$ress += ((isset($bid38[$row['f' . $i]]) && isset($bid38[$row['f' . $i]]['attri'])) ? $bid38[$row['f' . $i]]['attri'] * STORAGE_MULTIPLIER : 0);
|
||
}
|
||
|
||
//Granary
|
||
if ($row['f' . $i . 't'] == 11)
|
||
{
|
||
$crop += ((isset($bid11[$row['f' . $i]]) && isset($bid11[$row['f' . $i]]['attri'])) ? $bid11[$row['f' . $i]]['attri'] * STORAGE_MULTIPLIER : 0);
|
||
}
|
||
|
||
//Great granary
|
||
if ($row['f' . $i . 't'] == 39)
|
||
{
|
||
$crop += ((isset($bid39[$row['f' . $i]]) && isset($bid39[$row['f' . $i]]['attri'])) ? $bid39[$row['f' . $i]]['attri'] * STORAGE_MULTIPLIER : 0);
|
||
}
|
||
}
|
||
|
||
// no need for update, since we didn't find any warehouses or granaries
|
||
// and maximums would have been set to correct values inside prune* functions already
|
||
if ($ress == 0 && $crop == 0) continue;
|
||
|
||
// maxstore nor maxcrop can go below the minimum threshold
|
||
if ($ress < STORAGE_BASE) $ress = STORAGE_BASE;
|
||
if ($crop < STORAGE_BASE) $crop = STORAGE_BASE;
|
||
|
||
mysqli_query($database->dblink,'UPDATE `' . TB_PREFIX . 'vdata` SET `maxstore` = ' . (int) $ress . ', `maxcrop` = ' . (int) $crop . ' WHERE `wref` = ' . (int) $row['vref']);
|
||
}
|
||
mysqli_commit($database->dblink);
|
||
}
|
||
|
||
private function checkInvitedPlayes() {
|
||
global $database;
|
||
|
||
$q = "SELECT id, invited FROM ".TB_PREFIX."users WHERE invited > 0";
|
||
$array = $database->query_return($q);
|
||
|
||
// preload villages data
|
||
$userIDs = [];
|
||
foreach($array as $user) {
|
||
$userIDs[] = $user['id'];
|
||
}
|
||
$database->getProfileVillages($userIDs);
|
||
|
||
// continue...
|
||
foreach($array as $user) {
|
||
$numusers = mysqli_fetch_array(mysqli_query($database->dblink,"SELECT Count(*) as Total FROM ".TB_PREFIX."users WHERE id = ".(int) $user['invited']), MYSQLI_ASSOC);
|
||
if($numusers['Total'] > 0){
|
||
$varray = count($database->getProfileVillages($user['id']));
|
||
if($varray > 1){
|
||
$usergold = $database->getUserField($user['invited'],"gold",0);
|
||
$gold = $usergold+50;
|
||
$database->updateUserField($user['invited'],"gold",$gold,1);
|
||
$database->updateUserField($user['id'],"invited",0,1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private function updateGeneralAttack() {
|
||
global $database;
|
||
|
||
mysqli_query($database->dblink, "
|
||
UPDATE ".TB_PREFIX."general
|
||
SET
|
||
shown = 0
|
||
WHERE
|
||
shown = 1 AND
|
||
`time` < (UNIX_TIMESTAMP() - (86400 * 8))");
|
||
}
|
||
|
||
private function MasterBuilder() {
|
||
global $database;
|
||
|
||
$q = "SELECT id, wid, type, level, field, timestamp FROM ".TB_PREFIX."bdata WHERE master = 1";
|
||
$array = $database->query_return($q);
|
||
|
||
foreach($array as $master) {
|
||
$owner = $database->getVillageField($master['wid'], 'owner');
|
||
$tribe = $database->getUserField($owner, 'tribe', 0);
|
||
$villwood = $database->getVillageField($master['wid'], 'wood');
|
||
$villclay = $database->getVillageField($master['wid'], 'clay');
|
||
$villiron = $database->getVillageField($master['wid'], 'iron');
|
||
$villcrop = $database->getVillageField($master['wid'], 'crop');
|
||
$type = $master['type'];
|
||
$level = $master['level'];
|
||
$buildarray = $GLOBALS["bid".$type];
|
||
$buildwood = $buildarray[$level]['wood'];
|
||
$buildclay = $buildarray[$level]['clay'];
|
||
$buildiron = $buildarray[$level]['iron'];
|
||
$buildcrop = $buildarray[$level]['crop'];
|
||
$ww = count($database->getBuildingByType($master['wid'], 40));
|
||
|
||
if($tribe == 1){
|
||
if($master['field'] < 19){
|
||
$bdata = $database->getDorf1Building($master['wid']);
|
||
$bdataTotal = count($bdata);
|
||
$bbdata = count($database->getDorf2Building($master['wid']));
|
||
}else{
|
||
$bdata = $database->getDorf2Building($master['wid']);
|
||
$bdataTotal = count($bdata);
|
||
$bbdata = count($database->getDorf1Building($master['wid']));
|
||
}
|
||
}else{
|
||
$bdata = array_merge($database->getDorf1Building($master['wid']), $database->getDorf2Building($master['wid']));
|
||
$bdataTotal = $bbdata = count($bdata);
|
||
}
|
||
|
||
if($database->getUserField($owner, 'plus', 0) > time() || $ww > 0){
|
||
if($bbdata < 2) $inbuild = 2;
|
||
else $inbuild = 1;
|
||
}
|
||
else $inbuild = 1;
|
||
|
||
$usergold = $database->getUserField($owner, 'gold', 0);
|
||
|
||
if($bdataTotal < $inbuild && $buildwood <= $villwood && $buildclay <= $villclay && $buildiron <= $villiron && $buildcrop <= $villcrop && $usergold > 0){
|
||
$time = $master['timestamp'] + time();
|
||
|
||
if(!empty($bdata)){
|
||
foreach($bdata as $masterLoop) $time += ($masterLoop['timestamp'] - time());
|
||
}
|
||
|
||
if($bdataTotal == 0) $database->updateBuildingWithMaster($master['id'], $time, 0);
|
||
else $database->updateBuildingWithMaster($master['id'], $time, 1);
|
||
|
||
$database->updateUserField($owner, 'gold', --$usergold, 1);
|
||
$database->modifyResource($master['wid'], $buildwood, $buildclay, $buildiron, $buildcrop, 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Function for starvation - by brainiacX and Shadow
|
||
* Rework by ronix
|
||
* Refactored by iopietro
|
||
*/
|
||
|
||
private function starvation() {
|
||
global $database, $technology;
|
||
|
||
// Starvation is disabled during the peace period (holidays).
|
||
if(PEACE) return;
|
||
|
||
$time = time();
|
||
|
||
// 1. Update starvation data for all villages.
|
||
$this->starvationUpdateAllVillages();
|
||
|
||
// 2. Load villages with negative production.
|
||
$starvarray = $this->starvationGetVillagesWithDeficit();
|
||
if (empty($starvarray)) return;
|
||
|
||
$vilIDs = array_column($starvarray, 'wref');
|
||
|
||
// 3. Prepare caches to reduce queries.
|
||
$this->starvationPrepareCaches($vilIDs);
|
||
|
||
foreach ($starvarray as $starv) {
|
||
$this->starvationProcessVillage($starv, $time);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Update the starvation table for all villages.
|
||
**/
|
||
|
||
private function starvationUpdateAllVillages() {
|
||
global $database;
|
||
$starvarray = $database->getProfileVillages(0, 7);
|
||
foreach($starvarray as $starv) {
|
||
$database->addStarvationData($starv['wref']);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Return villages with crop deficit.
|
||
**/
|
||
|
||
private function starvationGetVillagesWithDeficit() {
|
||
global $database;
|
||
return $database->getStarvation();
|
||
}
|
||
|
||
/**
|
||
* Prepare troop caches.
|
||
**/
|
||
|
||
private function starvationPrepareCaches(array $vilIDs) {
|
||
global $database;
|
||
$database->getEnforceVillage($vilIDs, 0);
|
||
$database->getOasisEnforce($vilIDs, 2);
|
||
$database->getOasisEnforce($vilIDs, 3);
|
||
$database->getPrisoners($vilIDs, 1);
|
||
$database->getMovement(3, $vilIDs, 0);
|
||
$database->getMovement(4, $vilIDs, 1);
|
||
}
|
||
|
||
/**
|
||
* Process a single village for starvation.
|
||
**/
|
||
|
||
private function starvationProcessVillage($starv, $time) {
|
||
global $database, $technology;
|
||
|
||
$unitarrays = $this->getAllUnits($starv['wref']);
|
||
// Original upkeep formula
|
||
$upkeep = $starv['pop'] + $technology->getUpkeep($unitarrays, 0, $starv['wref']);
|
||
|
||
$troopData = $this->starvationFindFirstTroopGroup($starv);
|
||
if (empty($troopData['troops'])) {
|
||
// No troops exist, only check reset.
|
||
$this->starvationCheckReset($starv, $upkeep);
|
||
return;
|
||
}
|
||
|
||
$starvingTroops = $troopData['troops'];
|
||
$type = $troopData['type'];
|
||
$subtype = $troopData['subtype'];
|
||
|
||
$timedif = $time - $starv['starvupdate'];
|
||
$cropProd = $database->getCropProdstarv($starv['wref']) - $starv['starv'];
|
||
|
||
if($cropProd < 0){
|
||
// Deficit calculation
|
||
$starvsec = (abs($cropProd) / 3600);
|
||
$difcrop = ($timedif * $starvsec);
|
||
$oldcrop = $database->getVillageField($starv['wref'], 'crop');
|
||
|
||
// Use granary crop first for consumption.
|
||
if ($oldcrop > 100) {
|
||
$difcrop = $difcrop - $oldcrop;
|
||
if($difcrop < 0){
|
||
$difcrop = 0;
|
||
$newcrop = $oldcrop - $difcrop;
|
||
$database->setVillageField($starv['wref'], 'crop', $newcrop);
|
||
}
|
||
}
|
||
|
||
if($difcrop > 0 && $oldcrop <= 0){
|
||
$this->starvationKillTroops($starv, $starvingTroops, $type, $subtype, $difcrop, $upkeep, $time);
|
||
}
|
||
}
|
||
|
||
$this->starvationCheckReset($starv, $upkeep);
|
||
}
|
||
|
||
/**
|
||
* Locate the first troop group eligible for starvation.
|
||
* Processing order: oasis enforcement → village enforcement → prisoners → own units → attacks.
|
||
**/
|
||
|
||
private function starvationFindFirstTroopGroup($starv) {
|
||
global $database;
|
||
|
||
$enforceArrays = [
|
||
$database->getOasisEnforce($starv['wref'], 2),
|
||
$database->getOasisEnforce($starv['wref'], 3),
|
||
$database->getEnforceVillage($starv['wref'], 2),
|
||
$database->getEnforceVillage($starv['wref'], 3)
|
||
];
|
||
|
||
$prisonerArrays = [$database->getPrisoners($starv['wref'], 1)];
|
||
$unitArrays = ($database->getUnitsNumber($starv['wref'], 0) > 0) ? [[$database->getUnit($starv['wref'])]] : [];
|
||
$attackArrays = [
|
||
$database->getMovement(3, $starv['wref'], 0),
|
||
$database->getMovement(4, $starv['wref'], 1)
|
||
];
|
||
|
||
$allTroopsArray = [$enforceArrays, $prisonerArrays, $unitArrays, $attackArrays];
|
||
|
||
foreach($allTroopsArray as $type => $allTroops) {
|
||
if(!empty($allTroops)){
|
||
foreach($allTroops as $subtype => $troops){
|
||
if(!empty($troops)){
|
||
return [
|
||
'troops' => reset($troops),
|
||
'type' => $type,
|
||
'subtype' => $subtype
|
||
];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return ['troops' => [], 'type' => null, 'subtype' => null];
|
||
}
|
||
|
||
/**
|
||
* Kill troops according to crop deficit.
|
||
**/
|
||
|
||
private function starvationKillTroops($starv, &$starvingTroops, $type, $subtype, $difcrop, $upkeep, $time) {
|
||
global $database;
|
||
|
||
$tribe = $database->getUserField(($type == 2) ? $starv['owner'] : $database->getVillageField($starvingTroops['from'], "owner"), "tribe", 0);
|
||
$special = in_array($type, [1, 3]);
|
||
$start = $special ? 1 : ($tribe - 1) * 10 + 1;
|
||
$end = $special ? 10 : $tribe * 10;
|
||
$utype = $special ? 't' : 'u';
|
||
$heroType = $special ? 't11' : 'hero';
|
||
|
||
$killedUnits = [];
|
||
$totalUnits = 0;
|
||
$counting = true;
|
||
|
||
while($difcrop > 0) {
|
||
$maxcount = $maxtype = 0;
|
||
for($i = $start; $i <= $end; $i++) {
|
||
$units = (isset($starvingTroops[$utype.$i]) ? $starvingTroops[$utype.$i] : 0);
|
||
if($counting) $totalUnits += $units;
|
||
if($units > $maxcount){
|
||
$maxcount = $units;
|
||
$maxtype = $i;
|
||
}
|
||
}
|
||
if($counting) $counting = false;
|
||
|
||
if($maxtype > 0){
|
||
$starvingTroops[$utype.$maxtype]--;
|
||
$killedUnits[$maxtype] = ($killedUnits[$maxtype] ?? 0) + 1;
|
||
// Original per-unit crop consumption formula unchanged.
|
||
$unitIndex = $special ? $maxtype + ($tribe - 1) * 10 : $maxtype;
|
||
$difcrop -= $GLOBALS['u'.$unitIndex]['crop'];
|
||
} else break;
|
||
}
|
||
|
||
$totalKilledUnits = array_sum($killedUnits);
|
||
$newCrop = 0;
|
||
|
||
// Determine whether the hero dies.
|
||
if($starvingTroops[$heroType] > 0 && ($totalUnits == 0 || $totalUnits == $totalKilledUnits)){
|
||
$totalKilledUnits += $starvingTroops[$heroType];
|
||
$totalUnits += $starvingTroops[$heroType];
|
||
$heroOwner = ($type == 2) ? $starv['owner'] : $database->getVillageField(($type == 3 && $subtype == 1) ? $starvingTroops['to'] : $starvingTroops['from'], "owner");
|
||
$heroInfo = $database->getHero($heroOwner)[0];
|
||
$database->modifyHero("dead", 1, $heroInfo['heroid']);
|
||
$database->modifyHero("health", 0, $heroInfo['heroid']);
|
||
$newCrop = $GLOBALS['h'.$heroInfo['unit'].'_full'][min($heroInfo['level'], 60)]['crop'] + $difcrop;
|
||
} else if($maxtype > 0) {
|
||
$newCrop = $GLOBALS['u'.$maxtype]['crop'];
|
||
}
|
||
|
||
if($totalKilledUnits > 0) {
|
||
$this->starvationApplyDatabaseChanges($starv, $starvingTroops, $type, $killedUnits, $totalUnits, $totalKilledUnits, $newCrop, $upkeep, $time);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Apply starvation changes to the database.
|
||
**/
|
||
|
||
private function starvationApplyDatabaseChanges($starv, $starvingTroops, $type, $killedUnits, $totalUnits, $totalKilledUnits, $newCrop, $upkeep, $time) {
|
||
global $database;
|
||
|
||
switch($type){
|
||
case 0: // enforce
|
||
if($totalKilledUnits < $totalUnits){
|
||
$database->modifyEnforce($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0);
|
||
} else {
|
||
$database->deleteReinf($starvingTroops['id']);
|
||
}
|
||
break;
|
||
case 1: // prisoners
|
||
if($totalKilledUnits < $totalUnits){
|
||
$database->modifyPrisoners($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0);
|
||
$database->modifyUnit($starvingTroops['wref'], ["99o"], [$totalKilledUnits], [0]);
|
||
} else {
|
||
$database->deletePrisoners($starvingTroops['id']);
|
||
$database->modifyUnit($starvingTroops['wref'], ["99o"], [$totalUnits], [0]);
|
||
}
|
||
break;
|
||
case 2: // own units
|
||
$database->modifyUnit($starv['wref'], array_keys($killedUnits), array_values($killedUnits), [0]);
|
||
break;
|
||
case 3: // moving attacks.
|
||
if($totalKilledUnits < $totalUnits){
|
||
$database->modifyAttack2($starvingTroops['id'], array_keys($killedUnits), array_values($killedUnits), 0);
|
||
} else {
|
||
$database->setMovementProc($starvingTroops['moveid']);
|
||
}
|
||
break;
|
||
}
|
||
|
||
$database->modifyResource($starv['wref'], 0, 0, 0, max($newCrop, 0), 1);
|
||
$database->setVillageField($starv['wref'], ['starv', 'starvupdate'], [$upkeep, $time]);
|
||
}
|
||
|
||
/**
|
||
* Verify whether starvation reset is allowed.
|
||
**/
|
||
|
||
private function starvationCheckReset($starv, $upkeep) {
|
||
global $database;
|
||
$crop = $database->getCropProdstarv($starv['wref'], false);
|
||
if ($crop > $upkeep) {
|
||
$database->setVillageFields($starv['wref'], ['starv', 'starvupdate'], [0, 0]);
|
||
}
|
||
}
|
||
|
||
private function procNewClimbers() {
|
||
global $database, $ranking;
|
||
|
||
$ranking->procRankArray();
|
||
$climbers = $ranking->getRank();
|
||
if(count($climbers) > 0){
|
||
$q = "SELECT week FROM ".TB_PREFIX."medal order by week DESC LIMIT 0, 1";
|
||
$result = mysqli_query($database->dblink,$q);
|
||
if(mysqli_num_rows($result)) {
|
||
$row = mysqli_fetch_assoc($result);
|
||
$week = $row['week'] + 1;
|
||
}
|
||
else $week = 1;
|
||
|
||
$q = "SELECT id FROM ".TB_PREFIX."users where oldrank = 0 and id > 5";
|
||
$array = $database->query_return($q);
|
||
foreach($array as $user){
|
||
$newrank = $ranking->getUserRank($user['id']);
|
||
if($week > 1){
|
||
for($i = $newrank + 1; $i < count($climbers); $i++) {
|
||
if(isset($climbers[$i]['userid'])){
|
||
$oldrank = $ranking->getUserRank($climbers[$i]['userid']);
|
||
$totalpoints = $oldrank - $climbers[$i]['oldrank'];
|
||
$database->removeclimberrankpop($climbers[$i]['userid'], $totalpoints);
|
||
$database->updateoldrank($climbers[$i]['userid'], $oldrank);
|
||
}
|
||
}
|
||
$database->updateoldrank($user['id'], $newrank);
|
||
}else{
|
||
$totalpoints = count($climbers) - $newrank;
|
||
$database->setclimberrankpop($user['id'], $totalpoints);
|
||
$database->updateoldrank($user['id'], $newrank);
|
||
for($i = 1; $i < $newrank; $i++){
|
||
if(isset($climbers[$i]['userid'])){
|
||
$oldrank = $ranking->getUserRank($climbers[$i]['userid']);
|
||
$totalpoints = count($climbers) - $oldrank;
|
||
$database->setclimberrankpop($climbers[$i]['userid'], $totalpoints);
|
||
$database->updateoldrank($climbers[$i]['userid'], $oldrank);
|
||
}
|
||
}
|
||
for($i = $newrank + 1; $i < count($climbers); $i++){
|
||
if(isset($climbers[$i]['userid'])){
|
||
$oldrank = $ranking->getUserRank($climbers[$i]['userid']);
|
||
$totalpoints = count($climbers) - $oldrank;
|
||
$database->setclimberrankpop($climbers[$i]['userid'], $totalpoints);
|
||
$database->updateoldrank($climbers[$i]['userid'], $oldrank);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private function procClimbers($uid) {
|
||
global $database, $ranking;
|
||
|
||
$ranking->procRankArray();
|
||
$climbers = $ranking->getRank();
|
||
if(count($ranking->getRank()) > 0){
|
||
$q = "SELECT week FROM ".TB_PREFIX."medal order by week DESC LIMIT 0, 1";
|
||
$result = mysqli_query($database->dblink,$q);
|
||
if(mysqli_num_rows($result)) {
|
||
$row = mysqli_fetch_assoc($result);
|
||
$week = $row['week'] + 1;
|
||
}
|
||
else $week = 1;
|
||
|
||
$myrank = $ranking->getUserRank($uid);
|
||
if(isset($climbers[$myrank]['oldrank']) && $climbers[$myrank]['oldrank'] > $myrank){
|
||
for($i = $myrank + 1; $i <= $climbers[$myrank]['oldrank']; $i++) {
|
||
if(isset($climbers[$i]['oldrank'])){
|
||
$oldrank = $ranking->getUserRank($climbers[$i]['userid']);
|
||
if($week > 1){
|
||
$totalpoints = $oldrank - $climbers[$i]['oldrank'];
|
||
$database->removeclimberrankpop($climbers[$i]['userid'], $totalpoints);
|
||
$database->updateoldrank($climbers[$i]['userid'], $oldrank);
|
||
}else{
|
||
$totalpoints = count($ranking->getRank()) - $oldrank;
|
||
$database->setclimberrankpop($climbers[$i]['userid'], $totalpoints);
|
||
$database->updateoldrank($climbers[$i]['userid'], $oldrank);
|
||
}
|
||
}
|
||
}
|
||
if(isset($climbers[$myrank]['oldrank'])){
|
||
if($week > 1){
|
||
$totalpoints = $climbers[$myrank]['oldrank'] - $myrank;
|
||
$database->addclimberrankpop($climbers[$myrank]['userid'], $totalpoints);
|
||
$database->updateoldrank($climbers[$myrank]['userid'], $myrank);
|
||
}else{
|
||
$totalpoints = count($ranking->getRank()) - $myrank;
|
||
$database->setclimberrankpop($climbers[$myrank]['userid'], $totalpoints);
|
||
$database->updateoldrank($climbers[$myrank]['userid'], $myrank);
|
||
}
|
||
}
|
||
}else if(isset($climbers[$myrank]['oldrank']) && $climbers[$myrank]['oldrank'] < $myrank){
|
||
for($i = $climbers[$myrank]['oldrank']; $i < $myrank; $i++) {
|
||
if(isset($climbers[$i]['oldrank'])){
|
||
$oldrank = $ranking->getUserRank($climbers[$i]['userid']);
|
||
if($week > 1){
|
||
$totalpoints = $climbers[$i]['oldrank'] - $oldrank;
|
||
$database->addclimberrankpop($climbers[$i]['userid'], $totalpoints);
|
||
$database->updateoldrank($climbers[$i]['userid'], $oldrank);
|
||
}else{
|
||
$totalpoints = count($ranking->getRank()) - $oldrank;
|
||
$database->setclimberrankpop($climbers[$i]['userid'], $totalpoints);
|
||
$database->updateoldrank($climbers[$i]['userid'], $oldrank);
|
||
}
|
||
}
|
||
}
|
||
if(isset($climbers[$myrank-1]['oldrank'])){
|
||
if($week > 1){
|
||
$totalpoints = $myrank - $climbers[$myrank-1]['oldrank'];
|
||
$database->removeclimberrankpop($climbers[$myrank-1]['userid'], $totalpoints);
|
||
$database->updateoldrank($climbers[$myrank-1]['userid'], $myrank);
|
||
}else{
|
||
$totalpoints = count($ranking->getRank()) - $myrank;
|
||
$database->setclimberrankpop($climbers[$myrank-1]['userid'], $totalpoints);
|
||
$database->updateoldrank($climbers[$myrank-1]['userid'], $myrank);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
$ranking->procARankArray();
|
||
$aid = $database->getUserField($uid,"alliance",0);
|
||
if(count($ranking->getRank()) > 0 && $aid != 0){
|
||
$ally = $database->getAlliance($aid);
|
||
$memberlist = $database->getAllMember($ally['id']);
|
||
$oldrank = 0;
|
||
|
||
$memberIDs = [];
|
||
foreach($memberlist as $member) {
|
||
$memberIDs[] = $member['id'];
|
||
}
|
||
$data = $database->getVSumField($memberIDs,"pop");
|
||
|
||
if (count($data)) {
|
||
foreach ($data as $row) {
|
||
$oldrank += $row['Total'];
|
||
}
|
||
}
|
||
|
||
if($ally['oldrank'] != $oldrank){
|
||
if($ally['oldrank'] < $oldrank) {
|
||
$totalpoints = $oldrank - $ally['oldrank'];
|
||
$database->addclimberrankpopAlly($ally['id'], $totalpoints);
|
||
$database->updateoldrankAlly($ally['id'], $oldrank);
|
||
} else
|
||
if($ally['oldrank'] > $oldrank) {
|
||
$totalpoints = $ally['oldrank'] - $oldrank;
|
||
$database->removeclimberrankpopAlly($ally['id'], $totalpoints);
|
||
$database->updateoldrankAlly($ally['id'], $oldrank);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private function checkBan() {
|
||
global $database;
|
||
|
||
mysqli_query($database->dblink, "
|
||
UPDATE ".TB_PREFIX."banlist as b
|
||
JOIN ".TB_PREFIX."users as u ON b.uid = u.id
|
||
SET
|
||
b.active = 0,
|
||
u.access = 2
|
||
WHERE
|
||
b.active = 1 AND
|
||
b.`end` < UNIX_TIMESTAMP() AND
|
||
b.`end` > 0");
|
||
}
|
||
|
||
private function regenerateOasisTroops() {
|
||
global $database;
|
||
|
||
$timeFinal = time() - NATURE_REGTIME;
|
||
$q = "SELECT wref FROM " . TB_PREFIX . "odata where conqured = 0 and lastupdated2 < $timeFinal";
|
||
$array = $database->query_return($q);
|
||
if (count($array)) {
|
||
$ids = [];
|
||
foreach($array as $oasis) $ids[] = $oasis['wref'];
|
||
$database->regenerateOasisUnits($ids, true);
|
||
}
|
||
}
|
||
|
||
public static function updateMax($leader) {
|
||
global $bid18, $database;
|
||
|
||
$q = mysqli_fetch_array(mysqli_query($database->dblink,"SELECT Count(*) as Total FROM " . TB_PREFIX . "alidata where leader = ". (int) $leader), MYSQLI_ASSOC);
|
||
if ($q['Total'] > 0) {
|
||
$villages = $database->getVillagesID2($leader);
|
||
$max = 0;
|
||
|
||
// cache resource levels
|
||
$vilIDs = [];
|
||
foreach($villages as $village){
|
||
$vilIDs[$village['wref']] = true;
|
||
}
|
||
$database->cacheResourceLevels(array_keys($vilIDs));
|
||
|
||
foreach($villages as $village){
|
||
$field = $database->getResourceLevel($village['wref'], false);
|
||
for($i = 19; $i <= 40; $i++){
|
||
if($field['f'.$i.'t'] == 18){
|
||
$level = $field['f'.$i];
|
||
$attri = $bid18[$level]['attri'];
|
||
}
|
||
}
|
||
if($attri > $max){
|
||
$max = $attri;
|
||
}
|
||
}
|
||
$q = "UPDATE ".TB_PREFIX."alidata set max = ".(int) $max." where leader = ".(int) $leader;
|
||
$database->query($q);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Function for automate medals - by yi12345 and Shadow
|
||
*
|
||
*/
|
||
|
||
function medals() {
|
||
global $database;
|
||
|
||
// Check the timing window; $time is the next "last awarded" stamp to write.
|
||
$time = $this->shouldAwardMedalsNow();
|
||
if ($time === null) {
|
||
return;
|
||
}
|
||
|
||
// Exclude BANNED (0), MH (8), ADMIN (9)
|
||
$userFilter = "id > 5 AND access NOT IN (0,8,9)";
|
||
|
||
$week = $this->getNextMedalWeek('medal');
|
||
$allyweek = $this->getNextMedalWeek('allimedal');
|
||
|
||
$this->awardPlayerMedals($week, $userFilter);
|
||
$this->resetWeeklyStats('users', $userFilter, "ap=0, dp=0, Rc=0, clp=0, RR=0");
|
||
$this->awardAllianceMedals($allyweek);
|
||
$this->resetWeeklyStats('alidata', '', "ap=0, dp=0, RR=0, clp=0");
|
||
|
||
// Update last awarded time
|
||
$database->query("UPDATE ".TB_PREFIX."config SET lastgavemedal=".(int)$time);
|
||
}
|
||
|
||
// Returns the next "lastgavemedal" stamp to write if medals are due now, or
|
||
// null to skip this run. On the very first run after server start it only
|
||
// schedules the next run (and returns null).
|
||
private function shouldAwardMedalsNow() {
|
||
global $database;
|
||
|
||
$giveMedal = false;
|
||
$time = null;
|
||
$q = "SELECT lastgavemedal FROM ".TB_PREFIX."config";
|
||
$result = mysqli_query($database->dblink, $q);
|
||
if ($result) {
|
||
$row = mysqli_fetch_assoc($result);
|
||
$stime = strtotime(START_DATE) - strtotime(date('d.m.Y')) + strtotime(START_TIME);
|
||
|
||
if ($row['lastgavemedal'] == 0 && $stime < time()) {
|
||
// First run after server start - schedule next run
|
||
$setDays = round(MEDALINTERVAL / 86400);
|
||
$newtime = $setDays < 7? strtotime(($setDays + 1).' day midnight') : strtotime('next monday');
|
||
$database->query("UPDATE ".TB_PREFIX."config SET lastgavemedal = ".(int)$newtime);
|
||
} elseif ($row['lastgavemedal']!= 0) {
|
||
$time = $row['lastgavemedal'] + MEDALINTERVAL;
|
||
$giveMedal = $row['lastgavemedal'] < time();
|
||
}
|
||
}
|
||
|
||
if (!($giveMedal && MEDALINTERVAL > 0)) {
|
||
return null;
|
||
}
|
||
return $time;
|
||
}
|
||
|
||
// Next week number for a medal table (medal / allimedal).
|
||
private function getNextMedalWeek($table) {
|
||
global $database;
|
||
|
||
$q = "SELECT week FROM ".TB_PREFIX."$table ORDER BY week DESC LIMIT 1";
|
||
$res = mysqli_query($database->dblink, $q);
|
||
return $res && mysqli_num_rows($res)? (mysqli_fetch_assoc($res)['week'] + 1) : 1;
|
||
}
|
||
|
||
// Insert a user medal row.
|
||
private function insertUserMedal($userid, $category, $place, $week, $points, $img) {
|
||
global $database;
|
||
|
||
$q = "INSERT INTO ".TB_PREFIX."medal (userid, categorie, plaats, week, points, img) VALUES (".
|
||
(int)$userid.", ".(int)$category.", ".(int)$place.", ".(int)$week.", '".mysqli_real_escape_string($database->dblink, $points)."', '".mysqli_real_escape_string($database->dblink, $img)."')";
|
||
mysqli_query($database->dblink, $q);
|
||
}
|
||
|
||
// Insert an alliance medal row.
|
||
private function insertAllianceMedal($allyid, $cat, $place, $week, $points, $img) {
|
||
global $database;
|
||
|
||
$q = "INSERT INTO ".TB_PREFIX."allimedal (allyid, categorie, plaats, week, points, img) VALUES (".(int)$allyid.", '".(int)$cat."', ".(int)$place.", '".(int)$week."', '".mysqli_real_escape_string($database->dblink, $points)."', '".mysqli_real_escape_string($database->dblink, $img)."')";
|
||
mysqli_query($database->dblink, $q);
|
||
}
|
||
|
||
// Award the top 10 users for a stat field (one category).
|
||
private function awardTopMedals($field, $category, $imgPrefix, $week, $userFilter) {
|
||
global $database;
|
||
|
||
$q = "SELECT id, $field FROM ".TB_PREFIX."users WHERE $userFilter ORDER BY $field DESC, id DESC LIMIT 10";
|
||
$res = mysqli_query($database->dblink, $q);
|
||
$i = 0;
|
||
while ($row = mysqli_fetch_array($res)) {
|
||
$i++;
|
||
$this->insertUserMedal($row['id'], $category, $i, $week, $row[$field], $imgPrefix.$i);
|
||
}
|
||
}
|
||
|
||
// Award milestone ribbons for 3/5/10 appearances in a top3 or top10 category.
|
||
private function awardMilestoneMedals($field, $sourceCat, $topLimit, $targetCat, $imgBase, $week, $userFilter) {
|
||
global $database;
|
||
|
||
$res = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."users WHERE $userFilter ORDER BY $field DESC, id DESC LIMIT 10");
|
||
while ($u = mysqli_fetch_array($res)) {
|
||
$cnt = mysqli_fetch_row(mysqli_query($database->dblink,
|
||
"SELECT COUNT(*) FROM ".TB_PREFIX."medal WHERE userid=".(int)$u['id']." AND categorie=".(int)$sourceCat." AND plaats<=".(int)$topLimit))[0];
|
||
|
||
$map = ['3' => ['Three', $imgBase.'0_1'], '5' => ['Five', $imgBase.'1_1'], '10' => ['Ten', $imgBase.'2_1']];
|
||
if (isset($map[$cnt])) {
|
||
$this->insertUserMedal($u['id'], $targetCat, 0, $week, $map[$cnt][0], $map[$cnt][1]);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Player medals: top 10 of each category, the attack+defense bonus, then milestones.
|
||
private function awardPlayerMedals($week, $userFilter) {
|
||
global $database;
|
||
|
||
// Top 10 for each category
|
||
$this->awardTopMedals('ap', 1, 't2_', $week, $userFilter); // Attackers of the week (cat 1)
|
||
$this->awardTopMedals('dp', 2, 't3_', $week, $userFilter); // Defenders of the week (cat 2)
|
||
$this->awardTopMedals('Rc', 3, 't1_', $week, $userFilter); // Climbers of the week (cat 3)
|
||
$this->awardTopMedals('clp', 10, 't6_', $week, $userFilter); // Rank climbers of the week (cat 10)
|
||
$this->awardTopMedals('RR', 4, 't4_', $week, $userFilter); // Robbers of the week (cat 4)
|
||
|
||
// --- Bonus: player in both top10 attack AND defense (cat 5) ---
|
||
$topAttackers = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."users WHERE $userFilter ORDER BY ap DESC, id DESC LIMIT 10");
|
||
while ($a = mysqli_fetch_array($topAttackers)) {
|
||
$topDefenders = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."users WHERE $userFilter ORDER BY dp DESC, id DESC LIMIT 10");
|
||
while ($d = mysqli_fetch_array($topDefenders)) {
|
||
if ($a['id'] == $d['id']) {
|
||
$cnt = mysqli_fetch_row(mysqli_query($database->dblink, "SELECT COUNT(*) FROM ".TB_PREFIX."medal WHERE userid=".(int)$a['id']." AND categorie=5"))[0];
|
||
if ($cnt <= 2) {
|
||
$texts = [0 => '', 1 => 'twice ', 2 => 'three times '];
|
||
$this->insertUserMedal($a['id'], 5, 0, $week, $texts[$cnt], 't22'.$cnt.'_1');
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// --- Milestone ribbons for 3/5/10 times in top3 or top10 ---
|
||
// Attackers milestones
|
||
$this->awardMilestoneMedals('ap', 1, 3, 6, 't12', $week, $userFilter); // top3 attackers
|
||
$this->awardMilestoneMedals('ap', 1, 10, 12, 't13', $week, $userFilter); // top10 attackers
|
||
// Defenders milestones
|
||
$this->awardMilestoneMedals('dp', 2, 3, 7, 't14', $week, $userFilter);
|
||
$this->awardMilestoneMedals('dp', 2, 10, 13, 't15', $week, $userFilter);
|
||
// Climbers milestones
|
||
$this->awardMilestoneMedals('Rc', 3, 3, 8, 't10', $week, $userFilter);
|
||
$this->awardMilestoneMedals('Rc', 3, 10, 14, 't11', $week, $userFilter);
|
||
// Rank climbers milestones
|
||
$this->awardMilestoneMedals('clp', 10, 3, 11, 't20', $week, $userFilter);
|
||
$this->awardMilestoneMedals('clp', 10, 10, 16, 't21', $week, $userFilter);
|
||
// Robbers milestones
|
||
$this->awardMilestoneMedals('RR', 4, 3, 9, 't16', $week, $userFilter);
|
||
$this->awardMilestoneMedals('RR', 4, 10, 15, 't17', $week, $userFilter);
|
||
}
|
||
|
||
// Alliance medals: top 10 of each category, then the attack+defense bonus.
|
||
private function awardAllianceMedals($allyweek) {
|
||
global $database;
|
||
|
||
$allyCats = [
|
||
['ap', 1, 'a2_'],
|
||
['dp', 2, 'a3_'],
|
||
['RR', 4, 'a4_'],
|
||
['clp', 3, 'a1_']
|
||
];
|
||
foreach ($allyCats as [$field, $cat, $img]) {
|
||
$res = mysqli_query($database->dblink, "SELECT id, $field FROM ".TB_PREFIX."alidata ORDER BY $field DESC, id DESC LIMIT 10");
|
||
$i = 0;
|
||
while ($r = mysqli_fetch_array($res)) { $i++; $this->insertAllianceMedal($r['id'], $cat, $i, $allyweek, $r[$field], $img.$i); }
|
||
}
|
||
|
||
// Alliance bonus for attack+defense
|
||
$resA = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."alidata ORDER BY ap DESC, id DESC LIMIT 10");
|
||
while ($a = mysqli_fetch_array($resA)) {
|
||
$resD = mysqli_query($database->dblink, "SELECT id FROM ".TB_PREFIX."alidata ORDER BY dp DESC, id DESC LIMIT 10");
|
||
while ($d = mysqli_fetch_array($resD)) {
|
||
if ($a['id'] == $d['id']) {
|
||
$cnt = mysqli_fetch_row(mysqli_query($database->dblink, "SELECT COUNT(*) FROM ".TB_PREFIX."allimedal WHERE allyid=".(int)$a['id']." AND categorie=5"))[0];
|
||
if ($cnt <= 2) {
|
||
$texts = [0 => '', 1 => 'twice ', 2 => 'three times '];
|
||
$this->insertAllianceMedal($a['id'], 5, 0, $allyweek, $texts[$cnt], 't22'.$cnt.'_1');
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Reset the weekly stat columns for every id in a table (optionally filtered).
|
||
private function resetWeeklyStats($table, $where, $setClause) {
|
||
global $database;
|
||
|
||
$ids = [];
|
||
$sql = "SELECT id FROM ".TB_PREFIX.$table;
|
||
if ($where !== '') $sql .= " WHERE ".$where;
|
||
$res = mysqli_query($database->dblink, $sql);
|
||
while ($r = mysqli_fetch_row($res)) { $ids[] = (int)$r[0]; }
|
||
if ($ids) {
|
||
mysqli_query($database->dblink, "UPDATE ".TB_PREFIX.$table." SET ".$setClause." WHERE id IN(".implode(',', $ids).")");
|
||
}
|
||
}
|
||
|
||
private function artefactOfTheFool() {
|
||
global $database;
|
||
|
||
$time = time();
|
||
$q = "SELECT id, size FROM " . TB_PREFIX . "artefacts where type = 8 AND active = 1 AND del = 0 AND lastupdate <= ".($time - (86400 / (SPEED == 2 ? 1.5 : (SPEED == 3 ? 2 : SPEED))));
|
||
$array = $database->query_return($q);
|
||
if ($array) {
|
||
foreach($array as $artefact) {
|
||
$kind = rand(1, 7);
|
||
|
||
while($kind == 6) $kind = rand(1, 7);
|
||
|
||
if($artefact['size'] != 3) $bad_effect = rand(0, 1);
|
||
else $bad_effect = 0;
|
||
|
||
switch($kind) {
|
||
case 1:
|
||
$effect = rand(1, 5);
|
||
break;
|
||
case 2:
|
||
$effect = rand(1, 3);
|
||
break;
|
||
case 3:
|
||
$effect = rand(3, 10);
|
||
break;
|
||
case 4:
|
||
case 5:
|
||
$effect = rand(2, 4);
|
||
break;
|
||
case 7:
|
||
$effect = rand(1, 6);
|
||
break;
|
||
}
|
||
mysqli_query($database->dblink,"UPDATE ".TB_PREFIX."artefacts SET kind = ". (int) $kind. ", bad_effect = $bad_effect, effect2 = $effect, lastupdate = $time WHERE id = ".(int) $artefact['id']);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
$automation = new Automation;
|
||
|
||
// remove automation lock file
|
||
@unlink( AUTOMATION_LOCK_FILE_NAME );
|
||
?>
|