This commit is contained in:
Catalin Novgorodschi
2026-02-11 15:17:21 +02:00
17 changed files with 2459 additions and 989 deletions
+37
View File
@@ -0,0 +1,37 @@
# Git files
.git
.gitignore
.gitattributes
# Docker files
Dockerfile
docker-compose.yml
.dockerignore
.env
.env.example
# Documentation
README.md
DOCKER_README.md
CODE_OF_CONDUCT.md
CONTRIBUTING.md
ISSUE_TEMPLATE.md
LICENSE
# IDE and editor files
.idea
.vscode
*.swp
*.swo
*~
# OS files
.DS_Store
Thumbs.db
# Logs
*.log
# Temporary files
tmp/
temp/
+11
View File
@@ -0,0 +1,11 @@
# MySQL Database Configuration
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=travian
MYSQL_USER=travianz
MYSQL_PASSWORD=travianzpass
# Application Configuration
# These values will be used during the installation wizard
# Hostname for database connection (use 'db' when running in Docker)
DB_HOST=db
DB_PORT=3306
+3
View File
@@ -39,3 +39,6 @@ GameEngine/Admin/Mods/constant_format.tpl
# These 2 files change frequently on the server but not in the repository code.
Templates/text.tpl
var/log/access.log
# Docker environment file with sensitive credentials
.env
+410 -116
View File
@@ -10,8 +10,10 @@
## ##
#################################################################################
// ----------------- PHP (logic unchanged, except $pixelDiv = 255) -----------------
$check1 = $check2 = $check3 = "";
$includeSize = true;
$criteria = "";
if (isset($_POST['show1']) || isset($_POST['show2']) || isset($_POST['show3'])) {
$check1 = isset($_POST['show1']) ? "checked " : "";
@@ -44,127 +46,419 @@ if (isset($_POST['show1']) || isset($_POST['show2']) || isset($_POST['show3']))
}
}
if ($check1 == "" && $check2 == "" && $check3 == "") $criteria = "";
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><?php echo SERVER_NAME; ?> Map</title>
<link rel="stylesheet" href="../img/admin/map.css" type="text/css" media="all">
<div id="start">
<div class="clear">
<h2 class="left"><?php echo SERVER_NAME;?> Map!</h2>
</div>
<p>This is the map of <?php echo SERVER_NAME;?>. Search and find players.</p>
<h1>Show Option</h1>
<form id="show" name="show" action="admin.php?p=map" method="POST">
<table width="70%">
<tr><td>
<input id="show1" name="show1" type="checkbox" <?php echo $check1;?>value="1">Players
</td>
<td>
<input id="show2" name="show2" type="checkbox" <?php echo $check2;?>value="2"><?php echo TRIBE5; ?>
</td>
<td>
<input id="show3" name="show3" type="checkbox" <?php echo $check3;?>value="2">Artifacts
</td>
<td>
<input id="btnshow" type="button" value="Show" style="font-size:9px" onclick=submit()>
</td>
</tr>
</table>
</form>
</div>
<style>
:root { --map-width:510px; --map-height:510px; }
<div id="kaart">
/* Container */
#map {
width: 510px; height: 510px; position: relative; overflow: hidden;
border: 1px solid #333; background: #f3f3f3;
}
#map.grab { cursor: grab; } #map.grabbing { cursor: grabbing; }
<div id="map" title="">
/* World layer that we translate/scale */
#map_bg {
position:absolute; inset:0; width:var(--map-width); height:var(--map-height);
transform-origin:0 0; will-change:transform; transition: transform 80ms ease;
--zoom:1;
}
<div class="zoomlevels">
<span id="zl">-<?php echo WORLD_MAX;?></span>
<span id="zr"><?php echo WORLD_MAX;?></span>
<span id="zb"><?php echo WORLD_MAX;?></span>
<span id="zo">-<?php echo WORLD_MAX;?></span>
<span id="zc">(0,0)</span>
<div id="lijn_hor"></div>
<div id="lijn_ver"></div>
</div>
/* CRISP GRID (SVG lines) */
#gridSvg { position:absolute; inset:0; pointer-events:none; }
#gridSvg line { vector-effect: non-scaling-stroke; shape-rendering: crispEdges; }
#gridSvg .minor { stroke: rgba(0,0,0,0.12); stroke-width: 1; }
#gridSvg .major { stroke: rgba(0,0,0,0.25); stroke-width: 1; }
<div style="top: 0px; left: 0px;" id="map_bg">
<?php
if($criteria != ""){
$artifactsEffect = ['-', VILLAGE_EFFECT, ACCOUNT_EFFECT, UNIQUE_EFFECT];
$array_tribe = ['-', TRIBE1, TRIBE2, TRIBE3, TRIBE4, TRIBE5, TRIBE6];
$q = "SELECT v.wref, v.owner, v.name, v.capital, v.pop, u.username, u.tribe, u.access, w.x, w.y".($includeSize ? ", a.size" : "")." FROM ".TB_PREFIX."vdata AS v LEFT JOIN ".TB_PREFIX."users AS u ON v.owner = u.id LEFT JOIN ".TB_PREFIX."wdata AS w ON v.wref = w.id ".$criteria;
$player_info = $database->query_return($q);
foreach($player_info as $p_array) {
$p_name = htmlspecialchars(mysqli_escape_string($database->dblink, $p_array['username']));
$p_village = htmlspecialchars(mysqli_escape_string($database->dblink, $p_array['name']));
$p_coor = "(".$p_array['x']."|".$p_array['y'].")";
$p_pop = $p_array['pop'];
$p_tribe = $array_tribe[$p_array['tribe']];
$p_info ="<a href=\"?p=village&did=".$p_array['wref']."\" target=\"_blank\"><img src=\"../img/admin/map_".(isset($p_array['size']) ? "1".$p_array['size'] : ($p_array['access'] != ADMIN ? $p_array['tribe'] : 0)).".gif\" border=\"0\" onmouseout=\"med_closeDescription()\" onmousemove=\"med_mouseMoveHandler(arguments[0],'<ul class=\'p_info\'><li>Player name: <b>$p_name</b></li><li>Village name : <b>$p_village</b></li><li>Coordinate: <b>$p_coor</b></li><li>Population: <b>$p_pop</b></li><li>Tribe: <b>$p_tribe</b></li>".($check3 != "" && isset($p_array['size']) ? "<li>Artifact effect: <b>".$artifactsEffect[$p_array['size']]."</b></li>" : "")."</ul>')\"></a>";
//245px = 0
$pixelDiv = 245;
$xdiv = $pixelDiv / WORLD_MAX;
if($p_array['x'] <= 0) $p_x = $pixelDiv - intval(abs($p_array['x']) * $xdiv); //-x
elseif($p_array['x'] >= 0) $p_x = $pixelDiv + intval(abs($p_array['x']) * $xdiv); //+x
if($p_array['y'] <= 0) $p_y = $pixelDiv + intval(abs($p_array['y']) * $xdiv); //-y
elseif($p_array['y'] >= 0) $p_y = $pixelDiv - intval(abs($p_array['y']) * $xdiv); //+y
echo '<div style="left:'.$p_x.'px; top:'.$p_y.'px; position:absolute">'.$p_info.'</div>';
}
}
?>
</div>
</div>
<div id="legenda">
<div class="content">
<h3>Legend</h3>
<div id="items">
<div class="first"></div>
<table>
<tr>
<td><img src="../img/admin/map_1.gif" height="11" width="11"></td><td class="show"><?php echo TRIBE1;?></td>
</tr>
<tr>
<td><img src="../img/admin/map_2.gif" height="11" width="11"></td><td class="show"><?php echo TRIBE2;?></td>
</tr>
<tr>
<td><img src="../img/admin/map_3.gif" height="11" width="11"></td><td class="show"><?php echo TRIBE3;?></td>
</tr>
<tr>
<td><img src="../img/admin/map_5.gif" height="11" width="11"></td><td class="show"><?php echo TRIBE5;?></td>
</tr>
<tr>
<td><img src="../img/admin/map_0.gif" height="11" width="11"></td><td class="show">Multihunters</td>
</tr>
</table>
</div>
</div>
</div>
/* Markers centered on coords; keep constant size visually */
.marker { position:absolute; transform: translate(-50%,-50%); }
#map_bg .pin { transform-origin:center; transform: scale(calc(1 / var(--zoom))); cursor:pointer; }
<div id="legenda">
<div class="content">
<h3>Artifacts Legend</h3>
<div id="items">
<div class="first"></div>
<table>
<tr>
<td><img src="../img/admin/map_11.gif" height="11" width="11"></td><td class="show"><?php echo VILLAGE_EFFECT;?></td>
</tr>
<tr>
<td><img src="../img/admin/map_12.gif" height="11" width="11"></td><td class="show"><?php echo ACCOUNT_EFFECT;?></td>
</tr>
<tr>
<td><img src="../img/admin/map_13.gif" height="11" width="11"></td><td class="show"><?php echo UNIQUE_EFFECT;?></td>
</tr>
</table>
</div>
</div>
</div>
</div>
/* Zoom controls */
.zoom-controls {
position:absolute; top:8px; right:8px; display:flex; gap:6px; align-items:center;
z-index:1000; background:rgba(255,255,255,.9); padding:6px 8px; border-radius:8px;
box-shadow:0 2px 6px rgba(0,0,0,.15); user-select:none;
}
.zoom-controls button { font-size:12px; padding:6px 10px; border-radius:6px; border:1px solid #ccc; background:#f7f7f7; cursor:pointer; }
.zoom-controls span { min-width:56px; text-align:center; font:12px system-ui,Arial; }
/* Axis labels & crosshair */
.zoomlevels span { position:absolute; font:12px Arial; color:#000; }
#zl { left:2px; top:50%; transform:translateY(-50%); }
#zr { right:2px; top:50%; transform:translateY(-50%); }
#zo { right:50%; top:2px; transform:translateX(50%); }
#zb { right:50%; bottom:2px; transform:translateX(50%); }
#zc { left:50%; top:50%; transform:translate(-50%,-50%); background:rgba(255,255,255,.85); padding:2px 4px; border-radius:4px; }
#lijn_hor,#lijn_ver { position:absolute; background:#666; opacity:.5; }
#lijn_hor { left:0; right:0; top:50%; height:1px; }
#lijn_ver { top:0; bottom:0; left:50%; width:1px; }
/* Ensure tooltip list looks same for hover & sticky */
.p_info { list-style:none; margin:0; padding:0; }
.p_info li { margin:2px 0; }
/* Buttons inside tooltip */
.p_actions { margin-top:6px; display:flex; gap:6px; }
.p_btn {
display:inline-block; font:12px/1 Arial; padding:4px 8px;
border:1px solid #bbb; border-radius:6px; background:#fff; color:#2a2a2a; text-decoration:none;
}
.p_btn:hover { background:#f2f2f2; }
/* Sticky tooltip (same style family as your hover) */
#tipBackdrop { position:absolute; inset:0; display:none; z-index:1099; background:transparent; }
#stickyTip {
position:absolute; z-index:1100; display:none; max-width:320px;
background:#fff; border:1px solid #aaa; border-radius:8px;
box-shadow:0 8px 24px rgba(0,0,0,.25); padding:8px 10px;
}
</style>
</head>
<body>
<div id="start">
<div class="clear"><h2 class="left"><?php echo SERVER_NAME;?> Map!</h2></div>
<p>This is the map of <?php echo SERVER_NAME;?>. Search and find players.</p>
<h1>Show Option</h1>
<form id="show" name="show" action="admin.php?p=map" method="POST">
<table width="70%"><tr>
<td><input id="show1" name="show1" type="checkbox" <?php echo $check1;?> value="1">Players</td>
<td><input id="show2" name="show2" type="checkbox" <?php echo $check2;?> value="2"><?php echo TRIBE5; ?></td>
<td><input id="show3" name="show3" type="checkbox" <?php echo $check3;?> value="2">Artifacts</td>
<td><input id="btnshow" type="submit" value="Show" style="font-size:9px"></td>
</tr></table>
</form>
</div>
<div id="kaart">
<div id="map" class="grab" title="">
<!-- Zoom UI -->
<div class="zoom-controls">
<button type="button" id="zoomOut"></button>
<span id="zoomLabel">100%</span>
<button type="button" id="zoomIn">+</button>
<button type="button" id="zoomReset">Reset</button>
</div>
<div class="zoomlevels">
<span id="zl">-<?php echo WORLD_MAX;?></span>
<span id="zr"><?php echo WORLD_MAX;?></span>
<span id="zb"><?php echo WORLD_MAX;?></span>
<span id="zo">-<?php echo WORLD_MAX;?></span>
<span id="zc">(0,0)</span>
<div id="lijn_hor"></div>
<div id="lijn_ver"></div>
</div>
<!-- World layer -->
<div id="map_bg">
<!-- Grid -->
<svg id="gridSvg" width="510" height="510" aria-hidden="true"></svg>
<?php
if ($criteria != "") {
$artifactsEffect = ['-', VILLAGE_EFFECT, ACCOUNT_EFFECT, UNIQUE_EFFECT];
$array_tribe = ['-', TRIBE1, TRIBE2, TRIBE3, TRIBE4, TRIBE5, TRIBE6];
$q = "SELECT v.wref, v.owner, v.name, v.capital, v.pop, u.username, u.tribe, u.access, w.x, w.y"
. ($includeSize ? ", a.size" : "")
. " FROM ".TB_PREFIX."vdata AS v"
. " LEFT JOIN ".TB_PREFIX."users AS u ON v.owner = u.id"
. " LEFT JOIN ".TB_PREFIX."wdata AS w ON v.wref = w.id "
. $criteria;
$player_info = $database->query_return($q);
foreach ($player_info as $p_array) {
$p_name = htmlspecialchars($p_array['username'], ENT_QUOTES);
$p_village= htmlspecialchars($p_array['name'], ENT_QUOTES);
$p_pop = (int)$p_array['pop'];
$p_tribe = $array_tribe[$p_array['tribe']];
$did = (int)$p_array['wref'];
$uid = (int)$p_array['owner'];
$x = (int)$p_array['x'];
$y = (int)$p_array['y'];
$imgName = "../img/admin/map_".(isset($p_array['size']) ? "1".$p_array['size'] : ($p_array['access'] != ADMIN ? $p_array['tribe'] : 0)).".gif";
// world to pixels (510x510 world; center 255,255)
$pixelDiv = 255;
$xdiv = $pixelDiv / WORLD_MAX;
$p_x = $pixelDiv + ($x * $xdiv);
$p_y = $pixelDiv - ($y * $xdiv);
// Tooltip HTML (shared by hover & sticky)
$tooltip = "<ul class='p_info'>";
$tooltip .= "<li>Player name: <b>{$p_name}</b></li>";
$tooltip .= "<li>Village name : <b>{$p_village}</b></li>";
$tooltip .= "<li>Coordinate: <b>({$x}|{$y})</b></li>";
$tooltip .= "<li>Population: <b>{$p_pop}</b></li>";
$tooltip .= "<li>Tribe: <b>{$p_tribe}</b></li>";
if ($check3 != "" && isset($p_array['size'])) {
$tooltip .= "<li>Artifact effect: <b>".$artifactsEffect[$p_array['size']]."</b></li>";
}
$tooltip .= "</ul>";
$tooltip .= "<div class='p_actions'>"
. "<a class='p_btn' href='?p=village&did={$did}' target='_blank' rel='noopener'>Open village</a>"
. "<a class='p_btn' href='?p=player&uid={$uid}' target='_blank' rel='noopener'>Open profile</a>"
. "</div>";
// JS string version for hover (escape quotes and backslashes)
$tooltipHover = str_replace(["\\", "'"], ["\\\\", "\\'"], $tooltip);
// Attribute version for sticky (HTML-encode quotes)
$tooltipAttr = htmlspecialchars($tooltip, ENT_QUOTES);
echo '<div class="marker" style="left:'.$p_x.'px; top:'.$p_y.'px;" data-tip="'.$tooltipAttr.'">'
. '<img class="pin" src="'.$imgName.'" border="0" '
. ' onmouseout="med_closeDescription()" '
. " onmousemove=\"med_mouseMoveHandler(arguments[0], '".$tooltipHover."')\" "
. '>'
. '</div>';
}
}
?>
</div>
<!-- Sticky tooltip containers -->
<div id="tipBackdrop"></div>
<div id="stickyTip"></div>
</div>
<div id="legenda">
<div class="content">
<h3>Legend</h3>
<div id="items">
<div class="first"></div>
<table>
<tr><td><img src="../img/admin/map_1.gif" height="11" width="11"></td><td class="show"><?php echo TRIBE1;?></td></tr>
<tr><td><img src="../img/admin/map_2.gif" height="11" width="11"></td><td class="show"><?php echo TRIBE2;?></td></tr>
<tr><td><img src="../img/admin/map_3.gif" height="11" width="11"></td><td class="show"><?php echo TRIBE3;?></td></tr>
<tr><td><img src="../img/admin/map_5.gif" height="11" width="11"></td><td class="show"><?php echo TRIBE5;?></td></tr>
<tr><td><img src="../img/admin/map_0.gif" height="11" width="11"></td><td class="show">Multihunters</td></tr>
</table>
</div>
</div>
</div>
<div id="legenda">
<div class="content">
<h3>Artifacts Legend</h3>
<div id="items">
<div class="first"></div>
<table>
<tr><td><img src="../img/admin/map_11.gif" height="11" width="11"></td><td class="show"><?php echo VILLAGE_EFFECT;?></td></tr>
<tr><td><img src="../img/admin/map_12.gif" height="11" width="11"></td><td class="show"><?php echo ACCOUNT_EFFECT;?></td></tr>
<tr><td><img src="../img/admin/map_13.gif" height="11" width="11"></td><td class="show"><?php echo UNIQUE_EFFECT;?></td></tr>
</table>
</div>
</div>
</div>
</div>
<!-- ===================== JS: crisp SVG grid + clamped pan + cursor zoom + sticky tooltip ===================== -->
<script>
(function () {
const map = document.getElementById('map');
const layer = document.getElementById('map_bg');
const grid = document.getElementById('gridSvg');
const zl = document.getElementById('zl');
const zr = document.getElementById('zr');
const zo = document.getElementById('zo');
const zb = document.getElementById('zb');
const zc = document.getElementById('zc');
const zoomInBtn = document.getElementById('zoomIn');
const zoomOutBtn = document.getElementById('zoomOut');
const zoomResetBtn = document.getElementById('zoomReset');
const zoomLabel = document.getElementById('zoomLabel');
const sticky = document.getElementById('stickyTip');
const stickyBackdrop = document.getElementById('tipBackdrop');
const WORLD_MAX = <?php echo json_encode(WORLD_MAX); ?>;
// Base sizes (match CSS)
const baseW = parseFloat(getComputedStyle(layer).getPropertyValue('--map-width')) || 510;
const baseH = parseFloat(getComputedStyle(layer).getPropertyValue('--map-height')) || 510;
// World->pixel conversion (0,0 at center)
const pixelDiv = baseW / 2; // 255
const xdiv = pixelDiv / WORLD_MAX; // px per world unit
// Build SVG grid (even, crisp)
function buildGrid() {
const MAJOR_UNIT = 10;
grid.setAttribute('viewBox', `0 0 ${baseW} ${baseH}`);
grid.innerHTML = "";
for (let x = -WORLD_MAX; x <= WORLD_MAX; x++) {
const px = pixelDiv + x * xdiv;
const v = document.createElementNS('http://www.w3.org/2000/svg','line');
v.setAttribute('x1',px); v.setAttribute('y1',0);
v.setAttribute('x2',px); v.setAttribute('y2',baseH);
v.setAttribute('class', (x % MAJOR_UNIT === 0) ? 'major' : 'minor');
grid.appendChild(v);
}
for (let y = -WORLD_MAX; y <= WORLD_MAX; y++) {
const py = pixelDiv - y * xdiv;
const h = document.createElementNS('http://www.w3.org/2000/svg','line');
h.setAttribute('x1',0); h.setAttribute('y1',py);
h.setAttribute('x2',baseW); h.setAttribute('y2',py);
h.setAttribute('class', (y % MAJOR_UNIT === 0) ? 'major' : 'minor');
grid.appendChild(h);
}
}
buildGrid();
// Zoom levels
const STEP_SCALE = 1.25;
const MAX_Z = 16;
let MIN_Z = 0;
function sFromLvl(lvl){ return Math.pow(STEP_SCALE, lvl); }
// Clamp pan to avoid blank
function clampPan() {
const s = sFromLvl(zoomLevel);
const scaledW = baseW * s;
const scaledH = baseH * s;
if (scaledW <= map.clientWidth) {
tx = (map.clientWidth - scaledW) / 2;
} else {
const minTx = map.clientWidth - scaledW;
tx = Math.max(minTx, Math.min(tx, 0));
}
if (scaledH <= map.clientHeight) {
ty = (map.clientHeight - scaledH) / 2;
} else {
const minTy = map.clientHeight - scaledH;
ty = Math.max(minTy, Math.min(ty, 0));
}
}
function applyTransform(){
clampPan();
const s = sFromLvl(zoomLevel);
layer.style.setProperty('--zoom', s);
layer.style.transform = `translate(${tx}px, ${ty}px) scale(${s})`;
zoomLabel.textContent = Math.round(s*100) + '%';
updateAxes();
}
// Map-local px -> world coords
function screenToWorld(localX, localY){
const s = sFromLvl(zoomLevel);
const lx = (localX - tx) / s;
const ly = (localY - ty) / s;
return { x: (lx - pixelDiv) / xdiv, y: (pixelDiv - ly) / xdiv };
}
function updateAxes(){
const w=map.clientWidth, h=map.clientHeight;
const left = screenToWorld(0,h/2).x, right = screenToWorld(w,h/2).x;
const top = screenToWorld(w/2,0).y, bottom = screenToWorld(w/2,h).y;
const center = screenToWorld(w/2,h/2);
zl.textContent=Math.round(left); zr.textContent=Math.round(right);
zo.textContent=Math.round(top); zb.textContent=Math.round(bottom);
zc.textContent=`(${Math.round(center.x)},${Math.round(center.y)})`;
}
function computeMinLevel(){
const fit = Math.max(map.clientWidth / baseW, map.clientHeight / baseH);
let lvl=0, s=1;
if (fit >= 1) { while (s < fit) { s *= STEP_SCALE; lvl++; } }
else { while (s / STEP_SCALE >= fit) { s /= STEP_SCALE; lvl--; } }
MIN_Z = lvl;
}
function clampLevel(next){ return Math.min(MAX_Z, Math.max(MIN_Z, next)); }
function toLocal(e){ const r = map.getBoundingClientRect(); return { x:e.clientX-r.left, y:e.clientY-r.top }; }
// Zoom toward cursor
function zoomAt(deltaLevel, localX, localY){
const newLevel = clampLevel(zoomLevel + deltaLevel);
if (newLevel === zoomLevel) return;
const oldS = sFromLvl(zoomLevel), newS = sFromLvl(newLevel);
const lx = (localX - tx) / oldS, ly = (localY - ty) / oldS;
tx = localX - lx * newS; ty = localY - ly * newS;
zoomLevel = newLevel; applyTransform();
}
function zoomBy(delta){ zoomAt(delta, map.clientWidth/2, map.clientHeight/2); }
// State
let zoomLevel = 0, tx = 0, ty = 0;
// Controls
zoomInBtn.addEventListener('click', ()=>zoomBy(+1));
zoomOutBtn.addEventListener('click', ()=>zoomBy(-1));
zoomResetBtn.addEventListener('click', ()=>{
computeMinLevel(); zoomLevel = MIN_Z;
const s = sFromLvl(zoomLevel);
tx = (map.clientWidth - baseW * s) / 2;
ty = (map.clientHeight - baseH * s) / 2;
applyTransform();
});
// Wheel / dblclick
map.addEventListener('wheel', (e)=>{ e.preventDefault(); const p=toLocal(e); zoomAt(e.deltaY<0?+1:-1, p.x,p.y); }, {passive:false});
map.addEventListener('dblclick', (e)=>{ e.preventDefault(); const p=toLocal(e); zoomAt(e.shiftKey?-1:+1, p.x,p.y); });
// Drag pan
let dragging=false, lastX=0, lastY=0;
map.addEventListener('mousedown',(e)=>{ if(e.button!==0) return; dragging=true; map.classList.add('grabbing'); const p=toLocal(e); lastX=p.x; lastY=p.y; });
window.addEventListener('mousemove',(e)=>{ if(!dragging) return; const p=toLocal(e); tx+=(p.x-lastX); ty+=(p.y-lastY); lastX=p.x; lastY=p.y; applyTransform(); });
window.addEventListener('mouseup',()=>{ dragging=false; map.classList.remove('grabbing'); });
map.addEventListener('mouseleave',()=>{ dragging=false; map.classList.remove('grabbing'); });
// Touch
map.addEventListener('touchstart',(e)=>{ if(e.touches.length===1){ const t=e.touches[0], r=map.getBoundingClientRect(); dragging=true; map.classList.add('grabbing'); lastX=t.clientX-r.left; lastY=t.clientY-r.top; } }, {passive:true});
map.addEventListener('touchmove',(e)=>{ if(dragging && e.touches.length===1){ const t=e.touches[0], r=map.getBoundingClientRect(); const x=t.clientX-r.left, y=t.clientY-r.top; tx+=(x-lastX); ty+=(y-lastY); lastX=x; lastY=y; applyTransform(); e.preventDefault(); } }, {passive:false});
map.addEventListener('touchend',()=>{ dragging=false; map.classList.remove('grabbing'); });
// Init
computeMinLevel(); zoomLevel = MIN_Z;
tx = (map.clientWidth - baseW) / 2; ty = (map.clientHeight - baseH) / 2; applyTransform();
window.addEventListener('resize', ()=>{ computeMinLevel(); if(zoomLevel<MIN_Z) zoomLevel=MIN_Z; applyTransform(); });
// Sticky tooltip behavior
function openSticky(html, localX, localY){
sticky.innerHTML = html;
sticky.style.display = 'block';
const rect = sticky.getBoundingClientRect();
const W = rect.width || 280, H = rect.height || 140;
let left = localX + 12, top = localY + 12;
left = Math.max(8, Math.min(left, map.clientWidth - W - 8));
top = Math.max(8, Math.min(top, map.clientHeight - H - 8));
sticky.style.left = left + 'px';
sticky.style.top = top + 'px';
stickyBackdrop.style.display = 'block';
}
function closeSticky(){ sticky.style.display='none'; stickyBackdrop.style.display='none'; sticky.innerHTML=''; }
stickyBackdrop.addEventListener('click', closeSticky);
document.addEventListener('keydown', (e)=>{ if(e.key==='Escape') closeSticky(); });
sticky.addEventListener('click', (e)=> e.stopPropagation());
// Click marker -> sticky tooltip (uses clean HTML from data-tip)
layer.addEventListener('click', (e)=>{
const marker = e.target.closest('.marker'); if(!marker) return;
const html = marker.getAttribute('data-tip'); if(!html) return;
const r = map.getBoundingClientRect();
openSticky(html, e.clientX - r.left, e.clientY - r.top);
});
})();
</script>
</body>
</html>
+314
View File
@@ -0,0 +1,314 @@
# TravianZ Docker Setup
This guide will help you set up TravianZ using Docker and Docker Compose for easy deployment.
## Prerequisites
- Docker Engine 20.10 or higher
- Docker Compose 1.29 or higher
- At least 2GB of free RAM
- At least 5GB of free disk space
## Quick Start
### 1. Clone the Repository
```bash
git clone https://github.com/Shadowss/TravianZ.git
cd TravianZ
```
### 2. Configure Environment Variables
Copy the example environment file and customize it if needed:
```bash
cp .env.example .env
```
Edit `.env` file to set your database credentials:
```env
MYSQL_ROOT_PASSWORD=yourStrongRootPassword
MYSQL_DATABASE=travian
MYSQL_USER=travianz
MYSQL_PASSWORD=yourStrongPassword
```
### 3. Start the Containers
```bash
docker-compose up -d
```
This command will:
- Build the TravianZ web application container
- Start a MySQL 5.7 database container
- Start a phpMyAdmin container for database management
- Set up a network for all containers to communicate
### 4. Access the Installation Wizard
Once the containers are running, open your browser and navigate to:
```
http://localhost:8080/install
```
### 5. Complete the Installation
During the installation wizard, use these database settings:
- **SQL Hostname:** `db` (this is the Docker container name)
- **Port:** `3306`
- **Username:** `travianz` (or the value from your `.env` file)
- **Password:** `travianzpass` (or the value from your `.env` file)
- **DB name:** `travian` (or the value from your `.env` file)
- **Prefix:** `s1_` (or customize as needed)
- **Type:** `MYSQLi`
Complete the rest of the installation wizard with your preferred server settings.
## Services and Ports
After starting the containers, the following services will be available:
- **TravianZ Web Application:** http://localhost:8080
- **phpMyAdmin:** http://localhost:8081
- **MySQL Database:** localhost:3306 (for external connections)
## Container Management
### View Running Containers
```bash
docker-compose ps
```
### View Logs
```bash
# All containers
docker-compose logs
# Specific container
docker-compose logs web
docker-compose logs db
docker-compose logs phpmyadmin
# Follow logs in real-time
docker-compose logs -f web
```
### Stop Containers
```bash
docker-compose down
```
### Stop and Remove All Data
**WARNING:** This will delete all database data!
```bash
docker-compose down -v
```
### Restart Containers
```bash
docker-compose restart
```
### Rebuild Containers
If you make changes to the Dockerfile or application code:
```bash
docker-compose down
docker-compose up -d --build
```
## Accessing the Containers
### Access Web Container Shell
```bash
docker exec -it travianz-web bash
```
### Access MySQL Container
```bash
docker exec -it travianz-db mysql -u root -p
```
Enter the root password from your `.env` file.
## Troubleshooting
### Installation Files Not Writable
If you get permission errors during installation:
```bash
docker exec -it travianz-web chown -R www-data:www-data /var/www/html
docker exec -it travianz-web chmod -R 777 /var/www/html/var
```
### Database Connection Failed
1. Make sure the database container is running:
```bash
docker-compose ps
```
2. Check database logs:
```bash
docker-compose logs db
```
3. Verify the hostname is set to `db` (not `localhost` or `127.0.0.1`)
### Reset Installation
If you need to start the installation over:
1. Stop containers:
```bash
docker-compose down -v
```
2. Remove the installed flag:
```bash
rm -f var/installed
rm -f GameEngine/config.php
```
3. Start containers again:
```bash
docker-compose up -d
```
4. Access the installation wizard again at http://localhost:8080/install
### Port Already in Use
If port 8080 or 8081 is already in use, edit `docker-compose.yml` and change the ports:
```yaml
services:
web:
ports:
- "9080:80" # Change 8080 to any available port
phpmyadmin:
ports:
- "9081:80" # Change 8081 to any available port
```
## Backup and Restore
### Backup Database
```bash
docker exec travianz-db mysqldump -u root -p travian > backup_$(date +%Y%m%d).sql
```
### Restore Database
```bash
docker exec -i travianz-db mysql -u root -p travian < backup_20231125.sql
```
### Backup Application Files
```bash
tar -czf travianz_backup_$(date +%Y%m%d).tar.gz \
--exclude='./var/db' \
--exclude='./.git' \
.
```
## Production Deployment
For production environments, consider the following:
1. **Use Strong Passwords:** Change all default passwords in `.env`
2. **Use SSL/TLS:** Set up a reverse proxy (nginx/traefik) with Let's Encrypt
3. **Limit Database Access:** Remove the database port exposure in `docker-compose.yml`
4. **Regular Backups:** Set up automated backup scripts
5. **Resource Limits:** Add resource constraints to containers:
```yaml
services:
web:
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
```
6. **Monitoring:** Consider adding monitoring tools like Prometheus and Grafana
## Performance Optimization
### MySQL Tuning
Edit `docker-compose.yml` to add MySQL configuration:
```yaml
services:
db:
command: >
--default-authentication-plugin=mysql_native_password
--sql_mode=""
--max_connections=200
--innodb_buffer_pool_size=512M
--query_cache_size=32M
--query_cache_limit=2M
```
### PHP Tuning
Create a custom PHP configuration file `php-custom.ini`:
```ini
memory_limit = 256M
upload_max_filesize = 20M
post_max_size = 20M
max_execution_time = 300
```
Then mount it in `docker-compose.yml`:
```yaml
services:
web:
volumes:
- ./php-custom.ini:/usr/local/etc/php/conf.d/custom.ini
```
## Updates
To update TravianZ to the latest version:
```bash
git pull origin main
docker-compose down
docker-compose up -d --build
```
## Support
For issues and questions:
- GitHub Issues: https://github.com/Shadowss/TravianZ/issues
- Gitter Chat: https://gitter.im/TravianZ-V8/Lobby
## License
TravianZ Project - See LICENSE file for details
+44
View File
@@ -0,0 +1,44 @@
FROM php:7.4-apache
# Install system dependencies
RUN apt-get update && apt-get install -y \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libzip-dev \
zip \
unzip \
git \
&& rm -rf /var/lib/apt/lists/*
# Configure and install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) \
gd \
mysqli \
pdo \
pdo_mysql \
zip
# Enable Apache modules
RUN a2enmod rewrite headers
# Set working directory
WORKDIR /var/www/html
# Copy application files
COPY . /var/www/html/
# Set permissions
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html \
&& chmod -R 777 /var/www/html/var
# Configure Apache to use /var/www/html as DocumentRoot
RUN sed -i 's!/var/www/html!/var/www/html!g' /etc/apache2/sites-available/000-default.conf
# Expose port 80
EXPOSE 80
# Start Apache
CMD ["apache2-foreground"]
Executable → Regular
+738 -286
View File
File diff suppressed because it is too large Load Diff
+233 -91
View File
@@ -619,26 +619,25 @@ class MYSQLi_DB implements IDbConnection {
References: lietuvis10
***************************/
public function getBestOasisCropBonus($x, $y) {
$x = (int)$x;
$y = (int)$y;
// Adjust oasis type codes if your fork differs:
// - 50% crop only: type IN (12)
// - 25% crop (pure or mixed w/ wood/clay/iron): type IN (4,9,10,11)
$sql = "SELECT COALESCE(SUM(bonus), 0) AS total FROM (SELECT CASE
WHEN o.type IN (12) THEN 50 WHEN o.type IN (4,9,10,11) THEN 25 ELSE 0
END AS bonus FROM " . TB_PREFIX . "wdata w JOIN " . TB_PREFIX . "odata o ON o.wref = w.id
WHERE w.fieldtype = 0 AND ABS(w.x - $x) <= 3 AND ABS(w.y - $y) <= 3
AND o.type IN (12,4,9,10,11) -- only crop-giving oases ORDER BY bonus DESC LIMIT 3) t";
public function getBestOasisCropBonus($x, $y) {
$x = (int)$x;
$y = (int)$y;
$q = mysqli_query($this->dblink, $sql);
$row = mysqli_fetch_assoc($q);
$total = (int)($row['total'] ?? 0);
if ($total > 150) $total = 150; // safety cap
return $total;
}
// Adjust oasis type codes if your fork differs:
// - 50% crop only: type IN (12)
// - 25% crop (pure or mixed w/ wood/clay/iron): type IN (4,9,10,11)
$sql = "SELECT COALESCE(SUM(bonus), 0) AS total FROM (SELECT CASE
WHEN o.type IN (12) THEN 50 WHEN o.type IN (4,9,10,11) THEN 25 ELSE 0
END AS bonus FROM " . TB_PREFIX . "wdata w JOIN " . TB_PREFIX . "odata o ON o.wref = w.id
WHERE w.fieldtype = 0 AND ABS(w.x - $x) <= 3 AND ABS(w.y - $y) <= 3 AND o.type IN (12,4,9,10,11)
ORDER BY bonus DESC LIMIT 3) t";
$q = mysqli_query($this->dblink, $sql);
$row = mysqli_fetch_assoc($q);
$total = (int)($row['total'] ?? 0);
if ($total > 150) $total = 150; // safety cap
return $total;
}
/***************************
Function to process MYSQLi->fetch_all (Only exist in MYSQL)
@@ -7014,94 +7013,237 @@ References: User ID/Message ID, Mode
return true;
}
/*** Build/rebuild the croppers precompute table from wdata. */
public function TotalCroppers(): int {
$TBP = defined('TB_PREFIX') ? TB_PREFIX : 's1_';
$WDATA = $TBP . 'wdata';
$res = mysqli_query($this->dblink, "SELECT COUNT(*) AS cnt FROM `$WDATA` WHERE fieldtype IN (1,6)");
if (!$res) {
throw new Exception('Count query failed: ' . mysqli_error($this->dblink));
}
$row = mysqli_fetch_assoc($res);
return (int)($row['cnt'] ?? 0);
}
public function populateCroppers(int $countTotal = 0, bool $truncateFirst = false, int $batch = 20000, ?callable $reporter = null ): array {
@set_time_limit(0);
@ini_set('memory_limit', '1G');
$TBP = defined('TB_PREFIX') ? TB_PREFIX : 's1_';
$CROP_TABLE = $TBP . 'croppers';
$WDATA = $TBP . 'wdata';
// Count once if caller didn't
if ($countTotal <= 0) {
$row = mysqli_fetch_assoc(mysqli_query($this->dblink,
"SELECT COUNT(*) cnt FROM `$WDATA` WHERE fieldtype IN (1,6)"));
$countTotal = (int)($row['cnt'] ?? 0);
}
if ($truncateFirst) {
if (!mysqli_query($this->dblink, "TRUNCATE TABLE `$CROP_TABLE`")) {
return ['ok'=>false,'msg'=>'TRUNCATE failed: '.mysqli_error($this->dblink)];
}
}
// Session-level speed knobs (local to this connection)
@mysqli_query($this->dblink, "SET innodb_flush_log_at_trx_commit=2");
@mysqli_query($this->dblink, "SET sync_binlog=0");
@mysqli_query($this->dblink, "SET unique_checks=0");
@mysqli_query($this->dblink, "SET foreign_key_checks=0");
// Read big windows; write in safe slices to avoid max_allowed_packet
if ($batch < 1000) $batch = 1000;
if ($batch > 100000) $batch = 100000;
if($countTotal < 1000) $sliceSize = 200;
elseif($countTotal < 5000) $sliceSize = 500;
elseif($countTotal > 5000) $sliceSize = 1000;
$total = 0;
$lastId = 0;
// Cursor pagination (no OFFSET)
while (true) {
$res = mysqli_query(
$this->dblink,
"SELECT id AS wref, x, y, fieldtype
FROM `$WDATA`
WHERE fieldtype IN (1,6) AND id > $lastId
ORDER BY id ASC
LIMIT $batch"
);
if (!$res) {
return ['ok'=>false,'msg'=>'SELECT failed: '.mysqli_error($this->dblink),'processed'=>$total,'target'=>$countTotal];
}
$rows = [];
while ($r = mysqli_fetch_assoc($res)) { $rows[] = $r; }
if (!$rows) break;
mysqli_begin_transaction($this->dblink);
$n = count($rows);
for ($i = 0; $i < $n; $i += $sliceSize) {
$chunk = array_slice($rows, $i, $sliceSize);
$values = [];
foreach ($chunk as $r) {
$x = (int)$r['x'];
$y = (int)$r['y'];
// Your existing helper:
$bonus = (int)$this->getBestOasisCropBonus($x, $y);
if ($bonus < 0) $bonus = 0;
if ($bonus > 150) $bonus = 150;
$values[] = sprintf("(%d,%d,%d,%d,%d)",
(int)$r['wref'], $x, $y, (int)$r['fieldtype'], $bonus
);
}
if ($values) {
// ODKU is cheaper than REPLACE (no DELETE)
$sql = "INSERT INTO `$CROP_TABLE`
(`wref`,`x`,`y`,`fieldtype`,`best_oasis_bonus`)
VALUES ".implode(',', $values)."
ON DUPLICATE KEY UPDATE
`x`=VALUES(`x`),
`y`=VALUES(`y`),
`fieldtype`=VALUES(`fieldtype`),
`best_oasis_bonus`=VALUES(`best_oasis_bonus`)";
if (!mysqli_query($this->dblink, $sql)) {
mysqli_rollback($this->dblink);
return ['ok'=>false,'msg'=>'INSERT failed: '.mysqli_error($this->dblink),'processed'=>$total,'target'=>$countTotal];
}
}
// progress after each slice
$total += count($chunk);
if ($reporter) {
$pct = $countTotal ? min(100, (int)floor(($total / $countTotal) * 100)) : 0;
$reporter($total, $countTotal, $pct);
}
}
mysqli_commit($this->dblink);
// advance cursor
$lastId = (int)$rows[$n - 1]['wref'];
}
// Restore checks (optional)
@mysqli_query($this->dblink, "SET unique_checks=1");
@mysqli_query($this->dblink, "SET foreign_key_checks=1");
// Analyze once at the end
@mysqli_query($this->dblink, "ANALYZE TABLE `$CROP_TABLE`");
if ($reporter) { $reporter($total, $countTotal, 100); }
return ['ok'=>true,'msg'=>'Croppers populated','processed'=>$total,'target'=>$countTotal];
}
// no need to cache, not used in any loops or more than once for each page load
public function getAvailableExpansionTraining() {
global $building, $session, $technology, $village;
$vilData = $this->getVillage($village->wid);
$maxslots = (($vilData['exp1'] == 0 ? 1 : 0) + ($vilData['exp2'] == 0 ? 1 : 0) + ($vilData['exp3'] == 0 ? 1 : 0));
$residence = $building->getTypeLevel(25);
$palace = $building->getTypeLevel(26);
$vilData = $this->getVillage($village->wid);
$maxslots = (($vilData['exp1'] == 0 ? 1 : 0) + ($vilData['exp2'] == 0 ? 1 : 0) + ($vilData['exp3'] == 0 ? 1 : 0));
$residence = $building->getTypeLevel(25);
$palace = $building->getTypeLevel(26);
if($residence > 0) {
$maxslots -= (3 - floor($residence / 10));
}
if($residence > 0) {
$maxslots -= (3 - floor($residence / 10));
}
if($palace > 0) {
$maxslots -= (3 - floor(($palace - 5) / 5));
}
if($palace > 0) {
$maxslots -= (3 - floor(($palace - 5) / 5));
}
$q = "SELECT (u10+u20+u30) as R1, (u9+u19+u29) as R2 FROM " . TB_PREFIX . "units WHERE vref = ". (int) $village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$settlers = $row['R1'];
$chiefs = $row['R2'];
// Units at home
$q = "SELECT (u10+u20+u30) as R1, (u9+u19+u29) as R2
FROM " . TB_PREFIX . "units
WHERE vref = " . (int)$village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$settlers = (int)$row['R1'];
$chiefs = (int)$row['R2'];
$settlers += 3 * count($this->getMovement(5, $village->wid, 0));
$current_movement = $this->getMovement(3, $village->wid, 0);
if(!empty($current_movement)) {
foreach($current_movement as $build) {
$settlers += $build['t10'];
$chiefs += $build['t9'];
}
}
// Movements
$settlers += 3 * count($this->getMovement(5, $village->wid, 0));
$current_movement = $this->getMovement(4, $village->wid, 1);
if(!empty($current_movement)) {
foreach($current_movement as $build) {
$settlers += $build['t10'];
$chiefs += $build['t9'];
}
}
$current_movement = $this->getMovement(3, $village->wid, 0);
if(!empty($current_movement)) {
foreach($current_movement as $build) {
$settlers += (int)$build['t10'];
$chiefs += (int)$build['t9'];
}
}
$q = "SELECT (u10+u20+u30) FROM " . TB_PREFIX . "enforcement WHERE `from` = ".(int) $village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_row($result);
if(!empty($row)) {
foreach($row as $reinf) {
$settlers += $reinf[0];
}
}
$current_movement = $this->getMovement(4, $village->wid, 1);
if(!empty($current_movement)) {
foreach($current_movement as $build) {
$settlers += (int)$build['t10'];
$chiefs += (int)$build['t9'];
}
}
$q = "SELECT (u9+u19+u29) FROM " . TB_PREFIX . "enforcement WHERE `from` = ".(int) $village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_row($result);
if(!empty($row)) {
foreach($row as $reinf) {
$chiefs += $reinf[0];
}
}
// FIX: Count ALL reinforcements properly (SUM over ALL rows)
$q = "SELECT COALESCE(SUM(u10+u20+u30),0) AS s
FROM " . TB_PREFIX . "enforcement
WHERE `from` = " . (int)$village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$settlers += (int)$row['s'];
$trainlist = $technology->getTrainingList(4);
if(!empty($trainlist)) {
foreach($trainlist as $train) {
if($train['unit'] % 10 == 0) {
$settlers += $train['amt'];
}
if($train['unit'] % 10 == 9) {
$chiefs += $train['amt'];
}
}
}
$q = "SELECT COALESCE(SUM(u9+u19+u29),0) AS c
FROM " . TB_PREFIX . "enforcement
WHERE `from` = " . (int)$village->wid;
$result = mysqli_query($this->dblink,$q);
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$chiefs += (int)$row['c'];
$trappedTroops = $this->getPrisoners($village->wid, 1);
if(!empty($trappedTroops)){
foreach($trappedTroops as $trapped){
$settlers += $trapped['t10'];
$chiefs += $trapped['t9'];
}
}
// Training queue (your existing logic)
$trainlist = $technology->getTrainingList(4);
if(!empty($trainlist)) {
foreach($trainlist as $train) {
if($train['unit'] % 10 == 0) {
$settlers += (int)$train['amt'];
}
if($train['unit'] % 10 == 9) {
$chiefs += (int)$train['amt'];
}
}
}
$settlerslots = ($maxslots * 3) - ($chiefs * 3) - $settlers;
$chiefslots = $maxslots - $chiefs - floor(($settlers + 2) / 3);
// Trapped troops
$trappedTroops = $this->getPrisoners($village->wid, 1);
if(!empty($trappedTroops)){
foreach($trappedTroops as $trapped){
$settlers += (int)$trapped['t10'];
$chiefs += (int)$trapped['t9'];
}
}
if(!$technology->getTech(($session->tribe - 1) * 10 + 9)) {
$chiefslots = 0;
}
// Slot math (unchanged, but clamp to 0 to avoid negatives)
$settlerslots = ($maxslots * 3) - ($chiefs * 3) - $settlers;
$chiefslots = $maxslots - $chiefs - floor(($settlers + 2) / 3);
if(!$technology->getTech(($session->tribe - 1) * 10 + 9)) {
$chiefslots = 0;
}
if ($settlerslots < 0) $settlerslots = 0;
if ($chiefslots < 0) $chiefslots = 0;
return ["chiefs" => $chiefslots, "settlers" => $settlerslots];
}
return ["chiefs" => $chiefslots, "settlers" => $settlerslots];
}
/**
* Calculates how much artifacts affect troops speed, cranny efficency, etc.
+1 -1
View File
@@ -1031,7 +1031,7 @@ define('CRANNY', 'Cranny');
define('CURRENT_HIDDEN_UNITS', 'Currently hidden units per resource:');
define('HIDDEN_UNITS_LEVEL', 'Hidden units per resource at level');
define('UNITS', 'units');
define('CRANNY_DESC', 'The cranny hides some of your resources in case the village gets attacked. These resources cannot get stolen.<br>At level 1 the cranny can hold 200 of each resource. The capacity of Gallic crannies is 1.5 times larger.<br>If a Teutonic hero attacks a village, crannies can hide only 80% of their normal capacity');
define('CRANNY_DESC', 'The cranny hides some of your resources in case the village gets attacked. These resources cannot get stolen.<br>At level 1 the cranny can hold '.(100*((int)CRANNY_CAPACITY)).' of each resource. The capacity of Gallic crannies is 1.5 times larger.<br>If a Teutonic hero attacks a village, crannies can hide only 80% of their normal capacity');
define('TOWNHALL', 'Town Hall');
define('CELEBRATIONS_COMMENCE_TOWNHALL', 'Celebrations can commence when the town hall is completed.');
+24 -4
View File
@@ -1,12 +1,11 @@
[![Code Triagers Badge](https://www.codetriage.com/shadowss/travianz/badges/users.svg)](https://www.codetriage.com/shadowss/travianz)
[![Maintenance](https://img.shields.io/maintenance/yes/2024.svg)](https://github.com/Shadowss/TravianZ)
[![Maintenance](https://img.shields.io/maintenance/yes/2025.svg)](https://github.com/Shadowss/TravianZ)
[![GitHub Release](https://img.shields.io/github/release/Shadowss/TravianZ/all.svg)](https://github.com/Shadowss/TravianZ)
[![Github All Releases](https://img.shields.io/github/downloads/Shadowss/TravianZ/total.svg)](https://github.com/Shadowss/TravianZ)
[![GitHub contributors](https://img.shields.io/github/contributors/Shadowss/TravianZ.svg)](https://github.com/Shadowss/TravianZ)
[![license](https://img.shields.io/github/license/Shadowss/TravianZ.svg)](https://github.com/Shadowss/TravianZ)
[![GitHub last commit](https://img.shields.io/github/last-commit/Shadowss/TravianZ.svg)](https://github.com/Shadowss/TravianZ)
[![Proudly Coded in PHPStorm](https://img.shields.io/badge/coded%20in-PHPStorm-BD5CF3.svg)](https://www.jetbrains.com/buy/opensource/?product=phpstorm)
[![Donate to this project on LiberaPay](https://img.shields.io/badge/LiberaPay-donate-F6C915.svg)](https://liberapay.com/Shadowss/donate)
[![Join the chat at https://gitter.im/TravianZ-V8/Lobby](https://badges.gitter.im/TravianZ-V8/Lobby.svg)](https://gitter.im/TravianZ-V8/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Watch how this was made on YouTube](https://img.shields.io/badge/The%20making%20of...-YouTube-FF0000.svg)](https://www.youtube.com/watch?v=1XiHhpGUmQg&list=PLzV5avt1FFHorlIeoL9YX0pdb9bj-FO84)
======
@@ -17,13 +16,33 @@
[![Twitter URL](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/cata7007)
======
TravianZ Version **v.8.3.5**
TravianZ Version **v.8.3.5** - BETA 1
======
**Note:** this game is still in a pre-release state, although at this point it is very playable, tested and found to be fairly stable
**WARNING:** please note that ***this is in no way an upgrade*** from the old 8.3.2 version, so please ***do not try to just copy your files over***,
**WARNING:** please note that ***this is in no way an upgrade*** from the old 8.3.2 version, so please ***do not try to just copy your files over***,
since the installer logic has changed and you would just crash your old version
**Docker Quick Start:**
Get up and running quickly with Docker:
```bash
# Clone the repository
git clone https://github.com/Shadowss/TravianZ.git
cd TravianZ
# Copy environment file
cp .env.example .env
# Start containers
docker-compose up -d
# Open browser to http://localhost:8080/install
```
For detailed Docker setup instructions, see [DOCKER_README.md](DOCKER_README.md)
**Quick links:**
* [Download and Updates](https://github.com/Shadowss/TravianZ) &raquo;&raquo; https://github.com/Shadowss/TravianZ
* [Wiki](https://github.com/Shadowss/TravianZ/wiki)
@@ -54,6 +73,7 @@ or you have a feature request to be included in the play.
**The team**
* [Shadowss](https://github.com/Shadowss) - project owner and an occasional developer / tester
* [lietuvis10](https://github.com/lietuvis10) - active developer
* [iopietro](https://github.com/iopietro) - alumni developer
* [AL-Kateb](https://github.com/AL-Kateb) - alumni developer
* [martinambrus](https://github.com/martinambrus) - alumni developer
+3 -7
View File
@@ -15,6 +15,7 @@
## ##
#################################################################################
if(isset($_GET['z'])){
$currentcoor = $database->getCoor($_GET['z']);
$y = $currentcoor['y'];
@@ -316,8 +317,7 @@ $targetalliance=$donnees["aliance_id"];
$friendarray=$database->getAllianceAlly($donnees["aliance_id"],1);
$neutralarray=$database->getAllianceAlly($donnees["aliance_id"],2);
$enemyarray=$database->getAllianceWar2($donnees["aliance_id"]);
//var_dump($friendarray);
//echo "(".$friendarray[0]['alli1'].">0 or ".$donnees["aliance_id"].">0) and (".$friendarray[0]['alli1']."==".$donnees["aliance_id"]." or ".$friendarray[0]['alli2']."==".$donnees["aliance_id"].") and (".$session->alliance." != ".$targetalliance." and ".$session->alliance." and ".$targetalliance.")<br>\n";
if (isset($friendarray[0])) {
$friend = (($friendarray[0]['alli1']>0 and $friendarray[0]['alli2']>0 and $donnees["aliance_id"]>0) and ($friendarray[0]['alli1']==$session->alliance or $friendarray[0]['alli2']==$session->alliance) and ($session->alliance != $targetalliance and $session->alliance and $targetalliance)) ? '1':'0';
}else $friend='0';
@@ -328,11 +328,6 @@ if (isset($neutralarray[0])) {
$neutral = (($neutralarray[0]['alli1']>0 and $neutralarray[0]['alli2']>0 and $donnees["aliance_id"]>0) and ($neutralarray[0]['alli1']==$session->alliance or $neutralarray[0]['alli2']==$session->alliance) and ($session->alliance != $targetalliance and $session->alliance and $targetalliance)) ? '1':'0';
}else $neutral='0';
//echo $targetalliance.">>";
//var_dump($friendarray);
//echo"|||<br>";
//var_dump($arraydiplo);
//echo in_array($targetalliance,$friendarray);
$image = ($donnees['map_occupied'] == 1 && $donnees['map_fieldtype'] > 0)?(($donnees['ville_user'] == $session->uid)? ($donnees['ville_pop']>=100? $donnees['ville_pop']>= 250?$donnees['ville_pop']>=500? 'b30': 'b20' :'b10' : 'b00') : (($targetalliance != 0)? ($friend==1? ($donnees['ville_pop']>=100? $donnees['ville_pop']>= 250?$donnees['ville_pop']>=500? 'b31': 'b21' :'b11' : 'b01') : ($war==1? ($donnees['ville_pop']>=100? $donnees['ville_pop']>= 250?$donnees['ville_pop']>=500? 'b32': 'b22' :'b12' : 'b02') : ($neutral==1? ($donnees['ville_pop']>=100? $donnees['ville_pop']>= 250?$donnees['ville_pop']>=500? 'b35': 'b25' :'b15' : 'b05') : ($targetalliance == $session->alliance? ($donnees['ville_pop']>=100? $donnees['ville_pop']>= 250?$donnees['ville_pop']>=500? 'b33': 'b23' :'b13' : 'b03') : ($donnees['ville_pop']>=100? $donnees['ville_pop']>= 250?$donnees['ville_pop']>=500? 'b34': 'b24' :'b14' : 'b04'))))) : ($donnees['ville_pop']>=100? $donnees['ville_pop']>= 250?$donnees['ville_pop']>=500? 'b34': 'b24' :'b14' : 'b04'))) : $donnees['map_image'];
// Map Attacks by Shadow and MisterX - Fixed by iopietro
@@ -441,6 +436,7 @@ if (isset($neutralarray[0])) {
m_c.ad = [[<?php echo $map_js?>];
m_c.z = {"x":<?php echo $x ?>,"y":<?php echo $y ?>};
m_c.size = 13;
m_c.world_max = <?php echo WORLD_MAX; ?>; //lietuvis10 fix
var mdim = {"x":13,"y":13,"rad":6}
var mmode = 0;
function init_local(){map_init();}
+62
View File
@@ -0,0 +1,62 @@
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile
container_name: travianz-web
ports:
- "8080:80"
volumes:
- ./:/var/www/html
- ./var:/var/www/html/var
environment:
- APACHE_DOCUMENT_ROOT=/var/www/html
depends_on:
- db
networks:
- travianz-network
restart: unless-stopped
db:
image: mysql:5.7
container_name: travianz-db
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword}
MYSQL_DATABASE: ${MYSQL_DATABASE:-travian}
MYSQL_USER: ${MYSQL_USER:-travianz}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-travianzpass}
volumes:
- db-data:/var/lib/mysql
- ./var/db:/docker-entrypoint-initdb.d:ro
ports:
- "3306:3306"
networks:
- travianz-network
restart: unless-stopped
command: --default-authentication-plugin=mysql_native_password --sql_mode=""
phpmyadmin:
image: phpmyadmin/phpmyadmin:latest
container_name: travianz-phpmyadmin
environment:
PMA_HOST: db
PMA_PORT: 3306
PMA_USER: root
PMA_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword}
ports:
- "8081:80"
depends_on:
- db
networks:
- travianz-network
restart: unless-stopped
volumes:
db-data:
driver: local
networks:
travianz-network:
driver: bridge
+88
View File
@@ -0,0 +1,88 @@
<?php
// install/ajax_croppers.php
// Streams croppers build progress via SSE
// --- Includes ---
require_once __DIR__ . '/../GameEngine/config.php';
require_once __DIR__ . '/../GameEngine/Database.php';
require_once __DIR__ . '/../GameEngine/Admin/database.php';
// --- Headers for SSE and no buffering ---
header('Content-Type: text/event-stream; charset=utf-8');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
header('Connection: keep-alive');
// Disable nginx proxy buffering if present
header('X-Accel-Buffering: no');
@ini_set('zlib.output_compression', '0');
@ini_set('output_buffering', 'off');
@ini_set('implicit_flush', '1');
@set_time_limit(0);
// Kill any output buffers
while (ob_get_level() > 0) { @ob_end_flush(); }
ob_implicit_flush(true);
// If any installer/session code might lock the session, release it
if (session_status() === PHP_SESSION_ACTIVE) {
@session_write_close();
}
function sse_send(array $payload) {
echo "data: " . json_encode($payload, JSON_UNESCAPED_SLASHES) . "\n\n";
@flush();
@ob_flush();
}
function sse_ping() {
// Comment line per SSE spec, keeps connection alive
echo ":\n\n";
@flush();
@ob_flush();
}
global $database;
// 1) Count total croppers
try {
$total = $database->TotalCroppers();
} catch (Throwable $e) {
sse_send(['pct'=>0,'done'=>0,'total'=>0,'msg'=>'Count failed: '.$e->getMessage()]);
exit;
}
sse_send(['pct'=>0,'done'=>0,'total'=>$total,'msg'=>"Starting croppers build (found $total tiles)…"]);
// 2) Build with live reporter (pings to keep proxies happy)
$lastPing = time();
$reporter = function($done, $target, $pct) use (&$lastPing) {
sse_send(['pct'=>(int)$pct,'done'=>(int)$done,'total'=>(int)$target]);
// send keep-alive every ~10s
if (time() - $lastPing >= 10) {
sse_ping();
$lastPing = time();
}
if (connection_aborted()) { exit; } // client left
};
// Run it (fresh world => truncateFirst=true; big batch on dedicated server)
$out = $database->populateCroppers($total, true, 20000, $reporter);
if (!empty($out['ok'])) {
sse_send([
'pct'=>100,
'done'=>(int)$out['processed'],
'total'=>(int)$out['target'],
'msg'=>'Done building croppers.'
]);
} else {
sse_send([
'pct'=>0,
'done'=>0,
'total'=>(int)$total,
'msg'=>'Error: '.($out['msg'] ?? 'unknown')
]);
}
exit;
+17 -17
View File
@@ -207,26 +207,26 @@ class Process {
/**
* Generates map data and populates it with oasis.
*/
function createWdata() {
global $database;
function createWdata() {
global $database;
include ("../GameEngine/config.php");
include ("../GameEngine/Database.php");
include ("../GameEngine/Admin/database.php");
include ("../GameEngine/config.php");
include ("../GameEngine/Database.php");
include ("../GameEngine/Admin/database.php");
// populate world data
$result = $database->populateWorldData();
if ($result === false) {
header("Location: index.php?s=3&err=1");
exit;
} else if ($result === -1) {
header("Location: index.php?s=3&c=1");
exit;
}
// 1) Populate world data
$result = $database->populateWorldData();
if ($result === false) {
header("Location: index.php?s=3&err=1");
exit;
} else if ($result === -1) {
header("Location: index.php?s=3&c=1");
exit;
}
header("Location: index.php?s=4");
exit;
}
header("Location: index.php?s=3&startCroppers=1");
exit;
}
}
;
+186 -34
View File
@@ -1,54 +1,206 @@
<?php
#################################################################################
## -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =- ##
## --------------------------------------------------------------------------- ##
## Project: TravianZ ##
## Version: 22.06.2015 ##
## Version: 28.10.2025 ##
## Filename wdata.tpl ##
## Developed by: Mr.php , Advocaite , brainiacX , yi12345 , Shadow , ronix ##
## Fixed by: Shadow - STARVATION , HERO FIXED COMPL. ##
## Fixed by: InCube - double troops ##
## Fixed by: Shadow - STARVATION , HERO FIXED COMPL. ##
## Fixed by: InCube - double troops ##
## Fixed by: lietuvis10 - crop finder ##
## License: TravianZ Project ##
## Copyright: TravianZ (c) 2010-2015. All rights reserved. ##
## URLs: http://travian.shadowss.ro ##
## Source code: https://github.com/Shadowss/TravianZ ##
## URLs: http://travian.shadowss.ro ##
## Source code: https://github.com/Shadowss/TravianZ ##
## ##
#################################################################################
?>
<?php
// install/wdata.tpl
include_once('../GameEngine/config.php');
if(isset($_GET['c']) && $_GET['c'] == 1) {
echo "<br /><hr /><br /><div class=\"headline\"><span class=\"f10 c5\">Error creating wdata. Check configuration or file.</span></div><br><br>";
if (isset($_GET['c']) && $_GET['c'] == '1') {
echo '<br /><hr /><br /><div class="headline"><span class="f10 c5">Error creating wdata. Check configuration or file.</span></div><br><br>';
}
if (isset($_GET['err']) && $_GET['err'] == '1') {
echo '<br /><hr /><br /><div class="headline"><span class="f10 c5">Existing World Data found in the database! Please empty tables <i>'
. TB_PREFIX . 'odata, ' . TB_PREFIX . 'units, ' . TB_PREFIX . 'vdata, ' . TB_PREFIX . 'wdata</i> before continuing.</span></div><br /><br />';
}
if(isset($_GET['err']) && $_GET['err'] == 1) {
echo "<br /><hr /><br /><div class=\"headline\"><span class=\"f10 c5\">Existing World Data found in the database! Please empty tables <i>".TB_PREFIX."odata, ".TB_PREFIX."units, ".TB_PREFIX."vdata, ".TB_PREFIX."wdata</i> before continuing.</span></div><br /><br />";
}
$autoStartCroppers = isset($_GET['startCroppers']) && $_GET['startCroppers'] === '1';
?>
<form action="process.php" method="post" id="dataform">
<input type="hidden" name="subwdata" value="1">
<p>
<span class="f10 c">Create World Data</span>
<table>
<tr>
<td>
<b>Warning</b>: This can take some time. Please wait until the next page has been loaded. Click Create to proceed...
<br>
<br>
</td>
</tr>
<tr>
<td>
<center>
<input type="submit" name="Submit" id="Submit" value="Create..." onClick="return proceed()">
<br>
<br>
</center>
</td>
</tr>
</table>
</p>
<input type="hidden" name="subwdata" value="1" />
<p>
<span class="f10 c">Create World Data</span>
<table>
<tr>
<td>
<b>Warning</b>: This can take some time. Please wait until the next page has been loaded.
Click Create to proceed...
<br /><br />
<!-- Submit block (hidden when autoStartCroppers=1) -->
<div id="submitWrap" style="display:<?php echo $autoStartCroppers ? 'none' : 'block'; ?>;">
<center>
<input type="submit" name="Submit" id="Submit" value="Create..." onClick="return proceed()" />
<br /><br />
</center>
</div>
<!-- Progress UI (shown when startCroppers=1) -->
<div id="progressBox" style="display:<?php echo $autoStartCroppers ? 'block' : 'none'; ?>; margin-top:20px;">
<div style="font-weight:bold;margin-bottom:6px;">Building croppers…</div>
<div style="background:#ddd;border-radius:8px;overflow:hidden;height:20px;max-width:500px;">
<!-- Orange bar to match Travian vibes -->
<div id="pbar" style="background:#f6a21a;height:100%;width:0%;transition:width .2s;"></div>
</div>
<div id="pinfo" style="margin-top:6px;font-size:13px;color:#333;">Starting…</div>
<pre id="plog" style="margin-top:10px;background:#f9f9f9;border:1px solid #ddd;border-radius:8px;padding:8px;font-size:12px;max-height:200px;overflow:auto;"></pre>
<!-- Continue button appears on completion -->
<div id="autoNext" style="display:none;margin-top:10px;">
Proceeding to next step in <b id="cd">3</b>…
</div>
</div>
<script>
(function () {
var NEXT_URL = 'index.php?s=4'; // your next step
var COUNTDOWN_SECS = 3;
var finished = false;
function startCountdown() {
var box = document.getElementById('autoNext');
var cdEl = document.getElementById('cd');
var left = COUNTDOWN_SECS;
box.style.display = 'block';
cdEl.textContent = left;
var t = setInterval(function () {
left--;
cdEl.textContent = left;
if (left <= 0) {
clearInterval(t);
window.location.href = NEXT_URL;
}
}, 1000);
}
function startCroppersBuild() {
var box = document.getElementById('progressBox');
var pbar = document.getElementById('pbar');
var pinfo= document.getElementById('pinfo');
var plog = document.getElementById('plog');
var submitWrap = document.getElementById('submitWrap');
if (submitWrap) submitWrap.style.display = 'none';
box.style.display = 'block';
if (!('EventSource' in window)) {
plog.textContent += "Your browser does not support live progress.\n";
return;
}
var MAX_RETRIES = 3;
var retries = 0;
var es = new EventSource('ajax_croppers.php');
es.onopen = function () {
// When a connection (re)opens and we had errors before, log a small note
if (!finished && retries > 0) {
plog.textContent += "Reconnected to server.\n";
plog.scrollTop = plog.scrollHeight;
}
};
es.onmessage = function (e) {
// Ignore non-JSON messages (pings / blanks)
if (!e.data || e.data.charCodeAt(0) !== 123 /* '{' */) return;
try {
var d = JSON.parse(e.data);
var pct = (d.pct || 0) | 0;
var done = (d.done || 0) | 0;
var total = (d.total|| 0) | 0;
// If we've already finished, ignore further events
if (finished) return;
// Valid data received -> reset retry counter
retries = 0;
pbar.style.width = pct + '%';
pinfo.textContent = done + ' / ' + total + ' (' + pct + '%)';
if (d.msg) {
plog.textContent += d.msg + "\n";
plog.scrollTop = plog.scrollHeight;
}
if (pct >= 100) {
finished = true;
plog.textContent += "✅ Completed!\n";
plog.scrollTop = plog.scrollHeight;
es.close();
startCountdown();
}
// Optional: handle explicit error flag from server if you ever send it
if (d.error) {
finished = true;
plog.textContent += "❌ " + (d.msg || "Server reported an error.") + "\n";
plog.scrollTop = plog.scrollHeight;
es.close();
startCountdown();
}
} catch (err) {
// Silently ignore parsing problems now that we guard by '{'
// plog.textContent += "Parse error.\n";
}
};
es.onerror = function () {
// Dont spam after were done
if (finished) return;
retries++;
plog.textContent += "⚠ Connection hiccup (" + retries + "/" + MAX_RETRIES + "), retrying…\n";
plog.scrollTop = plog.scrollHeight;
// EventSource will auto-reconnect by itself; we just decide when to give up
if (retries >= MAX_RETRIES) {
finished = true;
plog.textContent += "❌ Too many connection failures — skipping croppers build.\n";
plog.scrollTop = plog.scrollHeight;
es.close();
// Reuse the same countdown UI to move on
startCountdown();
}
};
}
document.addEventListener('DOMContentLoaded', function () {
<?php if ($autoStartCroppers) { echo 'startCroppersBuild();'; } ?>
});
})();
</script>
</td>
</tr>
</table>
</p>
</form>
</div>
+5 -5
View File
@@ -897,13 +897,13 @@ if(_i.cookie){var date=new Date();date.setTime(date.getTime()+300000);document.c
if(_i.fest&&he('dorf2')){document.getElementById('content').innerHTML+=_i.fest;}
window.setTimeout(ti,30);}
function he(bj){return window.location.href.indexOf(bj+'.php')!=-1;}
function vil_levels_toggle(){var cj=$('levels'),dj=$('lswitch');cj.toggleClass('on');dj.toggleClass('on');if(cj.hasClass('on')){document.cookie='t3l=1; expires=Wed, 1 Jan 2020 00:00:00 GMT';}
function vil_levels_toggle(){var cj=$('levels'),dj=$('lswitch');cj.toggleClass('on');dj.toggleClass('on');if(cj.hasClass('on')){document.cookie='t3l=1; expires=Wed, 1 Jan 2030 00:00:00 GMT';}
else{document.cookie='t3l=1; expires=Thu, 01-Jan-1970 00:00:01 GMT';}
}
function gmwd(){if(is_ff2&&document.getElementById("gmwi").offsetWidth<50){document.cookie="a3=2; expires=Wed, 1 Jan 2020 00:00:00 GMT";}
else{document.cookie="a3=1; expires=Wed, 1 Jan 2020 00:00:00 GMT";}
function gmwd(){if(is_ff2&&document.getElementById("gmwi").offsetWidth<50){document.cookie="a3=2; expires=Wed, 1 Jan 2030 00:00:00 GMT";}
else{document.cookie="a3=1; expires=Wed, 1 Jan 2030 00:00:00 GMT";}
}
function gmc(){document.getElementById("gmw").style.display="none";document.cookie="a3=3; expires=Wed, 1 Jan 2020 00:00:00 GMT";}
function gmc(){document.getElementById("gmw").style.display="none";document.cookie="a3=3; expires=Wed, 1 Jan 2030 00:00:00 GMT";}
function documentWidth() {
return Math.max(
document.documentElement.clientWidth,
@@ -922,4 +922,4 @@ function documentHeight() {
document.body.offsetHeight,
document.documentElement.offsetHeight
);
}
}
+283 -428
View File
@@ -2,7 +2,8 @@
-- oasis regeneration script
-- used during installation, server reset, oasis reset & automation (nature repopulation)
--
-- author: martinambrus
-- original author: martinambrus
-- revised and improved: haki99
-- ----------------------------------------------------------------------------------------
@@ -33,25 +34,37 @@ INSERT INTO %PREFIX%oids VALUES %VILLAGEID%;
SET @noVillage = ((SELECT id FROM %PREFIX%oids LIMIT 1) = -1);
-- Get the number of players
SELECT COUNT(*) INTO @playerCount FROM %PREFIX%users WHERE id > 6;
-- Calculate average progression for all real players (owner > 6) from culture points (CP) and population of villages (pop)
SELECT IFNULL(AVG(pop + cp), 0) INTO @avgPlayerProgress FROM %PREFIX%vdata WHERE owner > 6;
-- ----------------------------------------------------------------
-- Calculate growth factor based on player progression
-- Scale between 0.3 and 3.0
-- ----------------------------------------------------------------
SET @growthFactor = LEAST(3.0, GREATEST(0.3, @avgPlayerProgress / 1000));
-- faster access to first oasis ID, so we don't need to reselect all the time below
SET @firstVillage = (SELECT id FROM %PREFIX%oids LIMIT 1);
-- minimum and maximum number of units for oasis with "high" field set to 0
SET @minUnitsForOasis0 = 15;
SET @maxUnitsForOasis0 = 30;
SET @minUnitsForOasis0 = GREATEST(5, FLOOR(5 * @growthFactor));
SET @maxUnitsForOasis0 = LEAST(FLOOR(@minUnitsForOasis0 + 5 + (@playerCount * 1.5) * @growthFactor), 30);
-- minimum and maximum number of units for oasis with "high" field set to 1
SET @minUnitsForOasis1 = 50;
SET @maxUnitsForOasis1 = 70;
SET @minUnitsForOasis1 = GREATEST(10, FLOOR(10 * @growthFactor));
SET @maxUnitsForOasis1 = LEAST(FLOOR(@minUnitsForOasis1 + 10 + (@playerCount * 2) * @growthFactor), 60);
-- minimum and maximum number of units for oasis with "high" field set to 2
SET @minUnitsForOasis2 = 90;
SET @maxUnitsForOasis2 = 120;
SET @minUnitsForOasis2 = GREATEST(20, FLOOR(20 * @growthFactor));
SET @maxUnitsForOasis2 = LEAST(FLOOR(@minUnitsForOasis2 + 15 + (@playerCount * 3) * @growthFactor), 90);
-- Setting a maximum for every type of Oasis so large servers won't turn oasis into fortresses
SET @maxUnitsForOasis0 = LEAST(@maxUnitsForOasis0, 30);
SET @maxUnitsForOasis1 = LEAST(@maxUnitsForOasis1, 60);
SET @maxUnitsForOasis2 = LEAST(@maxUnitsForOasis2, 90);
-- ----------------------------------------
-- reset oasis data (conquered > unoccupied)
@@ -106,444 +119,286 @@ WHERE
-- +25% lumber oasis
UPDATE %PREFIX%units u
JOIN %PREFIX%odata o
ON u.vref = o.wref
SET
u.u35 = u.u35 + (FLOOR(5 + RAND() * 10)),
u36 = u36 + (FLOOR(0 + RAND() * 5)),
u37 = u37 + (FLOOR(0 + RAND() * 5))
WHERE
(
(
@firstVillage = -1
AND
vref IN(
SELECT
id
FROM
%PREFIX%wdata
WHERE
oasistype IN(1,2)
)
)
OR
(
@firstVillage > -1
AND
vref IN ( SELECT id FROM %PREFIX%oids )
)
)
AND
(
u35 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
OR u36 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
OR u37 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
);
JOIN %PREFIX%odata o ON u.vref = o.wref
SET
u.u35 = LEAST(u.u35 + FLOOR((5 + RAND() * 10) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u.u36 = LEAST(u.u36 + FLOOR((0 + RAND() * 5) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u.u37 = LEAST(u.u37 + FLOOR((0 + RAND() * 5) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END)
WHERE
(
(@firstVillage = -1 AND u.vref IN (SELECT id FROM %PREFIX%wdata WHERE oasistype IN (1,2)))
OR
(@firstVillage > -1 AND u.vref IN (SELECT id FROM %PREFIX%oids))
);
-- +25% lumber and +25% crop oasis
UPDATE %PREFIX%units u
JOIN %PREFIX%odata o
ON u.vref = o.wref
SET
u35 = u35 + (FLOOR(5 + RAND() * 15)),
u36 = u36 + (FLOOR(0 + RAND() * 5)),
u37 = u37 + (FLOOR(0 + RAND() * 5)),
u38 = u38 + (FLOOR(0 + RAND() * 5)),
u40 = u40 + (FLOOR(0 + RAND() * 3))
WHERE
(
(
@firstVillage = -1
AND
vref IN(
SELECT
id
FROM
%PREFIX%wdata
WHERE
oasistype IN(3)
)
)
OR
(
@firstVillage > -1
AND
vref IN ( SELECT id FROM %PREFIX%oids )
)
)
AND
(
u36 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
OR u37 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
OR u38 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
);
JOIN %PREFIX%odata o ON u.vref = o.wref
SET
u35 = LEAST(u35 + FLOOR((5 + RAND() * 15) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u36 = LEAST(u36 + FLOOR((0 + RAND() * 5) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u37 = LEAST(u37 + FLOOR((0 + RAND() * 5) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u38 = LEAST(u38 + FLOOR((0 + RAND() * 5) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u40 = LEAST(u40 + FLOOR((0 + RAND() * 3) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END)
WHERE
(
(@firstVillage = -1 AND u.vref IN (SELECT id FROM %PREFIX%wdata WHERE oasistype = 3))
OR
(@firstVillage > -1 AND u.vref IN (SELECT id FROM %PREFIX%oids))
);
-- +25% clay oasis
UPDATE %PREFIX%units u
JOIN %PREFIX%odata o
ON u.vref = o.wref
SET
u31 = u31 + (FLOOR(10 + RAND() * 15)),
u32 = u32 + (FLOOR(5 + RAND() * 15)),
u35 = u35 + (FLOOR(0 + RAND() * 10))
WHERE
(
(
@firstVillage = -1
AND
vref IN(
SELECT
id
FROM
%PREFIX%wdata
WHERE
oasistype IN(4,5)
)
)
OR
(
@firstVillage > -1
AND
vref IN ( SELECT id FROM %PREFIX%oids )
)
)
AND u31 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u32 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u35 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
);
JOIN %PREFIX%odata o ON u.vref = o.wref
SET
u31 = LEAST(u31 + FLOOR((10 + RAND() * 15) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u32 = LEAST(u32 + FLOOR((5 + RAND() * 15) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u35 = LEAST(u35 + FLOOR((0 + RAND() * 10) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END)
WHERE
(
(@firstVillage = -1 AND u.vref IN (SELECT id FROM %PREFIX%wdata WHERE oasistype IN (4,5)))
OR
(@firstVillage > -1 AND u.vref IN (SELECT id FROM %PREFIX%oids))
);
-- +25% clay and +25% crop oasis
UPDATE %PREFIX%units u
JOIN %PREFIX%odata o
ON u.vref = o.wref
SET
u31 = u31 + (FLOOR(15 + RAND() * 20)),
u32 = u32 + (FLOOR(10 + RAND() * 15)),
u35 = u35 + (FLOOR(0 + RAND() * 10)),
u40 = u40 + (FLOOR(0 + RAND() * 3))
WHERE
(
(
@firstVillage = -1
AND
vref IN(
SELECT
id
FROM
%PREFIX%wdata
WHERE
oasistype IN(6)
)
)
OR
(
@firstVillage > -1
AND
vref IN ( SELECT id FROM %PREFIX%oids )
)
)
AND u31 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u32 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u35 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
);
JOIN %PREFIX%odata o ON u.vref = o.wref
SET
u31 = LEAST(u31 + FLOOR((15 + RAND() * 20) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u32 = LEAST(u32 + FLOOR((10 + RAND() * 15) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u35 = LEAST(u35 + FLOOR((0 + RAND() * 10) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u40 = LEAST(u40 + FLOOR((0 + RAND() * 3) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END)
WHERE
(
(@firstVillage = -1 AND u.vref IN (SELECT id FROM %PREFIX%wdata WHERE oasistype = 6))
OR
(@firstVillage > -1 AND u.vref IN (SELECT id FROM %PREFIX%oids))
);
-- +25% iron oasis
UPDATE %PREFIX%units u
JOIN %PREFIX%odata o
ON u.vref = o.wref
SET
u31 = u31 + (FLOOR(10 + RAND() * 15)),
u32 = u32 + (FLOOR(5 + RAND() * 15)),
u34 = u34 + (FLOOR(0 + RAND() * 10))
WHERE
(
(
@firstVillage = -1
AND
vref IN(
SELECT
id
FROM
%PREFIX%wdata
WHERE
oasistype IN(7,8)
)
)
OR
(
@firstVillage > -1
AND
vref IN ( SELECT id FROM %PREFIX%oids )
)
)
AND u31 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u32 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u34 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
);
JOIN %PREFIX%odata o ON u.vref = o.wref
SET
u31 = LEAST(u31 + FLOOR((10 + RAND() * 15) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u32 = LEAST(u32 + FLOOR((5 + RAND() * 15) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u34 = LEAST(u34 + FLOOR((0 + RAND() * 10) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END)
WHERE
(
(@firstVillage = -1 AND u.vref IN (SELECT id FROM %PREFIX%wdata WHERE oasistype IN (7,8)))
OR
(@firstVillage > -1 AND u.vref IN (SELECT id FROM %PREFIX%oids))
);
-- +25% iron and +25% crop oasis
UPDATE %PREFIX%units u
JOIN %PREFIX%odata o
ON u.vref = o.wref
SET
u31 = u31 + (FLOOR(15 + RAND() * 20)),
u32 = u32 + (FLOOR(10 + RAND() * 15)),
u34 = u34 + (FLOOR(0 + RAND() * 10)),
u39 = u39 + (FLOOR(0 + RAND() * 3))
WHERE
(
(
@firstVillage = -1
AND
vref IN(
SELECT
id
FROM
%PREFIX%wdata
WHERE
oasistype IN(9)
)
)
OR
(
@firstVillage > -1
AND
vref IN ( SELECT id FROM %PREFIX%oids )
)
)
AND u31 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u32 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u34 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
);
JOIN %PREFIX%odata o ON u.vref = o.wref
SET
u31 = LEAST(u31 + FLOOR((15 + RAND() * 20) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u32 = LEAST(u32 + FLOOR((10 + RAND() * 15) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u34 = LEAST(u34 + FLOOR((0 + RAND() * 10) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u39 = LEAST(u39 + FLOOR((0 + RAND() * 3) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END)
WHERE
(
(@firstVillage = -1 AND u.vref IN (SELECT id FROM %PREFIX%wdata WHERE oasistype = 9))
OR
(@firstVillage > -1 AND u.vref IN (SELECT id FROM %PREFIX%oids))
);
-- +25% crop oasis
UPDATE %PREFIX%units u
JOIN %PREFIX%odata o
ON u.vref = o.wref
SET
u31 = u31 + (FLOOR(5 + RAND() * 15)),
u33 = u33 + (FLOOR(5 + RAND() * 10)),
u37 = u37 + (FLOOR(0 + RAND() * 10)),
u38 = u38 + (FLOOR(0 + RAND() * 5)),
u39 = u39 + (FLOOR(0 + RAND() * 5))
WHERE
(
(
@firstVillage = -1
AND
vref IN(
SELECT
id
FROM
%PREFIX%wdata
WHERE
oasistype IN(10,11)
)
)
OR
(
@firstVillage > -1
AND
vref IN ( SELECT id FROM %PREFIX%oids )
)
)
AND u31 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u33 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u37 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u38 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
);
JOIN %PREFIX%odata o ON u.vref = o.wref
SET
u31 = LEAST(u31 + FLOOR((5 + RAND() * 15) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u33 = LEAST(u33 + FLOOR((5 + RAND() * 10) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u37 = LEAST(u37 + FLOOR((0 + RAND() * 10) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u38 = LEAST(u38 + FLOOR((0 + RAND() * 5) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u39 = LEAST(u39 + FLOOR((0 + RAND() * 5) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END)
WHERE
(
(@firstVillage = -1 AND u.vref IN (SELECT id FROM %PREFIX%wdata WHERE oasistype IN (10,11)))
OR
(@firstVillage > -1 AND u.vref IN (SELECT id FROM %PREFIX%oids))
);
-- +50% crop oasis
UPDATE %PREFIX%units u
JOIN %PREFIX%odata o
ON u.vref = o.wref
SET
u31 = u31 + (FLOOR(10 + RAND() * 15)),
u33 = u33 + (FLOOR(5 + RAND() * 10)),
u37 = u37 + (FLOOR(0 + RAND() * 10)),
u38 = u38 + (FLOOR(0 + RAND() * 5)),
u39 = u39 + (FLOOR(0 + RAND() * 5)),
u40 = u40 + (FLOOR(0 + RAND() * 3))
WHERE
(
(
@firstVillage = -1
AND
vref IN(
SELECT
id
FROM
%PREFIX%wdata
WHERE
oasistype IN(12)
)
)
OR
(
@firstVillage > -1
AND
vref IN ( SELECT id FROM %PREFIX%oids )
)
)
AND u31 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u33 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u37 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u38 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
)
AND u39 <= (
CASE o.high
WHEN 0 THEN (FLOOR(@minUnitsForOasis0 + RAND() * @maxUnitsForOasis0))
WHEN 1 THEN (FLOOR(@minUnitsForOasis1 + RAND() * @maxUnitsForOasis1))
WHEN 2 THEN (FLOOR(@minUnitsForOasis2 + RAND() * @maxUnitsForOasis2))
END
);
JOIN %PREFIX%odata o ON u.vref = o.wref
SET
u31 = LEAST(u31 + FLOOR((10 + RAND() * 15) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u33 = LEAST(u33 + FLOOR((5 + RAND() * 10) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u37 = LEAST(u37 + FLOOR((0 + RAND() * 10) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u38 = LEAST(u38 + FLOOR((0 + RAND() * 5) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u39 = LEAST(u39 + FLOOR((0 + RAND() * 5) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END),
u40 = LEAST(u40 + FLOOR((0 + RAND() * 3) * @growthFactor),
CASE o.high
WHEN 0 THEN FLOOR(@minUnitsForOasis0 + RAND() * (@maxUnitsForOasis0 - @minUnitsForOasis0))
WHEN 1 THEN FLOOR(@minUnitsForOasis1 + RAND() * (@maxUnitsForOasis1 - @minUnitsForOasis1))
WHEN 2 THEN FLOOR(@minUnitsForOasis2 + RAND() * (@maxUnitsForOasis2 - @minUnitsForOasis2))
END)
WHERE
(
(@firstVillage = -1 AND u.vref IN (SELECT id FROM %PREFIX%wdata WHERE oasistype = 12))
OR
(@firstVillage > -1 AND u.vref IN (SELECT id FROM %PREFIX%oids))
);