mirror of
https://github.com/Shadowss/TravianZ.git
synced 2026-07-24 05:26:19 +00:00
Last fix for Hero T4 & V11
/* |-------------------------------------------------------------------------- | FIX List |-------------------------------------------------------------------------- | | 1. Hero Attribute Points | - When adding attribute points to the hero, the page currently refreshes | after every click on the "+" button. | - Implement a faster allocation method (AJAX or similar) so multiple | points can be assigned without a full page refresh. | - The implementation must remain secure and not be exploitable | (validate requests server-side, prevent double submissions, | verify available points, etc.). | | 2. Hero Equipment Restrictions (Original Travian Behavior) | - Prevent equipping or unequipping hero items while the hero is: | • On an adventure. | • Attacking (alone or with troops). | • Away as reinforcements. | | 3. Oasis Navigation | - When opening an oasis (37_land.tpl), the 37_t4nav.tpl navigation | disappears. | - Include the navigation menu on the oasis page as well. | | 4. Auction House Redesign | - Redesign 37_auction.tpl to match the Merchant.png mockup. | - Keep the existing auction functionality exactly as it is. | - Only improve the UI/UX. | - You may crop and reuse graphics from Merchant.png. | - Add Gold ⇄ Silver exchange functionality. | | 5. Hero Item Bonuses (Highest Priority) | - Verify that hero equipment bonuses are applied correctly. | - Example: | • Helmet of the Ruler should reduce Barracks training time by 20%. | • Currently, training time remains unchanged whether the helmet | is equipped or not. | - Also verify: | • Stable training speed bonuses. | • Boots bonuses. | • Any other related hero equipment effects. | | 6. Adventures Page Redesign | - Redesign 37_adventures.tpl to match the Adventure.png mockup. | - Keep the "Expires in" column intact. | */
This commit is contained in:
@@ -50,7 +50,11 @@ if (!function_exists('admin_config_template_path')) {
|
||||
// - existing server -> keep the current CRON_KEY (survives config saves)
|
||||
// - key not defined -> provision a fresh random one (servers installed
|
||||
// before CRON_KEY existed in the template)
|
||||
function admin_config_template_contents() {
|
||||
// $overrides permite unui mod de editare sa IMPUNA o valoare pentru un
|
||||
// placeholder pe care altfel l-am pastra din constanta curenta (ex.
|
||||
// editServerSet care schimba HERO_BASE_REGEN). Celelalte module apeleaza
|
||||
// fara argumente si valorile curente raman neatinse.
|
||||
function admin_config_template_contents(array $overrides = array()) {
|
||||
$path = admin_config_template_path();
|
||||
|
||||
if ($path === false) {
|
||||
@@ -93,6 +97,15 @@ if (!function_exists('admin_config_template_path')) {
|
||||
'%CLEANUPMESSAGES%' => array('CLEANUP_MESSAGES_DAYS', 0),
|
||||
);
|
||||
|
||||
// regenerarea de baza a eroului: acelasi tratament, sa nu fie resetata
|
||||
$cleanupDefaults['%HEROBASEREGEN%'] = array('HERO_BASE_REGEN', 10);
|
||||
|
||||
foreach ($overrides as $ovPlaceholder => $ovValue) {
|
||||
if (strpos($text, $ovPlaceholder) !== false) {
|
||||
$text = str_replace($ovPlaceholder, (string) $ovValue, $text);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($cleanupDefaults as $placeholder => $info) {
|
||||
if (strpos($text, $placeholder) === false) {
|
||||
continue;
|
||||
|
||||
@@ -74,7 +74,13 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$NEW_FUNCTION_TRIBE_SPARTANS = (defined('NEW_FUNCTION_TRIBE_SPARTANS') && NEW_FUNCTION_TRIBE_SPARTANS ? 'true' : 'false');
|
||||
$NEW_FUNCTION_TRIBE_VIKINGS = (defined('NEW_FUNCTION_TRIBE_VIKINGS') && NEW_FUNCTION_TRIBE_VIKINGS ? 'true' : 'false');
|
||||
|
||||
$text = admin_config_template_contents();
|
||||
// Regenerarea de baza a vietii eroului (HP pe zi). 0 = dezactivata.
|
||||
$heroBaseRegen = isset($_POST['hero_base_regen']) ? (int) $_POST['hero_base_regen'] : 10;
|
||||
|
||||
if ($heroBaseRegen < 0) { $heroBaseRegen = 0; }
|
||||
if ($heroBaseRegen > 100) { $heroBaseRegen = 100; }
|
||||
|
||||
$text = admin_config_template_contents(array('%HEROBASEREGEN%' => $heroBaseRegen));
|
||||
$text = preg_replace("'%ERRORREPORT%'", $_POST['error'], $text);
|
||||
$text = preg_replace("'%ERROR%'", $_POST['error'], $text);
|
||||
$text = preg_replace("'%SERVERNAME%'", $_POST['servername'], $text);
|
||||
|
||||
@@ -121,13 +121,48 @@ trait AutomationHero {
|
||||
|
||||
// Passive HP regeneration: returns the new health value, or -1 if unchanged.
|
||||
private function calculateHealthRegen($hdata) {
|
||||
// Eroii morti nu se regenereaza (se ridica prin inviere, nu singuri).
|
||||
if ((int) $hdata['dead'] === 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if((time()-$hdata['lastupdate']) >= 1){
|
||||
if($hdata['health'] < 100 and $hdata['health'] > 0){
|
||||
// FIX: conditia veche era "health > 0", deci un erou VIU ajuns exact
|
||||
// la 0 ramanea blocat acolo pentru totdeauna. Acum conteaza doar sa
|
||||
// fie viu si sub 100.
|
||||
if($hdata['health'] < 100){
|
||||
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']);
|
||||
// FIX: bonusul HB_REGEN_HP al coifurilor (Regeneration / Health /
|
||||
// Healing = +10/15/20 HP pe zi) nu era aplicat nicaieri - exista
|
||||
// doar in definitia itemelor. Se adauga la regenerarea proprie a
|
||||
// eroului, scalat cu aceeasi viteza de server ca aceasta, altfel
|
||||
// pe un server rapid ar fi complet nesemnificativ.
|
||||
// ANOMALIE DE SEMNALAT: in T4 valoarea e "HP pe zi" fixa; aici o
|
||||
// scalam cu $speed pentru consecventa cu regenerarea din atribute.
|
||||
// Daca preferi valoarea fixa, scoate "* $speed" de mai jos.
|
||||
$itemRegen = $this->heroItemRegen($hdata);
|
||||
|
||||
// FIX PRINCIPAL: pana acum regenerarea depindea EXCLUSIV de
|
||||
// atributul 'regeneration'. Un erou nou porneste cu acest atribut
|
||||
// pe 0 (vezi INSERT-ul din 37_train.tpl), deci daca jucatorul nu
|
||||
// punea puncte acolo, viata NU se refacea niciodata: scadea la
|
||||
// fiecare aventura sau lupta si nu urca inapoi. Dupa destule
|
||||
// aventuri ajungea la 0 si eroul murea chiar si la o aventura
|
||||
// "Normal" (care ia doar 0-8 HP) - exact simptomul raportat.
|
||||
//
|
||||
// Acum exista o regenerare de BAZA, independenta de atribute, ca
|
||||
// in Travian T4 (implicit 10 HP pe zi). Se poate regla din
|
||||
// GameEngine/config.php: define('HERO_BASE_REGEN', 10);
|
||||
$baseRegen = defined('HERO_BASE_REGEN') ? (float) HERO_BASE_REGEN : 10;
|
||||
|
||||
// HP pe zi din toate sursele: baza + atribut (5 per punct) + iteme
|
||||
$perDay = $baseRegen + ($hdata['regeneration'] * 5) + $itemRegen;
|
||||
|
||||
$reg = $hdata['health']
|
||||
+ $perDay * $speed / 86400 * (time() - $hdata['lastupdate']);
|
||||
|
||||
return ($reg <= 100) ? $reg : 100;
|
||||
}
|
||||
@@ -135,6 +170,29 @@ trait AutomationHero {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* HP pe zi adaugat de itemele echipate (HB_REGEN_HP).
|
||||
*
|
||||
* HeroBattleBonus::bonuses() are gard de feature flag, cache per request si
|
||||
* intoarce null cand sistemul T4 e oprit - deci pe serverele fara erou T4
|
||||
* comportamentul ramane exact ca inainte.
|
||||
*/
|
||||
private function heroItemRegen($hdata) {
|
||||
if (!class_exists('HeroBattleBonus') || !HeroBattleBonus::enabled()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$uid = isset($hdata['uid']) ? (int) $hdata['uid'] : 0;
|
||||
|
||||
if ($uid <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$bonuses = HeroBattleBonus::bonuses($uid);
|
||||
|
||||
return $bonuses ? (float) $bonuses[HB_REGEN_HP] : 0;
|
||||
}
|
||||
|
||||
// Level-up + score points. Returns a column fragment.
|
||||
private function calculateLevelUp($hdata) {
|
||||
global $hero_levels;
|
||||
|
||||
@@ -212,10 +212,84 @@ trait AutomationVillageUpkeep {
|
||||
|
||||
private function culturePoints() {
|
||||
global $database;
|
||||
|
||||
|
||||
// FIX: punctele de cultura date de coifuri (Gladiator/Tribune/Consul =
|
||||
// 100/400/800 pe zi) nu erau adaugate nicaieri - HB_CP exista doar in
|
||||
// definitia itemelor si in agregarea din HeroItems::getBonuses().
|
||||
// Se aplica INAINTE de updateVSumField, fiindca acela reseteaza
|
||||
// users.lastupdate; asa ambele folosesc exact aceeasi fereastra de timp.
|
||||
$this->applyHeroCulturePoints();
|
||||
|
||||
$database->updateVSumField('cp');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adauga in users.cp punctele de cultura ale coifurilor echipate.
|
||||
*
|
||||
* Acumularea imita formula din updateVSumField(): valoarea e "pe zi", iar
|
||||
* ziua de joc e 86400/SPEED (minim o ora), la fel ca pentru cladiri.
|
||||
* Valorile sunt luate din catalog prin scaledBonusValue(), care aplica deja
|
||||
* regula de server rapid (coifurile de cultura se injumatatesc la SPEED>=3).
|
||||
*
|
||||
* Se numara doar eroii vii (hero.dead = 0). Cum exista un singur slot de
|
||||
* coif, un jucator nu poate avea doua iteme cu CP echipate simultan, deci
|
||||
* join-ul nu poate dubla castigul.
|
||||
*/
|
||||
private function applyHeroCulturePoints() {
|
||||
global $database, $heroItemCatalog;
|
||||
|
||||
if (!class_exists('HeroBattleBonus') || !HeroBattleBonus::enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($heroItemCatalog) || !is_array($heroItemCatalog)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$heroItems = new HeroItems();
|
||||
$cpValues = array();
|
||||
|
||||
foreach ($heroItemCatalog as $itemid => $def) {
|
||||
if (!isset($def['bonus']) || !is_array($def['bonus']) || !isset($def['bonus'][HB_CP])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = (int) $heroItems->scaledBonusValue($def, (int) $def['bonus'][HB_CP]);
|
||||
|
||||
if ($value > 0) {
|
||||
$cpValues[(int) $itemid] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($cpValues)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$durDay = 86400 / SPEED;
|
||||
|
||||
if ($durDay < 3600) {
|
||||
$durDay = 3600;
|
||||
}
|
||||
|
||||
$case = '';
|
||||
|
||||
foreach ($cpValues as $itemid => $value) {
|
||||
$case .= ' WHEN ' . (int) $itemid . ' THEN ' . (int) $value;
|
||||
}
|
||||
|
||||
$ids = implode(',', array_map('intval', array_keys($cpValues)));
|
||||
|
||||
$q = "UPDATE " . TB_PREFIX . "users AS u
|
||||
INNER JOIN " . TB_PREFIX . "hero_items AS hi
|
||||
ON hi.uid = u.id AND hi.equipped = 1 AND hi.itemid IN (" . $ids . ")
|
||||
INNER JOIN " . TB_PREFIX . "hero AS h
|
||||
ON h.uid = u.id AND h.dead = 0
|
||||
SET u.cp = u.cp + ((CASE hi.itemid" . $case . " END) * (UNIX_TIMESTAMP() - u.lastupdate) / " . $durDay . ")
|
||||
WHERE u.lastupdate < (UNIX_TIMESTAMP() - 600)";
|
||||
|
||||
mysqli_query($database->dblink, $q);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recompute a warehouse/granary capacity after a build completion.
|
||||
* Returns [$fieldDbName, $max]: the vdata column to update and its new value.
|
||||
|
||||
@@ -264,11 +264,100 @@ class HeroItems
|
||||
* - anything already in that slot is unequipped first (atomic enough for
|
||||
* a per-user action; both statements touch only this user's rows)
|
||||
*/
|
||||
/* =========================================================================
|
||||
* DISPONIBILITATEA EROULUI PENTRU SCHIMBAREA ECHIPAMENTULUI
|
||||
* ===================================================================== */
|
||||
|
||||
/** @var array cache per request: uid => motiv ('' = eroul e acasa) */
|
||||
private static $awayCache = array();
|
||||
|
||||
/**
|
||||
* Motivul pentru care eroul NU poate schimba echipamentul acum.
|
||||
*
|
||||
* Intoarce '' cand eroul e acasa si poate fi echipat, sau una dintre:
|
||||
* 'adventure' - plecat intr-o aventura
|
||||
* 'attack' - plecat intr-un atac (singur sau cu trupe), dus sau intors
|
||||
* 'reinforcement' - trimis ca intarire in alt sat
|
||||
*
|
||||
* Comportament ca in Travian original: echipamentul se schimba doar cat timp
|
||||
* eroul e in sat. Eroul MORT nu e blocat - e "acasa", doar fara viata, si in
|
||||
* T4 iti poti pregati echipamentul cat timp astepti invierea.
|
||||
*/
|
||||
public function heroAwayReason($uid)
|
||||
{
|
||||
$uid = (int) $uid;
|
||||
|
||||
if (isset(self::$awayCache[$uid])) {
|
||||
return self::$awayCache[$uid];
|
||||
}
|
||||
|
||||
$p = TB_PREFIX;
|
||||
|
||||
// ATENTIE: aliasul pentru miscarea de intoarcere NU poate fi "returning" -
|
||||
// e cuvant rezervat in MariaDB (clauza RETURNING) si strica query-ul.
|
||||
// Un singur query, cu motive separate, ca interfata sa poata afisa
|
||||
// exact de ce e blocat. Coloanele `from`/`to` sunt cuvinte rezervate,
|
||||
// de aceea sunt in backticks.
|
||||
$q = "SELECT
|
||||
(SELECT COUNT(*) FROM {$p}hero
|
||||
WHERE uid = ? AND COALESCE(intraining, 0) = 0) AS hero_exists,
|
||||
(SELECT COUNT(*) FROM {$p}hero_adventure
|
||||
WHERE uid = ? AND status = 1) AS adventure,
|
||||
(SELECT IFNULL(SUM(e.hero), 0) FROM {$p}enforcement e
|
||||
INNER JOIN {$p}vdata v ON v.wref = e.`from`
|
||||
WHERE v.owner = ?) AS reinforcement,
|
||||
(SELECT IFNULL(SUM(a.t11), 0) FROM {$p}movement m
|
||||
INNER JOIN {$p}attacks a ON a.id = m.ref
|
||||
INNER JOIN {$p}vdata v ON v.wref = m.`from`
|
||||
WHERE v.owner = ? AND m.proc = 0 AND m.sort_type = 3) AS outgoing,
|
||||
(SELECT IFNULL(SUM(a.t11), 0) FROM {$p}movement m
|
||||
INNER JOIN {$p}attacks a ON a.id = m.ref
|
||||
INNER JOIN {$p}vdata v ON v.wref = m.`to`
|
||||
WHERE v.owner = ? AND m.proc = 0 AND m.sort_type = 4) AS atk_return";
|
||||
|
||||
$stmt = $this->db->prepare($q);
|
||||
|
||||
if (!$stmt) {
|
||||
// daca query-ul nu se poate pregati, nu blocam jucatorul
|
||||
return self::$awayCache[$uid] = '';
|
||||
}
|
||||
|
||||
$stmt->bind_param('iiiii', $uid, $uid, $uid, $uid, $uid);
|
||||
$stmt->execute();
|
||||
$stmt->bind_result($heroExists, $adventure, $reinforcement, $outgoing, $atkReturn);
|
||||
$stmt->fetch();
|
||||
$stmt->close();
|
||||
|
||||
$reason = '';
|
||||
|
||||
// Fara erou antrenat nu exista pe cine echipa. Se numara si eroii morti
|
||||
// (randul exista, doar dead = 1): in T4 iti poti pregati echipamentul cat
|
||||
// astepti invierea. Eroii aflati INCA in antrenament (intraining = 1) nu
|
||||
// conteaza - inca nu exista cu adevarat.
|
||||
if ((int) $heroExists === 0) {
|
||||
$reason = 'nohero';
|
||||
} elseif ((int) $adventure > 0) {
|
||||
$reason = 'adventure';
|
||||
} elseif (((int) $outgoing + (int) $atkReturn) > 0) {
|
||||
$reason = 'attack';
|
||||
} elseif ((int) $reinforcement > 0) {
|
||||
$reason = 'reinforcement';
|
||||
}
|
||||
|
||||
return self::$awayCache[$uid] = $reason;
|
||||
}
|
||||
|
||||
public function equipItem($uid, $rowId)
|
||||
{
|
||||
global $database;
|
||||
$uid = (int) $uid; $rowId = (int) $rowId;
|
||||
|
||||
// Eroul trebuie sa fie in sat. Verificarea e AICI, nu doar in interfata,
|
||||
// ca un POST trimis manual sa nu poata ocoli regula.
|
||||
if ($this->heroAwayReason($uid) !== '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$row = $this->findRowById($uid, $rowId);
|
||||
if (!$row || $row['orphan'] || (int) $row['def']['slot'] === HSLOT_BAG) {
|
||||
return false;
|
||||
@@ -314,6 +403,11 @@ class HeroItems
|
||||
/** Unequip a specific owned row. Returns true on success. */
|
||||
public function unequipItem($uid, $rowId)
|
||||
{
|
||||
// Acelasi gard ca la echipare (vezi heroAwayReason).
|
||||
if ($this->heroAwayReason($uid) !== '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$uid = (int) $uid; $rowId = (int) $rowId;
|
||||
$stmt = $this->db->prepare(
|
||||
"UPDATE " . TB_PREFIX . "hero_items SET equipped = 0 WHERE uid = ? AND id = ? AND equipped = 1 LIMIT 1"
|
||||
|
||||
@@ -2813,6 +2813,7 @@ if (!function_exists('tz_loc_topic')) {
|
||||
* T4 HERO PORT (Phase 6) - items / adventures / auction house
|
||||
* ========================================================================== */
|
||||
tz_def('HERO_T4_TAB_HERO', 'Hero');
|
||||
tz_def('HERO_T4_TAB_OASIS', 'Oasis');
|
||||
tz_def('HERO_T4_TAB_ITEMS', 'Inventory');
|
||||
tz_def('HERO_T4_TAB_ADVENTURES', 'Adventures');
|
||||
tz_def('HERO_T4_TAB_AUCTION', 'Auctions');
|
||||
@@ -2842,6 +2843,7 @@ tz_def('HERO_ITEM_USED_OK', 'The item has been used.');
|
||||
tz_def('HERO_ITEM_USE_FAIL', 'This item cannot be used right now.');
|
||||
tz_def('HERO_ITEM_USE_BATTLE', 'This item is used automatically (bandages heal returning troops after battles).');
|
||||
tz_def('HERO_EQUIP_OK', 'Item equipped.');
|
||||
tz_def('HERO_LOCKED_NOHERO', 'You have no hero yet. Train one in the Hero\'s Mansion before equipping items.');
|
||||
tz_def('HERO_EQUIP_FAIL', 'This item cannot be equipped (wrong hero unit or item type).');
|
||||
tz_def('HERO_UNEQUIP_OK', 'Item taken off.');
|
||||
|
||||
@@ -2853,6 +2855,9 @@ tz_def('HERO_ADV_NONE', 'No adventures available right now. New ones ap
|
||||
tz_def('HERO_ADV_DIFFICULTY', 'Difficulty');
|
||||
tz_def('HERO_ADV_DIFF_NORMAL', 'Normal');
|
||||
tz_def('HERO_ADV_DIFF_HARD', 'Hard');
|
||||
tz_def('HERO_ADV_PLACE', 'Place');
|
||||
tz_def('HERO_ADV_DANGER', 'Danger');
|
||||
tz_def('HERO_ADV_LINK', 'Link');
|
||||
tz_def('HERO_ADV_DURATION', 'Travel time (one way)');
|
||||
tz_def('HERO_ADV_EXPIRES', 'Expires in');
|
||||
tz_def('HERO_ADV_GO', 'Start adventure');
|
||||
|
||||
@@ -2736,6 +2736,9 @@ tz_def('HERO_ADV_DIED_INFO', 'Toata prada a fost pierduta. Eroul poate fi in
|
||||
tz_def('HERO_ADV_DIFFICULTY', 'Dificultate');
|
||||
tz_def('HERO_ADV_DIFF_HARD', 'Grea');
|
||||
tz_def('HERO_ADV_DIFF_NORMAL', 'Normala');
|
||||
tz_def('HERO_ADV_PLACE', 'Loc');
|
||||
tz_def('HERO_ADV_DANGER', 'Pericol');
|
||||
tz_def('HERO_ADV_LINK', 'Actiune');
|
||||
tz_def('HERO_ADV_DURATION', 'Timp de calatorie (dus)');
|
||||
tz_def('HERO_ADV_EXPIRES', 'Expira in');
|
||||
tz_def('HERO_ADV_GO', 'Incepe aventura');
|
||||
@@ -2787,6 +2790,7 @@ tz_def('HERO_AUC_YOUR_MAX', 'Maximul tau');
|
||||
|
||||
// Hero - echipare / inventar
|
||||
tz_def('HERO_EQUIP', 'Echipeaza');
|
||||
tz_def('HERO_LOCKED_NOHERO', 'Nu ai inca un erou. Antreneaza unul in Resedinta Eroului inainte de a echipa iteme.');
|
||||
tz_def('HERO_EQUIP_FAIL', 'Acest obiect nu poate fi echipat (unitate de erou gresita sau tip de obiect gresit).');
|
||||
tz_def('HERO_EQUIP_OK', 'Obiect echipat.');
|
||||
tz_def('HERO_EXPERIENCE', 'Experienta');
|
||||
@@ -2807,6 +2811,7 @@ tz_def('HERO_SLOT_4', 'Mana stanga');
|
||||
tz_def('HERO_SLOT_5', 'Incaltaminte');
|
||||
tz_def('HERO_SLOT_6', 'Cal');
|
||||
tz_def('HERO_SLOT_7', 'Desaga');
|
||||
tz_def('HERO_T4_TAB_OASIS', 'Oaze');
|
||||
tz_def('HERO_T4_TAB_ADVENTURES', 'Aventuri');
|
||||
tz_def('HERO_T4_TAB_AUCTION', 'Licitatii');
|
||||
tz_def('HERO_T4_TAB_HERO', 'Erou');
|
||||
|
||||
@@ -634,9 +634,77 @@ class Technology {
|
||||
$each = round(($bid19[$building->getTypeLevel(36)]['attri'] / 100) * ${'u'.$unit}['time'] / SPEED);
|
||||
}
|
||||
|
||||
// FIX: bonusurile de instruire ale eroului (coifuri) nu erau aplicate
|
||||
// NICAIERI - HB_TRAIN_INF si HB_TRAIN_CAV existau doar in definitia
|
||||
// itemelor si in agregarea din HeroItems::getBonuses(), deci "Helmet of
|
||||
// the Ruler" (-20% infanterie) sau "Helmet of the Heavy Cavalry" (-20%
|
||||
// cavalerie) nu schimbau timpul cu nimic.
|
||||
//
|
||||
// Se aplica dupa formula cladirii, pe acelasi $each, deci merge identic
|
||||
// in cazarma/grajd normale si in cele mari (Great Barracks/Great Stable),
|
||||
// fiindca ramura e aleasa dupa TIPUL unitatii, nu dupa cladire.
|
||||
// Nu se aplica la atelier, unitati speciale sau capcane - la fel ca in T4.
|
||||
$each = $this->applyHeroTrainingBonus($each, $unit, $footies, $calvary);
|
||||
|
||||
return $each;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce timpul de instruire cu procentul dat de coiful echipat al eroului.
|
||||
*
|
||||
* Proprietarul e luat din satul in care se face instruirea (nu din sesiune),
|
||||
* ca sa fie corect si cand codul ruleaza in alt context decat o pagina a
|
||||
* jucatorului. HeroBattleBonus::bonuses() are deja gard de feature flag si
|
||||
* cache per request, si intoarce null cand sistemul T4 e oprit.
|
||||
*/
|
||||
private function applyHeroTrainingBonus($each, $unit, array $footies, array $calvary) {
|
||||
global $village, $database;
|
||||
|
||||
if (!class_exists('HeroBattleBonus') || !HeroBattleBonus::enabled()) {
|
||||
return $each;
|
||||
}
|
||||
|
||||
// Proprietarul satului in care se instruieste, NU utilizatorul din
|
||||
// sesiune: asa bonusul e corect si cand un admin lucreaza pe satul
|
||||
// altui jucator (conteaza eroul proprietarului, nu al adminului).
|
||||
// getVillageField e cache-uit per request, deci nu adauga query-uri.
|
||||
$uid = (isset($village->wid) && $database)
|
||||
? (int) $database->getVillageField($village->wid, 'owner')
|
||||
: 0;
|
||||
|
||||
if ($uid <= 0) {
|
||||
return $each;
|
||||
}
|
||||
|
||||
$bonuses = HeroBattleBonus::bonuses($uid);
|
||||
|
||||
if (!$bonuses) {
|
||||
return $each;
|
||||
}
|
||||
|
||||
$percent = 0;
|
||||
|
||||
if (in_array($unit, $footies)) {
|
||||
$percent = (int) $bonuses[HB_TRAIN_INF];
|
||||
} elseif (in_array($unit, $calvary)) {
|
||||
$percent = (int) $bonuses[HB_TRAIN_CAV];
|
||||
}
|
||||
|
||||
if ($percent <= 0) {
|
||||
return $each;
|
||||
}
|
||||
|
||||
// plasa de siguranta: chiar daca s-ar aduna mai multe surse, timpul nu
|
||||
// poate cobori sub 10% din valoarea de baza
|
||||
if ($percent > 90) {
|
||||
$percent = 90;
|
||||
}
|
||||
|
||||
$each = (int) round($each * (100 - $percent) / 100);
|
||||
|
||||
return $each > 0 ? $each : 1;
|
||||
}
|
||||
|
||||
private function getMaxTrainable($unit, $amt, $great) {
|
||||
global $database, $village, $bid36;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user