<?php

#################################################################################
##              -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =-                 ##
## --------------------------------------------------------------------------- ##
##  Filename       : Units.php                      	                       ##
##  Type           : Units System Backend                                      ##
## --------------------------------------------------------------------------- ##
##  Developed by   : Dzoki           			                               ##
##  Refactored by  : Shadow & Ferywir									       ##
##  Thanks to      : ronix, InCube, Akakori, Elmar & Kirilloid                 ##
## --------------------------------------------------------------------------- ##
##  Contact        : cata7007@gmail.com                                        ##
##  Project        : TravianZ                                                  ##
##  URLs:          : https://travianz.org                                      ##
##  GitHub         : https://github.com/Shadowss/TravianZ                      ##
## --------------------------------------------------------------------------- ##
##  License        : TravianZ Project                                          ##
##  Copyright      : TravianZ (c) 2010-2026. All rights reserved.              ##
## --------------------------------------------------------------------------- ##
#################################################################################

class Units {
    public $sending, $recieving, $return = [];

    public function procUnits($post) {
        if(isset($post['c'])) {
            if (!isset($post['disabled'])) $post['disabled'] = '';

            switch($post['c']) {
                case 1:
                case 2:
                case 3:
                case 4:
                    if (isset($post['a']) && $post['a'] == 533374 && empty($post['disabled'])) $this->sendTroops($post);
                    else
                    {
                        $post = $this->loadUnits($post);
                        return $post;
                    }
                    break;

                case 5:
                    if (isset($post['a']) && $post['a'] == "new") $this->Settlers($post);
                    else
                    {
                        $post = $this->loadUnits($post);
                        return $post;
                    }
                    break;
                    
                case 8:
                	$this->sendTroopsBack($post);
                	break;
            }
        }
    }
    
    private function loadUnits($post) {
        global $form;
        
        if(!empty($error = $this->checkErrors($post))) {
            $form->addError("error", $error);
            $_SESSION['errorarray'] = $form->getErrors();
            $_SESSION['valuearray'] = $_POST;
            header("Location: a2b.php");
            exit;
        }
        else return $post;
    }
    
    /**
     * Gets an error if the user did a mistake
     * 
     * @param array $post The array containing all of the needed informations
     * @return string Returns the errors, or empty if no errors was found
     */
    
    /**
     * PERMISIUNI SITTER - misiune -> drept necesar.
     *
     * Codurile de misiune vin din formularul de trimitere (Templates/a2b/):
     *     1 = spionaj      -> permis mereu (nu e in lista din Travian)
     *     2 = intarire     -> SITTER_PERM_REINF
     *     3 = atac normal  -> SITTER_PERM_ATTACK
     *     4 = raid         -> SITTER_PERM_RAID
     *     5 = colonizare   -> permis mereu
     *
     * Intoarce mesajul de eroare, sau "" daca actiunea e permisa.
     * Proprietarul contului primeste mereu "".
     */
    private function sitterMissionError($missionType) {
        global $session;

        if (!isset($session) || !is_object($session) || !method_exists($session, 'sitterCan')) {
            return "";
        }

        $map = array(
            2 => SITTER_PERM_REINF,
            3 => SITTER_PERM_ATTACK,
            4 => SITTER_PERM_RAID,
        );

        $missionType = (int) $missionType;

        if (!isset($map[$missionType])) {
            return "";
        }

        if ($session->sitterCan($map[$missionType])) {
            return "";
        }

        return defined('SITTER_P_DENIED')
            ? SITTER_P_DENIED
            : 'Your sitter permissions do not allow this action.';
    }

    public function checkErrors(&$post){
        global $database, $generator, $session;

        if(($error = $this->parseCoordinates($post)) !== "") return $error;

        $this->resolveTarget($post, $disabled, $disabledr, $isOasis);

        if(!empty($disabledr) && $post['c'] == 2) return "You can't reinforce this village/oasis";
        if(!empty($disabled) && $post['c'] == 3) return "You can't attack this village/oasis with normal attack";
        if($post['c'] < 2 || $post['c'] > 4) return "Invalid attack type.";

        // sitterul fara dreptul respectiv nu ajunge nici macar la confirmare
        if(($error = $this->sitterMissionError($post['c'])) !== "") return $error;

        //check if at least one troops has been selected
        $selectedTroops = 0;
        for($i = 1; $i <= 11; $i++) $selectedTroops += empty($post['t'.$i]) ? 0 : $post['t'.$i];
        if($selectedTroops == 0) return "You need to select min. one troop";

        if(!empty($post['dname']) && $post['x'] != "" && $post['y'] != "") return "Insert name or coordinates";

        // Neither a village name nor coordinates were provided at all -> $id would
        // otherwise stay undefined and reach array_push() below unset.
        if(empty($post['dname']) && (!isset($post['x']) || !isset($post['y']) || $post['x'] === "" || $post['y'] === "")) {
            return "Insert name or coordinates";
        }

        if(isset($post['dname']) && !empty($post['dname'])) {
            $id = $database->resolveVillageInput(stripslashes($post['dname']),
                (int) ($session->uid ?? 0));   // accepta si "Nume (x|y)"

            if (!isset($id)) return "Village doesn't exist";
            else $coor = $database->getCoor($id);
        }

        // People search by coordinates
        // We confirm and seek coordinate coordinates Village
        if(isset($post['x']) && isset($post['y']) && $post['x'] != "" && $post['y'] != "") {
            $coor = ['x' => $post['x'], 'y' => $post['y']];
            $id = $generator->getBaseID($coor['x'], $coor['y']);

            if (!$database->getVillageState($id)) return "Coordinates do not exist";
        }

        if (!empty($coor)) {
            if(($error = $this->validateTroopSelection($post)) !== "") return $error;
        }

        if(isset($id)) {
            if(($error = $this->validateTargetPlayer($id, $isOasis, $villageInfo)) !== "") return $error;
        }

        //no errors, we can add the additional information to the post array
        array_push($post, $id, $villageInfo['name'], $villageInfo['owner'], 0);

        return "";
    }

    /**
     * Trims, validates and normalises the x/y coordinates in $post.
     *
     * @param array $post The request array (modified in place)
     * @return string An error message, or "" if the coordinates are valid
     */
    private function parseCoordinates(&$post) {
        if(isset($post['x'])) $post['x'] = trim($post['x']);
        if(isset($post['y'])) $post['y'] = trim($post['y']);

        if(isset($post['x']) && isset($post['y']) && $post['x'] !== "" && $post['y'] !== ""){
            if(!preg_match('/^-?\d+$/', $post['x']) || !preg_match('/^-?\d+$/', $post['y'])) return "Invalid coordinates";

            $post['x'] = (int) $post['x'];
            $post['y'] = (int) $post['y'];

            if(abs($post['x']) > WORLD_MAX || abs($post['y']) > WORLD_MAX) return "Coordinates do not exist";
        }

        return "";
    }

