diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..6f89d4ed --- /dev/null +++ b/.dockerignore @@ -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/ diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..deebbeb7 --- /dev/null +++ b/.env.example @@ -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 diff --git a/.gitignore b/.gitignore index b9944138..fff5ce94 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Admin/Templates/map.tpl b/Admin/Templates/map.tpl index d320c8ac..a3ff13a4 100644 --- a/Admin/Templates/map.tpl +++ b/Admin/Templates/map.tpl @@ -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 = ""; - ?> + + + + +<?php echo SERVER_NAME; ?> Map -
-
-

Map!

-
-

This is the map of . Search and find players.

-

Show Option

-
- - - - - - -
- value="1">Players - - value="2"> - - value="2">Artifacts - - -
-
-
+ + + + +
+

Map!

+

This is the map of . Search and find players.

+ +

Show Option

+
+ + + + + +
value="1">Players value="2"> value="2">Artifacts
+
+
+ +
+
+ +
+ + 100% + + +
+ +
+ - + + + - + (0,0) +
+
+
+ + +
+ + + + 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 = "
    "; + $tooltip .= "
  • Player name: {$p_name}
  • "; + $tooltip .= "
  • Village name : {$p_village}
  • "; + $tooltip .= "
  • Coordinate: ({$x}|{$y})
  • "; + $tooltip .= "
  • Population: {$p_pop}
  • "; + $tooltip .= "
  • Tribe: {$p_tribe}
  • "; + if ($check3 != "" && isset($p_array['size'])) { + $tooltip .= "
  • Artifact effect: ".$artifactsEffect[$p_array['size']]."
  • "; + } + $tooltip .= "
"; + $tooltip .= "
" + . "Open village" + . "Open profile" + . "
"; + + // 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 '
' + . '' + . '
'; + } + } + ?> +
+ + +
+
+
+ +
+
+

Legend

+
+
+ + + + + + +
Multihunters
+
+
+
+ +
+
+

Artifacts Legend

