mirror of
https://github.com/Shadowss/TravianZ.git
synced 2026-07-21 20:16:11 +00:00
d89ffe4482
### Core Architecture Refactor - Split the `Database.php` class into domain-specific traits under `GameEngine/Database/`. - Split the `Automation` class into domain-specific traits under `GameEngine/Automation/`. - Grouped methods by functional domain for improved maintainability and navigation. - Preserved 100% backward compatibility. - No logic or behavioral changes. - Pure structural refactor.
291 lines
11 KiB
PHP
291 lines
11 KiB
PHP
<?php
|
|
|
|
#################################################################################
|
|
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
|
|
## --------------------------------------------------------------------------- ##
|
|
## Project: TravianZ ##
|
|
## Filename: AutomationStarvation.php ##
|
|
## Split&Refactor Shadow ##
|
|
## Purpose: Complete starvation pipeline (starvation*) ##
|
|
## ##
|
|
## Phase S2: Trait extracted from GameEngine/Automation.php ##
|
|
## (Automation class). ##
|
|
## Methods were moved IDENTICALLY, with no logic changes. ##
|
|
## ##
|
|
## License: TravianZ Project ##
|
|
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
|
|
## URLs: https://travianz.org ##
|
|
## https://github.com/Shadowss/TravianZ ##
|
|
#################################################################################
|
|
|
|
trait AutomationStarvation {
|
|
|
|
|
|
/**
|
|
* 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]);
|
|
}
|
|
}
|
|
}
|