From 3c99623a3342894b40d42ec2b9564e2d5a9bbfe6 Mon Sep 17 00:00:00 2001 From: novgorodschi catalin Date: Fri, 10 Jul 2026 09:15:42 +0300 Subject: [PATCH] Add T4 Hero System (configurable on install) Add T4 Hero System (configurable on install with TRUE/FALS) part of #285 -Adventures -Merchants -Equipment --- Admin/Templates/config.tpl | 1 + Admin/Templates/editHeroT4.tpl | 185 ++++++ Admin/Templates/editNewFunctions.tpl | 11 + Admin/Templates/map.tpl | 2 +- Admin/Templates/playerheroinfo.tpl | 5 + Admin/admin.php | 12 +- GameEngine/Admin/Mods/editExtraSet.php | 2 + GameEngine/Admin/Mods/editHeroT4.php | 131 +++++ GameEngine/Admin/Mods/editLogSet.php | 2 + GameEngine/Admin/Mods/editNewFunctions.php | 1 + GameEngine/Admin/Mods/editNewsboxSet.php | 2 + GameEngine/Admin/Mods/editPlusSet.php | 2 + GameEngine/Admin/Mods/editServerSet.php | 2 + GameEngine/Automation.php | 50 +- GameEngine/Battle.php | 33 +- GameEngine/Data/hero_items.php | 307 ++++++++++ GameEngine/Database.php | 9 + GameEngine/HeroAdventure.php | 643 +++++++++++++++++++++ GameEngine/HeroAuction.php | 533 +++++++++++++++++ GameEngine/HeroBattleBonus.php | 529 +++++++++++++++++ GameEngine/HeroItems.php | 632 ++++++++++++++++++++ GameEngine/Lang/en.php | 107 ++++ GameEngine/Message.php | 8 + GameEngine/Units.php | 7 +- Templates/Build/16.tpl | 5 + Templates/Build/16_walking.tpl | 35 ++ Templates/Build/37.tpl | 16 + Templates/Build/37_adventures.tpl | 78 +++ Templates/Build/37_auction.tpl | 160 +++++ Templates/Build/37_items.tpl | 112 ++++ Templates/Build/37_t4nav.tpl | 28 + Templates/Notice/26.tpl | 120 ++++ Templates/Notice/27.tpl | 95 +++ Templates/movement.tpl | 42 ++ activate.php | 24 +- ajax.php | 23 +- anleitung.php | 24 +- build_croppers.php | 2 +- css/hero_items.css | 116 ++++ img/hero/items.png | Bin 0 -> 180064 bytes img/hero/mapping.json | 199 +++++++ install/data/constant_format.tpl | 1 + install/process.php | 1 + install/templates/config.tpl | 3 +- var/db/struct.sql | 72 +++ var/tools/generate_hero_sprites.php | 27 + 46 files changed, 4367 insertions(+), 32 deletions(-) create mode 100644 Admin/Templates/editHeroT4.tpl create mode 100644 GameEngine/Admin/Mods/editHeroT4.php create mode 100644 GameEngine/Data/hero_items.php create mode 100644 GameEngine/HeroAdventure.php create mode 100644 GameEngine/HeroAuction.php create mode 100644 GameEngine/HeroBattleBonus.php create mode 100644 GameEngine/HeroItems.php create mode 100644 Templates/Build/37_adventures.tpl create mode 100644 Templates/Build/37_auction.tpl create mode 100644 Templates/Build/37_items.tpl create mode 100644 Templates/Build/37_t4nav.tpl create mode 100644 Templates/Notice/26.tpl create mode 100644 Templates/Notice/27.tpl create mode 100644 css/hero_items.css create mode 100644 img/hero/items.png create mode 100644 img/hero/mapping.json create mode 100644 var/tools/generate_hero_sprites.php diff --git a/Admin/Templates/config.tpl b/Admin/Templates/config.tpl index 64650d82..9a8c4f51 100644 --- a/Admin/Templates/config.tpl +++ b/Admin/Templates/config.tpl @@ -118,6 +118,7 @@ $editIcon = 'Special Medals?Special MedalsEnabled" : "Disabled"; ?> Server Milestones?Server MilestonesEnabled" : "Disabled"; ?> Server Medal Reset Timer?Server Medal Reset TimerEnabled" : "Disabled"; ?> + T4 Hero (items, adventures, auction)?T4 Hero (items, adventures, auction)Enabled" : "Disabled"; ?> diff --git a/Admin/Templates/editHeroT4.tpl b/Admin/Templates/editHeroT4.tpl new file mode 100644 index 00000000..9547f6c1 --- /dev/null +++ b/Admin/Templates/editHeroT4.tpl @@ -0,0 +1,185 @@ +Missing uid.

'; return; } +$id = (int) $_GET['uid']; + +include_once("../GameEngine/Data/hero_items.php"); +include_once("../GameEngine/HeroItems.php"); +include_once("../GameEngine/HeroAuction.php"); +include_once("../GameEngine/HeroAdventure.php"); + +$t4HeroItems = new HeroItems(); +$t4Auction = new HeroAuction(); +$t4Adv = new HeroAdventure(); + +$t4Silver = $t4HeroItems->getSilver($id); +$t4Inventory = $t4HeroItems->getInventory($id); +$t4Sales = $t4Auction->getMySales($id); +$t4Bids = $t4Auction->getMyBids($id); +$t4Offers = $t4Adv->getOffers($id); +$t4Running = $t4Adv->getRunning($id); +$t4SlotNames = [1=>'Helmet',2=>'Body',3=>'Right hand',4=>'Left hand',5=>'Shoes',6=>'Horse',7=>'Bag']; +?> + + +

T4 Hero controls — user # + « classic hero editor +

+ +
Action completed.
+
Action failed (check the values and try again).
+ +
+

Silver

+
+
+ + + + + +
+
+
+ +
+

Grant item

+
+
+ + + + + Qty: + +
+
+
+ +
+

Inventory ()

+
+ + + + + + + + + + + + + +
RowItemSlotQtyEquipped
#(orphan itemid ' . (int) $t4Row['itemid'] . ')'; ?> +
+ + + + + +
+
+ No items. +
+
+ +
+

Open auctions by this player () / bids ()