+
+
+ + + + +
+
+
+
+
+ + + + + + diff --git a/DOCKER_README.md b/DOCKER_README.md new file mode 100644 index 00000000..4541a471 --- /dev/null +++ b/DOCKER_README.md @@ -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 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..8c0959ff --- /dev/null +++ b/Dockerfile @@ -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"] diff --git a/GameEngine/Battle.php b/GameEngine/Battle.php old mode 100755 new mode 100644 index fb77b9a0..aed840bf --- a/GameEngine/Battle.php +++ b/GameEngine/Battle.php @@ -18,324 +18,776 @@ ## ## ################################################################################# -/* -========================================================= -= BATTLE ENGINE – CORE COMBAT CALCULATION -========================================================= -= Responsible for: -= - Battle simulation (warsim) -= - Real combat calculation -= - Hero combat logic -= - Wall / Residence bonus -= - Catapults & Rams damage -= - Casualties computation -= - Bounty calculation -========================================================= -*/ -class Battle -{ - /* - ===================================================== - = INTERNAL MATH ENGINE - ===================================================== - */ +class Battle { - private $sigma; - - public function __construct() - { - // Kirilloid sigma curve for catapult damage - $this->sigma = function ($x) { - return ($x > 1 ? 2 - $x ** -1.5 : $x ** 1.5) / 2; - }; - } - - /* - ===================================================== - = ================= SIMULATION ====================== - ===================================================== - */ - - public function procSim($post) - { - global $form; - - if (!isset($post['a1_v'])) { - return; - } - - $sum = 0; - for ($i = 1; $i <= 10; $i++) { - $sum += (int)($post['a1_'.$i] ?? 0); - } - - if ($sum <= 0) { - return; - } - - $_POST['result'] = $this->simulate($post); - $form->valuearray = $post; - } - - /* - ===================================================== - = SIMULATION CORE - ===================================================== - */ - - private function simulate($post) - { - $attacker = $this->buildAttackerArray($post); - $defender = $this->buildDefenderArray($post); - - return $this->calculateBattle( - $attacker, - $defender, - $post['walllevel'], - $post['a1_v'], - $post['tribe'], - $post['palast'], - $post['ew1'], - $post['ew2'], - $post['ktyp'] + 3, - [], - 0,0,0,0,0,0,0,0, - $post['kata'], - $post['stonemason'], - $post['walllevel'], - $post['h_off_bonus'], - $post['h_off'], - $post['h_def_bonus'], - 0,0,0,0,0 - ); - } - - /* - ===================================================== - = HERO ENGINE - ===================================================== - */ - - private function getBattleHero($uid) - { - global $database; - - $hero = $database->getHero($uid); - if (!count($hero)) { - return [ - 'heroid'=>0,'unit'=>'','atk'=>0, - 'di'=>0,'dc'=>0,'ob'=>0,'db'=>0,'health'=>0 - ]; - } - - $unitData = $GLOBALS["h".$hero[0]['unit']]; - - $atk = $unitData['atk'] + ($hero[0]['attack'] * $unitData['atkp']); - $di = $unitData['di'] + 5 * floor($hero[0]['defence'] * $unitData['dip'] / 5); - $dc = $unitData['dc'] + 5 * floor($hero[0]['defence'] * $unitData['dcp'] / 5); - - return [ - 'heroid' => (int)$hero[0]['heroid'], - 'unit' => $hero[0]['unit'], - 'atk' => $atk, - 'di' => $di, - 'dc' => $dc, - 'ob' => 1 + 0.010 * ($hero[0]['attackbonus'] / 5), - 'db' => 1 + 0.010 * ($hero[0]['defencebonus'] / 5), - 'health' => $hero[0]['health'] - ]; - } - - /* - ===================================================== - = MAIN BATTLE CALCULATION - ===================================================== - */ - - public function calculateBattle( - $Attacker, - $Defender, - $def_wall, - $att_tribe, - $def_tribe, - $residence, - $attpop, - $defpop, - $type, - $def_ab, - $att_ab1,$att_ab2,$att_ab3,$att_ab4, - $att_ab5,$att_ab6,$att_ab7,$att_ab8, - $tblevel, - $stonemason, - $walllevel, - $offhero, - $hero_strenght, - $deffhero, - $AttackerID, - $DefenderID, - $AttackerWref, - $DefenderWref, - $conqureby, - $defReinforcements = null - ) - { - global $database; - - /* - ================================================= - = 1. CALCULATE RAW ATTACK / DEFENSE - ================================================= + /** + * + * @author Kirilloid --> https://github.com/kirilloid/travian/blob/master/src/model/base/combat.ts + * @var double The number of attacking catapults: 1 = 100%, 0 = 0% + * */ + + private $sigma; + + public function __construct(){ + + $this->sigma = function($x) { return ($x > 1 ? 2 - $x ** -1.5 : $x ** 1.5) / 2; }; + + } + + public function procSim($post) { + global $form; + + // receive form and process + if(isset($post['a1_v']) && (isset($post['a2_v1']) || isset($post['a2_v2']) || isset($post['a2_v3']) || isset($post['a2_v4']) || isset($post['a2_v5']))){ + $_POST['mytribe'] = $post['a1_v']; + + $target = []; + if(isset($post['a2_v1'])) array_push($target, 1); + if(isset($post['a2_v2'])) array_push($target, 2); + if(isset($post['a2_v3'])) array_push($target, 3); + if(isset($post['a2_v4'])) array_push($target, 4); + if(isset($post['a2_v5'])) array_push($target, 5); + + $_POST['target'] = $target; + if(isset($post['h_off_bonus'])){ + if(intval($post['h_off_bonus']) > 20) $post['h_off_bonus'] = 20; + } + else $post['h_off_bonus'] = 0; + + if(isset($post['h_def_bonus'])){ + if(intval($post['h_def_bonus']) > 20) $post['h_def_bonus'] = 20; + } + else $post['h_def_bonus'] = 0; + + $sum = 0; + for($i = 1; $i <= 10; $i++) $sum += (!empty($post['a1_'.$i]) ? $post['a1_'.$i] : 0); + + if($sum > 0){ + $post['palast'] = intval($post['palast']); + if($post['palast'] > 20) $post['palast'] = 20; + + for($i = 1; $i <= 5; $i++){ + if(isset($post['wall'.$i])){ + $post['wall'.$i] = intval($post['wall'.$i]); + if($post['wall'.$i] > 20) $post['wall'.$i] = 20; + elseif($post['wall'.$i] < 0) $post['wall'.$i] = 0; + $post['walllevel'] = $post['wall'.$i]; + } + } - $attackPoints = $this->calculateAttackPoints($Attacker, $att_tribe); - $defensePoints = $this->calculateDefensePoints($Defender); + $post['tribe'] = $target[0]; + $_POST['result'] = $this->simulate($post); + $newWallLevel = $_POST['result'][7]; + $oldWallLevel = $_POST['result'][8]; + + //If the wall level is reduce, we have to re-do the whole battle + if($newWallLevel != $oldWallLevel){ + $post['walllevel'] = $newWallLevel; + $_POST['result'] = $this->simulate($post); + + //Reset the datas + $_POST['result'][7] = $newWallLevel; + $_POST['result'][8] = $post['walllevel'] = $oldWallLevel; + } + + $form->valuearray = $post; + } + } + } + + private function getBattleHero($uid) { + global $database; + $heroarray = $database->getHero($uid); - /* - ================================================= - = 2. WALL & RESIDENCE BONUS - ================================================= - */ + if (!count($heroarray)) return ['heroid' => 0, 'unit' =>'','atk' => 0,'di' => 0,'dc' => 0,'ob' => 0,'db' => 0,'health' => 0]; - $defensePoints = $this->applyWallBonus( - $defensePoints, - $def_wall, - $def_tribe, - $residence - ); + $herodata = $GLOBALS["h".$heroarray[0]['unit']]; + if(!isset($heroarray['health'])) $heroarray['health'] = 0; + $h_atk = $herodata['atk'] + ($heroarray[0]['attack'] * $herodata['atkp']); + $h_di = $herodata['di'] + 5 * floor($heroarray[0]['defence'] * $herodata['dip'] / 5); + $h_dc = $herodata['dc'] + 5 * floor($heroarray[0]['defence'] * $herodata['dcp'] / 5); + $h_ob = 1 + 0.010 * ($heroarray[0]['attackbonus'] / 5); + $h_db = 1 + 0.010 * ($heroarray[0]['defencebonus'] / 5); - /* - ================================================= - = 3. FINAL RAP / RDP - ================================================= - */ + return ['heroid' => (int) $heroarray[0]['heroid'], 'unit' => $heroarray[0]['unit'], 'atk' => $h_atk, 'di' => $h_di, 'dc' => $h_dc, 'ob' => $h_ob, 'db' => $h_db, 'health' => $heroarray['health']]; + } - $rap = round($attackPoints); - $rdp = round($defensePoints); + private function getBattleHeroSim($attbonus) { + $h_atk = 0; + $h_ob = 1 + 0.010 * $attbonus; - $winner = $rap > $rdp; + return ['unit' => 16,'atk' => $h_atk,'ob' => $h_ob]; + } - /* - ================================================= - = 4. CASUALTY FACTOR - ================================================= - */ + private function simulate($post) { + //set the arrays with attacking and defending units + $attacker = ['u1' => 0, 'u2' => 0, 'u3' => 0, 'u4' => 0, 'u5' => 0, 'u6' => 0, 'u7' => 0, 'u8' => 0, 'u9' => 0, 'u10' => 0, 'u11' => 0, 'u12' => 0, 'u13' => 0, 'u14' => 0, 'u15' => 0, 'u16' => 0, 'u17' => 0, 'u18' => 0, 'u19' => 0, 'u20' => 0, 'u21' => 0, 'u22' => 0, 'u23' => 0, 'u24' => 0, + 'u25' => 0, 'u26' => 0, 'u27' => 0, 'u28' => 0, 'u29' => 0, 'u30' => 0, 'u31' => 0, 'u32' => 0, 'u33' => 0, 'u34' => 0, 'u35' => 0, 'u36' => 0, 'u37' => 0, 'u38' => 0, 'u39' => 0, 'u40' => 0, 'u41' => 0, 'u42' => 0, 'u43' => 0, 'u44' => 0, 'u45' => 0, 'u46' => 0, 'u47' => 0, 'u48' => 0, + 'u49' => 0, 'u50' => 0]; + $start = ($post['a1_v'] - 1) * 10 + 1; + $offhero = intval($post['h_off_bonus']); + $hero_strenght = intval($post['h_off']); + $deffhero = intval($post['h_def_bonus']); + for($i = $start, $index = 1; $i <= $start + 9; $i++, $index++) { + if(isset($post['a1_'.$index]) && !empty($post['a1_'.$index])) { + $attacker['u'.$i] = $post['a1_'.$index]; + } + else $attacker['u'.$i] = 0; + + if($index <=8 && isset($post['f1_'.$index]) && !empty($post['f1_'.$index])) { + ${'att_ab'.$index} = $post['f1_'.$index]; + } + else ${'att_ab'.$index} = 0; + } - $ratio = $rap > 0 - ? pow(($rdp / $rap), 1.5) - : 1; + $defender = []; + $defscout = 0; + //fix by ronix + for($i = 1;$i <= 50; $i++) { + if(isset($post['a2_'.$i]) && !empty($post['a2_'.$i])) { + $defender['u'.$i] = $post['a2_'.$i]; + $def_ab[$i] = $post['f2_'.$i]; + if($i == 4 || $i == 14 || $i == 23 || $i == 44){ + $defscout += $defender['u'.$i]; + } + + } + else { + $defender['u'.$i] = 0; + $def_ab[$i] = 0; + } + } + + $deftribe = $post['tribe']; + $wall = 0; - if ($ratio > 1) $ratio = 1; + if(empty($post['kata'])) $post['kata'] = 0; + // check scout + + $scout = 1; + for($i = $start; $i <= $start + 9 ; $i++) { + if($i == 4 || $i == 14 || $i == 23 || $i == 44){ + }else{ + if($attacker['u'.$i] > 0) { + $scout = 0; + break; + } + } + } + + $walllevel = $post['walllevel']; + $wall = $walllevel; + $palast = $post['palast']; + + if($scout == 1 && $defscout == 0) $walllevel = $wall = $palast = 0; + + if($scout == 1) $palast = 0; //no def point palace and residence when scout + + if(!$scout) return $this->calculateBattle($attacker,$defender,$wall,$post['a1_v'],$deftribe,$palast,$post['ew1'],$post['ew2'],$post['ktyp']+3,$def_ab,$att_ab1,$att_ab2,$att_ab3,$att_ab4,$att_ab5,$att_ab6,$att_ab7,$att_ab8,$post['kata'],$post['stonemason'],$walllevel,$offhero,$post['h_off'],$deffhero,0,0,0,0,0); + else return $this->calculateBattle($attacker,$defender,$wall,$post['a1_v'],$deftribe,$palast,$post['ew1'],$post['ew2'],1,$def_ab,$att_ab1,$att_ab2,$att_ab3,$att_ab4,$att_ab5,$att_ab6,$att_ab7,$att_ab8,$post['kata'],$post['stonemason'],$walllevel,0,0,0,0,0,0,0,0); + + } + + public function getTypeLevel($tid,$vid) { + global $village,$database; + + $keyholder = []; + $resourcearray = $database->getResourceLevel($vid); + + foreach(array_keys($resourcearray, $tid) as $key) { + if(strpos($key,'t')) { + $key = preg_replace("/[^0-9]/", '', $key); + array_push($keyholder, $key); + } + } + + $element = count($keyholder); + if($element >= 2) { + if($tid <= 4) { + $temparray = []; + for($i = 0; $i <= $element - 1; $i++){ + array_push($temparray, $resourcearray['f'.$keyholder[$i]]); + } + foreach ($temparray as $key => $val) { + if ($val == max($temparray)) + $target = $key; + } + } + else { + $target = 0; + for($i=1;$i<=$element-1;$i++) { + if($resourcearray['f'.$keyholder[$i]] > $resourcearray['f'.$keyholder[$target]]) { + $target = $i; + } + } + } + } + else if($element == 1) $target = 0; + else return 0; + + + if(!empty($keyholder[$target])) return $resourcearray['f'.$keyholder[$target]]; + else return 0; + } + + //1 raid 0 normal + function calculateBattle($Attacker, $Defender, $def_wall, $att_tribe, $def_tribe, $residence, $attpop, $defpop, $type, $def_ab, $att_ab1, $att_ab2, $att_ab3, $att_ab4, $att_ab5, $att_ab6, $att_ab7, $att_ab8, $tblevel, $stonemason, $walllevel, $offhero, $hero_strenght, $deffhero, $AttackerID, $DefenderID, $AttackerWref, $DefenderWref, $conqureby, $defReinforcements = null) { + global $bid34, $bid35, $database; + + // Define the array, with the units + $calvary = [4, 5, 6, 15, 16, 23, 24, 25, 26, 45, 46]; + $catapult = [8, 18, 28, 48]; + $rams = [7, 17, 27, 47]; + $catp = $ram = 0; + + // Array to return the result of the calculation back $result = []; - $result['Attack_points'] = $rap; - $result['Defend_points'] = $rdp; - $result['Winner'] = $winner ? "attacker" : "defender"; - $result[1] = $ratio; - $result[2] = 1 - $ratio; + $involve = 0; + $winner = false; + + // at 0 all partial results + + //cap = Cavalry attack points + //ap = Infantry attack points + //cdp = Cavalry attack points + //dp = Infantry defense points + //rap = Result attack points + //rdp = Result defense points + //detected = Detected or not by defender spies + $cap = $ap = $dp = $cdp = $rap = $rdp = 0; + $detected = false; + + //Get involved artifacts + $attacker_artefact = $database->getArtifactsValueInfluence($AttackerID, $AttackerWref, 3, 1, false); + $defender_artefact = $database->getArtifactsValueInfluence($DefenderID, $DefenderWref, 3, 1, false); + $strongerbuildings = $database->getArtifactsValueInfluence($DefenderID, $DefenderWref, 1, 1, false); + $isWWVillage = $database->getVillageField($DefenderWref, 'natar'); + + if(isset($Attacker['uhero']) && $Attacker['uhero'] > 0){ + $atkhero = $this->getBattleHero($AttackerID); + } + if(isset($Defender['hero']) && $Defender['hero'] > 0){ + $defenderhero = $this->getBattleHero($DefenderID); + } + //own defender units + if ($type == 1) { + $datadefScout = $this->getDataDefScout($Defender, $def_ab, $defender_artefact); + $dp += $datadefScout['dp']; + $cdp += $datadefScout['cdp']; + $involve = $datadefScout['involve']; + if(!$detected && $datadefScout['detect']) $detected = $datadefScout['detect']; + }else{ + $datadef = $this->getDataDef($Defender, $def_ab); + $own_dp = $datadef['dp']; + $own_cdp = $datadef['cdp']; + $involve = $datadef['involve']; + if(isset($Defender['hero']) && $Defender['hero'] != 0){ + $units['Def_unit']['hero'] = $Defender['hero']; + $own_cdp += $defenderhero['dc']; + $own_dp += $defenderhero['di']; + $own_dp *= $defenderhero['db']; + $own_cdp *= $defenderhero['db']; + } + $dp += $own_dp; + $cdp += $own_cdp; + } + $DefendersAll = (!is_null($defReinforcements) ? $database->getEnforceVillage($DefenderWref, 0) : $defReinforcements); - /* - ================================================= - = 5. CASUALTIES - ================================================= - */ + if(!empty($DefendersAll) && $DefenderWref > 0){ + // preload village IDs + $vilIDs = []; + foreach($DefendersAll as $defenders) { + $vilIDs[$defenders['from']] = true; + $vilIDs[$defenders['to']] = true; + } + $vilIDs = array_keys($vilIDs); + $database->getABTech($vilIDs); - $result['casualties_attacker'] = - $this->calculateCasualties($Attacker, $ratio, $att_tribe); + foreach($DefendersAll as $defenders) { + for ($i = 1; $i <= 50; $i++) $def_ab[$i] = 0; + $fromvillage = $defenders['from']; - /* - ================================================= - = 6. BOUNTY - ================================================= - */ + $userdataCache[$fromvillage] = $database->getUserArray($database->getVillageField($fromvillage, "owner"), 1); - $result['bounty'] = - $this->calculateBounty($Attacker, $result['casualties_attacker'], $att_tribe); + $enforcetribe = $userdataCache[$fromvillage]["tribe"]; + $ud=($enforcetribe - 1) * 10; + if($defenders['from'] > 0) { //don't check nature tribe + $armory = $database->getABTech($defenders['from']); // Armory level every village enforcement + $def_ab[$ud + 1] = $armory['a1']; + $def_ab[$ud + 2] = $armory['a2']; + $def_ab[$ud + 3] = $armory['a3']; + $def_ab[$ud + 4] = $armory['a4']; + $def_ab[$ud + 5] = $armory['a5']; + $def_ab[$ud + 6] = $armory['a6']; + $def_ab[$ud + 7] = $armory['a7']; + $def_ab[$ud + 8] = $armory['a8']; + } + if ($type == 1) { + $datadefScout = $this->getDataDefScout($defenders, $def_ab, $defender_artefact); + $dp += $datadefScout['dp']; + $cdp += $datadefScout['cdp']; + $involve = $datadefScout['involve']; + if(!$detected && $datadefScout['detect']) $detected = $datadefScout['detect']; + }else{ + $datadef = $this->getDataDef($defenders, $def_ab); + $reinf_dp = $datadef['dp']; + $reinf_cdp = $datadef['cdp']; + $involve += $datadef['involve']; + $reinfowner = $database->getVillageField($fromvillage, "owner"); + $defhero = $this->getBattleHero($reinfowner); + + if($defenders['hero'] != 0 && $type != 1){ + $reinf_cdp += $defhero['dc']; + $reinf_dp += $defhero['di']; + $reinf_dp *= $defhero['db']; + $reinf_cdp *= $defhero['db']; + } + $dp += $reinf_dp; + $cdp += $reinf_cdp; + } + } + } + // Calculate the total number of points Attacker + $start = ($att_tribe - 1) * 10 + 1; + $end = $att_tribe * 10; + + if($att_tribe == 3) $abcount = 3; + else $abcount = 4; + + if($type == 1) {//scout + for($i = $start;$i <= $end; $i++) { + global ${'u'.$i}; + $j = $i - $start + 1; + if($Attacker['u'.$i] > 0 && ($i == 4 || $i == 14 || $i == 23 || $i == 44)){ + if(${'att_ab'.$abcount} > 0) { + $ap += round(35 + (35 + 300 * ${'u'.$i}['pop'] / 7) * (pow(1.007, ${'att_ab'.$abcount}) - 1), 4) * $Attacker['u'.$i]; + } + else $ap += $Attacker['u'.$i] * 35; + } + $involve += $Attacker['u'.$i]; + $units['Att_unit'][$i] = $Attacker['u'.$i]; + + } + $ap *= $attacker_artefact; + + }else{ //type=3 normal 4=raid + $abcount = 1; + for($i = $start; $i <= $end; $i++) { + global ${'u'.$i}; + $j = $i - $start + 1; + if($abcount <= 8 && ${'att_ab'.$abcount} > 0) { + if(in_array($i,$calvary)) { + $cap += round(${'u'.$i}['atk'] + (${'u'.$i}['atk'] + 300 * ${'u'.$i}['pop'] / 7) * (pow(1.007, ${'att_ab'.$abcount}) - 1), 4) * (int) $Attacker['u'.$i]; + }else{ + $ap += round(${'u'.$i}['atk'] + (${'u'.$i}['atk'] + 300 * ${'u'.$i}['pop'] / 7) * (pow(1.007, ${'att_ab'.$abcount}) - 1), 4) * (int) $Attacker['u'.$i]; + } + }else{ + if(in_array($i,$calvary)) $cap += (int) $Attacker['u'.$i]*${'u'.$i}['atk']; + else $ap += (int) $Attacker['u'.$i]*${'u'.$i}['atk']; + } + + $abcount += 1; + + // Points catapult the attacker + if(in_array($i, $catapult)) $catp += (int) $Attacker['u'.$i]; + + // Points of the Rams attacker + if(in_array($i, $rams)) $ram += (int) $Attacker['u'.$i]; + + $involve += (int) $Attacker['u'.$i]; + $units['Att_unit'][$i] = (int) $Attacker['u'.$i]; + } + if (isset($Attacker['uhero']) && $Attacker['uhero'] != 0){ + $units['Att_unit']['hero'] = $Attacker['uhero']; + $ap *= $atkhero['ob']; + $cap *= $atkhero['ob']; + $ap += $atkhero['atk']; + } + + if ($offhero > 0 || $hero_strenght > 0) { + $atkhero= $this->getBattleHeroSim($offhero); + $ap *= $atkhero['ob']; + $cap *= $atkhero['ob']; + $ap += $hero_strenght; + } + if ($deffhero > 0) { + $dfdhero = $this->getBattleHeroSim($deffhero); + $dp *= $dfdhero['ob']; + $cdp *= $dfdhero['ob']; + } + } + // Formula for calculating the bonus defensive wall and Residence + + if($def_wall > 0) { + // Set the factor calculation for the "wall" as the type of the civilization + // Factor = 1030 Wall Roman + // Factor = 1020 Wall Teuton + // Factor = 1025 Wall Goul + $factor = ($def_tribe == 1)? 1.030 : (($def_tribe == 2)? 1.020 : 1.025); + $wallMultiplier = round(pow($factor, $def_wall), 3); + // Defense infantry = Infantry * Wall (%) + // Defense calvary calvary = * Wall (%) + if ($dp > 0 || $cdp > 0){ + if($type == 1) { + $dp *= $wallMultiplier; + $dp += 10; + }else{ + $dp *= $wallMultiplier; + $cdp *= $wallMultiplier; + + // Calculation of the Basic defense bonus "Residence" + $dp += (2 * (pow($residence, 2)) + 10) * $wallMultiplier; + $cdp += (2 * (pow($residence, 2)) + 10) * $wallMultiplier; + } + }else{ + $dp = 10 * $wallMultiplier * $def_wall; + // Defense calvary calvary = * Wall (%) + $cdp = 10 * $wallMultiplier * $def_wall; + if($type != 1){ + // Calculation of the Basic defense bonus "Residence" + $dp += (2 * (pow($residence, 2)) + 10) * $wallMultiplier; + $cdp += (2 * (pow($residence, 2)) + 10) * $wallMultiplier; + }else{ + $dp += 10; + $cdp = 0; + } + } + }elseif($type != 1) { + + // Calculation of the Basic defense bonus "Residence" + $dp += (2 * (pow($residence, 2)) + 10); + $cdp += (2 * (pow($residence, 2)) + 10); + } + + // Formula for calculating Attacking Points (Infantry & Cavalry) + if($AttackerWref != 0){ + $rap = round(($ap + $cap) + (($ap + $cap) / 100 * (isset($bid35[$this->getTypeLevel(35, $AttackerWref)]) ? $bid35[$this->getTypeLevel(35, $AttackerWref)]['attri'] : 0))); + } + else $rap = round($ap + $cap); + + // Formula for calculating Defensive Points + if ($rap == 0) $rdp = round(($dp) + ($cdp)); + else $rdp = round(round($cap / $rap, 4) * ($cdp) + round($ap / $rap, 4) * ($dp)); + + // The Winner is....: + $result['Attack_points'] = $rap; + $result['Defend_points'] = $rdp; + $winner = ($rap > $rdp); + + // Formula for calculating the Morale bonus + // WW villages aren't affected by this bonus + if($attpop > $defpop && !$isWWVillage) { + $moralbonus = 1 / round(max(0.667, pow($defpop / $attpop, 0.2 * min(1, $rap / ($rdp > 0 ? $rdp : 1)))), 3); + } + else $moralbonus = 1.0; + + if($involve >= 1000 && $type != 1) $Mfactor = 2 * round((1.8592 - pow($involve, 0.015)), 4); + else $Mfactor = 1.5; + + if ($Mfactor < 1.2578) $Mfactor = 1.2578; + elseif ($Mfactor > 1.5) $Mfactor = 1.5; + + // Formula for calculating losses + // $type = 1 Scout, 2 Enforcement + // $type = 3 Normal, 4 Raid + if($type == 1){ + $holder = pow((($rdp * $moralbonus) / $rap), $Mfactor); + if($holder > 1) $holder = 1; + if ($rdp > $rap) $holder = 1; + + //Birds of Prey cannot die when scouting + //Spies cannot die if the attacked village has no defending spies + //Attacker result + $result[1] = ($att_tribe == 5 || !$detected) ? 0 : $holder; + + //Defender result + $result[2] = 0; + }else if($type == 4) { + $holder = ($winner) ? pow((($rdp * $moralbonus) / $rap), $Mfactor) : pow(($rap / ($rdp * $moralbonus)), $Mfactor); + $holder = $holder / (1 + $holder); + //Attacker result + $result[1] = $winner ? $holder : 1 - $holder; + //Defender result + $result[2] = $winner ? 1 - $holder : $holder; + $ram -= round($ram * $result[1] / 100); + $catp -= round($catp * $result[1] / 100); + }else if($type == 3){ + + // Attacker + $result[1] = ($winner) ? pow((($rdp * $moralbonus) / $rap), $Mfactor) : 1; + + if ($result[1] > 1){ + $result[1] = 1; + $winner = false; + $result['Winner'] = "defender"; + } + + // Defender + $result[2] = (!$winner) ? pow(($rap / ($rdp * $moralbonus)), $Mfactor) : 1; + + if ($result[1] == 1) $result[2] = pow(($rap / ($rdp * $moralbonus)), $Mfactor); + + if ($result[2] > 1) { + $result[2] = 1; + $result['Winner'] = "attacker"; + $winner = true; + } + + // If attacked with "Hero" + $ku = ($att_tribe - 1) * 10 + 9; + $kings = (int) $Attacker['u'.$ku]; + + $aviables = $kings - round($kings * (int) $result[1]); + if ($aviables > 0){ + switch($aviables){ + case 1: $fealthy = rand(20, 30); break; + case 2: $fealthy = rand(40, 60); break; + case 3: $fealthy = rand(60, 80); break; + case 4: $fealthy = rand(80, 100); break; + default: $fealthy = 100; break; + } + $result['hero_fealthy'] = $fealthy; + } + + $ram -= ($winner) ? round($ram * $result[1] / 100) : round($ram * $result[2] / 100); + $catp -= ($winner) ? round($catp * $result[1] / 100) : round($catp * $result[2] / 100); + } + + if($catp > 0 && $tblevel != 0) { + + //Catapults blacksmith upgrades + $upgrades = round(200 * pow(1.0205, $att_ab8)) / 200; + + //Buildings durability + $durability = ($stonemason > 0 ? $bid34[$stonemason]['attri'] / 100 : 1); + + //Calculates the catapults morale bonus + $catpMoraleBonus = min(max(($attpop / ($defpop > 0 ? $defpop : 1)) ** 0.3, 1), 3); + + //New level of the building (only for warsim.php) + $catapultsDamage = $this->calculateCatapultsDamage($catp, $upgrades, $durability, $rap / $rdp, $strongerbuildings, $catpMoraleBonus); + $result[3] = $this->calculateNewBuildingLevel($tblevel, $catapultsDamage); + $result[4] = $tblevel; + + //Results for Automation.php + $result['catapults']['upgrades'] = $upgrades; + $result['catapults']['durability'] = $durability; + $result['catapults']['attackDefenseRatio'] = $rap / $rdp; + $result['catapults']['strongerBuildings'] = $strongerbuildings; + $result['catapults']['moraleBonus'] = $catpMoraleBonus; + } + + if($ram > 0 && $walllevel != 0) { + + //Rams blacksmith upgrades + $upgrades = round(200 * pow(1.0205, $att_ab7)) / 200; + + //Building durability + $durability = ($stonemason > 0 ? $bid34[$stonemason]['attri'] / 100 : 1); + + // New level of the building (only for warsim.php) + $ramsDamage = $this->calculateCatapultsDamage($ram, $upgrades, $durability, $rap / $rdp, $strongerbuildings, 1); + $result[7] = $this->calculateNewBuildingLevel($walllevel, $ramsDamage); + $result[8] = $walllevel; + + // Results for Automation.php + $result['rams']['upgrades'] = $upgrades; + $result['rams']['durability'] = $durability; + $result['rams']['attackDefenseRatio'] = $rap / $rdp; + $result['rams']['strongerBuildings'] = $strongerbuildings; + $result['rams']['moraleBonus'] = 1; + } + + $result[6] = pow($rap / ($rdp * $moralbonus > 0 ? $rdp * $moralbonus : 1), $Mfactor); + $result['moralBonus'] = $moralbonus; + + $total_att_units = count($units['Att_unit']); + $start = intval(($att_tribe - 1) * 10 + 1); + $end = intval($att_tribe * 10); + + for($i = $start; $i <= $end; $i++){ + $y = $i - (($att_tribe - 1) * 10); + $result['casualties_attacker'][$y] = round($result[1] * $units['Att_unit'][$i]); + } + + if (isset($units['Att_unit']['hero']) && $units['Att_unit']['hero'] >0){ + + $_result = mysqli_query($database->dblink,"select heroid, health from " . TB_PREFIX . "hero where `dead`='0' and `heroid`=".(int) $atkhero['heroid']); + $fdb = mysqli_fetch_array($_result); + $hero_id = (int) $fdb['heroid']; + $hero_health = $fdb['health']; + $damage_health = round(100 * $result[1]); + + if ($hero_health <= $damage_health || $damage_health > 90){ + //hero die + $result['casualties_attacker'][11] = 1; + mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `dead` = 1, `health` = 0 where `heroid`=".(int) $hero_id); + }else{ + mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `health`=`health`-".(int) $damage_health." where `heroid`=".(int) $hero_id); + } + } + unset($_result, $fdb, $hero_id, $hero_health, $damage_health); + + + if (isset($units['Def_unit']['hero']) && $units['Def_unit']['hero'] >0){ + + $_result = mysqli_query($database->dblink,"select heroid, health from " . TB_PREFIX . "hero where `dead`='0' and `heroid`=".(int) $defenderhero['heroid']); + $fdb = mysqli_fetch_array($_result); + $hero_id = (int) $fdb['heroid']; + $hero_health = $fdb['health']; + $damage_health = round(100 * $result[2]); + if ($hero_health <= $damage_health || $damage_health > 90){ + //hero die + $result['deadherodef'] = 1; + mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `dead` = 1, `health` = 0 where `heroid`=".(int) $hero_id); + }else{ + $result['deadherodef'] = 0; + mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `health`=`health`-".(int) $damage_health." where `heroid`=".(int) $hero_id); + } + } + unset($_result, $fdb, $hero_id, $hero_health, $damage_health); + + if(!empty($DefendersAll)){ + $battleHeroesCache = []; + foreach($DefendersAll as $defenders) { + if($defenders['hero'] > 0) { + $battleHeroesCache[$defenders['from']] = $this->getBattleHero($database->getVillageField($defenders['from'],"owner")); + $heroarraydefender = $battleHeroesCache[$defenders['from']]; + $_result = mysqli_query($database->dblink,"select heroid, health from " . TB_PREFIX . "hero where `dead`='0' and `heroid`=".(int) $heroarraydefender['heroid']); + $fdb = mysqli_fetch_array($_result); + $hero_id = (int) $fdb['heroid']; + $hero_health = $fdb['health']; + $damage_health = round(100 * $result[2]); + if ($hero_health <= $damage_health || $damage_health > 90){ + //hero die + $result['deadheroref'][$defenders['id']] = 1; + mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `dead` = 1, `health` = 0 where `heroid`=".(int) $hero_id); + }else{ + $result['deadheroref'][$defenders['id']] = 0; + mysqli_query($database->dblink,"update " . TB_PREFIX . "hero set `health`=`health`-".(int) $damage_health." where `heroid`=".(int) $hero_id); + } + } + } + } + unset($_result, $fdb, $hero_id, $hero_health, $damage_health); + + + // Work out bounty + $start = ($att_tribe - 1) * 10 + 1; + $end = ($att_tribe * 10); + + $max_bounty = 0; + + for($i = $start; $i <= $end; $i++) { + $j = $i - $start + 1; + $y = $i -(($att_tribe - 1) * 10); + + $max_bounty += ((int) $Attacker['u'.$i] - (int) $result['casualties_attacker'][$y]) * (int) ${'u'.$i}['cap']; + } + + $result['bounty'] = $max_bounty; return $result; } - /* - ===================================================== - = ATTACK / DEFENSE HELPERS - ===================================================== - */ + public function getDataDefScout($defenders, $def_ab, $defender_artefact) { + $abcount = 1; + $invol = $dp = $cdp = 0; + $detected = false; + + for($y = 4; $y <= 50; $y++) { + if($y == 4 || $y == 14 || $y == 23 || $y == 44){ + global ${'u'.$y}; - private function calculateAttackPoints($Attacker, $tribe) - { - $start = ($tribe - 1) * 10 + 1; - $end = $tribe * 10; - - $points = 0; - - for ($i = $start; $i <= $end; $i++) { - global ${'u'.$i}; - $points += (int)$Attacker['u'.$i] * ${'u'.$i}['atk']; + if($defenders['u'.$y] > 0 && $def_ab[$y] > 0){ + $dp += round(20 + (20 + 300 * ${'u'.$y}['pop'] / 7) * (pow(1.007, $def_ab[$y]) - 1), 4) * $defenders['u'.$y] * $defender_artefact; + $detected = true; + }else{ + if($defenders['u'.$y] > 0){ + $dp += $defenders['u'.$y] * 20 * $defender_artefact; + $detected = true; + } + } + + $invol += $defenders['u'.$y]; //total troops + $units['Def_unit'][$y] = $defenders['u'.$y]; + } } - return $points; + $datadef['dp'] = $dp; + $datadef['cdp'] = $cdp; + $datadef['detect'] = $detected; + $datadef['involve'] = $invol; + return $datadef; } - private function calculateDefensePoints($Defender) - { - $points = 0; + public function getDataDef($defenders,$def_ab) { + $dp = $cdp = $invol = 0; + for($y = 1;$y <= 50; $y++) { + global ${'u'.$y}; + if ($defenders['u'.$y] > 0) { + if (!isset($def_ab[$y])) { + $def_ab[$y] = 0; + } + if ($def_ab[$y] > 0) { + $dp += round(${'u'.$y}['di'] + (${'u'.$y}['di'] + 300 * ${'u'.$y}['pop'] / 7) * (pow(1.007, $def_ab[$y]) - 1), 4) * $defenders['u'.$y]; + $cdp += round(${'u'.$y}['dc'] + (${'u'.$y}['dc'] + 300 * ${'u'.$y}['pop'] / 7) * (pow(1.007, $def_ab[$y]) - 1), 4) * $defenders['u'.$y]; + }else{ + $dp += $defenders['u'.$y] * ${'u'.$y}['di']; + $cdp += $defenders['u'.$y] * ${'u'.$y}['dc']; + } - for ($i = 1; $i <= 50; $i++) { - global ${'u'.$i}; - $points += (int)$Defender['u'.$i] * ${'u'.$i}['di']; + } + $invol += $defenders['u'.$y]; //total troops + $units['Def_unit'][$y] = $defenders['u'.$y]; } - - return $points; + $datadef['dp'] = $dp; + $datadef['cdp'] = $cdp; + $datadef['involve'] = $invol; + return $datadef; } - - private function applyWallBonus($defPoints, $wall, $tribe, $residence) - { - if ($wall <= 0) { - return $defPoints + (2 * pow($residence,2) + 10); - } - - $factor = ($tribe == 1) - ? 1.030 - : (($tribe == 2) ? 1.020 : 1.025); - - return $defPoints * pow($factor, $wall); + + /** + * @author Kirilloid --> https://github.com/kirilloid/travian/blob/master/src/model/base/combat.ts + * + * Calculates the new building level, after damaging it + * + * @param int $oldLevel The old building level + * @param float $damage The damage done by catapults + * @return int Returns the new building level + */ + + public function calculateNewBuildingLevel($oldLevel, $damage){ + $damage -= 0.5; + if ($damage < 0) return $oldLevel; + + while ($damage >= $oldLevel && $oldLevel) $damage -= $oldLevel--; + + return $oldLevel; } - - private function calculateCasualties($Attacker, $ratio, $tribe) - { - $casualties = []; - - $start = ($tribe - 1) * 10 + 1; - $end = $tribe * 10; - - for ($i = $start; $i <= $end; $i++) { - $index = $i - (($tribe - 1) * 10); - $casualties[$index] = - round($ratio * (int)$Attacker['u'.$i]); - } - - return $casualties; + + /** + * @author Kirilloid --> https://github.com/kirilloid/travian/blob/master/src/model/base/combat.ts + * + * Calculates the damage done by catapults + * + * @param int $catapultsQuantity The quantity of catapults which take part in the attack + * @param double $catapultsUpgrade The catapults upgrade multiplier, affected by the cataputls level in the blacksmith + * @param double $durability The building durability, affected by the stonemason's lodge + * @param double $ADRatio The attack points / defensive points ratio + * @param double $strongerBuildings The artifacts multiplier, which strengthens the building, affected by durability artifacts + * @param double $moraleBonus The defender morale bonus + * @return double Returns the damage done by catapults + */ + + public function calculateCatapultsDamage($catapultsQuantity, $catapultsUpgrade, $durability, $ADRatio, $strongerBuildings, $moraleBonus){ + $catapultsEfficiency = floor($catapultsQuantity / ($durability * $strongerBuildings)); + return 4 * ($this->sigma)($ADRatio) * $catapultsEfficiency * $catapultsUpgrade / $moraleBonus; } - - private function calculateBounty($Attacker, $casualties, $tribe) - { - $start = ($tribe - 1) * 10 + 1; - $end = $tribe * 10; - - $bounty = 0; - - for ($i = $start; $i <= $end; $i++) { - global ${'u'.$i}; - $index = $i - (($tribe - 1) * 10); - - $alive = (int)$Attacker['u'.$i] - (int)$casualties[$index]; - - $bounty += $alive * (int)${'u'.$i}['cap']; - } - - return $bounty; - } -} +}; $battle = new Battle; ?> diff --git a/GameEngine/Database.php b/GameEngine/Database.php index 262de472..e81cb16b 100755 --- a/GameEngine/Database.php +++ b/GameEngine/Database.php @@ -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. diff --git a/GameEngine/Lang/en.php b/GameEngine/Lang/en.php index b006d0d0..147d9dae 100755 --- a/GameEngine/Lang/en.php +++ b/GameEngine/Lang/en.php @@ -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.
At level 1 the cranny can hold 200 of each resource. The capacity of Gallic crannies is 1.5 times larger.
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.
At level 1 the cranny can hold '.(100*((int)CRANNY_CAPACITY)).' of each resource. The capacity of Gallic crannies is 1.5 times larger.
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.'); diff --git a/README.md b/README.md index c59d48d2..205ede99 100644 --- a/README.md +++ b/README.md @@ -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) »» 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 diff --git a/Templates/Map/mapviewlarge.tpl b/Templates/Map/mapviewlarge.tpl index 1602498e..e37e567c 100644 --- a/Templates/Map/mapviewlarge.tpl +++ b/Templates/Map/mapviewlarge.tpl @@ -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.")
\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"|||
"; -//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 = [[]; m_c.z = {"x":,"y":}; m_c.size = 13; + m_c.world_max = ; //lietuvis10 fix var mdim = {"x":13,"y":13,"rad":6} var mmode = 0; function init_local(){map_init();} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..1edb3c95 --- /dev/null +++ b/docker-compose.yml @@ -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 diff --git a/install/ajax_croppers.php b/install/ajax_croppers.php new file mode 100644 index 00000000..5d2e9d28 --- /dev/null +++ b/install/ajax_croppers.php @@ -0,0 +1,88 @@ + 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; diff --git a/install/process.php b/install/process.php index 2e947fc6..61385cf2 100644 --- a/install/process.php +++ b/install/process.php @@ -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; + } } ; diff --git a/install/templates/wdata.tpl b/install/templates/wdata.tpl index 514aae08..f7bc384d 100644 --- a/install/templates/wdata.tpl +++ b/install/templates/wdata.tpl @@ -1,54 +1,206 @@ + +