    /**
     * Resolves the target village from coordinates or name and derives the
     * reinforce/attack "disabled" flags and whether the target is an oasis.
     *
     * @param array  $post      The request array (dname may be unset)
     * @param string $disabled  Out: "disabled=disabled" when normal attack is forbidden
     * @param string $disabledr Out: "disabled=disabled" when reinforcing is forbidden
     * @param mixed  $isOasis   Out: truthy when the target is an oasis
     */
    private function resolveTarget(&$post, &$disabled, &$disabledr, &$isOasis) {
        global $database, $session;

        // Search by town name
        // Coordinates and look confirm name people
        if(isset($post['x']) && isset($post['y']) && $post['x'] != "" && $post['y'] != "") {
            $vid = $database->getVilWref($post['x'], $post['y']);
            unset($post['dname']);
        }
        else if(isset($post['dname']) && !empty($post['dname'])) $vid = $database->resolveVillageInput(stripslashes($post['dname']),
                (int) ($session->uid ?? 0));   // accepta si "Nume (x|y)"

        if (!empty($vid)) {
            if($isOasis = $database->isVillageOases($vid)){
                $too = $database->getOasisField($vid, "conqured");
                if($too == 0) $disabled = $disabledr ="disabled=disabled";
                else
                {
                    $disabledr = "";
                    if($session->sit == 0) $disabled = "";
                    else $disabled ="disabled=disabled";
                }
            }else{
                $too = $database->getVillage($vid);
                if($too['owner'] == 3){
                    $disabledr = "disabled=disabled";
                    $disabled = "";
                }else{
                    $disabledr = "";
                    if($session->sit == 0) $disabled = "";
                    else $disabled ="disabled=disabled";
                }
            }
        }else{
            $disabledr = "";
            if($session->sit == 0) $disabled = "";
            else $disabled ="disabled=disabled";
        }
    }

    /**
     * Validates the selected troop amounts against what the village owns.
     *
     * @param array $post The request array
     * @return string An error message, or "" if the selection is valid
     */
    private function validateTroopSelection($post) {
        global $village, $session;

        $Gtribe = $session->tribe == 1 ? "" : $session->tribe - 1;
        for($i = 1; $i < 12; $i++){
            if(isset($post['t'.$i])){
                if($i < 10) $troophave = $village->unitarray['u'.$Gtribe.$i];
                if($i == 10) $troophave = $village->unitarray['u'.floor(intval($Gtribe) + 1) * $i];
                if($i == 11) $troophave = $village->unitarray['hero'];

                if(intval($post['t'.$i]) > $troophave) return "You can't send more units than you have";
                if(intval($post['t'.$i]) < 0) return "You can't send negative units.";
                if(preg_match('/[^0-9]/',$post['t'.$i])) return "Special characters can't entered";
            }
        }

        return "";
    }

    /**
     * Validates the target player (beginners protection, ban/admin, vacation,
     * self-attack) and loads the target village/oasis info.
     *
     * @param int   $id          The target base id
     * @param mixed $isOasis      Whether the target is an oasis
     * @param array $villageInfo  Out: the resolved village/oasis row
     * @return string An error message, or "" if the target can be attacked
     */
    /**
     * Jucatori protejati impotriva atacurilor.
     *
     * Lista vine din PROTECTED_PLAYERS (config.php), separata prin virgula:
     *     define("PROTECTED_PLAYERS", "Shadow,Multihunter");
     *
     * Verificarea sta AICI fiindca e punctul unic prin care trec toate
     * trimiterile de trupe ale jucatorilor - din a2b.php, din harta sau de
     * oriunde altundeva. Nu e nevoie sa modificam fiecare pagina.
     *
     * Se blocheaza doar ATACUL si RAIDUL (c = 3 si 4). Intaririle (c = 2) trec,
     * ca jucatorul protejat sa poata primi ajutor.
     *
     * @param  int    $ownerId  proprietarul satului tinta
     * @param  mixed  $missionType  tipul misiunii din formular
     * @return bool
     */
    private function isProtectedTarget($ownerId, $missionType) {
        global $database;

        if (!defined('PROTECTED_PLAYERS') || trim(PROTECTED_PLAYERS) === '') {
            return false;
        }

        // intaririle raman permise
        if ((int) $missionType !== 3 && (int) $missionType !== 4) {
            return false;
        }

        $targetName = (string) $database->getUserField((int) $ownerId, 'username', 0);

        if ($targetName === '') {
            return false;
        }

        foreach (explode(',', PROTECTED_PLAYERS) as $protectedName) {
            $protectedName = trim($protectedName);

            if ($protectedName !== '' && strcasecmp($protectedName, $targetName) === 0) {
                return true;
            }
        }

        return false;
    }

    private function validateTargetPlayer($id, $isOasis, &$villageInfo) {
        global $database, $village;

        //check if the attacked village/oasis' owner is under beginners protection
        if($database->hasBeginnerProtection($id) == 1) return "Player is under beginners protection. You can't attack him";

        //check if it's an oasis or not
        $villageInfo = (!$isOasis) ? $database->getVillage($id) : $database->getOasisV($id);

        //check if banned/admin:
        $villageOwner = $villageInfo['owner'];
        $userAccess = $database->getUserField($villageOwner, 'access', 0);
        $userID = $database->getUserField($villageOwner, 'id', 0);
        //check if he's an Admin and if he's attackable
        if($userAccess == 0 || ($userAccess == MULTIHUNTER && $userID == 5) || (!ADMIN_ALLOW_INCOMING_RAIDS && $userAccess == ADMIN)){
            return "Player is Banned. You can't attack him";
        }

        //check if the user' is on the vacation mode:
        if($database->getvacmodexy($id)) return "User is on vacation mode";
        // jucator protejat prin PROTECTED_PLAYERS
        if($this->isProtectedTarget($villageOwner, isset($_POST['c']) ? $_POST['c'] : 0)) {
            return "This player is protected from attacks.";
        }

        //check if attacking same village that units are in
        if($id == $village->wid) return "You cant attack same village you are sending from.";

        return "";
    }
    