+
+ + + + $t4Sales, 'role' => 'seller'], ['rows' => $t4Bids, 'role' => 'top bidder']] as $t4Group) { ?> + + + + + + + + + + + +
AuctionRoleItemPriceEnds
# (x) +
+ + + + + +
+
+ No open auction involvement. +
+
+ +
+

Adventures

+
+ +

Running: adventure #, + difficulty , + arrives (do not delete running adventures).

+ + + + + + + + + + + + + +
OfferDifficultyDurationExpires
# +
+ + + + + +
+
+ No available offers. +
+
diff --git a/Admin/Templates/editNewFunctions.tpl b/Admin/Templates/editNewFunctions.tpl index bc8113ba..0ff40069 100644 --- a/Admin/Templates/editNewFunctions.tpl +++ b/Admin/Templates/editNewFunctions.tpl @@ -232,6 +232,17 @@ if($_SESSION['access'] < 9) die(ACCESS_DENIED_ADMIN); + + + T4 Hero (items, adventures, auction) ?Enable (Disable) the "T4 Hero (items, adventures, auction)" System +
Not present in config.php yet — saving this form once will add it (defaults to False until then). + + + + diff --git a/Admin/Templates/map.tpl b/Admin/Templates/map.tpl index ebc9987e..2225e789 100644 --- a/Admin/Templates/map.tpl +++ b/Admin/Templates/map.tpl @@ -173,7 +173,7 @@ body{margin:0;background:#f1f5f9;font-family:system-ui,-apple-system,Segoe UI,Ro &hid=' title="Edit"> + + + + + diff --git a/Admin/admin.php b/Admin/admin.php index 6892f67d..005b66f1 100644 --- a/Admin/admin.php +++ b/Admin/admin.php @@ -91,7 +91,7 @@ function admin_validated_page(string $raw): string 'editExtraSet', 'editAdminInfo', 'resetServer', 'player', 'editUser', 'deletion', 'Newmessage', 'editPlus', 'editSitter', 'editPassword', 'editProtection', 'editOverall', - 'editWeek', 'userlogin', 'userillegallog', 'editHero', 'editAdditional', + 'editWeek', 'userlogin', 'userillegallog', 'editHero', 'editHeroT4', 'editAdditional', 'village', 'editResources', 'addTroops', 'addABTroops', 'editVillage', 'villagelog', 'techlog', 'msg', 'alliance', 'editAli', 'delAli','editNewFunctions', @@ -433,6 +433,16 @@ if ($page !== '') { } break; + case 'editHeroT4': + $uid = admin_input_id($_GET, 'uid'); + if ($uid !== null) { + $user = $database->getUserArray($uid, 1); + $subpage = 'T4 Hero Controls (' . e($user['username']) . ')'; + } else { + $subpage = 'T4 Hero Controls'; + } + break; + case 'editAdditional': $uid = admin_input_id($_GET, 'uid'); if ($uid !== null) { diff --git a/GameEngine/Admin/Mods/editExtraSet.php b/GameEngine/Admin/Mods/editExtraSet.php index 5ed682ee..51c57d7e 100755 --- a/GameEngine/Admin/Mods/editExtraSet.php +++ b/GameEngine/Admin/Mods/editExtraSet.php @@ -73,6 +73,7 @@ $fh = fopen($myFile, 'w') or die("


Can't open file: GameEngine\con $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM = (NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM == false ? 'false' : 'true'); $NEW_FUNCTIONS_MILESTONES = (NEW_FUNCTIONS_MILESTONES == false ? 'false' : 'true'); $NEW_FUNCTIONS_MEDAL_RESET = (NEW_FUNCTIONS_MEDAL_RESET == false ? 'false' : 'true'); + $NEW_FUNCTIONS_HERO_T4 = (NEW_FUNCTIONS_HERO_T4 == false ? 'false' : 'true'); $text = admin_config_template_contents(); $text = preg_replace("'%ERRORREPORT%'", ERROR_REPORT, $text); @@ -164,6 +165,7 @@ $fh = fopen($myFile, 'w') or die("


Can't open file: GameEngine\con $text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM, $text); $text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $NEW_FUNCTIONS_MILESTONES, $text); $text = preg_replace("'%NEW_FUNCTIONS_MEDAL_RESET%'", $NEW_FUNCTIONS_MEDAL_RESET, $text); + $text = preg_replace("'%NEW_FUNCTIONS_HERO_T4%'", $NEW_FUNCTIONS_HERO_T4, $text); // PLUS settings need to be kept intact $text = preg_replace("'%PLUS_TIME%'", PLUS_TIME, $text); diff --git a/GameEngine/Admin/Mods/editHeroT4.php b/GameEngine/Admin/Mods/editHeroT4.php new file mode 100644 index 00000000..6201a56a --- /dev/null +++ b/GameEngine/Admin/Mods/editHeroT4.php @@ -0,0 +1,131 @@ += 9, same conventions as ## +## editHero.php): ## +## t4admin=setsilver uid, silver -> absolute silver balance ## +## t4admin=giveitem uid, itemid, qty -> grant catalog item ## +## t4admin=delitem uid, rowid -> delete inventory row ## +## t4admin=cancelauction aucid -> refund + return item ## +## t4admin=deladventure advid -> remove an offer ## +## ## +################################################################################# + +// Load CSRF helpers + admin_deny() before the access check (same as editHero.php #299). +require_once(__DIR__ . '/../csrf.php'); +if (!isset($_SESSION)) { + session_start(); +} +if (empty($_SESSION['access']) || $_SESSION['access'] < 9) { + admin_deny('You must be signed in as an administrator to view this page. Your session may have expired — please return to the admin panel and sign in again.'); +} + +// POSTed to directly, so it verifies the CSRF token itself (#139). +csrf_verify(); + +// --------------------------------------------------------------------------- +// Autoloader path (same discovery loop as the other Mods) +// --------------------------------------------------------------------------- +$autoprefix = ''; +for ($i = 0; $i < 5; $i++) { + $autoprefix = str_repeat('../', $i); + if (file_exists($autoprefix . 'autoloader.php')) { + break; + } +} + +include_once($autoprefix . "GameEngine/config.php"); +include_once($autoprefix . "GameEngine/Database.php"); +include_once($autoprefix . "GameEngine/Data/hero_items.php"); +include_once($autoprefix . "GameEngine/HeroItems.php"); +include_once($autoprefix . "GameEngine/HeroAuction.php"); + +$uid = (int) ($_POST['uid'] ?? 0); +$action = (string) ($_POST['t4admin'] ?? ''); +$status = '&e=1'; // error by default; flipped to &ok=1 on success + +$heroItems = new HeroItems(); + +switch ($action) { + + case 'setsilver': + // Absolute set (not delta) - simplest to reason about as an admin. + $silver = max(0, (int) ($_POST['silver'] ?? 0)); + if ($uid > 0) { + $stmt = $database->dblink->prepare( + "UPDATE " . TB_PREFIX . "hero SET silver = ? WHERE uid = ? LIMIT 1" + ); + $stmt->bind_param('ii', $silver, $uid); + $stmt->execute(); + if ($stmt->affected_rows > 0 || $heroItems->getSilver($uid) === $silver) { + $status = '&ok=1'; + } + $stmt->close(); + } + break; + + case 'giveitem': + $itemid = (int) ($_POST['itemid'] ?? 0); + $qty = max(1, (int) ($_POST['qty'] ?? 1)); + if ($uid > 0 && $heroItems->addItem($uid, $itemid, $qty) > 0) { + $status = '&ok=1'; + } + break; + + case 'delitem': + // Hard delete of an inventory row (equipped or not) - admin override. + $rowid = (int) ($_POST['rowid'] ?? 0); + if ($uid > 0 && $rowid > 0) { + $stmt = $database->dblink->prepare( + "DELETE FROM " . TB_PREFIX . "hero_items WHERE id = ? AND uid = ? LIMIT 1" + ); + $stmt->bind_param('ii', $rowid, $uid); + $stmt->execute(); + if ($stmt->affected_rows > 0) { + $status = '&ok=1'; + } + $stmt->close(); + } + break; + + case 'cancelauction': + $aucid = (int) ($_POST['aucid'] ?? 0); + $auction = new HeroAuction(); + if ($aucid > 0 && $auction->adminCancel($aucid)) { + $status = '&ok=1'; + } + break; + + case 'deladventure': + // Remove an available offer (status 0 only - never a running one, + // that would strand the hero mid-movement). + $advid = (int) ($_POST['advid'] ?? 0); + if ($advid > 0) { + $stmt = $database->dblink->prepare( + "DELETE FROM " . TB_PREFIX . "hero_adventure WHERE id = ? AND status = 0 LIMIT 1" + ); + $stmt->bind_param('i', $advid); + $stmt->execute(); + if ($stmt->affected_rows > 0) { + $status = '&ok=1'; + } + $stmt->close(); + } + break; +} + +header("Location: ../../../Admin/admin.php?p=editHeroT4&uid=$uid$status"); +exit; diff --git a/GameEngine/Admin/Mods/editLogSet.php b/GameEngine/Admin/Mods/editLogSet.php index 5230e643..faca4cb9 100755 --- a/GameEngine/Admin/Mods/editLogSet.php +++ b/GameEngine/Admin/Mods/editLogSet.php @@ -69,6 +69,7 @@ $fh = fopen($myFile, 'w') or die("


Can't open file: GameEngine\con $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM = (NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM == false ? 'false' : 'true'); $NEW_FUNCTIONS_MILESTONES = (NEW_FUNCTIONS_MILESTONES == false ? 'false' : 'true'); $NEW_FUNCTIONS_MEDAL_RESET = (NEW_FUNCTIONS_MEDAL_RESET == false ? 'false' : 'true'); + $NEW_FUNCTIONS_HERO_T4 = (NEW_FUNCTIONS_HERO_T4 == false ? 'false' : 'true'); $text = admin_config_template_contents(); $text = preg_replace("'%ERRORREPORT%'", $ERRORREPORT, $text); @@ -164,6 +165,7 @@ $fh = fopen($myFile, 'w') or die("


Can't open file: GameEngine\con $text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM, $text); $text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $NEW_FUNCTIONS_MILESTONES, $text); $text = preg_replace("'%NEW_FUNCTIONS_MEDAL_RESET%'", $NEW_FUNCTIONS_MEDAL_RESET, $text); + $text = preg_replace("'%NEW_FUNCTIONS_HERO_T4%'", $NEW_FUNCTIONS_HERO_T4, $text); // PLUS settings need to be kept intact $text = preg_replace("'%PLUS_TIME%'", PLUS_TIME, $text); diff --git a/GameEngine/Admin/Mods/editNewFunctions.php b/GameEngine/Admin/Mods/editNewFunctions.php index 1ff6e27f..9056df49 100644 --- a/GameEngine/Admin/Mods/editNewFunctions.php +++ b/GameEngine/Admin/Mods/editNewFunctions.php @@ -131,6 +131,7 @@ $fh = fopen($myFile, 'w') or die("


Can't open file: GameEngine\con $text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $_POST['new_functions_special_medals_system'], $text); $text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $_POST['new_functions_milestones'], $text); $text = preg_replace("'%NEW_FUNCTIONS_MEDAL_RESET%'", $_POST['new_functions_medal_reset'], $text); + $text = preg_replace("'%NEW_FUNCTIONS_HERO_T4%'", $_POST['new_functions_hero_t4'], $text); // PLUS settings need to be kept intact $text = preg_replace("'%PLUS_TIME%'", PLUS_TIME, $text); diff --git a/GameEngine/Admin/Mods/editNewsboxSet.php b/GameEngine/Admin/Mods/editNewsboxSet.php index 2660bbaf..2e5958cd 100755 --- a/GameEngine/Admin/Mods/editNewsboxSet.php +++ b/GameEngine/Admin/Mods/editNewsboxSet.php @@ -75,6 +75,7 @@ $fh = fopen($myFile, 'w') or die("


Can't open file: GameEngine\con $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM = (NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM == false ? 'false' : 'true'); $NEW_FUNCTIONS_MILESTONES = (NEW_FUNCTIONS_MILESTONES == false ? 'false' : 'true'); $NEW_FUNCTIONS_MEDAL_RESET = (NEW_FUNCTIONS_MEDAL_RESET == false ? 'false' : 'true'); + $NEW_FUNCTIONS_HERO_T4 = (NEW_FUNCTIONS_HERO_T4 == false ? 'false' : 'true'); $text = admin_config_template_contents(); $text = preg_replace("'%ERRORREPORT%'", $ERRORREPORT, $text); @@ -170,6 +171,7 @@ $fh = fopen($myFile, 'w') or die("


Can't open file: GameEngine\con $text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM, $text); $text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $NEW_FUNCTIONS_MILESTONES, $text); $text = preg_replace("'%NEW_FUNCTIONS_MEDAL_RESET%'", $NEW_FUNCTIONS_MEDAL_RESET, $text); + $text = preg_replace("'%NEW_FUNCTIONS_HERO_T4%'", $NEW_FUNCTIONS_HERO_T4, $text); // PLUS settings need to be kept intact $text = preg_replace("'%PLUS_TIME%'", PLUS_TIME, $text); diff --git a/GameEngine/Admin/Mods/editPlusSet.php b/GameEngine/Admin/Mods/editPlusSet.php index 4450aff9..313e863b 100644 --- a/GameEngine/Admin/Mods/editPlusSet.php +++ b/GameEngine/Admin/Mods/editPlusSet.php @@ -57,6 +57,7 @@ $fh = fopen($myFile, 'w') or die("


Can't open file: GameEngine\con $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM = (NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM == false ? 'false' : 'true'); $NEW_FUNCTIONS_MILESTONES = (NEW_FUNCTIONS_MILESTONES == false ? 'false' : 'true'); $NEW_FUNCTIONS_MEDAL_RESET = (NEW_FUNCTIONS_MEDAL_RESET == false ? 'false' : 'true'); + $NEW_FUNCTIONS_HERO_T4 = (NEW_FUNCTIONS_HERO_T4 == false ? 'false' : 'true'); // SERVER SETTINGS - we need to keep these intact $text = preg_replace("'%ERRORREPORT%'", ERROR_REPORT, $text); @@ -148,6 +149,7 @@ $fh = fopen($myFile, 'w') or die("


Can't open file: GameEngine\con $text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM, $text); $text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $NEW_FUNCTIONS_MILESTONES, $text); $text = preg_replace("'%NEW_FUNCTIONS_MEDAL_RESET%'", $NEW_FUNCTIONS_MEDAL_RESET, $text); + $text = preg_replace("'%NEW_FUNCTIONS_HERO_T4%'", $NEW_FUNCTIONS_HERO_T4, $text); // PLUS SETTINGS $text = preg_replace("'%PLUS_TIME%'", $_POST['plus_time'], $text); diff --git a/GameEngine/Admin/Mods/editServerSet.php b/GameEngine/Admin/Mods/editServerSet.php index 6ab73c8c..826abc9a 100755 --- a/GameEngine/Admin/Mods/editServerSet.php +++ b/GameEngine/Admin/Mods/editServerSet.php @@ -68,6 +68,7 @@ $fh = fopen($myFile, 'w') or die("


Can't open file: GameEngine\con $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM = (NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM == false ? 'false' : 'true'); $NEW_FUNCTIONS_MILESTONES = (NEW_FUNCTIONS_MILESTONES == false ? 'false' : 'true'); $NEW_FUNCTIONS_MEDAL_RESET = (NEW_FUNCTIONS_MEDAL_RESET == false ? 'false' : 'true'); + $NEW_FUNCTIONS_HERO_T4 = (NEW_FUNCTIONS_HERO_T4 == false ? 'false' : 'true'); $text = admin_config_template_contents(); $text = preg_replace("'%ERRORREPORT%'", $_POST['error'], $text); @@ -159,6 +160,7 @@ $fh = fopen($myFile, 'w') or die("


Can't open file: GameEngine\con $text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM, $text); $text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $NEW_FUNCTIONS_MILESTONES, $text); $text = preg_replace("'%NEW_FUNCTIONS_MEDAL_RESET%'", $NEW_FUNCTIONS_MEDAL_RESET, $text); + $text = preg_replace("'%NEW_FUNCTIONS_HERO_T4%'", $NEW_FUNCTIONS_HERO_T4, $text); // PLUS settings need to be kept intact $text = preg_replace("'%PLUS_TIME%'", PLUS_TIME, $text); diff --git a/GameEngine/Automation.php b/GameEngine/Automation.php index db44a779..727b2b0d 100644 --- a/GameEngine/Automation.php +++ b/GameEngine/Automation.php @@ -95,7 +95,8 @@ class Automation { "trainingComplete", "starvation", "celebrationComplete", "festivalComplete", "sendUnitsComplete", "loyaltyRegeneration", "sendreinfunitsComplete", "returnunitsComplete", "sendSettlersComplete", "spawnNatars", - "spawnWWVillages", "spawnWWBuildingPlans", "activateArtifacts"]; + "spawnWWVillages", "spawnWWBuildingPlans", "activateArtifacts", + "heroAdventureComplete"]; foreach($methodsArrays as $method){ $file = fopen($autoprefix."GameEngine/Prevention/".$method.".txt", "w"); @@ -2885,9 +2886,17 @@ class Automation { { $returningTroops = []; for($i = 1; $i <= 11; $i++) $returningTroops['t'.$i] = $data['t'.$i] - $traped[$i] - $dead[$i]; + // T4 hero port (Phase 6): bandages revive part of the + // losses when the hero comes home alive; revived units + // are written back to the attacks row (the source + // returnunitsComplete reads on arrival). No-op when off. + $returningTroops = HeroBattleBonus::applyBandages($from['owner'], $returningTroops, $dead, (int) $data['ref']); $troopsTime = $units->getWalkingTroopsTime($from['wref'], $to['wref'], $from['owner'], $owntribe, $returningTroops, 1, 't'); $endtime = $database->getArtifactsValueInfluence($from['owner'], $from['wref'], 2, $troopsTime); $endtime += $AttackArrivalTime; + // T4 hero port (Phase 5): the map shortens the return + // leg when the SURVIVING hero travels home. No-op when off. + $endtime = HeroBattleBonus::adjustReturnEndtime($from['owner'], $returningTroops['t11'], $AttackArrivalTime, $endtime); if($type == 1){ if($from['owner'] == 3){ // fix natar report by ronix $database->addNotice($to['owner'], $to['wref'], $targetally, 20, '' . addslashes($from['name']) . ' scouts ' . addslashes($to['name']) . '', $data2, $AttackArrivalTime); @@ -3130,6 +3139,15 @@ class Automation { $enforDefender = $defUnits['enforDefender']; $enforcementarray = $defUnits['enforcementarray']; + // T4 hero port (Phase 8): cages capture animals on an + // UNOCCUPIED oasis before the fight - captured animals do + // not defend and are stationed at home as reinforcements. + // No-op when the flag is off, no hero rides along, the + // oasis is owned, or the attacker has no cages. + if ($isoasis != 0 && (int) $conqureby === 0 && (int) $data['t11'] > 0) { + $Defender = HeroBattleBonus::applyCages($from['owner'], (int) $data['from'], (int) $data['to'], $Defender); + } + // attacker army built — extracted to buildAttackerUnits() [#155] $atkUnits = $this->buildAttackerUnits($dataarray[$data_num], $owntribe, $isoasis); $Attacker = $atkUnits['Attacker']; @@ -3363,7 +3381,10 @@ class Automation { $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']); + // T4 hero port (Phase 5): thief sacks raise the carry + // bounty when the attacker's living hero joined the strike. + $heroBounty = (int) round($battlepart['bounty'] * HeroBattleBonus::raidMultiplier($from['owner'], $data['t11'])); + $steal = $this->applyBounty([$avwood, $avclay, $aviron, $avcrop], $heroBounty); //chiefing village — extracted to handleConquest() [#155] if (!isset($village_destroyed)) $village_destroyed = 0; @@ -3431,6 +3452,9 @@ class Automation { $troopsTime = $units->getWalkingTroopsTime($from['wref'], $to['wref'], $from['owner'], $owntribe, $data, 1, 't'); $endtime = $database->getArtifactsValueInfluence($from['owner'], $from['wref'], 2, $troopsTime); $endtime += $AttackArrivalTime; + // T4 hero port (Phase 5): map return-speed bonus, same as + // the combat return leg above. No-op when the flag is off. + $endtime = HeroBattleBonus::adjustReturnEndtime($from['owner'], $data['t11'], $AttackArrivalTime, $endtime); $database->setMovementProc($data['moveid']); $database->addMovement(4, $to['wref'], $from['wref'], $data['ref'], $AttackArrivalTime, $endtime); @@ -4338,6 +4362,28 @@ class Automation { } + /** + * T4 Hero port (Phase 3+4): process adventure arrivals (sort_type 20, + * rewards + ntype 26 report + return movement), returns (sort_type 21, + * hero re-enters units.hero, loot credited), top up offer lists, then + * finalize ended auctions and restock the NPC merchant. + * Fully feature-flagged - a no-op unless NEW_FUNCTIONS_HERO_T4 is enabled. + */ + private function heroAdventureComplete() { + if (!defined('NEW_FUNCTIONS_HERO_T4') || !NEW_FUNCTIONS_HERO_T4) { + return; + } + + $adventures = new HeroAdventure(); + $adventures->processArrivals(); + $adventures->processReturns(); + $adventures->generateOffersBatch(); + + $auctions = new HeroAuction(); + $auctions->processFinished(); + $auctions->seedNpcAuctions(); + } + private function updateHero() { global $database, $hero_levels; diff --git a/GameEngine/Battle.php b/GameEngine/Battle.php index fa994310..afc07ba2 100644 --- a/GameEngine/Battle.php +++ b/GameEngine/Battle.php @@ -190,6 +190,16 @@ class Battle { $h_ob = 1 + (0.010 * ($attackBonus / 5)); $h_db = 1 + (0.010 * ($defenceBonus / 5)); + // T4 hero port (Phase 5): flat fighting strength from equipped items + // (weapons/cuirasses/shields) counts on whichever side the hero fights. + // No-ops to 0 when NEW_FUNCTIONS_HERO_T4 is off. + $itemStrength = HeroBattleBonus::statBonus($uid); + if ($itemStrength > 0) { + $h_atk += $itemStrength; + $h_di += $itemStrength; + $h_dc += $itemStrength; + } + return [ 'heroid' => (int)$hero['heroid'], 'unit' => $heroUnit, @@ -198,6 +208,8 @@ class Battle { 'dc' => $h_dc, 'ob' => $h_ob, 'db' => $h_db, + // T4 hero port: carried along for the offense/defense/damage hooks. + 'uid' => (int)$uid, 'health' => isset($hero['health']) ? (int)$hero['health'] : 0 @@ -550,6 +562,14 @@ class Battle { $units['Def_unit']['hero'] = $Defender['hero']; $own_cdp += $defenderhero['dc']; $own_dp += $defenderhero['di']; + + // T4 hero port (Phase 5): weapon adds +N defense per unit of + // its type in the defending army, counted in both pools + // (see HeroBattleBonus::unitDefense). No-op when flag is off. + $itemDef = HeroBattleBonus::unitDefense($defenderhero['uid'] ?? 0, $Defender); + $own_dp += $itemDef; + $own_cdp += $itemDef; + $own_dp *= $defenderhero['db']; $own_cdp *= $defenderhero['db']; } @@ -762,7 +782,14 @@ class Battle { $ap *= $atkhero['ob']; $cap *= $atkhero['ob']; - $ap += $atkhero['atk']; + // T4 hero port (Phase 5): hunting horn boosts the HERO's own + // contribution vs the Natars (uid 3); weapon adds +N attack per + // accompanying unit of its type. Neutral no-ops when the flag is off. + $ap += $atkhero['atk'] * HeroBattleBonus::natarMultiplier($atkhero['uid'] ?? 0, $DefenderID); + + list($itemAp, $itemCap) = HeroBattleBonus::unitOffense($atkhero['uid'] ?? 0, $Attacker, $calvaryLookup); + $ap += $itemAp; + $cap += $itemCap; } if ($offhero > 0 || $hero_strenght > 0) { @@ -1136,6 +1163,10 @@ class Battle { $hero_health = (int)$fdb['health']; $damage_health = round(100 * $result[1]); + // T4 hero port (Phase 5): armors reduce the health damage the + // hero takes in one battle (flat, floored at 0). No-op when off. + $damage_health = HeroBattleBonus::reduceDamage($atkhero['uid'] ?? 0, $damage_health); + if ($hero_health <= $damage_health || $damage_health > 90) { $result['casualties_attacker'][11] = 1; diff --git a/GameEngine/Data/hero_items.php b/GameEngine/Data/hero_items.php new file mode 100644 index 00000000..9d14d00e --- /dev/null +++ b/GameEngine/Data/hero_items.php @@ -0,0 +1,307 @@ + lang constant ## +## first and falls back to the English catalog name. ## +## ## +################################################################################# + +/* ============================================================================ + * GAME CONSTANTS (unchanged from Phase 1; guarded against re-includes) + * movement.sort_type in use: 0,2,3,4,5,6,10,11,12,13 -> 20/21 free + * attacks.attack_type in use: 1,2 -> 9 free + * ndata.ntype mapped up to 25 (settlers) -> 26 free + * ------------------------------------------------------------------------- */ +if (!defined('MOVEMENT_ADVENTURE_OUT')) define('MOVEMENT_ADVENTURE_OUT', 20); +if (!defined('MOVEMENT_ADVENTURE_BACK')) define('MOVEMENT_ADVENTURE_BACK', 21); +if (!defined('ATTACK_TYPE_ADVENTURE')) define('ATTACK_TYPE_ADVENTURE', 9); +if (!defined('NTYPE_ADVENTURE_REPORT')) define('NTYPE_ADVENTURE_REPORT', 26); +if (!defined('NTYPE_AUCTION_REPORT')) define('NTYPE_AUCTION_REPORT', 27); + +/* Equipment slots */ +if (!defined('HSLOT_HELMET')) define('HSLOT_HELMET', 1); +if (!defined('HSLOT_BODY')) define('HSLOT_BODY', 2); +if (!defined('HSLOT_RIGHT')) define('HSLOT_RIGHT', 3); // weapon +if (!defined('HSLOT_LEFT')) define('HSLOT_LEFT', 4); // shield/horn/sack/map/pennant/standard +if (!defined('HSLOT_SHOES')) define('HSLOT_SHOES', 5); // boots + spurs +if (!defined('HSLOT_HORSE')) define('HSLOT_HORSE', 6); +if (!defined('HSLOT_BAG')) define('HSLOT_BAG', 7); // stackable consumables + +/* ============================================================================ + * BONUS TYPES (keys inside $item['bonus']) + * Flat vs percent is documented per constant. Consumers: HeroItems::getBonuses() + * (Phase 2), Battle/Automation/a2b integration (Phase 5). + * ------------------------------------------------------------------------- */ +if (!defined('HB_FIGHT')) define('HB_FIGHT', 'fight_strength'); // flat hero fighting strength +if (!defined('HB_UNIT_BONUS')) define('HB_UNIT_BONUS', 'unit_bonus'); // ['unit'=>uX,'per_unit'=>N] +N off & +N def per unit of that type in the hero's army +if (!defined('HB_VS_NATARS')) define('HB_VS_NATARS', 'vs_natars'); // +% hero strength vs Natars +if (!defined('HB_RAID')) define('HB_RAID', 'raid_percent'); // +% resources stolen on raids +if (!defined('HB_RETURN_SPEED')) define('HB_RETURN_SPEED', 'return_speed'); // +% troop return speed +if (!defined('HB_SPEED_OWN')) define('HB_SPEED_OWN', 'speed_own'); // +% speed between own villages +if (!defined('HB_SPEED_ALLY')) define('HB_SPEED_ALLY', 'speed_ally'); // +% speed between ally villages +if (!defined('HB_XP')) define('HB_XP', 'xp_percent'); // +% hero experience +if (!defined('HB_REGEN_HP')) define('HB_REGEN_HP', 'regen_hp'); // flat +HP per day +if (!defined('HB_CP')) define('HB_CP', 'culture_points'); // flat +culture points per day +if (!defined('HB_TRAIN_CAV')) define('HB_TRAIN_CAV', 'train_cavalry'); // -% cavalry training time +if (!defined('HB_TRAIN_INF')) define('HB_TRAIN_INF', 'train_infantry'); // -% infantry training time +if (!defined('HB_DMG_REDUCE')) define('HB_DMG_REDUCE', 'damage_reduce'); // flat damage reduction per hit +if (!defined('HB_TROOP_SPEED_20')) define('HB_TROOP_SPEED_20', 'troop_speed_20'); // +% troop speed beyond 20 tiles +if (!defined('HB_MOUNT_SPEED')) define('HB_MOUNT_SPEED', 'mount_speed'); // flat +fields/hour, ONLY while a horse is equipped (spurs) +if (!defined('HB_HORSE_SPEED')) define('HB_HORSE_SPEED', 'horse_speed'); // flat hero speed in fields/hour (horse) +/* consumables */ +if (!defined('HB_HEAL_SELF')) define('HB_HEAL_SELF', 'heal_self'); // +1% hero HP per unit (max 99%, hero must be alive) +if (!defined('HB_SCROLL')) define('HB_SCROLL', 'scroll_xp'); // +XP per scroll +if (!defined('HB_BUCKET')) define('HB_BUCKET', 'bucket'); // instant free revive +if (!defined('HB_LOYALTY')) define('HB_LOYALTY', 'loyalty'); // +1% own village loyalty per tablet (max 125%) +if (!defined('HB_RESET')) define('HB_RESET', 'reset_points'); // reset attribute points +if (!defined('HB_ARTWORK')) define('HB_ARTWORK', 'artwork_cp'); // CP = daily production (cap: 5000 normal / 2500 speed) +if (!defined('HB_HEAL_TROOP')) define('HB_HEAL_TROOP', 'heal_troops'); // heals % of the hero's army (bandages) +if (!defined('HB_CAGE')) define('HB_CAGE', 'cage'); // traps 1 oasis animal per cage + +/* ============================================================================ + * ITEM CATALOG + * itemid => [ + * 'name' => English display name, + * 'slot' => HSLOT_*, + * 'tier' => 1..3, + * 'bonus' => [HB_* => value, ...], + * 'unit' => uX (weapons only: hero must BE this unit to equip), + * 'x2_on_speed' => true (value doubles on speed servers, per design sheet: + * "+3 (1x) / +6 (3x)" - runtime multiplies by 2 when SPEED >= 3), + * 'requires_horse' => true (spurs: bonus only counts with a horse equipped) + * ] + * + * ID ranges: 1-15 helmets | 20-31 body | 40-57 left hand | 60-68 shoes + * 70-72 horses | 101-145 weapons | 200-208 consumables + * ------------------------------------------------------------------------- */ +$heroItemCatalog = array( + + /* ---------------- HELMETS (sheet: Coifuri) ---------------- */ + 1 => array('name' => 'Helmet of Awareness', 'slot' => HSLOT_HELMET, 'tier' => 1, 'bonus' => array(HB_XP => 15)), + 2 => array('name' => 'Helmet of Enlightenment', 'slot' => HSLOT_HELMET, 'tier' => 2, 'bonus' => array(HB_XP => 20)), + 3 => array('name' => 'Helmet of Wisdom', 'slot' => HSLOT_HELMET, 'tier' => 3, 'bonus' => array(HB_XP => 25)), + 4 => array('name' => 'Helmet of Regeneration', 'slot' => HSLOT_HELMET, 'tier' => 1, 'bonus' => array(HB_REGEN_HP => 10)), + 5 => array('name' => 'Helmet of Health', 'slot' => HSLOT_HELMET, 'tier' => 2, 'bonus' => array(HB_REGEN_HP => 15)), + 6 => array('name' => 'Helmet of Healing', 'slot' => HSLOT_HELMET, 'tier' => 3, 'bonus' => array(HB_REGEN_HP => 20)), + 7 => array('name' => 'Helmet of the Gladiator', 'slot' => HSLOT_HELMET, 'tier' => 1, 'bonus' => array(HB_CP => 100), 'x2_on_speed' => false), // sheet: 100/day (1x), 50 (3x) -> HALVED on speed; handled below + 8 => array('name' => 'Helmet of the Tribune', 'slot' => HSLOT_HELMET, 'tier' => 2, 'bonus' => array(HB_CP => 400), 'x2_on_speed' => false), + 9 => array('name' => 'Helmet of the Consul', 'slot' => HSLOT_HELMET, 'tier' => 3, 'bonus' => array(HB_CP => 800), 'x2_on_speed' => false), + 10 => array('name' => 'Helmet of the Horseman', 'slot' => HSLOT_HELMET, 'tier' => 1, 'bonus' => array(HB_TRAIN_CAV => 10)), + 11 => array('name' => 'Helmet of the Cavalry', 'slot' => HSLOT_HELMET, 'tier' => 2, 'bonus' => array(HB_TRAIN_CAV => 15)), + 12 => array('name' => 'Helmet of the Heavy Cavalry', 'slot' => HSLOT_HELMET, 'tier' => 3, 'bonus' => array(HB_TRAIN_CAV => 20)), + 13 => array('name' => 'Helmet of the Mercenary', 'slot' => HSLOT_HELMET, 'tier' => 1, 'bonus' => array(HB_TRAIN_INF => 10)), + 14 => array('name' => 'Helmet of the Warrior', 'slot' => HSLOT_HELMET, 'tier' => 2, 'bonus' => array(HB_TRAIN_INF => 15)), + 15 => array('name' => 'Helmet of the Ruler', 'slot' => HSLOT_HELMET, 'tier' => 3, 'bonus' => array(HB_TRAIN_INF => 20)), + + /* ---------------- BODY ARMOUR (sheet: Armuri) ---------------- */ + 20 => array('name' => 'Light Breastplate of Regeneration', 'slot' => HSLOT_BODY, 'tier' => 1, 'bonus' => array(HB_REGEN_HP => 20)), + 21 => array('name' => 'Breastplate of Regeneration', 'slot' => HSLOT_BODY, 'tier' => 2, 'bonus' => array(HB_REGEN_HP => 30)), + 22 => array('name' => 'Heavy Breastplate of Regeneration', 'slot' => HSLOT_BODY, 'tier' => 3, 'bonus' => array(HB_REGEN_HP => 40)), + 23 => array('name' => 'Light Armor', 'slot' => HSLOT_BODY, 'tier' => 1, 'bonus' => array(HB_DMG_REDUCE => 4, HB_REGEN_HP => 10)), + 24 => array('name' => 'Armor', 'slot' => HSLOT_BODY, 'tier' => 2, 'bonus' => array(HB_DMG_REDUCE => 6, HB_REGEN_HP => 15)), + 25 => array('name' => 'Heavy Armor', 'slot' => HSLOT_BODY, 'tier' => 3, 'bonus' => array(HB_DMG_REDUCE => 8, HB_REGEN_HP => 20)), + 26 => array('name' => 'Light Cuirass', 'slot' => HSLOT_BODY, 'tier' => 1, 'bonus' => array(HB_FIGHT => 500)), + 27 => array('name' => 'Cuirass', 'slot' => HSLOT_BODY, 'tier' => 2, 'bonus' => array(HB_FIGHT => 1000)), + 28 => array('name' => 'Heavy Cuirass', 'slot' => HSLOT_BODY, 'tier' => 3, 'bonus' => array(HB_FIGHT => 1500)), + 29 => array('name' => 'Light Segmented Armor', 'slot' => HSLOT_BODY, 'tier' => 1, 'bonus' => array(HB_DMG_REDUCE => 3, HB_FIGHT => 250)), + 30 => array('name' => 'Segmented Armor', 'slot' => HSLOT_BODY, 'tier' => 2, 'bonus' => array(HB_DMG_REDUCE => 4, HB_FIGHT => 500)), + 31 => array('name' => 'Heavy Segmented Armor', 'slot' => HSLOT_BODY, 'tier' => 3, 'bonus' => array(HB_DMG_REDUCE => 5, HB_FIGHT => 750)), + + /* ---------------- LEFT HAND (sheets: Mana_Stanga + Diverse) ---------------- */ + 40 => array('name' => 'Small Shield', 'slot' => HSLOT_LEFT, 'tier' => 1, 'bonus' => array(HB_FIGHT => 500)), + 41 => array('name' => 'Shield', 'slot' => HSLOT_LEFT, 'tier' => 2, 'bonus' => array(HB_FIGHT => 1000)), + 42 => array('name' => 'Great Shield', 'slot' => HSLOT_LEFT, 'tier' => 3, 'bonus' => array(HB_FIGHT => 1500)), + 43 => array('name' => 'Small Hunting Horn', 'slot' => HSLOT_LEFT, 'tier' => 1, 'bonus' => array(HB_VS_NATARS => 20)), + 44 => array('name' => 'Hunting Horn', 'slot' => HSLOT_LEFT, 'tier' => 2, 'bonus' => array(HB_VS_NATARS => 25)), + 45 => array('name' => 'Great Hunting Horn', 'slot' => HSLOT_LEFT, 'tier' => 3, 'bonus' => array(HB_VS_NATARS => 30)), + 46 => array('name' => "Thief's Satchel", 'slot' => HSLOT_LEFT, 'tier' => 1, 'bonus' => array(HB_RAID => 10)), + 47 => array('name' => "Thief's Bag", 'slot' => HSLOT_LEFT, 'tier' => 2, 'bonus' => array(HB_RAID => 15)), + 48 => array('name' => "Thief's Sack", 'slot' => HSLOT_LEFT, 'tier' => 3, 'bonus' => array(HB_RAID => 20)), + 49 => array('name' => 'Small Map', 'slot' => HSLOT_LEFT, 'tier' => 1, 'bonus' => array(HB_RETURN_SPEED => 30)), + 50 => array('name' => 'Map', 'slot' => HSLOT_LEFT, 'tier' => 2, 'bonus' => array(HB_RETURN_SPEED => 40)), + 51 => array('name' => 'Great Map', 'slot' => HSLOT_LEFT, 'tier' => 3, 'bonus' => array(HB_RETURN_SPEED => 50)), + 52 => array('name' => 'Small Pennant', 'slot' => HSLOT_LEFT, 'tier' => 1, 'bonus' => array(HB_SPEED_OWN => 30)), + 53 => array('name' => 'Pennant', 'slot' => HSLOT_LEFT, 'tier' => 2, 'bonus' => array(HB_SPEED_OWN => 40)), + 54 => array('name' => 'Great Pennant', 'slot' => HSLOT_LEFT, 'tier' => 3, 'bonus' => array(HB_SPEED_OWN => 50)), + 55 => array('name' => 'Small Standard', 'slot' => HSLOT_LEFT, 'tier' => 1, 'bonus' => array(HB_SPEED_ALLY => 15)), + 56 => array('name' => 'Standard', 'slot' => HSLOT_LEFT, 'tier' => 2, 'bonus' => array(HB_SPEED_ALLY => 20)), + 57 => array('name' => 'Great Standard', 'slot' => HSLOT_LEFT, 'tier' => 3, 'bonus' => array(HB_SPEED_ALLY => 25)), + + /* ---------------- SHOES (sheet: Incaltaminte_Cai - boots + spurs) ---------------- */ + 60 => array('name' => 'Boots of Regeneration', 'slot' => HSLOT_SHOES, 'tier' => 1, 'bonus' => array(HB_REGEN_HP => 10)), + 61 => array('name' => 'Boots of Recovery', 'slot' => HSLOT_SHOES, 'tier' => 2, 'bonus' => array(HB_REGEN_HP => 15)), + 62 => array('name' => 'Boots of Healing', 'slot' => HSLOT_SHOES, 'tier' => 3, 'bonus' => array(HB_REGEN_HP => 20)), + 63 => array('name' => 'Boots of the Mercenary', 'slot' => HSLOT_SHOES, 'tier' => 1, 'bonus' => array(HB_TROOP_SPEED_20 => 25)), + 64 => array('name' => 'Boots of the Warrior', 'slot' => HSLOT_SHOES, 'tier' => 2, 'bonus' => array(HB_TROOP_SPEED_20 => 50)), + 65 => array('name' => 'Boots of the Ruler', 'slot' => HSLOT_SHOES, 'tier' => 3, 'bonus' => array(HB_TROOP_SPEED_20 => 75)), + // Spurs share the shoes slot (worn on boots) and only apply while a horse is equipped. + // Design sheet: "+3 f/h (1x) / +6 (3x)" -> base value is the 1x one, doubled on speed servers. + 66 => array('name' => 'Small Spurs', 'slot' => HSLOT_SHOES, 'tier' => 1, 'bonus' => array(HB_MOUNT_SPEED => 3), 'x2_on_speed' => true, 'requires_horse' => true), + 67 => array('name' => 'Spurs', 'slot' => HSLOT_SHOES, 'tier' => 2, 'bonus' => array(HB_MOUNT_SPEED => 4), 'x2_on_speed' => true, 'requires_horse' => true), + 68 => array('name' => 'Great Spurs', 'slot' => HSLOT_SHOES, 'tier' => 3, 'bonus' => array(HB_MOUNT_SPEED => 5), 'x2_on_speed' => true, 'requires_horse' => true), + + /* ---------------- HORSES (sheet: Incaltaminte_Cai) ---------------- */ + 70 => array('name' => 'Riding Horse', 'slot' => HSLOT_HORSE, 'tier' => 1, 'bonus' => array(HB_HORSE_SPEED => 14), 'x2_on_speed' => true), + 71 => array('name' => 'Thoroughbred', 'slot' => HSLOT_HORSE, 'tier' => 2, 'bonus' => array(HB_HORSE_SPEED => 17), 'x2_on_speed' => true), + 72 => array('name' => 'Warhorse', 'slot' => HSLOT_HORSE, 'tier' => 3, 'bonus' => array(HB_HORSE_SPEED => 20), 'x2_on_speed' => true), + + /* ---------------- WEAPONS (sheets: Romani / Barbari / Daci) ---------------- + * Weapons are unit-bound: the hero must be trained from that unit. + * Each gives flat fight strength (500/1000/1500 by tier) plus + * +N off AND +N def per unit of that type accompanying the hero. */ + + // Romans - u1 Legionnaire + 101 => array('name' => "Legionnaire's Short Sword", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 1, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 1, 'per_unit' => 3))), + 102 => array('name' => "Legionnaire's Sword", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 1, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 1, 'per_unit' => 4))), + 103 => array('name' => "Legionnaire's Long Sword", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 1, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 1, 'per_unit' => 5))), + // Romans - u2 Praetorian + 104 => array('name' => "Praetorian's Short Sword", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 2, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 2, 'per_unit' => 3))), + 105 => array('name' => "Praetorian's Sword", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 2, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 2, 'per_unit' => 4))), + 106 => array('name' => "Praetorian's Long Sword", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 2, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 2, 'per_unit' => 5))), + // Romans - u3 Imperian + 107 => array('name' => "Imperian's Short Sword", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 3, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 3, 'per_unit' => 3))), + 108 => array('name' => "Imperian's Sword", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 3, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 3, 'per_unit' => 4))), + 109 => array('name' => "Imperian's Long Sword", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 3, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 3, 'per_unit' => 5))), + // Romans - u5 Equites Imperatoris + 110 => array('name' => "Imperatoris' Short Saber", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 5, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 5, 'per_unit' => 9))), + 111 => array('name' => "Imperatoris' Saber", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 5, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 5, 'per_unit' => 12))), + 112 => array('name' => "Imperatoris' Long Saber", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 5, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 5, 'per_unit' => 15))), + // Romans - u6 Equites Caesaris + 113 => array('name' => "Caesaris' Light Lance", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 6, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 6, 'per_unit' => 12))), + 114 => array('name' => "Caesaris' Lance", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 6, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 6, 'per_unit' => 16))), + 115 => array('name' => "Caesaris' Heavy Lance", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 6, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 6, 'per_unit' => 20))), + + // Teutons - u11 Clubswinger + 116 => array('name' => "Clubswinger's Cudgel", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 11, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 11, 'per_unit' => 3))), + 117 => array('name' => "Clubswinger's Club", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 11, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 11, 'per_unit' => 4))), + 118 => array('name' => "Clubswinger's Mace", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 11, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 11, 'per_unit' => 5))), + // Teutons - u12 Spearman + 119 => array('name' => "Spearman's Pitchfork", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 12, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 12, 'per_unit' => 3))), + 120 => array('name' => "Spearman's Pike", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 12, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 12, 'per_unit' => 4))), + 121 => array('name' => "Spearman's Spear", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 12, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 12, 'per_unit' => 5))), + // Teutons - u13 Axeman + 122 => array('name' => "Axeman's Hatchet", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 13, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 13, 'per_unit' => 3))), + 123 => array('name' => "Axeman's Axe", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 13, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 13, 'per_unit' => 4))), + 124 => array('name' => "Axeman's Battle Axe", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 13, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 13, 'per_unit' => 5))), + // Teutons - u15 Paladin + 125 => array('name' => "Paladin's Small Hammer", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 15, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 15, 'per_unit' => 6))), + 126 => array('name' => "Paladin's Hammer", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 15, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 15, 'per_unit' => 8))), + 127 => array('name' => "Paladin's Sledgehammer", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 15, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 15, 'per_unit' => 10))), + // Teutons - u16 Teutonic Knight + 128 => array('name' => "Teutonic Knight's Short Sword", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 16, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 16, 'per_unit' => 9))), + 129 => array('name' => "Teutonic Knight's Sword", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 16, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 16, 'per_unit' => 12))), + 130 => array('name' => "Teutonic Knight's Long Sword", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 16, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 16, 'per_unit' => 15))), + + // Gauls - u21 Phalanx (design sheet tribe "Daci": Scutier) + 131 => array('name' => "Phalanx's Pitchfork", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 21, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 21, 'per_unit' => 3))), + 132 => array('name' => "Phalanx's Spear", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 21, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 21, 'per_unit' => 4))), + 133 => array('name' => "Phalanx's Lance", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 21, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 21, 'per_unit' => 5))), + // Gauls - u22 Swordsman (Pedestras) + 134 => array('name' => "Swordsman's Short Sword", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 22, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 22, 'per_unit' => 3))), + 135 => array('name' => "Swordsman's Sword", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 22, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 22, 'per_unit' => 4))), + 136 => array('name' => "Swordsman's Long Sword", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 22, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 22, 'per_unit' => 5))), + // Gauls - u24 Theutates Thunder (Calaret Fulger) + 137 => array('name' => "Thunder's Short Bow", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 24, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 24, 'per_unit' => 6))), + 138 => array('name' => "Thunder's Bow", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 24, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 24, 'per_unit' => 8))), + 139 => array('name' => "Thunder's Long Bow", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 24, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 24, 'per_unit' => 10))), + // Gauls - u25 Druidrider + 140 => array('name' => "Druidrider's Baton", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 25, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 25, 'per_unit' => 6))), + 141 => array('name' => "Druidrider's Staff", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 25, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 25, 'per_unit' => 8))), + 142 => array('name' => "Druidrider's Great Staff", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 25, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 25, 'per_unit' => 10))), + // Gauls - u26 Haeduan (Tarabostes) + 143 => array('name' => "Haeduan's Light Lance", 'slot' => HSLOT_RIGHT, 'tier' => 1, 'unit' => 26, 'bonus' => array(HB_FIGHT => 500, HB_UNIT_BONUS => array('unit' => 26, 'per_unit' => 9))), + 144 => array('name' => "Haeduan's Lance", 'slot' => HSLOT_RIGHT, 'tier' => 2, 'unit' => 26, 'bonus' => array(HB_FIGHT => 1000, HB_UNIT_BONUS => array('unit' => 26, 'per_unit' => 12))), + 145 => array('name' => "Haeduan's Heavy Lance", 'slot' => HSLOT_RIGHT, 'tier' => 3, 'unit' => 26, 'bonus' => array(HB_FIGHT => 1500, HB_UNIT_BONUS => array('unit' => 26, 'per_unit' => 15))), + + /* ---------------- CONSUMABLES (sheet: Consumabile; stackable, bag) ---------------- */ + 200 => array('name' => 'Ointment', 'slot' => HSLOT_BAG, 'tier' => 1, 'bonus' => array(HB_HEAL_SELF => 1)), // +1% HP per unit, max 99%, hero must be alive + 201 => array('name' => 'Scroll', 'slot' => HSLOT_BAG, 'tier' => 1, 'bonus' => array(HB_SCROLL => 10)), // +10 XP per scroll + 202 => array('name' => 'Bucket of Water', 'slot' => HSLOT_BAG, 'tier' => 2, 'bonus' => array(HB_BUCKET => 1)), // instant revive, no resource cost + 203 => array('name' => 'Law Tablet', 'slot' => HSLOT_BAG, 'tier' => 2, 'bonus' => array(HB_LOYALTY => 1)), // +1% own village loyalty, max 125% + 204 => array('name' => 'Book of Wisdom', 'slot' => HSLOT_BAG, 'tier' => 3, 'bonus' => array(HB_RESET => 1)), // reset attribute points + 205 => array('name' => 'Artwork', 'slot' => HSLOT_BAG, 'tier' => 3, 'bonus' => array(HB_ARTWORK => 1)), // CP = daily production, cap 5000 (1x) / 2500 (speed) + 206 => array('name' => 'Small Bandage', 'slot' => HSLOT_BAG, 'tier' => 1, 'bonus' => array(HB_HEAL_TROOP => 25)), // heals 25% of hero's army + 207 => array('name' => 'Bandage', 'slot' => HSLOT_BAG, 'tier' => 2, 'bonus' => array(HB_HEAL_TROOP => 33)), // heals 33% of hero's army + 208 => array('name' => 'Cage', 'slot' => HSLOT_BAG, 'tier' => 1, 'bonus' => array(HB_CAGE => 1)), // traps 1 oasis animal per cage +); + +/* Helmets 7-9 (culture) are HALVED on speed servers per the design sheet + * ("+100/day (1x) / +50 (3x)"). Runtime consumers must apply: + * value = (SPEED >= 3) ? value / 2 : value + * whenever 'x2_on_speed' === false is explicitly set (see HeroItems::scaledBonusValue()). */ + +/* ============================================================================ + * ADVENTURE CONFIGURATION (Phase 1, itemids updated to the new catalog; + * equipment drops added per design feedback: adventures are the primary + * T4 source of gear, rarer than consumables and weighted toward low tiers) + * ------------------------------------------------------------------------- */ +$heroAdventureConfig = array( + 'offer_lifetime' => 24 * 3600, + 'max_offers' => 3, + 'refresh_interval' => 8 * 3600, + + // Tier weights for equipment drops (percent, must sum to 100). + 'equip_tier_weights' => array(1 => 70, 2 => 25, 3 => 5), + + 0 => array( // NORMAL + 'label' => 'HERO_ADV_NORMAL', + 'exp' => array(6, 12), + 'silver' => array(0, 40), + 'hp_loss' => array(0, 8), + 'resources' => array(0, 400), + 'equip_chance' => 3, // % chance: 1 random piece of gear + 'item_chance' => 5, // % chance: consumables (rolled only if no gear dropped) + 'consumable' => array(200, 201, 208), // Ointment, Scroll, Cage + ), + 1 => array( // HARD + 'label' => 'HERO_ADV_HARD', + 'exp' => array(18, 30), + 'silver' => array(30, 120), + 'hp_loss' => array(10, 35), + 'resources' => array(200, 1200), + 'equip_chance' => 10, + 'item_chance' => 20, + 'consumable' => array(200, 201, 203, 206, 208), // + Law Tablet, Small Bandage + ), +); + +/* Convenience index: itemids grouped by slot. */ +$heroSlotIndex = array(); +foreach ($heroItemCatalog as $iid => $def) { + $heroSlotIndex[$def['slot']][] = $iid; +} + +/** + * Resolve the display name of an item. Checks for a HERO_ITEM_ lang + * constant first (future localisation hook), falls back to the English + * catalog name, and finally to 'Unknown item' for ids not in the catalog. + */ +if (!function_exists('heroItemName')) { + function heroItemName($itemid) { + global $heroItemCatalog; + $const = 'HERO_ITEM_' . (int) $itemid; + if (defined($const)) { + return constant($const); + } + return isset($heroItemCatalog[$itemid]['name']) ? $heroItemCatalog[$itemid]['name'] : 'Unknown item'; + } +} diff --git a/GameEngine/Database.php b/GameEngine/Database.php index 4e66370e..4904dddc 100755 --- a/GameEngine/Database.php +++ b/GameEngine/Database.php @@ -5863,6 +5863,15 @@ References: User ID/Message ID, Mode case 8: $q = "SELECT * FROM " . TB_PREFIX . "movement, " . TB_PREFIX . "attacks where " . TB_PREFIX . "movement.`" . $where . "` IN(".implode(', ', $village).") and " . 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 = 1 ORDER BY endtime ASC"; break; + // T4 hero port: hero travelling to an adventure (plain select, + // ref points to hero_adventure - NOT to attacks, so no join). + case 20: + $q = "SELECT * FROM " . TB_PREFIX . "movement where " . TB_PREFIX . "movement.`" . $where . "` IN(".implode(', ', $village).") and sort_type = 20 and proc = 0 ORDER BY endtime ASC"; + break; + // T4 hero port: hero returning from an adventure. + case 21: + $q = "SELECT * FROM " . TB_PREFIX . "movement where " . TB_PREFIX . "movement.`" . $where . "` IN(".implode(', ', $village).") and sort_type = 21 and proc = 0 ORDER BY endtime ASC"; + break; case 34: $q = "SELECT * FROM " . TB_PREFIX . "movement, " . TB_PREFIX . "attacks where " . TB_PREFIX . "movement.`" . $where . "` IN(".implode(', ', $village).") and " . TB_PREFIX . "movement.ref = " . TB_PREFIX . "attacks.id and " . TB_PREFIX . "movement.proc = 0 and " . TB_PREFIX . "movement.sort_type = 3 or " . TB_PREFIX . "movement.`" . $where . "` IN(".implode(', ', $village).") and " . TB_PREFIX . "movement.ref = " . TB_PREFIX . "attacks.id and " . TB_PREFIX . "movement.proc = 0 and " . TB_PREFIX . "movement.sort_type = 4 ORDER BY endtime ASC"; break; diff --git a/GameEngine/HeroAdventure.php b/GameEngine/HeroAdventure.php new file mode 100644 index 00000000..2e84a42f --- /dev/null +++ b/GameEngine/HeroAdventure.php @@ -0,0 +1,643 @@ + running (status 1, movement sort_type 20) ## +## -> arrival processed by Automation (rewards + ntype 26 report, ## +## status 2, return movement sort_type 21 carrying resources) ## +## -> return processed by Automation (hero back in units.hero, ## +## resources credited) ## +## ## +## Death: if HP loss would drop health to 0, the hero dies AT the site ## +## (dead=1, health=0, loot forfeited, no return movement). units.hero was ## +## already decremented at send - identical end state to dying in battle, ## +## so the existing revive flow (37_revive.tpl + handleReviveCompletion) ## +## works unchanged. ## +## ## +## XP: only `experience` is incremented (with the helmet HB_XP% bonus). ## +## Level-up/points are intentionally NOT touched here - Automation's ## +## updateHero()/calculateLevelUp() already handles that every tick. ## +## ## +################################################################################# + +class HeroAdventure +{ + /** Hero base speed on foot, fields/hour (T4 convention). */ + const BASE_HERO_SPEED = 7; + + /** How far from the hero's village adventure sites are picked (tiles). */ + const SITE_MIN_RADIUS = 3; + const SITE_MAX_RADIUS = 25; + + /** Max users to top-up with fresh offers per automation tick (bounded work). */ + const BATCH_LIMIT = 50; + + /** startAdventure() result codes */ + const START_OK = 1; + const START_INVALID = 0; // offer missing/expired/not yours/already running + const START_NO_HERO = -1; // no living hero + const START_HERO_AWAY = -2; // hero not at home village + + /** @var mysqli */ + private $db; + + public function __construct() + { + global $database; + $this->db = $database->dblink; + + global $heroItemCatalog, $heroAdventureConfig; + if (!isset($heroAdventureConfig)) { + include_once __DIR__ . '/Data/hero_items.php'; + } + } + + /* ========================================================================= + * OFFERS + * ===================================================================== */ + + /** Non-expired, still-available offers for a user. */ + public function getOffers($uid) + { + $uid = (int) $uid; $now = time(); + $stmt = $this->db->prepare( + "SELECT id, uid, wref, difficulty, duration, created, expire, status, moveid + FROM " . TB_PREFIX . "hero_adventure + WHERE uid = ? AND status = 0 AND expire > ? + ORDER BY difficulty ASC, duration ASC" + ); + $stmt->bind_param('ii', $uid, $now); + $stmt->execute(); + $res = $stmt->get_result(); + $out = array(); + while ($row = $res->fetch_assoc()) { + $out[] = $row; + } + $stmt->close(); + return $out; + } + + /** The adventure the hero is currently running, or null. */ + public function getRunning($uid) + { + $uid = (int) $uid; + $stmt = $this->db->prepare( + "SELECT a.id, a.wref, a.difficulty, a.moveid, m.endtime, m.sort_type + FROM " . TB_PREFIX . "hero_adventure a + JOIN " . TB_PREFIX . "movement m ON m.moveid = a.moveid + WHERE a.uid = ? AND a.status = 1 AND m.proc = 0 LIMIT 1" + ); + $stmt->bind_param('i', $uid); + $stmt->execute(); + $res = $stmt->get_result(); + $row = $res->fetch_assoc(); + $stmt->close(); + return $row ?: null; + } + + /** + * Top up a single user's offer list (expires stale ones first). + * Respects max_offers and refresh_interval from $heroAdventureConfig. + * Returns the number of offers created. + */ + public function generateOffers($uid) + { + global $database, $heroAdventureConfig; + $uid = (int) $uid; $now = time(); + + // Living hero needed - offers are anchored to the hero's village. + $hero = $this->getLivingHero($uid); + if (!$hero) { + return 0; + } + + // Expire stale offers (kept as rows with status untouched would clutter + // the uid_status index; a hard delete of expired *available* offers is safe). + $stmt = $this->db->prepare( + "DELETE FROM " . TB_PREFIX . "hero_adventure WHERE uid = ? AND status = 0 AND expire <= ?" + ); + $stmt->bind_param('ii', $uid, $now); + $stmt->execute(); + $stmt->close(); + + // Respect refresh interval: don't regenerate too often. + $stmt = $this->db->prepare( + "SELECT COUNT(*) AS cnt, COALESCE(MAX(created), 0) AS latest + FROM " . TB_PREFIX . "hero_adventure WHERE uid = ? AND status = 0" + ); + $stmt->bind_param('i', $uid); + $stmt->execute(); + $res = $stmt->get_result(); + $info = $res->fetch_assoc(); + $stmt->close(); + + $have = (int) $info['cnt']; + $latest = (int) $info['latest']; + $max = (int) $heroAdventureConfig['max_offers']; + + if ($have >= $max) { + return 0; + } + if ($latest > 0 && ($now - $latest) < (int) $heroAdventureConfig['refresh_interval']) { + return 0; + } + + // Pick random unoccupied tiles around the hero's village. + $coor = $database->getCoor($hero['wref']); + if (!$coor) { + return 0; + } + $x = (int) $coor['x']; $y = (int) $coor['y']; $r = self::SITE_MAX_RADIUS; + + $need = $max - $have; + // Min radius enforced in SQL so LIMIT never undercounts (the bounding + // box doesn't wrap around the map seam, so plain coordinate distance + // is exact within it). + $stmt = $this->db->prepare( + "SELECT id, x, y FROM " . TB_PREFIX . "wdata + WHERE occupied = 0 AND id <> ? + AND x BETWEEN ? AND ? AND y BETWEEN ? AND ? + AND (POW(x - ?, 2) + POW(y - ?, 2)) >= ? + ORDER BY RAND() LIMIT ?" + ); + $wref = (int) $hero['wref']; + $x1 = $x - $r; $x2 = $x + $r; $y1 = $y - $r; $y2 = $y + $r; + $minSq = self::SITE_MIN_RADIUS * self::SITE_MIN_RADIUS; + $stmt->bind_param('iiiiiiiii', $wref, $x1, $x2, $y1, $y2, $x, $y, $minSq, $need); + $stmt->execute(); + $res = $stmt->get_result(); + $tiles = array(); + while ($row = $res->fetch_assoc()) { + $tiles[] = $row; + } + $stmt->close(); + + $created = 0; + foreach ($tiles as $tile) { + $dist = $database->getDistance($x, $y, $tile['x'], $tile['y']); + // Hard adventures ~30% of offers. + $difficulty = (mt_rand(1, 100) <= 30) ? 1 : 0; + $duration = $this->travelSeconds($uid, $dist); + $expire = $now + (int) $heroAdventureConfig['offer_lifetime']; + + $stmt = $this->db->prepare( + "INSERT INTO " . TB_PREFIX . "hero_adventure + (uid, wref, difficulty, duration, created, expire, status, moveid) + VALUES (?, ?, ?, ?, ?, ?, 0, 0)" + ); + $tileId = (int) $tile['id']; + $stmt->bind_param('iiiiii', $uid, $tileId, $difficulty, $duration, $now, $expire); + $stmt->execute(); + $stmt->close(); + $created++; + } + return $created; + } + + /** + * Batch top-up used by Automation: refresh offers for users with living, + * at-home heroes that are below max_offers. Work bounded by BATCH_LIMIT. + */ + public function generateOffersBatch() + { + global $heroAdventureConfig; + $now = time(); + $threshold = $now - (int) $heroAdventureConfig['refresh_interval']; + $max = (int) $heroAdventureConfig['max_offers']; + $limit = self::BATCH_LIMIT; + + // Users with a living hero whose available-offer count is below max + // and whose newest offer (if any) is older than the refresh interval. + $stmt = $this->db->prepare( + "SELECT h.uid + FROM " . TB_PREFIX . "hero h + LEFT JOIN " . TB_PREFIX . "hero_adventure a + ON a.uid = h.uid AND a.status = 0 AND a.expire > ? + WHERE h.dead = 0 + GROUP BY h.uid + HAVING COUNT(a.id) < ? AND COALESCE(MAX(a.created), 0) < ? + LIMIT ?" + ); + $stmt->bind_param('iiii', $now, $max, $threshold, $limit); + $stmt->execute(); + $res = $stmt->get_result(); + $uids = array(); + while ($row = $res->fetch_assoc()) { + $uids[] = (int) $row['uid']; + } + $stmt->close(); + + foreach ($uids as $uid) { + $this->generateOffers($uid); + } + return count($uids); + } + + /* ========================================================================= + * START + * ===================================================================== */ + + /** + * Send the hero on an available adventure offer. + * Returns one of the START_* class constants. + */ + public function startAdventure($uid, $adventureId) + { + $uid = (int) $uid; $adventureId = (int) $adventureId; $now = time(); + + $hero = $this->getLivingHero($uid); + if (!$hero) { + return self::START_NO_HERO; + } + // Only one adventure at a time. + if ($this->getRunning($uid)) { + return self::START_INVALID; + } + + // Load the offer (must be this user's, available, not expired). + $stmt = $this->db->prepare( + "SELECT id, wref, difficulty, duration FROM " . TB_PREFIX . "hero_adventure + WHERE id = ? AND uid = ? AND status = 0 AND expire > ? LIMIT 1" + ); + $stmt->bind_param('iii', $adventureId, $uid, $now); + $stmt->execute(); + $res = $stmt->get_result(); + $offer = $res->fetch_assoc(); + $stmt->close(); + if (!$offer) { + return self::START_INVALID; + } + + // Hero must be AT HOME: race-safe conditional decrement of units.hero. + $homeWref = (int) $hero['wref']; + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "units SET hero = hero - 1 WHERE vref = ? AND hero >= 1 LIMIT 1" + ); + $stmt->bind_param('i', $homeWref); + $stmt->execute(); + $atHome = $stmt->affected_rows > 0; + $stmt->close(); + if (!$atHome) { + return self::START_HERO_AWAY; + } + + // Claim the offer atomically; if someone double-clicked and a parallel + // request claimed it first, put the hero back and bail out. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero_adventure SET status = 1 WHERE id = ? AND status = 0 LIMIT 1" + ); + $stmt->bind_param('i', $adventureId); + $stmt->execute(); + $claimed = $stmt->affected_rows > 0; + $stmt->close(); + if (!$claimed) { + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "units SET hero = hero + 1 WHERE vref = ? LIMIT 1" + ); + $stmt->bind_param('i', $homeWref); + $stmt->execute(); + $stmt->close(); + return self::START_INVALID; + } + + // Movement out: from = home village, to = adventure tile, ref = adventure id. + // Recompute duration now (equipment may have changed since the offer was made). + global $database; + $coorHome = $database->getCoor($homeWref); + $coorSite = $database->getCoor((int) $offer['wref']); + $dist = $database->getDistance($coorHome['x'], $coorHome['y'], $coorSite['x'], $coorSite['y']); + $duration = $this->travelSeconds($uid, $dist); + $endtime = $now + $duration; + + $stmt = $this->db->prepare( + "INSERT INTO " . TB_PREFIX . "movement + (sort_type, `from`, `to`, ref, ref2, starttime, endtime, proc, send, wood, clay, iron, crop, marker) + VALUES (?, ?, ?, ?, 0, ?, ?, 0, 1, 0, 0, 0, 0, 0)" + ); + $sortType = MOVEMENT_ADVENTURE_OUT; $siteWref = (int) $offer['wref']; + $stmt->bind_param('iiiiii', $sortType, $homeWref, $siteWref, $adventureId, $now, $endtime); + $stmt->execute(); + $moveid = (int) $stmt->insert_id; + $stmt->close(); + + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero_adventure SET moveid = ? WHERE id = ? LIMIT 1" + ); + $stmt->bind_param('ii', $moveid, $adventureId); + $stmt->execute(); + $stmt->close(); + + return self::START_OK; + } + + /* ========================================================================= + * AUTOMATION PROCESSORS + * ===================================================================== */ + + /** + * Process heroes ARRIVING at adventure sites (sort_type 20). + * Rolls rewards, applies HP loss/XP/silver/items, files the ntype 26 + * report, and creates the return movement carrying the resource loot. + */ + public function processArrivals() + { + global $database, $heroAdventureConfig; + $now = time(); + + $q = "SELECT m.moveid, m.`from`, m.`to`, m.ref, m.endtime, + a.id AS advid, a.uid, a.difficulty + FROM " . TB_PREFIX . "movement m + JOIN " . TB_PREFIX . "hero_adventure a ON a.id = m.ref + WHERE m.sort_type = " . (int) MOVEMENT_ADVENTURE_OUT . " + AND m.proc = 0 AND m.endtime < $now"; + $rows = $database->query_return($q); + if (!$rows || !count($rows)) { + return 0; + } + + $processed = 0; + foreach ($rows as $data) { + if (!$this->claimMovement($data['moveid'])) { + continue; + } + + $uid = (int) $data['uid']; + $hero = $this->getLivingHero($uid); + $cfg = $heroAdventureConfig[(int) $data['difficulty']]; + + // Hero vanished/died meanwhile (edge case): close the adventure quietly. + if (!$hero) { + $this->finishAdventure($data['advid']); + continue; + } + + /* ---- Roll rewards ---- */ + $exp = mt_rand($cfg['exp'][0], $cfg['exp'][1]); + $silver = mt_rand($cfg['silver'][0], $cfg['silver'][1]); + $hpLoss = mt_rand($cfg['hp_loss'][0], $cfg['hp_loss'][1]); + $wood = mt_rand($cfg['resources'][0], $cfg['resources'][1]); + $clay = mt_rand($cfg['resources'][0], $cfg['resources'][1]); + $iron = mt_rand($cfg['resources'][0], $cfg['resources'][1]); + $crop = mt_rand($cfg['resources'][0], $cfg['resources'][1]); + + // Item drop: gear first (rarer, tier-weighted toward T1), and only + // if no gear dropped, roll the consumable pool. One item max. + $itemId = 0; $itemQty = 0; + if (mt_rand(1, 100) <= (int) ($cfg['equip_chance'] ?? 0)) { + $itemId = $this->rollEquipment(); + $itemQty = ($itemId > 0) ? 1 : 0; + } + if ($itemId === 0 && mt_rand(1, 100) <= (int) $cfg['item_chance']) { + $pool = $cfg['consumable']; + $itemId = (int) $pool[array_rand($pool)]; + $itemQty = 1; + } + + $health = (float) $hero['health']; + $newHealth = $health - $hpLoss; + + if ($newHealth <= 0) { + /* ---- Hero dies at the site: loot forfeited, no return trip. ---- */ + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero SET dead = 1, health = 0, lastupdate = ? WHERE heroid = ? LIMIT 1" + ); + $heroid = (int) $hero['heroid']; + $stmt->bind_param('ii', $now, $heroid); + $stmt->execute(); + $stmt->close(); + + $payload = 'difficulty=' . (int) $data['difficulty'] . '&died=1&hp=' . $hpLoss; + $database->addNotice($uid, (int) $data['from'], 0, NTYPE_ADVENTURE_REPORT, + 'Hero fell on an adventure', $payload, $now); + + $this->finishAdventure($data['advid']); + $processed++; + continue; + } + + /* ---- Hero survives: apply rewards ---- */ + $heroItems = new HeroItems(); + + // XP with helmet bonus; level-up handled by Automation::updateHero(). + $bonuses = $heroItems->getBonuses($uid); + $xpGain = (int) floor($exp * (100 + (int) $bonuses[HB_XP]) / 100); + $database->modifyHeroXp('experience', $xpGain, (int) $hero['heroid']); + + // Health loss. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero SET health = ? WHERE heroid = ? LIMIT 1" + ); + $heroid = (int) $hero['heroid']; + $stmt->bind_param('di', $newHealth, $heroid); + $stmt->execute(); + $stmt->close(); + + // Silver + item drop. + if ($silver > 0) { + $heroItems->addSilver($uid, $silver); + } + if ($itemId > 0) { + $heroItems->addItem($uid, $itemId, $itemQty); + } + + /* ---- Return movement carries the resource loot home ---- */ + // Same travel time back as out: reuse the adventure's stored one-way duration. + $legDuration = $this->travelLegDuration($data); + $stmt = $this->db->prepare( + "INSERT INTO " . TB_PREFIX . "movement + (sort_type, `from`, `to`, ref, ref2, starttime, endtime, proc, send, wood, clay, iron, crop, marker) + VALUES (?, ?, ?, ?, 0, ?, ?, 0, 1, ?, ?, ?, ?, 0)" + ); + $sortType = MOVEMENT_ADVENTURE_BACK; + $fromSite = (int) $data['to']; $toHome = (int) $data['from']; + $advid = (int) $data['advid']; + $end = $now + $legDuration; + $stmt->bind_param('iiiiiiiiii', $sortType, $fromSite, $toHome, $advid, $now, $end, $wood, $clay, $iron, $crop); + $stmt->execute(); + $stmt->close(); + + /* ---- Report (ntype 26) ---- */ + $payload = 'difficulty=' . (int) $data['difficulty'] + . '&exp=' . $xpGain . '&silver=' . $silver . '&hp=' . $hpLoss + . '&wood=' . $wood . '&clay=' . $clay . '&iron=' . $iron . '&crop=' . $crop; + if ($itemId > 0) { + $payload .= '&itemid=' . $itemId . '&itemqty=' . $itemQty; + } + $database->addNotice($uid, $toHome, 0, NTYPE_ADVENTURE_REPORT, + 'Hero returned from an adventure', $payload, $now); + + $this->finishAdventure($data['advid']); + $processed++; + } + return $processed; + } + + /** + * Process heroes RETURNING home (sort_type 21): hero re-enters units.hero, + * resource loot is credited to the village. + */ + public function processReturns() + { + global $database; + $now = time(); + + $q = "SELECT moveid, `from`, `to`, wood, clay, iron, crop + FROM " . TB_PREFIX . "movement + WHERE sort_type = " . (int) MOVEMENT_ADVENTURE_BACK . " + AND proc = 0 AND endtime < $now"; + $rows = $database->query_return($q); + if (!$rows || !count($rows)) { + return 0; + } + + $processed = 0; + foreach ($rows as $data) { + if (!$this->claimMovement($data['moveid'])) { + continue; + } + + $home = (int) $data['to']; + + // Hero is home again. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "units SET hero = hero + 1 WHERE vref = ? LIMIT 1" + ); + $stmt->bind_param('i', $home); + $stmt->execute(); + $stmt->close(); + + // Credit the loot (same pattern as returnunitsComplete). + if ($data['wood'] + $data['clay'] + $data['iron'] + $data['crop'] > 0) { + $database->modifyResource($home, $data['wood'], $data['clay'], $data['iron'], $data['crop'], 1); + $database->addStarvationData($home); + } + $processed++; + } + return $processed; + } + + /** + * Roll one random piece of EQUIPMENT (any non-bag catalog item, including + * weapons for other tribes - unusable finds feed the auction house, same + * as T4). Tier picked by 'equip_tier_weights' (T1 common, T3 very rare), + * then a uniform pick inside that tier. Returns an itemid, or 0 if the + * rolled tier happens to be empty (defensive; never true with the + * shipped catalog). + */ + public function rollEquipment() + { + global $heroItemCatalog, $heroAdventureConfig; + + $weights = $heroAdventureConfig['equip_tier_weights']; + $roll = mt_rand(1, 100); $acc = 0; $tier = 1; + foreach ($weights as $t => $w) { + $acc += (int) $w; + if ($roll <= $acc) { $tier = (int) $t; break; } + } + + $pool = array(); + foreach ($heroItemCatalog as $iid => $def) { + if ((int) $def['slot'] !== HSLOT_BAG && (int) $def['tier'] === $tier) { + $pool[] = $iid; + } + } + return count($pool) ? (int) $pool[array_rand($pool)] : 0; + } + + /* ========================================================================= + * INTERNALS + * ===================================================================== */ + + /** + * Travel time in seconds for a given distance, honoring equipment: + * horse replaces the on-foot base speed, spurs add on top (only with a + * horse - already enforced inside HeroItems::getBonuses()). + */ + public function travelSeconds($uid, $distance) + { + $heroItems = new HeroItems(); + $bonuses = $heroItems->getBonuses((int) $uid); + + $speed = ((int) $bonuses[HB_HORSE_SPEED] > 0) + ? (int) $bonuses[HB_HORSE_SPEED] + (int) $bonuses[HB_MOUNT_SPEED] + : self::BASE_HERO_SPEED; + + $increase = defined('INCREASE_SPEED') ? max(1, (int) INCREASE_SPEED) : 1; + return max(60, (int) round($distance / $speed * 3600 / $increase)); + } + + /** Duration of the just-finished leg (endtime - starttime is not selected; use stored offer duration). */ + private function travelLegDuration($data) + { + // The return leg mirrors the outbound one. We recompute from the + // adventure's stored one-way duration to avoid drift when Automation + // runs late (endtime long in the past). + $stmt = $this->db->prepare( + "SELECT duration FROM " . TB_PREFIX . "hero_adventure WHERE id = ? LIMIT 1" + ); + $advid = (int) $data['advid']; + $stmt->bind_param('i', $advid); + $stmt->execute(); + $stmt->bind_result($duration); + $found = $stmt->fetch(); + $stmt->close(); + return $found ? max(60, (int) $duration) : 3600; + } + + /** Same claim pattern as Automation::claimMovementRecord (race-safe). */ + private function claimMovement($moveid) + { + $moveid = (int) $moveid; + if ($moveid <= 0) { + return false; + } + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "movement SET proc = 1 WHERE moveid = ? AND proc = 0 LIMIT 1" + ); + $stmt->bind_param('i', $moveid); + $stmt->execute(); + $ok = $stmt->affected_rows === 1; + $stmt->close(); + return $ok; + } + + private function finishAdventure($advid) + { + $advid = (int) $advid; + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero_adventure SET status = 2 WHERE id = ? LIMIT 1" + ); + $stmt->bind_param('i', $advid); + $stmt->execute(); + $stmt->close(); + } + + private function getLivingHero($uid) + { + $uid = (int) $uid; + $stmt = $this->db->prepare( + "SELECT heroid, uid, unit, wref, level, experience, health, dead + FROM " . TB_PREFIX . "hero WHERE uid = ? AND dead = 0 LIMIT 1" + ); + $stmt->bind_param('i', $uid); + $stmt->execute(); + $res = $stmt->get_result(); + $row = $res->fetch_assoc(); + $stmt->close(); + return $row ?: null; + } +} diff --git a/GameEngine/HeroAuction.php b/GameEngine/HeroAuction.php new file mode 100644 index 00000000..61816fa1 --- /dev/null +++ b/GameEngine/HeroAuction.php @@ -0,0 +1,533 @@ + at any moment: sum of all player silver + escrowed bid_max of top ## +## bidders is constant (minus the seller fee at finalization). ## +## ## +## SELLER FEE: player sellers receive silver_current minus AUCTION_FEE_PCT ## +## (10%, T4 convention). NPC-seller (seller = 0) proceeds vanish (silver ## +## sink). Unsold player items are returned to the seller's inventory. ## +## ## +## No cancellation once listed (T4 convention) - flagged, not implemented. ## +## ## +################################################################################# + +class HeroAuction +{ + /** Seller fee percent taken from the hammer price (player sellers only). */ + const AUCTION_FEE_PCT = 10; + + /** Minimum bid increment over the visible price. */ + const MIN_INCREMENT = 1; + + /** Allowed listing durations, seconds (4h / 8h / 24h like T4). */ + public static $allowedDurations = array(14400, 28800, 86400); + + /** How many NPC auctions to keep open at once. */ + const NPC_OPEN_TARGET = 10; + + /** NPC listing duration: 8 hours. */ + const NPC_DURATION = 28800; + + /** status values (mirror the auction table docs from Phase 1) */ + const ST_OPEN = 0; + const ST_SOLD = 1; // reserved intermediate state; finalization goes straight to 2 + const ST_PROCESSED = 2; + const ST_EXPIRED = 3; + + /** result codes for placeBid() / createAuction() */ + const BID_OK = 1; + const BID_OUTBID = 2; // accepted, but instantly below the holder's secret max + const BID_INVALID = 0; // bad auction/own auction/too low + const BID_NO_SILVER = -1; + + /** @var mysqli */ + private $db; + + public function __construct() + { + global $database; + $this->db = $database->dblink; + + global $heroItemCatalog; + if (!isset($heroItemCatalog)) { + include_once __DIR__ . '/Data/hero_items.php'; + } + } + + /* ========================================================================= + * READS + * ===================================================================== */ + + /** All open, unexpired auctions (newest ending first optional; default: ending soonest). */ + public function getOpenAuctions($limit = 50) + { + $now = time(); $limit = max(1, (int) $limit); + $stmt = $this->db->prepare( + "SELECT id, seller, itemid, slot, stat_value, quantity, + silver_start, silver_current, bidder, created, time_end, status + FROM " . TB_PREFIX . "auction + WHERE status = " . self::ST_OPEN . " AND time_end > ? + ORDER BY time_end ASC LIMIT ?" + ); + $stmt->bind_param('ii', $now, $limit); + $stmt->execute(); + $res = $stmt->get_result(); + $out = array(); + while ($row = $res->fetch_assoc()) { + $row['name'] = heroItemName($row['itemid']); + // bid_max intentionally NOT selected - it's the bidder's secret. + $out[] = $row; + } + $stmt->close(); + return $out; + } + + /** Auctions where $uid is the current top bidder. */ + public function getMyBids($uid) + { + $uid = (int) $uid; $now = time(); + $stmt = $this->db->prepare( + "SELECT id, seller, itemid, quantity, silver_current, bid_max, time_end + FROM " . TB_PREFIX . "auction + WHERE bidder = ? AND status = " . self::ST_OPEN . " AND time_end > ? + ORDER BY time_end ASC" + ); + $stmt->bind_param('ii', $uid, $now); + $stmt->execute(); + $res = $stmt->get_result(); + $out = array(); + while ($row = $res->fetch_assoc()) { + $row['name'] = heroItemName($row['itemid']); + $out[] = $row; + } + $stmt->close(); + return $out; + } + + /** Open auctions listed by $uid. */ + public function getMySales($uid) + { + $uid = (int) $uid; + $stmt = $this->db->prepare( + "SELECT id, itemid, quantity, silver_start, silver_current, bidder, time_end, status + FROM " . TB_PREFIX . "auction + WHERE seller = ? AND status = " . self::ST_OPEN . " + ORDER BY time_end ASC" + ); + $stmt->bind_param('i', $uid); + $stmt->execute(); + $res = $stmt->get_result(); + $out = array(); + while ($row = $res->fetch_assoc()) { + $row['name'] = heroItemName($row['itemid']); + $out[] = $row; + } + $stmt->close(); + return $out; + } + + /* ========================================================================= + * LISTING + * ===================================================================== */ + + /** + * List an owned, UNEQUIPPED item (or part of a consumable stack). + * The item leaves the inventory immediately (escrowed in the auction row). + * Returns the new auction id, or 0 on failure. + */ + public function createAuction($uid, $inventoryRowId, $quantity, $startPrice, $duration) + { + $uid = (int) $uid; $inventoryRowId = (int) $inventoryRowId; + $quantity = max(1, (int) $quantity); + $startPrice = max(1, (int) $startPrice); + $duration = (int) $duration; + + if (!in_array($duration, self::$allowedDurations, true)) { + return 0; + } + + $heroItems = new HeroItems(); + $row = null; + foreach ($heroItems->getInventory($uid) as $r) { + if ((int) $r['id'] === $inventoryRowId) { $row = $r; break; } + } + // Must exist, be catalog-known, unequipped, and cover the quantity. + if (!$row || $row['orphan'] || (int) $row['equipped'] === 1 || (int) $row['quantity'] < $quantity) { + return 0; + } + // Gear always lists as a single piece. + if ((int) $row['def']['slot'] !== HSLOT_BAG) { + $quantity = 1; + } + + // Take the item out of the inventory first (race-safe decrement inside). + if (!$heroItems->removeItem($uid, $inventoryRowId, $quantity)) { + return 0; + } + + $now = time(); $end = $now + $duration; + $itemid = (int) $row['itemid']; $slot = (int) $row['def']['slot']; + $statValue = (int) $row['stat_value']; + + $stmt = $this->db->prepare( + "INSERT INTO " . TB_PREFIX . "auction + (seller, itemid, slot, stat_value, quantity, + silver_start, silver_current, bidder, bid_max, created, time_end, status) + VALUES (?, ?, ?, ?, ?, ?, ?, 0, 0, ?, ?, " . self::ST_OPEN . ")" + ); + $stmt->bind_param('iiiiiiiii', $uid, $itemid, $slot, $statValue, $quantity, + $startPrice, $startPrice, $now, $end); + $stmt->execute(); + $auctionId = (int) $stmt->insert_id; + $stmt->close(); + + return $auctionId; + } + + /* ========================================================================= + * BIDDING (proxy model) + * ===================================================================== */ + + /** + * Place a maximum bid. Returns a BID_* code. + * + * Escrow choreography (all conditional statements, race-safe): + * 1. take $maxBid silver from the bidder (refused if balance is short); + * 2. try to become top bidder atomically + * (WHERE bid_max < $maxBid guards the proxy comparison); + * 3a. success -> refund the PREVIOUS top bidder's full escrow; + * 3b. failure -> we lost against the holder's secret max: bump the + * visible price to min(holderMax, ourMax + 1) and refund ourselves. + */ + public function placeBid($uid, $auctionId, $maxBid) + { + $uid = (int) $uid; $auctionId = (int) $auctionId; $maxBid = (int) $maxBid; + $now = time(); + + // Snapshot for validation (authoritative checks are in the UPDATEs). + // bid_max is only used for the self-raise delta below - it is the + // caller's OWN secret in that case, never exposed to rivals. + $stmt = $this->db->prepare( + "SELECT seller, silver_current, bidder, bid_max FROM " . TB_PREFIX . "auction + WHERE id = ? AND status = " . self::ST_OPEN . " AND time_end > ? LIMIT 1" + ); + $stmt->bind_param('ii', $auctionId, $now); + $stmt->execute(); + $res = $stmt->get_result(); + $auction = $res->fetch_assoc(); + $stmt->close(); + + if (!$auction || (int) $auction['seller'] === $uid) { + return self::BID_INVALID; + } + // A fresh auction (no bidder) can be won AT the start price; + // an active one needs current + increment. + $minBid = (int) $auction['silver_current'] + + ((int) $auction['bidder'] > 0 ? self::MIN_INCREMENT : 0); + if ($maxBid < $minBid) { + return self::BID_INVALID; + } + // Self-raise: the old escrow (old bid_max) is already held, so only + // the difference is taken; a raise below the current max is pointless. + $selfRaise = ((int) $auction['bidder'] === $uid); + if ($selfRaise && $maxBid <= (int) $auction['bid_max']) { + return self::BID_INVALID; + } + $escrow = $selfRaise ? $maxBid - (int) $auction['bid_max'] : $maxBid; + + $heroItems = new HeroItems(); + + // 1. escrow (conditional - fails on insufficient silver). + if (!$heroItems->spendSilver($uid, $escrow)) { + return self::BID_NO_SILVER; + } + + // 2. capture the current holder as the refund target, then claim. + // Correctness anchor is the conditional UPDATE below - this read + // only records who to refund if we displace them. + $stmt = $this->db->prepare( + "SELECT bidder, bid_max FROM " . TB_PREFIX . "auction WHERE id = ? LIMIT 1" + ); + $stmt->bind_param('i', $auctionId); + $stmt->execute(); + $res = $stmt->get_result(); + $prev = $res->fetch_assoc(); + $stmt->close(); + + if ($selfRaise) { + // Deepening our own secret max never moves the visible price and + // never refunds anyone - the old escrow stays held, only the + // delta was added above. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "auction + SET bid_max = ? + WHERE id = ? AND status = " . self::ST_OPEN . " AND time_end > ? + AND bidder = ? AND bid_max < ?" + ); + $stmt->bind_param('iiiii', $maxBid, $auctionId, $now, $uid, $maxBid); + $stmt->execute(); + $won = $stmt->affected_rows > 0; + $stmt->close(); + + if ($won) { + return self::BID_OK; + } + // Race lost: a rival displaced us between snapshot and update, so + // their winning path already refunded our OLD escrow - the delta + // is all we still hold. Return it. + $heroItems->addSilver($uid, $escrow); + return self::BID_OUTBID; + } + + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "auction + SET silver_current = LEAST(?, GREATEST(silver_current, bid_max + " . self::MIN_INCREMENT . ")), + bidder = ?, bid_max = ? + WHERE id = ? AND status = " . self::ST_OPEN . " AND time_end > ? + AND bid_max < ? AND seller <> ? AND bidder <> ?" + ); + $stmt->bind_param('iiiiiiii', $maxBid, $uid, $maxBid, $auctionId, $now, $maxBid, $uid, $uid); + $stmt->execute(); + $won = $stmt->affected_rows > 0; + $stmt->close(); + + if ($won) { + // 3a. refund the displaced bidder's full escrow. + if ($prev && (int) $prev['bidder'] > 0 && (int) $prev['bidder'] !== $uid && (int) $prev['bid_max'] > 0) { + $heroItems->addSilver((int) $prev['bidder'], (int) $prev['bid_max']); + } + return self::BID_OK; + } + + // 3b. lost against the holder's secret max (or the auction closed in + // the meantime): bump the visible price if still open, refund us. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "auction + SET silver_current = LEAST(bid_max, ? + " . self::MIN_INCREMENT . ") + WHERE id = ? AND status = " . self::ST_OPEN . " AND time_end > ? AND bid_max >= ?" + ); + $stmt->bind_param('iiii', $maxBid, $auctionId, $now, $maxBid); + $stmt->execute(); + $stmt->close(); + + $heroItems->addSilver($uid, $escrow); + return self::BID_OUTBID; + } + + /* ========================================================================= + * AUTOMATION PROCESSORS + * ===================================================================== */ + + /** + * Finalize ended auctions: + * - with a bidder: award the item, refund (bid_max - silver_current) to + * the winner, pay the player seller (minus fee), file reports; + * - without: return the item to a player seller, mark expired. + * NPC (seller 0) items simply vanish when unsold; NPC proceeds vanish too. + */ + public function processFinished() + { + global $database; + $now = time(); + + $q = "SELECT id, seller, itemid, stat_value, quantity, + silver_current, bidder, bid_max + FROM " . TB_PREFIX . "auction + WHERE status = " . self::ST_OPEN . " AND time_end <= $now"; + $rows = $database->query_return($q); + if (!$rows || !count($rows)) { + return 0; + } + + $heroItems = new HeroItems(); + $processed = 0; + + foreach ($rows as $a) { + // Claim the row (same race-safe pattern as movement claiming). + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "auction SET status = " . self::ST_PROCESSED . " + WHERE id = ? AND status = " . self::ST_OPEN . " LIMIT 1" + ); + $aid = (int) $a['id']; + $stmt->bind_param('i', $aid); + $stmt->execute(); + $claimed = $stmt->affected_rows > 0; + $stmt->close(); + if (!$claimed) { + continue; + } + + $bidder = (int) $a['bidder']; + $seller = (int) $a['seller']; + $price = (int) $a['silver_current']; + + if ($bidder > 0) { + /* ---- SOLD ---- */ + $heroItems->addItem($bidder, (int) $a['itemid'], (int) $a['quantity'], (int) $a['stat_value']); + + // Winner escrowed bid_max; they only pay the hammer price. + $refund = (int) $a['bid_max'] - $price; + if ($refund > 0) { + $heroItems->addSilver($bidder, $refund); + } + + // Player seller gets the price minus the fee; NPC proceeds sink. + if ($seller > 0) { + $fee = (int) floor($price * self::AUCTION_FEE_PCT / 100); + $heroItems->addSilver($seller, $price - $fee); + $database->addNotice($seller, 0, 0, NTYPE_AUCTION_REPORT, + 'Auction sold', + 'role=seller&itemid=' . (int) $a['itemid'] . '&qty=' . (int) $a['quantity'] + . '&price=' . $price . '&fee=' . $fee, $now); + } + + $database->addNotice($bidder, 0, 0, NTYPE_AUCTION_REPORT, + 'Auction won', + 'role=winner&itemid=' . (int) $a['itemid'] . '&qty=' . (int) $a['quantity'] + . '&price=' . $price . '&refund=' . max(0, $refund), $now); + } else { + /* ---- UNSOLD ---- */ + if ($seller > 0) { + $heroItems->addItem($seller, (int) $a['itemid'], (int) $a['quantity'], (int) $a['stat_value']); + $database->addNotice($seller, 0, 0, NTYPE_AUCTION_REPORT, + 'Auction expired', + 'role=seller&expired=1&itemid=' . (int) $a['itemid'] . '&qty=' . (int) $a['quantity'], $now); + } + // Correct the claimed status for the unsold case. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "auction SET status = " . self::ST_EXPIRED . " WHERE id = ? LIMIT 1" + ); + $stmt->bind_param('i', $aid); + $stmt->execute(); + $stmt->close(); + } + $processed++; + } + return $processed; + } + + /** + * ADMIN-ONLY: cancel an open auction outright (Phase 7 admin panel). + * Player-facing cancellation intentionally does not exist (T4 convention); + * this is the moderation escape hatch. Refunds the top bidder's full + * escrow and returns the item to a player seller (NPC items vanish). + * Returns true when an open auction was cancelled. + */ + public function adminCancel($auctionId) + { + $auctionId = (int) $auctionId; + + // Snapshot before claiming so we know who to refund. + $stmt = $this->db->prepare( + "SELECT seller, itemid, stat_value, quantity, bidder, bid_max + FROM " . TB_PREFIX . "auction + WHERE id = ? AND status = " . self::ST_OPEN . " LIMIT 1" + ); + $stmt->bind_param('i', $auctionId); + $stmt->execute(); + $res = $stmt->get_result(); + $a = $res->fetch_assoc(); + $stmt->close(); + if (!$a) { + return false; + } + + // Race-safe claim straight to EXPIRED so processFinished can't touch it. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "auction SET status = " . self::ST_EXPIRED . " + WHERE id = ? AND status = " . self::ST_OPEN . " LIMIT 1" + ); + $stmt->bind_param('i', $auctionId); + $stmt->execute(); + $claimed = $stmt->affected_rows > 0; + $stmt->close(); + if (!$claimed) { + return false; + } + + $heroItems = new HeroItems(); + if ((int) $a['bidder'] > 0 && (int) $a['bid_max'] > 0) { + $heroItems->addSilver((int) $a['bidder'], (int) $a['bid_max']); + } + if ((int) $a['seller'] > 0) { + $heroItems->addItem((int) $a['seller'], (int) $a['itemid'], (int) $a['quantity'], (int) $a['stat_value']); + } + return true; + } + + /** + * Keep ~NPC_OPEN_TARGET NPC-seller auctions open so the market always has + * stock. Random catalog items; consumables list in small stacks. + * Start price scales with tier so tier-3 gear stays expensive. + */ + public function seedNpcAuctions() + { + global $heroItemCatalog; + $now = time(); + + $stmt = $this->db->prepare( + "SELECT COUNT(*) FROM " . TB_PREFIX . "auction + WHERE seller = 0 AND status = " . self::ST_OPEN . " AND time_end > ?" + ); + $stmt->bind_param('i', $now); + $stmt->execute(); + $stmt->bind_result($open); + $stmt->fetch(); + $stmt->close(); + + $need = self::NPC_OPEN_TARGET - (int) $open; + if ($need <= 0) { + return 0; + } + + $ids = array_keys($heroItemCatalog); + $created = 0; + for ($i = 0; $i < $need; $i++) { + $itemid = (int) $ids[array_rand($ids)]; + $def = $heroItemCatalog[$itemid]; + $isBag = ((int) $def['slot'] === HSLOT_BAG); + $qty = $isBag ? mt_rand(3, 15) : 1; + // Start price: tier-scaled for gear, per-piece for consumables. + $start = $isBag ? $qty * mt_rand(2, 5) : (int) $def['tier'] * mt_rand(40, 80); + $end = $now + self::NPC_DURATION; + $slot = (int) $def['slot']; + + $stmt = $this->db->prepare( + "INSERT INTO " . TB_PREFIX . "auction + (seller, itemid, slot, stat_value, quantity, + silver_start, silver_current, bidder, bid_max, created, time_end, status) + VALUES (0, ?, ?, 0, ?, ?, ?, 0, 0, ?, ?, " . self::ST_OPEN . ")" + ); + $stmt->bind_param('iiiiiii', $itemid, $slot, $qty, $start, $start, $now, $end); + $stmt->execute(); + $stmt->close(); + $created++; + } + return $created; + } +} diff --git a/GameEngine/HeroBattleBonus.php b/GameEngine/HeroBattleBonus.php new file mode 100644 index 00000000..e1f18aa6 --- /dev/null +++ b/GameEngine/HeroBattleBonus.php @@ -0,0 +1,529 @@ + statBonus(), attaches uid/unit_bonus/ ## +## vs_natars/dmg_reduce to the hero array ## +## Battle HERO OFFENSE block -> unitOffense(), natarMultiplier() ## +## Battle defender hero block -> unitDefense() ## +## Battle HERO DAMAGE block -> reduceDamage() ## +## Automation bounty callsite -> raidMultiplier() ## +## Automation return callsites -> adjustReturnEndtime() ## +## Units::getWalkingTroopsTime -> adjustTravelTime() ## +## ## +################################################################################# + +class HeroBattleBonus +{ + /** @var array per-request bonus cache: uid => bonuses array */ + private static $cache = array(); + + /** @var array per-request "does uid have a living hero" cache */ + private static $livingCache = array(); + + /** Feature flag gate used by every public entry point. */ + public static function enabled() + { + return defined('NEW_FUNCTIONS_HERO_T4') && NEW_FUNCTIONS_HERO_T4; + } + + /** Cached HeroItems::getBonuses(). Returns null when the feature is off. */ + public static function bonuses($uid) + { + if (!self::enabled()) { + return null; + } + $uid = (int) $uid; + if (!array_key_exists($uid, self::$cache)) { + $heroItems = new HeroItems(); + self::$cache[$uid] = $heroItems->getBonuses($uid); + } + return self::$cache[$uid]; + } + + /* ========================================================================= + * BATTLE MATH + * ===================================================================== */ + + /** + * Flat fighting strength from items (weapons, cuirasses, shields). + * Added to the hero's atk AND di/dc - T4 fight strength is a single stat + * that counts on whichever side the hero fights. + */ + public static function statBonus($uid) + { + $b = self::bonuses($uid); + return $b ? (int) $b[HB_FIGHT] : 0; + } + + /** [unitId => perUnit] map from the equipped weapon (empty when none). */ + public static function unitBonusMap($uid) + { + $b = self::bonuses($uid); + return ($b && !empty($b[HB_UNIT_BONUS])) ? $b[HB_UNIT_BONUS] : array(); + } + + /** + * Extra offense from the weapon's per-unit bonus: + * +N attack per accompanying unit of the weapon's type. + * $isCavalry tells the caller whether to add it to ap or cap. + * Returns [addedInfantryAp, addedCavalryAp]. + */ + public static function unitOffense($uid, array $attackerUnits, array $cavalryLookup) + { + $map = self::unitBonusMap($uid); + if (empty($map)) { + return array(0, 0); + } + $ap = 0; $cap = 0; + foreach ($map as $unitId => $perUnit) { + $count = isset($attackerUnits['u' . $unitId]) ? (int) $attackerUnits['u' . $unitId] : 0; + if ($count <= 0) { + continue; + } + if (isset($cavalryLookup[$unitId])) { + $cap += $perUnit * $count; + } else { + $ap += $perUnit * $count; + } + } + return array($ap, $cap); + } + + /** + * Extra defense from the weapon's per-unit bonus: + * +N defense per accompanying unit of the weapon's type. The design sheet + * grants a single "+N def" figure, so it is added to BOTH the infantry + * (dp) and cavalry (cdp) defense pools. + * Returns the flat amount to add to each pool. + */ + public static function unitDefense($uid, array $defenderUnits) + { + $map = self::unitBonusMap($uid); + if (empty($map)) { + return 0; + } + $add = 0; + foreach ($map as $unitId => $perUnit) { + $count = isset($defenderUnits['u' . $unitId]) ? (int) $defenderUnits['u' . $unitId] : 0; + if ($count > 0) { + $add += $perUnit * $count; + } + } + return $add; + } + + /** + * Hunting horn: multiplier applied to the HERO's own attack contribution + * when the defender is the Natars (uid 3, see Artifacts::NATARS_UID). + */ + public static function natarMultiplier($uid, $defenderUid) + { + if ((int) $defenderUid !== 3) { + return 1.0; + } + $b = self::bonuses($uid); + if (!$b || (int) $b[HB_VS_NATARS] <= 0) { + return 1.0; + } + return 1 + ((int) $b[HB_VS_NATARS] / 100); + } + + /** + * Armor damage reduction: flat points subtracted from the health damage + * the hero takes in one battle (never below 0). + */ + public static function reduceDamage($uid, $damage) + { + $b = self::bonuses($uid); + if (!$b || (int) $b[HB_DMG_REDUCE] <= 0) { + return (int) $damage; + } + return max(0, (int) $damage - (int) $b[HB_DMG_REDUCE]); + } + + /* ========================================================================= + * RAID BOUNTY (thief sacks) + * ===================================================================== */ + + /** + * Sacks increase the resources stolen. Applies only when the attacker's + * living hero actually took part in the strike ($heroSent > 0). The hero + * that died in this very battle was already flagged dead by Battle.php + * before the bounty step runs, so livingHero() naturally excludes them. + */ + public static function raidMultiplier($uid, $heroSent) + { + if ((int) $heroSent <= 0 || !self::livingHero($uid)) { + return 1.0; + } + $b = self::bonuses($uid); + if (!$b || (int) $b[HB_RAID] <= 0) { + return 1.0; + } + return 1 + ((int) $b[HB_RAID] / 100); + } + + /* ========================================================================= + * TRAVEL TIME (boots / pennant / standard / map) + * ===================================================================== */ + + /** + * Adjust an OUTBOUND troop-travel time (seconds) computed by + * Generator::procDistanceTime(). Only applies when the hero rides along. + * + * - Pennant (HB_SPEED_OWN): +% speed when the target village belongs + * to the same player; + * - Standard (HB_SPEED_ALLY): +% speed when the target belongs to an + * alliance mate (pennant wins if both match); + * - Boots (HB_TROOP_SPEED_20): +% speed for the part of the journey + * beyond 20 tiles, same piecewise idea as the Tournament Square + * (issue #304). NOTE: applied proportionally on the final time, which + * is exact on its own; when a Tournament Square is also active the two + * boosts compose multiplicatively on the >20 segment - a documented, + * deliberate approximation. + */ + public static function adjustTravelTime($ownerUid, $heroInArmy, $fromWref, $toWref, $seconds) + { + global $database; + + if (!self::enabled() || (int) $heroInArmy <= 0 || $seconds <= 0) { + return (int) $seconds; + } + $b = self::bonuses($ownerUid); + if (!$b) { + return (int) $seconds; + } + + $seconds = (float) $seconds; + + // --- pennant / standard --- + // Pennant: BOTH endpoints must belong to the player (journeys between + // own villages). Standard: both endpoints inside the same alliance. + // Destination-only checks would wrongly buff every return leg home. + $pct = 0; + $fromOwner = (int) $database->getVillageField((int) $fromWref, 'owner'); + $targetOwner = (int) $database->getVillageField((int) $toWref, 'owner'); + if ($fromOwner > 0 && $targetOwner > 0) { + if ($fromOwner === (int) $ownerUid && $targetOwner === (int) $ownerUid) { + $pct = (int) $b[HB_SPEED_OWN]; + } elseif ((int) $b[HB_SPEED_ALLY] > 0) { + $ownAlly = (int) $database->getUserField($fromOwner, 'alliance', 0); + $targetAlly = (int) $database->getUserField($targetOwner, 'alliance', 0); + if ($ownAlly > 0 && $ownAlly === $targetAlly) { + $pct = (int) $b[HB_SPEED_ALLY]; + } + } + } + if ($pct > 0) { + $seconds = $seconds / (1 + $pct / 100); + } + + // --- boots: only the beyond-20-tiles share of the journey --- + $bootsPct = (int) $b[HB_TROOP_SPEED_20]; + if ($bootsPct > 0) { + $fromCoor = $database->getCoor((int) $fromWref); + $toCoor = $database->getCoor((int) $toWref); + if ($fromCoor && $toCoor) { + $distance = (float) $database->getDistance($fromCoor['x'], $fromCoor['y'], $toCoor['x'], $toCoor['y']); + if ($distance > 20) { + $shareBeyond = ($distance - 20) / $distance; // time share past 20 tiles + $timeBeyond = $seconds * $shareBeyond; + $seconds = ($seconds - $timeBeyond) + $timeBeyond / (1 + $bootsPct / 100); + } + } + } + + return max(1, (int) round($seconds)); + } + + /** + * Map: shorten the RETURN leg after a battle. Receives the arrival time + * and the planned return endtime; compresses the leg duration by the + * map's percentage when the (still living) hero is coming home. + */ + public static function adjustReturnEndtime($ownerUid, $heroSent, $arrivalTime, $endtime) + { + if (!self::enabled() || (int) $heroSent <= 0 || !self::livingHero($ownerUid)) { + return $endtime; + } + $b = self::bonuses($ownerUid); + if (!$b || (int) $b[HB_RETURN_SPEED] <= 0) { + return $endtime; + } + $leg = (float) $endtime - (float) $arrivalTime; + if ($leg <= 0) { + return $endtime; + } + return $arrivalTime + $leg / (1 + (int) $b[HB_RETURN_SPEED] / 100); + } + + /* ========================================================================= + * BANDAGES (post-battle troop healing) + * ===================================================================== */ + + /** + * Heal part of the attacker's fallen troops after a battle (Phase 6): + * up to pct% of each unit type's losses return home, consuming ONE + * bandage per healed unit. Better bandages (33%, itemid 207) are used + * before small ones (25%, itemid 206). Applies only when the hero came + * home alive ($returning['t11'] > 0). The hero itself is never healable. + * + * NOTE: hero XP was already granted on the full casualty count before + * this runs - healed troops still award XP (deliberate, documented). + * + * @param int $uid Attacking player. + * @param array $returning ['t1'..'t11' => surviving counts] (modified copy returned). + * @param array $dead [1..11 => losses per unit index]. + * @param int $attackRef attacks.id of this strike. returnunitsComplete + * reads the homecoming troop counts from the + * attacks row (movement.ref join), NOT from the + * in-memory array - healed units MUST be written + * back there or they would vanish on arrival. + * @return array The adjusted returning-troops array. + */ + public static function applyBandages($uid, array $returning, array $dead, $attackRef = 0) + { + global $database; + + if (!self::enabled() || (int) ($returning['t11'] ?? 0) <= 0) { + return $returning; + } + + $heroItems = new HeroItems(); + // Bandage stacks, strongest first: [rowId, remaining, pct] + $stacks = array(); + foreach ($heroItems->getInventory((int) $uid) as $row) { + if ($row['orphan'] || $row['equipped'] == 1) { + continue; + } + if ((int) $row['itemid'] === 207) { + $stacks[0] = array('id' => (int) $row['id'], 'qty' => (int) $row['quantity'], 'pct' => 33); + } elseif ((int) $row['itemid'] === 206) { + $stacks[1] = array('id' => (int) $row['id'], 'qty' => (int) $row['quantity'], 'pct' => 25); + } + } + if (empty($stacks)) { + return $returning; + } + ksort($stacks); + + $healedPerUnit = array(); + + foreach ($stacks as $stack) { + if ($stack['qty'] <= 0) { + continue; + } + $budget = $stack['qty']; + $used = 0; + + for ($i = 1; $i <= 10 && $budget > 0; $i++) { + $lost = isset($dead[$i]) ? (int) $dead[$i] : 0; + if ($lost <= 0) { + continue; + } + $healable = (int) floor($lost * $stack['pct'] / 100); + $healed = min($healable, $budget); + if ($healed <= 0) { + continue; + } + $returning['t' . $i] = (int) ($returning['t' . $i] ?? 0) + $healed; + $dead[$i] -= $healed; // a unit is only healed once across stacks + $budget -= $healed; + $used += $healed; + $healedPerUnit[$i] = ($healedPerUnit[$i] ?? 0) + $healed; + } + + if ($used > 0) { + $heroItems->removeItem((int) $uid, $stack['id'], $used); + } + } + + // Persist the revived units into the attacks row: it is the source of + // truth returnunitsComplete hands back to the village on arrival. + if (!empty($healedPerUnit) && (int) $attackRef > 0) { + $sets = array(); + foreach ($healedPerUnit as $i => $healed) { + $sets[] = 't' . (int) $i . ' = t' . (int) $i . ' + ' . (int) $healed; + } + mysqli_query( + $database->dblink, + "UPDATE " . TB_PREFIX . "attacks SET " . implode(', ', $sets) + . " WHERE id = " . (int) $attackRef . " LIMIT 1" + ); + } + + return $returning; + } + + /* ========================================================================= + * CAGES (oasis animal capture) + * ===================================================================== */ + + /** + * Cages (itemid 208): when the hero attacks an UNOCCUPIED oasis, up to + * one animal per cage is captured BEFORE the fight - captured animals do + * not defend. They are stationed as defensive reinforcements in the + * attack's home village (enforcement row vref=home, from=home; nature + * units u31-u40 count in defense through the normal enforcement path). + * + * Deliberate, documented simplification vs T4: the animals appear at + * home the moment the battle resolves instead of travelling back with + * the hero (the attacks row can only carry the attacker's own tribe + * units, and a schema change for this isn't worth it). + * + * Removal from the oasis is a CONDITIONAL decrement per unit type + * (u31 >= N guards), so a parallel process can never drive counts + * negative; on a lost race the capture simply shrinks. + * + * @param int $uid Attacking player. + * @param int $homeWref Village the attack was sent from. + * @param int $oasisWref The unoccupied oasis under attack. + * @param array $Defender Defender unit array (modified copy returned). + * @return array The Defender array minus whatever was captured. + */ + public static function applyCages($uid, $homeWref, $oasisWref, array $Defender) + { + global $database; + + if (!self::enabled()) { + return $Defender; + } + + // Animals present? + $animals = 0; + for ($i = 31; $i <= 40; $i++) { + $animals += max(0, (int) ($Defender['u' . $i] ?? 0)); + } + if ($animals <= 0) { + return $Defender; + } + + // Cage stack (unequipped, itemid 208). + $heroItems = new HeroItems(); + $cageRow = null; + foreach ($heroItems->getInventory((int) $uid) as $row) { + if (!$row['orphan'] && (int) $row['itemid'] === 208 && $row['equipped'] == 0) { + $cageRow = $row; + break; + } + } + if (!$cageRow || (int) $cageRow['quantity'] <= 0) { + return $Defender; + } + + $budget = (int) $cageRow['quantity']; + $captured = array(); + + for ($i = 31; $i <= 40 && $budget > 0; $i++) { + $have = max(0, (int) ($Defender['u' . $i] ?? 0)); + if ($have <= 0) { + continue; + } + $take = min($have, $budget); + + // Conditional removal from the oasis - authoritative count. + $stmt = $database->dblink->prepare( + "UPDATE " . TB_PREFIX . "units + SET u{$i} = u{$i} - ? + WHERE vref = ? AND u{$i} >= ? LIMIT 1" + ); + $stmt->bind_param('iii', $take, $oasisWref, $take); + $stmt->execute(); + $removed = $stmt->affected_rows > 0; + $stmt->close(); + if (!$removed) { + continue; // lost a race; leave these animals to fight + } + + $Defender['u' . $i] = $have - $take; + $captured[$i] = $take; + $budget -= $take; + } + + if (empty($captured)) { + return $Defender; + } + + $used = array_sum($captured); + $heroItems->removeItem((int) $uid, (int) $cageRow['id'], $used); + + // Station the animals at home: reuse the self-enforcement row if one + // exists (vref = from = home), otherwise create it. + $homeWref = (int) $homeWref; + $res = mysqli_query( + $database->dblink, + "SELECT id FROM " . TB_PREFIX . "enforcement + WHERE vref = $homeWref AND `from` = $homeWref LIMIT 1" + ); + $rowE = $res ? mysqli_fetch_assoc($res) : null; + if (!$rowE) { + mysqli_query( + $database->dblink, + "INSERT INTO " . TB_PREFIX . "enforcement (vref, `from`) VALUES ($homeWref, $homeWref)" + ); + $enfId = (int) mysqli_insert_id($database->dblink); + } else { + $enfId = (int) $rowE['id']; + } + + $sets = array(); + foreach ($captured as $i => $n) { + $sets[] = 'u' . (int) $i . ' = u' . (int) $i . ' + ' . (int) $n; + } + mysqli_query( + $database->dblink, + "UPDATE " . TB_PREFIX . "enforcement SET " . implode(', ', $sets) + . " WHERE id = $enfId LIMIT 1" + ); + + return $Defender; + } + + /* ========================================================================= + * INTERNALS + * ===================================================================== */ + + /** Cached "uid has a living hero" check. */ + private static function livingHero($uid) + { + global $database; + $uid = (int) $uid; + if (!array_key_exists($uid, self::$livingCache)) { + $res = mysqli_query( + $database->dblink, + "SELECT heroid FROM " . TB_PREFIX . "hero WHERE uid = $uid AND dead = 0 LIMIT 1" + ); + self::$livingCache[$uid] = ($res && mysqli_fetch_row($res)) ? true : false; + } + return self::$livingCache[$uid]; + } + + /** Test helper: clear per-request caches (mirrors HeroItems invalidation). */ + public static function flushCache() + { + self::$cache = array(); + self::$livingCache = array(); + } +} diff --git a/GameEngine/HeroItems.php b/GameEngine/HeroItems.php new file mode 100644 index 00000000..8a0b9264 --- /dev/null +++ b/GameEngine/HeroItems.php @@ -0,0 +1,632 @@ +db = $database->dblink; + + // Catalog is idempotent to include (guarded defines). + global $heroItemCatalog, $heroAdventureConfig, $heroSlotIndex; + if (!isset($heroItemCatalog)) { + include_once __DIR__ . '/Data/hero_items.php'; + } + } + + /* ========================================================================= + * READS + * ===================================================================== */ + + /** + * Full inventory of a user (equipped + bag), catalog-joined. + * Rows whose itemid vanished from the catalog are returned flagged with + * 'orphan' => true instead of being hidden - anomaly surfaced, not altered. + */ + public function getInventory($uid) + { + global $heroItemCatalog; + $uid = (int) $uid; + + if (isset(self::$inventoryCache[$uid])) { + return self::$inventoryCache[$uid]; + } + + $stmt = $this->db->prepare( + "SELECT id, uid, heroid, itemid, slot, stat_value, quantity, equipped, tstamp + FROM " . TB_PREFIX . "hero_items + WHERE uid = ? ORDER BY equipped DESC, slot ASC, itemid ASC" + ); + $stmt->bind_param('i', $uid); + $stmt->execute(); + $res = $stmt->get_result(); + $rows = array(); + while ($row = $res->fetch_assoc()) { + if (isset($heroItemCatalog[$row['itemid']])) { + $row['def'] = $heroItemCatalog[$row['itemid']]; + $row['name'] = heroItemName($row['itemid']); + $row['orphan'] = false; + } else { + $row['def'] = null; + $row['name'] = 'Unknown item #' . $row['itemid']; + $row['orphan'] = true; + } + $rows[] = $row; + } + $stmt->close(); + + self::$inventoryCache[$uid] = $rows; + return $rows; + } + + /** Equipped items only, indexed by slot (one item per slot). */ + public function getEquipped($uid) + { + $equipped = array(); + foreach ($this->getInventory($uid) as $row) { + if ($row['equipped'] == 1 && !$row['orphan']) { + $equipped[(int) $row['slot']] = $row; + } + } + return $equipped; + } + + /** Silver balance (0 if the user has no hero row). */ + public function getSilver($uid) + { + $uid = (int) $uid; + $stmt = $this->db->prepare("SELECT silver FROM " . TB_PREFIX . "hero WHERE uid = ? LIMIT 1"); + $stmt->bind_param('i', $uid); + $stmt->execute(); + $stmt->bind_result($silver); + $found = $stmt->fetch(); + $stmt->close(); + return $found ? (int) $silver : 0; + } + + /* ========================================================================= + * BONUS AGGREGATION + * Returns a normalized array all later phases read from: + * [ + * 'fight_strength' => int, 'damage_reduce' => int, + * 'regen_hp' => int, 'xp_percent' => int, + * 'culture_points' => int, 'train_cavalry' => int, 'train_infantry' => int, + * 'vs_natars' => int, 'raid_percent' => int, + * 'return_speed' => int, 'speed_own' => int, 'speed_ally' => int, + * 'troop_speed_20' => int, 'horse_speed' => int, 'mount_speed' => int, + * 'unit_bonus' => [ unitId => perUnitValue, ... ] + * ] + * ===================================================================== */ + public function getBonuses($uid) + { + $uid = (int) $uid; + if (isset(self::$bonusCache[$uid])) { + return self::$bonusCache[$uid]; + } + + $bonuses = array( + HB_FIGHT => 0, HB_DMG_REDUCE => 0, HB_REGEN_HP => 0, HB_XP => 0, + HB_CP => 0, HB_TRAIN_CAV => 0, HB_TRAIN_INF => 0, HB_VS_NATARS => 0, + HB_RAID => 0, HB_RETURN_SPEED => 0, HB_SPEED_OWN => 0, HB_SPEED_ALLY => 0, + HB_TROOP_SPEED_20 => 0, HB_HORSE_SPEED => 0, HB_MOUNT_SPEED => 0, + HB_UNIT_BONUS => array(), + ); + + $equipped = $this->getEquipped($uid); + $hasHorse = isset($equipped[HSLOT_HORSE]); + + foreach ($equipped as $row) { + $def = $row['def']; + + // Spurs count only while a horse is equipped. + if (!empty($def['requires_horse']) && !$hasHorse) { + continue; + } + + foreach ($def['bonus'] as $type => $value) { + if ($type === HB_UNIT_BONUS) { + $u = (int) $value['unit']; + if (!isset($bonuses[HB_UNIT_BONUS][$u])) { + $bonuses[HB_UNIT_BONUS][$u] = 0; + } + $bonuses[HB_UNIT_BONUS][$u] += (int) $value['per_unit']; + continue; + } + if (isset($bonuses[$type])) { + $bonuses[$type] += $this->scaledBonusValue($def, (int) $value); + } + } + } + + self::$bonusCache[$uid] = $bonuses; + return $bonuses; + } + + /** + * Apply the server-speed scaling rules from the design sheet: + * 'x2_on_speed' => true -> value * 2 on speed servers (spurs, horses) + * 'x2_on_speed' => false -> value / 2 on speed servers (culture helmets) + * key absent -> value unchanged + * "Speed server" = SPEED >= 3 (per the sheet's "(1x) / (3x)" notation). + */ + public function scaledBonusValue(array $def, $value) + { + if (!array_key_exists('x2_on_speed', $def)) { + return $value; + } + $isSpeed = defined('SPEED') && SPEED >= 3; + if (!$isSpeed) { + return $value; + } + return $def['x2_on_speed'] ? $value * 2 : (int) floor($value / 2); + } + + /* ========================================================================= + * WRITES + * ===================================================================== */ + + /** + * Grant an item to a user (adventure loot, auction win, admin). + * Consumables (bag slot) stack onto an existing row; gear inserts a new row. + * Returns the hero_items.id of the affected row, or 0 on failure. + */ + public function addItem($uid, $itemid, $quantity = 1, $statValue = 0) + { + global $heroItemCatalog; + $uid = (int) $uid; $itemid = (int) $itemid; + $quantity = max(1, (int) $quantity); $statValue = (int) $statValue; + + if (!isset($heroItemCatalog[$itemid])) { + return 0; + } + $def = $heroItemCatalog[$itemid]; + $slot = (int) $def['slot']; + $now = time(); + + if ($slot === HSLOT_BAG) { + // Stack consumables. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero_items + SET quantity = quantity + ?, tstamp = ? + WHERE uid = ? AND itemid = ? AND equipped = 0 LIMIT 1" + ); + $stmt->bind_param('iiii', $quantity, $now, $uid, $itemid); + $stmt->execute(); + $affected = $stmt->affected_rows; + $stmt->close(); + + if ($affected > 0) { + $this->invalidateCaches($uid); + $row = $this->findRow($uid, $itemid); + return $row ? (int) $row['id'] : 0; + } + } + + $equipped = 0; + $stmt = $this->db->prepare( + "INSERT INTO " . TB_PREFIX . "hero_items + (uid, heroid, itemid, slot, stat_value, quantity, equipped, tstamp) + VALUES (?, 0, ?, ?, ?, ?, ?, ?)" + ); + $stmt->bind_param('iiiiiii', $uid, $itemid, $slot, $statValue, $quantity, $equipped, $now); + $stmt->execute(); + $newId = (int) $stmt->insert_id; + $stmt->close(); + + $this->invalidateCaches($uid); + return $newId; + } + + /** + * Equip an owned item into its slot. Returns true on success. + * Rules enforced: + * - item must belong to $uid and not be a consumable + * - weapons ('unit' key) require the LIVING hero to be that exact unit + * - anything already in that slot is unequipped first (atomic enough for + * a per-user action; both statements touch only this user's rows) + */ + public function equipItem($uid, $rowId) + { + global $database; + $uid = (int) $uid; $rowId = (int) $rowId; + + $row = $this->findRowById($uid, $rowId); + if (!$row || $row['orphan'] || (int) $row['def']['slot'] === HSLOT_BAG) { + return false; + } + + // Weapon unit restriction. + if (isset($row['def']['unit'])) { + $hero = $database->getHero($uid); + $heroUnit = 0; + if (is_array($hero)) { + foreach ($hero as $h) { + if ($h['dead'] != 1) { $heroUnit = (int) $h['unit']; break; } + } + } + if ($heroUnit !== (int) $row['def']['unit']) { + return false; + } + } + + $slot = (int) $row['def']['slot']; + + // Vacate the slot. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero_items SET equipped = 0 WHERE uid = ? AND slot = ? AND equipped = 1" + ); + $stmt->bind_param('ii', $uid, $slot); + $stmt->execute(); + $stmt->close(); + + // Equip the requested row. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero_items SET equipped = 1 WHERE uid = ? AND id = ? LIMIT 1" + ); + $stmt->bind_param('ii', $uid, $rowId); + $stmt->execute(); + $ok = $stmt->affected_rows > 0; + $stmt->close(); + + $this->invalidateCaches($uid); + return $ok; + } + + /** Unequip a specific owned row. Returns true on success. */ + public function unequipItem($uid, $rowId) + { + $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" + ); + $stmt->bind_param('ii', $uid, $rowId); + $stmt->execute(); + $ok = $stmt->affected_rows > 0; + $stmt->close(); + + $this->invalidateCaches($uid); + return $ok; + } + + /** + * Remove quantity from an owned row (used by useItem, auction listing). + * Deletes the row when the stack reaches zero. Returns true on success. + */ + public function removeItem($uid, $rowId, $quantity = 1) + { + $uid = (int) $uid; $rowId = (int) $rowId; $quantity = max(1, (int) $quantity); + + // Conditional decrement - never goes below zero even under races. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero_items + SET quantity = quantity - ? + WHERE uid = ? AND id = ? AND quantity >= ? LIMIT 1" + ); + $stmt->bind_param('iiii', $quantity, $uid, $rowId, $quantity); + $stmt->execute(); + $ok = $stmt->affected_rows > 0; + $stmt->close(); + + if ($ok) { + $stmt = $this->db->prepare( + "DELETE FROM " . TB_PREFIX . "hero_items WHERE uid = ? AND id = ? AND quantity <= 0 LIMIT 1" + ); + $stmt->bind_param('ii', $uid, $rowId); + $stmt->execute(); + $stmt->close(); + } + + $this->invalidateCaches($uid); + return $ok; + } + + /** + * Use a consumable. Applies immediate effects (ointment, scroll, bucket, + * book of wisdom, law tablets, artwork); returns USE_DEFERRED for + * consumables whose mechanic is wired into battle processing (bandages, + * cages - Phase 6) WITHOUT consuming them. + * $vid: target village, required for law tablets (loyalty target). + * Returns one of the USE_* class constants. + */ + public function useItem($uid, $rowId, $quantity = 1, $vid = 0) + { + global $database; + $uid = (int) $uid; $rowId = (int) $rowId; $quantity = max(1, (int) $quantity); + + $row = $this->findRowById($uid, $rowId); + if (!$row || $row['orphan'] || (int) $row['def']['slot'] !== HSLOT_BAG) { + return self::USE_INVALID; + } + if ((int) $row['quantity'] < $quantity) { + return self::USE_INVALID; + } + + $bonus = $row['def']['bonus']; + + /* ---- Ointment: +1% HP per unit, cap 99, hero must be alive ---- */ + if (isset($bonus[HB_HEAL_SELF])) { + $hero = $this->getLivingHeroRow($uid); + if (!$hero) { + return self::USE_INVALID; + } + $health = (float) $hero['health']; + if ($health >= 99) { + return self::USE_INVALID; + } + // Only consume as many as actually heal (no waste past the 99 cap). + $usable = min($quantity, (int) ceil(99 - $health)); + $newHealth = min(99, $health + $usable * (int) $bonus[HB_HEAL_SELF]); + + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero SET health = ? WHERE heroid = ? LIMIT 1" + ); + $heroid = (int) $hero['heroid']; + $stmt->bind_param('di', $newHealth, $heroid); + $stmt->execute(); + $stmt->close(); + + $this->removeItem($uid, $rowId, $usable); + return self::USE_OK; + } + + /* ---- Scroll: +10 XP each ---- */ + if (isset($bonus[HB_SCROLL])) { + $hero = $this->getLivingHeroRow($uid); + if (!$hero) { + return self::USE_INVALID; + } + $xp = (int) $bonus[HB_SCROLL] * $quantity; + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero SET experience = experience + ? WHERE heroid = ? LIMIT 1" + ); + $heroid = (int) $hero['heroid']; + $stmt->bind_param('ii', $xp, $heroid); + $stmt->execute(); + $stmt->close(); + + $this->removeItem($uid, $rowId, $quantity); + return self::USE_OK; + } + + /* ---- Bucket of Water: instant free revive of the dead hero ---- */ + if (isset($bonus[HB_BUCKET])) { + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero + SET dead = 0, health = 100, inrevive = 0, lastupdate = ? + WHERE uid = ? AND dead = 1 LIMIT 1" + ); + $now = time(); + $stmt->bind_param('ii', $now, $uid); + $stmt->execute(); + $revived = $stmt->affected_rows > 0; + $stmt->close(); + + if (!$revived) { + return self::USE_INVALID; // no dead hero to revive + } + $this->removeItem($uid, $rowId, 1); // one bucket per revive + return self::USE_OK; + } + + /* ---- Book of Wisdom: reset attribute points ---- */ + if (isset($bonus[HB_RESET])) { + $hero = $this->getLivingHeroRow($uid); + if (!$hero) { + return self::USE_INVALID; + } + // Refund all spent points: 5 free points per level gained (same + // convention as Automation::calculateLevelUp). Attributes return to 0. + $level = (int) $hero['level']; + $points = $level * 5; + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero + SET points = ?, attack = 0, defence = 0, attackbonus = 0, + defencebonus = 0, regeneration = 0 + WHERE heroid = ? LIMIT 1" + ); + $heroid = (int) $hero['heroid']; + $stmt->bind_param('ii', $points, $heroid); + $stmt->execute(); + $stmt->close(); + + $this->removeItem($uid, $rowId, 1); + return self::USE_OK; + } + + /* ---- Law Tablet: +1% loyalty per tablet on an OWN village, max 125% ---- */ + if (isset($bonus[HB_LOYALTY])) { + $vid = (int) $vid; + if ($vid <= 0) { + return self::USE_INVALID; + } + // Village must belong to the user; cap enforced in SQL so + // concurrent uses can never overshoot 125. + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "vdata + SET loyalty = LEAST(125, loyalty + ?) + WHERE wref = ? AND owner = ? AND loyalty < 125 LIMIT 1" + ); + $gain = (int) $bonus[HB_LOYALTY] * $quantity; + $stmt->bind_param('iii', $gain, $vid, $uid); + $stmt->execute(); + $ok = $stmt->affected_rows > 0; + $stmt->close(); + + if (!$ok) { + return self::USE_INVALID; // not owner, or already at cap + } + $this->removeItem($uid, $rowId, $quantity); + return self::USE_OK; + } + + /* ---- Artwork: culture points equal to the account's daily CP + * production, capped at 5000 (normal) / 2500 (speed >= 3). + * users.cp is the accumulator culturePoints() ticks into. ---- */ + if (isset($bonus[HB_ARTWORK])) { + $stmt = $this->db->prepare( + "SELECT COALESCE(SUM(cp), 0) FROM " . TB_PREFIX . "vdata WHERE owner = ?" + ); + $stmt->bind_param('i', $uid); + $stmt->execute(); + $stmt->bind_result($dailyCp); + $stmt->fetch(); + $stmt->close(); + + $cap = (defined('SPEED') && SPEED >= 3) ? 2500 : 5000; + $gain = min($cap, (int) $dailyCp) * $quantity; + if ($gain <= 0) { + return self::USE_INVALID; + } + + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "users SET cp = cp + ? WHERE id = ? LIMIT 1" + ); + $stmt->bind_param('ii', $gain, $uid); + $stmt->execute(); + $ok = $stmt->affected_rows > 0; + $stmt->close(); + + if (!$ok) { + return self::USE_INVALID; + } + $this->removeItem($uid, $rowId, $quantity); + return self::USE_OK; + } + + /* ---- Deferred consumables (mechanic lands in Phase 6 with the UI: + * bandages consume on post-battle healing, cages on oasis + * attacks). NOT consumed here. ---- */ + if (isset($bonus[HB_HEAL_TROOP]) || isset($bonus[HB_CAGE])) { + return self::USE_DEFERRED; + } + + return self::USE_INVALID; + } + + /* ========================================================================= + * SILVER (race-safe conditional updates) + * ===================================================================== */ + + /** Add silver to the user's hero row. Returns true if a hero row exists. */ + public function addSilver($uid, $amount) + { + $uid = (int) $uid; $amount = max(0, (int) $amount); + if ($amount === 0) { + return true; + } + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero SET silver = silver + ? WHERE uid = ? LIMIT 1" + ); + $stmt->bind_param('ii', $amount, $uid); + $stmt->execute(); + $ok = $stmt->affected_rows > 0; + $stmt->close(); + return $ok; + } + + /** + * Spend silver. The WHERE silver >= ? guard makes this race-safe: + * two concurrent spends can never take the balance negative. + */ + public function spendSilver($uid, $amount) + { + $uid = (int) $uid; $amount = max(0, (int) $amount); + if ($amount === 0) { + return true; + } + $stmt = $this->db->prepare( + "UPDATE " . TB_PREFIX . "hero + SET silver = silver - ? + WHERE uid = ? AND silver >= ? LIMIT 1" + ); + $stmt->bind_param('iii', $amount, $uid, $amount); + $stmt->execute(); + $ok = $stmt->affected_rows > 0; + $stmt->close(); + return $ok; + } + + /* ========================================================================= + * INTERNALS + * ===================================================================== */ + + private function invalidateCaches($uid) + { + unset(self::$inventoryCache[(int) $uid], self::$bonusCache[(int) $uid]); + } + + private function findRow($uid, $itemid) + { + foreach ($this->getInventory($uid) as $row) { + if ((int) $row['itemid'] === (int) $itemid && $row['equipped'] == 0) { + return $row; + } + } + return null; + } + + private function findRowById($uid, $rowId) + { + foreach ($this->getInventory($uid) as $row) { + if ((int) $row['id'] === (int) $rowId) { + return $row; + } + } + return null; + } + + /** Fetch the raw LIVING hero row directly (health/level/points included). */ + private function getLivingHeroRow($uid) + { + $uid = (int) $uid; + $stmt = $this->db->prepare( + "SELECT heroid, uid, unit, level, points, experience, health, dead + FROM " . TB_PREFIX . "hero WHERE uid = ? AND dead = 0 LIMIT 1" + ); + $stmt->bind_param('i', $uid); + $stmt->execute(); + $res = $stmt->get_result(); + $row = $res->fetch_assoc(); + $stmt->close(); + return $row ?: null; + } +} diff --git a/GameEngine/Lang/en.php b/GameEngine/Lang/en.php index 93489cb8..e434baf6 100755 --- a/GameEngine/Lang/en.php +++ b/GameEngine/Lang/en.php @@ -2678,7 +2678,114 @@ if (!function_exists('tz_loc_topic')) { 'Unoccupied Oasis' => TZ_RT_UNOCC_OASIS, 'New village founded' => TZ_RT_NEW_VILLAGE, 'Settlers returned - valley occupied' => TZ_RT_VALLEY_OCCUPIED, + // T4 hero port (Phase 6) + 'Hero returned from an adventure' => TZ_RT_ADV_RETURNED, + 'Hero fell on an adventure' => TZ_RT_ADV_FELL, + 'Auction won' => TZ_RT_AUC_WON, + 'Auction sold' => TZ_RT_AUC_SOLD, + 'Auction expired' => TZ_RT_AUC_EXPIRED, ); return strtr($s, $map); } } + +/* ============================================================================= + * T4 HERO PORT (Phase 6) - items / adventures / auction house + * ========================================================================== */ +tz_def('HERO_T4_TAB_HERO', 'Hero'); +tz_def('HERO_T4_TAB_ITEMS', 'Inventory'); +tz_def('HERO_T4_TAB_ADVENTURES', 'Adventures'); +tz_def('HERO_T4_TAB_AUCTION', 'Auctions'); + +tz_def('HERO_SILVER', 'Silver'); +tz_def('HERO_EXPERIENCE', 'Experience'); +tz_def('RESOURCES', 'Resources'); + +/* Equipment slots */ +tz_def('HERO_SLOT_1', 'Helmet'); +tz_def('HERO_SLOT_2', 'Body'); +tz_def('HERO_SLOT_3', 'Right hand'); +tz_def('HERO_SLOT_4', 'Left hand'); +tz_def('HERO_SLOT_5', 'Shoes'); +tz_def('HERO_SLOT_6', 'Horse'); +tz_def('HERO_SLOT_7', 'Bag'); + +/* Inventory actions */ +tz_def('HERO_ITEMS_EQUIPPED', 'Equipped'); +tz_def('HERO_ITEMS_BAG', 'Inventory'); +tz_def('HERO_ITEMS_EMPTY', 'Your hero owns no items yet. Adventures and the auction house are good places to find some.'); +tz_def('HERO_EQUIP', 'Equip'); +tz_def('HERO_UNEQUIP', 'Take off'); +tz_def('HERO_USE_ITEM', 'Use'); +tz_def('HERO_QUANTITY', 'Amount'); +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_EQUIP_FAIL', 'This item cannot be equipped (wrong hero unit or item type).'); +tz_def('HERO_UNEQUIP_OK', 'Item taken off.'); + +/* Adventures */ +tz_def('HERO_ADV_NORMAL', 'a normal adventure'); +tz_def('HERO_ADV_HARD', 'a hard adventure'); +tz_def('HERO_ADV_LIST', 'Available adventures'); +tz_def('HERO_ADV_NONE', 'No adventures available right now. New ones appear over time.'); +tz_def('HERO_ADV_DIFFICULTY', 'Difficulty'); +tz_def('HERO_ADV_DIFF_NORMAL', 'Normal'); +tz_def('HERO_ADV_DIFF_HARD', 'Hard'); +tz_def('HERO_ADV_DURATION', 'Travel time (one way)'); +tz_def('HERO_ADV_EXPIRES', 'Expires in'); +tz_def('HERO_ADV_GO', 'Start adventure'); +tz_def('HERO_ADV_RUNNING', 'Your hero is on an adventure and will arrive in'); +tz_def('HERO_ADV_START_OK', 'Your hero has set off on the adventure.'); +tz_def('HERO_ADV_START_NOHERO', 'You need a living hero to start an adventure.'); +tz_def('HERO_ADV_START_AWAY', 'Your hero is not at home.'); +tz_def('HERO_ADV_START_FAIL', 'This adventure is no longer available.'); +tz_def('HERO_ADV_RETURNED', 'Your hero has returned from'); +tz_def('HERO_ADV_REWARD', 'Reward'); +tz_def('HERO_ADV_AMOUNT', 'Amount'); +tz_def('HERO_ADV_ITEM_FOUND', 'Item found'); +tz_def('HERO_ADV_HP_LOST', 'Health lost'); +tz_def('HERO_ADV_DIED', 'Your hero fell on'); +tz_def('HERO_ADV_DIED_INFO', "All loot was lost. The hero can be revived at the Hero's Mansion."); + +/* Auction house */ +tz_def('HERO_AUC_OPEN', 'Open auctions'); +tz_def('HERO_AUC_NONE', 'There are no open auctions at the moment.'); +tz_def('HERO_AUC_ITEM', 'Item'); +tz_def('HERO_AUC_PRICE', 'Current price'); +tz_def('HERO_AUC_FINAL_PRICE', 'Final price'); +tz_def('HERO_AUC_TIME_LEFT', 'Ends in'); +tz_def('HERO_AUC_YOUR_MAX', 'Your maximum'); +tz_def('HERO_AUC_BID', 'Bid'); +tz_def('HERO_AUC_BID_OK', 'Your bid was placed. You are the highest bidder.'); +tz_def('HERO_AUC_BID_OUTBID', 'Your bid was immediately outbid by a higher maximum.'); +tz_def('HERO_AUC_BID_FAIL', 'Your bid was not accepted.'); +tz_def('HERO_AUC_BID_NOSILVER', 'You do not have enough silver for this bid.'); +tz_def('HERO_AUC_MY_BIDS', 'My bids'); +tz_def('HERO_AUC_MY_SALES', 'My sales'); +tz_def('HERO_AUC_SELL', 'Sell an item'); +tz_def('HERO_AUC_SELL_OK', 'Your item has been listed.'); +tz_def('HERO_AUC_SELL_FAIL', 'This item cannot be listed (equipped items must be taken off first).'); +tz_def('HERO_AUC_START_PRICE', 'Starting price'); +tz_def('HERO_AUC_DURATION', 'Duration'); +tz_def('HERO_AUC_LIST', 'List item'); +tz_def('HERO_AUC_SELLER_NPC', 'Merchant'); +tz_def('HERO_AUC_WON', 'You won the auction for'); +tz_def('HERO_AUC_SOLD', 'Your auction sold:'); +tz_def('HERO_AUC_REFUND', 'Refunded from your maximum bid'); +tz_def('HERO_AUC_FEE', 'Auction fee'); +tz_def('HERO_AUC_PAYOUT', 'Payout'); +tz_def('HERO_AUC_EXPIRED', 'Your auction ended without bids. The item was returned to your inventory:'); + +/* Report topics (raw English strings live in ndata.topic) */ +tz_def('TZ_RT_ADV_RETURNED', 'Hero returned from an adventure'); +tz_def('TZ_RT_ADV_FELL', 'Hero fell on an adventure'); +tz_def('TZ_RT_AUC_WON', 'Auction won'); +tz_def('TZ_RT_AUC_SOLD', 'Auction sold'); +tz_def('TZ_RT_AUC_EXPIRED', 'Auction expired'); + +/* T4 hero port - movement display (dorf1 + rally point) */ +tz_def('HERO_ADV_MOV_OUT', 'Hero on an adventure'); +tz_def('HERO_ADV_MOV_BACK', 'Hero returning from an adventure'); +tz_def('HERO_ADV_MOV_SHORT', 'Adventure'); diff --git a/GameEngine/Message.php b/GameEngine/Message.php index bad34158..8bfcd276 100755 --- a/GameEngine/Message.php +++ b/GameEngine/Message.php @@ -452,6 +452,14 @@ class Message case 24: case 25: return 24; + + // Hero adventure report (T4 hero port) + case 26: + return 26; + + // Hero auction report (T4 hero port) + case 27: + return 27; } return $type; } diff --git a/GameEngine/Units.php b/GameEngine/Units.php index 84402413..1ef20ea0 100755 --- a/GameEngine/Units.php +++ b/GameEngine/Units.php @@ -796,7 +796,12 @@ class Units { $speeds[] = $GLOBALS['u'.$heroUnit]['speed']; } - return $generator->procDistanceTime($fromCor, $toCor, min($speeds), $mode, $from); + $walkTime = $generator->procDistanceTime($fromCor, $toCor, min($speeds), $mode, $from); + + // T4 hero port (Phase 5): boots (>20 tiles), pennant (own villages) + // and standard (ally villages) shorten the trip when the hero rides + // along. Returns the input unchanged when NEW_FUNCTIONS_HERO_T4 is off. + return HeroBattleBonus::adjustTravelTime($owner, $unitArray[10] ?? 0, $from, $to, $walkTime); } public function startRaidList($post){ diff --git a/Templates/Build/16.tpl b/Templates/Build/16.tpl index cef6f5e3..a421fb48 100644 --- a/Templates/Build/16.tpl +++ b/Templates/Build/16.tpl @@ -207,6 +207,11 @@ $hasRally = $village->resarray['f39'] > 0; $set = $database->getMovement(5,$village->wid,0); $cnt = count($set); foreach($out as $u) if($u['vref']==$village->wid) $cnt++; + // T4 hero port: adventure legs count as troops on their way too. + if (defined('NEW_FUNCTIONS_HERO_T4') && NEW_FUNCTIONS_HERO_T4) { + $cnt += count($database->getMovement(20,$village->wid,0)); + $cnt += count($database->getMovement(21,$village->wid,1)); + } ?> =1): ?>

