mirror of
https://github.com/Shadowss/TravianZ.git
synced 2026-07-02 02:24:21 +00:00
Added Milestone On Install
Added Milestone On Install
This commit is contained in:
@@ -116,6 +116,7 @@ $editIcon = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke=
|
||||
<tr><td>Medal Veteran Player 5a <em class="tooltip">?<span class="classic">5 years</span></em></td><td><?php echo NEW_FUNCTIONS_MEDAL_5YEAR ? "<span class='badge green'>Enabled</span>" : "<span class='badge red'>Disabled</span>"; ?></td></tr>
|
||||
<tr><td>Medal Veteran Player 10a <em class="tooltip">?<span class="classic">10 years</span></em></td><td><?php echo NEW_FUNCTIONS_MEDAL_10YEAR ? "<span class='badge green'>Enabled</span>" : "<span class='badge red'>Disabled</span>"; ?></td></tr>
|
||||
<tr><td>Special Medals<em class="tooltip">?<span class="classic">Special Medals</span></em></td><td><?php echo NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM ? "<span class='badge green'>Enabled</span>" : "<span class='badge red'>Disabled</span>"; ?></td></tr>
|
||||
<tr><td>Server Milestones<em class="tooltip">?<span class="classic">Server Milestones</span></em></td><td><?php echo NEW_FUNCTIONS_MILESTONES ? "<span class='badge green'>Enabled</span>" : "<span class='badge red'>Disabled</span>"; ?></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -211,6 +211,17 @@ if($_SESSION['access'] < 9) die(ACCESS_DENIED_ADMIN);
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="b">Server Milestones <em class="tooltip">?<span class="classic">Enable (Disable) the "Server Milestones" widget (first player to settle a 2nd village, reach 1000 population, capture an artefact, conquer a WW, conquer a WW building plan, found an alliance, or conquer a village from another player) shown at the top of Statistics » General</span></em>
|
||||
<?php if (!defined('NEW_FUNCTIONS_MILESTONES')): ?><br><span style="color:#c0392b;font-size:11px;font-weight:normal;text-transform:none;">Not present in config.php yet — saving this form once will add it (defaults to False until then).</span><?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<select name="new_functions_milestones">
|
||||
<option value="True" <?php if(defined('NEW_FUNCTIONS_MILESTONES') && NEW_FUNCTIONS_MILESTONES == true) echo "selected";?>>True</option>
|
||||
<option value="False" <?php if(!defined('NEW_FUNCTIONS_MILESTONES') || NEW_FUNCTIONS_MILESTONES == false) echo "selected";?>>False</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@ $deletedArtifacts = $database->getDeletedArtifacts();
|
||||
<style>
|
||||
.nat-wrap{max-width:1150px;margin:18px auto;font-family:Verdana;font-size:12px}
|
||||
.nat-head{display:flex;align-items:center;gap:8px;margin-bottom:14px}
|
||||
.nat-head h1{margin:0;font-size:18px;color:#2c3e50}
|
||||
.nat-head h1{margin:0;font-size:18px;color:#ffffff} /* MODIFICAT */
|
||||
.nat-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:20px}
|
||||
@media(max-width:950px){.nat-grid{grid-template-columns:1fr}}
|
||||
.nat-card{background:#fff;border:1px solid #ddd;border-radius:8px;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.05)}
|
||||
@@ -35,7 +35,8 @@ $deletedArtifacts = $database->getDeletedArtifacts();
|
||||
.nat-card button:hover{background:#1a68d1}
|
||||
.nat-table{width:100%;border-collapse:collapse;font-size:12px;margin-top:10px}
|
||||
.nat-table th{background:#34495e;color:#fff;padding:6px;text-align:left;font-weight:normal}
|
||||
.nat-table td{padding:6px;border-bottom:1px solid #eee;text-align:center}
|
||||
.nat-table td{padding:6px;border-bottom:1px solid #eee;text-align:center;color:#000000} /* MODIFICAT */
|
||||
.nat-table td a{color:#000000;text-decoration:none} /* ADAUGAT */
|
||||
.nat-table tr:hover{background:#f9f9f9}
|
||||
.nat-table td.icon img{width:20px;height:20px}
|
||||
.bon{color:#27ae60;font-weight:bold}
|
||||
|
||||
@@ -84,7 +84,7 @@ $typeNames = [1=>'reinforcement',2=>'attack',3=>'defence',4=>'scout',5=>'trade',
|
||||
<style>
|
||||
.reports-wrap{max-width:1100px;margin:20px auto;font-family:Verdana;color:#222}
|
||||
.reports-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;flex-wrap:wrap;gap:8px}
|
||||
.reports-head h1{margin:0;font-size:18px;color:#111}
|
||||
.reports-head h1{margin:0;font-size:18px;color:#ffffff}
|
||||
.filters{display:flex;gap:6px}
|
||||
.filters a{padding:6px 10px;border:1px solid #ddd;background:#f7f7f7;border-radius:4px;font-size:12px;text-decoration:none;color:#333}
|
||||
.filters a.active{background:#333;color:#fff;border-color:#333}
|
||||
|
||||
@@ -74,7 +74,7 @@ if(isset($id)){
|
||||
|
||||
if($village && $user){
|
||||
include("search2.tpl");
|
||||
$svgEdit = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/></svg>';
|
||||
$svgEdit = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#000000" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/></svg>';
|
||||
$svgRefresh = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 3v6h-6"/></svg>';
|
||||
$svgDel = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18M8 6V4h8v2m-1 0v14a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V6h10z"/></svg>';
|
||||
?>
|
||||
|
||||
@@ -70,6 +70,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$NEW_FUNCTIONS_MEDAL_5YEAR = (NEW_FUNCTIONS_MEDAL_5YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MEDAL_10YEAR = (NEW_FUNCTIONS_MEDAL_10YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM = (NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MILESTONES = (NEW_FUNCTIONS_MILESTONES == false ? 'false' : 'true');
|
||||
|
||||
$text = file_get_contents("constant_format.tpl");
|
||||
$text = preg_replace("'%ERRORREPORT%'", $ERRORREPORT, $text);
|
||||
@@ -161,6 +162,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_5YEAR%'", $NEW_FUNCTIONS_MEDAL_5YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_10YEAR%'", $NEW_FUNCTIONS_MEDAL_10YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $NEW_FUNCTIONS_MILESTONES, $text);
|
||||
|
||||
// PLUS settings need to be kept intact
|
||||
$text = preg_replace("'%PLUS_TIME%'", PLUS_TIME, $text);
|
||||
|
||||
@@ -69,6 +69,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$NEW_FUNCTIONS_MEDAL_5YEAR = (NEW_FUNCTIONS_MEDAL_5YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MEDAL_10YEAR = (NEW_FUNCTIONS_MEDAL_10YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM = (NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MILESTONES = (NEW_FUNCTIONS_MILESTONES == false ? 'false' : 'true');
|
||||
|
||||
$text = file_get_contents("constant_format.tpl");
|
||||
$text = preg_replace("'%ERRORREPORT%'", ERROR_REPORT, $text);
|
||||
@@ -158,6 +159,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_5YEAR%'", $NEW_FUNCTIONS_MEDAL_5YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_10YEAR%'", $NEW_FUNCTIONS_MEDAL_10YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $NEW_FUNCTIONS_MILESTONES, $text);
|
||||
|
||||
// PLUS settings need to be kept intact
|
||||
$text = preg_replace("'%PLUS_TIME%'", PLUS_TIME, $text);
|
||||
|
||||
@@ -65,6 +65,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$NEW_FUNCTIONS_MEDAL_5YEAR = (NEW_FUNCTIONS_MEDAL_5YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MEDAL_10YEAR = (NEW_FUNCTIONS_MEDAL_10YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM = (NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MILESTONES = (NEW_FUNCTIONS_MILESTONES == false ? 'false' : 'true');
|
||||
|
||||
$text = file_get_contents("constant_format.tpl");
|
||||
$text = preg_replace("'%ERRORREPORT%'", $ERRORREPORT, $text);
|
||||
@@ -158,6 +159,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_5YEAR%'", $NEW_FUNCTIONS_MEDAL_5YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_10YEAR%'", $NEW_FUNCTIONS_MEDAL_10YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $NEW_FUNCTIONS_MILESTONES, $text);
|
||||
|
||||
// PLUS settings need to be kept intact
|
||||
$text = preg_replace("'%PLUS_TIME%'", PLUS_TIME, $text);
|
||||
|
||||
@@ -127,6 +127,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_5YEAR%'", $_POST['new_functions_medal_5year'], $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_10YEAR%'", $_POST['new_functions_medal_10year'], $text);
|
||||
$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);
|
||||
|
||||
// PLUS settings need to be kept intact
|
||||
$text = preg_replace("'%PLUS_TIME%'", PLUS_TIME, $text);
|
||||
|
||||
@@ -71,6 +71,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$NEW_FUNCTIONS_MEDAL_5YEAR = (NEW_FUNCTIONS_MEDAL_5YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MEDAL_10YEAR = (NEW_FUNCTIONS_MEDAL_10YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM = (NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MILESTONES = (NEW_FUNCTIONS_MILESTONES == false ? 'false' : 'true');
|
||||
|
||||
$text = file_get_contents("constant_format.tpl");
|
||||
$text = preg_replace("'%ERRORREPORT%'", $ERRORREPORT, $text);
|
||||
@@ -164,6 +165,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_5YEAR%'", $NEW_FUNCTIONS_MEDAL_5YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_10YEAR%'", $NEW_FUNCTIONS_MEDAL_10YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $NEW_FUNCTIONS_MILESTONES, $text);
|
||||
|
||||
// PLUS settings need to be kept intact
|
||||
$text = preg_replace("'%PLUS_TIME%'", PLUS_TIME, $text);
|
||||
|
||||
@@ -53,6 +53,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$NEW_FUNCTIONS_MEDAL_5YEAR = (NEW_FUNCTIONS_MEDAL_5YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MEDAL_10YEAR = (NEW_FUNCTIONS_MEDAL_10YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM = (NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MILESTONES = (NEW_FUNCTIONS_MILESTONES == false ? 'false' : 'true');
|
||||
|
||||
// SERVER SETTINGS - we need to keep these intact
|
||||
$text = preg_replace("'%ERRORREPORT%'", ERROR_REPORT, $text);
|
||||
@@ -142,6 +143,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_5YEAR%'", $NEW_FUNCTIONS_MEDAL_5YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_10YEAR%'", $NEW_FUNCTIONS_MEDAL_10YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $NEW_FUNCTIONS_MILESTONES, $text);
|
||||
|
||||
// PLUS SETTINGS
|
||||
$text = preg_replace("'%PLUS_TIME%'", $_POST['plus_time'], $text);
|
||||
|
||||
@@ -64,6 +64,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$NEW_FUNCTIONS_MEDAL_5YEAR = (NEW_FUNCTIONS_MEDAL_5YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MEDAL_10YEAR = (NEW_FUNCTIONS_MEDAL_10YEAR == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM = (NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM == false ? 'false' : 'true');
|
||||
$NEW_FUNCTIONS_MILESTONES = (NEW_FUNCTIONS_MILESTONES == false ? 'false' : 'true');
|
||||
|
||||
$text = file_get_contents("constant_format.tpl");
|
||||
$text = preg_replace("'%ERRORREPORT%'", $_POST['error'], $text);
|
||||
@@ -153,6 +154,7 @@ $fh = fopen($myFile, 'w') or die("<br/><br/><br/>Can't open file: GameEngine\con
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_5YEAR%'", $NEW_FUNCTIONS_MEDAL_5YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MEDAL_10YEAR%'", $NEW_FUNCTIONS_MEDAL_10YEAR, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%'", $NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM, $text);
|
||||
$text = preg_replace("'%NEW_FUNCTIONS_MILESTONES%'", $NEW_FUNCTIONS_MILESTONES, $text);
|
||||
|
||||
// PLUS settings need to be kept intact
|
||||
$text = preg_replace("'%PLUS_TIME%'", PLUS_TIME, $text);
|
||||
|
||||
@@ -403,6 +403,12 @@ class Alliance {
|
||||
}
|
||||
$maxMembers = $bid18[$building->getTypeLevel(18)]['attri'];
|
||||
$aid = $database->createAlliance($tag, $name, $session->uid, $maxMembers);
|
||||
|
||||
// Milestone: first alliance ever founded on the server.
|
||||
if (defined('NEW_FUNCTIONS_MILESTONES') && NEW_FUNCTIONS_MILESTONES) {
|
||||
$database->recordMilestoneIfFirst('first_alliance', $session->uid, 0, $tag . ' - ' . $name);
|
||||
}
|
||||
|
||||
$database->updateUserField($session->uid, "alliance", $aid, 1);
|
||||
$database->procAllyPop($aid);
|
||||
$database->createAlliPermissions($session->uid, $aid, 'Alliance founder', '1', '1', '1', '1', '1', '1', '1', '1');
|
||||
|
||||
@@ -152,6 +152,23 @@ class Automation {
|
||||
$q = "UPDATE ".TB_PREFIX."vdata set pop = $popTot where wref = $vid";
|
||||
mysqli_query($database->dblink, $q);
|
||||
$owner = $database->getVillageField($vid, "owner");
|
||||
|
||||
// Milestone: first player ever to reach 1000 total population,
|
||||
// summed across all their villages. recountPop() is the single
|
||||
// funnel every population-changing event (building, demolishing,
|
||||
// founding/conquering a village) already passes through, so this
|
||||
// is the one place that's guaranteed to catch the threshold being
|
||||
// crossed regardless of which village/action caused it.
|
||||
// Excludes owner 3 (Natars) — see Artifacts::NATARS_UID — same
|
||||
// convention already used elsewhere in this file (e.g. the
|
||||
// "fix natar report by ronix" check a few hundred lines below).
|
||||
if (defined('NEW_FUNCTIONS_MILESTONES') && NEW_FUNCTIONS_MILESTONES && $owner > 0 && $owner != 3) {
|
||||
$totalPop = (int) $database->getVSumField($owner, 'pop', false);
|
||||
if ($totalPop >= 1000) {
|
||||
$database->recordMilestoneIfFirst('population_1000', $owner, $vid);
|
||||
}
|
||||
}
|
||||
|
||||
$this->procClimbers($owner);
|
||||
|
||||
return $popTot;
|
||||
@@ -1304,6 +1321,23 @@ class Automation {
|
||||
}
|
||||
|
||||
$database->setVillageFields($data['to'], ['loyalty', 'owner'], [0, $database->getVillageField($data['from'], 'owner')]);
|
||||
|
||||
// Milestones: first WW village ever conquered, and — separately —
|
||||
// first village ever conquered FROM ANOTHER PLAYER (not from
|
||||
// Natars). $to is this function's own parameter (not re-fetched),
|
||||
// so $to['natar']/$to['owner'] still reflect the village's state
|
||||
// from BEFORE this conquest, which is exactly what we need here.
|
||||
// natar==1 marks one of the 13 pre-built WW conquest targets (see
|
||||
// Artifacts::createWWVillages()) — Natars' capital and artifact/
|
||||
// plan villages are natar=0, so this check cannot misfire on those.
|
||||
if (defined('NEW_FUNCTIONS_MILESTONES') && NEW_FUNCTIONS_MILESTONES) {
|
||||
$newOwner = $database->getVillageField($data['from'], 'owner');
|
||||
if ((int)($to['natar'] ?? 0) === 1) {
|
||||
$database->recordMilestoneIfFirst('first_ww', $newOwner, $data['to']);
|
||||
} elseif ((int)($to['owner'] ?? 0) !== 3) {
|
||||
$database->recordMilestoneIfFirst('first_pvp_conquest', $newOwner, $data['to']);
|
||||
}
|
||||
}
|
||||
|
||||
$database->query("DELETE FROM " . TB_PREFIX . "abdata WHERE vref = " . (int)$data['to']);
|
||||
$database->addABTech($data['to']);
|
||||
@@ -3790,6 +3824,15 @@ class Automation {
|
||||
// Report: new village founded (issue #178)
|
||||
$ncoor = $database->getCoor($data['to']);
|
||||
$database->addNotice($to['owner'], $data['to'], 0, 24, 'New village founded', ($ncoor['x'] ?? 0) . ',' . ($ncoor['y'] ?? 0), time());
|
||||
|
||||
// Milestone: first player ever to settle their 2nd village.
|
||||
// Checked right after the new village row exists, so the
|
||||
// COUNT below already includes it.
|
||||
if (defined('NEW_FUNCTIONS_MILESTONES') && NEW_FUNCTIONS_MILESTONES) {
|
||||
if ($database->countVillages($to['owner']) == 2) {
|
||||
$database->recordMilestoneIfFirst('second_village', $to['owner'], $data['to']);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
// here must come movement from returning settlers
|
||||
$types[] = 4;
|
||||
|
||||
+114
-31
@@ -7854,6 +7854,78 @@ References: User ID/Message ID, Mode
|
||||
|
||||
return $cachedData[$vref];
|
||||
}
|
||||
|
||||
/**
|
||||
* =====================================================================
|
||||
* SERVER MILESTONES (NEW_FUNCTIONS_MILESTONES)
|
||||
* =====================================================================
|
||||
* "First player on the server to..." achievements. Each milestone_key
|
||||
* is recorded AT MOST ONCE, ever — the table's UNIQUE KEY on
|
||||
* milestone_key does the actual "first wins" enforcement at the DB
|
||||
* level via INSERT IGNORE, so this is race-condition-safe even if two
|
||||
* players' actions are processed in the same cron batch: only one
|
||||
* INSERT can ever succeed for a given key, no matter how many
|
||||
* processes attempt it concurrently.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Attempts to record a milestone. Only the very first call for a given
|
||||
* $key across the server's lifetime actually stores anything.
|
||||
*
|
||||
* @param string $key Unique milestone identifier, e.g. 'second_village'
|
||||
* @param int $uid The user who achieved it
|
||||
* @param int $vref The village where it happened (0 if not applicable)
|
||||
* @param string $extra Optional free-form context (e.g. alliance name)
|
||||
* @return bool true if THIS call is the one that recorded the milestone
|
||||
*/
|
||||
function recordMilestoneIfFirst($key, $uid, $vref = 0, $extra = '') {
|
||||
list($key, $extra) = $this->escape_input((string) $key, (string) $extra);
|
||||
list($uid, $vref) = [(int) $uid, (int) $vref];
|
||||
|
||||
$time = time();
|
||||
$q = "INSERT IGNORE INTO " . TB_PREFIX . "milestones (milestone_key, uid, vref, extra, achieved_time)
|
||||
VALUES ('$key', $uid, $vref, '$extra', $time)";
|
||||
mysqli_query($this->dblink, $q);
|
||||
|
||||
return mysqli_affected_rows($this->dblink) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array All recorded milestones, keyed by milestone_key, each
|
||||
* row enriched with the achiever's username and (if
|
||||
* applicable) the village name.
|
||||
*/
|
||||
function getMilestones() {
|
||||
$q = "SELECT m.milestone_key, m.uid, m.vref, m.extra, m.achieved_time,
|
||||
u.username, v.name AS village_name
|
||||
FROM " . TB_PREFIX . "milestones m
|
||||
LEFT JOIN " . TB_PREFIX . "users u ON u.id = m.uid
|
||||
LEFT JOIN " . TB_PREFIX . "vdata v ON v.wref = m.vref";
|
||||
$result = mysqli_query($this->dblink, $q);
|
||||
$rows = $this->mysqli_fetch_all($result);
|
||||
|
||||
$byKey = [];
|
||||
foreach ($rows as $row) {
|
||||
$byKey[$row['milestone_key']] = $row;
|
||||
}
|
||||
return $byKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $uid
|
||||
* @return int How many villages this user currently owns. Deliberately
|
||||
* uncached (always a fresh COUNT) since it's used right
|
||||
* after a village INSERT to decide a one-time milestone.
|
||||
*/
|
||||
function countVillages($uid) {
|
||||
list($uid) = $this->escape_input((int) $uid);
|
||||
|
||||
$q = "SELECT COUNT(*) AS total FROM " . TB_PREFIX . "vdata WHERE owner = $uid";
|
||||
$result = mysqli_query($this->dblink, $q);
|
||||
$row = mysqli_fetch_assoc($result);
|
||||
|
||||
return $row ? (int) $row['total'] : 0;
|
||||
}
|
||||
|
||||
function claimArtefact($vref, $ovref, $id) {
|
||||
list($vref, $ovref, $id) = $this->escape_input((int) $vref, (int) $ovref, (int) $id);
|
||||
@@ -7863,7 +7935,23 @@ References: User ID/Message ID, Mode
|
||||
|
||||
if(mysqli_query($this->dblink, $q))
|
||||
{
|
||||
$artifactID = reset($this->getOwnArtefactInfo($vref, false))['id'];
|
||||
$artifactInfo = reset($this->getOwnArtefactInfo($vref, false));
|
||||
$artifactID = $artifactInfo['id'];
|
||||
|
||||
// Milestones: first artifact EVER captured by any player (any
|
||||
// type, including the WW Building Plan), and — separately —
|
||||
// the first WW Building Plan (artefacts.type == 11) specifically.
|
||||
// claimArtefact() is the single function that transfers artifact
|
||||
// ownership (both the "conquer the artifact's own village" path
|
||||
// and the "hero carries it home" path call this), so hooking
|
||||
// here covers every capture route with no risk of missing one.
|
||||
if (defined('NEW_FUNCTIONS_MILESTONES') && NEW_FUNCTIONS_MILESTONES) {
|
||||
$this->recordMilestoneIfFirst('first_artifact', $id, $vref);
|
||||
if ((int)($artifactInfo['type'] ?? 0) === 11) {
|
||||
$this->recordMilestoneIfFirst('first_ww_plan', $id, $vref);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->addArtifactsChronology($artifactID, $id, $vref, $time);
|
||||
}
|
||||
else return false;
|
||||
@@ -8985,39 +9073,34 @@ References: User ID/Message ID, Mode
|
||||
* @return bool Return true if the query was successful, false otherwise
|
||||
*/
|
||||
|
||||
function changeCapital($wref, $mode = 1) {
|
||||
list($wref, $mode) = $this->escape_input($wref, $mode);
|
||||
function changeCapital($wref, $mode = 1){
|
||||
list($wref, $mode) = $this->escape_input($wref, $mode);
|
||||
|
||||
$wref = (int)$wref;
|
||||
$mode = (int)$mode;
|
||||
if ($mode == 1) {
|
||||
// Bug fix: this function only ever did `SET capital = $mode WHERE
|
||||
// wref = $wref` — it set the NEW capital's flag but never cleared
|
||||
// any OTHER village belonging to the same owner that was already
|
||||
// flagged capital = 1. Nothing enforces uniqueness at the schema
|
||||
// level (no UNIQUE key on owner+capital), so after any capital
|
||||
// change that doesn't delete the old village, the owner was left
|
||||
// with two (or more) rows with capital = 1. This was harmless for
|
||||
// years because nothing queried "owner=X AND capital=1" expecting
|
||||
// a single row — until getVillage(..., 3) (added for the Brewery
|
||||
// Mead-Festival empire-wide bonus lookup) started relying on that
|
||||
// invariant, at which point a stale duplicate could be the row
|
||||
// returned (no ORDER BY / LIMIT 1 on an ambiguous match), silently
|
||||
// pointing Brewery's bonus/penalty checks at the wrong village.
|
||||
$owner = $this->getVillageField($wref, 'owner');
|
||||
if ($owner !== false && $owner !== null) {
|
||||
$owner = (int) $owner;
|
||||
$q = "UPDATE " . TB_PREFIX . "vdata SET capital = 0 WHERE owner = $owner AND wref != $wref";
|
||||
mysqli_query($this->dblink, $q);
|
||||
}
|
||||
}
|
||||
|
||||
// We retrieve the owner of the current village.
|
||||
$owner = (int)$this->getVillageField($wref, 'owner');
|
||||
|
||||
if ($owner <= 0) {
|
||||
return false;
|
||||
$q = "UPDATE ".TB_PREFIX."vdata SET capital = ".$mode." WHERE wref = $wref";
|
||||
return mysqli_query($this->dblink, $q);
|
||||
}
|
||||
|
||||
// if we are setting a new capital
|
||||
if ($mode == 1) {
|
||||
|
||||
// 1. We reset ALL capitals of that owner (VERY fast)
|
||||
mysqli_query(
|
||||
$this->dblink,
|
||||
"UPDATE " . TB_PREFIX . "vdata
|
||||
SET capital = 0
|
||||
WHERE owner = $owner"
|
||||
);
|
||||
}
|
||||
|
||||
// 2. we set the new capital
|
||||
return mysqli_query(
|
||||
$this->dblink,
|
||||
"UPDATE " . TB_PREFIX . "vdata
|
||||
SET capital = $mode
|
||||
WHERE wref = $wref"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -2315,6 +2315,17 @@ tz_def('TZ_YOUR_VILLAGE_AND_YOUR_NEIGHBOURS', 'Your village and your neighbours'
|
||||
tz_def('TZ_YOU_CAN_UNDO_THE_REGISTRATION_AND', 'You can undo the registration and re-register with a');
|
||||
tz_def('TZ_YOU_CAN_USE_THIS_GOLD_FOR_PLUS_OR', '. You can use this gold for Plus or any gold advantage.');
|
||||
|
||||
// ===== Server Milestones (NEW_FUNCTIONS_MILESTONES) =====
|
||||
tz_def('TZ_SERVER_MILESTONES', 'Server Milestones');
|
||||
tz_def('TZ_MILESTONE_NOT_YET', 'Not yet achieved');
|
||||
tz_def('TZ_MILESTONE_SECOND_VILLAGE', 'First to Settle a 2nd Village');
|
||||
tz_def('TZ_MILESTONE_POPULATION_1000', 'First to reach 1,000 Population');
|
||||
tz_def('TZ_MILESTONE_FIRST_ARTIFACT', 'First to Capture an Artefact');
|
||||
tz_def('TZ_MILESTONE_FIRST_WW', 'First to Conquer a Wonder of the World');
|
||||
tz_def('TZ_MILESTONE_FIRST_WW_PLAN', 'First to Conquer a WW Building Plan');
|
||||
tz_def('TZ_MILESTONE_FIRST_ALLIANCE', 'First Alliance Founded');
|
||||
tz_def('TZ_MILESTONE_FIRST_PVP_CONQUEST', 'First Village Conquered from a Player');
|
||||
|
||||
// ===== i18n etape 2 (lot suivant) =====
|
||||
tz_def('TZ_ACCOUNT_OR_INCREASE_YOUR_RESOURCE', '-Account or increase your resource production.To do so click');
|
||||
tz_def('TZ_ADDITIONALLY_THE_TRAVIAN_TEAM_WILL', 'Additionally, the Travian Team will not provide information concerning bans to any person, other than the account owner.');
|
||||
|
||||
@@ -2604,4 +2604,15 @@ define('MANUAL_NF_D_23', "Médaille décernée aux joueurs utilisant la même ad
|
||||
define('MANUAL_NF_D_24', "Médaille décernée aux joueurs utilisant la même adresse e-mail depuis 5 ans ou plus. Peut être ajoutée à la description du profil. Cette fonction a été présentée dans Travian T4.");
|
||||
define('MANUAL_NF_D_25', "Médaille décernée aux joueurs utilisant la même adresse e-mail depuis 10 ans ou plus. Peut être ajoutée à la description du profil. Cette fonction a été présentée dans Travian T4.");
|
||||
define('MANUAL_NF_D_26', "Médaille décernée aux joueurs utilisant la même adresse e-mail depuis 10 ans ou plus. Peut être ajoutée à la description du profil. Cette fonction a été présentée dans Travian T4.");
|
||||
|
||||
// ===== Server Milestones (NEW_FUNCTIONS_MILESTONES) =====
|
||||
define('TZ_SERVER_MILESTONES', 'Jalons du serveur');
|
||||
define('TZ_MILESTONE_NOT_YET', 'Pas encore atteint');
|
||||
define('TZ_MILESTONE_SECOND_VILLAGE', 'Premier à fonder un 2e village');
|
||||
define('TZ_MILESTONE_POPULATION_1000', 'Premier à atteindre 1 000 population');
|
||||
define('TZ_MILESTONE_FIRST_ARTIFACT', 'Premier à capturer un artefact');
|
||||
define('TZ_MILESTONE_FIRST_WW', 'Premier à conquérir une Merveille du Monde');
|
||||
define('TZ_MILESTONE_FIRST_WW_PLAN', 'Premier à conquérir un Plan de Merveille');
|
||||
define('TZ_MILESTONE_FIRST_ALLIANCE', 'Première alliance fondée');
|
||||
define('TZ_MILESTONE_FIRST_PVP_CONQUEST', 'Premier village conquis sur un joueur');
|
||||
?>
|
||||
|
||||
@@ -2592,3 +2592,14 @@ define('MANUAL_NF_D_23', "Medalie acordată jucătorilor care folosesc aceeași
|
||||
define('MANUAL_NF_D_24', "Medalie acordată jucătorilor care folosesc aceeași adresă de e-mail de 5 ani sau mai mult. Poate fi adăugată la descrierea profilului. Această funcție a fost prezentată în Travian T4.");
|
||||
define('MANUAL_NF_D_25', "Medalie acordată jucătorilor care folosesc aceeași adresă de e-mail de 10 ani sau mai mult. Poate fi adăugată la descrierea profilului. Această funcție a fost prezentată în Travian T4.");
|
||||
define('MANUAL_NF_D_26', "Medalie acordată jucătorilor care folosesc aceeași adresă de e-mail de 10 ani sau mai mult. Poate fi adăugată la descrierea profilului. Această funcție a fost prezentată în Travian T4.");
|
||||
|
||||
// ===== Server Milestones (NEW_FUNCTIONS_MILESTONES) =====
|
||||
define('TZ_SERVER_MILESTONES', 'Recorduri de Server');
|
||||
define('TZ_MILESTONE_NOT_YET', 'Neatins încă');
|
||||
define('TZ_MILESTONE_SECOND_VILLAGE', 'Primul care a fondat al 2-lea sat');
|
||||
define('TZ_MILESTONE_POPULATION_1000', 'Primul care a atins 1.000 populație');
|
||||
define('TZ_MILESTONE_FIRST_ARTIFACT', 'Primul care a capturat un artefact');
|
||||
define('TZ_MILESTONE_FIRST_WW', 'Primul care a cucerit un Wonder of the World');
|
||||
define('TZ_MILESTONE_FIRST_WW_PLAN', 'Primul care a cucerit un Plan WW');
|
||||
define('TZ_MILESTONE_FIRST_ALLIANCE', 'Prima alianță fondată');
|
||||
define('TZ_MILESTONE_FIRST_PVP_CONQUEST', 'Primul sat cucerit de la un jucător');
|
||||
@@ -48,10 +48,11 @@ $current = $level > 0 ? (int)$bid35[$level]['attri'] : 0;
|
||||
</tr>
|
||||
<?php endif;?>
|
||||
</table>
|
||||
|
||||
</br>
|
||||
<?php if ($level > 0):?>
|
||||
<?php include("35_1.tpl");?>
|
||||
<?php else:?>
|
||||
</br>
|
||||
<p><b><?php echo MEAD_FESTIVAL_COMMENCE_BREWERY;?></b></p>
|
||||
<?php endif;?>
|
||||
|
||||
|
||||
@@ -16,6 +16,55 @@
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_OFF);
|
||||
|
||||
// =========================
|
||||
// SERVER MILESTONES (NEW_FUNCTIONS_MILESTONES)
|
||||
// "First player on the server to..." achievements. Data is recorded by
|
||||
// hooks placed at the relevant game events (see GameEngine/Database.php's
|
||||
// recordMilestoneIfFirst()/getMilestones(), and the hooks in
|
||||
// GameEngine/Automation.php, GameEngine/Alliance.php). This section only
|
||||
// READS what's already been recorded — it never writes anything itself.
|
||||
// =========================
|
||||
$milestonesEnabled = defined('NEW_FUNCTIONS_MILESTONES') && NEW_FUNCTIONS_MILESTONES;
|
||||
$milestonesData = $milestonesEnabled ? $database->getMilestones() : [];
|
||||
|
||||
// Lightens ($percent > 0) or darkens ($percent < 0) a "#rrggbb" color, used
|
||||
// to build each badge's radial-gradient inline (kept as plain computed hex
|
||||
// stops rather than CSS color-mix()/custom properties, for broader browser
|
||||
// compatibility with this project's existing baseline).
|
||||
function tzms_shade($hex, $percent) {
|
||||
$hex = ltrim($hex, '#');
|
||||
$r = hexdec(substr($hex, 0, 2));
|
||||
$g = hexdec(substr($hex, 2, 2));
|
||||
$b = hexdec(substr($hex, 4, 2));
|
||||
$adjust = function ($c) use ($percent) {
|
||||
$c = $percent < 0 ? $c * (1 + $percent / 100) : $c + (255 - $c) * ($percent / 100);
|
||||
return max(0, min(255, (int) round($c)));
|
||||
};
|
||||
return sprintf('#%02x%02x%02x', $adjust($r), $adjust($g), $adjust($b));
|
||||
}
|
||||
|
||||
// Simple line-icon set (inline SVG, no external image files, so there's
|
||||
// nothing that can 404 regardless of where this is deployed).
|
||||
$milestoneIcons = [
|
||||
'village' => '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 21V11.5L9 7.5L14 11.5V21" stroke="white" stroke-width="1.6" stroke-linejoin="round"/><path d="M9 21v-5h3v5" stroke="white" stroke-width="1.6" stroke-linejoin="round"/><path d="M14 13.5L18 10.5L21 13v8h-5" stroke="white" stroke-width="1.4" stroke-linejoin="round" opacity="0.85"/></svg>',
|
||||
'population' => '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8.2" r="2.6" stroke="white" stroke-width="1.5"/><circle cx="16" cy="8.2" r="2.6" stroke="white" stroke-width="1.5"/><circle cx="12" cy="7" r="2.6" stroke="white" stroke-width="1.5" opacity="0.9"/><path d="M3.3 19c.6-2.8 2.6-4.3 4.5-4.3s3.9 1.5 4.5 4.3" stroke="white" stroke-width="1.4" stroke-linecap="round"/><path d="M11.7 19c.6-2.8 2.6-4.3 4.5-4.3s3.9 1.5 4.5 4.3" stroke="white" stroke-width="1.4" stroke-linecap="round"/></svg>',
|
||||
'artifact' => '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3l4.5 3-1.7 5.5H9.2L7.5 6z" stroke="white" stroke-width="1.5" stroke-linejoin="round"/><path d="M7.5 6L12 21 16.5 6" stroke="white" stroke-width="1.5" stroke-linejoin="round"/><path d="M9.2 11.5h5.6" stroke="white" stroke-width="1.2"/></svg>',
|
||||
'wonder' => '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 2.5 14.5 6H9.5Z" stroke="white" stroke-width="1.3" stroke-linejoin="round"/><rect x="8" y="6" width="8" height="3" stroke="white" stroke-width="1.3"/><rect x="6.5" y="9" width="11" height="3" stroke="white" stroke-width="1.3"/><rect x="5" y="12" width="14" height="3.5" stroke="white" stroke-width="1.3"/><rect x="3.5" y="15.5" width="17" height="4" stroke="white" stroke-width="1.3"/><path d="M3 21h18" stroke="white" stroke-width="1.4" stroke-linecap="round"/></svg>',
|
||||
'plan' => '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6 4h9l4 4v12H6z" stroke="white" stroke-width="1.5" stroke-linejoin="round"/><path d="M15 4v4h4" stroke="white" stroke-width="1.5" stroke-linejoin="round"/><path d="M8.5 11h7M8.5 14h7M8.5 17h4.5" stroke="white" stroke-width="1.3" stroke-linecap="round"/></svg>',
|
||||
'alliance' => '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3l7 3v5.5c0 4.5-3 7.7-7 9-4-1.3-7-4.5-7-9V6z" stroke="white" stroke-width="1.5" stroke-linejoin="round"/><path d="M8.5 12l2.3 2.3L15.8 9.5" stroke="white" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>',
|
||||
'conquest' => '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 4l7 7M4 4l1 3.2L7.2 8" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 4l-7 7M20 4l-1 3.2L16.8 8" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 20l6.5-6.5M21 20l-6.5-6.5" stroke="white" stroke-width="1.6" stroke-linecap="round"/><path d="M11 13l1 1 1-1" stroke="white" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>',
|
||||
];
|
||||
|
||||
$milestoneDefs = [
|
||||
'second_village' => ['label' => TZ_MILESTONE_SECOND_VILLAGE, 'icon' => 'village', 'color' => '#c0783c'],
|
||||
'population_1000' => ['label' => TZ_MILESTONE_POPULATION_1000, 'icon' => 'population', 'color' => '#3c78c0'],
|
||||
'first_artifact' => ['label' => TZ_MILESTONE_FIRST_ARTIFACT, 'icon' => 'artifact', 'color' => '#8a4fc0'],
|
||||
'first_ww' => ['label' => TZ_MILESTONE_FIRST_WW, 'icon' => 'wonder', 'color' => '#c0a030'],
|
||||
'first_ww_plan' => ['label' => TZ_MILESTONE_FIRST_WW_PLAN, 'icon' => 'plan', 'color' => '#2f9e8f'],
|
||||
'first_alliance' => ['label' => TZ_MILESTONE_FIRST_ALLIANCE, 'icon' => 'alliance', 'color' => '#3fa14a'],
|
||||
'first_pvp_conquest' => ['label' => TZ_MILESTONE_FIRST_PVP_CONQUEST, 'icon' => 'conquest', 'color' => '#b6362f'],
|
||||
];
|
||||
|
||||
// =========================
|
||||
// TRIBES COUNT
|
||||
// =========================
|
||||
@@ -162,6 +211,86 @@ return isset($units[$k])? (int)$units[$k] : 0;
|
||||
}
|
||||
?>
|
||||
|
||||
<?php if ($milestonesEnabled): ?>
|
||||
<!-- ================= SERVER MILESTONES ================= -->
|
||||
<div class="tzms-wrap">
|
||||
<div class="tzms-title"><?php echo TZ_SERVER_MILESTONES; ?></div>
|
||||
<div class="tzms-row">
|
||||
<?php foreach ($milestoneDefs as $mkey => $mdef):
|
||||
$achieved = $milestonesData[$mkey] ?? null;
|
||||
$light = tzms_shade($mdef['color'], 28);
|
||||
$dark = tzms_shade($mdef['color'], -32);
|
||||
$bg = $achieved
|
||||
? sprintf('background:radial-gradient(circle at 32%% 28%%, %s, %s 55%%, %s 100%%);border-color:rgba(255,255,255,0.35);', $light, $mdef['color'], $dark)
|
||||
: 'background:#d8d8d8;border-color:rgba(0,0,0,0.08);';
|
||||
$title = htmlspecialchars($mdef['label'], ENT_QUOTES, 'UTF-8');
|
||||
$meta = $achieved
|
||||
? htmlspecialchars(($achieved['username'] ?: '-') . ' — ' . date('d.m.y G:i:s', (int)$achieved['achieved_time']), ENT_QUOTES, 'UTF-8')
|
||||
: htmlspecialchars(TZ_MILESTONE_NOT_YET, ENT_QUOTES, 'UTF-8');
|
||||
?>
|
||||
<div class="tzms-badge<?php echo $achieved ? ' tzms-achieved' : ' tzms-locked'; ?>"
|
||||
data-title="<?php echo $title; ?>"
|
||||
data-meta="<?php echo $meta; ?>"
|
||||
onmouseover="tzmsShow(this)"
|
||||
onmouseout="tzmsHide()">
|
||||
<?php if ($achieved): ?><a href="spieler.php?uid=<?php echo (int)$achieved['uid']; ?>" class="tzms-icon" style="<?php echo $bg; ?>"><?php echo $milestoneIcons[$mdef['icon']] ?? ''; ?></a>
|
||||
<?php else: ?><span class="tzms-icon" style="<?php echo $bg; ?>"><?php echo $milestoneIcons[$mdef['icon']] ?? ''; ?></span>
|
||||
<?php endif; ?>
|
||||
<div class="tzms-caption"><?php echo $achieved ? htmlspecialchars($achieved['username'] ?: '', ENT_QUOTES, 'UTF-8') : ''; ?></div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tzms_tooltip" class="tzms-tooltip"></div>
|
||||
|
||||
<style>
|
||||
.tzms-wrap{max-width:760px;margin:0 auto 16px;text-align:center;padding:12px 10px 6px;background:#fbfbfb;border:1px solid #ddd;border-radius:6px;}
|
||||
.tzms-title{font-size:11px;font-weight:bold;letter-spacing:1.5px;color:#8a8a8a;text-transform:uppercase;margin-bottom:12px;}
|
||||
.tzms-row{display:flex;justify-content:center;gap:20px;flex-wrap:wrap;}
|
||||
.tzms-badge{position:relative;width:64px;}
|
||||
.tzms-icon{display:flex;align-items:center;justify-content:center;width:56px;height:56px;margin:0 auto;border-radius:50%;border:2px solid;box-shadow:0 2px 6px rgba(0,0,0,0.3), inset 0 1px 2px rgba(255,255,255,0.35);text-decoration:none;transition:transform .12s ease;}
|
||||
.tzms-badge.tzms-achieved{cursor:pointer;}
|
||||
.tzms-badge.tzms-achieved:hover .tzms-icon{transform:scale(1.08);}
|
||||
.tzms-badge.tzms-locked .tzms-icon{opacity:0.6;box-shadow:none;}
|
||||
.tzms-badge.tzms-locked svg{opacity:0.55;}
|
||||
.tzms-icon svg{width:28px;height:28px;}
|
||||
.tzms-caption{margin-top:5px;font-size:10px;color:#666;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:64px;margin-left:auto;margin-right:auto;}
|
||||
.tzms-tooltip{display:none;position:absolute;z-index:9999;background:#1b1b1b;color:#fff;padding:8px 12px;border-radius:5px;font-size:12px;line-height:1.5;box-shadow:0 3px 10px rgba(0,0,0,0.4);max-width:260px;text-align:left;}
|
||||
.tzms-tip-title{font-weight:bold;color:#fff;}
|
||||
.tzms-tip-meta{color:#b7b7b7;font-size:11px;margin-top:2px;}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
function tzmsShow(el) {
|
||||
var tip = document.getElementById('tzms_tooltip');
|
||||
if (!tip) return;
|
||||
var title = document.createElement('div');
|
||||
title.className = 'tzms-tip-title';
|
||||
title.textContent = el.getAttribute('data-title') || '';
|
||||
var meta = document.createElement('div');
|
||||
meta.className = 'tzms-tip-meta';
|
||||
meta.textContent = el.getAttribute('data-meta') || '';
|
||||
tip.innerHTML = '';
|
||||
tip.appendChild(title);
|
||||
tip.appendChild(meta);
|
||||
tip.style.display = 'block';
|
||||
|
||||
var rect = el.getBoundingClientRect();
|
||||
var scrollY = window.pageYOffset || document.documentElement.scrollTop;
|
||||
var scrollX = window.pageXOffset || document.documentElement.scrollLeft;
|
||||
var top = rect.bottom + scrollY + 8;
|
||||
var left = rect.left + scrollX;
|
||||
var maxLeft = document.documentElement.clientWidth - 270;
|
||||
if (left > maxLeft) left = Math.max(8, maxLeft);
|
||||
tip.style.top = top + 'px';
|
||||
tip.style.left = left + 'px';
|
||||
}
|
||||
function tzmsHide() {
|
||||
var tip = document.getElementById('tzms_tooltip');
|
||||
if (tip) tip.style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- ================= WORLD STATS ================= -->
|
||||
<table cellpadding="1" cellspacing="1" id="world_player" class="world">
|
||||
<thead><tr><th colspan="2"><?php echo TZ_WORLD_STATS;?></th></tr>
|
||||
|
||||
@@ -333,6 +333,7 @@ define("NEW_FUNCTIONS_MEDAL_3YEAR", %NEW_FUNCTIONS_MEDAL_3YEAR%);
|
||||
define("NEW_FUNCTIONS_MEDAL_5YEAR", %NEW_FUNCTIONS_MEDAL_5YEAR%);
|
||||
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%);
|
||||
|
||||
|
||||
//////////////////////////////////////////
|
||||
@@ -392,7 +393,7 @@ $requse = 0;
|
||||
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
|
||||
## --------------------------------------------------------------------------- ##
|
||||
## Filename config.php ##
|
||||
## Version 4.8.5 ##
|
||||
## Version 10.0 Full Refactor & Security ##
|
||||
## Developed by: Dzoki and Dixie Edited by Advocaite ##
|
||||
## License: TravianZ Project ##
|
||||
## Copyright: TravianZ (c) 2010-2026. All rights reserved. ##
|
||||
|
||||
@@ -169,6 +169,7 @@ class Process {
|
||||
$findReplace["%NEW_FUNCTIONS_MEDAL_5YEAR%"] = $_POST['new_functions_medal_5year'];
|
||||
$findReplace["%NEW_FUNCTIONS_MEDAL_10YEAR%"] = $_POST['new_functions_medal_10year'];
|
||||
$findReplace["%NEW_FUNCTIONS_SPECIAL_MEDALS_SYSTEM%"] = $_POST['new_functions_special_medals_system'];
|
||||
$findReplace["%NEW_FUNCTIONS_MILESTONES%"] = $_POST['new_functions_milestones'];
|
||||
|
||||
fwrite($gameConfig, str_replace(array_keys($findReplace), array_values($findReplace), $text));
|
||||
|
||||
|
||||
@@ -206,7 +206,8 @@ $mechs = [
|
||||
'new_functions_medal_3year' => 'Medal 3y',
|
||||
'new_functions_medal_5year' => 'Medal 5y',
|
||||
'new_functions_medal_10year' => 'Medal 10y',
|
||||
'new_functions_special_medals_system' => 'Special Medals System'
|
||||
'new_functions_special_medals_system' => 'Special Medals System',
|
||||
'new_functions_milestones' => 'Server Milestones'
|
||||
];
|
||||
|
||||
foreach($mechs as $k => $l){
|
||||
|
||||
+22
-1
@@ -1856,4 +1856,25 @@ CREATE TABLE IF NOT EXISTS `%PREFIX%debug_log` (
|
||||
-- Dumping data for table `%PREFIX%debug_log`
|
||||
--
|
||||
INSERT INTO `%PREFIX%debug_log` (`id`, `active`, `lvl_warning`, `lvl_notice`, `lvl_deprecated`, `lvl_fatal`, `max_size_mb`, `auto_off_hours`, `started_by`, `started_at`) VALUES
|
||||
(1, 0, 1, 1, 1, 1, 5, 6, NULL, NULL);
|
||||
(1, 0, 1, 1, 1, 1, 5, 6, NULL, NULL);
|
||||
|
||||
--
|
||||
-- Table structure for `%PREFIX%milestones`
|
||||
--
|
||||
-- Records server "first player to..." achievements (NEW_FUNCTIONS_MILESTONES).
|
||||
-- One row per milestone_key, EVER: the UNIQUE KEY makes the very first
|
||||
-- INSERT for a given key the permanent, race-condition-safe winner — any
|
||||
-- later attempt to insert the same key is silently ignored by the DB itself.
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `%PREFIX%milestones` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`milestone_key` varchar(50) NOT NULL,
|
||||
`uid` int(11) NULL,
|
||||
`vref` int(11) NULL DEFAULT '0',
|
||||
`extra` varchar(100) NULL DEFAULT '',
|
||||
`achieved_time` int(11) NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `milestone_key` (`milestone_key`),
|
||||
KEY `uid` (`uid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
Reference in New Issue
Block a user