    /**
     * Returneaza trupele stationate in sat si/sau in oazele acestuia.
     *
     * IMPORTANT:
     * - fiecare reinforcement este procesat sub lock;
     * - fiecare oaza este procesata sub un lock separat;
     * - datele sunt re-citite din DB dupa obtinerea lock-ului;
     * - nu folosim cache pentru datele critice.
     *
     * @param int $wref
     * @param int $mode
     *
     * mode = 0:
     *     returneaza reinforcement-urile din sat + oazele lui
     *
     * mode = 1:
     *     returneaza doar reinforcement-urile din oazele satului
     */
    public function returnTroops($wref, $mode = 0) {
        global $database;

        $wref = (int) $wref;

        if ($wref <= 0) {
            return;
        }

        /*
         * Reinforcement-uri stationate direct in sat.
         *
         * Le procesam individual sub enforce lock.
         */
        if (!$mode) {
            $getenforce = $database->getEnforceVillage($wref, 0, false);

            if ($getenforce && count($getenforce)) {
                foreach ($getenforce as $enforce) {
                    if (!empty($enforce['id'])) {
                        $this->returnEnforcementRecord((int)$enforce['id']);
                    }
                }
            }
        }

        /*
         * OAZE
         *
         * getOasisEnforce() ne da toate reinforcement-urile din oazele
         * cucerite de acest sat.
         *
         * Nu procesam direct rezultatul deoarece acesta poate fi cached.
         * Extragem doar ID-urile oazelor si apoi fiecare oaza este re-citita
         * dupa obtinerea lock-ului.
         */
        $getenforce1 = $database->getOasisEnforce($wref, 1, false);

        if (!$getenforce1 || !count($getenforce1)) {
            return;
        }

        $oasisRefs = [];

        foreach ($getenforce1 as $enforce) {
            $oasisWref = isset($enforce['vref']) ? (int)$enforce['vref'] : 0;

            if ($oasisWref > 0) {
                $oasisRefs[$oasisWref] = true;
            }
        }

        foreach (array_keys($oasisRefs) as $oasisWref) {
            $this->returnOasisTroops($oasisWref);
        }
    }

    /**
     * Returneaza TOATE reinforcement-urile dintr-o singura oaza.
     *
     * Aceasta este protectia principala impotriva exploitului de duplicare.
     *
     * Doua request-uri simultane pentru aceeasi oaza:
     *
     *   Request A -> obtine lock
     *   Request B -> asteapta
     *
     *   A -> citeste reinforcement
     *   A -> creeaza movement
     *   A -> sterge reinforcement
     *   A -> release lock
     *
     *   B -> obtine lock
     *   B -> re-citeste DB
     *   B -> nu mai gaseste reinforcement
     *   B -> nu mai poate duplica nimic
     */
    public function returnOasisTroops($oasisWref) {
        global $database;

        $oasisWref = (int)$oasisWref;

        if ($oasisWref <= 0) {
            return false;
        }

        if (!$database->getOasisReturnLock($oasisWref)) {
            return false;
        }

        try {
            /*
             * IMPORTANT:
             * Re-fetch DIRECT din DB dupa lock.
             * Nu folosim getOasisEnforce(..., cache).
             */
            $reinforcements = $database->getOasisEnforceByWref(
                $oasisWref,
                false
            );

            if (!$reinforcements || !count($reinforcements)) {
                return false;
            }

            foreach ($reinforcements as $enforce) {
                if (empty($enforce['id'])) {
                    continue;
                }

                /*
                 * processReturnTroops() sterge reinforcement-ul dupa ce
                 * creeaza movement-ul de retur.
                 */
                $this->processReturnTroops($enforce);
            }

            /*
             * Comportamentul original:
             * dupa ce reinforcement-urile au fost returnate, oaza isi
             * regenereaza trupele naturale.
             */
            $database->regenerateOasisUnits($oasisWref);

            return true;

        } finally {
            $database->releaseOasisReturnLock($oasisWref);
        }
    }

    /**
     * Returneaza un singur reinforcement record.
     *
     * Protectie suplimentara pentru cazurile in care un sat este sters
     * sau reinforcement-ul este procesat dintr-o alta cale.
     */
    private function returnEnforcementRecord($enforceId) {
        global $database;

        $enforceId = (int)$enforceId;

        if ($enforceId <= 0) {
            return false;
        }

        if (!$database->getEnforceLock($enforceId)) {
            return false;
        }

        try {
            /*
             * Re-fetch dupa lock.
             */
            $enforce = $database->getEnforceArray(
                $enforceId,
                0,
                false
            );

            /*
             * Poate sa fi fost deja procesat de alt request.
             */
            if (!$enforce || empty($enforce['id'])) {
                return false;
            }

            $this->processReturnTroops($enforce);

            return true;

        } finally {
            $database->releaseEnforceLock($enforceId);
        }
    }

    /**
     * Creeaza movement-ul de retur pentru un reinforcement.
     *
     * ATENTIE:
     * Aceasta functie NU mai este responsabila de locking.
     * Lock-ul este facut de:
     *
     *   returnEnforcementRecord()
     *   returnOasisTroops()
     *
     * astfel incat sa avem o singura responsabilitate pentru lock.
     */
    private function processReturnTroops($enforce) {
        global $database;

        if (empty($enforce['id']) || empty($enforce['from'])) {
            return false;
        }

        $fromWref = (int)$enforce['from'];
        $oasisWref = (int)$enforce['vref'];

        /*
         * Satul de origine al reinforcement-ului.
         */
        $to = $database->getVillage($fromWref);

        if (!$to || empty($to['owner'])) {
            return false;
        }

        $tribe = (int)$database->getUserField(
            $to['owner'],
            'tribe',
            0
        );

        if ($tribe <= 0) {
            return false;
        }

        $start = ($tribe - 1) * 10 + 1;

        /*
         * Calculeaza timpul de mers Oaza -> Sat.
         */
        $troopsTime = $this->getWalkingTroopsTime(
            $fromWref,
            $oasisWref,
            $to['owner'],
            $tribe,
            $enforce,
            1
        );

        /*
         * BUG FIX:
         *
         * Codul vechi folosea:
         *
         * $from['owner']
         *
         * dar $from nu exista in aceasta functie.
         *
         * Proprietarul corect este $to['owner'].
         */
        $time = $database->getArtifactsValueInfluence(
            $to['owner'],
            $fromWref,
            2,
            $troopsTime
        );

        /*
         * Unit-urile din reinforcement sunt stocate in coloanele
         * corespunzatoare tribului proprietarului.
         */
        $t1  = (int)($enforce['u' . $start] ?? 0);
        $t2  = (int)($enforce['u' . ($start + 1)] ?? 0);
        $t3  = (int)($enforce['u' . ($start + 2)] ?? 0);
        $t4  = (int)($enforce['u' . ($start + 3)] ?? 0);
        $t5  = (int)($enforce['u' . ($start + 4)] ?? 0);
        $t6  = (int)($enforce['u' . ($start + 5)] ?? 0);
        $t7  = (int)($enforce['u' . ($start + 6)] ?? 0);
        $t8  = (int)($enforce['u' . ($start + 7)] ?? 0);
        $t9  = (int)($enforce['u' . ($start + 8)] ?? 0);
        $t10 = (int)($enforce['u' . ($start + 9)] ?? 0);
        $hero = (int)($enforce['hero'] ?? 0);

        /*
         * Nu cream movement pentru un reinforcement gol.
         */
        if (
            $t1 <= 0 &&
            $t2 <= 0 &&
            $t3 <= 0 &&
            $t4 <= 0 &&
            $t5 <= 0 &&
            $t6 <= 0 &&
            $t7 <= 0 &&
            $t8 <= 0 &&
            $t9 <= 0 &&
            $t10 <= 0 &&
            $hero <= 0
        ) {
            $database->deleteReinf((int)$enforce['id']);
            return false;
        }

        /*
         * Creeaza attack record pentru retur.
         */
        $reference = $database->addAttack(
            $fromWref,
            $t1,
            $t2,
            $t3,
            $t4,
            $t5,
            $t6,
            $t7,
            $t8,
            $t9,
            $t10,
            $hero,
            2,
            0,
            0,
            0,
            0
        );

        /*
         * Creeaza EXACT UN singur movement de retur.
         */
        $now = time();

        $database->addMovement(
            4,
            $oasisWref,
            $fromWref,
            $reference,
            $now,
            $now + $time
        );

        /*
         * Acum reinforcement-ul nu mai poate fi procesat din nou,
         * deoarece row-ul este sters IN TIMP CE lock-ul este detinut.
         */
        $database->deleteReinf((int)$enforce['id']);

        return true;
    }
    