Error creating wdata. Check configuration or file.


"; +if (isset($_GET['c']) && $_GET['c'] == '1') { + echo '


Error creating wdata. Check configuration or file.


'; +} +if (isset($_GET['err']) && $_GET['err'] == '1') { + echo '


Existing World Data found in the database! Please empty tables ' + . TB_PREFIX . 'odata, ' . TB_PREFIX . 'units, ' . TB_PREFIX . 'vdata, ' . TB_PREFIX . 'wdata before continuing.


'; } -if(isset($_GET['err']) && $_GET['err'] == 1) { -echo "


Existing World Data found in the database! Please empty tables ".TB_PREFIX."odata, ".TB_PREFIX."units, ".TB_PREFIX."vdata, ".TB_PREFIX."wdata before continuing.


"; -} +$autoStartCroppers = isset($_GET['startCroppers']) && $_GET['startCroppers'] === '1'; ?> +
- - -

- Create World Data - - - - - - - - -
- Warning: This can take some time. Please wait until the next page has been loaded. Click Create to proceed... -
-
-
-
- -
-
-
-
-

+ + +

+ Create World Data + + + + + +
+ Warning: This can take some time. Please wait until the next page has been loaded. + Click Create to proceed... +

+ + +
+
+ +

+
+
+ + +
+
Building croppers…
+ +
+ +
+
+ +
Starting…
+ +

+
+                        
+						
+                    
+ + + + +
+

diff --git a/unx.js b/unx.js index 7605361d..d61cf7c3 100644 --- a/unx.js +++ b/unx.js @@ -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 ); -} \ No newline at end of file +} diff --git a/var/db/datagen-oasis-troops-regen.sql b/var/db/datagen-oasis-troops-regen.sql index 6cd3efe0..81c76126 100644 --- a/var/db/datagen-oasis-troops-regen.sql +++ b/var/db/datagen-oasis-troops-regen.sql @@ -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 - ); \ No newline at end of file +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)) +);