diff --git a/Templates/Build/16_walking.tpl b/Templates/Build/16_walking.tpl index 1082fed1..7f5d366b 100644 --- a/Templates/Build/16_walking.tpl +++ b/Templates/Build/16_walking.tpl @@ -20,6 +20,41 @@ ################################################################################# $outgoing = $database->getMovement(3, $village->wid, 0); + +// T4 hero port: hero adventure legs (out = from this village, back = to it). +$advWalking = []; +if (defined('NEW_FUNCTIONS_HERO_T4') && NEW_FUNCTIONS_HERO_T4) { + foreach ($database->getMovement(20, $village->wid, 0) as $m) { $m['t4adv'] = 'out'; $advWalking[] = $m; } + foreach ($database->getMovement(21, $village->wid, 1) as $m) { $m['t4adv'] = 'back'; $advWalking[] = $m; } +} +?> + +timer++; + $action = $m['t4adv'] === 'out' + ? (defined('HERO_ADV_MOV_OUT') ? HERO_ADV_MOV_OUT : 'Hero on an adventure') + : (defined('HERO_ADV_MOV_BACK') ? HERO_ADV_MOV_BACK : 'Hero returning from an adventure'); + $dt = $generator->procMtime($m['endtime']); +?> + + + + + + + + + + + + + +
vname?>
  