    private function sendTroops($post) {
        global $form, $database, $village, $session;

        $data = $database->getA2b($post['timestamp_checksum']);
        $Gtribe = ($session->tribe == 1) ? "" : $session->tribe - 1;

        /**
         * A DOUA verificare, cea care conteaza cu adevarat.
         *
         * checkErrors() ruleaza pe formular, dar un sitter poate trimite POST-ul
         * de confirmare direct, sarind peste el. Aici e ultimul punct inainte ca
         * trupele sa fie scazute din sat, deci verificam din nou - pe tipul REAL
         * al misiunii, citit din baza de date ($data['type']), nu din $_POST.
         */
        if (($sitterError = $this->sitterMissionError($data['type'] ?? 0)) !== "") {
            $form->addError("error", $sitterError);
            $_SESSION['errorarray'] = $form->getErrors();
            $_SESSION['valuearray'] = $_POST;
            header("Location: a2b.php");
            exit;
        }

        for ($i = 1; $i < 10; $i++) {
            if (isset($data['u'.$i])) {
                if ($data['u'.$i] > $village->unitarray['u'.$Gtribe.$i]) {
                    $form->addError("error", "You can't send more units than you have");
                    break;
                }

                if ($data['u'.$i] < 0) {
                    $form->addError("error", "You can't send negative units.");
                    break;
                }
            }
        }

		if($data['u11'] > $village->unitarray['hero']) $form->addError("error", "You can't send more units than you have");
		if($data['u11'] < 0) $form->addError("error", "You can't send negative units.");   
		if($data['type'] != 1) $post['spy'] = 0; else $post['spy'] = $post['spy']?? 0;
        
        if($form->returnErrors() > 0){
            $_SESSION['errorarray'] = $form->getErrors();
            $_SESSION['valuearray'] = $_POST;
            header( "Location: a2b.php" );
            exit;
        }else{
            if (empty($data['id']) || !$database->claimA2b((int)$data['id'], $post['timestamp_checksum'])) {
                $form->addError("error", "This troop send request was already processed. Please try again.");
                $_SESSION['errorarray'] = $form->getErrors();
                $_SESSION['valuearray'] = $_POST;
                header("Location: a2b.php");
                exit;
            }

            $u = ($session->tribe == 1) ? "" : $session->tribe - 1;
            
            $database->modifyUnit(
                $village->wid,
                [
                    $u . "1",
                    $u . "2",
                    $u . "3",
                    $u . "4",
                    $u . "5",
                    $u . "6",
                    $u . "7",
                    $u . "8",
                    $u . "9",
                    $session->tribe . "0",
                    "hero"
                ],
                [
                    $data['u1'],
                    $data['u2'],
                    $data['u3'],
                    $data['u4'],
                    $data['u5'],
                    $data['u6'],
                    $data['u7'],
                    $data['u8'],
                    $data['u9'],
                    $data['u10'],
                    $data['u11']
                ],
                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                );
            
            $troopsTime = $this->getWalkingTroopsTime($village->wid, $data['to_vid'], $session->uid, $session->tribe, $data, 1, 'u');
            $time = $database->getArtifactsValueInfluence($session->uid, $village->wid, 2, $troopsTime);
            
            $this->resolveCatapultTargets($post, $data);
            
            if(!isset($post['spy'])) $post['spy'] = 0;
            
            $abdata      = $database->getABTech($village->wid);
            $reference   = $database->addAttack(($village->wid), $data['u1'], $data['u2'], $data['u3'], $data['u4'], $data['u5'], $data['u6'], $data['u7'], $data['u8'], $data['u9'], $data['u10'], $data['u11'], $data['type'], $post['ctar1'], $post['ctar2'], $post['spy'], $abdata['b1'], $abdata['b2'], $abdata['b3'], $abdata['b4'], $abdata['b5'], $abdata['b6'], $abdata['b7'], $abdata['b8']);
            $checkexist  = $database->checkVilExist($data['to_vid']);
            $checkoexist = $database->checkOasisExist($data['to_vid']);
            if($checkexist || $checkoexist) {
                $database->addMovement(3, $village->wid, $data['to_vid'], $reference, time(), ($time + time()));
                if ($database->hasBeginnerProtection($village->wid) == 1 && $checkexist) {
                    mysqli_query($database->dblink, "UPDATE " . TB_PREFIX . "users SET protect = 0 WHERE id = ".(int) $session->uid);
                }
            }
            
            if($form->returnErrors() > 0) {
                $_SESSION['errorarray'] = $form->getErrors();
                $_SESSION['valuearray'] = $_POST;
                header("Location: a2b.php" );
                exit;
            }
            
            header("Location: build.php?id=39");
            exit;
        }
    }

