Add T4 Alliance Bonuses NEW_FUNCTION

Add T4 Alliance Bonuses NEW_FUNCTION
This commit is contained in:
novgorodschi catalin
2026-07-27 14:52:15 +03:00
parent 5c4b377daf
commit 064416826e
58 changed files with 1332 additions and 120 deletions
@@ -112,6 +112,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
tz_config_set($text, '%HOMEPAGE%', HOMEPAGE);
tz_config_set($text, '%SERVER%', SERVER);
tz_config_set($text, '%NEW_FUNCTIONS_OASIS%', $_POST['new_functions_oasis'] ?? '');
tz_config_set($text, '%ALLIANCEBONUSES%', $_POST['alliance_bonuses'] ?? 'false');
tz_config_set($text, '%NEW_FUNCTIONS_ALLIANCE_INVITATION%', $_POST['new_functions_alliance_invitation'] ?? '');
tz_config_set($text, '%NEW_FUNCTIONS_EMBASSY_MECHANICS%', $_POST['new_functions_embassy_mechanics'] ?? '');
tz_config_set($text, '%NEW_FUNCTIONS_FORUM_POST_MESSAGE%', $_POST['new_functions_forum_post_message'] ?? '');
+12
View File
@@ -352,6 +352,12 @@ class Alliance {
// Acceptăm invitația
$database->removeInvitation($inviteID);
$database->updateUserField($invite['uid'], "alliance", $invite['alliance'], 1);
// Bonusurile de alianta devin disponibile treptat pentru
// membrii noi, deci retinem cand a intrat. Fara asta,
// cineva ar putea sari dintr-o alianta in alta doar ca sa
// culeaga bonusuri de nivel mare.
$database->updateUserField($invite['uid'], "alliance_joined", time(), 1);
$database->createAlliPermissions($invite['uid'], $invite['alliance'], '', 0, 0, 0, 0, 0, 0, 0, 0);
// Invalidate the 30s session user-cache (see Session::PopulateVar) so the
// new alliance membership shows up immediately, without a re-login.
@@ -410,6 +416,12 @@ class Alliance {
}
$database->updateUserField($session->uid, "alliance", $aid, 1);
// Bonusurile de alianta devin disponibile treptat pentru
// membrii noi, deci retinem cand a intrat. Fara asta,
// cineva ar putea sari dintr-o alianta in alta doar ca sa
// culeaga bonusuri de nivel mare.
$database->updateUserField($session->uid, "alliance_joined", time(), 1);
$database->procAllyPop($aid);
$database->createAlliPermissions($session->uid, $aid, 'Alliance founder', '1', '1', '1', '1', '1', '1', '1', '1');
// Invalidate the 30s session user-cache (see Session::PopulateVar) so the
+638
View File
@@ -0,0 +1,638 @@
<?php
#################################################################################
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
## --------------------------------------------------------------------------- ##
## Filename : AllianceBonus.php ##
## Type : AllianceBonus port ##
## --------------------------------------------------------------------------- ##
## Developed by : Shadow ##
## Project : TravianZ ##
## GitHub : https://github.com/Shadowss/TravianZ ##
## --------------------------------------------------------------------------- ##
## License : TravianZ Project ##
## Copyright : TravianZ (c) 2010-2026. All rights reserved. ##
## --------------------------------------------------------------------------- ##
#################################################################################
## ##
## Alliance members donate resources, allowing the alliance to unlock four ##
## bonuses, each with five levels: ##
## ##
## 1 Recruitment - Faster troop training in all military buildings ##
## 2 Philosophy - Increased Culture Point production ##
## 3 Metallurgy - Higher attack and defense values (in addition to Smithy ##
## upgrades) ##
## 4 Commerce - Increased merchant carrying capacity ##
## ##
## The mechanics follow Travian T4: the type of resource donated does not ##
## matter, only the total amount; while a bonus level is being unlocked, ##
## donations to that bonus are temporarily disabled; each player has a daily ##
## donation limit that increases with the alliance level; and new members ##
## gradually gain access to higher bonus levels. ##
#################################################################################
class AllianceBonus
{
const RECRUITMENT = 1;
const PHILOSOPHY = 2;
const METALLURGY = 3;
const COMMERCE = 4;
const MAX_LEVEL = 5;
// rezultate pentru donate()
const DONATE_OK = 0;
const DONATE_DISABLED = 1;
const DONATE_NO_ALLIANCE = 2;
const DONATE_UPGRADING = 3;
const DONATE_LIMIT = 4;
const DONATE_RESOURCES = 5;
const DONATE_INVALID = 6;
const DONATE_NO_GOLD = 7;
private $db;
/** cache per cerere: nivelurile fiecarei aliante deja citite */
private static $levelCache = array();
public function __construct()
{
global $database;
$this->db = $database->dblink;
}
/**
* Functiile sunt pornite? Verificam si flagul, si existenta tabelei, ca sa
* nu cada jocul pe un server care nu a rulat inca migrarea SQL.
*/
public static function enabled()
{
static $ok = null;
if ($ok !== null) {
return $ok;
}
if (!defined('NEW_FUNCTIONS_ALLIANCE_BONUSES') || !NEW_FUNCTIONS_ALLIANCE_BONUSES) {
return $ok = false;
}
global $database;
$res = @mysqli_query($database->dblink,
"SHOW TABLES LIKE '" . TB_PREFIX . "alliance_bonus'");
return $ok = ($res && mysqli_num_rows($res) > 0);
}
/** Lista tipurilor, cu cheia de limba si iconita. */
public static function types()
{
return array(
self::RECRUITMENT => array('key' => 'recruitment', 'lang' => 'ALLYBONUS_RECRUITMENT'),
self::PHILOSOPHY => array('key' => 'philosophy', 'lang' => 'ALLYBONUS_PHILOSOPHY'),
self::METALLURGY => array('key' => 'metallurgy', 'lang' => 'ALLYBONUS_METALLURGY'),
self::COMMERCE => array('key' => 'commerce', 'lang' => 'ALLYBONUS_COMMERCE'),
);
}
/* ------------------------------------------------------------------ */
/* Tabele de valori (din config, cu rezerva pe valorile din T4) */
/* ------------------------------------------------------------------ */
private static function listFromConst($name, $fallback)
{
$raw = defined($name) ? (string) constant($name) : $fallback;
$out = array();
foreach (explode(',', $raw) as $v) {
$v = trim($v);
if ($v !== '' && is_numeric($v)) {
$out[] = (float) $v;
}
}
return $out ? $out : array_map('floatval', explode(',', $fallback));
}
/** Resurse necesare pentru a trece de la $level la $level+1. */
public static function costFor($level)
{
$costs = self::listFromConst('ALLIANCE_BONUS_COSTS',
'1200000,5600000,17100000,51200000,153600000');
return isset($costs[$level]) ? (float) $costs[$level] : 0.0;
}
/** Durata upgrade-ului, in secunde, scalata cu viteza serverului. */
public static function upgradeSeconds($level)
{
$hours = self::listFromConst('ALLIANCE_BONUS_HOURS', '24,48,72,96,120');
$h = isset($hours[$level]) ? (float) $hours[$level] : 24.0;
$speed = (defined('SPEED') && SPEED > 0) ? (float) SPEED : 1.0;
return (int) max(60, round($h * 3600 / $speed));
}
/**
* Plafonul zilnic de donatie al unui jucator, dupa cel mai mare nivel
* deblocat in alianta. Se scaleaza cu viteza serverului, ca in T4.
*/
public static function dailyLimit($highestLevel)
{
$limits = self::listFromConst('ALLIANCE_BONUS_DAILY',
'300000,300000,400000,550000,750000,1000000');
$idx = max(0, min(count($limits) - 1, (int) $highestLevel));
$val = (float) $limits[$idx];
$speed = (defined('SPEED') && SPEED > 0) ? (float) SPEED : 1.0;
return (float) round($val * $speed);
}
/** Procentul acordat de un nivel, pentru un tip de bonus. */
public static function percentFor($btype, $level)
{
$level = max(0, min(self::MAX_LEVEL, (int) $level));
if ($level === 0) {
return 0.0;
}
$small = defined('ALLIANCE_BONUS_PCT_SMALL') ? (float) ALLIANCE_BONUS_PCT_SMALL : 2.0;
$large = defined('ALLIANCE_BONUS_PCT_LARGE') ? (float) ALLIANCE_BONUS_PCT_LARGE : 4.0;
// Recruitment si Philosophy cresc cu 2% pe nivel, Metallurgy si
// Commerce cu 4% pe nivel (vezi tabelul din T4).
$step = in_array((int) $btype, array(self::METALLURGY, self::COMMERCE), true)
? $large : $small;
return $level * $step;
}
/* ------------------------------------------------------------------ */
/* Citire */
/* ------------------------------------------------------------------ */
/**
* Starea completa a bonusurilor unei aliante: nivel, progres, upgrade activ.
* Randurile lipsa se completeaza cu nivel 0, deci apelantul primeste mereu
* toate cele patru tipuri.
*/
public function getState($aid)
{
$aid = (int) $aid;
$out = array();
foreach (array_keys(self::types()) as $btype) {
$out[$btype] = array(
'btype' => $btype,
'level' => 0,
'pool' => 0.0,
'upgrade_end' => 0,
);
}
if ($aid <= 0 || !self::enabled()) {
return $out;
}
$res = mysqli_query($this->db,
"SELECT btype, level, pool, upgrade_end FROM " . TB_PREFIX . "alliance_bonus
WHERE aid = " . $aid);
while ($res && ($row = mysqli_fetch_assoc($res))) {
$b = (int) $row['btype'];
if (isset($out[$b])) {
$out[$b] = array(
'btype' => $b,
'level' => (int) $row['level'],
'pool' => (float) $row['pool'],
'upgrade_end' => (int) $row['upgrade_end'],
);
}
}
return $out;
}
/**
* Nivelurile efective ale unei aliante, ca array btype => nivel.
* Cache-uit pe cerere: se citeste des (lupte, instruire, productie).
*/
public function getLevels($aid)
{
$aid = (int) $aid;
if (isset(self::$levelCache[$aid])) {
return self::$levelCache[$aid];
}
$levels = array();
foreach (array_keys(self::types()) as $btype) {
$levels[$btype] = 0;
}
if ($aid > 0 && self::enabled()) {
$res = mysqli_query($this->db,
"SELECT btype, level FROM " . TB_PREFIX . "alliance_bonus WHERE aid = " . $aid);
while ($res && ($row = mysqli_fetch_assoc($res))) {
$b = (int) $row['btype'];
if (isset($levels[$b])) {
$levels[$b] = (int) $row['level'];
}
}
}
return self::$levelCache[$aid] = $levels;
}
/** Cel mai mare nivel deblocat in alianta (pentru plafonul de donatie). */
public function highestLevel($aid)
{
$levels = $this->getLevels($aid);
return $levels ? max($levels) : 0;
}
/**
* Nivelul de care beneficiaza EFECTIV un jucator.
*
* Membrii noi capata acces treptat: nivelul 1 imediat, nivelul 2 dupa 24h,
* nivelul 3 dupa 48h si asa mai departe (scalat cu viteza serverului).
* Asa nu se poate sari dintr-o alianta in alta doar ca sa culegi bonusuri.
*/
public function effectiveLevel($uid, $btype)
{
if (!self::enabled()) {
return 0;
}
$uid = (int) $uid;
$res = mysqli_query($this->db,
"SELECT alliance, alliance_joined FROM " . TB_PREFIX . "users WHERE id = " . $uid . " LIMIT 1");
$row = $res ? mysqli_fetch_assoc($res) : null;
if (!$row || (int) $row['alliance'] <= 0) {
return 0;
}
$levels = $this->getLevels((int) $row['alliance']);
$level = isset($levels[(int) $btype]) ? (int) $levels[(int) $btype] : 0;
if ($level <= 1) {
return $level; // nivelul 1 e disponibil imediat
}
$joined = (int) $row['alliance_joined'];
// 0 sau 1 inseamna membru dinainte de aceasta functie: primeste tot
if ($joined <= 1) {
return $level;
}
$speed = (defined('SPEED') && SPEED > 0) ? (float) SPEED : 1.0;
$elapsed = time() - $joined;
// nivelul N devine disponibil dupa (N-1) * 24h de la intrare
$allowed = 1 + (int) floor($elapsed / max(60, (86400 / $speed)));
return max(0, min($level, $allowed));
}
/* ------------------------------------------------------------------ */
/* Donatii */
/* ------------------------------------------------------------------ */
/** Cat a donat un jucator azi. */
public function donatedToday($uid)
{
if (!self::enabled()) {
return 0.0;
}
$uid = (int) $uid;
$day = (int) floor(time() / 86400);
$res = mysqli_query($this->db,
"SELECT amount FROM " . TB_PREFIX . "alliance_donation
WHERE uid = " . $uid . " AND day = " . $day . " LIMIT 1");
$row = $res ? mysqli_fetch_assoc($res) : null;
return $row ? (float) $row['amount'] : 0.0;
}
/**
* Doneaza resurse catre un bonus.
*
* $amounts - array cu patru valori: lemn, lut, fier, cereale
* $triple - daca jucatorul plateste aur ca donatia sa conteze intreit
*
* Tipul resursei nu conteaza, doar totalul; resursele se scad din satul
* activ. Intoarce una dintre constantele DONATE_*.
*/
public function donate($uid, $wref, $btype, array $amounts, $triple = false)
{
global $database, $session;
if (!self::enabled()) {
return self::DONATE_DISABLED;
}
$uid = (int) $uid;
$wref = (int) $wref;
$btype = (int) $btype;
if (!isset(self::types()[$btype])) {
return self::DONATE_INVALID;
}
// sumele trebuie sa fie intregi pozitivi
$clean = array();
$total = 0.0;
foreach (array_slice($amounts, 0, 4) as $a) {
$a = (float) $a;
if ($a < 0 || !is_finite($a)) {
return self::DONATE_INVALID;
}
$a = floor($a);
$clean[] = $a;
$total += $a;
}
while (count($clean) < 4) {
$clean[] = 0.0;
}
if ($total <= 0) {
return self::DONATE_INVALID;
}
// alianta jucatorului
$res = mysqli_query($this->db,
"SELECT alliance FROM " . TB_PREFIX . "users WHERE id = " . $uid . " LIMIT 1");
$row = $res ? mysqli_fetch_assoc($res) : null;
$aid = $row ? (int) $row['alliance'] : 0;
if ($aid <= 0) {
return self::DONATE_NO_ALLIANCE;
}
// bonusul nu trebuie sa fie in curs de upgrade
$state = $this->getState($aid);
if ($state[$btype]['upgrade_end'] > time()) {
return self::DONATE_UPGRADING;
}
if ($state[$btype]['level'] >= self::MAX_LEVEL) {
return self::DONATE_INVALID;
}
// valoarea contorizata (triplata sau nu) si plafonul zilnic
$counted = $triple ? $total * 3 : $total;
$limit = self::dailyLimit($this->highestLevel($aid));
$already = $this->donatedToday($uid);
if ($already + $counted > $limit) {
return self::DONATE_LIMIT;
}
// aurul pentru triplare
if ($triple) {
$cost = defined('ALLIANCE_BONUS_TRIPLE_GOLD') ? (int) ALLIANCE_BONUS_TRIPLE_GOLD : 3;
if ((int) $session->gold < $cost) {
return self::DONATE_NO_GOLD;
}
}
// Resursele trebuie sa existe in sat.
// FIX: getResource() nu exista in clasa de baza de date; randul satului
// (cu wood/clay/iron/crop) se citeste cu getVillage(), fara cache, ca sa
// vedem valorile de dupa ultima productie, nu unele vechi de cateva zeci
// de secunde.
$vres = $database->getVillage($wref, 0, false);
if (!$vres) {
return self::DONATE_RESOURCES;
}
$fields = array('wood', 'clay', 'iron', 'crop');
foreach ($fields as $i => $f) {
if ((float) $vres[$f] < $clean[$i]) {
return self::DONATE_RESOURCES;
}
}
/* ---- de aici incolo modificam starea; totul intr-o tranzactie ---- */
mysqli_query($this->db, "START TRANSACTION");
// ATENTIE LA SEMN: modifyResource cu modul 0 SCADE deja
// ("wood = wood - $wood"). Trimiterea de valori negative ar fi
// ADAUGAT resurse - aceeasi capcana ca la aurul de la demolare.
$ok = $database->modifyResource($wref, $clean[0], $clean[1], $clean[2], $clean[3], 0);
if (!$ok) {
mysqli_query($this->db, "ROLLBACK");
return self::DONATE_RESOURCES;
}
// randul de bonus poate lipsi: il cream la prima donatie
mysqli_query($this->db,
"INSERT INTO " . TB_PREFIX . "alliance_bonus (aid, btype, level, pool, upgrade_end)
VALUES (" . $aid . ", " . $btype . ", 0, 0, 0)
ON DUPLICATE KEY UPDATE aid = aid");
// adaugam in fond DOAR daca bonusul tot nu e in upgrade (protejeaza
// impotriva a doua donatii simultane care ar sari peste verificare)
$res = mysqli_query($this->db,
"UPDATE " . TB_PREFIX . "alliance_bonus
SET pool = pool + " . $counted . "
WHERE aid = " . $aid . " AND btype = " . $btype . "
AND upgrade_end <= " . time() . " AND level < " . self::MAX_LEVEL . " LIMIT 1");
if (!$res || mysqli_affected_rows($this->db) === 0) {
mysqli_query($this->db, "ROLLBACK");
return self::DONATE_UPGRADING;
}
$day = (int) floor(time() / 86400);
mysqli_query($this->db,
"INSERT INTO " . TB_PREFIX . "alliance_donation (uid, day, amount)
VALUES (" . $uid . ", " . $day . ", " . $counted . ")
ON DUPLICATE KEY UPDATE amount = amount + " . $counted);
mysqli_query($this->db,
"INSERT INTO " . TB_PREFIX . "alliance_donation_log (aid, uid, btype, amount, time)
VALUES (" . $aid . ", " . $uid . ", " . $btype . ", " . $counted . ", " . time() . ")");
if ($triple) {
$cost = defined('ALLIANCE_BONUS_TRIPLE_GOLD') ? (int) ALLIANCE_BONUS_TRIPLE_GOLD : 3;
$database->modifyGold($uid, $cost, 0);
$session->gold -= $cost;
}
mysqli_query($this->db, "COMMIT");
// daca fondul a atins pragul, pornim upgrade-ul
$this->startUpgradeIfReady($aid, $btype);
unset(self::$levelCache[$aid]);
return self::DONATE_OK;
}
/**
* Porneste numaratoarea daca fondul acopera costul nivelului urmator.
* Costul se scade din fond, ca surplusul sa se reporteze la nivelul urmator.
*/
public function startUpgradeIfReady($aid, $btype)
{
$aid = (int) $aid;
$btype = (int) $btype;
$res = mysqli_query($this->db,
"SELECT level, pool, upgrade_end FROM " . TB_PREFIX . "alliance_bonus
WHERE aid = " . $aid . " AND btype = " . $btype . " LIMIT 1");
$row = $res ? mysqli_fetch_assoc($res) : null;
if (!$row || (int) $row['upgrade_end'] > time() || (int) $row['level'] >= self::MAX_LEVEL) {
return false;
}
$level = (int) $row['level'];
$cost = self::costFor($level);
if ($cost <= 0 || (float) $row['pool'] < $cost) {
return false;
}
$end = time() + self::upgradeSeconds($level);
mysqli_query($this->db,
"UPDATE " . TB_PREFIX . "alliance_bonus
SET pool = pool - " . $cost . ", upgrade_end = " . $end . "
WHERE aid = " . $aid . " AND btype = " . $btype . "
AND upgrade_end <= " . time() . " LIMIT 1");
return true;
}
/**
* Finalizeaza upgrade-urile expirate. Se apeleaza din Automation.
* Dupa crestere, verificam din nou fondul: daca surplusul acopera si
* nivelul urmator, upgrade-ul continua fara alta donatie.
*/
public function processUpgrades()
{
if (!self::enabled()) {
return 0;
}
$now = time();
$done = 0;
$res = mysqli_query($this->db,
"SELECT aid, btype FROM " . TB_PREFIX . "alliance_bonus
WHERE upgrade_end > 0 AND upgrade_end <= " . $now);
$rows = array();
while ($res && ($row = mysqli_fetch_assoc($res))) {
$rows[] = array((int) $row['aid'], (int) $row['btype']);
}
foreach ($rows as $r) {
list($aid, $btype) = $r;
$ok = mysqli_query($this->db,
"UPDATE " . TB_PREFIX . "alliance_bonus
SET level = level + 1, upgrade_end = 0
WHERE aid = " . $aid . " AND btype = " . $btype . "
AND upgrade_end > 0 AND upgrade_end <= " . $now . "
AND level < " . self::MAX_LEVEL . " LIMIT 1");
if ($ok && mysqli_affected_rows($this->db) > 0) {
$done++;
unset(self::$levelCache[$aid]);
$this->startUpgradeIfReady($aid, $btype);
}
}
return $done;
}
/** Contributia fiecarui membru, pentru afisare. */
public function contributions($aid, $limit = 30)
{
if (!self::enabled()) {
return array();
}
$aid = (int) $aid;
$limit = max(1, min(100, (int) $limit));
$out = array();
$res = mysqli_query($this->db,
"SELECT d.uid, u.username, SUM(d.amount) AS total
FROM " . TB_PREFIX . "alliance_donation_log d
LEFT JOIN " . TB_PREFIX . "users u ON u.id = d.uid
WHERE d.aid = " . $aid . "
GROUP BY d.uid, u.username
ORDER BY total DESC
LIMIT " . $limit);
while ($res && ($row = mysqli_fetch_assoc($res))) {
$out[] = array(
'uid' => (int) $row['uid'],
'username' => (string) $row['username'],
'total' => (float) $row['total'],
);
}
return $out;
}
/* ------------------------------------------------------------------ */
/* Ajutor pentru hook-urile din joc */
/* ------------------------------------------------------------------ */
/**
* Multiplicatorul unui bonus pentru un jucator, ca numar (1.0 = fara bonus).
* Folosit de Technology, Battle, Market si Building.
*
* Rezultatul e cache-uit pe cerere: se cere de multe ori intr-o singura
* pagina (fiecare unitate din lista de instruire, fiecare val din lupta).
*/
public static function multiplier($uid, $btype)
{
static $cache = array();
$uid = (int) $uid;
$btype = (int) $btype;
$key = $uid . ':' . $btype;
if (isset($cache[$key])) {
return $cache[$key];
}
if (!self::enabled() || $uid <= 0) {
return $cache[$key] = 1.0;
}
$engine = new self();
$level = $engine->effectiveLevel($uid, $btype);
$pct = self::percentFor($btype, $level);
return $cache[$key] = 1.0 + ($pct / 100.0);
}
}
+7
View File
@@ -52,6 +52,7 @@ include_once("Data/hero_full.php");
include_once("Data/cp.php");
include_once("Units.php");
include_once("Battle.php");
include_once("AllianceBonus.php");
include_once("Technology.php");
include_once("Ranking.php");
include_once("Generator.php");
@@ -149,6 +150,12 @@ class Automation {
$this->updateGeneralAttack();
$this->checkInvitedPlayes();
$this->updateStore();
// Finalizeaza upgrade-urile de bonus de alianta ajunse la termen.
if (class_exists('AllianceBonus') && AllianceBonus::enabled()) {
$allianceBonus = new AllianceBonus();
$allianceBonus->processUpgrades();
}
$this->CheckBan();
$this->regenerateOasisTroops();
$this->medals();
+27
View File
@@ -1097,6 +1097,33 @@ class Battle {
}
}
// Bonusul de alianta "Metallurgy": forta trupelor creste peste
// upgrade-urile de fierarie, care sunt deja aplicate mai sus pe fiecare
// unitate. Se inmulteste cu ele, nu se aduna - la fel ca in T4.
//
// Se aplica pe toate cele patru totaluri, ca sa acopere si atacul, si
// apararea: aceeasi functie calculeaza ambele parti, in functie de rolul
// jucatorului in lupta.
if (class_exists('AllianceBonus') && AllianceBonus::enabled()) {
$metalOwner = 0;
if (isset($Attacker) && (int) $Attacker > 0) {
global $database;
$metalOwner = (int) $database->getVillageField((int) $Attacker, 'owner');
}
if ($metalOwner > 0) {
$metalMult = AllianceBonus::multiplier($metalOwner, AllianceBonus::METALLURGY);
if ($metalMult > 1.0) {
$ap *= $metalMult;
$cap *= $metalMult;
$dp *= $metalMult;
$cdp *= $metalMult;
}
}
}
return [
'ap' => $ap,
'cap' => $cap,
+12
View File
@@ -1644,6 +1644,18 @@ class Building {
$cpTot += self::buildingCP($building, $lvl);
}
// Bonusul de alianta "Philosophy": mai multe puncte de cultura.
// Se aplica peste CP-ul dat de cladiri (si, prin acesta, peste coifurile
// eroului, care se adauga separat). Serbarile din primarie NU sunt
// afectate - ele nu trec pe aici, se acorda direct la finalizare.
if (class_exists('AllianceBonus') && AllianceBonus::enabled()) {
$cpOwner = (int) $database->getVillageField($vid, 'owner');
if ($cpOwner > 0) {
$cpTot = (int) round($cpTot * AllianceBonus::multiplier($cpOwner, AllianceBonus::PHILOSOPHY));
}
}
mysqli_query(
$database->dblink,
"
+51 -36
View File
@@ -189,44 +189,59 @@ trait DatabaseMovementQueries {
}
function addMovement($type, $from, $to, $ref, $time, $endtime, $send = 1, $wood = 0, $clay = 0, $iron = 0, $crop = 0, $ref2 = 0) {
// always prepare for multiple inserts at once
if (!is_array($type)) {
$type = [$type];
$from = [$from];
$to = [$to];
$ref = [$ref];
$time = [$time];
$endtime = [$endtime];
$send = [$send];
$wood = [$wood];
$clay = [$clay];
$iron = [$iron];
$crop = [$crop];
$ref2 = [$ref2];
// Caz 1: apel simplu, un singur movement
if (!is_array($type)) {
$type = [$type];
$from = [$from];
$to = [$to];
$ref = [$ref];
$time = [$time];
$endtime = [$endtime];
$send = [$send];
$wood = [$wood];
$clay = [$clay];
$iron = [$iron];
$crop = [$crop];
$ref2 = [$ref2];
} else {
// Caz 2: apel multiplu - $type e array, dar restul pot fi int
// le transformam si pe ele in array de aceeasi lungime
$count = count($type);
if (!is_array($from)) $from = array_fill(0, $count, $from);
if (!is_array($to)) $to = array_fill(0, $count, $to);
if (!is_array($ref)) $ref = array_fill(0, $count, $ref);
if (!is_array($time)) $time = array_fill(0, $count, $time);
if (!is_array($endtime)) $endtime = array_fill(0, $count, $endtime);
if (!is_array($send)) $send = array_fill(0, $count, $send);
if (!is_array($wood)) $wood = array_fill(0, $count, $wood);
if (!is_array($clay)) $clay = array_fill(0, $count, $clay);
if (!is_array($iron)) $iron = array_fill(0, $count, $iron);
if (!is_array($crop)) $crop = array_fill(0, $count, $crop);
if (!is_array($ref2)) $ref2 = array_fill(0, $count, $ref2);
}
$counter = 0;
$pairs = [];
foreach ($type as $index => $typeValue) {
// ?? 0 ca safety, sa nu mai dea niciodata warning
$pairs[] = '(0, '.(int) $typeValue.', '.(int) ($from[$index] ?? 0).', '.(int) ($to[$index] ?? 0).', '.(int) ($ref[$index] ?? 0).', '.(int) ($ref2[$index] ?? 0).', '.(int) ($time[$index] ?? 0).', '.(int) ($endtime[$index] ?? 0).', 0, '.(int) ($send[$index] ?? 1).', '.(int) ($wood[$index] ?? 0).', '.(int) ($clay[$index] ?? 0).', '.(int) ($iron[$index] ?? 0).', '.(int) ($crop[$index] ?? 0).')';
if ($counter++ > 25) {
$q = "INSERT INTO " . TB_PREFIX . "movement (moveid, sort_type, `from`, `to`, ref, ref2, starttime, endtime, proc, send, wood, clay, iron, crop) VALUES ".implode(', ', $pairs);
mysqli_query($this->dblink,$q);
$pairs = [];
$counter = 0;
}
}
$counter = 0;
$pairs = [];
foreach ($type as $index => $typeValue) {
$pairs[] = '(0, '.(int) $typeValue.', '.(int) $from[$index].', '.(int) $to[$index].', '.(int) $ref[$index].', '.(int) $ref2[$index].', '.(int) $time[$index].', '.(int) $endtime[$index].', 0, '.(int) $send[$index].', '.(int) $wood[$index].', '.(int) $clay[$index].', '.(int) $iron[$index].', '.(int) $crop[$index].')';
if ($counter++ > 25) {
$q = "INSERT INTO " . TB_PREFIX . "movement (moveid, sort_type, `from`, `to`, ref, ref2, starttime, endtime, proc, send, wood, clay, iron, crop) VALUES ".implode(', ', $pairs);
mysqli_query($this->dblink,$q);
$pairs = [];
$counter = 0;
}
}
if ($counter > 0) {
$q = "INSERT INTO " . TB_PREFIX . "movement (moveid, sort_type, `from`, `to`, ref, ref2, starttime, endtime, proc, send, wood, clay, iron, crop) VALUES " . implode( ', ', $pairs );
return mysqli_query( $this->dblink, $q );
} else {
return true;
}
}
if ($counter > 0) {
$q = "INSERT INTO " . TB_PREFIX . "movement (moveid, sort_type, `from`, `to`, ref, ref2, starttime, endtime, proc, send, wood, clay, iron, crop) VALUES " . implode( ', ', $pairs );
return mysqli_query( $this->dblink, $q );
} else {
return true;
}
}
function addAttack($vid, $t1, $t2, $t3, $t4, $t5, $t6, $t7, $t8, $t9, $t10, $t11, $type, $ctar1, $ctar2, $spy,$b1=0,$b2=0,$b3=0,$b4=0,$b5=0,$b6=0,$b7=0,$b8=0) {
if (!is_array($vid)) {
+37
View File
@@ -4200,3 +4200,40 @@ tz_def('ADM_THE_7_DAY_BALANCE_FILLS_IN_AS_MERCHANT_DELIV', 'The 7-day balance fi
tz_def('ADM_EDIT_THE_REWARD_EACH_QUEST_GRANTS_WOOD_CLAY', 'Edit the reward each quest grants (wood / clay / iron / crop / gold / Plus days) and the requirement level (e.g. main-building level for building quests). Values are seeded from the shipped defaults, so nothing changes until you edit. The two quest variants have different quests and rewards &mdash; pick the one your server uses (players on');
tz_def('ADM_IN_THE_QUEST_TEMPLATES_QUESTS_MARKED', 'in the quest templates. Quests marked');
tz_def('ADM_KEEP_THEIR_ORIGINAL_HARDCODED_LOGIC_CONDITIO', 'keep their original hardcoded logic (conditional rewards, atomic milestone claims, special mechanics) and are not affected by edits here. The reward numbers shown inside each quest\'s on-screen text are separate template strings &mdash; edits here change what is actually granted; update the quest language strings if you want the preview to match.');
//////////////////////////////////////////////////////////////////////////////////////////////////////
// BONUSURI DE ALIANTA (port T4)
//////////////////////////////////////////////////////////////////////////////////////////////////////
tz_def('ALLYBONUS_TAB', 'Bonuses');
tz_def('ALLYBONUS_RECRUITMENT', 'Recruitment');
tz_def('ALLYBONUS_PHILOSOPHY', 'Philosophy');
tz_def('ALLYBONUS_METALLURGY', 'Metallurgy');
tz_def('ALLYBONUS_COMMERCE', 'Commerce');
tz_def('ALLYBONUS_LEVEL', 'Level');
tz_def('ALLYBONUS_NEXT', 'next');
tz_def('ALLYBONUS_MAXED', 'Fully unlocked');
tz_def('ALLYBONUS_UNLOCKING', 'Unlocking level');
tz_def('ALLYBONUS_DONATE', 'Donate');
tz_def('ALLYBONUS_TRIPLE', 'Triple this donation');
tz_def('ALLYBONUS_DAILY_LEFT', 'Your donation allowance left today');
tz_def('ALLYBONUS_CONTRIBUTORS', 'Contributions');
tz_def('ALLYBONUS_MEMBER', 'Member');
tz_def('ALLYBONUS_TOTAL', 'Donated');
tz_def('ALLYBONUS_MSG_OK', 'Resources donated.');
tz_def('ALLYBONUS_MSG_UPGRADING', 'This bonus is currently being unlocked; donations are paused.');
tz_def('ALLYBONUS_MSG_LIMIT', 'That would exceed your daily donation limit.');
tz_def('ALLYBONUS_MSG_RESOURCES', 'Not enough resources in this village.');
tz_def('ALLYBONUS_MSG_NOGOLD', 'Not enough gold to triple this donation.');
tz_def('ALLYBONUS_MSG_NOALLY', 'You are not in an alliance.');
tz_def('ALLYBONUS_MSG_INVALID', 'Invalid donation.');
tz_def('ALLYBONUS_HINT', 'The resource type does not matter, only the total amount. While a level is unlocking, that bonus cannot receive donations.');
//////////////////////////////////////////////////////////////////////////////////////////////////////
// PANOU DE ADMINISTRARE - pachet grafic si bonusuri de alianta
//////////////////////////////////////////////////////////////////////////////////////////////////////
tz_def('ADM_SERVER_GRAPHIC_PACK', 'Server graphic pack');
tz_def('ADM_SERVER_GRAPHIC_PACK_TIP', 'The graphic pack every player sees by default, read from the gpack/ folder.');
tz_def('ADM_PLAYER_GRAPHIC_PACKS', 'Player graphic packs');
tz_def('ADM_PLAYER_GRAPHIC_PACKS_TIP', 'When enabled, players can point their profile at their own graphic pack (Profile &rarr; Graphics). When disabled, everyone sees the server pack.');
tz_def('ADM_ALLIANCE_BONUSES', 'Alliance bonuses');
tz_def('ADM_ALLIANCE_BONUSES_TIP', 'T4 alliance bonuses: members donate resources to unlock Recruitment, Philosophy, Metallurgy and Commerce.');
+37
View File
@@ -4143,3 +4143,40 @@ tz_def('ADM_THE_7_DAY_BALANCE_FILLS_IN_AS_MERCHANT_DELIV', 'Le bilan sur 7 jours
tz_def('ADM_EDIT_THE_REWARD_EACH_QUEST_GRANTS_WOOD_CLAY', 'Modifiez la récompense de chaque quête (bois / argile / fer / céréales / or / jours de Plus) et le niveau requis (p. ex. niveau du bâtiment principal pour les quêtes de construction). Les valeurs proviennent des réglages livrés par défaut, rien ne change tant que vous ne modifiez pas. Les deux variantes de quêtes ont des quêtes et des récompenses différentes &mdash; choisissez celle qu\'utilise votre serveur (les joueurs en');
tz_def('ADM_IN_THE_QUEST_TEMPLATES_QUESTS_MARKED', 'dans les modèles de quêtes. Les quêtes marquées');
tz_def('ADM_KEEP_THEIR_ORIGINAL_HARDCODED_LOGIC_CONDITIO', 'conservent leur logique d\'origine codée en dur (récompenses conditionnelles, réclamations d\'étapes atomiques, mécaniques spéciales) et ne sont pas affectées par ces modifications. Les montants affichés dans le texte de chaque quête sont des chaînes de modèle distinctes &mdash; les modifications ici changent ce qui est réellement accordé ; mettez à jour les chaînes de langue si vous voulez que l\'aperçu corresponde.');
//////////////////////////////////////////////////////////////////////////////////////////////////////
// BONUSURI DE ALIANTA (port T4)
//////////////////////////////////////////////////////////////////////////////////////////////////////
tz_def('ALLYBONUS_TAB', 'Bonus');
tz_def('ALLYBONUS_RECRUITMENT', 'Recrutement');
tz_def('ALLYBONUS_PHILOSOPHY', 'Philosophie');
tz_def('ALLYBONUS_METALLURGY', 'Métallurgie');
tz_def('ALLYBONUS_COMMERCE', 'Commerce');
tz_def('ALLYBONUS_LEVEL', 'Niveau');
tz_def('ALLYBONUS_NEXT', 'suivant');
tz_def('ALLYBONUS_MAXED', 'Entièrement débloqué');
tz_def('ALLYBONUS_UNLOCKING', 'Déblocage du niveau');
tz_def('ALLYBONUS_DONATE', 'Faire un don');
tz_def('ALLYBONUS_TRIPLE', 'Tripler ce don');
tz_def('ALLYBONUS_DAILY_LEFT', 'Votre quota de don restant aujourd\'hui');
tz_def('ALLYBONUS_CONTRIBUTORS', 'Contributions');
tz_def('ALLYBONUS_MEMBER', 'Membre');
tz_def('ALLYBONUS_TOTAL', 'Donné');
tz_def('ALLYBONUS_MSG_OK', 'Ressources données.');
tz_def('ALLYBONUS_MSG_UPGRADING', 'Ce bonus est en cours de déblocage ; les dons sont suspendus.');
tz_def('ALLYBONUS_MSG_LIMIT', 'Cela dépasserait votre limite de don quotidienne.');
tz_def('ALLYBONUS_MSG_RESOURCES', 'Pas assez de ressources dans ce village.');
tz_def('ALLYBONUS_MSG_NOGOLD', 'Pas assez d\'or pour tripler ce don.');
tz_def('ALLYBONUS_MSG_NOALLY', 'Vous n\'êtes pas dans une alliance.');
tz_def('ALLYBONUS_MSG_INVALID', 'Don invalide.');
tz_def('ALLYBONUS_HINT', 'Le type de ressource n\'a pas d\'importance, seul le total compte. Pendant le déblocage d\'un niveau, ce bonus n\'accepte plus de dons.');
//////////////////////////////////////////////////////////////////////////////////////////////////////
// PANOU DE ADMINISTRARE - pachet grafic si bonusuri de alianta
//////////////////////////////////////////////////////////////////////////////////////////////////////
tz_def('ADM_SERVER_GRAPHIC_PACK', 'Pack graphique du serveur');
tz_def('ADM_SERVER_GRAPHIC_PACK_TIP', 'Le pack graphique que voient tous les joueurs par défaut, lu depuis le dossier gpack/.');
tz_def('ADM_PLAYER_GRAPHIC_PACKS', 'Packs graphiques des joueurs');
tz_def('ADM_PLAYER_GRAPHIC_PACKS_TIP', 'Si activé, les joueurs peuvent choisir leur propre pack graphique depuis leur profil (Profil &rarr; Graphismes). Si désactivé, tout le monde voit le pack du serveur.');
tz_def('ADM_ALLIANCE_BONUSES', 'Bonus d\'alliance');
tz_def('ADM_ALLIANCE_BONUSES_TIP', 'Bonus d\'alliance T4 : les membres donnent des ressources pour débloquer Recrutement, Philosophie, Métallurgie et Commerce.');
+37
View File
@@ -3940,3 +3940,40 @@ tz_def('ADM_THE_7_DAY_BALANCE_FILLS_IN_AS_MERCHANT_DELIV', 'Bilantul pe 7 zile s
tz_def('ADM_EDIT_THE_REWARD_EACH_QUEST_GRANTS_WOOD_CLAY', 'Editeaza recompensa acordata de fiecare misiune (lemn / lut / fier / cereale / aur / zile de Plus) si nivelul necesar (de ex. nivelul cladirii principale pentru misiunile de constructie). Valorile pornesc de la cele implicite livrate, deci nu se schimba nimic pana nu editezi. Cele doua variante de misiuni au misiuni si recompense diferite &mdash; alege-o pe cea folosita de serverul tau (jucatorii pe');
tz_def('ADM_IN_THE_QUEST_TEMPLATES_QUESTS_MARKED', 'in sabloanele de misiuni. Misiunile marcate');
tz_def('ADM_KEEP_THEIR_ORIGINAL_HARDCODED_LOGIC_CONDITIO', 'isi pastreaza logica originala scrisa in cod (recompense conditionate, revendicari atomice de etape, mecanici speciale) si nu sunt afectate de modificarile de aici. Sumele afisate in textul fiecarei misiuni sunt siruri separate de sablon &mdash; modificarile de aici schimba ce se acorda efectiv; actualizeaza sirurile de limba daca vrei ca previzualizarea sa corespunda.');
//////////////////////////////////////////////////////////////////////////////////////////////////////
// BONUSURI DE ALIANTA (port T4)
//////////////////////////////////////////////////////////////////////////////////////////////////////
tz_def('ALLYBONUS_TAB', 'Bonusuri');
tz_def('ALLYBONUS_RECRUITMENT', 'Recrutare');
tz_def('ALLYBONUS_PHILOSOPHY', 'Filosofie');
tz_def('ALLYBONUS_METALLURGY', 'Metalurgie');
tz_def('ALLYBONUS_COMMERCE', 'Comert');
tz_def('ALLYBONUS_LEVEL', 'Nivel');
tz_def('ALLYBONUS_NEXT', 'urmator');
tz_def('ALLYBONUS_MAXED', 'Deblocat complet');
tz_def('ALLYBONUS_UNLOCKING', 'Se deblocheaza nivelul');
tz_def('ALLYBONUS_DONATE', 'Doneaza');
tz_def('ALLYBONUS_TRIPLE', 'Tripleaza aceasta donatie');
tz_def('ALLYBONUS_DAILY_LEFT', 'Cat mai poti dona azi');
tz_def('ALLYBONUS_CONTRIBUTORS', 'Contributii');
tz_def('ALLYBONUS_MEMBER', 'Membru');
tz_def('ALLYBONUS_TOTAL', 'Donat');
tz_def('ALLYBONUS_MSG_OK', 'Resurse donate.');
tz_def('ALLYBONUS_MSG_UPGRADING', 'Acest bonus se deblocheaza acum; donatiile sunt oprite.');
tz_def('ALLYBONUS_MSG_LIMIT', 'Ai depasi plafonul zilnic de donatie.');
tz_def('ALLYBONUS_MSG_RESOURCES', 'Nu ai destule resurse in acest sat.');
tz_def('ALLYBONUS_MSG_NOGOLD', 'Nu ai destul aur ca sa triplezi donatia.');
tz_def('ALLYBONUS_MSG_NOALLY', 'Nu esti intr-o alianta.');
tz_def('ALLYBONUS_MSG_INVALID', 'Donatie invalida.');
tz_def('ALLYBONUS_HINT', 'Tipul resursei nu conteaza, ci doar totalul. Cat timp un nivel se deblocheaza, bonusul acela nu mai primeste donatii.');
//////////////////////////////////////////////////////////////////////////////////////////////////////
// PANOU DE ADMINISTRARE - pachet grafic si bonusuri de alianta
//////////////////////////////////////////////////////////////////////////////////////////////////////
tz_def('ADM_SERVER_GRAPHIC_PACK', 'Pachetul grafic al serverului');
tz_def('ADM_SERVER_GRAPHIC_PACK_TIP', 'Pachetul grafic pe care il vad implicit toti jucatorii, citit din folderul gpack/.');
tz_def('ADM_PLAYER_GRAPHIC_PACKS', 'Pachete grafice ale jucatorilor');
tz_def('ADM_PLAYER_GRAPHIC_PACKS_TIP', 'Daca e activat, jucatorii isi pot alege propriul pachet grafic din profil (Profil &rarr; Grafica). Daca e dezactivat, toata lumea vede pachetul serverului.');
tz_def('ADM_ALLIANCE_BONUSES', 'Bonusuri de alianta');
tz_def('ADM_ALLIANCE_BONUSES_TIP', 'Bonusuri de alianta T4: membrii doneaza resurse ca sa deblocheze Recrutare, Filosofie, Metalurgie si Comert.');
+8
View File
@@ -181,6 +181,14 @@ class Market
if ($building->getTypeLevel(28) != 0) {
$this->maxcarry *= $bid28[$building->getTypeLevel(28)]['attri'] / 100;
}
// Bonusul de alianta "Commerce". Se inmulteste cu biroul comercial, ca
// in T4: 750 de baza x 2.2 (Commerce 4) x 5 (birou 20) = 8250.
if (class_exists('AllianceBonus') && AllianceBonus::enabled()) {
$this->maxcarry *= AllianceBonus::multiplier((int) $session->uid, AllianceBonus::COMMERCE);
}
$this->maxcarry = floor($this->maxcarry);
}
/**
+38 -1
View File
@@ -645,7 +645,12 @@ class Technology {
// 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);
// Bonusul de alianta "Recruitment": instruire mai rapida in toate
// cladirile de trupe. Se inmulteste cu bonusul eroului, ca in T4 (coiful
// mercenarului si bonusul de alianta se cumuleaza multiplicativ).
$each = $this->applyAllianceRecruitmentBonus($each);
return $each;
}
@@ -657,6 +662,38 @@ class Technology {
* jucatorului. HeroBattleBonus::bonuses() are deja gard de feature flag si
* cache per request, si intoarce null cand sistemul T4 e oprit.
*/
/**
* Aplica bonusul de alianta "Recruitment" asupra timpului de instruire.
*
* Proprietarul se ia din satul in care se instruieste, nu din sesiune, ca sa
* fie corect si cand codul ruleaza in alt context (cron, sitter).
*/
private function applyAllianceRecruitmentBonus($each) {
global $village, $database;
if (!class_exists('AllianceBonus') || !AllianceBonus::enabled()) {
return $each;
}
// acelasi mod de a afla proprietarul ca la bonusul de erou de mai jos
$owner = (isset($village->wid) && $database)
? (int) $database->getVillageField($village->wid, 'owner')
: 0;
if ($owner <= 0) {
return $each;
}
$mult = AllianceBonus::multiplier($owner, AllianceBonus::RECRUITMENT);
if ($mult <= 1.0) {
return $each;
}
// bonusul de X% inseamna productie mai rapida, deci timp impartit
return max(1, (int) round($each / $mult));
}
private function applyHeroTrainingBonus($each, $unit, array $footies, array $calvary) {
global $village, $database;