1 
+
getTimeFormat($m['endtime']-time())?> h
+
+
+ + resarray['f' . $id] >= 1) { include_once("37_land.tpl"); + } else if (defined('NEW_FUNCTIONS_HERO_T4') && NEW_FUNCTIONS_HERO_T4 + && $village->resarray['f' . $id] >= 1 + && isset($_GET['t4tab']) + && in_array($_GET['t4tab'], ['items', 'adventures', 'auction'], true)) { + + // T4 hero port (Phase 6): items / adventures / auction tabs. + // The classic hero flow below stays byte-identical when the + // feature flag is off or no tab is selected. + $t4tab = $_GET['t4tab']; + include_once("37_t4nav.tpl"); + include_once("37_" . $t4tab . ".tpl"); + } else if ($village->resarray['f' . $id] >= 1) { + if (defined('NEW_FUNCTIONS_HERO_T4') && NEW_FUNCTIONS_HERO_T4) { + $t4tab = 'hero'; + include_once("37_t4nav.tpl"); + } $include_training = true; $include_revive = false; if (isset($heroes) && is_array($heroes) && count($heroes)) { diff --git a/Templates/Build/37_adventures.tpl b/Templates/Build/37_adventures.tpl new file mode 100644 index 00000000..a68130db --- /dev/null +++ b/Templates/Build/37_adventures.tpl @@ -0,0 +1,78 @@ +timer JS convention (same as 37.tpl's training timer). # +################################################################################# + +$t4Adventures = new HeroAdventure(); +$t4Msg = ''; + +if (isset($_POST['t4action'], $_POST['advid']) && $_POST['t4action'] === 'startadv') { + $t4Result = $t4Adventures->startAdventure($session->uid, (int) $_POST['advid']); + if ($t4Result === HeroAdventure::START_OK) { + $t4Msg = HERO_ADV_START_OK; + } elseif ($t4Result === HeroAdventure::START_NO_HERO) { + $t4Msg = HERO_ADV_START_NOHERO; + } elseif ($t4Result === HeroAdventure::START_HERO_AWAY) { + $t4Msg = HERO_ADV_START_AWAY; + } else { + $t4Msg = HERO_ADV_START_FAIL; + } +} + +// Top up the list opportunistically (respects max/refresh limits internally). +$t4Adventures->generateOffers($session->uid); + +$t4Offers = $t4Adventures->getOffers($session->uid); +$t4Running = $t4Adventures->getRunning($session->uid); +$t4Now = time(); +?> + + +

+ + + + + + + +
+ getTimeFormat(max(0, $t4Running['endtime'] - $t4Now)); ?> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
getTimeFormat((int) $t4Offer['duration']); ?>getTimeFormat(max(0, $t4Offer['expire'] - $t4Now)); ?> + +
+ + + +
+ - +
diff --git a/Templates/Build/37_auction.tpl b/Templates/Build/37_auction.tpl new file mode 100644 index 00000000..8a7f2071 --- /dev/null +++ b/Templates/Build/37_auction.tpl @@ -0,0 +1,160 @@ +placeBid($session->uid, (int) $_POST['aucid'], (int) $_POST['maxbid']); + if ($t4Result === HeroAuction::BID_OK) { + $t4Msg = HERO_AUC_BID_OK; + } elseif ($t4Result === HeroAuction::BID_OUTBID) { + $t4Msg = HERO_AUC_BID_OUTBID; + } elseif ($t4Result === HeroAuction::BID_NO_SILVER) { + $t4Msg = HERO_AUC_BID_NOSILVER; + } else { + $t4Msg = HERO_AUC_BID_FAIL; + } + } elseif ($_POST['t4action'] === 'sell' + && isset($_POST['rowid'], $_POST['qty'], $_POST['price'], $_POST['duration'])) { + $t4Result = $t4Auction->createAuction( + $session->uid, (int) $_POST['rowid'], + (int) $_POST['qty'], (int) $_POST['price'], (int) $_POST['duration'] + ); + $t4Msg = ($t4Result > 0) ? HERO_AUC_SELL_OK : HERO_AUC_SELL_FAIL; + } +} + +$t4Open = $t4Auction->getOpenAuctions(); +$t4MyBids = $t4Auction->getMyBids($session->uid); +$t4MySales = $t4Auction->getMySales($session->uid); +$t4Now = time(); + +// Unequipped items sellable from the inventory. +$t4Sellable = array(); +foreach ($t4HeroItems->getInventory($session->uid) as $t4Row) { + if ($t4Row['equipped'] == 0 && !$t4Row['orphan']) { + $t4Sellable[] = $t4Row; + } +} +?> + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ () + getTimeFormat(max(0, $t4A['time_end'] - $t4Now)); ?> + uid) { ?> +
+ + + + +
+ - +
+ + + + + + + + + + + + + + + + +
(x): : getTimeFormat(max(0, $t4A['time_end'] - $t4Now)); ?>
+ + + + + + + + + + + + + + + + +
(x): getTimeFormat(max(0, $t4A['time_end'] - $t4Now)); ?>
+ + + + + + + + + + + + + + + + + + + +
+ + : : : + +
+ diff --git a/Templates/Build/37_items.tpl b/Templates/Build/37_items.tpl new file mode 100644 index 00000000..f55b3326 --- /dev/null +++ b/Templates/Build/37_items.tpl @@ -0,0 +1,112 @@ +wid). # +################################################################################# + +$t4Msg = ''; + +if (isset($_POST['t4action'], $_POST['rowid'])) { + $t4RowId = (int) $_POST['rowid']; + + switch ($_POST['t4action']) { + case 'equip': + $t4Msg = $t4HeroItems->equipItem($session->uid, $t4RowId) + ? HERO_EQUIP_OK : HERO_EQUIP_FAIL; + break; + + case 'unequip': + $t4Msg = $t4HeroItems->unequipItem($session->uid, $t4RowId) + ? HERO_UNEQUIP_OK : HERO_ITEM_USE_FAIL; + break; + + case 'useitem': + $t4Qty = max(1, (int) ($_POST['qty'] ?? 1)); + $t4Result = $t4HeroItems->useItem($session->uid, $t4RowId, $t4Qty, $village->wid); + if ($t4Result === HeroItems::USE_OK) { + $t4Msg = HERO_ITEM_USED_OK; + } elseif ($t4Result === HeroItems::USE_DEFERRED) { + $t4Msg = HERO_ITEM_USE_BATTLE; + } else { + $t4Msg = HERO_ITEM_USE_FAIL; + } + break; + } +} + +$t4Inventory = $t4HeroItems->getInventory($session->uid); +$t4Equipped = $t4HeroItems->getEquipped($session->uid); +?> + + +

+ + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
-
+ + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + + + + +
+
diff --git a/Templates/Build/37_t4nav.tpl b/Templates/Build/37_t4nav.tpl new file mode 100644 index 00000000..136c068c --- /dev/null +++ b/Templates/Build/37_t4nav.tpl @@ -0,0 +1,28 @@ +getSilver($session->uid); +$t4Tabs = [ + 'hero' => ['label' => HERO_T4_TAB_HERO, 'url' => 'build.php?id=' . $id], + 'items' => ['label' => HERO_T4_TAB_ITEMS, 'url' => 'build.php?id=' . $id . '&t4tab=items'], + 'adventures' => ['label' => HERO_T4_TAB_ADVENTURES, 'url' => 'build.php?id=' . $id . '&t4tab=adventures'], + 'auction' => ['label' => HERO_T4_TAB_AUCTION, 'url' => 'build.php?id=' . $id . '&t4tab=auction'], +]; +?> + +
+ $tab) { ?> + + + + + + + : +
+
diff --git a/Templates/Notice/26.tpl b/Templates/Notice/26.tpl new file mode 100644 index 00000000..b38f80f0 --- /dev/null +++ b/Templates/Notice/26.tpl @@ -0,0 +1,120 @@ +readingNotice['data'] ?? ''), $adv); + +$difficulty = (int)($adv['difficulty'] ?? 0); +$died = (int)($adv['died'] ?? 0); +$exp = (int)($adv['exp'] ?? 0); +$silver = (int)($adv['silver'] ?? 0); +$hp = (int)($adv['hp'] ?? 0); +$wood = (int)($adv['wood'] ?? 0); +$clay = (int)($adv['clay'] ?? 0); +$iron = (int)($adv['iron'] ?? 0); +$crop = (int)($adv['crop'] ?? 0); +$itemId = (int)($adv['itemid'] ?? 0); +$itemQty = (int)($adv['itemqty'] ?? 0); + +// Resolve the item name from the catalog (heroItemName falls back safely). +// berichte.php runs from the web root, so the relative path is stable. +if ($itemId > 0 && !function_exists('heroItemName')) { + include_once 'GameEngine/Data/hero_items.php'; +} + +$diffLabel = $difficulty === 1 + ? tz_hero_adv('HERO_ADV_HARD', 'a hard adventure') + : tz_hero_adv('HERO_ADV_NORMAL', 'a normal adventure'); +?> + + + + + + + + + + + procMtime($message->readingNotice['time']); ?> + + + + + + + + + + +
:readingNotice['topic']); ?>
+ + + +

.

+

+ + + +

.

+ + + + + + + + + + + 0) { ?> + + + + + + + + + + + + 0 && $itemQty > 0) { ?> + + + + + + 0) { ?> + + + + + +
+
+
+ + + + +
×
-%
+ + + +
diff --git a/Templates/Notice/27.tpl b/Templates/Notice/27.tpl new file mode 100644 index 00000000..e0ded769 --- /dev/null +++ b/Templates/Notice/27.tpl @@ -0,0 +1,95 @@ +readingNotice['data'] ?? ''), $auc); + +$role = (string)($auc['role'] ?? ''); +$expired = (int)($auc['expired'] ?? 0); +$itemId = (int)($auc['itemid'] ?? 0); +$qty = (int)($auc['qty'] ?? 0); +$price = (int)($auc['price'] ?? 0); +$refund = (int)($auc['refund'] ?? 0); +$fee = (int)($auc['fee'] ?? 0); + +if ($itemId > 0 && !function_exists('heroItemName')) { + include_once 'GameEngine/Data/hero_items.php'; +} +$itemLabel = $qty . '× ' . ($itemId > 0 ? heroItemName($itemId) : '?'); +?> + + + + + + + + + + + procMtime($message->readingNotice['time']); ?> + + + + + + + + + + +
:readingNotice['topic']); ?>
+ + + +

.