    private function resolveCatapultTargets(&$post, $data) {
        global $database, $village, $session;

        // Check if have WW owner have artefact Rivals great confusion or Artefact of the unique fool with that effect
        // If is a WW village you can target on WW , if is not a WW village catapults will target randomly.
        // Like it says : Exceptions are the WW which can always be targeted and the treasure chamber which can always be targeted, except with the unique artifact.
        // Fixed by Advocaite and Shadow - Optimized by iopietro

        $to_owner = $database->getVillageField($data['to_vid'], "owner");
        $rivalsGreatConfusion = $database->getArtifactsSumByKind($to_owner, $data['to_vid'], 7);

        $rallyPointLevel = ($village->resarray)['f39'];
        $invalidBuildings = $this->catapultInvalidBuildings($rallyPointLevel);

        // UM-W1: validarea per-tinta extrasa in catapultTargetAllowed(); comportament
        // identic cu vechile blocuri inline (aceleasi praguri, aceleasi fallback-uri).
        $post['ctar1'] = $this->normalizeCatapultTarget(
            $post['ctar1'] ?? 0, 1, $data, $rallyPointLevel, $invalidBuildings
        );
        $post['ctar2'] = $this->normalizeCatapultTarget(
            $post['ctar2'] ?? 0, 2, $data, $rallyPointLevel, $invalidBuildings
        );

        // Bug fix: Brewery (35) is Teuton-only, capital-only, but empire-wide —
        // the catapult-randomization side effect must be checked on the SENDER'S
        // CAPITAL (not $village->wid, the village the rally point belongs to,
        // which may not be the capital), and only while a Mead-Festival is
        // actually active there — not simply because the Brewery is built.
        $hasActiveBrewery = false;
        if ($session->tribe == 2) {
            $senderCapital = $database->getVillage($session->uid, 3);
            $hasActiveBrewery = $senderCapital
                && (int)$senderCapital['festival'] > time()
                && $database->getFieldLevelInVillage($senderCapital['wref'], 35) > 0;
        }

        $post['ctar1'] = $this->applyConfusion(
            $post['ctar1'] ?? 0, 1, $hasActiveBrewery, $rivalsGreatConfusion
        );
        $post['ctar2'] = $this->applyConfusion(
            $post['ctar2'] ?? 0, 2, $hasActiveBrewery, $rivalsGreatConfusion
        );
    }

    /**
     * UM-W1: lista cladirilor care nu pot fi tinta directa de catapulta, in
     * functie de nivelul Punctului de Adunare. Extrasa 1:1 din vechiul switch.
     */
    private function catapultInvalidBuildings($rallyPointLevel) {
        if ($rallyPointLevel >= 3 && $rallyPointLevel < 5) {
            $invalid = [];
            for ($i = 1; $i <= 50; $i++) {
                if (!in_array($i, [10, 11])) $invalid[] = $i;
            }
            return $invalid;
        }

        if ($rallyPointLevel >= 5 && $rallyPointLevel < 10) {
            $invalid = [];
            for ($i = 12; $i <= 50; $i++) $invalid[] = $i;
            return $invalid;
        }

        if ($rallyPointLevel >= 10) {
            // zidurile nu pot fi tinta directa (31,32,33 + cele noi 42,43,47,50)
            return [23, 31, 32, 33, 34, 36, 42, 43, 47, 50];
        }

        return [];
    }

    /**
     * UM-W1: validarea de baza a unei tinte de catapulta (ctar1 sau ctar2),
     * inainte de efectul de confuzie. $slot = 1 sau 2. Pastreaza exact regulile
     * vechi: ctar1 cere RP>=3, minim 1 catapulta si tinta valida; ctar2 cere
     * minim 20 catapulte, RP == 20 si cade pe 99 (random) cand tinta e invalida.
     */
    private function normalizeCatapultTarget($target, $slot, $data, $rallyPointLevel, array $invalidBuildings) {
        if ($slot == 1) {
            if ($target != 0) {
                if ($rallyPointLevel < 3 || $data['u8'] == 0 || in_array($target, $invalidBuildings) || $target < 0 || $target > 50) {
                    return 0;
                }
            }
            return $target;
        }

        // slot 2
        if ($target != 0) {
            if ($data['u8'] < 20 || $rallyPointLevel != 20) {
                return 0;
            }
            if (in_array($target, $invalidBuildings) || ($target < 0 || $target > 50 && $target != 99)) {
                return 99;
            }
        }
        return $target;
    }

    /**
     * UM-W1: efectul artefactului "Rivals' Great Confusion" / festivalul de bere.
     * ctar1 -> 0 (random), ctar2 -> 99 (random) cand tinta nu e WW(40) sau
     * Trezorerie(27, exceptata doar de artefactul unic). Comportament 1:1.
     */
    private function applyConfusion($target, $slot, $hasActiveBrewery, $rivalsGreatConfusion) {
        $random = ($slot == 1) ? 0 : 99;

        if ($slot == 2 && $target <= 0) {
            return 0;
        }

        if ($hasActiveBrewery) {
            return $random;
        }

        // Conditie pastrata IDENTIC cu originalul (verificata prin test de
        // echivalenta). Nota: ramura "$target == 27 && unique > 0" nu schimba
        // rezultatul - orice tinta != 40 ajunge random - dar o pastram 1:1 ca
        // sa nu introducem o schimbare de comportament subtila.
        if ($rivalsGreatConfusion['totals'] > 0) {
            if ($target != 40 && ($target != 27 || ($target == 27 && $rivalsGreatConfusion['unique'] > 0))) {
                return $random;
            }
        }

        return $target;
    }

    private function sendTroopsBack($post) {
        global $form, $database, $village, $session, $technology;

        if (!$database->getEnforceLock($post['ckey'])) return;
        try {
            // Re-fetch after lock to prevent TOCTOU race condition
            $enforce      = $database->getEnforceArray( $post['ckey'], 0 );
            $enforceoasis = $database->getOasisEnforceArray( $post['ckey'], 0 );
            if ( ( $enforce['from'] == $village->wid ) || ( $enforce['vref'] == $village->wid ) || ( $enforceoasis['conqured'] == $village->wid ) ) {
                $to     = $database->getVillage( $enforce['from'] );
                $Gtribe = ($ownerTribe = $database->getUserField( $to['owner'], 'tribe', 0)) == 1 ? "" : $ownerTribe - 1;

				for ($i = 1; $i <= 10; $i++) {
				if (!isset($post['t'.$i])) {
					$post['t'.$i] = 0;
				continue;
			}

				if (!is_numeric($post['t'.$i])) {
				$form->addError(
				"error",
				"Invalid troop amount."
			);
			break;
		}

			$post['t'.$i] = (int)$post['t'.$i];

			if ($post['t'.$i] < 0) {
				$form->addError(
				"error",
				"You can't send back negative units."
			);
			break;
		}

			if ($post['t'.$i] > (int)$enforce['u'.$Gtribe.$i]) {
			$form->addError(
            "error",
            "You can't send back more units than you have"
			);
			break;
		}
	}
                if ( isset( $post['t11'] ) ) {
                    if ( $post['t11'] > $enforce['hero'] ) {
                        $form->addError( "error", "You can't send back more units than you have" );
                    }

                    if ( $post['t11'] < 0 ) {
                        $form->addError( "error", "You can't send back negative units." );
                    }
                } else {
                    $post['t11'] = '0';
                }

                if ( $form->returnErrors() > 0 ) {
                    $_SESSION['errorarray'] = $form->getErrors();
                    $_SESSION['valuearray'] = $_POST;
                    $database->releaseEnforceLock($post['ckey']);
                    header( "Location: a2b.php" );
                    exit;
                } else {

                    /**
                     * Formularul de retragere trimite doar casutele completate,
                     * deci $post['t1'..'t10'] pot lipsi cu totul. Bucla de mai jos
                     * avea deja isset(), dar addAttack() si getWalkingTroopsTime()
                     * le citeau direct si scoteau "Undefined array key t10".
                     * Le normalizam o singura data, aici, ca toti consumatorii de
                     * mai jos sa primeasca numere.
                     */
                    for ($t = 1; $t <= 10; $t++) {
                        if (!isset($post['t'.$t]) || !is_numeric($post['t'.$t])) {
                            $post['t'.$t] = 0;
                        }
                    }

                    //change units
                    $tribe = $database->getUserField($to['owner'], 'tribe', 0);
                    $start = ($tribe - 1 ) * 10 + 1;
                    $end = $tribe * 10 ;

                    $units = [];
                    $amounts = [];
                    $modes = [];

                    $j = 1;
                    for ( $i = $start; $i <= $end; $i ++ ) {
                        $units[] = $i;
                        // Formularul trimite doar casutele completate; o unitate necompletata
                        // inseamna zero trupe din tipul acela.
                        $amounts[] = isset($post['t'.$j]) ? $post['t'.$j] : 0;
                        $modes[] = 0;
                        $j ++;
                    }

                    $units[] = 'hero';
                    $amounts[] = $post['t11'];
                    $modes[] = 0;

                    $database->modifyEnforce($post['ckey'], $units, $amounts, $modes);
                    $j++;

                    $troopsTime = $this->getWalkingTroopsTime($enforce['from'], $enforce['vref'], $to['owner'], $tribe, $post, 1, 't');
                    $time = $database->getArtifactsValueInfluence($session->uid, $village->wid, 2, $troopsTime);

                    $reference = $database->addAttack($enforce['from'], $post['t1'], $post['t2'], $post['t3'], $post['t4'], $post['t5'], $post['t6'], $post['t7'], $post['t8'], $post['t9'], $post['t10'], $post['t11'], 2, 0, 0, 0, 0);
                    $database->addMovement(4, $village->wid, $enforce['from'], $reference, time(), ($time + time()));
                    $technology->checkReinf($post['ckey'], false);

                    $database->releaseEnforceLock($post['ckey']);
                    header("Location: build.php?id=39&refresh=1");
                    exit();
                }
            }else{
                $form->addError("error", "You cant change someones troops.");
                if($form->returnErrors() > 0){
                    $_SESSION['errorarray'] = $form->getErrors();
                    $_SESSION['valuearray'] = $_POST;
                    $database->releaseEnforceLock($post['ckey']);
                    header("Location: a2b.php");
                    exit();
                }
            }
        } finally {
            $database->releaseEnforceLock($post['ckey']);
        }
    }
    
    public function Settlers($post) {
        global $form, $database, $village, $session;
        // FIX securitate (UM-D1): tablourile de CP (cp0..cpN) sunt globale, definite
        // in GameEngine/Data/cp.php. Fara aceste "global", ${'cp'.$mode} era null in
        // scope-ul metodei, deci $need_cps = null si "$cps >= $need_cps" trecea mereu -
        // verificarea de puncte de cultura la intemeierea satului era ocolita
        // server-side (doar UI-ul o respecta). Importam exact tabloul folosit.
        global ${'cp'.CP};

        $mode = CP;
        $total = count($database->getProfileVillages($session->uid));
        $need_cps = isset(${'cp'.$mode}[$total + 1]) ? ${'cp'.$mode}[$total + 1] : PHP_INT_MAX;
        $cps = $session->cp;
        $rallypoint = $database->getResourceLevel($village->wid);
        
        //-- Prevent user from founding a new village if there are not enough settlers or the player put an invalid village ID or an already occupied one
        //-- fix by AL-Kateb - Semplified and additions by iopietro
        if ($rallypoint['f39'] > 0 && $village->unitarray['u'.$session->tribe.'0'] >= 3 && isset($post['s']) && ($newvillage = $database->getMInfo($post['s']))['id'] > 0 && $newvillage['occupied'] == 0 && $newvillage['oasistype'] == 0) {
            if ($cps >= $need_cps) {
                $troopsTime = $this->getWalkingTroopsTime($village->wid, $newvillage['id'], 0, 0, [300], 0);
                $time = $database->getArtifactsValueInfluence($session->uid, $village->wid, 2, $troopsTime);
                
                $unit = ($session->tribe * 10);
                $database->modifyResource($village->wid, 750, 750, 750, 750, 0);
                $database->modifyUnit($village->wid, [$unit], [3], [0]);
                $database->addMovement(5, $village->wid, $post['s'], 0, time(), time() + $time);
            }
            header("Location: build.php?id=39");
            exit;
        } else {
            header("Location: dorf1.php");
            exit;
        }
    }

    public function Hero($uid, $all = 0, $include_dead = false) {
        global $database;
        $heroarray = $database->getHero($uid, $all, $include_dead);
        $herodata = false;
        $singleHeroArrayID = 0;

        // no hero data found
        if (!count($heroarray)) {
            return false;
        }

        // check all heroes and load hero data for the one,
        // whose data were updated more recently - if we're not getting all of them
        if (!$all) {
            foreach ($heroarray as $id => $hero) {
                // try to load a hero who's alive first
                if (!$herodata && $hero['dead'] != 1) {
                    // this global value comes from GameEngine/Data/unitdata.php
                    $herodata = $GLOBALS["h".$hero['unit']];
                    $singleHeroArrayID = $id;
                    break;
                }
            }

            // if we couldn't get a living hero,
            // resort to loading the first one from the list,
            // as that would be the one most recently updated/used
            if (!$herodata) {
                // this global value comes from GameEngine/Data/unitdata.php
                $herodata = $GLOBALS["h".$heroarray[0]['unit']];
            }

            return $this->buildHeroStats($heroarray[$singleHeroArrayID], $herodata);
        } else {
            // build up a full array of heroes and their stats
            $heroes = [];
            foreach ($heroarray as $id => $hero) {
                $herodata = $GLOBALS["h".$heroarray[$id]['unit']];
                $heroes[] = $this->buildHeroStats($heroarray[$id], $herodata);
            }
            
            return $heroes;
        }
    }