+ + + + + + 0) { ?> + + + + + +
+
+ + + +

.

+ + + + + + + + + + + + + +
-
+
+ + + +

.

+ + + +
diff --git a/Templates/movement.tpl b/Templates/movement.tpl index d675044a..415244a0 100644 --- a/Templates/movement.tpl +++ b/Templates/movement.tpl @@ -112,6 +112,12 @@ $movement3_0 = $database->getMovement(3, $village->wid, 0); $movement5_0 = $database->getMovement(5, $village->wid, 0); $movement8_1 = $database->getMovement(8, $village->wid, 1); $movement9_0 = $database->getMovement(9, $village->wid, 0); +// T4 hero port: hero adventure movements are only relevant for the hero's +// home village and only when the feature is on. +$movement20_0 = (defined('NEW_FUNCTIONS_HERO_T4') && NEW_FUNCTIONS_HERO_T4) + ? $database->getMovement(20, $village->wid, 0) : []; +$movement21_1 = (defined('NEW_FUNCTIONS_HERO_T4') && NEW_FUNCTIONS_HERO_T4) + ? $database->getMovement(21, $village->wid, 1) : []; /** * --------------------------------------------------------- @@ -285,6 +291,42 @@ renderMovementRow( $session ); +/** + * ========================================================= + * HERO ADVENTURE (T4 hero port): outbound + returning + * ========================================================= + */ + +$advOutArrival = array(); +foreach ($movement20_0 as $receive) { + $advOutArrival[] = $receive['endtime']; +} +renderMovementRow( + 'att2', + 'a2', + defined('HERO_ADV_MOV_OUT') ? HERO_ADV_MOV_OUT : 'Hero on an adventure', + defined('HERO_ADV_MOV_SHORT') ? HERO_ADV_MOV_SHORT : 'Adventure', + count($movement20_0), + !empty($advOutArrival) ? min($advOutArrival) : 0, + $generator, + $session +); + +$advBackArrival = array(); +foreach ($movement21_1 as $receive) { + $advBackArrival[] = $receive['endtime']; +} +renderMovementRow( + 'def1', + 'd1', + defined('HERO_ADV_MOV_BACK') ? HERO_ADV_MOV_BACK : 'Hero returning from an adventure', + defined('HERO_ADV_MOV_SHORT') ? HERO_ADV_MOV_SHORT : 'Adventure', + count($movement21_1), + !empty($advBackArrival) ? min($advBackArrival) : 0, + $generator, + $session +); + /** * ========================================================= * FOUNDING NEW VILLAGE diff --git a/activate.php b/activate.php index 5ed4b35a..ae376d90 100644 --- a/activate.php +++ b/activate.php @@ -3,15 +3,21 @@ ################################################################################# ## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ## ## --------------------------------------------------------------------------- ## -## Project: TravianZ ## -## Filename activate.php ## -## Developed by: Dixie ## -## Developed by: Dzoki ## -## License: TravianZ Project ## -## Copyright: TravianZ (c) 2010-2026. All rights reserved. ## -## URLs: http://travian.shadowss.ro ## -## Source code: https://github.com/Shadowss/TravianZ ## -## ## +## Filename : activate.php ## +## Type : In Game Activate File ## +## --------------------------------------------------------------------------- ## +## Developed by : Dzoki & Advocaite & Donnchadh ## +## Refactored by : Shadow ## +## Redesign by : Shadow ## +## --------------------------------------------------------------------------- ## +## Contact : cata7007@gmail.com ## +## Project : TravianZ ## +## URLs: : https://travianz.org ## +## GitHub : https://github.com/Shadowss/TravianZ ## +## --------------------------------------------------------------------------- ## +## License : TravianZ Project ## +## Copyright : TravianZ (c) 2010-2026. All rights reserved. ## +## --------------------------------------------------------------------------- ## ################################################################################# use App\Utils\AccessLogger; diff --git a/ajax.php b/ajax.php index a0e38230..c1543abf 100644 --- a/ajax.php +++ b/ajax.php @@ -1,14 +1,23 @@ - <?php echo SERVER_NAME ?> - Mass Message + <?php echo SERVER_NAME ?> - Build Croppers diff --git a/css/hero_items.css b/css/hero_items.css new file mode 100644 index 00000000..7c5b6ca5 --- /dev/null +++ b/css/hero_items.css @@ -0,0 +1,116 @@ +/* AUTO-GENERATED from GameEngine/Data/hero_items.php - regenerate via var/tools/generate_hero_sprites.php + Placeholder art: replace img/hero/items.png with real 48x48 sprites on the SAME grid + (16 columns, catalog order) and this CSS keeps working unchanged. */ +.heroT4Item{display:inline-block;width:48px;height:48px;vertical-align:middle; + background-image:url('../img/hero/items.png');background-repeat:no-repeat;} +.heroT4Item.item1{background-position:-0px -0px;} /* Helmet of Awareness */ +.heroT4Item.item2{background-position:-48px -0px;} /* Helmet of Enlightenment */ +.heroT4Item.item3{background-position:-96px -0px;} /* Helmet of Wisdom */ +.heroT4Item.item4{background-position:-144px -0px;} /* Helmet of Regeneration */ +.heroT4Item.item5{background-position:-192px -0px;} /* Helmet of Health */ +.heroT4Item.item6{background-position:-240px -0px;} /* Helmet of Healing */ +.heroT4Item.item7{background-position:-288px -0px;} /* Helmet of the Gladiator */ +.heroT4Item.item8{background-position:-336px -0px;} /* Helmet of the Tribune */ +.heroT4Item.item9{background-position:-384px -0px;} /* Helmet of the Consul */ +.heroT4Item.item10{background-position:-432px -0px;} /* Helmet of the Horseman */ +.heroT4Item.item11{background-position:-480px -0px;} /* Helmet of the Cavalry */ +.heroT4Item.item12{background-position:-528px -0px;} /* Helmet of the Heavy Cavalry */ +.heroT4Item.item13{background-position:-576px -0px;} /* Helmet of the Mercenary */ +.heroT4Item.item14{background-position:-624px -0px;} /* Helmet of the Warrior */ +.heroT4Item.item15{background-position:-672px -0px;} /* Helmet of the Ruler */ +.heroT4Item.item20{background-position:-720px -0px;} /* Light Breastplate of Regeneration */ +.heroT4Item.item21{background-position:-0px -48px;} /* Breastplate of Regeneration */ +.heroT4Item.item22{background-position:-48px -48px;} /* Heavy Breastplate of Regeneration */ +.heroT4Item.item23{background-position:-96px -48px;} /* Light Armor */ +.heroT4Item.item24{background-position:-144px -48px;} /* Armor */ +.heroT4Item.item25{background-position:-192px -48px;} /* Heavy Armor */ +.heroT4Item.item26{background-position:-240px -48px;} /* Light Cuirass */ +.heroT4Item.item27{background-position:-288px -48px;} /* Cuirass */ +.heroT4Item.item28{background-position:-336px -48px;} /* Heavy Cuirass */ +.heroT4Item.item29{background-position:-384px -48px;} /* Light Segmented Armor */ +.heroT4Item.item30{background-position:-432px -48px;} /* Segmented Armor */ +.heroT4Item.item31{background-position:-480px -48px;} /* Heavy Segmented Armor */ +.heroT4Item.item40{background-position:-528px -48px;} /* Small Shield */ +.heroT4Item.item41{background-position:-576px -48px;} /* Shield */ +.heroT4Item.item42{background-position:-624px -48px;} /* Great Shield */ +.heroT4Item.item43{background-position:-672px -48px;} /* Small Hunting Horn */ +.heroT4Item.item44{background-position:-720px -48px;} /* Hunting Horn */ +.heroT4Item.item45{background-position:-0px -96px;} /* Great Hunting Horn */ +.heroT4Item.item46{background-position:-48px -96px;} /* Thief's Satchel */ +.heroT4Item.item47{background-position:-96px -96px;} /* Thief's Bag */ +.heroT4Item.item48{background-position:-144px -96px;} /* Thief's Sack */ +.heroT4Item.item49{background-position:-192px -96px;} /* Small Map */ +.heroT4Item.item50{background-position:-240px -96px;} /* Map */ +.heroT4Item.item51{background-position:-288px -96px;} /* Great Map */ +.heroT4Item.item52{background-position:-336px -96px;} /* Small Pennant */ +.heroT4Item.item53{background-position:-384px -96px;} /* Pennant */ +.heroT4Item.item54{background-position:-432px -96px;} /* Great Pennant */ +.heroT4Item.item55{background-position:-480px -96px;} /* Small Standard */ +.heroT4Item.item56{background-position:-528px -96px;} /* Standard */ +.heroT4Item.item57{background-position:-576px -96px;} /* Great Standard */ +.heroT4Item.item60{background-position:-624px -96px;} /* Boots of Regeneration */ +.heroT4Item.item61{background-position:-672px -96px;} /* Boots of Recovery */ +.heroT4Item.item62{background-position:-720px -96px;} /* Boots of Healing */ +.heroT4Item.item63{background-position:-0px -144px;} /* Boots of the Mercenary */ +.heroT4Item.item64{background-position:-48px -144px;} /* Boots of the Warrior */ +.heroT4Item.item65{background-position:-96px -144px;} /* Boots of the Ruler */ +.heroT4Item.item66{background-position:-144px -144px;} /* Small Spurs */ +.heroT4Item.item67{background-position:-192px -144px;} /* Spurs */ +.heroT4Item.item68{background-position:-240px -144px;} /* Great Spurs */ +.heroT4Item.item70{background-position:-288px -144px;} /* Riding Horse */ +.heroT4Item.item71{background-position:-336px -144px;} /* Thoroughbred */ +.heroT4Item.item72{background-position:-384px -144px;} /* Warhorse */ +.heroT4Item.item101{background-position:-432px -144px;} /* Legionnaire's Short Sword */ +.heroT4Item.item102{background-position:-480px -144px;} /* Legionnaire's Sword */ +.heroT4Item.item103{background-position:-528px -144px;} /* Legionnaire's Long Sword */ +.heroT4Item.item104{background-position:-576px -144px;} /* Praetorian's Short Sword */ +.heroT4Item.item105{background-position:-624px -144px;} /* Praetorian's Sword */ +.heroT4Item.item106{background-position:-672px -144px;} /* Praetorian's Long Sword */ +.heroT4Item.item107{background-position:-720px -144px;} /* Imperian's Short Sword */ +.heroT4Item.item108{background-position:-0px -192px;} /* Imperian's Sword */ +.heroT4Item.item109{background-position:-48px -192px;} /* Imperian's Long Sword */ +.heroT4Item.item110{background-position:-96px -192px;} /* Imperatoris' Short Saber */ +.heroT4Item.item111{background-position:-144px -192px;} /* Imperatoris' Saber */ +.heroT4Item.item112{background-position:-192px -192px;} /* Imperatoris' Long Saber */ +.heroT4Item.item113{background-position:-240px -192px;} /* Caesaris' Light Lance */ +.heroT4Item.item114{background-position:-288px -192px;} /* Caesaris' Lance */ +.heroT4Item.item115{background-position:-336px -192px;} /* Caesaris' Heavy Lance */ +.heroT4Item.item116{background-position:-384px -192px;} /* Clubswinger's Cudgel */ +.heroT4Item.item117{background-position:-432px -192px;} /* Clubswinger's Club */ +.heroT4Item.item118{background-position:-480px -192px;} /* Clubswinger's Mace */ +.heroT4Item.item119{background-position:-528px -192px;} /* Spearman's Pitchfork */ +.heroT4Item.item120{background-position:-576px -192px;} /* Spearman's Pike */ +.heroT4Item.item121{background-position:-624px -192px;} /* Spearman's Spear */ +.heroT4Item.item122{background-position:-672px -192px;} /* Axeman's Hatchet */ +.heroT4Item.item123{background-position:-720px -192px;} /* Axeman's Axe */ +.heroT4Item.item124{background-position:-0px -240px;} /* Axeman's Battle Axe */ +.heroT4Item.item125{background-position:-48px -240px;} /* Paladin's Small Hammer */ +.heroT4Item.item126{background-position:-96px -240px;} /* Paladin's Hammer */ +.heroT4Item.item127{background-position:-144px -240px;} /* Paladin's Sledgehammer */ +.heroT4Item.item128{background-position:-192px -240px;} /* Teutonic Knight's Short Sword */ +.heroT4Item.item129{background-position:-240px -240px;} /* Teutonic Knight's Sword */ +.heroT4Item.item130{background-position:-288px -240px;} /* Teutonic Knight's Long Sword */ +.heroT4Item.item131{background-position:-336px -240px;} /* Phalanx's Pitchfork */ +.heroT4Item.item132{background-position:-384px -240px;} /* Phalanx's Spear */ +.heroT4Item.item133{background-position:-432px -240px;} /* Phalanx's Lance */ +.heroT4Item.item134{background-position:-480px -240px;} /* Swordsman's Short Sword */ +.heroT4Item.item135{background-position:-528px -240px;} /* Swordsman's Sword */ +.heroT4Item.item136{background-position:-576px -240px;} /* Swordsman's Long Sword */ +.heroT4Item.item137{background-position:-624px -240px;} /* Thunder's Short Bow */ +.heroT4Item.item138{background-position:-672px -240px;} /* Thunder's Bow */ +.heroT4Item.item139{background-position:-720px -240px;} /* Thunder's Long Bow */ +.heroT4Item.item140{background-position:-0px -288px;} /* Druidrider's Baton */ +.heroT4Item.item141{background-position:-48px -288px;} /* Druidrider's Staff */ +.heroT4Item.item142{background-position:-96px -288px;} /* Druidrider's Great Staff */ +.heroT4Item.item143{background-position:-144px -288px;} /* Haeduan's Light Lance */ +.heroT4Item.item144{background-position:-192px -288px;} /* Haeduan's Lance */ +.heroT4Item.item145{background-position:-240px -288px;} /* Haeduan's Heavy Lance */ +.heroT4Item.item200{background-position:-288px -288px;} /* Ointment */ +.heroT4Item.item201{background-position:-336px -288px;} /* Scroll */ +.heroT4Item.item202{background-position:-384px -288px;} /* Bucket of Water */ +.heroT4Item.item203{background-position:-432px -288px;} /* Law Tablet */ +.heroT4Item.item204{background-position:-480px -288px;} /* Book of Wisdom */ +.heroT4Item.item205{background-position:-528px -288px;} /* Artwork */ +.heroT4Item.item206{background-position:-576px -288px;} /* Small Bandage */ +.heroT4Item.item207{background-position:-624px -288px;} /* Bandage */ +.heroT4Item.item208{background-position:-672px -288px;} /* Cage */ diff --git a/img/hero/items.png b/img/hero/items.png new file mode 100644 index 0000000000000000000000000000000000000000..36b8b3da46bd9ccbd2e03a1c45335870d73bc64b GIT binary patch literal 180064 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

Cdd{d{7{#{bgR)lIs-H!;5T;sH+MvIb`-(H5J z%LRuR{6xp@W#~ajcxttg$3J+5Og?@?ithh)m1r$oPvH!p zb;HztN)L?kv|2tJycIdXa^Z*zPOf1~TZP&Ndt?&|gT$E0d1{rhB@D33% z-7Zp)v4dZ6rJ$N|oDoJ0l-egM0!s8Tq$wvSx(sF~ghyD0H*ri++B>KaOcHwe8?K_r z-W6uDI!rbp?DUdEqqyvR=Y{KUXJvAw)aRe^PP84S2!A&9sk>Q(-X`SRYA{WO4lVOF zWy*NI-luN-1H-V4I^15vE~E;-cSD$PfK?JKRk=(6g+SH}!iDDAVX;L6yMuJh+!{2T z!96qr*OPvWoi4ba7}11@nHZXtH!G^r0dpr9;w{^9i<0_V+wJbO1$on11s}A^l*UY; zw6o+kL0OTJ&SpmwE=4ivN*v<1_Oq%&!paPW#Ospe;RR}zLm`Yo_RyC9+wqfZHmkhd zdRqs7!Z!z4$5roNyN~hckmLb;u*>a*%K*IzN-b4a?RU=;Y_9?Lot7kIE1$7NGt&Wq z8+(ng60`Tj#2P=TYJe8%CfS9#^@U5PWJ`aK_)V^#c$`;&NrNt?=6sH+>PyqR0Fdb13FQn$9Bfb>cU^SqPvG_U1I|JG>1QG@OM1tm`UY0y! zP4+92hT4#9v7dhqMl-D{c-$)ZT-LYSsQW`A-kd(FH$5$FB_xmvJsh$NGm?Y-K`XU1 zDG$jNjIWm29Pd`0Sh9zVfPFg3->Uszhj6?cX2Q#0D%M~I*&2axiqngO0L{CU;w`-o zzh?V%a(DwjycZKG{1K{3_m%x_WJVx!_C<;dxtCEp91ZtYgxwag=Y3=`MiEwocSMcz zyeX088EgVwC65c8!c4Fh&Pbe~B46NP4a(NP2|wh4wXkUFgsIchT)w4PFIm-#BSTU} zS+cvFETLh`5e*6|0;Y_DlE}5F?qiUn3MCO(MBWCOq60zYgE}zq;OGk-d-&v=eITcf z3A)qv&`pCi@1que+)Zk`J6(ZbgUyxVjo&rt6?AvyS^W1HnL9f-Or!u9m#$CDjEw}P z6DZAV_F4e}8;Z|&q|E{;!-b8FP<=&3EUw52TmA|e>S4Xx^e<;s0l^C=;;*|_mk2LB zv+bl?+ib8QVFMx#r5=#ViiLP$NHg6;fbT=;c>1kjFmm87y=E9vuSs4|6Rr?@V;j_f zEvTx~WA$Cq@DCt)ji5O5U4iPmf`p2`YKdou|6?NHale|iV(;p5IguAWv~+l;tFt@S z)UdGLiBX!!=sLXF)0n$_d>u09B%r6=4GY`KG^JSguDv0>f)0#;G@Gz1ILORpG3G!l9WP?L(2)qLsjz#* zazOSerH zU1kYD4tn>nA)}tSnMgfRt2Vy6%;E+g-(NVNY{eeZ-{0Q^{go`^TR~wNT3-UK!c6a^ z2>SkbE>yrz#QT>s=I^wkeX}~3o`OR^5{G`Mrn6cg?er$xpD(sEX35&|=iUJ(w1=zG z#K?lxAH#;X!N^bSvw{Y9!dA|DCO0uKuV|@crDUym z)#D?qm+CoP?gY%{SHlcv>ql_`H@VZV%ZGLrM)?za2MssH(ohUf^_0&Ud}kJ4a?TEN zPG6*npsBw546&x_-JY$kFmQFW!!)D-)<=C150haX%9juVsSo%Pm{zk|S|(Go^s62A z{8&}Yb#=eQy-pu10%{m%yk5h2*ZC42-TFTN*rwe0#q_Q$mff7t=bpG*p3tznU>>I# zi`&||tUF8@!H4f9a!NE;wzO-ESK4@}D#v>{<7ajwk@pp5{<#7Q&)(wtY^394yyIj< z|E+QMvbL?2u9Q&fDBx&_Fhm4NTvzA_JntMG%9x+*$7UXL#T3^KqjI-jz!;*ZB@=M} zj`Y315dLCm=zl)q1NA!=C0^g_qVEI)(bKeR5N>9x+4{^`;TIhgU?Di)T%AK!F)#=o zY&?|~Rc^>#n>TH6MI&6fBmPQR^OteZTHs#+h5(WS4Lbc<4G(6@8)TGLqd|Yf3dAsV zg$I+=p}q^$l2c@R@Fmf%VLJ8O68fE~35p*KNjC4qG~ec-@CZVSrhczaX2YlaA_&4$qbt(B z&@xqgYbSh&cZAw30F{-@%H;|8SoP=LXlmMxnX7FG&8|iJRlppJNQpRmy;r8Q?>u#N zy`?sG<|eQKJMK`Il4GH0Pn@6AHs34>dNg>TzNKs$o+ObN<}AL%^-a#qJr*AUt@n_^;!3wot+QFjPm3N4i2U;osqT1f5HKgNC{rg`w`ntyy5)4y`@Z@ zH3~f-W7!cCxOPVPR$(Hi(SjX~`Y9BDknxV{-z67{5vV+y@+{qYK$UJnhR zpneN=E;+zOnd3!py{di@ngEx~(LKxtWP3e=<6r(BoiOFOO&OUoyK~)+5L3bfB)VWB zbRn;`B^Fm=p(=!%(z$K5iw>-C$E|hTJ32jdGKu z05?DIr)r{DL4u*+w z3XI`3{9Y$a2Zb`w1NDi*e zjGL{nnT@9_AhH5Imq&{}?*y;&{a>DFDp{6#2CODRk}C8Jb_Vk}*n*Ez`BQ@~i)?T0*JW(<0k!l`Bg6wd^i zKU+k$qWSs3hTxuRa$};=TQxEY#G~SdQc+yM>L@&Mb$r1T<=<5zyTeG{C?(5 zT{b-KdQ$AugKl8f31RXVF+@)RgNxz==7;efrh&1NjZ6fphH8O_ZX8aqpG-$Hir{g{t5eet!x5au)KF>656d zYdUJopIt+UiC@UZiH`Q6+PRO_LO~wCem!yY6`HB4vGrtk1BjS39f=3DBma!MtYZ?I0HwV=@x6VTl?W16nUUM9~pE$R&@qU0&=ln zp*C>x05ip4ha987-)y6r;p5L{jd55}wv5n)oggqZWlF8tT0QIJN+jHkc~SmP=zy1< zJeI|c*rPgJF{koN1en8%BMCO4C@=ZXK|9)-uAVYS_WTdByM^g-(f{-c;rG$UC04tb z7A-LGgVE}2P*n>P*pg7>>PIJMy`4FFq@)CgNp;@g{Hep@Va1hnGZvMMKu=izdzF^) zlNKWw`T#l`Sq2k|9P7vx6B~POk`q;jF>+kfy zuxPghH z>-YdivkZUd=Bk~-Xy#`Ih8YpABvB56etBkrc)6zd`Og`V*T>;{h~$9c64ytYVk#1& zbctEJ#30r~#z@O_4|)Bin8${CR@%KHuRMH{_wce)b08_Y@KJM|$e7)H?5i$P*+fpa zWf@*yJtl<{zp6VcJcWp%2YMw>l`a}=-Sx%A8$zun#-4Fh*i5Z*vLEfG&*bb8utDt) zT)Bnu&bg*`<^Z!c?}rHoVIC}C9Vldll9>s<`a>Yk)9wtALAi!O)YM3>&}p=l{8{3E zo!5|nBa_@?q@O?q&BTMZWJKfQd~;_MIA1ubo;?b#6?%zLFT>xw`V_ti_1xnwKX9NhXBfq z^W?@%kPf>AupbKPyybv|<6N9 z_5SD#c-{u%4QDtqKNowF;RV~Vd$#+dprh7iw&t(SPyfjaIFDq$SZX=*I8vv-UiN6r z-`jlSbpbw^;N5`?WT|k3-}rU=pIV{>7!Ll-)P1MMC&@zxu4JkYPJlJ7x*8)$1QfT^ z-AKIX&czKcW<;~?m<|{od+8RjDTeufI=%DiR z6RkS#|Dtt7hV`@mt;2gi=plUF8xeoRaKp6oF*J~M{^~FUl07{??Rpt=!^DjhZx|~{ zNlVArW90GahX@>53uDAmEfsin&$*Awg*#+dwWp;|L!&?S;CyY_&2hm)vCo&52yJ0- zhxH30tAKy*4l5K;uZJ7n%B0eifS)rX@V8V@&?Tmbg^8^#F*xD)b|>leZ)>O@Zqxh& zeMrxw-1^vN=e5X%R^xeRz*E@dli_MSc2IqZM;)Fo)IoO7`koL;V4nq5Pf!Tl%!z-i zzx+)^CBCxD z>OifS3y_rv!|dv0W;)j(3V(LEal()*D$j-bi#?4qQN9pYr1N{=&~Seq{GcQd)WP5M zb)dcY5_=G+HW}c) z8Zu?0rKg{UfA(pQ7ru}co&3(4z zvd>spVg&;BAO^_}9uJEUT)*cHB$GBay6JtB23O4XNm3AuJfbDlyr51Kg z8X~bkW$5UoyS5;Rq6=uCBdKhV($I4zV#R_n9pV^S9&wK*(ayJ8drB94t`i!Co%hG+ zsPu9I<{)J3mC(QK$W@@x9`r{R5z=5S#vT>n&jtMtpiN?MP_L1=$e?^|Xj0ZQYU7~ga;tT_3I0&1^Zq2E2< zpKu?tVWvQgzDc(FBCiQj*EL_6R5Ln#-6gMqmNc}nbioS?(2uL%HzsEZz>Ku+W#=MK zUqgmTpbZ7QA5r7miM~~h3%AFfK(rWCK7P|D-|7UZI+vPPSJvJPKK>F|?w}j&DG;ts z5u|Zr%0jrfBsMv%=ydhV{^ej!d~xX-M6xj-si-K9r)U`)q4kLe2N0%-0?rL^DRO1d z0w58>X#RgGo7UMU*Rrpk+J}yDYQ4|BKFW&7JD~9EQpl9lDVrjV!|5|2K?hrkMU)L0 z#f0#-@Xg}573L;B5mG5A*JjVfWTg<$r1ziyc@g^X{yZDM@a6|J4VzOZb~Q1$-n1EDW5C#k5T6%w(+& zP}cVy+{AOP5IEGPv~3I)@$ww(6nM6*608JMfiWl+`00~AYjy%PmMenMOC2)^vFt07 z-(;(=sFauchKuL57P@h5IriK)K!a|c9!qF>BRVD0CB>-G^OxSgK6S`PC)N#MkrR)<0bFUpwCY&c*F- ze7)adjz{oPYkR$|sUxW)j``0~M&shj;T@a9xVWiutkhr^aYU2!NgmqrgjkHC8J4rI zWb^Uhe2^}wju!Q~Ad73&9zO)eiMA4hA)?P5&Gw^~ePS$UG5!G+l~xqnK^VL#h0%!d z$rB-y;K`IU$)mQk*2P5JSN;DA`q+VZ3_{{qsqhG+l^A&B-a~7f-omgOR)TC1|-M451tGOEw`INIbq&hpZ>vVI7ycVWHA5hBZWW_&LcT;5HTYB(D=W@a^KfM=Qqy>)G9*tE#Z}Nirh3IMOrO zWw4+}nH#r^%={vKYC$0PFHpgy`X8Bw9*u;S$H>mtYK<(k87 zk=4wGib@m4a?HuqVKojL$@8jeEV`w== zC|TARPojdySWegBYQfgG29E=@MfoyVXxuBF{S?vy2xM|B?7v@uV6Reyf zw#qhJ9C!*oh9xn#cRk8~{wT1=W{zOmZlv^-DZhl{o4O{5A`LZt ziUIN8pf@hbN8~+5^A}78c)(hU7wPgcCoO$Npq0 z3KVy+$FjFv3|i?uO!pmQQ63`fi>j>bk-ko<@08`=y&XG=gU5JMNOO;O^<%f-^Sy1C z&BEQ-8sWUK9|GvPf=R@sY6}B;{w~e1{RM{fZCA!+hNP&`z7|k9B>)5u8+JsA830R% ziyyqt6i9d4`+6~K^Tw!xG&ZVXR+Jnp$HsQVadLdTcyABXS-haQK1n|Oj+;)mCC&mG4j{idVUl>^rSB1o?z4h!G}C&fT(bI&<^)sX$XYvu zRe|sY=CjUb329s6jf~jp^J4Tg5k>^*1W{m84ymVGY{7|+5{*sw398Xnt;XRgu>`n) zYgE^p#uh9=WgW;%q|U>_h&-%KB(``7AHBs)yI;=KoPddu+ThyawNr;ktGqpB-03(h z*XX!p6|mNRM18u_ax`4fc|P$&ZPV za>F=3DgF-lMQniHKW{GEvhC{fG@bE89N=HL`E?A*&f)0rO-Lk;{ypbEr`*oA$qpWo ztkXWCOmZ+QJajj;ybR#8CB~N;3`(mh9DwN?x8o*Eg#l=2P__dD-W7;|*spl>&5Q?0 zJqWrBsm~om-ffFlVpV=7zWl|fHTTKOL;4w_=QeU(CtlFa+giC1KxJ_F-;1+SAAe+( zqtWuH%#hYTh4W&izpCq~<7atZ%$m>L*XLAR6$72mi*5w(z0muz+1P7yJp=G~JF*8gtF^hrk1W|Q(D-O7M?o?!d%mJz%#DZ#D zLJiIapeqJ=ftMnA*|OB?p#9--hyyNY2jT8f;5GiDvKY%QJ54|ElhCz1N?aU8%KUr6 z)83-96XgK|u?~NX@&iYP^m@Yk%9WtIMP1)}hkN6`T$0uM8*>GEKheta8=EHmQFHrs zX69IGpz)}-i!rH4$`LMI?QvU^V5^`cCLI`=S?E>MM^@zCa&JqJ8Q4S)BTo-2Sl3j@ zX+v33CtA`LE_CKzEd$Vxp$c4#ZHUTP>MHsMVNoNxPrD}QpJHc9^h)!b;IsH0&RZCt z@3Y%%2Ino7(as_CCU=@K_=VDMEJHV4NiQY+w-MZ5?~dV)`)Va<8eFY~3kA{h4sN^& zu=m}b!) z38I`Y5yBle`BWXur!wN?Fyw@J1r})$ZbrxP9$O!Vzf=c=vSuiUa@eUkZSefu!U&m1 zL5ZJIz^Ww`B&YI%SV~#kP>${w4nhmFssWR=OFWLQP5@UG5N(|eWcn*USN_WSyQBla z&WrYE!_)mj%iprGI}PNyR!_gV5*m7Bze< zmCcuDVv&XWygLz`0BT)XQK32Omz*)&Mz(l5T7V4kU-*uuyT%YTTe(aUj;!1$iZ|5hM=4rI|cBMJ*ydug5QOhCC1K!#(!`7xd%MJRI=d( zCuFcgrtClpKuXe#6GfaGxTYVYn_dSanmS&#FVHu>@ajBnsFDV*_5?{#bUxM+ZhX|@ z;<%1mC-+q6R*3F#s>SFP^@_wDo`Mcd^MziyujsCnz8H|aX{}zqWjwVnIFw}!u~~|6 zoCqRgL=rOo{pIwR&t+J5rUGBfx#vQ!pw;7G+zC4DyPxWL!`8m*FJ7*+X8V|;>2OPX z!7I3bhK#t)8T3(SYA5|1KMIr)N~mr+MlBD|bc-r$;{kTax*FrxI-dLB=qazC_*sVf z@qivckt$=n(8%cgGao~;zu4A!2Sp)c8juo?9`}1*+q!rsXYOVW4-Xey8M8+IBVn$G zwG2m2Us;oe>Ia-*lP0VhkkB)z^6@XL=xW9lI=(OA{M=U(D}?eOsT1-O?n8;L+notE$E+t(iUtBUerfq$mP-MO#I+bE~zSV@RAM?`&IodHwew~@B03~>oVTv zl;>|Ya+T*{Qr=rH9Cd&HybM*;iTzXH^&gY?P3etCKDkfU`DW9$dq)RDJ<18zdVGi} z%F4>HvZ>!=;+^*qZGLvCt^Iu|LcUdSZW<$E_0Nf`ssM-?h;K!F`-}f$>8hgI>bh-k zcZwB?yIX-G!QClP9E!VJkzhrNODXQ|?iRFof#Sv8-66U8{&9KA6JsQMpR?AQYl`2& zl+i0pa+@9eQ!^P^!rG5j0Ly5-<>@={e7Rmo$oBYF>0rWOR^3>`()FAvf^itZr`Vw+ z2w+ZLVITw3AeerTE_VaS=B^!@nmbn5u=YO`lK zvR$Vz!Eruy;q(FW`oTZXd+BwKn?>lgX16pYNYnQ|#dhw&$mPj%>`}Q1|C*n|#q1%4sE?l(4dCpymu_YZp zKSDhXow`y=q8n{6G|#Xi2KfBbe0mUTb8`_)?xL~(rnpm)3D8D zCN$)Ck=J$1lV@_N!2rVASRwRrpa24aWcOVOz+B-OF`=sqk2UsOB!+G`%Q6N?tuY>^ z?>xwK1Vr90iCmd`!egB>svBcJt4L)7y8XZ`A60?qzE zISp~k+Wx<;;sZ91ttK*VM&Fg@+~se90|)3?*Om1V@?mbcQVc(XQWZkS(Ep3#=qN#k zG<=h$9mxCq-!2VZ$?g%nz);C!U(rp$ZX;TUVV&)suPi|Ohs_Unj- zZ7Z1Z`X}2li(BtzGZ$09mJ8m;v(-3LCPY-YIr+jaE24h-JD;yFQfWr>aY%E74x)XV zNi<9tT1$qK+B;7+E1jgQcI;I~z3ea>kxOAA&pkUO zn2w+KHoMbnEpRww3`d6uDcwxSjf3=Oh!DB$WQZ*{NjO%n9GrRkb#l9-ExaO{!>U9d zCE~P&87j7Tf$igfu89vf`G}aGsp%%1WSk1T`+sN|ocu3C*E8ExX;<#1H+fd_ltz9^#4?_w6lVGEZNIfOCJ~CH4j|}Aba&2i3GSejZoFG4?cO-`G-B4L7UGv;c&F0-Z3F^7q8M@rz+8T_BktW>m1vSqS8^up;;AdC zvm*l~G(J0}Aq&XH3_QUkZcM<-3Rq@Y3FKCZqsR1qy+lgZ%q+Oy0b{WEs|{1gwj1f$ z*;!dlHYObQ{7!_W4VMZQ7Vo0zfm3q);PX;B#yk3)d>2N~!yb%8MT@!uCzRJf;(Ky8 zmpA>dZ`+B;de=TR0DM6r<0M!qyNvIR$2H|jZTB2l&yO3LQ|jH~nnI-FWjqLINk^Eo zB2ghl^)bXX3>k1YwvO1t*C0LkZG|PDG@&GdZ-1oLL_s3I#t41$VpdnbErzZ06s>ti znrancQrtaCrLQS9J?Gsx{%^v-HX9q8-}07x+KZlaNw%2Svan4z%CCY;A2uuBuHY0{ zg4W^B2l!e(?%8IC@#vFWkL3xoiz~1;*-jwuf_BS-G*w_FeX?D$Vgh4}FtL`)E$|KgV%NrC|+CBMcKNMA1w*`I0Xd$_HjEy1j#j{A%g7~T5nN$4OiEa9ZK{Tf@rtsb1%rf*;nYrzxhVn*^KGw*CO z@HfZ1V0AbeTB%6vJMl#<7}dBVRg&i1U&-Oi^PkCs0XyF$#1N78>OU3fOB)y5RfvV3!Pv`h| zSigEmq86Eo^Q1qktV z42~1cNnho_xgVYhQ6!ktz^3kkjjDn>UE>r{9I^25oTyCf%Td1LspfNX7iQ2Ld@dE4 z(T@4DrS^9yiYqN0P)%i+jVL@*w+{WZ3@a}s63VWEoz+#06Q`QI=t9Js7Z}$iP=@| zxT1A#y*zEU>Ly_HtJ|+e+E3jZgr?ZK|9|v1o+~ho9i0Mf0Qq!*ser$+zQXo!=hgqU z*>vGm<~J!na$s(J`sCMyAU(=1=)K9sQp?c+0CR@~1v+pBoqPe1ya}C8i$S{>dE^cX z3rE-)V0^rh{K8_=4t((6{=YPZ;wwy zU)*xlRufV(zPW2E8on}C=9uZS+2q{-<;vPky#^hx#k&lcg1!A%RAR&<@QfA1iyMDbAb>iId`QUnWb$EQ$NAxSQ zCDn>hISbw0pNEX~K_ZpzumjG{=F(4e=@xR*&{_R7H?{s}Ud2&=(4vLZPWs`R;Y}_3 zytPWKRsgWMgqa^0@rD92V@Q4~(ozLtB+5(If2o2;kT71ip`)kU9EcZ#-$VNR>kN?| z)61GBNpp7d++myb-T^M?0ecD$CS`q<3x~pHC!MJ*G3w@$9vOunt!PdrxM6^>>q~C| z(~)QI#B3m*Im1NIYa7t7vF!u5nt`BWdqse*ZFyTqib&K zW7{nX?YWJrF!T`_F@e5s@G>#75i%u_!qXjmEj$4+5z>EULLVDD?hkz*_g1{fD(n+S z)-i>ePdK!LLAj)-W#Ydw*0_&BR9Sbnd{CmOX+a4Hqdl(2dr2L9jabj+jk53$e8#Ey zl6noUjnc?`ff51etIC0vSP~av`Q0_`KpvdyULVY;**2XIW=8 zfln_Xb;3KizY=0%x~~3^Na?y`$;M_EOzL@cNHXsgc6|fxQ_@2r%+h_2{jWn&3ZqpG zT`79ftRd8BDexgwd0qa%vQ6=s-Q~J3)#~q%LQKO;TM2aJe&DCVOZpzwoWR+`oRg5)a_GV+%#2jYb z+1Y%jC&NwkM}qd@+5A6iH2xVobFn>DXD-(0ugt8s=q%ph5vUKn_JS}|vT2cp83`{( z!=3u<8ks*vgx1!UCwzE5%GUkoK0e?6->I1JV+mwn7DIL80I)n2E@W$e$zf*eZ12R? zGb}R9t_l`?U2Ddya?(G0xC=c`8$JI>y zTsWD8Y3z&@g3nI5%zPPu~=GmjKzimpGsymy=2_)wB|z#n+`Y?T?sT4?YTc|3uSx6DwclUj*hk`*HvV_WapKYHqHgeIyYLJB{+I5UYJGD5Jp`f1_Sq%Y&ki`+;gf7>}aF!2sdbnt_*#u9m>Nmy7H&rbN^r>QQ)aHWJuF z;S3-_&i-ird_wv?`OnIXrMG1fEsYXYSV5i*{>QF68jpVS=PT=i1`7Uuf+#)4_qx<#;;|P(8%g?S` zPUzD8CEPPLm%XgJcmBz zmcD#nU+-aj6>~1dk3vMt|5Qv)GAHL?gjEa`h;>e5P|e{JbAFX3N}%~Wc!T;~^6=S@=<>BYpShGc@~@Mj$|% zfcO1l(Jo<6cO!~zc{v>}dN4kG+|JCxXB5>sN!%ew->m7TFM& z9)jM?2Ok-Rtr#PtOD*0hqMPRj<+gZq;Kh)zEld%ZsQf9!RPtWj&;mKO-&eus)B-q8+!PR!#xUsR1(ay zWR#h1OFN6F7HoP5zN)=|Dr5V4CcqNVKYJPHl-sHb4Y8uuW_IOZzQc(ili8W`d9?=x zur~^jSCga)rBa66^Q5}<=Tt^72>|!fckA3P_|$gjgwjb`M!TAMtRjMLAG#K@!{m`I z!Ie&xHt7mB{X^_nIsp4&M6r!WESmMv%wv8xi13WG;YdtC>!y$gxYR!jKG!bhcyHg` z5Ge;2R2(`wNar;Y;L51ypzs1<6rZ=)fG;Kl5_CTYoI!jaIC~bJ?n@22tqHy!&iHN& z!)h(iVhJGVst)+H6wJc_yu!rL7AUY#X5j@bK_Pr&54s z@AQ`2=TM+;HdHBJr5siP*RBYkMSM)YX93Nh>P$JN>KB7F2D=`oK)8oxa`xKD+ zck$q=m5=}TB-VUGY2k2ZYXclC$K42hKrl5uBrYut>Kz&!Yn*q2IajLkf!Ay{?4e3$ zJch|W!2T;_W^WVweIwKLRn*miJGu9lX^0K}4e-^So{oWj-?GT?YwP8@7)agD@i?V( zb;VLmJ~aTGKpyp*2Sthj(Ji-Bt0X_$?FTI#Z+~a4Akep1hERuOSVI*r@(FbC8i5KY z&Ju(h^eh!%Y8N{kKQoaCIQ`pxuHcz8&Co(e%0ukV zuQw%u%+BroeQ?vWJhvvwOC#5G(JzX30~Eo8DTBWcICVRFRLnUYR=^t(N9Idu zeWoj-%bTVcF6nkmutd9XUXO@M?!3CZ*r%NodI$A)d++C^Ev=+9jw-_xDnN3inWz1 zblt|db96CQ+F&2A9uLK}5Yg#*SXf#8neE>5AFatA=c-fdlmDhM^eXo;bk95-t_1EN zr~NSIJ;&Iqii}a7%VdVAje|pWp&jk6l^`NFG%*xR0*hTTJ1;=u^m+cr=u7sGmDa83 zxf53efg3uI4>}f-_}ayLzTZf^?)w~d)130pkoxe3>hxy7ZvSeggvEhnHOg1Xfy4;) zAKxe@EDZsl2>g#-&};S7okC z&Rm3K0YhZRde>bG?5b>Srn2dq-%)%KpdBU61Ws^r1o8P3`D?nZ*M4}~;UzZm!ur(V z_h)uJwxq<$VWW-AZ_%OHxVALY*EUsx4man|fiu%Mo zQ9k*mn^Cc^KZ6L+-G7Ev8CsES^7F&Pp|3A%MxQwvTn(>cP|*z{bfCKsCXk4*2d12! z4b?*Q87H!xhPQ=WbwPDHtMb1ui_>6Z@gkHd9V-u6{75&zw2lfp(XXVinYf|Ebzt}2 z@zMAhqy+U} z-nA&@-0uXc@;-QLd%X5(Ql%C8*m55iXDY~MknQT)MTnW#;9tumN9g#s>~-T}rQ--o zTbni_J2VBC1i9~~tIox4UkXoV#$sr5=QOO=3Cr>5=;q>lW6|aC*w-ch8e9|M?mtYT z93sRyzejfS+WR5-vkEaE3k|m@&ZbP*t5{4rAF*F+^*mkqTO-f#D^%b$f=m*%4eYzm zHAg_y!2rBExbb2v+1u>aE_MjmldP@`z!G&-?7W;~j6Fftnutx2Rha+{;{8fbb|3I1 zHhJCou0$*Z)W6FCiivT|tj*mlc4TCDKMNbmRte+Y0BMkOONq01pitNz{~P!M!$x@y zPEx!a@Dr|ll;#6Oiv6=t46m93fF7}8OkHm^Ja#pcJK!t44u^N-!&=hRv&c$euER`A zm?UIFCo@D1zy;|1Zo>0XSgcXFOo}^l@5^`qM$aO|Bt5E+3)9f! zX}-7f>7H_9?(bi^qYNwWPZUkn#ds(zW2W^hwp(pq1$VaRw6*t8dff$aZ>?q15{N-V zkIKoFYZP$YnDkV(iniGo;lVgqXegy-yqE|v^|Y$y6)Y92i+UqGh6+q1qUcDZXo7W) z?fxbs(~v7 zr#43Nq7-{=b}&!HB5Fu;O^*7wxF7Xs z91_(NRDa)ZK>1*1-A+j`9M!fvA?>if914V%X8*9F+4!cd9~h$5w#tQkvqEVAtJrry zc>F(<{vB{WJx2ow4`{jxq~4)xR>KR|_suCuq9aRJCy`HD4F|ol0H%@PqfbHyUC|$2 zX5MC&|F1Cmh}`M_QU+h!Fsu4zXUOaoL}K`TP0t z>i%U?FtDD|+b}aO7t({k*|SH)A;zjPI%S%y|H+5I#erB8_S!!IA=2f6>W}W7Z?=MK zfS2U_v8tYIu{NeG1Z3JWQ41bDY0w}jF&QAm&9oy9MpH7nF3f#9UO3J;rpo{8^f9yE zcZUp>0nV0G4+b1eaEbH1vrQM`IrKk_;SCi(K2zLqYh)#{T(C- zgrQWyz5ihRO%7kT-|fL2oRC_+0Xi4Y0eMl zMu>{4Y2($6>DR^iDvi#jbU6{_5Fx z)|v2lv1*C0S?>jmazcQ>`N`bCE~&98d67}Wk`h^(;DxO(J{1400$PLeK<4Fq{^OhO zmj>Z*bW4*0v6F5M&t5>s)5A^ybju%4piyWevN*^&JZ^BXXx9&N@e8QM#}E}_IzLH% zf4+jO$i&0bNImWby%eHWr@`BgQL>mjnDX217BB$@<9_weL_Y%Rg5q6VC03^2AbC1# ze;HROh^p%iJUc^W7Z3JCJJ4ZBq}fxs^{;HtBhT*?j_i*%XAI>vE$BnZCDeKNr)%9x zH%;1l>{d7CTahsK{kewAb6NQfNzo*EuF&0qxLuFE28P^%56u0Cc`)ao zxsua-JDGB3vE>4z+t0--1BTO0gAzjF6;>5Wb0L|T&b@CXHF4N~=XLB4a{NB@ew!pa zWo0!V9UWCPP>OEd?+cdW!VhOdj(nngk6QfOh2ENujzPVI7yY)^dOi}fycS2b=9_XN^ zXY1aZK`qx^(NF0+5kAl|>mSv5dAvvk6W%ng(d~Y@JF#>3eM>H({;6sdiSp^$S$zl3 z&E=8Y1=y$?&i3QG17%Oi%#T|IV+(&|a?kC$I0#d75iXZGoWs;F8K(H$7ha7H41JDl zl1*nsy3e{+i4mh7>zbR5N5@t7*3NsZX}2!bTQL7CpOpBh)Li0lT<4I^)Gm()9L8L8 zcR`rUA5T_}w)c1~wt(}%+l8*^>rFV8_()~jok#8Ll4D_f_aAq5Oe2AUrKkTkUcgJ= z%~6tpvaT)yr>?h_UQYc6I_R7B#b^dOo>SKo5hPZ?cpw;IzAmz$-#shfCHxKtAwjnP z4b~8NLBh=d;lV%0iMx`w6vK+jzJe4L`xfVrhAzHKpo%m73#24cf$d|hKG|ht)VJop zyI~;eqdXpbgpYGRSNBn%*}idBU;=J-5(wT)8UOzY!Yv_+3uSTM)!rboF{GxEF&1GE z;R&h37JB0Ki6aPB}*`I94FWyUOzl@{T1=zL0ZZeWFS8qZn=za3`AYUdRUm+U(Q1|?g^ zOMY`B=R=TJYOv)UKK>#Iz8hquSLrn|ne{M{9$`@w`p(@wALKH&C-6_N={km>ILCN& z<=+b2Tu;DKt0(_N7g%h)Ez_&1b>l(XGX8&~a2VK@R_|zP@Bk7hJG5;{T3(S;=-G;d zl>EZq)i{9ZeLTHcmtOCA!{AeCGGxRmygOKgcwzzx9qEQ!8_<9kRlErP-)@cc&{I|L zpdX|54Xh#{R(0~p*k>Q8I5ViQJvOJi-F{#!?iqm4pP!JLlsZ-mwYeJ+T@&pkMFaD~ z&Q=E#=A0}hR^CSS3$5UHh*>&%NKhSNP;KG69|aOIjRTZ1rq0?JRub-Aq83(^mH z^pQ>a+59k?M93{BbeJ2*&ke1;0{S2ZWr0Td)s}XWs>E{R^{RC~Q!+)Yorf{~;7WtT zCRb)4zywqjU`~yeY`Ms%^ExYNST3&{D0w;frOiTiB_qCFl7A)7^rK~+N;+6dUi0bnRyszt`FG>U;Obd=GKBA}4FgJdjaV!4asgsD z=r|-RIXQiZ7jw$yv;3sYz`LES$z`|V<~TCK8QnauU3=8A{_U|hFAXYxO{plBc`S>X zF6e$l$w)L*y6~}hykt{#<{b3?VzFAB?m;%v4M$P&zR#$W4ERN7q`@$x&PxEh>o)P> z13)9w`@?@#O5)4#JK_z{P&2UYqK)8}`N2KKEF|D%aE~#UZ6aHUK<)L-wdu}R94?Zm zkqAxUt9if~dq<5ut;LqZ1e8SL`QgI1iRDr&m#c<2#{iAiC@_fEy89t_SrNzgwBcmD8?*q;Wtb3PH=#8*HCxX{JX zgZKg1e&7>ROh$c&r=h~+R6S|qRsEe7ln_m3E;e2dIqH3AGvswM0#PClMt;yc_I!}6 zTPfQ(kPsvZV`V3(sOYz=?_*oR!wk=3V(};mEhIz)n1`2DWVnGXfsf4vWqxD#ss4z! zIGKS$k)ZOc?y1;HdVR^%Oqh*I+6p z>O)8S;L>WWkUm;TACapSI%PgXIK6O?_6FLU7a z=cnk@tY68=g-DWS%U1OT<@rP*%kYZKOvR|*qsmDAKv_ioB8Xm`zX5Nz*s#jpKR@B7ABCRAraAcL2ma9dwF_4n5=jB|7&ie_h*Ej_Xfm!_K%vA@xhd_pKjv}y$8;4?LZ zMcRKzHH6r+iMi6x)0pMAc8-i?vUlkcZEdrH>UeNbfBV`}Q=e83RlcZ7t(@l4Ea`@3tXRS9 zdw&qfI`<9Eq{w{92vAb7+gMk8NQE4+!qyI%P^uViSR<4p)YA3^Bb*+jZAm{)@srii z<%<3{1wWZ{_Y)DzG1->lwB%mS+So2B@1Qp<@^^kcC+DG%LS*DMVi70?9>tUF^Pt!= z1weAJakVAF@Lsoy01kdEv6oZP&P#;BL-Mf7(Q-(;h;00+AYKGV= z@&CSz$ydT>;ji{_`&41LOK0~XQ9^NZ_*wgT;Pl5a*^5sAt{C%AJRi}yq0W0iL8Qou zj8>0=g7=@fu0AJ8+BiGPD%yP_nF9(hQH4;!rgoQX<<#snSC1^3Q!UbX?>5@gmbQ_BG?(v_T^v;_5yF|sBm{1n6 zTHkW6EXZISUQ;4kR^cS+eh0J8dj&K``&!it?~YkpPXtM z$l;0!lL%q$Ao+;IoQH(AMQUo!&z$5;5TqaWa%B7Nw8-}^qXPW3nTAba(-+%}S=!z0 z=8KY4b$64+30d#70k_Yq)Rm|32=*(?O}l?bj#EF-?y?oe z*sM`}hELuMiF^^*P;YO-z8kaSvQ_L&y?Nfho^%!&uO@Bo_->YWHc^)gd#f(&N@j?} zwik(Fo(1)!WSJI^v7SB%%o*WA3MIz%xD6xx-Hc zYvvmojPx>5}|5Y=amIS6y5Ct5&QI zq+zc#K3uD>Bl;Mms;a8sKFnCu%D2#5ma+b_2Qq1T{k!A$YWCa;gh;pu?r1c)3-_PB zB#6@E6;K83pq_@17W5Ua5`?d=VeMzcC3E&*Rge$ntaaAeKn!;e4$LzotQS1BfbCP5R7>n>0D--y@x@>BJ#Wmt1m|X^AO8PF zCKhC`nc=i60=L?(yWqJDGBV5LD1uf8L#$}t_gZQ_$iz_rs@56Y1&LUx_gN>i$xKwY6fv7P!;d^tmfOSG&IoU#AP%+R0m6?DpJCVOexISC^d$C}Yl0=|8pYY+`FV{+%-f>;z`+RJrf&{8XzXbEomxiV zKbYT>`*=?FDuhSy@2AcEw4TjDIf|?D7&itmX>Qnjt!S`qp>)ut&G|Kr06TWUGGY)7 ztAeyq={s}PcH}5mFTRCEu-$%QVsga2lYq|*cCw9(c>q>JLt{QNyt*d^eW%ygop3BJ zk!BLd8yZoGJXbFa2J1bGTs{n87K7I%&Go0xTEC#Gp+`B+TGs#T$dE#BfLey5g@I2I zBkx1NK^7#+|LPr4`jf(VIoinRgrmP{6 zbWDXq>2Yu3GhpE|L|OjBNAG-*zQ2}*z&EuvOI-VDaCucFn0!Gkor8;*jC)x3C%7Tv z98PoH(J*eo3Ctyq-p^ud=I7Tl33vwHSP0?AQb?IYMwXzfF;+a;q;Lw~!Gwnu*kgE1 z)JOuqUXQenS|i871HKa86)sKrZX0gI%PK z`oWu%HmnXwNzG>~`C&s(Oc13J>M?AM1wTxZlw@Si%i|u$n!!~pK#rbe&|oGSNViXewx`!ZfPJO8e+Y<{wDekB8)^_s>k)R7sS!qcDP4XK zFVAh_-X6;DLj;>xVRfG4I;a1-NZnJ}W*iFA#ooro)ha7m0^0U-U@hJBhLbnP+8V*Z+1@l-=TuyxN1h=P1XZPBwkk$azVY=>u!YmzMU8p8Ao zX){XMS7J$b?G2=?J0SJsANxd^F>rMNmzU@Ang#jr2ti3JvE3AcG;URc!}|od1uY}F z-x@bTxN$ppmoHR5XeJ6!Yp0`o){TSRvUorApgSTJ`HL6X^VT89)5H+LQH{Z)=f6Qx*pjaUzrO-1kWXIo)fvx#<{En`QN<}0R(bzoL9GOfpJ?T1*k%X8stadWJ$#?R z?h{aZAvVwlz;FyFBnpffbUW=99j!J9qhSvCmfZ;uGVxV|;BLU~byx%9T!(V1P8@U! zA=4+{r&l9(Q$B<3nr~RA+aG7R&7%W5{YLiXj!*0;=RW_;kkO#j5`;`eX_ahbrluoh zuEnX#7r0)bu~AcVuO8fee*Y%Q@v)?wIJ@K8!*X@uO!8=bEdinX&nb%sGE+sII1@a9 zS|TB1KXF~qF8lX278;s}K35SwZ5cN^%NeyPYURJaEBY*Li?23&_YX>^{4hp5P<{u{xH>!JG)~6S^L)IG-%xmOl~z5;JDXro%_*o zpG{qy+e6A{>j_H5%uK3l0%8|2=ENLMIP}yCRb%-P|2mf zSZb5-xheid0*<{wLmkK zofyH&S%?!6N~RNVJXCs;`BCKcS8EmHbZ*$3YF2REik(IKcD?B}iGN!&VIhxET^f?1 z=QaH4(nb(7Qo8(}%Cgzsgv1q%6 zI)_Lw!!(UyaPMi@mzIyCfxx&AohNSr!UeI`bl#;$bM&6i=A2{GGcVIStTtg!wfUfi zxcsJ`45joiaiaPU>K(J53xsW}MqCplsmm%ts!qv0ygUlp%drEx*OD6i!$%fd1arv1!3IzfIdjcxoAdydG^%%yqYIeKg zb%{?uzUR@>bQy7M1zUKVE?DmosQqDEc| zx2CVUTxLj@6H6gNGF#*y^Fgi5$YnwM+J9!5^BX6lGcrEV(`7ct@X-tk$pnIaQet@$ zrRi~45ywl0kH>W^IHi!=^s(A|nH?2fwV4jV`-I}Jb~W;q1+PLL78i1`P2~~({D*Y+ zh6@6A;2eaJM@kB3vnX@81zr%D3&tizlj=P5OIkYD4+4Def|bd^sx1*z|6Qy^APyj8 zS(#+cEU#D2n``w46>4rk!d)*lDfMon;V44x0ZM2YI5!;;uEXok6RHFgiMW*)c99DS zBnT(p6ac$^Ft`SXZTCAaCNe2NmU$kr5tRfvBp5L5nx#$14r6;23?YwYCY&l>JpG%b zVkRy0S=&t6ZE}#0y+eKOmLH3bFvZ7Bc=pSc0f$YA;WV|4!Z=B|sL%Di{*)70aKo4u zu^fmaSFi*PE)v1y$bjP2nKk`rW>U;iH0+cGFZ1C?%w0VivyqM}Ds?M6;)p7s$o!<@~gEpWV6ToLk#5njx zgWUUA2-prK?tQV8PA$T+U(}~<2s?7$H{a;d^k`|#FNOdRlS%b_zsCrqlT1e~%u>8J zHhzJe5HtT6lTUH`U$X+@Vm}KT)#B)alw|(+=H~}9AhxJp>ZLpZ8bvow3**dLPe{wj zG2(CUB%~*W*w?m`G02gZC})Kk344VQu$7h94EOJ-{Xv!c=}B32Y*Xc&#!Vhu3+t>N zahkN0O`kgp8`5v%dw-#2XQNNh;1D>=8fKwQ34VBLCnDS*GCO}Imx#CN4<-{BW9`bA z8@^#=L#Q}rt!t@5J~w8hQyADjdP>ts!{{`U{-&R$SzoR4XyLrG`m=fR7giLkE0TT$ zA)o<<;g-H(#!c>75BCO)-QE(Qnz=a|s83A0$ul$#J&(rC8o=2|aRo5^cZ2F-H^^Y2 zw%u`0kJwle^?M2L62<-WJ`~5|Kx4v60~axvr2?IvCiIAmX-`~8b>G(*Ag-;AM2{E- zSn2D4i`$`6qDF-U5F}E#cri$OuZN?=U_Ky$cxu4KHl?~Yg zskl>#%X^rP@SR#;i;-V_;0}XwHaCh@aKscUOf0k1YK=3i{2^wvPO9mPDNnjfUj2JS zEhQh`LaX*9iD+UG{+Q#tK zk~BQ-*J*l$wraY>mCKEgmg@J55Cv;4RO(QzvhdjdP>}9`0Hb_y@y0JzuO)XUo98#1 z5#H;;gGF?(HkyW+`@@mKXxsA`Yrd-p12J(ZH^r0%u~(HzM1ewb;-R7$eWrlk!^N6k zV@1W5S#tanr4}@JlRzgC^0Ib7#FooNub~k_R$CE7$NYz%s({7D#>;#sPRze+I5rIN z-N)!i+qv1g>s=4yAD4%xn35WROs4$CQY0gG)v9%!xH`N3{(s7^ckS_qP5qdT!;hyk zKbzSZ6!Wl$a+v=Ij~mSZ*iiEFfFrN!Ql*4+J?$2f$LPpXafhERM4I%H<@A8d%d;~F z$s4D&tF#>E6VMYO7FhGtb>8DbNAtk>YWazci1k&B6UL1vK!giZ&u=cq`#-wADlDq^ z3wP*-kw!Ws1Ox=6Q@R@gDe3Nxp+Qngx=R{Cx}}ls?(S}8&i?<-xjmQM4EuS$z1O$a zyWTKs^=MmPTpJGmD^|?>hA?I0BSADXKhOptRn2fMCa2TCkA&}k@^R&hz1i=}`M|W#rLI{ec=SJ_56K6>vP&kc7hw*j98bD$cJg+P&7LLrhX3M&KTP^W=r4 zVm;_AQ@+-SFT+N9c~yuw?}$MS zYWu4G;MkbB-J@`!g;{>?FBz*+hpuI|PUp{t4(IAQlB@u7-zGwhA0`&*&}x4(99pt3 zN8kA^^xx9@J1LL~JaLyo`f3h~%H+iIySy^~h_NcFwddS7WpeCp>Es2qo`VTtxuGbd z?mdL#Dh7Qjx@LU|rFaja+iG;JeJva9G0zNMZEFd6Usj0!2Tsn{5O?u|K7U&GotQUo zWXlqwKTE-r>Kip-H`S;8=9Uu9nv+#h?}n6NV4%-|72upS#A*UH{Koj|1p;!HkYD`w z9Q(yIxxZAnySlksE;cK5G?WEZ>Rorg7PWdnX*cfgeJ_tS0RURqX)38yd7p?-@$}4E zyPrt;&670NAEru0*ytBbIAwZ@!bL# z)%u6xrv(YP=))g{>MGG<#jqZl+)5rq?I41( zext^8jsiwJGfh^NDW$)|b!>g>uqWw1+&tfglS+Rvu73Ys_tWS+R`Ev>Re~Dlb0caB zJeXCBYc}ck+1TFte+7$Y)wXLNW6|Xby$*6)!a&*|%`C-!p#I5Wfy#5G{fq00Eb7DS zLTd@4W|4qVoU-`bFAj9f&nGtJJsIP|*?8#4dSN~tzW-YV1)qU(t#pT<&#V6O?dODB zO&8OEQF9>-?gCQ@2~Mx|A_2w?Mq`ZEBeE@ZUEgDi0nqv2!Th3ha;duRuII|CG`lt^-6 zfL26WRMHG3{mAFHmkT0xE6Ftd0+lr443|tQ=WKwO_3!#(@(ek(H%WZ-=`!moX4&8W z2NQuWKzn(Rj>jJ#K5=6r5OPAc{$75kx zVV#qmMC=1%s`!@a=pQGht2vREa<1?~a2eCf{OQGH7FL&4w=SEaK8xk_WByfF3MjKN zGDcF$AZdr9epSNLu=yAi2^XFg%*=|AtvM5^V&3W;u}nO;misfrR-J*-3SUxURb}jT$&T|L>a|OfY&k zov5}(b6vC4O8sSU1_@{3xGwa6^`0^aYLn>OKw_W-aIY4L^e>}0**c)_yIHK^I#cWx zYqWs(_sgFX#GBijZ|7^P3x>5MXhWrgWI|eVZuFnDlV9s?_tV$0|DOmw<=;1AWO093 zIkR@Q|L%wmXK8CA5fvg~6q40<*@td|l;EY4RAC>ArSv1}NSZbZoU;2AV<2aQ1a;Eg zc9G_E>30F|+n5@F8FxwPil@-PjJK6`^UygwF9WTNaeyrvpg*Xp;)zZ2VnxnL ztUpkR3V6d~fVe0|p`ZV|^pKc9O7IsnVdj@dVk$Nb|2nF8r#b4Hcn!WYCN<2oJPy?* zZs3sMGJGF{w5vb;EB-W&?7Z_oG%_6kqN6^61tQ1C6E6%U0f3xe;khj?-Tv%xK z*Q${QSa}rrc+h?r3N4(m4FIF&BgH$4UU;qO`l=npoMl%*4qGVx`%ug6F?-l^kC8!+ zRwQW{=nSKJ`&b#5OU%`ripgHtoLxYYx@<{7K|8{(nHl4HI|15|)!^W0N*CAh9QXOE zdac_kE$=}(#fSeE0v|)b2ho1E2XFB;i(1Bv8X{yI`JGm0wS&KFDP#&F#nfv$@@Cd7 zWi9+M=O!fc5F~TD)RmU!>^w}Ih45e&2<^c-w?!7<@)+aK?v%dxt${!4!~gvV2s=c% zvn~)pd9UY&+1o1XWK#0s@MYmfU@E@Fz&0}G{*c*nB zhPbz`2d`lNt}NWo>@J<49d zsl{v|v-IMjBFW!zj{~*1;CnRtv*75A|E68SzQ9kDSthU~Iv0VB%O=C7(%4%_q>cAaJYa!SGi$r28M*&1N_r za?#7qg}-M}IG&l^c(Rj7w3e2W!d{fuAho1jZ^%wc2HQ-}O07Mh$*kE@02svu4K26? zJ`J=0K9*L1jvvdZGw*kCEr+KIRp!#&!j)Mpa>0(sT7b)|F(P8jV9s2Esf6?3@^Xt6KDmlg$Ue`#+s)%5d(3-g zCaECr8|_kS>+kkLl<#Qp=1PkF(ciI6%`Q^;C&Mm7Y1Uvf;p;Dzc=Lt{(X!ht5BL9s zz=-*RkIOgmlu*6p+R@e|G*BDR%oU3~X}<^FH27bFk=wom4W&p!W*GepMaP8sFE`yJ z@QK6}u=#HqT20B?&}o*lDtn0IdSF!!mzF1#>SWlEq zf*>k1g3!&5N%!tu$yc`?yVcdzEN3PA4qj_lVc^o!)y1(6r-8}spZ@71L^W_Vu+uwWWI9c z(qy9JRMm7#|F|t4oz06Qfzfk(7FcB!p{x`t3(jMFk0=(fyc%;#*%{_p1ov18VDN4C1-q^MC{&+0#|*9}XDfBTmPBiSn>7bm^Ztna9>4QAx2Gkr>sfkyPSe$sqVI zobMZLyUNW<@;t|y4=imY%)I!@hTW;Chmf?jvj3{SKUr;ncEsegT^~bB;gMsz!RL@S z|7&@*Ie7IYz#*y_KUw@u0T;^so4Z@9C_>hab>CYzfR+Ocvv`rkVt<1tt7W%>NPf-df)h41e7KjR$fUcd#A+sy&{AoeFbanIQ8@j1835%%avTGj{c zZl{?oaA?_eO7t+cT$%e6Nwe?gN*re8e_|?=$q;B={CaK4E6?m``#rhr1=)36w#YiA zD?&@vZ)u1dY{iI51xxfDZyO6qm~ntgWJkvuuoG{173AZ4w1~F)NUd@6?2G#3yTK^w z?2OKP*14}j(Y8tqVo} zF0kM|VmG$VBsf8n_r(0+g?i=>r-I-s9cmD$WH#DVo8P;Ie^bGgdPJ~>Kf$P8pabmt z7Q~w^z4z<)uZ4E8A3fCAFvSrOYv}8^Pez|i^TEqusg`t1v8$a17hYbDL6&BG>>*|h8v((jtr-p7{dQ4inyXV7P4aT z&S8h+!fh{Q%##aKkyo#~FV&Do=8(_SnQoKsK)Y1v<|?s&&#!4VTue5v@baw6_70En z+#OccDodAYP{ui6zaB_YV2?T;#6_s#FAOTy7p$X-k+dJ)l$D7le znXUxFUgusK=l)i@Wj03)4$M)9C)?~_05(LoJaAurSsejs2si@b->2>E1f8T>AIu@2ht}WH-h4K{&a?JPi#d{I6#0gvb*!$5`2|c@+g4iBoG>>Gl`&pZ92; z5!Dz2N;%CL~P#!xv{ivj~H#YAWqVo?*IFLDGOYuehbAyNyDw@9+(e8h& zm}!M2Tc!Hu+0ianNArx?2+vX>1BpJUQiXL3uF1ZBAaEUM%ySi(k3audsgF>WwP+g} zdE(craA>>8nat7`%7Y$zdlK;jgvx!`ZY?Na(Y8T3jpKA+y!~b>oM+XyPOa7>PRr0R zcqa+}tH(dq!I9yxGHU?!#0N#K%h`;{L`^R{=>JBTvMf;--#;3kwfcHn>E zbaoE0N4pVk0kRYdIoDKWUKg<{=CN;TuHqp^qQRnG3OT9WxD`&rv2Wwtq^+)r*=J#1 zG30~*bDsSx)G#8qlP34Mp`RTSVLOt6EM;f-Yn#nJ_Kb$+MOhgQF}gK;zZK@e7u_-@yuO7+ zkUcb8e><)_8dQj9u-IfwtGmPx_xH0V!C)<>923nOq)7WNhEhvFWCBXqVv&ii3vfn+ zOEX?yYyVa8tM>*lkJOnDEB?TCGNi+v>OWy6qRoYg&_>1XV2EQ=QT?8ygPtF7FzJMC z?5PpjB!~q)Gd&>wVu1^K7nHSZefe!ylDKojdJw90!dfZObEfSC-5E_y8mG<&$CMgF zdw8-(56@04h5Nm-aAf~Cw4-o3+8-b8XX-;wqS&C;&^E8D=Nw-apg&V)jJs?2#Ji36 zBQXV_@!zY(7Qj!tZFY4C{r$68_ZS_GIb>e7Y#0jCmPC-~nCL{4G&u)VD; zgyV$vVRs$khi}Y;DDKrez$eCwcXUL$aouFRMT}0*DlT_fuOh?WaCXqOo_#3pk%O*@ zp!FecV5`~5bK|$xO%wNump<~G9mbbRt@p35c`MK4pElvVHQ>ZXAgM3!K%dv9IUL(_ zYE7-@q>&Dl7On9R^?9hmN$8^$P57!LPGj6+w)$5qAKCvb74T|~z`&R(B9IHi*duD5 zi&fb~^A|-NE~LB%$sC0V7412k zrzLO*>HSpck~=$l-TQ8|+(Pn<%XPs&&lVra91kia3^lRk07I2^ZA1M_b8WqzB7o>w z=O@r;X{T7tXi^N5+Sh`B{$<$ioXtu;k>o z-(Nj%N7-(;v_GuW7U6DV%wNrklFHrBebZdkO!ZyCx^SQUcHAIj$>u|@%~mSugdM(G z8Af;|=j*b2SK>?I@y`(@=fAT{3mn7TA1bN*^*DM?;pCJ%HDDuBk8aLM%n8OsGQvd! zfM**fE320VtYf`&Ya0U$R6JmJyF>ym;sf*wSXd-mg3T(-#Tq`xaO>c;P9?en}75NHHBqOKBmKIq~F~4;&vH zGY{lspY+;kmrzs34n)hkFI130pEQWczn&07xJo{z1-D#A6aw$8uu;K9#L1aAQ z$pGZ47|`$!(=_hJn{6D}Y%%2sZ$)Snz$TxE@G{sH{9S1~IPc zI~)4Am^>z>xm-ulE0~3Vy)T48ui-~HBW)Bs#p9V6YrQ=bL~NwUc!u70lswGw5xz~$ zsVLWZVelO|hQ2R(>gKF`x8p7;RfG4>e!5@KukM;m0T?lM+lUx5y&rte-fg=FSWvtt zrinxr=bLGC6 z%S93)OZuh+V%=7|6UB_#|rhW{>WE?s^_yuRmY*L-MfKh!J~rotj(Cu zjqO+^R^@lS*ZMcb|E7P9VMdPBl6+oSwp=R~| zUu`pF9PXMa4i+%v6XszZy4(JpHg=3YEh!V3w^%`MFp0)ONU=zQW-H~?a5aVU+>6u5 zP4H2_obQu6(2QIGeEug|;~HY9XpQNt5^Ql-G5w2Xhu(>`!~f`hPcE>2VKUHQ^7B82 zv$fx54qjH@`>*MdeLJv?*o!P()DdcHU4A6|40PoEi;J7M(C!8aVd&fxUiy91G6NGf#~a@yhu5zz6HRYLAkR(1x9$lsbeq)98^O#`P3p{_@0e< zmT)A8d0b>)_2r<{v*N7nEgs=3NQk_Cq1KJ9<-Nf`pUW}F?}^xmcueb2wX3O8H&};| z{F7kdnPp+d|Cryf4d*V7207Z^wr;R*$8kJg!u!f9s(wgCYiPvo1;snp^*pf-f43-aW_qZ z9Y<7t1c^y_#pE|XgN#~`%SVyN=tQ~H7u4thm~N9HwsA5Xy0Rux`*uoIa4xdXW$SXe zv$1<~qn>$8UXlvD$d~kN{-15gm$2@}t*&L9v8E8dkf}!#7u*~XH+QY=$ivck#c`KG zM2+KQd|{zJ_#O~$Vr4FWBG0K6uAz>ee(tgEsG~>Y|Locz2T~wp4&rkABcFnn@0@Rz zP=aUKF-PJ*Vv>h|A6`2gKl`mt&HPbNHrJ}!lN--1!-X5i0Aad%xKXh|Sm>3~0#(>i z!&gac9jsiTr#Lul903=TXT7`=&*hDuX0u_JHp<_+nf7yR>I7b7t60`pzHCHC5iMa} zRC(4Fa+0r6)uk)qBb%_J{t!07MDJG&pbJ%1Z^kj7j68Yy28LH5&PBNu24Y6Pu%tJZ zd|tUb^ts>EJE;V?P{Oe8W6SX|atP~c{cE?Wc36F02OP_`ZgiRp{F2^rjhzje_g!aL z6cJZ}Cs%llcpi$Tta{K~mf4zsxbXc`pki5g*8kPa5%(3geK&OM4zQkEd{AxZ=L^W0z-1GNXNpm z0%MNlCu(kc6)B?=jvMp zgb`JEGVf&o;_)NSB5GInjYr2(dN&tV_!lZM)IfYp8G5|+$+;;|taG#)g+UxCaK#Oh zauTXrj7hTN2MyW&M#7d3p<^mXshIYqukIHlAMw69Jo4V87h;*QCCrRa@_R(?;6Z`8ml|$CNup1I~~x?4#7W>w)-{GdDw2XGSh8+ z@?R@v!tFK}W%ykM&pks}(9Zpke@@ET%Ad>9z2ahNSA8CAH#SM8hv-Kf_JknF#&sdO z$pE2}yuzrl{;HqM_+S9bKS%y7)bQYkomC>^-(NV~w_I7iI+5&UIU;8ckvP&7*PhMM zpC&_I*8Fo_GpKaQhy?G>JBsqVRLd7pp~poOO$myY$68K(UQATT2R#rM<`3H5+7NEC zS3iR5xLDW*)kD|pNKsL7X8cc6AIHt!KONVEF=f0?-b{rhmU&DYb>sHc}HbR*BRcwilHPxTC(6WY+lv+u~adO3F2XSTD>*Li^kyQB3r&xr88)GCjp(-q#1 zD(CU9a*Wi7BYQGUq#@a7?)TXmxbc<->DTQ+uC1xrMq#*U@9l0;9$qdJE%nq{S@Kam zlo^s_P^(5JH$35xr%~>-CXDL^lH2u!^8kR65G>aNSO@{KqV%MU#AJ1Cefv@7!TEk= zoxha71Or;q5q>Q-k}u{1^ah0Qm!FC!F%|zXh~M9$Dz+Cl^v`v0ckSDdg=PHr1#gTA z-j6g=LQl_?YSJ}JD+`vNRxr-D`fbLgl#~b~8KRA!Z-h%HB(b zp#l?;V~A;%%jRy1{fE*Y&Qfu;-;7!+%H9+681qXJDFyYUMaW2mF{Z0_Sdfko0&98B zrS#*6*x0*7uB<<*!~AF*_pi4)kRgP|0Y!+1n~D!4CPl>9m<%2+E2~3L_GREY`Q7QN zp8(1@`8CY`i4>U%kVg_GX7kYZT&@&Oey`#!$LM9p55^_@>6X3y%TguMvm~@{V3Dh2 zw1Ur;aPs^#aCYp^j%&u8{_^q5y}VC@OhOyl=ghN@|7$c&ra%w$$ZXb<@sCL}T)pq! zeS3M31xqsHD}I(iA42>7UA$}mH|z)opqATD^&&$3I5zZ1!5Jtp+>7UN3D^NDLcr3e zxP=AsHe1*9SC2!b1jhHm3)|{Cec7xt$=OJ%C+iYnY7}Gx!Eh69Sf5&_*O%80R_Y!L zIN@fQrtu#N;%=Zd$|Xxq0Sr z^ytHaJ!p};gmc=F^wnoetaSz1J^{SfGRXbsHcn0v9tg`eQ<2~5DKWSITUSM->3ZnK zbQ<`?&cU_g^@dVAv}e;Jyr7Y35Ap!jX)c(CozKTLuzhiV@7>b6=;P2%4}`D-#B21$ zf~)wh%XccDZJsk*LTu^V!mR2 zmi%uGPP%Ik62}KlXhDQplZ%8BJ|3Vmd;JUnzL&IYpXXK5VKGwZ9xuv#||f@1K3x zUUpYSyL{+De!Rng_TCo;s1HVXt#;+KTAl-DCMWHQBr%wo7c1QAx!-T*Ql7FZikrK~ z3UTsK#&>YI9?vE49Ftz`1&rmfhR6S}2Bxs+(V6Y;S&j?CuptAZ`$1rY)iqRapO)lk z%#@mf!iK}`I3A)rNKdN&`Wt+}1w{ITDOEnP;n$Z|JY`|cg@$US40b1Ye%MFgyLdLI&4#yi%h*@fPr|s^-C=JHUJlr`C zPjQ|vCi^ESvY0ZyLDyMf-5b;9n`~b{a-4?#;bpGH*xJEU8hyh+(1&s7iCvSb(%KUq z_hf2b7p2>>N`F#)?K7XG@%8K4MB~zg$hu1an2PEaCA(+R2GG36Lb#+sH~)Y8(~HNK zjW-c34$onn1WX&_Y}qln)kNlEq6U*(4^J(s9VS?xd~cRwYUzXmO={#qG?e~)$j#p9!wwu4RqfLFe0gUgS-Z5CaG>SK*3``#k>CcH{3@TR_|>ydJA&Rh14k_AOp@ho+zWv{Chxs z+oqi52D!}=9Q$UU5qbMOXJ0(GWy#Uu(uEuWhC~XKrQIT+05GnlgB9QM@nt5-#SOHZ zu<`V6HA6(4cV~TVO6A!BNXilLC+C3q58s{rv0wkwUG}R*Q&n?@oUt$aG=u0{?k1J2 zaeaj?Y}@c*%EH6+1|br7Z;jJUor-#+@JbZGjSlNEY10_g|8U=z>)AV4Swo|Y*|CV5 z+sA-5VMugC#$r_G;^{>4-|vDyQ^GEHWG75aVt2oM3tQACmzJ>qrcHinCe0#e{Q++f zoyPuorP(Tm3^mN~xmXIv1ld$)>37M;ZRukV_c7_k)~Z_1TEN4sW3E+iUrl}B4la_dozqL*^Q>hM|9M(r`Beb-Beh@7p~HN? zKMKb;G8^RVP>lT6i=8!`MV(0$CjV7kE+7ARl(??c`Se zn^o0OFZYwf>ZG7_H+%ubjc5r>G>Gh7DQ| z{rKMl%}vhaPY$rA{;0FR*bgN^ll*5MV>k;-J81G-e=IxZan~P+a2laT5{` z(@5#j_s5F?kcLiSV~N4*VO-UxlTZIvL0g?$b%h=iDRwYs4 z##P}#F6f+;tb+<;NZ&AmJ6XfM&9UY#-}vX~viyv0Q|0scp1&9xTr3;Av%f8~_ivW4 zy#E|~U?aT*)D$R^n5DGu&`(GpDWS>*q9ZSG>F53F*yi|@j$;tpc6mh`HfrwUc(EV; z#H|Jd9KQoWFR!1g)!SE>5ky*H-oe|J^x&-U&;J2p11WFmW3N9nJ)}c~UW1(GyoiYV z5=*KIlG3HF5CA3gjfxy^9peZB`*~`%CGkU9i(Gx=Pv(8ag`AYEbOk230VhLC*FN5l z$kppGZ1K(qgeb{OEv|-*Hs%jsOV?h4F=F(pTL+tVR@9>t<1(rVqsRDqXd)9up3eX- zmIHebr5#5Y5e@1~kIUY;_5cAvC}-Wrn2-?9?G8gh%a+D##PDT0R`-oGUdSpP~4y!92A&K$HRS;AizDPWk*8&hosqXN?%Ufemb{ zB%_&MCsmCGUM!Qx*P9QCRelDlv&?AJ{f?vU{{dPVyq;BFy58PnuQ-Al>FGb;!Um)v z`^s{%Zf8pkR$_WK$9wVVLhtqNaVp1aDoHzGITUKyjHHqEl07Lb03uFFPeNC8xctY3hCYv;^Xf? zr;{^95!VV^{ovG1O#ZnjR+2klnid{Cr=K(Im=>+KTb|wYxtJ@aTQuqz5lAvdj$jTm z88gu!z8Vap_63%Cx7`o@RLsf}DnW?YeHO<2s6Y2to$wtrgF}LU8Q#~dbwX`@uNhu} zG-1`xgbXheG(s3<_T8BHpQ#R%iMrQ>xc^P{HLm1!5zouTorPBWZP?uEY`ZY0fKS=1 zdELKSmfm-C`Sqo5f~cs#nD&ky9w`#|yB~Z9<``lT!&vbKy5w_|(h_n=;(Zwx^%~&< zaB{s$vf65|wlLUsOLMpTh_v&noM7f2+SA!{V`t4FKTu7O)88(SEwUv$7a|kTJ3Lo7TQwTVmj#`3JED8(1rgHIDv76Q6$$d#YVRENaj_Ir`E!-X}9?c3cej(|q z3|<0_HGJtEijTOWFtL5M5aEU%r~QK4;EO#1WXwfZ&j|Ji7$pP-Jv$Ti?tqZw@}E!@ zqTV-l;&iB()$5CJ=LxrysTg3Jf2E{ZizzM#ERdF~tT1A+`IR4J`9}*L0R$?)$Yuz$w;+5Yt^vwnb@T#yus{mx4Iz1&NId@dW5OUn0B7_ zUBv5z@Eh5xe2X~BIl^xwxBIDB{&_yudsLqvoh%j{K4E@ZV0KNFpc#&xno{eWJ#+Og zFFVJroU5aabcWbz&JsK#A@T5$#U73-j$*kGjTja(4(HcXLS8>HSWGMRcZ~++~?Vcht9GZ7e)s^(W@8|eQLGL?Z z??y6(!rxLs@7KLJZRldOFoA`KGb(kaF)@Q#ar0URD1Yz>Sju1v%11uvXklP^oW#3f*7?62mB1AN%Z7UeBi!{!-G7P5Y=~ixWH5M`` zF%ClxuR;gfe`>Sy3ymg+4E2Tn4>!z3od4?bZ#>)kP@fZI#+ZrhZ`f;IPX@V?E$RGY zv{@lh%@82+`WQFgDwNF-yK6bqJv|i59tHwAl4*0<=w3oetN1CA3S3MrGSw&CvbvnoQ`CYCSa*#fMZ1GtYv9Ui^6JD*^zgK67d%R30%7UxYnP4I0bgrxW zk(oqwC5W?XG{HrVY*Mq#;{jukA>*4gRy_7)H#Fb6WS|C%ZPyqsu*&v?Eh@XGHS(sg{zj}XtxB*6LDa%MkH z8)obYx&idkcLNSE z-FMsV>8QLqB6OR9QK#8!t4>+?QXZsAS;~tKz&-=)nD%#>Xy^J~yE?<^#flZ&Q#4J< z5k|FW5zJs}^F^xrA>Ulhtw&G1%s#Z4c@h;kDh2^J_)RQVkK0jX6&$0l_DkDo@vjN3Ty1JAqFZJvCVu)tK4palBZ z(M18fCkeYN8e>0u_qnF+pC9mRot=$7Mb#|W9kOS1a}{%FPHQM?yN;DAr{x@6n}DZGvM!j z9<#I~yNxf$%WQ%f0#1)VgQrGRXRtXqI_o=|LZ6wN8BQ;wqUAOqPTs}ciGXfbM4QeX z-&r>=9{$ZtQ#Zy{Fp8$mFJpg-yIpLGFeV0^762v}NB=-y-)|oWSRj|r+>9RO^D?Ki zmDg-LtsA-_7gbW15%QRz@b);QmBgZ6)18)jepvk|#%8a%luH~=t1js znDs2yS3C>yPNSz|`TcQmKXp#wrJ7?=8=rYMP(v<+&bhFL3*veP*f;yUZSi^9DgOD1 zgBUfhuAJ0818Y&|a3&z{bV>gwa*w6RR3aBQR~wh{Rnm~`&`{j%-OY-%<=Yr}nFmDx zE>_Zp*lccA?**2Gx;p(mo%Ojjl zQLn}Gi39}Q!w3z18)sX2Q+lC_5cXaSO`dAwpiQUmJBTHCfIThrN0~JjM>k?rUy{Jr zGhiNih8V<-Z;{$>Zkhcv^g11l)pKaR<%%tsT0U>1^>5iz?>6^%KW#DE{WIox&EC%1 zH$sj7n?VZ)EYE4f^9<#BpU{VAkn|1sg@&@&bt;-GIwKWPJ0IyFD&PotM2BXl82mzD z5I}_u+S;})Z?Ii^rU}po;d1tuXpD7QQO>G`OGl~P#q^<^4TFMM^t_9zW|8Z4w- zb7^IFpVAnjNTI^WCul*Yst783UyX?H%|p7i?sq=jqX=93wLB=EdJx?-UC{Vt%wyft zQC^*)z1q`a`tVJgMbT01T0CNsBVh?fr5;jgGs$5Mf3;{z$ zQM+>yWzna9&@HF1WiSXq4`8c_BK*%pfH=AK6IK_peL#c#yZyEtPpwxs?#9OAU}Hnc zRJ(;Y{Y^~nsiSS*IOzV@cm6QGsg{1Gq*KCIj1zfD)H8rwUWO-^vk5Cc-L{MTSbG{k z*!FkbmOQS^Z^G;PA?Ol|D~GC+nUVN-n4FSDE4x{vRa>HPa_kli4ti<(T3ATeWnMR| z&OmUPzv#>CxRd5h6F->PE~M}4Z@lRXVg;7-<7Rt1Bk(^qZ@s;=b(fy03Y$yHT@)`1 zWt%}t#Fq8zHA6$02bBnQHIW5_A0moSY6|Iox}E?8Z`3XIGg9@t}>#7 z1JRcHN)q0P(HpEpUdYfegjz^F?l0D_*;EKakSbkgj95}8wX-YTnH!Es@(mTi*G&P2Iy7zaKAx6 z2OAT6WO+c*;tC0S#b(m_iK&!!baL4c4Yfnu=O5xZS)#1KTL%BV^} zj|#7Vs0ENf-IvJQ3;-f7PeAriJS-QIT7VhH8k-)PEG&etD~pXo}ajyj-O0|wN>Tc`I7vK(0>+6;m|*2 zw)>vrIz%!zWt+mnrc3BBN@gb2_n{HURb;flw-NCiPHdXocM(x$Vi%|Q{yDJDNJDFP zyYo@?>83Q%G>46h%c58r^)`puo}4 zrdarM6M{#0veL4vkRwvVH@Vvq0xI$FaJL^G{gt9l89++A9Dwi|q5 z{o8J(h@y^$+2Dft9Hf*&(nQz{H0evh19voye5f$$5iq09Ou~NU^wMq(uxB^<#BY&HAcGQ||ZghbL!e zzyFrRssxd zNPMw{TTHeg+Mc#UMZzZ(8HWc4-||TodFkmTk`$8&j$;JoN`RbGID5PV(%(rc;PtH> zGDXPO2GprA`eViTtke{vvmYX6ox&xZS>l_s;VqtcnJ6!`*>?uelK*5dl~l`o9Xe(c z8E}Is+lMV$S%j5w@ZlRv)S%J_Z)3qnm~#SDXe$>zftU6y%X7={`D2uK$RqRyR}Y`( zEDWCy?DgkGi1Uwj*?9MZ+;D^*GWh)LbMk+UWdBHt0P30|I&$c;eT%#DmjsG}aMZUr z7FDM_esdGtD6Zs@`H1RjTOrru0w};FaTf%6eHPI>>o|^;2jsIZhO?;bpbN5;r-|1_ z1D8i6X0yft1~vZFwd?D1Oxum;&=aY$GI3S>!jfX6$IGaT{%0Ewz*;c`Y?+RXH{N6Y zCO#7%2Xyfq45U$sv3IkiiZUUM-&ZGp%J5W?>vqCV3euj5;Bp!6WvXs}bW)X2eV@-q za8TE`3W^?w#Cd_1;0Am40-@_f)3d^{xR!6s?+%Aj*`>l(M=s8M4A?m+)qmcj;;<5r@i+2>;@3SqBvfr14q(GN{d9A@8>w`Z(}}!E?;rxzfFbwcH}2VMf|+M7&v_> zdbt2^$-mv|)3W0N+RwF#fvkyVrekfb^;H=5bh4rfvA4ENHQDm&?Hg!c zU0Qib9ks8fBfAe+KKA<@3nU8puk03|9sP4jI0btRdx~>uT0|(b(~1l!Fjz^M7wGxd zbugf2boCHT9<5MgDX4xxox zl=VSGp;49e*J?RDpxAw33QiuE?xf@b?x<0^kQVU+D{S$*QW;WGl*yqU?#b^B2>xHB z^nN5}#TrS2vz2mqJ?2bpm%lg#SCMv#z{5&0EEK)Zvok)Jp;#Xky%s$`$Z{5Agbn{9 zJ`ci0FQ~6as*I`sRaq*@OD0Z-8)o#8`W@e!?f$(U`#Ne`&a4jTn01U=?L2$OkIH?S zO=W)nzE~0HX0^J206#%_A59m#C@*{6Y_EMAG+faP<^6bW5`qaVEB^9Y9q(V4;Sdot zStWRzagv+$MKc{}!_^`q%KV&RulELAd6vT%I(%uhtM4P7zAL!{QNyx2Bz)rro|k;- z_~;$3zo^pH65RI{Q|7<=LeZ_Je`bAiQl<9H4rh_h#T;2KNfMuSlgVQjMN7clrcQmb z_yQIOW6H`X%S&}1kO2*fpZkd;U650#3HxtP!2l?7FjZ=9*;~YJL_CKe*^TaNL6-dH z&Yn>4K{_%qP$I_U_^ic}NPclr1n@eo00eM7eut4E2J*%&kGPk1yW3*LZKii{pCl%^ zNo=kBE>~V*kcoX)bE^EX`-aBnyHP#M4@+&o!$a|u3YnF1FbbZTRK!D$`$e185c4;65*Y~&xuB_#%q-K=8 zTtaR3S6{l;Ke^ww>|>cop-%3~l|uK|{WDnq)z%giYPhxvMZ~$lQvrFMH4*0cgt4EU zCcoU=x&qQk86~;B>nrEfQS(ajEo~&n_#D9b0<@MRz+bU2aQ>@c4htZgATE@Z1v9x(*MH z)MAqZm{ZS`uC{CT9+{Ay(MDm9(_0?V4Zfnr@ll%=v2~wwa?dt{lXb(`=o8h!;o(Vs zJ<`PnMbdHUq-p&Am$;ZtV`8x$_BHYg6Oet&*^xY?w!qZkI=kI$e}4AQvP9HFEuZY* z4pXvt`({914TYq7jyv4~<)mc7BHjf@qLD86Vo*k=@j`*S%Zv}4fHi1LAXG1mDof*y zfX=@Hry%2OHS54Lc>BC8&d-_jGP#JI&BHq5w#)t&^jhtwiqEj(JsPSjt=(vQP^&-5 zs4#|)$lqGyH(K(myi}}fKMdd||4R-EY9j-(-KfV7LDHaL6?R1vrlHIK12#d)zG7Qf zZh}f|fC^5B4T3qF4=!FN zgiu&T)7gf>tUPbh|G&NO43w*?+J5#vb^6@7J-4LX^j-)MT0%#PA|jxm6tN&SEQpG| zAYyq{P~cS%K}6}j1_%Vwd#|_mnLGW|UA`YPHv|L?n!@Xs=SObt%-l22K6{^Y)>?b5 zXN3aBsj+}^sjd<*0T`MBU7?TwJSJfK5~-YXr{j4GIhWnG<1Q*2I(u#+!#fK@ne>h) zo_!p1X3Z1X%n)Wwn?ff|9UngZj0J@+U-Ol}{_uz2dzKKAe# zBs5Kd5GC1LLI@^~Zz+F2AcTaJ0#Zou@{XAVz_wElM#dLx@g9NjmVI^g0qW$tP$7G3qL8?SE~-!j@;zG|&LqP7N&BkI6~L~~<3 z#!r}tNt34{8V(>mlmrMxI2=S(Rf0w%VK%O1bZqt74RHXOcgvx?{`k5zYpy-x?6ZHS z>)IW;?9iH7v*(;@CU=aF$3v(NjbZf}kNJguGhaDcb&Lv?5(kPR;V}-&ahU&sCxP+o=Cmwaou@8Ov(^tjkFIWJTf)$EI-D3=oKKu}p$w6%2 zz8zJy^%ym##eXY;U~S*NWBgGI zF8qj9^uF%cPLtE+)T7kc$pe8T1iDnjMKj(vX3~a*twG0fKHtBCy{~ga`m~y{QK2eC zZ^sXASfZ1{JD60!R&}5v_=#A`wF<94VyInS`!uP2qrfkEzP) zKtLx^Q>}>yRd6m4(g`HM6iVPY4uS@St_WzVhOFfy=Lk%$i(L-j^3G%t{RO?3P?h$l z3LjxYba2LYy>YgPDY%D%RYbwEv25i!v~~92%rh70pZw&f*gb!~>qo8atycqY^i5C{ zW!gm_JZ)My94z$o4ulhl3P=e=BVmL?L1?N9LMaI3aF_E^YFl}|^KyZ1zED7CcMqmb z8IPK(3Z%1HtvPyoSTv^JZ|CN)i~ zibq38rft;Lw;`2Fs^O}!i0XadsWvD|0FVTfNca{Bj~z`57ac?Iy>CfW?dZ+j{a$qf z%IB^UKrKoEk*Z_oPJ8f+SA4kUgn4tkY&NG-0!j#h;2s3?A-E601-RhgjDd3w-}8}( zg`7JdUSuv`v*|WUh&YgW>%;S>>)Po*`|&p?A9=(KYcQ1xPMSCtef?%PBoSr?tj!Y@*`zeSPq3@*e$OOrd$ zIAm4Pdo6|(fWPeh-f*;~d=vTV55M>MpG==Q#_8(pF<*Oq{Y!*kJ0XX#cnOF3A1MB1 zUgpSAqyBWkM?ZeyRiF7ZDq>N~w(VfPXsMR%fVzF_ zM!I|F&g%|ke(hoY2Z6r?gcOb6{pQ!da>do}w{HC5jrxi6XR|S*YOmd}vE%yyER^z@ z0hD9!5*~BK6L~muhWau;0&ufnV#&gr*Pl6dK?CNTJBkf-6lq2Zn&-@8UAD>!KJDL- z33hHD*iI<bP1Ex*iA^P)!q|L=$RbIyh8#Tq2h%LdouCLQ=1)+EsgdyXp<%BVSwdVq4NiSF-Ry z%k>_Cl%SNb=B9D*TpJak7~-)wy8CR_n&tfyYM3yUGmeP-f$Cuw&C5CK)_Uv zoITyNY{#mqPN2573PK2&hW>`+s+0gJ8O@Ay5a105fD448A>8}-$MEAtg9td)Bw(dVrLregFVerDWv$z7Ng>2n9^TgpeA7p};;VqU>eR z2T*Fj_SV&e;hhs9MDd>=jno`_#GL=R?Ba7jGVjPKFoI#hp?J3I>xOQqW-tW7I5=}r z%;kb7oqjU=>36Pw^0Qz2))^A-K6iWg+OLR(PCW6%W3HMub0*-Bg(o(9L z=eh8FE)tcAs&qQz6^b?sha&oj5!Gr1Z9{fP9^7~LC?*ji0Z1^epsqF$TreZ;v}Zzp z?jCgR?uIpt@lUq~xGfMJN2q?lC88hhYmYR5u zVg7@!Y@G>U>47-!hXM!E0U!WAT>km@yRXnCSMb*WfD~etQ*bWW^u+EP z`nIQAtU()EmAOk4S&P$e0}9#hd0sc6w4}h|8C2hSR^#^toNNKI=cGlP-=iyRmM>fi@=JCf)u16%G zTDhWYMsx``Iu?2;X6mug7+XII4LUJG<-=`#UO)m)z*J*Xr%lGz&AVX+O>oZ8P*ab-!9lufTL-(~ z{0pKt{qzQt@3kfx>#GCNNT~Ec-)oF-x>uEQbs&ce+skWJN(m4N*Yz-ILJRu(2C#C? zCI~4Iiv}@w=2S>2uyD~*6bgBqaLiojhK?mmSHdt&n1%rW2#15E>q$xw0Fg)-rU`H+ zP+e6CUDu@Nd3f9TBBec@n#$@U>T7G}%$Ygv=JDgltF<-t2n9pXbrp~jzVBko_U+ic zs}<`u?*78AjXo0p)Z5Y4@ysu7{@uH3DmOe2W5+a|^0lvg_W4LS21-@rip+vl zgk@!DvEb`g(MC3#MKU!+i-n>{rjmMHeVrbQNo;Q8IQsMi=(0Za4!9T@jDvY4j-@mi zj^q%hV%LoH%v&*!8k*rq5_GEIPQPw5uAP2nINixzTRH6weAK5hhgeaDZ!fc zn=xnR6fiDS31aPR?f5}kTgP*R5cgm3*W!`zOS8u$j;b&yq9(Oes_6O7?W*2#9Bu8^ zXq@CQs8*vVJpiAk;hL4`&UV3-8EDZ_81yKVf&(UoZ5j^S<50>s1=nZr7%$Zr@Y0N_ zlp`s=crK783!P<(Ds0bU@OY6Jz|2D;-1K|MBriS{vFTUjbGe?aa6Ss<4 z>~3wnHJeG(#fw*MYwhU%6iPa+Ys8x>joz?J0UUeAsq-HB+-EOMj2Ttuq>?>i)#|O$ zJMMn!5-H^lO38uT@;l7`Ao1T@T?RnidFNlS?!t>cS~KsMV_@p4H#nHobNK>50IF!X z_ny1J1+ZoFCXAgp6@&nc`@#%{=}XVw-`m~Qb>n}|>M;NJJpWQukauirSD$!hwG}j# z8_s(DOW$>h&X4hSR&e}YIba5`W1s*o^58;% zDheWzS~Nsr=xp1K9&S+P>Trv~l!OREsHZ3@=bVvak2>bDfUZL!G-&DuB#}^61<_y# z>o;!Ir%q`>Q)A=VZCzbkhn?4{lgB+4jfB9t2b@*N zK;SqYW=x%kIWwnX>yF)^gkW6DD7bDZLhJiJvY8xw&qpK@K|CIVX&9)jtwz8sIXZ+; zC<-uKoXxoa0fFava2&Vv%zc|1C@FD@j%=)3@rxU;$FawsAo97aM+o59HeA<+s%l`q zha=}sgJ2$&g8gRa&fQqGc31B4XBYl)$;$P23&~?A%scAQ8@_dU)C`#3u3bB5Q&Y2E zRZ|1z`{?cKgBb`x2#I(sf_OzGG~EE9DmdqGY>$5L`fIE;Yghf{#l_1mqJ;2wWB6FM zm#eG@TiJpG#;9pIUNDnRqobn-t*sr%7c3Ml3)P7r8b>5(Pmhh{KtC!HwbNCmJ`do4*ZvA2A`6b6x9A(!UY&CsFRW?`6so}ErIaMw^Dt-TRNVT< zzrgoBj33tmRUrrkbhLMNL2w4cFmTfRc?g;&I(z!n%F4KR{4sN8wzYLcrIds3^xfs> zk#A_qqATA&a~4$;Z*a&7=L<#nMjkr`JCIT0aICyAsspsMAW<91p`gdnmvmt&E)*+` zAvFdq*9n_bI39zhD$pqQq~!qsr3!=ubd5sD()o9M313LmSB1rbnWJfC%tR{ZV$H68 z+E)RDI)zK5*jnxpR*|Bog$Vf8k|w)vAr= zy?=kEYc2cQ4&v2(fn z7oS?V_G9OK;28742VS}jgnS?WJOoZDr6Z*xJ^7Xbsz5NIUgD?#fj%!0z6eKEQAP{)+ znU}F-$#N7N*D4cz>-X#vC)c(UTeojPvcCsrAb`$}Hk!^3VaCLXP*sX(B975b4X7Wb z)9rmWT6H%1V_QP)uP2Q zYwo<8+Pd!l`S4h(t13<1Fko3Wmakfm(IXonAyFt4p=lb1Bkzhr%jc++?x<@j!lBTf z^JY6945JjvwQUEEo^e1 zcM_GA^ur%&j-Gq=@z*S0wegxqpLh|UzVs|Wp={IEHhs#(u^<%a?dgG{>Zpi^5s60; z3qP7f9e?+_}IRzrQtF4^!BesLz1 z)mL8pft$bf`A=d*eLYJi2Tb3wK!lHBEe%80Kqv(yFk$ovwD$~R_0~=_)x=So2*GiE zn7Ss2qR?j+Z`6PL=f?}3J^d32A>9Z1nNUtmqa*6-e*1&(Uh7e+2Ngw9LQ1<=jQNm| z&BpeVG@fbF5-LV6}KkXkf z{Q5vT_rmw?T6XQ|#wzIGIDTd`=FF^xzqS?I+HD*=e?Hy0bu*%)r+Oq{s#Y>X)1Hda zaUFK53sSE^d&0fTF2Lg!DbyWR$#7J(agybY~qTq9G8KA{;a!s7iMZ6vfl) z`{`?IcRi3RT7Nt0`1$t-Bl*=sL+Q+aQ#bGZ;sET)`>TkY{Oe!-sdU~Q? zx%LkIrB_xzS}ZzO1Ly|!?*sWT|3k{3Br2s%=a06_VI`B;@n?)FpcNK{0|9#ioN8+ ze}CtF_}^Qv10-Mj;DfJx2f#7_>+trDKux$N^kL8QlZ24(0Qi}d@}L%TNm(jB-PP7} z<6}>~{OON>`eLgh9u0?rCIE?CHjhHl0woHfksvDKaTJP0gaQU^2VjhWF$TlXaLS3t z>&G8EPdxGJx-X_Pt&b8)1i`QpO{r7?g`mK^lV3(qyKSheDZzE_-OY6g^G%aP?uYBl) z`KM#;#;s~aMFhd1$uw1`R5hrjD=?uWpz9b}U;n1`6bd2)=g@TxTej^&q9O**_c1h- zK{OHqKtRiS<+@ff##B`)FQ_-_0HiFbuQ1L_I|k+RN~uK4m}YP;;rTv1pCKF$K}rG6 zIXu@Z8B*n<6QH~UAUFa+lg4AA+&`HLY8YnI#pj*=$%YY?V$`TH;hwHeOqwzSmDM#6 zoPqfsnD0Sw4vA7Ng6|Ye%eIk9<)Bc(=FdOEn>}+ZUAAnsK1w0#_?AW#iVlia5jEA- zsHv@kuIsQI7nw{RmQ@4@K_n7GyrKf3Pzb83!Et?k(h;+*?|tjb=U#QqH(Mm+u6+so zly`^aqQz@IckH~`Hw(eNcty3YX$B@w8jCqo6PP+S0-g8Z(P!49_plxq+9LuKp%~zX2#5jAugmJ5Kui)mL0P zYt{^_x3@27nkFpEg6G;L8C$~y=Ny(*L{sAkZ$w8Y9XGB;J!0-Lh$iX)$uKm~iF_^# zRnws;u4o*QC@gw)S#-_X%|8TS@5@-ElrNA-jo6)mzGL$yY)KImGV4%V5kztz%kKWe zZTU;Q}~a*8(5>@I^0v?Q7Q_ zJ!aHMr&zRTM_0EhrKE*|1;=$ksRAw-{&4%Pm^5)BHm+X_#(`sxI~ILiZ3stVXlx#d zMGK!ZJ3Bi+gM%QOd6@qp;ol6P(G2R-M^uJysjZHCi3&{*m>OlAVf*G@djH)w*ZlRI zyKjB!$yIphiPvAUZRgtnC_sEbssPqeN{_0aQ6+}9XUw8%eV+?)jg+EU2$?@Xyx##5 zgpmH<-SuGW#TT625DuCwSFqJW(T4B4sIII)AYdY!D}oRSpK)Xg7Dm)m0}6rU970GK zhN=c)5%01K&YFMi4{ttF3OQVuHn}1mZ8+nEBc9ma-t*$>_1mtMQf~Msgt}Ad^c92< zhXHL2@!1T~-{WFQg@pduOu2t{8qi)_)u+{w-Gm_V** zVPGf^+w}mN#e{JqkcgJ@;vL6DUowk~?V~mk1yNMk_8@$t5_3-c7%CfQqP8ab!%IGP zB|5u$;5oJ$G)=E}Af*RP6Jwg1*br4|V|^8kMWav+?M)A4pL3=tN~!jssUC)|L)TQq zB4JQkiuw;X>e6)$s;ZWZeIfQ1JpiQ?fq)Jv%8^?`hv$`2fGpbsrDc~vN^o8()*M#u zDNiv3myi(Uk^@mLW~Faa&s-UVatyt>ON zR&8yqOVe3LNGTZST&D^}MMV`}TD%$0zQ~}VfW|5vtvk1&DW1cW(I#5EhA_Eq2sM>$ z*wMz3PZbf@BwqN-b7~WfBfrjq!91e zyzgy5M>aQNaG;;*nu@v+_3E1LF4QzMA{-H$)9LhO4b7ugFJ1iVPtxi1$8=3=8tCg2 z{o^}nDw9*A0ToA%Z-OzsR;1E4mapASiyp(kU;zpRT+c^sbxcedGlB+Gf~7l?ca{cZB`-!hWp*Adm>f>Q$w;1Nm$QeSN)X zZS7?nw(Qcodj^n7++#`l~OUpeTyN`9C%%IuJO_|Df$6WPyy_m^*L8hv=$#L<_Y zF@Mf+b7xJ#q_Oo_x@zkgzy9MRXRO+|<3q`z+(ZA07XW||P(oyDTdVf1>#zCxCqH-n zk0wu>>#*cvGiX*|C~F}UkkB<1J{M3Zz=Q;q0xIF)e#yAe6auxJYNOB+V_j1q2>~rP zhf*nltMM)t|%71Ve$_II);GC9fN2Ek)PNjt4Z{JfWY=&<+ZsN%*t!Q@`^UUVv zkw^{pW8&l~$fh$$rBbM=sl&iPA9T}%u9mtC`<@F;Q$UG?YZsv^K~Ud^Yg=d;GZM>J z6~K!Y#*P^UT~#nR&`CJoOs`(EX5;3`lP6BjS&R`v)I?)mmUJ43OIM6>80IC&viiv5D4hb!;d{@{`u|)@7TVp^~Uy&?w2pS>|^zY87}C$ z9uAobUAuNQo`3!Y^`5^zG_-!h=5GPG6~Mch5Mh`m7~?8c6vSguD3n0cG`Ozo8>X2r zOYq)RQ&)FvHkE3yU7rmmlXT6>l~0Tw*>vve)|jXc5!zT4LL_A3)Z<1nAtcsq?^AQ> zBBqS4rt3R1-iB^Re`e|CpG(i)F?vGiHjgn7K)PNj{grbb&@@fP5()VJe_v0q{pkRZ z{{Cd5bN3nu!J+HG+I2gopz7zBV#TU&pFvb@>qwL6FZo&y-#k9^)O z4)Z_2{D*fczH;fzuP3Z$*q6V&GyLHTKaRPxrXXL)qENIzD21VGxc&Dxg>SimT^oHLYKVy%2LO6UOkYU5A#3h{3{Z%;FhyanpAt_+y;a- zh*dj1XV$C}R7EKID~p~-u`meJFc6N7#%af$fKaT8TDhdD6N#FtsJQr|Q=Cs+8OGgz zegfTF7i0ANrtp9huP0G&-fz`J@KOY!Lv?gBkC#v zLcn~s$G~Ttqq;JRy6Si--D5A~pAbM(RS4HrCrlWP(-zDl%5svwkmlyO+ja_@fXiTkfTdDiIYk@aC{3WcGmAha|F z&u1lxJw<|`q+lW;!G(aPDxd_Advy&agg{pn7^(t=QiKBr+6Rh=Pi;YevLCTn2&sXj zX%!vhas{?&!%Fk!TYr59fEQj~^y1VHZ+ZXCAv~tJp1H244j)%aDHktW z^Ubi2+k!GRfQR# zAXEVeFnMANR=b1;%te^Smo}I|Ae#@3dMl~u@kUYkAu&ha{S!ij%loBoo#L5Xhjv6 z??RzUS)_oLQYeKi?;de5=8II$_EI@V-`?J1KKa6uWNTO7_32FZVU(V~sr^cJ-$n#L zD#y*6d(kN;pU51?H+9{F<5>T^H% z(U#^>qbAz6<8^g(EML8LeeYm0bv1wj-bR&e9}qH4lX{;23sqHUC>pzlbKwz6bt1>qH0jeDSy>9)riMpAX4sBHC4nDmGJ$27R35L zgZ)_pAhv97`(|%X+m9&KST>h~VN$)VqvJyW9v=QIB?Lmsw>321&`AD|+uFY?9OnPV zaZ1>~{D*Ivs%FpyM>PFO+Cv|!s4&@BWtqC|#U89)bu!-n{<9Dam~dPVf4Kekm@<7P z29ilEUA6@Cjz0-jA&>6Pc8qEnk6~+}66|@`JJ6dzk-$=TFK}VIs-HmtQcw z_JpGwtu0$x&E;DLamiIb2+lofE;g@Qf%l#HL!@&)gdiB#G8NRI;GPX#S0EgQfT7VH zI}y~C-T25wXW+W8Zh+R?E5?s+5wypKkaGROF5=n~Ax0@G$V(|9&MQmT-jdGbu1X~0 zo)lc)TO+=dMqV1-q0vUQ92(T(4u zPCosDqA1UE&P6(tS(naaj^Dn0du4?oyGD)tCEd7rqXWDVb>6n@s*s}8Vfd{m7g=_!?O5{dku_s) z5zO?0A=3ap-UN`))l$P;e+l7@!8E1OfpARMlV_CK&UP$z%`?hhYXI@EuqG z#Fdv=&%Utuc21!`ITT*KVJoIj z9*2rVHGHa;)STpS^q+weU~nkU`i2T>XMfs!>Gjp#OH0;V=6U`j`1cIt{f#p&xZvEQ z!;z?$$z)ZKQlY$}=x`htLJH`bX8quXUkC5`>qFNQLT)1E&ex9Poc-97&r$$KqjY~s z?*~3HMbqGW?gm9OXi3qF3D>s37>fo1f%71V0FX5eGfb(nPzc`PSjCHl5TWMgCNz$0 zR#z`s{Jm}c1##^7y4!Aj;>q9M`Rw}2InyS7bW3ZONDmF2+tl2=LZL*LQtDDdYiMdj zZ+ABUAQTQW5CSgcPc=oEgz|g>2XTAmAb|eskNj&4pP26c!6zSj^f?-dm|iFt5{}Ek ze7^8a55Rx1eYkgI=3wD4|2K|vVEC6W9Kn>6W=B6ZabmNlm{B$F8TiHpl?Zj-id$~^ z9d@^O}36K7)L)FUB~ zftz0m?yQA$H-La6Tt5Zl#x$Y1u@Z%>i%38qZyEI2`A6XsAO8@hOd1bJAeSq^vWh4c zi;$3rMnaH~2m}m7A|b@%QB=euNF-vYh(%%OdN~zHA{aFP;T)HoB7*)@@148)wyWJ+ zeciHiC8ZKVz4v72(vZT@DC$QvpsK2}6se5Ikw`>QRS|`01fc6C48wru z3yc}n0FO~b!Vv_6K@1M`!*lJDM!IA$)ROUUJ1#7%2-|ibq(mSPfTAiW7INtB>Y$r8 zY@nx{cw|dB6ux3#ub+HAr{wcC0;U1ib)l*XD&h$YY4`5e1`&X9s5*ORi7*ri9wcvqwAi<*%w?1RiSwJ!M|g{$)_U_454-BcGT3@L(^1Y27+wy z%L`Mf!GYTk^Z)PRU(VSbH(_j3aNf*nr>EV;>RlQ#7Q$HGArqUE@YKN7rv@!|0}(ryP%M^O;cN(EzKijGtRqT`_vr)UnvHyAw-SZorB2 zk1;uykffxdq9_VD=VsQOVv4Q$F*Pix~`LFR)y($4v0aZ>IQ0SE77)lC#KJw4cB#0$me0(HYfo+F5p-mBzMaxM*>80 zNJ#*yplL(}`uYb@U7dj798@(*MaxtH_bn8dk8mW0NH_{j)8V=dg9ClYq*L%c3%-?w zRmhoR8^g{yr_8_k?@v6tigUK~otn>;>LLg{&riMh^3q4ndf$m>_x7b(Wo1mQsjfyv zq5{1GDU7ZQg1|?rkOA`rG=oCd4XCP)mPP|rAqO)j8d$l>M^n9yCkAscs^YlsoUX4&Bj#r<*A85M|i(VWMTeg^D<4*IZQaB|G?mycFhz1Sgd-IN*cFkoM zz8@zZGZ!ia8yf6YDW&i{54uJ%x-O3XWD2(9ptdRjUDsgS4t94Z*~`l|>Wh}FUbJS@ zj!OZwzx7-lDwNL)nKEUfJ#*%CIF18FQA)2<6^>&e7K^bLUtFZ${QEzBl@RiagTa{) za;>5$a2(q+bVKuf4~%o@h5;eOkgjVr#eDt|MOCXHc#CD(q9RdIo6F~!Lh0``!$3Zp zfspdc!JrAMX^0uhPbQ6P!h}hauzmAJDun!fZT$#ZQ&R)SaSBq(0_O~#=Yjw-7LCFy z^=6QS5a~D$0Q}c^0Q)Be2&J4*#yL|c2Gi*w3h4*}4pMs398eXU{}hM$A6S$lpTU-f z(2pOyo=~X!I%-Q(HgeqKf+Y=~yI*vQxL?jXkwC>uD zNHm6UI07Nz4W&}SP3zZuju7Gl2j>QQnE!$2FC|D*edwV%lVe!DF>juHL(4m@s7yv8Vwv5{DE%a)lx|Bd{zBty@IoIX<-)oz@4;VMH;n8zXKR%jH=W|C-AFEEBIOfb_=gmITbBZMyK+gpL z$21L$8(FK~e&0g1kjuj`bSMge?fA%Mvanna0n@;&8IvGI$)Fla=b$Mnd;o%~5RP3o zEENo;vlvVbVRZ8doO;YuXr-pH@LeB*2~bMZ38QM+$Pv{?Z`i!+U(OpP1g__8oZOt~ zzWVZcj#Q#z+P2o<#*QSCT}dQ!S@aI1K|zqqx!5U&AcX|u0zyg{8r@T1D5Qj2kz|b|Q zs)A4`fS_qYRZ9(kgcLAz9fqNUU^u-BV4RhAqe^ucx~2~o4?qZxcszpPUMQTia_4jb z$TxN(hIdilIL9R?Kv7EiL2t$I=Xz7!_jROqetGRH>f{MiI>o4_Cd!x(5Q0P^f$pv@ z^z`&VRS0@}dJv69QOeCW5DWsI3wSPvrbyTx2PFcYXQR5N8r^-{QBy6!7=r>83iaVw z4Dom+%s>DP1YPYrk1Otf2BJg~MXO^dtEignvD#ka*QCVG$n(8WK3LM!& zH>Nc!Fm!^(S`|UUp$Ne7IjTcBAry_m9EYhB8{yameDK0c@#4aI6n8$Pr_>QR?&Ov) zB<*LnwYGNNFQv4HdA*DKRaGn;{Pcw%IORtlI{Q@2ojuD_6e@E0oSwHVRaI5^9tDBi zQ)5z>2q9;Au!?#3z9BmMhn!~?tqk7z_ouISJ@3c=28RQRva>pE&aCMlFpPlZdVWxr z)8?cswaju|kKXvp-xoONzdX=v}{+Ez-f_i2&sVqG{^O+&2?39Y#U<|8jFVvjBFXJ*2e-6VS3uc31bD- zgD`dN99>h7qKe9L`GP6|)plJfIfn!kpZTiixd7ewuGs%q@U9$yf7ZZ+l~q8B1*;rc zAZjoeKrk5iZLw&5_`R^`5A#3hP$SXkv`a3zX_bU*(1S^`=i4y%xXBovj^HL!|B7LUix7oUCnp7hYrUBewb z5A*-;;m>{QoH|D_MQj3IUzNe)4LL+(5+fU{aP-`XsH<&(f=YO9MXA6}aX>W-sz%}2 z6cD>WyiFPhSRhF<+8iR5kd%3(gr#(?C^K09E*6p6){3o}JZ7GL0t^YLR6#TvL49=`wq>ECKZ}vI5#()-bhd!q?Y-F9 z)(y*csN?LLl!Yw!%-q>qxbLUGd*w43Dzi>m)< z0;JSCI(txGHy!Uk^JM6{j!Y(pn(9gbi0*-qCHYTL&hH(L-t+SEuxuAn4A*0n zUgKPrQ=13`B#0>0LX?Ud2vF`tMTjiDCL{=;axSb`By^Il>yus2+x*shdAZ}XyJKMO z;P{#uc}XK(;)?i6ecuHMK}TwDWDXJ zklZcp3aC12Ypc=QH-N^*CSKA|HLyx3VY3evdJsdJnYGkHhOmh=f zhd07w0%IEEsH?3(qOt~>rkA=!2P}+;7ZD4o@R@|56u#rZRDkYuKLoRBUWq$yK{M}r z@Ogab{U;!(F`Rzt$tWCqEHs6{_htC>Q;xdprA14B_1wZG-`KEe>wS!g+&-)#0^k=H zzWn675)x1V@?nWr5CMRmP6G{jGsCawpD}~46u%lHgDbPRo4#Z1s~@B z-^IV+Fp(>|m_0p=iQ`A2tGj@KWDdJ~a=7iT7cg<`CM1V!j2k}*k!T2-VL~-@{@B5Py)HVG;4j;-kbLr`5;{yKYV3;G7rXsoM3 z?_d_$oTqm798l4lDqz*>%~VK< zDHBGsHJf&XUt6{L3hcw~$FQB^`QD;$-@WpTb=w9Wx%|XYblSK&y{5Sd(MS-{$~bDq zOaPG#bBGPNKEQo|+j#h?ZK#PFShuqWnOq(O6h7xrD8+>41g4Izf#vv66eqw|=2#SqM?$ zp(&-5D?%tjp)f#n=nBEerh2T|kVih3MqOP!as?Y(x2{KZTt_5mKv5_R-2n4_q|+_} zrU_kB!5IhVqFivUp|@vH4~LXYQ+?yIfuVt8Ez4T@ri-8)7??U`;>EV@9rMa-%bcT+ zoMmz$kR06^1pKnfNP8Z>1)lnaWwS_;RP5I%!yQjBg; zuzFo8x4c{LpEJfBcj?EEMo(Xo{ruN=tLL0KA9H4mg+?eOp~wq{3FF5&wKX+~Ns}ht z_V9f7xk5JC)H|Lrfof6dh&zu+8JbQsGOY@HArQk42VaIXm8 zu}YGjZ;^H7LPFORmdV@nd;fFK)@AFq+zjCNZ_Mv|3sOonHPrun#*ArjT~{Xrprj<3 zYZw8M8p^7_zwORm08bw*;~xM_Q-`8z;Jz;jA(9IYMNudv6pV{8Mlj+E&d&=}uw#PJ z80L(rF?BbE0A&y%P4yWEa|&>MMuYK66f7H53TWXtY^w;#eFzX0QX-$vL6yGHi4czM zs(vwx36*9|McBkZwulL}p(fKbkZaq5Q3+$#cE@h@e6~+LG5#;$UpfGLMJgh(NZ`r= zT`i*0&wcLlU&dmUP*ok-%m4)UP?@N}lu6Ba>e<(D-g##t772pFbEv9@v$?{T-g2CS zio^U54gd0Yi9#;>{%?K#tJ?~Ny!w#~FHkQ!|NRICOwTF2e-D2`2nvpuux$(3 zTpp?!Kx0D$>~bt&tEY8i0L@(^m9=es=%>F zo{PI5_zgzY6Er0N+a}1TJQRz}6A~&QAK$OU-+&MTpV-;ncT0VBTqAZwd3kfoQvk>Q81{B8-IHrY}cD0?adag{R|laRCN#r@p;FY;hCvkq_&x>; z9I>#0+IR@{)lrzb4j}|=*TZ0@P*UBICB1xw5}NmO z&erX_nQ0noT}>6d@}`I9`K4~koR^Ah0myQilA@^PG%N*$ViCD)9>GAV#vz@}qgb#| zU70{nZx5EP+<*yVn=yI9IBed!9iGn+3rUO5Db>( z!C^jZ*N4vms^}=%9#lm^bxjo->nq{A1*}}X6`MD2#f+(=FqE;-)-{BxsyLc!C=`W4 z2#%qQgR06XT;{O8{*379x3Fg($v1cT>!5Q2MBLf^JyC%U?Naa=R7 zz0E~sOvk7?icB8p$x|VPTio7eP3!76=8)>gANt&y0Qp)4Mea#hDU;eT4GlR)& zoI-$7fHNOl2ynLu-zmcLJxI>LIR_UdhmvuQprLsV2mSuOb&;(~p zS2RttEenBANUx}=M^k0%L)8%tMj(VrMXk^5T94|WLcw$Jd;p3H;aaeZ7AU2NL?S5W zvmlT#gJD=j3(P7&(F{N}z?>pDGNjX4aox37qo=1` z^z^jCv7GQDkG!(kvfXD6hQV`~|9g)=Aq0JWeO-#8d~n&4CC^@X!39=2oe9pEJ_Uc% zTJV#HwqtAs#|J-fBJOx*1x%gdLubyy3rjk2{v02TbRBlKjug|UjLUU(v?jK1-TbAJ zBXe-Za+v=?*OQO2E9v}I@Rk-<2-^a(!h(lKeIP*cvDJ|Hmx!wMT=XrOmShw|4 zN6(sc)Q;AkOlN;uKWffoQz0siQUaptIOp_Zkjdsy88k*c zm73jvstQu+9M;|WFkA`D3xpBYH1rg5P_ucoclU|j!8CpPv6cQzh64ZCDU1jbB;x?E<BT66uXqt+EX+qO9gu}s7s*waVl|q*1l>jIfi_p;#mN4 zSLd#)cGRu>g^!RykV`nouFwe=vAEg~8Tf)I&-K@p1>!f`p;b`N{;*xlNO zU~mc=LLqQ25e$V6K(9(m$r zB!_HF8b1olR<76ETHD36spAVLo^Yi3`jX`z*|wwO9{P_(3>#iswXy#bxnhJ!DF8`_ zQ^O?pA-D(Td*IB26eu|WoP!W4D&k?*-JjOK^XtFuTDErcEJdOD;kuGTjkifXYKh0f zzqsyeU;4l&uJ{DY6>_SkYNhBu1PEbFNYFpueg7*$h-VMB{or#*LKR5h$AM5d7}tSg zN5xF;WiWqYov{22<=_H@BIXTJP6=#I!6gGgAwVIR4^34OudG7b?p=@oRNX+aSSZ!@ zm^wgJSe65e6KFnzZCP+#7ZFpVsn)G1dK_E`N>u~nnrkc}sJ^m2`=@;oQ24)scXR+s z4kIOy@+*J1{g*$v-N?=dU^)02EY!D@p+j4q2*7?Y7jI$VdCVQcI?p`eM~ZGF%Kqj1?pm!X&*f}#+ZU^wQu6RAD^Y1q-I3BRP~oQ++jbUE8B0 zU#T0Qq@+$$Ob$7_a|QF|rRz^YNs4d3>WUAiANFOe4`(g5wxLq@T52Pvp{b}!gpn`0 z7|0Z{t;jGw9)sig=o=~^9xfTBloEJ8M<8HACo1}f3h-D-lRs~HDA*o+k0DcV8utfZ zDFN5@P*+=py1E(!112UjZHg;X%u-;2K9UYLO>TwkI(5rXG=V2p!t2G4QecpRK@ zsH%eQo(zUkSvYPfjj5w&5WBWG;P6ox^dQ9$sLEMacg~9h(rDjZ4EFS9X{z5r!E()P zE)UN5LYHJ(b8hCvOmXNDDFrzD#}u03F&%UC>_4lTcFyt@tF{U$7x(oK{=~N3zT=M# zZZiz6$#HA}$tfXJ1Ofp^)o5_#%C*?IVdF2ldi%fi!TGiK9W@SfM%3xzoZ|>hrUjB2 zpe{kcj?>kZOL9(a)cSfVAK~kE=f3sYvW;K7?%Ovx*M0Rf*Pd|PoZJhqtPFSeC267P zVe*7g_}@Q0gqbtOBa39Gw*r)HT@5Nf9}p?D*br?#eTp=cD)Db~FH5?*}p z0n`$L2OfL`PrmdLzW9yrV#bu&$fkyH+}z`E@81`}b@CuoE!Ag2;y`;}Qc4$qtE$Q` zcD8lhB#HdP#Fob6lc}6DajX*F+1?LHRJcO(3RaO43N;i6Kth5NiuU$SB!_YkoI%%h zeZk>?P4Toc|9<*p7!Y zPCg!IoC0jzybZ;og`=<12^PsXIy!fS^;@A^fsHU56TpM+@5hP*;VzCe) z3AkYJJs%zuU_Jwv65ah-w6}G^^E^172VDtZWxvqH+2*?`+uy5hr+SYmJrY$=k%Vu+r0dR-kcU*r< zO-*gXz~BI@ud6|GQ={I~+ZTNHv1h-xZTqfUI2XxSJU;#Ox%AwQ9;@I<-4tB$3?0a1 zeKaOike~_}iYO!*f~IyRfNxPsv1#k>uYdc8zr}T5{nWL`9X&@p_~;Ab6PKP#pL*s6 zeBkVp@zSEDkkZG@3W7Mco zsH#dJ6bzxVZZsI<4y9DJt(*!eq3HpDmZIjI`!MyOe#aemtF~?5dazQ5hkdb7IC%fD z6RMj!`#ag>5f!k9I*{`PT52l5iCXSE;)9oW)}+*scNu&kp@PE~3Vh#(N&wXefD;YA z>w!T5lK?yyTmVoMFd@P10(6yvbB2K}D-hlE6Mqd z0QwL3$vB93M+ZPkiI!0fw}0uH&$C@ymj)^IkjrK1_kZ$7x_ZsFj|w3k0Pv1En{Ttd zc2IGc|DoYOe8Y%WRDA2lKmN&&K6}+?uw}h+)=x3r2?8+M3u&pd&3w(mCo z`tb9RL_tL&hJ2xjzJVlS(E!GbYD6p=2Iss~K*~5wMM2d$C!%iqE^HV`qFETI7*QeO ziLl<=pV}%V_&aaa?}vFKF#6Iq`qM=yv=l`rgn)4Yg;IE)56k5U2TQ30lF;%trYskR zatKMmxP(GV4GDeEh0lbLM1|{%rT;lefR!Z!MR_NH5`Z!B#=TGuNBrNAI|TNU?UWJ@ zW6(4e^|dt^NDjfWY>2WHtCXehpp=5K(hdhD1iW0}Tsl>6k30qka z$XnmK^4jsWbNnLrNeR($JSGpsk}D z-F;~YAw^AP3@ewV06RzxLxZjv!jT0}*Q4l4^}X6QxbwHVs%S;WTIM?5KLn0S4$a$8`{^bS@+pwD*Y5tdR}ESnf1SY z2p|2_`y=-{Tt_oMr#WIMo#&o3c~(_4fTbI|kWOU~EPBW~0=mjzs0u1V26UCe^#u%F!9dPI z!S=wIL{ZTpr~+L9LYj(Vp#a}6rNsrq5o8M%6iPt32QNPaPAI6NfHNN~(~p|U7{)Zz zI+n}Lmp69PO>L>`IrnY@kUTgi;^5++)c}n7#y74$!O)mzTNyK#Nm}3d_RYbKo7<)U z*!-^!d?=0QG7j@U$QZr>*45RW_tTqh`q3pH`G~iE^LDjREULP$Av2W0vGY&FA8)@M z>7gNH!oF%0S8}TK3v|AARxYszmJcuAYIdN6(u0w-**ISD#+A;{Mk5 z-tT?v{IecZqM?bZV~d)q1dYdIdm`1PhG3;S0A{;5;fw{S8{2{xfA&8ZIWd7vHicju zoUw`nIfimQLTAT7{tiuTx=ccPo-cLA1$0e?N(eY8JjNl)g}syl3Mm=ZLP&T%gRYkB zUqS)S1q!xT`V0YFCIi5KzCq);K0Lo9v!f_vk7^H4Dt@HHP6?%Z4E{ZJ4&}&psc2Gx z?YJlui!gK@TyXe)$>7&@t&|c%1*oEw=c<(Nuc>Njmt(l_vV3qZ1pIwdVBK@A4Q5jJ ztZiTX-D5_bUE8s9Na#i%)f5WL5oqrk0Hr`sr%*H%MaM>0cNX2f1+?x=dVPHsvvLFu zvxA#t7KH22HHAqDPk|b&j?|ht(u*Cvo4@Kh?)tZK*!!A)H`hKXpWi+HR{1|eL#d&M zAAOc~^`zFXSh?!je7?94d)4C%Kd(0K%>4BWzsx^anTSlDHCF$)M!C-f(FBB_D-vZ} zC;3w@m)``GCU(!<5rY2yfhWHA#LLUp>kmHj+O1u^18V`?fxUuo zhuZirbI4|M`S3?Se$`9A`Bi%Eg%_Ofxvrz9vjtE9v3LdaNCmj>fDj5r)xdlYzUROU zhD3i~554xfZx>wGyZ9h8AO10MmwRgQ7qaZ+($GNKviuWa;Xh4m68C!)|HFlW^$mQ zh=fBJSzpP5L5*!|A2OG2>)D(w*z*AV_f-784gSFaD0}Q=?AYd8rcIq>wzX~-j9K2Y zm0Nn2A3eKY5O|tF2RaT$cg_gM%rg(?jU!=){cK zbFgIbBC&kQVlld9oXVN+jc9CCGwJF54=-MP8l_|tAmrYSbq@3Y@8=Hytd#pxQf8ql z3YG^(W1s;OBIn`yum1!C$t1q`mCquU%^(ys@WC&C1@mX0fXv_^q~H*Y0jh!uGlZeM zgx~^Hp?azNDg7pdxKtj*M9yN-T2?ID!Z7q}yZeTgy|!}G#Q=6uO3^T)stV=qw6RzK zjQM*~M|ipWY&oV3jVh?Dtiy!!kHpDid(rz$0iXZ&Z;~C|z1x8|qM-)}GL?7xrQlSd z6r2l$gF19ogXK67LcsA$dC{7xAY_&zjJ(_&OfTo%3nAe%0oU{4F%DIs$QC`pINLAe zHC=^D73AC^To=%Et=y%TgMh%70-STW9xJag0zxTtRW09pOAWt#W`p@2;;{%axjZu2 z92!T|!F4_K4`yNL8p5Fve7~eDq^fG!!Ju$F7oxn5bzOt1D5aVJ$bBw!F5lzbE#2#W zJh6Vx&01frw-MV-O=Dof2c!ZkpF_UDy#8(`(y1aH=r8JyBVZbuP6%`#Hd1!~K!Ig? zRHCB7sGS+s;_9H+zA=}{t?3!+xR!I#M=+eK^u{Ie@28BteQJ>J^Xz?pe?GHYL0jy%s!28pg)O?o<@MX|ed1~%B(k~O4^fKvzb8?4UW&PV;g~Bw zea#vD0|SqTf`+H5ihkU2$0Hhz!gETp;EGnNSK)%g2u3_bk?d2S`C@SEwq0i(eBPy> z{ptLIpWOZG7wc;(&z?J>cGlVRT2QooF?mc4m7KGN`h+@|wa`15MLJ`lE1idA91sHW zu!#{N0xz`@O^qQ4s-bVN2O&j*6N*423J`)3@c@Kspr>m$B0&=kqw7%_4YHwJQGIS@ zkGi!Zc`_hg#Xrfg_;0~G?EuhI7aVm)!0=&NOtiOksdxS5nb)PnN=gad+JW&sh{OC3 zGKM2Rnh`Xr&Gh6Ozj=u>=j6-PGfrKAzP>)#wuRolJ`AMNfDmYG8bx1z{z=-lYa6O+ zYW1Uzo$vK@bza4NcjNF=@?aXChxs2`{$+@KRx+RY@L2(b)&oR>2nLk9=myLXdIx&p zWHYD^1dt~LxBcLIpkKKbbEhAJT(JNm0LEN&rh5=o3=oBbQdQM-0;#TUR}Rsr@HXf<#_qE z)tK9&tIB62tJq46ctc4EUlXl}HHSxL(;9m*~&faC`|*5M*se2_f44V5+JV07Y+aANmH8m@|7i zcDHt-Sg;U_ML-n_L)Snlg{G9$tQRIZinC2qY?E*t%mklBqOe(I`wqhorf;W6hzK*-8{KI2k? zfFP62-m$7<(XFQjFI2m>au!bb=-8QJ?YlB?TuupyE*OOm8y3sJrw+RFnQIGHZk3cm zwH^CqDWoSQFlZ(x7`on+%jaI$+y4D$DLwlWo?q(nyf00$$Z&79y-_yX2H1Kr)wVg>+O>kEI5%-MO8t{JE64H_=_=yKrqhonSTAsPkvf|@|lH416X#bNDCoS z0KU^ZkowMJ1F5-BzP@dAq9XRUV<*=mleg7*QyOv7>?VwBu7b}PY}d!`juh)n<|%hv z+M9Er`W}1`!~#Igz*b}r<8Y0$=)gd_q21bu^P5JHg6=3p8IbX`L* z6o95E2#10wDYcYB$y{& z)zgi^VyEf~2YHc2&Pm;1IgaP~Rz6$I{zeK&Dc)Y{)^(h1uHzgsIo`g!_MU{4TuOP}bI&h&_S92OactLD z4MPRz926RYkqBIu=hm)zEqc{epL=Tgs`cLku;LK2-Ww}8=PL%2Lk~al%8&$b=Bk|o zkFM-uET8OAXN;-DkyA&pvCWn0(KALOS9Gj&-ZckOdAepxH(ZsXqB;oXF<5yU*1ClV zD-^Y(>Jbd;B3p3i)~>X^cK4vyHG7GweLN%a1H(VhmSVD^uYn#_ShfrSb7l6 zqsPK379pjy3i(`k@uC-h@4D_T07$3OzmR-qx|q*D2^{3e`CeS_lpfkEUtQDv z_`)Uq=bUmv;<@cU1`Y3t5inYNx3Q&h#1Qi=(U3XKL7WV675^ZbmYj zS(`E4P${7ljQLPZtyEM;2`q=fbv+0P7@7)|0-ndgoISyB(vH60Ym5Th;dZA6k=JCZ@6}J7=e_aD3O5LJH1!>fM~5L-`Cj zcnWBxpF&8nyS?WLwBw0`^2P5#aL%9m-R*aMcfo?=zW>1wp63*bwi=FAFjZB}u8!UK z^PlgI-t>!K-AbJbKy)G}gqRNFkS7 z?;Ze!Lp;Vqf;|qv@Wol1sC{_B#B;{|?&dq$S!W-``uYd-S6*B9jlDnfUc_PkhXy4^ zit^zzr#*V>w~jh1YD#wPjnARtiPtc`LBna&f%}&{C^i+|C&rDR$TFE!c;WL;{cW(n z@7m#qkq`nyL#aCs^Z);k|DJJ_2Eqo@_6IM!?%9VwaenHY&wc6)Z~XYt(6OGTGdbE& zT}2(c2%S(26w}!J=#yw3GZLdmjYh672`NkZ+_7*78@KOZ)21XuG#0{r4{S1jen;Em zgX!XRgpl2b`Z_9qzjo2Nr~OZ0M4iP_8Ja6N>hJ&bAf8^h8g2!+I`<_1wH+IoF`jaMkzR9C4O*;p@p zE>y-jR7#i>5*$+HOzgQo4;K@2E@0@I<$1pT>`O~-Cxkfvt^wZN-|@-r{(lTm?g<>p zeM{vz)NB7>qm+J!_hj1aLBy~A=Qp=~Kv{hI;!3&j;`_ z-n+&>fU@HX^zGQPRfIx8{gl&B2c-my7cINXvh2%t_vfo$U9)ps@Nx6^Ge^}-O~iw= zC+(PHM#Pbb1<+U*cS0&f-#|{>|Jv^0gRg8}<9hzF*w1}a{;xp=paGaMZ`!=uyZd^? zX-A$tNfs)^(r4GHk3IKGr@ppIDA4->=e@c>4)Z^RAd044a`yDU-~R0*&!W0sP$>zY zw_pVBdSneQIk^f&PZ1|g@#!BHzeaV#RF}T`+@oC`T_3@odZoks|9|HHHsHc%=iYwr z(l0-~Xy;Ae{rZOy4Qg1oei_DG`e{p36?plAT46-=1)u(`_}g8#Q7WY{OoRH|WASK( z*Eck%u4`MS8*C8Y{pmVv-PQk90KdX}D20wvWgJx$_2ysPt}a`-9&0x2ynp-d_8$V+ zi1Nc&p%h#Q6q(q$a@G2;^%wc?0RW7cAN|gYkDfb!+y^RSChzV$@O;ZLZhvdX(CbBq z9TN$t1<&VUDJ67GEg4yyLjs_LLRA%*MoBG)`K;9SwVeJzP)Y?-6yjMP*KL>8?k}F( zcC%3pn+}dGLLP!Bkq+R22bJM(bi_$>r;eVwd}yeEEj>H%iL*bB z&W?T=*t03F6*{)ZNcc<9JqUo(5z1s|GPUu~F1T{vcf!Qw%#KC;Xkcw)W2LZxJPGMW4$-a{AOVgB!FgaCXtQ4v`X)b&wE9)I-Y z$t^87X6kfQ$Ex892TRs3#c9W$4m+Pm=TJLVuHA%}USEl|8@D~>Io_uMq;Zg?KlcTu zY5vA>oW}ty2cQ5L+Lymm6lxf{_G8O-zgAIE`7OykKaHD==(?=&BJaPJn z(alw1G}l)`f;xQzY4l|Z=IHtgXqpC1RnVKvV#BUpG*w4YUsEB#g}0+8iGZ%Egh1WZ zo5zYBDQ{PAdZZBIKXzk=lc1U#>pyq$2}gZ97K_vjzrz?u(XuvLw%sA6oKjPj7#Roz zuzurqwrbtx=gvO;gi|%Ry zIE;T6<(;Prly(*WuiXFl7brkt<$Y!eAbY3@y;q<{lnR@BQRlCV?+{VbG*_+JOoK`U zyNjJ75lXOi-OH?jWcLWXhoWbD<1qh2i?fe!ur_S(XAO%2>Xgv|ba%AFcZ$%6@Cvr8 zFWqUh-}7>DGLW|pHP^SpVgB!F6am~cl+NEY1gKpPJUR2BQaAoy7zq_gToT>bGc zZ(~KRs9a*|s7jL(0+oPCDTJvKC<+iVDNI#?c5(UA;H|>93F8QB_$nK?orX&ffcb zGuiytfzJIggu^(D1A?;ckUrEr_80agm5!AEkrD!s|21RZpHNqCUxQRShxgAt!{fd| zrBGo@8&!Ihm&^@Dm+yS(Iso?Dwf}xE;4uF~i*Q{9E;(&9`_ZqTbiQ_BJ@Q2l$)O_4 z**yI0){6EQR-~o^!g{N2^Lqt{`M>v3zS%PXE6XpfBOtfi1rHsCf#FYqkT;t9{9hjL zTOolxgW;S%$++0NTkD~SX8FF)pLlIkSJi8qx@syS!4HN5y4GAB`b}eX9u;8|nx-IM zbkNq9hvf-m3eNUq-us2`Gp*?Qiv{QFaR4Hb!%~n^N^3AVwC1hvmn1p4H!zsm^Bw_^ z&*w7$kV>Vu4S#O$^?gQhWY6jCX?Cncf{ZX4-M) z2aY_?{NsyvVp1RhCIC~>%on#l$_SUY;ytK7GmOLh4=t2}9hf#fWJZG0+}xVO$SR6d z0eE$ra!v2hz|Guu81UXT#t!p;@8cb$_}=p3n{$2tFOT=1!DqbdP;=M|5C zTj(6f+<|i7?F|rFe$4^ozO};rPf6`@0^guATnPGh@9pI@ysg3hcFnE!b$z{8@7srQ z7>98fgb-p*YnU@$r>5vBc2H9fpgBBBbmZFT?*47PC>2+{XK|SSp~a+8ar4-z(eW!b z^$A_0G!c?KV{6KWPP)S?=Ir;Pbm`tW%>OVB<39r_@3sHSE%`UpG7sZ04&yKm;}GNj Y0sh%ymY!2J#{d8T07*qoM6N<$f=MF=@Bjb+ literal 0 HcmV?d00001 diff --git a/img/hero/mapping.json b/img/hero/mapping.json new file mode 100644 index 00000000..aa87e302 --- /dev/null +++ b/img/hero/mapping.json @@ -0,0 +1,199 @@ +{ + "helmet 1.gif": [ + 1, + 2, + 3 + ], + "helmet 2.gif": [ + 4, + 5, + 6 + ], + "helmet 3.gif": [ + 7, + 8, + 9 + ], + "helmet 4.gif": [ + 10, + 11, + 12 + ], + "helmet 5.gif": [ + 13, + 14, + 15 + ], + "armour 1.gif": [ + 20, + 21, + 22 + ], + "armour 2.gif": [ + 23, + 24, + 25 + ], + "armour 3.gif": [ + 26, + 27, + 28 + ], + "armour 4.gif": [ + 29, + 30, + 31 + ], + "scout.gif": [ + 40, + 41, + 42 + ], + "horn.gif": [ + 43, + 44, + 45 + ], + "bag.gif": [ + 46, + 47, + 48 + ], + "map.gif": [ + 49, + 50, + 51 + ], + "item64.gif": [ + 52, + 53, + 54 + ], + "item67.gif": [ + 55, + 56, + 57 + ], + "shoes 1.gif": [ + 60, + 61, + 62 + ], + "shoes 2.gif": [ + 63, + 64, + 65 + ], + "shoes 3.gif": [ + 66, + 67, + 68 + ], + "horse.gif": [ + 70, + 71, + 72 + ], + "sabie 1 roman legionar.gif": [ + 101, + 102, + 103 + ], + "sabie 2 roman praetorian.gif": [ + 104, + 105, + 106 + ], + "sabie 3 roman imperian.gif": [ + 107, + 108, + 109 + ], + "sabie 4 romani imperatoris.gif": [ + 110, + 111, + 112 + ], + "sabie 4 romani caesaris.gif": [ + 113, + 114, + 115 + ], + "sabie 1 teutoni clubswinger.gif": [ + 116, + 117, + 118 + ], + "sabie 2 teutoni spearman.gif": [ + 119, + 120, + 121 + ], + "sabie 2 teutoni axeman.gif": [ + 122, + 123, + 124 + ], + "Paladin teutons.gif": [ + 125, + 126, + 127 + ], + "Teutonic Knight teutons.gif": [ + 128, + 129, + 130 + ], + "Phalanx gauls.gif": [ + 131, + 132, + 133 + ], + "Swordsman gauls.gif": [ + 134, + 135, + 136 + ], + "Theutates Thunder gauls.gif": [ + 137, + 138, + 139 + ], + "Druidrider gauls.gif": [ + 140, + 141, + 142 + ], + "Haeduan teutons.gif": [ + 143, + 144, + 145 + ], + "ointment.gif": [ + 200 + ], + "scroll.gif": [ + 201 + ], + "water.gif": [ + 202 + ], + "tablet.gif": [ + 203 + ], + "book.gif": [ + 204 + ], + "artwork.gif": [ + 205 + ], + "bandage25.gif": [ + 206 + ], + "bandage33.gif": [ + 207 + ], + "cage.gif": [ + 208 + ] +} \ No newline at end of file diff --git a/install/data/constant_format.tpl b/install/data/constant_format.tpl index 51057835..86ec98ca 100644 --- a/install/data/constant_format.tpl +++ b/install/data/constant_format.tpl @@ -335,6 +335,7 @@ define("NEW_FUNCTIONS_MEDAL_10YEAR", %NEW_FUNCTIONS_MEDAL_10YEAR%); define("NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM", %NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%); define("NEW_FUNCTIONS_MILESTONES", %NEW_FUNCTIONS_MILESTONES%); define("NEW_FUNCTIONS_MEDAL_RESET", %NEW_FUNCTIONS_MEDAL_RESET%); +define("NEW_FUNCTIONS_HERO_T4", %NEW_FUNCTIONS_HERO_T4%); ////////////////////////////////////////// // **** DO NOT EDIT SETTINGS **** // diff --git a/install/process.php b/install/process.php index 595524df..a2e6a798 100644 --- a/install/process.php +++ b/install/process.php @@ -171,6 +171,7 @@ class Process { $findReplace["%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%"] = $_POST['new_functions_special_medals_system']; $findReplace["%NEW_FUNCTIONS_MILESTONES%"] = $_POST['new_functions_milestones']; $findReplace["%NEW_FUNCTIONS_MEDAL_RESET%"] = $_POST['new_functions_medal_reset']; + $findReplace["%NEW_FUNCTIONS_HERO_T4%"] = $_POST['new_functions_hero_t4']; fwrite($gameConfig, str_replace(array_keys($findReplace), array_values($findReplace), $text)); diff --git a/install/templates/config.tpl b/install/templates/config.tpl index 706971a9..d2ba7bd4 100644 --- a/install/templates/config.tpl +++ b/install/templates/config.tpl @@ -207,7 +207,8 @@ $mechs = [ 'new_functions_medal_10year' => 'Medal 10y', 'new_functions_special_medals_system' => 'Special Medals System', 'new_functions_medal_reset' => 'Medal Reset', - 'new_functions_milestones' => 'Server Milestones' + 'new_functions_milestones' => 'Server Milestones', + 'new_functions_hero_t4' => 'T4 Hero (items, adventures, auction)' ]; foreach($mechs as $k => $l){ diff --git a/var/db/struct.sql b/var/db/struct.sql index bd7cdf1e..44e16b5b 100644 --- a/var/db/struct.sql +++ b/var/db/struct.sql @@ -951,6 +951,8 @@ CREATE TABLE IF NOT EXISTS `%PREFIX%hero` ( `level` tinyint(3) NULL, `points` int(3) NULL, `experience` int(11) NULL, + `silver` int(11) NOT NULL DEFAULT 0, + `production` tinyint(1) NOT NULL DEFAULT 0, `dead` tinyint(1) NULL, `health` float(12,9) NULL, `attack` tinyint(3) NULL, @@ -976,6 +978,76 @@ CREATE TABLE IF NOT EXISTS `%PREFIX%hero` ( +-- -------------------------------------------------------- + +-- +-- Table structure for table `%prefix%hero_items` (T4 hero port) +-- + +CREATE TABLE IF NOT EXISTS `%PREFIX%hero_items` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uid` int(11) NOT NULL, + `heroid` int(11) NOT NULL DEFAULT 0, + `itemid` smallint(5) NOT NULL, + `slot` tinyint(2) NOT NULL DEFAULT 0, + `stat_value` int(11) NOT NULL DEFAULT 0, + `quantity` int(11) NOT NULL DEFAULT 1, + `equipped` tinyint(1) NOT NULL DEFAULT 0, + `tstamp` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `uid_equipped` (`uid`,`equipped`), + KEY `uid_slot` (`uid`,`slot`), + KEY `itemid` (`itemid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `%prefix%hero_adventure` (T4 hero port) +-- + +CREATE TABLE IF NOT EXISTS `%PREFIX%hero_adventure` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `uid` int(11) NOT NULL, + `wref` int(11) NOT NULL DEFAULT 0, + `difficulty` tinyint(1) NOT NULL DEFAULT 0, + `duration` int(11) NOT NULL DEFAULT 0, + `created` int(11) NOT NULL DEFAULT 0, + `expire` int(11) NOT NULL DEFAULT 0, + `status` tinyint(1) NOT NULL DEFAULT 0, + `moveid` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `uid_status` (`uid`,`status`), + KEY `expire` (`expire`), + KEY `moveid` (`moveid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `%prefix%auction` (T4 hero port) +-- + +CREATE TABLE IF NOT EXISTS `%PREFIX%auction` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `seller` int(11) NOT NULL DEFAULT 0, + `itemid` smallint(5) NOT NULL, + `slot` tinyint(2) NOT NULL DEFAULT 0, + `stat_value` int(11) NOT NULL DEFAULT 0, + `quantity` int(11) NOT NULL DEFAULT 1, + `silver_start` int(11) NOT NULL DEFAULT 0, + `silver_current` int(11) NOT NULL DEFAULT 0, + `bidder` int(11) NOT NULL DEFAULT 0, + `bid_max` int(11) NOT NULL DEFAULT 0, + `created` int(11) NOT NULL DEFAULT 0, + `time_end` int(11) NOT NULL DEFAULT 0, + `status` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `status_end` (`status`,`time_end`), + KEY `bidder` (`bidder`), + KEY `seller` (`seller`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + -- -------------------------------------------------------- -- diff --git a/var/tools/generate_hero_sprites.php b/var/tools/generate_hero_sprites.php new file mode 100644 index 00000000..95877bc1 --- /dev/null +++ b/var/tools/generate_hero_sprites.php @@ -0,0 +1,27 @@ + $def) { + $x = ($idx % $cols) * $cell; + $y = intdiv($idx, $cols) * $cell; + $css .= ".heroT4Item.item{$iid}{background-position:-{$x}px -{$y}px;} /* {$def['name']} */\n"; + $idx++; +} +file_put_contents(__DIR__ . '/../../css/hero_items.css', $css); +echo "css/hero_items.css written: $idx items\n";