    /**
     * Builds the stat array for a single hero entry, deriving atk/di/dc/ob/db
     * from the unit's base data ($herodata) and the hero's own attack/defence.
     *
     * @param array $hero One entry from the getHero() result
     * @param array $herodata The unit base data ($GLOBALS["h".$hero['unit']])
     * @return array The hero stats array
     */
    private function buildHeroStats($hero, $herodata) {
        $h_atk = $herodata['atk'] + 5 * floor($hero['attack'] * $herodata['atkp'] / 5);
        $h_di = $herodata['di'] + 5 * floor($hero['defence'] * $herodata['dip'] / 5);
        $h_dc = $herodata['dc'] + 5 * floor($hero['defence'] * $herodata['dcp'] / 5);
        $h_ob = 1 + 0.002 * $hero['attackbonus'];
        $h_db = 1 + 0.002 * $hero['defencebonus'];

        return [
            'heroid' => $hero['heroid'],
            'unit' => $hero['unit'],
            'name' => $hero['name'],
            'inrevive' => $hero['inrevive'],
            'intraining' => $hero['intraining'],
            'trainingtime' => $hero['trainingtime'],
            'level' => $hero['level'],
            'attack' => $hero['attack'],
            'atk' => $h_atk,
            'defence' => $hero['defence'],
            'di' => $h_di,
            'dc' => $h_dc,
            'attackbonus' => $hero['attackbonus'],
            'ob' => $h_ob,
            'defencebonus' => $hero['defencebonus'],
            'db' => $h_db,
            'regeneration' => $hero['regeneration'],
            // Atributul T4 "Resources" si resursa aleasa. Sunt citite cu ?? 0
            // fiindca serverele care inca nu au rulat add-hero-resources.sql nu
            // au coloanele - pagina trebuie sa mearga si asa.
            'resources' => $hero['resources'] ?? 0,
            'res_type' => $hero['res_type'] ?? 0,
            'health' => $hero['health'],
            'dead' => $hero['dead'],
            'points' => $hero['points'],
            'experience' => $hero['experience']
        ];
    }

    /**
     * Function to kill/release prisoners
     * 
     * @param int the ID of the prisoners you want to release
     */
    
    /**
     * T4 hero port: destroy a NATURE enforcement row (captured oasis animals,
     * `from` = 0) stationed in the CURRENT village. Mirrors deletePrisoners'
     * ownership model: the row must belong to the village the player is
     * viewing, which the session already validated as theirs. Nature rows
     * never travel back (no owner village to walk to) - releasing kills them.
     */
    public function releaseNatureEnforcement($id){
        global $village, $database;

        $id = (int) $id;
        if ($id <= 0) {
            return false;
        }

        $stmt = $database->dblink->prepare(
            "DELETE FROM " . TB_PREFIX . "enforcement
              WHERE id = ? AND `from` = 0 AND vref = ? LIMIT 1"
        );
        $wid = (int) $village->wid;
        $stmt->bind_param('ii', $id, $wid);
        $stmt->execute();
        $ok = $stmt->affected_rows > 0;
        $stmt->close();
        return $ok;
    }

    public function deletePrisoners($id){
        global $village, $database, $session, $building, $bid19, $u99;
        
        $prisoner = $database->getPrisonersByID($id);
        $troops = 0;
        if($prisoner['wref'] == $village->wid){
            $p_owner = $database->getVillageField($prisoner['from'], "owner");
            $p_tribe = $database->getUserField($p_owner, "tribe", 0);
            
            $troopsTime = $this->getWalkingTroopsTime($prisoner['from'], $prisoner['wref'], $p_owner, $p_tribe, $prisoner, 1, 't');
            $p_time = $database->getArtifactsValueInfluence($p_owner, $prisoner['from'], 2, $troopsTime);
            
            $p_reference = $database->addAttack($prisoner['from'], $prisoner['t1'],$prisoner['t2'], $prisoner['t3'], $prisoner['t4'], $prisoner['t5'], $prisoner['t6'], $prisoner['t7'], $prisoner['t8'], $prisoner['t9'], $prisoner['t10'], $prisoner['t11'], 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
            $database->addMovement(4, $prisoner['wref'], $prisoner['from'], $p_reference, time(), ($p_time + time()));
            
            for($i = 1; $i <= 11; $i++) $troops += $prisoner['t'.$i];
            
            //Reset traps
            $database->modifyUnit($village->wid, ["99", "99o"], [$troops, $troops], [0, 0]);            
            $repairDuration = $database->getArtifactsValueInfluence($session->uid, $village->wid, 5, round(($bid19[max($building->getTypeLevel(36, $village->wid), 1)]['attri'] / 100) * $u99['time'] / SPEED));
            $database->trainUnit($village->wid, 99, $troops, $u99['pop'], $repairDuration, 0);           
            
            $database->deletePrisoners($prisoner['id']);
        }else if($prisoner['from'] == $village->wid){    
            $prisonersToOwner = $database->getVillageField($prisoner['wref'], "owner");
            
            for($i = 1; $i <= 11; $i++) $troops += $prisoner['t'.$i];
            
            if($prisoner['t11'] > 0){
                $p_owner = $database->getVillageField($prisoner['from'], "owner");
                mysqli_query($database->dblink, "UPDATE ".TB_PREFIX."hero SET `dead` = '1', `health` = '0' WHERE `uid` = '".$p_owner."' AND dead = 0");
            }
        
            //Reset traps
            $database->modifyUnit($prisoner['wref'], ["99", "99o"], [$troops, $troops], [0, 0]);   
            
            if(($troops = round($troops / 3)) > 0){
                $repairDuration = $database->getArtifactsValueInfluence($prisonersToOwner, $prisoner['wref'], 5, round(($bid19[max($building->getTypeLevel(36, $prisoner['wref']), 1)]['attri'] / 100) * $u99['time'] / SPEED));
                $database->trainUnit($prisoner['wref'], 99, $troops, $u99['pop'], $repairDuration, 0);
            }                       
            
            $database->deletePrisoners($prisoner['id']);
        }
        
        header("Location: build.php?id=39");
        exit;
    }
    
    /**
     * Get how much time troops spend to walk from a village to another
     * 
     * @param int $from The start village ID
     * @param int $to The target village ID
     * @param int $owner The owner of the troops
     * @param int $tribe The tribe of the owner's troops
     * @param array $unitArray The array containing troops count if mode is 0, otherwise it'll contains the troop speed
     * @param int $mode How the time should be calculated
     * @return int Returns the time troops take to walk from a village to another
     */
    
    public function getWalkingTroopsTime($from, $to, $owner, $tribe, $unitArray, $mode, $unit = ""){
        global $generator, $database;

        $fromCoor = $database->getCoor($from);
        $toCoor = $database->getCoor($to);
        $fromCor = ['x' => $fromCoor['x'], 'y' => $fromCoor['y']];
        $toCor = ['x' => $toCoor['x'], 'y' => $toCoor['y']];
        
        if(!$mode) return $generator->procDistanceTime($fromCor, $toCor, $unitArray[0], $mode, $from);

        $start = ($tribe - 1) * 10 + 1;
        $end = $tribe * 10;
        
        $speeds = [];
        
        //Find slowest unit
        if(!empty($unit)){
            for($i = 1; $i <= 11; $i++){
                if(isset($unitArray[$unit.$i]) && $unitArray[$unit.$i] > 0) $unitArray[$i - 1] = $unitArray[$unit.$i];
                else $unitArray[$i - 1] = 0;
            }    
        }else{
            for($i = $start; $i <= $end; $i++){
                if(isset($unitArray['u'.$i]) && $unitArray['u'.$i] > 0) $unitArray[$i - $start] = $unitArray['u'.$i];
                else $unitArray[$i - $start] = 0;
            } 
            
            if(isset($unitArray['hero']) && $unitArray['hero'] > 0){
                $unitArray[10] = $unitArray['hero'];
            }
            else $unitArray[10] = 0;
        }

        for($i = 0; $i <= 9; $i++){
            if(isset($unitArray[$i]) && $unitArray[$i] > 0){
                $speeds[] = $GLOBALS['u'.($i + $start)]['speed'];
            }
        }
        
        if(isset($unitArray[10]) && $unitArray[10] > 0){
            $heroUnit = $database->getHeroField($owner, 'unit');
            $speeds[]  = $GLOBALS['u'.$heroUnit]['speed'];
        }

        // Defensive: the tribe-offset loop above only inspects the owner's
        // own 10 unit columns, so an army made purely of foreign/nature units
        // (e.g. captured oasis animals) yields no speeds and min() would
        // fatal (the "Send back" crash). Unreachable through normal flows
        // now that nature rows disable Send back, but never crash movement
        // creation - fall back to the slowest common speed.
        if (empty($speeds)) {
            $speeds[] = 3;
        }

        $walkTime = $generator->procDistanceTime($fromCor, $toCor, min($speeds), $mode, $from);

        // T4 hero port (Phase 5): boots (>20 tiles), pennant (own villages)
        // and standard (ally villages) shorten the trip when the hero rides
        // along. Returns the input unchanged when NEW_FUNCTIONS_HERO_T4 is off.
        return HeroBattleBonus::adjustTravelTime($owner, $unitArray[10] ?? 0, $from, $to, $walkTime);
    }
    
    public function startRaidList($post){   
    	global $database, $generator, $session;

    	/**
    	 * Lista de ferme trimite raiduri, deci intra tot sub SITTER_PERM_RAID.
    	 * Fara garda aici, un sitter caruia i s-ai taiat raidurile le-ar fi
    	 * putut porni oricum din Punctul de Adunare.
    	 */
    	if (($sitterError = $this->sitterMissionError(4)) !== "") {
    		$_SESSION['errorarray'] = array($sitterError);
    		header("Location: build.php?id=39&t=99");
    		exit();
    	}

    	$slots = $post['slot'] ?? [];
    	if(empty($slots)){
    		header("Location: build.php?id=39&t=99");
    		exit();
    	}
    	
		$tribe = $session->tribe;

		foreach($slots as $slot){
			$raidList = $database->getRaidList($slot);
			$getFLData = $database->getFLData($raidList['lid']);

			//Check if we're trying to start our raidlists or other players raidlist
			if($getFLData['owner'] != $session->uid) continue;
				
			//Get the units in the village
			$villageUnits = $database->getUnit($getFLData['wref'], false);
			
			$sid = $raidList['id'];
			$wref = $raidList['towref'];
			
			for($i = 1; $i <= 6; $i++) ${'t'.$i} = $raidList['t'.$i];

			if(!$database->isVillageOases($wref)) $villageOwner = $database->getVillageField($wref, 'owner');
		    else $villageOwner = $database->getOasisField($wref, 'owner');
		    
			$userAccess = $database->getUserField($villageOwner, 'access', 0);
			$userID = $database->getUserField($villageOwner, 'id', 0);
			
			if($userAccess != 0 && !($userAccess == MULTIHUNTER && $userID == 5) && ($userAccess != ADMIN || (ADMIN_ALLOW_INCOMING_RAIDS && $userAccess == ADMIN))){
				
				//Start = the first troop of the player's tribe
				//End =  the last selectable troop of the player's tribe
				$start = ($session->tribe - 1) * 10 + 1;
				$end = $start + 5; 
				
				//Check if we've enough troops
				$canSend = true;
				for($i = $start; $i <= $end; $i++){
					if($villageUnits['u'.$i] < ${'t'.($i - $start + 1)}){
						$canSend = false;
						break;
					}
				}

				//Send the attack
				if($canSend){				
					$ckey = $generator->generateRandStr(6);
					$id = $database->addA2b($ckey, 0, $wref, $t1, $t2, $t3, $t4, $t5, $t6, 0, 0, 0, 0, 0, 4);
					$data = $database->getA2b($ckey);
					
					$troopsTime = $this->getWalkingTroopsTime($getFLData['wref'], $data['to_vid'], $session->uid, $session->tribe, $data, 1, 'u');
					$time = $database->getArtifactsValueInfluence($getFLData['owner'], $getFLData['wref'], 2, $troopsTime);

					$abdata = $database->getABTech($getFLData['wref']);
					$reference = $database->addAttack(($getFLData['wref']), $data['u1'], $data['u2'], $data['u3'], $data['u4'], $data['u5'], $data['u6'], 0, 0, 0, 0, 0, $data['type'], 0, 0, 0, $abdata['b1'], $abdata['b2'], $abdata['b3'], $abdata['b4'], $abdata['b5'], $abdata['b6'], $abdata['b7'], $abdata['b8']);
					
					$troops = [];
					$amounts = [];
					$modes = [];
					
					for($u = $start; $u <= $end; $u++){					
						$troops[] = $u;
						$amounts[] = $data['u'.($u - $start + 1)];
						$modes[] = 0;
					}

					$database->modifyUnit($getFLData['wref'], $troops, $amounts, $modes);
					$database->addMovement(3, $getFLData['wref'], $data['to_vid'], $reference, time(), ($time + time()));
					
					//Prevent re-use of the same attack via re-POSTing the same data
					$database->remA2b($id);
				}
			}
		}
		header("Location: build.php?id=39&t=99");
		exit();
    }
};
$units = new Units;

?>