<?php
require_once('config.php');
require_once('packetstream.php');
require_once('protocols.php');
require_once('pclzip.lib.php');
require_once('scoring_config.php');
require_once('boss_name.php');
require_once('task_name.php');
require_once('achievement_name.php');
$factrole = array(2=>"Глава",3=>"Маршал",4=>"Капитан",5=>"Сержант",6=>"");
$gender = array("М","Ж");
mb_internal_encoding("UTF-8");

class my_db extends mysqli {
	function query($sql, $resultmode = NULL) {
		//$dh = fopen ('mysql_sql.log', 'a+');
		//if ($dh) {
		//	fwrite($dh, date("Y-m-d H:i:s").' '.$sql."\n\n");
		//	fclose($dh);
		//}

		$res = parent::query($sql, $resultmode);
		if (!$res) {
			$msg = $this->error;
			$dh = fopen ('mysql_error.log', 'a+');
			if ($dh) {
				fwrite($dh, date("Y-m-d H:i:s").' '.$sql."\n");
				fwrite($dh, $msg."\n\n");
				fclose($dh);
			}
		}
		return $res;
	}
}

$db = new my_db($mysql_host, $mysql_user, $mysql_pass, $mysql_dbname);
if ($db->connect_errno) {
    die('ErrorBase');
}

$occ = array(0 => 'Новичок', 1 => 'Вим I', 2 => 'Вим II', 3 => 'Вим III', 4 => 'Хаккан I', 5 => 'Хаккан II', 6 => 'Хаккан III', 7 => 'Айне I', 8 => 'Айне II', 9 => 'Айне III', 10 => 'Скайя I', 11 => 'Скайя II', 12 => 'Скайя III', 13 => 'Вим IV', 14 => 'Вим V', 16 => 'Хаккан IV', 17 => 'Хаккан V', 19 => 'Айне IV', 20 => 'Айне V', 22 => 'Скайя IV', 23 => 'Скайя V', 25 => 'Морто I', 26 => 'Морто II', 27 => 'Морто III', 28 => 'Морто IV', 29 => 'Морто V', 33 => 'Титаны I', 34 => 'Титаны II', 35 => 'Титаны III', 36 => 'Титаны IV', 37 => 'Титаны V', 39 => 'Арден I', 40 => 'Арден II', 41 => 'Арден III', 42 => 'Арден IV', 43 => 'Арден V', 45 => 'Умбра I', 46 => 'Умбра II', 47 => 'Умбра III', 48 => 'Умбра IV', 49 => 'Умбра V', 51 => 'Вайлин I', 52 => 'Вайлин II', 53 => 'Вайлин III', 54 => 'Вайлин IV', 55 => 'Вайлин V', 56 => 'Ниру I', 57 => 'Ниру II', 58 => 'Ниру III', 59 => 'Ниру IV', 60 => 'Ниру V', 64 => 'Тайо I', 65 => 'Тайо II', 66 => 'Тайо III', 67 => 'Тайо IV', 68 => 'Тайо V', 96 => 'Войда I', 97 => 'Войда II', 98 => 'Войда III', 99 => 'Войда IV', 100 => 'Войда V', 102 => 'Психея I', 103 => 'Психея II', 104 => 'Психея III', 105 => 'Психея IV', 106 => 'Психея V', 108 => 'Кентавр I', 109 => 'Кентавр II', 110 => 'Кентавр III', 111 => 'Кентавр IV', 112 => 'Кентавр V', 117 => 'Гидран I', 118 => 'Гидран II', 119 => 'Гидран III', 120 => 'Гидран IV', 121 => 'Гидран V' , 71 => 'Сейра I', 72 => 'Сейра II', 73 => 'Сейра III', 74 => 'Сейра IV', 75 => 'Сейра V');

$cult = array(0=>'Нет', 1=>'Эйдос', 2=>'Малеус', 4=>'Феор');

$map_names = array(2=>'Айне', 3=>'Скайя', 4=>'Волнодол', 5=>'Мертвая Трясина', 6=>'Дикие Земли', 7=>'Долина Благовоний', 8=>'Южные Рубежи', 9=>'Грот крыланов', 10=>'Куньлунь', 11=>'Асур', 12=>'Пещера Черного камня', 13=>'Поле боя', 14=>'Грот Крыланов', 15=>'Рай горы Куньлунь', 16=>'Лунная обитель', 17=>'Чистилище', 18=>'Владения Хаоса', 19=>'Земли Созидания', 20=>'Новая область', 21=>'Примечание: Не доступно.', 101=>'Поле боя 1', 102=>'Поле боя 2', 103=>'Поле боя 3', 104=>'Поле боя 4', 105=>'Поле боя 5', 106=>'Поле боя 6', 107=>'Поле боя 7', 108=>'Поле боя 8', 109=>'Поле боя 9', 110=>'Поле боя 10', 111=>'Поле боя 11', 112=>'Поле боя 12', 113=>'Башня Меча истины', 114=>'Деревня Травников', 115=>'Небесная сокровищница', 121=>'Поле боя \'Перерождение\' 1', 122=>'Поле боя \'Перерождение\' 2', 123=>'Поле боя \'Перерождение\' 3', 124=>'Поле боя \'Перерождение\' 4', 125=>'Поле боя \'Перерождение\' 5', 126=>'Поле боя \'Перерождение\' 6', 127=>'Поле боя \'Перерождение\' 7', 128=>'Поле боя \'Перерождение\' 8', 131=>'Чу-Хань поле боя', 133=>'Арена', 134=>'Арена Перерождения', 201=>'Форт 01', 202=>'Форт 02', 309=>'Грот Крыланов 2', 312=>'Лисий холм 2', 314=>'Грот Крыланов 2', 319=>'Мир родства', 321=>'Примечание: Не доступно.', 501=>'Война за территорию нач. ур. 01');

$territory_names = array(1=>'Заобл.крепость', 2=>'Царьград', 3=>'Город.Хэян', 4=>'Гора.Айне', 5=>'Храм.Скайи', 6=>'Поселок.Прудов.', 7=>'Лихой.Стан', 8=>'Дикие.земли', 9=>'Зал.Наблюдений', 10=>'Пещера.сед.мили', 11=>'Поселок.Хладост', 12=>'Дворец.Асура', 13=>'Дворец.Пашни', 14=>'Древние.Земли', 15=>'Деревня.Травник', 16=>'Хижина.Иньшо', 17=>'Сторожевая.гора', 18=>'Лунная.обитель', 19=>'Лес.Бамбука', 20=>'Пик.М.бамбука', 21=>'Нетронут.стена', 22=>'Земля.Благород.', 23=>'Пагода.Множеств', 24=>'Лунный.колодец', 25=>'Камень.верности', 26=>'Зал.бесед', 27=>'Небесн.сокров.', 28=>'Скверные.холмы', 29=>'Зал.долголетия', 30=>'Зал.реинкарнац.', 31=>'Лазурное.озеро', 32=>'Пустыня.облаков', 33=>'Храм.мист.огня', 34=>'Отмель.мурлоков', 35=>'Лисья.тюрьма', 36=>'Змеиные.покои', 37=>'Тридевятые.горы', 38=>'Лагерь.Ливней', 39=>'Небесное.озеро', 40=>'Тихий.ручей', 41=>'Гора.свеж.ветра', 42=>'Кровавая.топь', 43=>'Терраса.вишни', 44=>'Храм.Лотоса', 45=>'Пурпурный.город', 46=>'Алтарь.королей', 47=>'Цветочное.море', 48=>'Небесное.прист.', 49=>'Пещера.обсерв.', 50=>'Терраса.восхож.');

function GetOccupationName($o)
{
	global $occ;
	if (array_key_exists($o, $occ)) return $occ[$o];
	return $o;
}
function GetUserDonateSumm($login, $userid = '', $to_user_id = false) {
	global $db, $act_bonus, $accumulation_system, $acc_param, $bon, $cur_accum_id, $servid;
	$bon = 0;
	$cur_accum_id = 0;

	if ($login == '' && $userid == '') return 0;
	if (!$accumulation_system && !$to_user_id) return 0;

	$sum = 0;

	// Unitpay
	if ($userid == '') {
		$sql = sprintf("SELECT SUM(`out_summ`) as `sum` FROM `donate_unitpay` WHERE `login`='%s' AND `status`>=100 AND `serv`=%d", $db->real_escape_string($login), $servid);
	} else {
		$sql = sprintf("SELECT SUM(`out_summ`) as `sum` FROM `donate_unitpay` WHERE `userid`=%d AND `status`>=100 AND `serv`=%d", $userid, $servid);
	}
	if ($res = $db->query($sql)) {
		if ($row = mysqli_fetch_assoc($res)) {
			if (!is_null($row['sum'])) $sum += $row['sum'];
		}
	}

	// FreeKassa
	if ($userid == '') {
		$sql = sprintf("SELECT SUM(`out_summ`) as `sum` FROM `donate_freekassa` WHERE `login`='%s' AND `status`>=100 AND `serv`=%d", $db->real_escape_string($login), $servid);
	} else {
		$sql = sprintf("SELECT SUM(`out_summ`) as `sum` FROM `donate_freekassa` WHERE `userid`=%d AND `status`>=100 AND `serv`=%d", $userid, $servid);
	}
	if ($res = $db->query($sql)) {
		if ($row = mysqli_fetch_assoc($res)) {
			if (!is_null($row['sum'])) $sum += $row['sum'];
		}
	}

	// SeverPay — добавляем сюда
	if ($userid == '') {
		$sql = sprintf("SELECT SUM(`out_summ`) as `sum` FROM `donate_severpay` WHERE `login`='%s' AND `status`>=100 AND `serv`=%d", $db->real_escape_string($login), $servid);
	} else {
		$sql = sprintf("SELECT SUM(`out_summ`) as `sum` FROM `donate_severpay` WHERE `userid`=%d AND `status`>=100 AND `serv`=%d", $userid, $servid);
	}
	if ($res = $db->query($sql)) {
		if ($row = mysqli_fetch_assoc($res)) {
			if (!is_null($row['sum'])) $sum += $row['sum'];
		}
	}

	// Manual донаты
	if ($userid == '') {
		$sql = sprintf("SELECT SUM(`out_summ`) as `sum` FROM `donate_manual` WHERE `login`='%s' AND `status`>=100", $db->real_escape_string($login));
	} else {
		$sql = sprintf("SELECT SUM(`out_summ`) as `sum` FROM `donate_manual` WHERE `userid`=%d AND `status`>=100", $userid);
	}
	if ($res = $db->query($sql)) {
		if ($row = mysqli_fetch_assoc($res)) {
			if (!is_null($row['sum'])) $sum += $row['sum'];
		}
	}

	// Подсчёт бонусов за накопление
	if ($sum > 0 && count($acc_param) > 0) {
		foreach ($acc_param as $i => $val) {
			if ($sum >= $val->summ) {
				$bon = $val->bonus;
				$cur_accum_id = $i;
			}
		}
	}
	$act_bonus += $bon;
	return $sum;
}

function query2mysql($sql, $check_num_rows = false){
	global $db;
	$res = $db->query($sql);	
	if (!$res || $db->errno > 0 || ($check_num_rows && $res->num_rows < 1)) {
		die('15');
	}
	return $res;
}

function GetRoleName($roleId) {
    $role = @GetRole($roleId, 63);
    if (is_array($role) && isset($role['value']['name'])) {
        return $role['value']['name'] ?: 'Unknown';
    }
    return 'Unknown';
}

function getElo($playerId) {
    global $db;
    $sql = "SELECT stats FROM players WHERE player = " . intval($playerId);
    $res = $db->query($sql);
    if ($res && $res->num_rows > 0) {
        $row = $res->fetch_assoc();
        return (int)$row['stats'];
    }
    return 0;
}

function getEloRank($elo) {
    global $db;
    $sql = "SELECT COUNT(*) as higher FROM players WHERE stats > $elo";
    $res = $db->query($sql);
    if ($res && $res->num_rows > 0) {
        $row = $res->fetch_assoc();
        return (int)$row['higher'] + 1;
    }
    return 0;
}

if (!function_exists('GetRoleData')) {
    function GetRoleData($roleid, $data_mask = 0xFFFFFFFF) {
        global $pp;
        $send_data = array(
            'roleid' => $roleid,
            'data_mask' => $data_mask
        );
        $send_structure = array(
            'roleid' => 'int',
            'data_mask' => 'int'
        );
        $receive_structure = array(
            'retcode' => 'int',
            'value' => array(
                'base' => array(
                    'name' => 'string',
                    'lastlogin_time' => 'int'
                ),
                'base2' => array(
                    'runescore' => 'int',
                    'deity_level' => 'int'
                ),
                'status' => array(
                    'battlescore' => 'int',
                    'talismanscore' => 'int',
                    'reputation' => 'int',
                    'exp' => 'int64',
                    'time_used' => 'int',
                    'level' => 'int',
                    'occupation' => 'int',
                    'cultivation' => 'int',
                    'credit' => 'Octets'
                ),
                'reborndata' => 'Octets',
                'task' => array(
                    'task_data' => 'Octets',
                    'task_complete' => 'Octets',
                    'task_finishtime' => 'Octets',
                    'task_inventory' => 'GRoleInventoryVector'
                ),
                'achievement' => array(
                    'version' => 'int',
                    'achieve_map' => 'Octets',
                    'achieve_active' => 'Octets',
                    'achieve_spec_info' => 'Octets',
                    'achieve_award_map' => 'Octets',
                    'reserved4' => 'int',
                    'reserved1' => 'int',
                    'reserved2' => 'int',
                    'reserved3' => 'int'
                ),
                'monster_kill' => array(
                    'kill_info' => array(
                        'index' => 'int',
                        'kill_count' => 'int'
                    )
                ),
                'pocket' => array( // Добавляем pocket для экипировки
                    'capacity' => 'unsigned int',
                    'timestamp' => 'int',
                    'money' => 'unsigned int',
                    'items' => 'GRoleInventoryVector',
                    'equipment' => 'GRoleInventoryVector',
                    'petbadge' => 'GRoleInventoryVector',
                    'petequip' => 'GRoleInventoryVector',
                    'pocket_capacity' => 'short',
                    'pocket_items' => 'GPocketInventoryVector',
                    'fashion' => 'GRoleInventoryVector',
                    'mountwing' => 'Octets',
                    'gifts' => 'GRoleInventoryVector'
                ),
                'credit_details' => 'Octets',
                'money' => 'int'
            )
        );
        return $pp->SendToGameDB(8003, $send_data, $send_structure, $receive_structure);
    }
}

function decodeOctets($octets) {
    if (empty($octets)) return '';
    $hex_clean = str_replace(' ', '', $octets);
    $bin = @hex2bin($hex_clean);
    if ($bin !== false) {
        $decoded = mb_convert_encoding(trim($bin), 'UTF-8', 'UTF-8');
        $decoded = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $decoded);
        return $decoded;
    }
    return htmlspecialchars($octets);
}

function getPaginatedUrl($params = array(), $fragment = '') {
    $query = $_GET;
    foreach ($params as $key => $value) {
        $query[$key] = $value;
    }
    $url = '?' . http_build_query($query);
    if (!empty($fragment)) {
        $url .= '#' . $fragment;
    }
    return $url;
}

function GetClassImageName($occupation) {
    if ($occupation == 'Новичок' || $occupation == 'Неизвестно') {
        return '';
    }
    $parts = explode(' ', $occupation);
    return $parts[0];
}

function GetSkillName($id) {
    $skill_names = array(1=>'Сила Единения', 2=>'Одна судьба', 3=>'Командный дух', 4=>'Общая удача');
    if (isset($skill_names[$id])) return $skill_names[$id];
    return 'Неизвестный навык';
}

function getRankName($rep) {
    if ($rep < 300) return 'Нет ранга';
    if ($rep < 1000) return 'I';
    if ($rep < 2500) return 'II';
    if ($rep < 5000) return 'III';
    if ($rep < 10000) return 'IV';
    if ($rep < 35000) return 'V';
    if ($rep < 100000) return 'VI';
    if ($rep < 200000) return 'VII';
    if ($rep < 300000) return 'VIII';
    return 'IX';
}

function parseCreditHex($hex) {
    if (!is_string($hex)) {
        return array();
    }
    $hex = str_replace(' ', '', $hex);
    $bin = hex2bin($hex);
    if ($bin === false) {
        return array();
    }
    $values = array();
    for ($i = 0; $i < strlen($bin); $i += 4) {
        $field = substr($bin, $i, 4);
        $val = unpack('V', $field)[1];
        $values[] = $val;
    }
    return $values;
}

function GetCashFromAcc($id, $login=''){
	global $db;
	if ($login == '') $sql = "SELECT `ID`,`name`,`lkgold`, `lksilver` FROM `users` WHERE `ID`=".$id; else
			  $sql = "SELECT `ID`,`name`,`lkgold`, `lksilver` FROM `users` WHERE `name`='".$db->real_escape_string($login)."'";
	$result = query2mysql($sql);
	if ($result->num_rows > 0){
		$row = mysqli_fetch_assoc($result);
		$a = array($row['lkgold'], $row['lksilver'], $row['ID'], $row['name']);
	} else $a = array(0, 0, 0, '');
	return $a;
}

function AddLKLogs($id, $ip, $gpoint, $spoint, $desc) {
	global $db;
	$db->query("SET NAMES utf8");
	$CashLeft = GetCashFromAcc($id);
	$query = "insert into `lklogs` (`userid` ,`data` , `ip`, `gold` ,`silver`, `gold_rest` ,`silver_rest`, `desc` ) VALUES ($id, now(), '".$db->real_escape_string($ip)."', -$gpoint, -$spoint, ".$CashLeft[0].", ".$CashLeft[1].", '".$db->real_escape_string($desc)."');";
	return $db->query($query);	
}

function GiveGold($user_id, $gold, $silver, $desc, $ip = ''){
	if ($gold != 0 || $silver != 0)	$res = query2mysql("UPDATE `users` SET `lkgold`=`lkgold`+$gold, `lksilver`=`lksilver`+$silver WHERE `ID`=$user_id");
	AddLKLogs($user_id, $ip, $gold, $silver, $desc);
}

function UseCash($id, $zoneid, $aid, $silver)
{
	global $db;
	$sql="call usecash(".$id.",".$zoneid.",0,".$aid.",0,".$silver.",1,@error)";
	return query2mysql($sql);
}

function AddRoleToTop($rid)
{
	global $db;
	$role = GetRole($rid, 63);
	if ($role['retcode'] != 0) die('Get role '.$rid.' error '.$role['retcode']);
	if ($role['value']['status']['level'] == 0) return 0;
	$res1 = $db->query('DELETE FROM `top` WHERE `roleid`='.$rid);
	if (!$res1) die($db->error);
	$sql = "INSERT INTO `top` (`add_date`,roleid, userid, rolename, rolelevel, reborn, pkvalue, rolegender, roleprof, rolerep, factionid, factionrole, hp, mp, timeused,cashadd,cashtotal,cashused) VALUES (now(),'".$rid."', '".$role['value']['userid']."', '".$db->real_escape_string($role['value']['name'])."', '".$role['value']['status']['level']."', '".(int)($role['value']['status']['reborndata']!='')."', '".$role['value']['status']['pkvalue']."', '".$role['value']['gender']."', '".$role['value']['status']['occupation']."', '".$role['value']['status']['reputation']."', ".$role['value']['factionid'].", ".$role['value']['title'].", '".$role['value']['status']['hp']."', '".$role['value']['status']['mp']."', '".$role['value']['status']['time_used']."', '".$role['value']['cash_add2']."', '".$role['value']['cash_total']."', '".$role['value']['cash_used']."');";
	$result = $db->query($sql);
	if ($db->errno > 0) die($db->error);
	return $role['value']['factionid'];
}

function InitItem()
{
	global $Structures;
	$pp = new Protocols();
	$i = $pp->unmarshal('', $Structures['GRoleInventory']);				
	return $i;
}

function CheckRoleLvl($userid, $level_need){
	$f = GetUserRoles($userid);
	if ($f['retcode'] != 0) return false;
	$fnd = false;
	$answ['roles_count'] = count($f['data']);	
	foreach ($f['data'] as $i => $val){
		if ($val['level'] >= $level_need) {
			$fnd = $val['name'];
			break;
		}
	}
	return $fnd;
}

function ShowCost($cost){
	global $gpoint, $spoint;
	$c = explode('|', $cost);
	$gpoint = (int)$c[0];
	if (count($c) > 1) $spoint = (int)$c[1]; else $spoint = 0;	
}

function MakeAnswer($paramarray, $errorcode=0, $errtxt='', $errparam1='', $errparam2='', $errparam3=''){
	$answer = array(
		'errorcode' => $errorcode,
		'errtxt' => $errtxt,
		'errparam1' => $errparam1,
		'errparam2' => $errparam2,
		'errparam3' => $errparam3
	);
	if (is_array($paramarray)) $answer = array_merge($paramarray, $answer);
	echo serialize($answer);
	die();
}

function MakeAnswerErrGetRoles($answ)
{
	MakeAnswer($answ, 1000, 'Ошибка получения списка персонажей');
}

function MakeAnswerErrRolesCount($answ)
{
	MakeAnswer($answ, 1000, 'Сначала создайте персонажа в игре');
}

function finditem($id,$itemid){
	$cnt = 0;
	$rd = GetRoleData($id);
	if ($rd['retcode'] !=0 || $rd['packet_check_error'] != 0) return $cnt;
	foreach ($rd['value']['pocket']['items'] as $i => $val){
		if ($val['id'] == $itemid) $cnt += $val['count'];
	}
	foreach ($rd['value']['storehouse']['items'] as $i => $val){
		if ($val['id'] == $itemid) $cnt += $val['count'];
	}
	return $cnt;
}

function takeitem($id,$itemid,$count){
	$cnt = $count;
	$rd = GetRoleData($id);
	if ($rd['retcode'] !=0 || $rd['packet_check_error'] != 0) die('13');	
	foreach ($rd['value']['pocket']['items'] as $i => $val){
		if (($val['id'] == $itemid)&&($cnt>0)) {
			if ($cnt >= $val['count']) {
				$cnt -= $val['count'];
				$rd['value']['pocket']['items'][$i]['count'] = 0;
				unset($rd['value']['pocket']['items'][$i]);
			} else {
				$rd['value']['pocket']['items'][$i]['count'] -= $cnt;
				$cnt = 0;				
			}			
		}
	}
	if ($cnt > 0)
	foreach ($rd['value']['storehouse']['items'] as $i => $val){
		if (($val['id'] == $itemid)&&($cnt>0)) {
			if ($cnt >= $val['count']) {
				$cnt -= $val['count'];
				$rd['value']['storehouse']['items'][$i]['count'] = 0;
				unset($rd['value']['storehouse']['items'][$i]);
			} else {
				$rd['value']['storehouse']['items'][$i]['count'] -= $cnt;
				$cnt = 0;				
			}			
		}
	}
	if ($cnt == 0) $rd = PutRoleData($id, $rd['value']); else return false;
	if (!$rd || $rd['retcode'] != 0) return false;
	return true;
}

class ExtItem {
	var	$name='';
	var	$icon='';
	var	$list=0;
}

class BonusItem {
	var	$ids;
	var	$names;
	
	function BonusItem(){
		$this->ids = array();
		$this->names = array();
	}
}

function isonline($id, $roleid, $sleep_time=3, $check1min = true){
	global $db;
	$ban_reason = 'Сохранение персонажа';
	$l = GMListOnlineUser(0, 0, $id, '');
	if (isset($l['userlist']) && count($l['userlist']) > 0) 
	{
		foreach ($l['userlist'] as $i => $val)
		{
			if ($val['userid'] == $id) die('25');
		}
	}
	if ($check1min) {
		// не менее 1 минуты с момента выхода из игры
		$d = @date('Y-m-d H:i:s', time()-60);
		$sql = sprintf("SELECT * FROM `login_log` WHERE `action`=2 AND `userid`=%d AND `data`>='%s'", $id, $d);
		$res = query2mysql($sql);
		if ($res->num_rows) die('108');
	}
	$b = GetRoleBase($roleid);
	$banned = false;
	if (!@AssignData(ak)) die('13');
	if (count($b['value']['forbid']) > 0)
	{
		foreach ($b['value']['forbid'] as $ii => $val){
			if ($val['type'] != 0 && $val['reason'] != $ban_reason)
			{
				$rest = $val['createtime'] + $val['time'] - time();
				if ($rest > 0) $banned = true;
			}				
		}		
	}
	if (!$banned)
	{
		$kick = GMKickoutRole(1024, 1, $roleid, 2, $ban_reason);
		if ($kick['retcode'] != 0 ) die('25');
		sleep($sleep_time);
	}
	return false;		
}

function isroleonline($userid){
	$l = GMListOnlineUser(0, 0, $userid, '');
	if (count($l['userlist']) > 0) 
	{
		foreach ($l['userlist'] as $i => $val)
		{
			if ($val['userid'] == $userid && $val['roleid'] > 0) return $val['roleid'];
		}		
	}	
	return 0;
}

function CheckPoints($id, $gold, $silver){
	global $db;	
	if ($gold == 0 && $silver == 0) return;
	// Пытаемся рассинхронизировать параллельные потоки для избежания дюпа и багания монет
	usleep(rand(50000,900000));
	// Блокируем таблицу перед проверкой
	query2mysql('LOCK TABLES `users` WRITE, `shop_names` WRITE, `klan_items` WRITE, `lklogs` WRITE, `shop_items` WRITE, `klan_pic` WRITE');
	// Проверка остатка монет
	$query = "SELECT * FROM `users` WHERE `ID` = ".$id;
	$rresult = query2mysql($query, true);
	$row = mysqli_fetch_assoc($rresult);
	if ($row['lkgold'] < $gold) die('17');
	if ($row['lksilver'] < $silver) die('18');
}

function CheckKlanMaster($id, $fid, $f){
	global $master_name;
	foreach ($f['data'] as $i => $val){
		$r = GetRole($val['id'], 63);
		if (($r['value']['factionid'] == $fid)&&($r['value']['title'] == 2)) {
			$master_name = $r['value']['name'];
			return true;
		}
	}
	return false;
}

function GetTime($t, $h = true, $days_only = false){	
	$pinkdays = floor($t/86400);
	$pinkhours = floor(($t-$pinkdays*86400)/3600);
	$pinkmin = floor(($t-$pinkdays*86400-$pinkhours*3600)/60);
	$pinksec = round($t-$pinkdays*86400-$pinkhours*3600-$pinkmin*60,0);
	if ($pinkhours<10) $pinkhours='0'.$pinkhours;
	if ($pinkmin<10) $pinkmin='0'.$pinkmin;
	if ($pinksec<10) $pinksec='0'.$pinksec;
	$timeused = '';
	if ($pinkdays>0) {
		if ($h) $timeused='<font color="#00aa00"><b>'.$pinkdays.'</b></font> дн '; else	
			$timeused=$pinkdays.' дн ';
	}
	if (!$days_only)
		if (($pinkhours!="00")||($pinkmin!="00")||($pinksec!="00")) $timeused .= $pinkhours.':'.$pinkmin.':'.$pinksec;
	if ($timeused=='') $timeused = 0;
	return $timeused;
}

function getbaninfo($id,$check_only_ban_acc = true){
	global $ElementsVer, $db;
	$answ = array();
	//if ($ElementsVer <= 12)	{
		$answ['lastlogin'] = 0;
		$answ['lastip'] = '';
		$query = "select * from `forbid` WHERE userid = ".$id;
		$rresult = $db->query($query);
		if ($rresult==false) {
			MakeAnswer($answ, 1000, 'Ошибка запроса');
		}
		$answ['bancount'] = $rresult->num_rows;
		$answ['bans'] = array();
		if ($answ['bancount'] > 0) {			
			while ($row = mysqli_fetch_assoc($rresult)){
				$a = strpos($row['reason'], "\x00\x00");				
				if ($a > 0) $row['reason'] = substr($row['reason'], 0, $a+1);
				$ban = array(
					'type' => $row['type'],
					'createtime' => strtotime($row['ctime']),
					'time' => $row['forbid_time'],
					'reason' => @iconv("UTF-16","UTF-8",$row['reason'])
				);
				array_push($answ['bans'], $ban);
			}
		}		
	//} else {
	/*	$u = new GUser();
		if (isset($_POST['ip'])) $ip=$_POST['ip']; else $ip='127.0.0.1';
		$u->GetUser($id,$fp,$ip);
		if ($u->retcode!=0 || $u->error!=0) {
			if ($u->retcode == 60) MakeAnswer($answ, 1000, 'Сначала создайте персонажа в игре', 95);
			MakeAnswer($answ, 1000, 'Ошибка получения данных аккаунта '.$u->retcode.' - '.$u->error);
		}		
		$p = new PacketStream($u->login_record);
		$lasttime = $p->ReadInt32();
		$ip1 = $p->ReadByte(); $ip2 = $p->ReadByte(); $ip3 = $p->ReadByte(); $ip4 = $p->ReadByte();
		$ip = sprintf('%d.%d.%d.%d',$ip4,$ip3,$ip2,$ip1);
		$answ['lastlogin'] = $lasttime;
		$answ['lastip'] = $ip;	
		$answ['bancount'] = $u->forbid->count;
		$answ['bans'] = array();
		if ($answ['bancount'] > 0) {	
			foreach($u->forbid->forbids as $i => $val){
				$ban = array(
					'type' => $val->type,
					'createtime' => $val->createtime,
					'time' => $val->time,
					'reason' => $val->reason
				);
				array_push($answ['bans'], $ban);
			}
		}
	}*/
	// Проверка банов на истечение
	if ($answ['bancount'] > 0) {
		$c = false;
		foreach ($answ['bans'] as $i => $val){
			$c1 = false;
			$rest = $val['createtime'] + $val['time'] - time();
			if ($rest>0) {
				$c1 = true;
				if ($check_only_ban_acc && $val['type'] != 100) $c1 = false;
			}
			$c = $c || $c1;
		}
		if (!$c) $answ['bancount'] = 0;
	}
	return $answ;
}

function GetRegGold($id){
	global $db;
	if (!isset($_POST['zoneid']) || !isset($_POST['aid']) || !isset($_POST['ip']) || !isset($_POST['register_gold'])) die('10');
	$register_gold = intval($_POST['register_gold']);
	$aid = intval($_POST['aid']);
	$zoneid = intval($_POST['zoneid']);
	$ip = $_POST['ip'];
	$db->query('set names utf8');
	// Проверка на доступность выдачи голда
	$res = query2mysql('SELECT `bonus_data` FROM `users` WHERE `ID`='.$id);
	$row = mysqli_fetch_assoc($res);
	if ($row['bonus_data']=='') $bonus_data = array(); else $bonus_data = @unserialize($row['bonus_data']);
	if (!is_array($bonus_data)) $bonus_data = array();
	if (isset($bonus_data['reg_gold'])) die('99');
	$result = UseCash($id, $zoneid, $aid, $register_gold*100);	
	$bonus_data['reg_gold'] = 1;
	AddLKLogs($id, $ip, 0, 0, 'Получение '.$register_gold.' стартового голда');
	$res = $db->query("UPDATE `users` SET `bonus_data`='".$db->real_escape_string(serialize($bonus_data))."' WHERE `ID`=".$id);
	die('16');
}

function checkban($id){
	global $db;
	$answ = getbaninfo($id, false);
	$db->query('set names utf8');
	extract($_POST);
	$mmotop1_item = unserialize($mmotop1_item); $mmotop2_item = unserialize($mmotop2_item); $mmotop3_item = unserialize($mmotop3_item); $mmotop4_item = unserialize($mmotop4_item);
	$qtop1_item = unserialize($qtop1_item); $qtop2_item = unserialize($qtop2_item);
	if ($mmotop1_item['id'] > 0) {
		$item = GetExtItem(intval($mmotop1_item['id']));
		$answ['mmotop1_itemname'] = $item->name;
		$answ['mmotop1_itemicon'] = $item->icon;
	} else $answ['mmotop1_itemname'] = '';
	if ($mmotop2_item['id'] > 0) {
		$item = GetExtItem(intval($mmotop2_item['id']));
		$answ['mmotop2_itemname'] = $item->name;
		$answ['mmotop2_itemicon'] = $item->icon;
	} else $answ['mmotop2_itemname'] = '';
	if ($mmotop3_item['id'] > 0) {
		$item = GetExtItem(intval($mmotop3_item['id']));
		$answ['mmotop3_itemname'] = $item->name;
		$answ['mmotop3_itemicon'] = $item->icon;
	} else $answ['mmotop3_itemname'] = '';
	if ($mmotop4_item['id'] > 0) {
		$item = GetExtItem(intval($mmotop4_item['id']));
		$answ['mmotop4_itemname'] = $item->name;
		$answ['mmotop4_itemicon'] = $item->icon;
	} else $answ['mmotop4_itemname'] = '';
	if ($qtop1_item['id'] > 0) {
		$item = GetExtItem(intval($qtop1_item['id']));
		$answ['qtop1_itemname'] = $item->name;
		$answ['qtop1_itemicon'] = $item->icon;
	} else $answ['qtop1_itemname'] = '';
	if ($qtop2_item['id'] > 0) {
		$item = GetExtItem(intval($qtop2_item['id']));
		$answ['qtop2_itemname'] = $item->name;
		$answ['qtop2_itemicon'] = $item->icon;
	} else $answ['qtop2_itemname'] = '';
	$answ['roles_count'] = 0;
	$f = GetUserRoles($id, 1);
	if ($f['retcode'] != 0) MakeAnswerErrGetRoles($answ);
	$answ['roles_count'] = count($f['data']);
	$answ['roles'] = array();
	if ($answ['roles_count']) {
		foreach ($f['data'] as $i => $val){
			$role = array(
				'index' => $i,
				'name' => $val['name']
			);
			array_push($answ['roles'], $role);		
		}
	}
	// Проверка на доступность выдачи голда
	$res = $db->query('SELECT `bonus_data` FROM `users` WHERE `ID`='.$id);
	if ($db->errno > 0) MakeAnswer($answ, 1000, $db->error);
	$row = mysqli_fetch_assoc($res);
	if ($row['bonus_data']=='') $bonus_data = array(); else $bonus_data = @unserialize($row['bonus_data']);
	if (!is_array($bonus_data)) $bonus_data = array();
	$answ['allow_reg_gold'] = (!isset($bonus_data['reg_gold']));
	$answ['cur_reward_role_index'] = -1;
	if (isset($bonus_data['cur_reward_role_index'])) $answ['cur_reward_role_index'] = $bonus_data['cur_reward_role_index'];
	if ($answ['cur_reward_role_index'] >= $answ['roles_count']) $answ['cur_reward_role_index'] = -1;
	if ($answ['allow_reg_gold']){
		// Проверяем наличие персонажей
		$answ['roles_count'] = count($f['data']);
	}	
	$res = $db->query('SELECT * FROM `users` WHERE `referal`='.$id);
	if ($db->errno > 0) MakeAnswer($answ, 1000, $db->error);	
	$answ['refcount'] = $res->num_rows;
	$answ['refdata'] = array();
	if ($answ['refcount'] == 0) MakeAnswer($answ);
	while ($row = mysqli_fetch_assoc($res)){
		$refdata = array(
			'id' => ($row['ID']/16)-1,
			'creatime' => $row['creatime'],
			'ref_status' => $row['ref_status'],
			'ref_bonus' => $row['ref_bonus']
		);
		array_push($answ['refdata'], $refdata);
	}		
	MakeAnswer($answ);
}

function CheckAntibrutIP(){
	global $db;
	$query = sprintf("SELECT * FROM `antibrut` WHERE `ip`='%s'", $db->real_escape_string($_POST['ip']));
	$result = $db->query($query);
	if ($result->num_rows) {
		$row = mysqli_fetch_assoc($result);
		$plusfail = 0;
		if (($row['last_date_fail']+3)>=time()) $plusfail = 1;
		$db->query(sprintf("UPDATE `antibrut` SET `fail_count`=`fail_count`+%d, `last_date_fail`=%d WHERE `ip`='%s'", $plusfail, time(), $db->real_escape_string($_POST['ip'])));
	} else
		$db->query(sprintf("INSERT INTO `antibrut` (`ip`, `last_date_fail`, `fail_count`) VALUES ('%s', %d, %d)", $db->real_escape_string($_POST['ip']), time(), 1));
}

function GetExtItem($id){
	global $db;
	$a=new ExtItem();
	$a->name = 'Неправильный предмет';
	$a->list = 0;
	$a->icon = 'unknown.dds';
	$res = $db->query('SELECT * FROM `shop_names` WHERE `id`='.$id);	
	if ($db->errno>0) return $a;
	if ($res->num_rows==0) return $a;
	$row = mysqli_fetch_assoc($res);
	$a->name = $row['name'];
	$a->icon = $row['icon'];
	$a->list = $row['list'];
	return $a;
}

function GetItemInfo($data, $name, $list){
	global $gender;
	$s='';
	return $s;
}

function shopheaders($id){	
	global $db;
	$answ = getbaninfo($id);	
	if ($answ['bancount'] > 0) MakeAnswer($answ, 81);
	$base_err_txt = 'Ошибка запроса к базе данных.';
	$f = GetUserRoles($id, 1);
	if ($f['retcode'] != 0) MakeAnswerErrGetRoles($answ);
	$answ['roles_count'] = count($f['data']);
	$answ['roles'] = array();
	if (count($f['data']) == 0) MakeAnswerErrRolesCount($answ);
	foreach ($f['data'] as $i => $val){
		$role = array(
			'index' => $i,
			'name' => $val['name']
		);
		array_push($answ['roles'], $role);		
	}
	$db->query("SET NAMES utf8");
	$num = $_POST['num'];
	$page = (isset($_POST['page']))?intval($_POST['page']):1;
	$subcat = (isset($_POST['subcat']))?intval($_POST['subcat']):'';
	$query = 'select * from `shop_cat` order by `id`';
	$result = $db->query($query);
	if ($db->errno) MakeAnswer($answ, 1000, $base_err_txt);
	$catcount = $result->num_rows;
	$answ['cat'] = array();
	$answ['subcat'] = array();
	$answ['items'] = array();
	if ($catcount) {
		while ($row = mysqli_fetch_assoc($result)){
			$cat = array(
				'id' => $row['id'],
				'name' => $row['name']
			);
			array_push($answ['cat'], $cat);
		}
	}
	$query = 'select * from `shop_subcat` where catid='.$page.' order by id';
	$result = $db->query($query);
	if ($db->errno) MakeAnswer($answ, 1000, $base_err_txt);
	$subcatcount = $result->num_rows;
	if ($subcatcount) {
		while ($row = mysqli_fetch_assoc($result)){
			if ($subcat == '') $subcat = $row['id'];
			$subcatadd = array(
				'id' => $row['id'],
				'name' => $row['name']
			);
			array_push($answ['subcat'], $subcatadd);
		}
	}
	$query = 'select * from `shop_items` where subcat='.$subcat.' order by id';	
	$result = $db->query($query);
	if ($db->errno) MakeAnswer($answ, 1000, $base_err_txt);
	$itcount = $result->num_rows;
	if ($itcount) {
		while ($row = mysqli_fetch_assoc($result)){
			$it = GetExtItem($row['itemid']);
			$desc = GetItemInfo($row['data'], $it->name, $it->list).'<font color="#ffffff">'.$row['desc'].'</font>';
			$item = array(
				'id' => $row['id'],
				'name' => $it->name,
				'icon' => $it->icon,
				'itemid' => $row['itemid'],
				'count' => $row['count'],
				'maxcount' => $row['maxcount'],
				'data' => $row['data'],
				'client_size' => $row['client_size'],
				'proctype' => $row['proctype'],
				'subcat' => $row['subcat'],
				'cost_timeless' => $row['cost_timeless'],
				'cost_expire' => $row['cost_expire'],
				'expire' => $row['expire'],
				'discount_data' => $row['discount_data'],
				'desc' => $desc,
				'clean_desc' => $row['desc'],
				'rest' => $row['rest'],
				'buycount' => $row['buycount']
			);
			array_push($answ['items'], $item);
		}
	}
	MakeAnswer($answ);	
}

function make_auth_res($row, $login, $vk_id='', $vk_name='', $vk_photo='', $steam_id='', $steam_name='', $steam_photo=''){
	global $db, $act_key, $rest_time, $is_timed;
	$res = array();
	$res['name'] = $row['name'];
	$res['pwd_hash'] = base64_encode($row['passwd']);
	$res['lkgold'] = $row['lkgold'];
	$res['lksilver'] = $row['lksilver'];
	$res['id'] = $row['ID'];
	$res['rules_cnt'] = $row['rules_cnt'];				
	$res['acc_email'] = $row['email'];
	$res['ipdata'] = @unserialize($row['ipdata']);
	$res['session_data'] = $row['session_data'];
	$res['vk_id'] = $row['vkid'];
	$res['steam_id'] = $row['steamid'];	
	if (isset($_POST['session'])) $res['session_data'] = $_POST['session'];
	if (!is_array($res['ipdata'])) {
		$res['ipdata'] = array();
		$res['ipdata'][0] = false;
		$res['ipdata'][1] = array();	
		$res['ipdata'][2] = false;		
	}
	if (!isset($res['ipdata'][2])) $res['ipdata'][2] = false;
	$db->query('set names utf8');
	if ($login) {
		if (isset($_POST['session'])) {
			@$db->query(sprintf("UPDATE `users` SET `session_data`='%s' WHERE `name` = '%s'", $db->real_escape_string($_POST['session']), $login));
		}
		$sql = "SELECT `Prompt`, `answer`, `vkname`, `vkphoto`, `steamname`, `steamphoto` FROM `users` WHERE `name` = '$login'";
	} else 
	if ($vk_id) {
		if (isset($_POST['session'])) {
			@$db->query(sprintf("UPDATE `users` SET `session_data`='%s', `vkname`='%s', `vkphoto`='%s' WHERE `vkid` = '%s'", $db->real_escape_string($_POST['session']), $vk_name, $vk_photo, $vk_id));
		}
		$sql = "SELECT `Prompt`, `answer`, `vkname`, `vkphoto`, `steamname`, `steamphoto` FROM `users` WHERE `vkid` = '$vk_id'";
	} else {
		if (isset($_POST['session'])) {
			@$db->query(sprintf("UPDATE `users` SET `session_data`='%s', `steamname`='%s', `steamphoto`='%s' WHERE `steamid` = '%s'", $db->real_escape_string($_POST['session']), $steam_name, $steam_photo, $steam_id));
		}
		$sql = "SELECT `Prompt`, `answer`, `vkname`, `vkphoto`, `steamname`, `steamphoto` FROM `users` WHERE `steamid` = '$steam_id'";
	}
	if ($rest_time < 0) $rest_time = 0;
	$result = $db->query($sql);
	$row = mysqli_fetch_assoc($result);
	$res['question'] = $row['Prompt'];
	$res['answer'] = $row['answer'];
	$res['vk_name'] = $row['vkname'];
	$res['vk_photo'] = $row['vkphoto'];
	$res['steam_name'] = $row['steamname'];
	$res['steam_photo'] = $row['steamphoto'];
	$res['is_timed'] = $is_timed;
	$res['rest_time'] = GetTime($rest_time, false, true);
	$res['script'] = $_SERVER['SCRIPT_FILENAME'];
	$res['act_key'] = $act_key;
	$result = $db->query("SELECT * FROM `lklogs` WHERE `userid` = 0");
	$res['notice'] = array();
	while ($row = mysqli_fetch_assoc($result)) {
		$ar = array($row['gold'], $row['desc']);
		array_push($res['notice'], $ar);
	}
	$res = serialize($res);
	die($res);
}

function AddVK(){
	global $db;
	if (!$db->query(sprintf("DELETE FROM `antibrut` WHERE `last_date_fail`<=%d", time()-600))) die('6'); // Чистка таблицы антибрута
	$query = sprintf("SELECT * FROM `antibrut` WHERE `ip`='%s'", $db->real_escape_string($_POST['ip']));
	$result = $db->query($query);
	if ($result->num_rows) {		
		$row = mysqli_fetch_assoc($result);
		if (($row['fail_count'])>=5) die('7');
	}
	CheckAntibrutIP();
	if ( !isset($_POST['vk_id']) || !isset($_POST['vk_name']) || !isset($_POST['id'])) die('10');
	$id = intval($_POST['id']);
	$vk_id = trim($db->real_escape_string($_POST['vk_id']));
	$vk_name = $db->real_escape_string($_POST['vk_name']);
	$vk_photo = $db->real_escape_string($_POST['vk_photo']);
	$query = sprintf("SELECT `ID` FROM `users` WHERE `vkid` = '%s'", $vk_id);		
	$result = query2mysql($query);
	if ($result->num_rows) {
		die('109');
	}
	$db->query('set names utf8');
	$result = query2mysql(sprintf("UPDATE `users` SET `vkname`='%s', `vkphoto`='%s', `vkid` = '%s' WHERE `ID`=%s", $vk_name, $vk_photo, $vk_id, $id));
	die('16');
}

function AddSteam(){
	global $db;
	if (!$db->query(sprintf("DELETE FROM `antibrut` WHERE `last_date_fail`<=%d", time()-600))) die('6'); // Чистка таблицы антибрута
	$query = sprintf("SELECT * FROM `antibrut` WHERE `ip`='%s'", $db->real_escape_string($_POST['ip']));
	$result = $db->query($query);
	if ($result->num_rows) {		
		$row = mysqli_fetch_assoc($result);
		if (($row['fail_count'])>=5) die('7');
	}
	CheckAntibrutIP();
	if ( !isset($_POST['steam_id']) || !isset($_POST['steam_name']) || !isset($_POST['id'])) die('10');
	$id = intval($_POST['id']);
	$steam_id = trim($db->real_escape_string($_POST['steam_id']));
	$steam_name = $db->real_escape_string($_POST['steam_name']);
	$steam_photo = $db->real_escape_string($_POST['steam_photo']);
	$query = sprintf("SELECT `ID` FROM `users` WHERE `steamid` = '%s'", $steam_id);		
	$result = query2mysql($query);
	if ($result->num_rows) {
		die('110');
	}
	$db->query('set names utf8');
	$result = query2mysql(sprintf("UPDATE `users` SET `steamname`='%s', `steamphoto`='%s', `steamid` = '%s' WHERE `ID`=%s", $steam_name, $steam_photo, $steam_id, $id));
	die('16');
}

function vkauth(){
	global $db;
	if (!$db->query(sprintf("DELETE FROM `antibrut` WHERE `last_date_fail`<=%d", time()-600))) die('6'); // Чистка таблицы антибрута
	$query = sprintf("SELECT * FROM `antibrut` WHERE `ip`='%s'", $db->real_escape_string($_POST['ip']));
	$result = $db->query($query);
	if ($result->num_rows) {		
		$row = mysqli_fetch_assoc($result);
		if (($row['fail_count'])>=5) die('7');
	}
	if ( !isset($_POST['vk_id']) || !isset($_POST['vk_name'])) die('nodata');
	$vk_id = trim($db->real_escape_string($_POST['vk_id']));
	$vk_name = $db->real_escape_string($_POST['vk_name']);
	$vk_photo = $db->real_escape_string($_POST['vk_photo']);
$query = sprintf("SELECT `users`.*, count(`auth`.`rid`) AS `rules_cnt` FROM users LEFT JOIN `auth` ON `auth`.`userid`=`users`.`ID` WHERE `vkid` = '%s'", $vk_id);	
	$result = $db->query($query);
	if (!$result) die('6'); // Ошибка запроса		
	if ($result->num_rows) {
		$row = mysqli_fetch_assoc($result);		
		if ($row['vkid'] != $vk_id) { 	// Нет аккаунта с таким vkid
			CheckAntibrutIP();
			die('15');
		}
		// Проверка на наличие неактивированного аккаунта
		$query = sprintf("SELECT `ID` FROM `users` WHERE `vkid` = '%s' AND `passwd`='confirm'", $vk_id);		
		$result = $db->query($query);
		if (!$result) die('6'); // Ошибка запроса	
		if ($result->num_rows) {
			die('14');
		}
		make_auth_res($row, '', $vk_id, $vk_name, $vk_photo);
	} else {
		CheckAntibrutIP();
		die('0');	// Неверный логин/пароль		
	}
}

function steamauth(){
	global $db;
	if (!$db->query(sprintf("DELETE FROM `antibrut` WHERE `last_date_fail`<=%d", time()-600))) die('6'); // Чистка таблицы антибрута
	$query = sprintf("SELECT * FROM `antibrut` WHERE `ip`='%s'", $db->real_escape_string($_POST['ip']));
	$result = $db->query($query);
	if ($result->num_rows) {		
		$row = mysqli_fetch_assoc($result);
		if (($row['fail_count'])>=5) die('7');
	}
	if ( !isset($_POST['steam_id']) || !isset($_POST['steam_name'])) die('nodata');
	$steam_id = trim($db->real_escape_string($_POST['steam_id']));
	$steam_name = $db->real_escape_string($_POST['steam_name']);
	$steam_photo = $db->real_escape_string($_POST['steam_photo']);
$query = sprintf("SELECT `users`.*, count(`auth`.`rid`) AS `rules_cnt` FROM users LEFT JOIN `auth` ON `auth`.`userid`=`users`.`ID` WHERE `steamid` = '%s'", $steam_id);	
	$result = $db->query($query);
	if (!$result) die('6'); // Ошибка запроса		
	if ($result->num_rows) {
		$row = mysqli_fetch_assoc($result);		
		if ($row['steamid'] != $steam_id) { 	// Нет аккаунта с таким steamid
			CheckAntibrutIP();
			die('15');
		}
		// Проверка на наличие неактивированного аккаунта
		$query = sprintf("SELECT `ID` FROM `users` WHERE `steamid` = '%s' AND `passwd`='confirm'", $steam_id);		
		$result = $db->query($query);
		if (!$result) die('6'); // Ошибка запроса	
		if ($result->num_rows) {
			die('14');
		}
		make_auth_res($row, '', '', '', '', $steam_id, $steam_name, $steam_photo);
	} else {
		CheckAntibrutIP();
		die('0');	// Неверный логин/пароль		
	}
}

function auth(){
	global $db;
	// Проверка на брут
	if (!$db->query(sprintf("DELETE FROM `antibrut` WHERE `last_date_fail`<=%d", time()-600))) die('6'); // Чистка таблицы антибрута
	$query = sprintf("SELECT * FROM `antibrut` WHERE `ip`='%s'", $db->real_escape_string($_POST['ip']));
	$result = $db->query($query);
	if ($result->num_rows) {		
		$row = mysqli_fetch_assoc($result);
		if (($row['fail_count'])>=5) die('7');
	}
	if ( !isset($_POST['login']) || !isset($_POST['passw'])) die('nodata');
	$login = strtolower($db->real_escape_string($_POST['login']));
	$pass = base64_decode($_POST['passw']);
	$query = sprintf("SELECT `users`.*, count(`auth`.`rid`) AS `rules_cnt` FROM users LEFT JOIN `auth` ON `auth`.`userid`=`users`.`ID` WHERE `name` = '%s' AND `passwd`='%s'", $db->real_escape_string($login), $db->real_escape_string($pass));
	if (isset($_POST['email'])) $query.=sprintf(" AND `email`='%s'", $db->real_escape_string($_POST['email']));
	$result = $db->query($query);
	if (!$result) die('6'); // Ошибка запроса		
	if ($result->num_rows) {
		$row = mysqli_fetch_assoc($result);		
		if ($row['name']!=$login) { 	// Неверный логин/пароль
			// Проверка на наличие неактивированного аккаунта
			$query = sprintf("SELECT `ID` FROM users WHERE `name` = '%s' AND `passwd2`='%s' AND `passwd`='confirm'", $db->real_escape_string($login), $db->real_escape_string($pass));		
			if (isset($_POST['email'])) $query.=sprintf(" AND `email`='%s'", $db->real_escape_string($_POST['email']));		
			$result = $db->query($query);
			if (!$result) die('6'); // Ошибка запроса	
			if ($result->num_rows) {
				die('14');
			} else {
				CheckAntibrutIP();
				die('0');
			}
		}
		make_auth_res($row, $login);
	} else {
		CheckAntibrutIP();
		die('0');	// Неверный логин/пароль		
	}
}

function persklan(){
	global $db;
	if (!isset($_POST['id'])) die();
	$id = intval($_POST['id']);
	$answ = getbaninfo($id);
	if ($answ['bancount'] > 0) MakeAnswer($answ, 81);
	$f = GetUserRoles($id, 1);
	if ($f['retcode'] != 0) MakeAnswerErrGetRoles($answ);	
	$answ['roles_count'] = 0;
	$answ['roles'] = array();
	$answ['factions'] = array();
	if (count($f['data']) == 0) MakeAnswer($answ);	
	$db->query("SET NAMES utf8");	
	foreach ($f['data'] as $i => $val){
		$r = GetRole($val['id'], 63);		
		if ($r['value']['factionid'] > 0) {
			$sql = 'select * from `klan_items` where klanid='.$r['value']['factionid'];
			$res = $db->query($sql);
			if ($db->errno != 0) MakeAnswer($answ, 1000, 'Ошибка запроса к базе данных (получение списка клан артов)');
			if ($r['value']['title'] == 2){
				$answ['roles_count']++;
				$klanitems = array();				
				if ($res->num_rows>0)
				while ($row = mysqli_fetch_assoc($res)){
					$it = GetExtItem($row['itemid']);
					$it1 = GetExtItem($row['cost_item_id']);
					$klanit = array(
						'id' => $row['id'],
						'itemid' => $row['cost_item_id'],
						'count' => $row['count'],
						'name' => $it->name,
						'icon' => $it->icon,
						'costgold' => $row['costgold'],
						'costsilver' => $row['costsilver'],
						'cost_item_count' => $row['cost_item_count'],
						'cost_name' => $it1->name,
						'cost_icon' => $it1->icon
					);
					array_push($klanitems, $klanit);
				}
				$fact = GetFactionInfo($r['value']['factionid']);
				$role = array(
					'factionid' => $r['value']['factionid'],
					'factionname' => $fact['value']['name'],
					'rolename' => $val['name'],
					'roleindex' => $i,
					'level' => $r['value']['status']['level'],           // Передаем уровень
					'occupation' => $r['value']['status']['occupation'], // Передаем класс
					'klanitems' => $klanitems
				);
				
				array_push($answ['roles'], $role);
			} else {
				if ($res->num_rows > 0) {
					$art = array();
					$fact = GetFactionInfo($r['value']['factionid']);
					$art['faction_name'] = $fact['value']['name'];
					$art['items'] = array();				
					if ($res->num_rows > 0)
					while ($row = mysqli_fetch_assoc($res)){
						$it = GetExtItem($row['itemid']);
						$it1 = GetExtItem($row['cost_item_id']);
						$art_i['item'] = $it->name;					
						$klanit = array(
							'item' => $it->name,
							'icon' => $it->icon,
							'count' => $row['count'],
							'costgold' => $row['costgold'],
							'costsilver' => $row['costsilver'],
							'cost_item_count' => $row['cost_item_count'],
							'cost_name' => $it1->name,
							'cost_icon' => $it1->icon
						);
						array_push($art['items'], $klanit);
					}
					array_push($answ['factions'], $art);
				}
			}
		}
	}
	MakeAnswer($answ);
}

function persuah(){
	global $db;	
	if (!isset($_POST['id']) || !isset($_POST['allow_lk_gold_exchange']) || !isset($_POST['gold_itemid']) || !isset($_POST['gold_item_exchange_rate']) || !isset($_POST['allow_lk_silver_exchange']) || !isset($_POST['silver_itemid']) || !isset($_POST['silver_item_exchange_rate'])) die();
	extract($_POST);
	$id = intval($_POST['id']);	
	$answ = getbaninfo($id);
	$answ['roles'] = array();
	$f = GetUserRoles($id, 1);
	if ($f['retcode'] != 0) MakeAnswerErrGetRoles($answ);
	if (!count($f['data'])) MakeAnswerErrRolesCount($answ);
	if ($answ['bancount'] > 0 || (!$_POST['allow_lk_gold_exchange'] && !$_POST['allow_lk_silver_exchange'])) MakeAnswer($answ);
	$gold_itemid = intval($_POST['gold_itemid']); $silver_itemid = intval($_POST['silver_itemid']);
	$gold_item_exchange_rate = $_POST['gold_item_exchange_rate']; $silver_item_exchange_rate = $_POST['silver_item_exchange_rate'];
	$db->query('set names utf8');
	if ($_POST['allow_lk_gold_exchange']) {
		$it = GetExtItem($gold_itemid); 
		$answ['gold_item_name'] = $it->name;
	}
	if ($_POST['allow_lk_silver_exchange']) {
		$it = GetExtItem($silver_itemid); 
		$answ['silver_item_name'] = $it->name;
	}
	if ($gold_item_exchange_rate < 1) $gold_item_exchange_rate = 1;
	if ($silver_item_exchange_rate < 1) $silver_item_exchange_rate = 1;
	foreach ($f['data'] as $i => $val){
		if ($_POST['allow_lk_gold_exchange']) $cnt = finditem($val['id'], $gold_itemid); else $cnt = 0;
		if ($_POST['allow_lk_silver_exchange']) $cnt1 = finditem($val['id'] ,$silver_itemid); else $cnt1 = 0;
		if ( $cnt >= $gold_item_exchange_rate || $cnt1 >= $silver_item_exchange_rate ){
			$role = array(
				'index' => $i,
				'name' => $val['name'],				
				'gold_item_cnt' => $cnt,
				'silver_item_cnt' => $cnt1
			);	
			array_push($answ['roles'], $role);
		}
	}
	MakeAnswer($answ);
}

function pers(){
	global $db;	
	if (!isset($_POST['id'])) die();
	$id = intval($_POST['id']);	
	$answ = getbaninfo($id);	
	$f = GetUserRoles($id, 1);
	if ($f['retcode'] != 0) MakeAnswerErrGetRoles($answ);
	if (!@AssignData(ak)) die('13');
	$answ['roles_count'] = count($f['roles']);
	$answ['roles'] = array();
	$k = array('value' => array('fid'=>0,'name'=>''));
	if (count($f['roles']) == 0) MakeAnswer($answ);	
	$db->query("SET NAMES utf8");	
	foreach ($f['data'] as $i => $val){
		$b = GetRoleBase($val['id']);
		if ($b['retcode'] != 0) MakeAnswer($answ, 1000, 'Ошибка получения базы персонажа '.$val['name']);
		$r = GetRole($val['id'], 63);
		if ($r['retcode'] == 0){
			if ($k['value']['fid'] != $r['value']['factionid'] && $r['value']['factionid'] != 0) $k = GetFactionInfo($r['value']['factionid']);
			$forbids = array();
			if (count($b['value']['forbid']) > 0) {
				$cnt=0;
				foreach ($b['value']['forbid'] as $ii => $val1){
					if ($val1['type'] != 0 && $val1['reason'] != 'Сохранение персонажа'){
						$forbid = array(
							'type' => $val1['type'],
							'time' => $val1['time'],
							'createtime' => $val1['createtime'],
							'reason' => $val1['reason']
						);
						array_push($forbids, $forbid);
					}				
				}						
			}			
			$role = array(
				'index' => $i,
				'name' => $val['name'],
				'factionid' => $r['value']['factionid'],
				'factionname' => $k['value']['name'],
				'factionrole' => $r['value']['title'],
				'time_used' => $r['value']['status']['time_used'],
				'occupation' => $r['value']['status']['occupation'],
				'gender' => $r['value']['gender'],
				'level' => $r['value']['status']['level'],
				'forbids' => $forbids,
				'worldtag' => $r['value']['status']['worldtag'],
				'roleid' => $val['id'],
				'storehousepasswd' => $r['value']['status']['storehousepasswd'] != ''
			);			
		} else {
			// Ошибка получения персонажа
			$role = array(
				'index' => $i,
				'name' => 'Ошибка получения персонажа',
				'factionid' => 0,
				'factionname' => '',
				'factionrole' => 0,
				'time_used' => 0,
				'occupation' => 0,
				'gender' => 0,
				'level' => 0,
				'forbids' => array(),
				'worldtag' => 1,
				'storehousepasswd' => false
			);
		}
		array_push($answ['roles'], $role);		
	}
	if ($answ['bancount'] > 0) MakeAnswer($answ, 81);	
	MakeAnswer($answ);	
}

function CheckCostPoints($nam, $err1, $id){
	global $gpoint, $spoint;
	if (!isset($_POST[$nam])) die($err1);
	$cost = $_POST[$nam];			
	ShowCost($cost);
	if ($gpoint > 0 || $spoint > 0)
		CheckPoints($id, $gpoint, $spoint);	// Проверяем есть ли на аккаунте поинты
}

function getFormattedKey($key) {
	$num1 = substr($key, 0, 4);
	$num2 = substr($key, 4, 4);
	$num3 = substr($key, 8, 4);
	$num4 = substr($key, 12, 4);
	return $num1 . "-" . $num2 . "-" . $num3 . "-" . $num4;
}

function GenerateCode($length = 16, $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'){
	$chars_length = (strlen($chars) - 1);
	$string = $chars{rand(0, $chars_length)};    
	for ($i = 1; $i < $length; $i = strlen($string))  {
        	$r = $chars{rand(0, $chars_length)};        
        	//if ($r != $string{$i - 1}) 
		$string .=  $r;
    	}    
    	return getFormattedKey($string);
}

function AdminGenPromo(){
	global $db;		
	if (!isset($_POST['promo_count']) || !isset($_POST['promo_expire']) || !isset($_POST['promo_group']) || !isset($_POST['promo_gold']) || !isset($_POST['promo_silver']) || !isset($_POST['promo_item_id']) || !isset($_POST['promo_item_count']) || !isset($_POST['promo_item_maxcount']) || !isset($_POST['promo_item_data']) || !isset($_POST['promo_item_client_size']) || !isset($_POST['promo_item_proctype']) || !isset($_POST['promo_item_expire']) || !isset($_POST['promo_desc'])) die('Input data error');
	$db->query('set names utf8');
	extract($_POST);	
	if (isset($promo_multi_user)) $promo_multi_user = 1; else $promo_multi_user = 0;
	if ($promo_count < 1) die('Количество генерируемых кодов должно быть не менее 1');
	if ($promo_count > 10000) die('Количество генерируемых кодов должно быть не более 10000 за раз');
	set_time_limit(round($promo_count/10));
	if ($promo_expire > 0) $promo_expire = time() + $promo_expire;
	$f = fopen(sprintf('%s_%d-promo_codes.txt', @date('Y-m-d_H-i-s', time()), $promo_count), 'w');
	if (!$f) die('Error create file with promo codes');
	for ($a = 0; $a < $promo_count; $a++){
		$res = false; $q = 0;
		while (!$res && $q < 20) {			
			$code = GenerateCode();
			$sql = sprintf("INSERT INTO `promo_codes` (`code`,`expire`,`group`,`bonus_money_gold`,`bonus_money_silver`, `bonus_item_id`, `bonus_item_count`, `bonus_item_max_count`, `bonus_item_data`, `bonus_item_client_size`, `bonus_item_proctype`, `bonus_item_expire`, `multi_user`, `used_userid`, `desc`) VALUES ('%s', %d, %d, %d, %d, %d, %d, %d, '%s', %d, %d, %d, %d, 0, '%s')", $code, $promo_expire, $promo_group, $promo_gold, $promo_silver, $promo_item_id, $promo_item_count, $promo_item_maxcount, $db->real_escape_string($promo_item_data), $promo_item_client_size, $promo_item_proctype, $promo_item_expire, $promo_multi_user, $db->real_escape_string($promo_desc));
			$res = $db->query($sql);
			//if (!$res) echo $db->error;
			//echo GenerateCode().'<br>';
			$q++;
		}
		fwrite($f, $code."\n");
	}
	fclose($f);
	die('ok');
}

function act($id,$n,$num,$ip){	
	global $gpoint, $spoint, $db, $factrole;
	$err1='10';//"Ошибка входящих данных";
	$err2='13';//"Ошибка получения персонажа";
	$err3='14';//"Ошибка записи персонажа";	
	if (($n!=7)&&($n!=10)&&($n!=11)&&($n!=17)&&($n!=18)&&($n!=19)&&($n!=20)&&($n!=53)&&($n!=54)&&($n!=55)&&($n!=56)&&($n!=57)&&($n!=58)&&($n!=59)&&($n!=60)&&($n!=61)&&($n!=62)&&($n!=69)&&($n!=70)&&($n!=71)&&($n!=72)&&($n!=73)&&($n!=77)&&($n!=78)&&($n!=79)&&($n!=80)) {
		$f = GetUserRoles($id, 1);
		if ($f['retcode'] != 0) die('12');
		if (count($f['data']) < $num+1) die($err1); // Проверяем номер персонажа
		$cur_role_name = $f['data'][$num]['name'];
	}
	if (($n!=8)&&($n!=10)&&($n!=11)&&($n!=12)&&($n!=3)&&($n!=13)&&($n!=7)&&($n!=17)&&($n!=18)&&($n!=19)&&($n!=20)&&($n!=21)&&($n!=23)&&($n!=53)&&($n!=54)&&($n!=55)&&($n!=56)&&($n!=57)&&($n!=58)&&($n!=59)&&($n!=60)&&($n!=61)&&($n!=62)&&($n!=63)&&($n!=69)&&($n!=70)&&($n!=71)&&($n!=72)&&($n!=73)&&($n!=77)&&($n!=78)&&($n!=79)&&($n!=80)&&($n!=82)&&($n!=84)) {
		isonline($id, $f['data'][$num]['id']);		// Проверка оффлайна аккаунта
	}
	if ($n!=73&&$n!=77) {
		$answ = getbaninfo($id);
		if ($answ['bancount'] > 0) die('81');//MakeAnswer($answ, 81);
	}
	$gpoint = 0; $spoint = 0;
	switch ($n)
	{

		case 800: // Выбор перса для отправки наград за голосование в топах
			$db->query("SET NAMES utf8");
			$res = $db->query('SELECT `bonus_data` FROM `users` WHERE `ID`='.$id);
			if ($db->errno > 0) die('15');
			$row = mysqli_fetch_assoc($res);
			if ($row['bonus_data']=='') $bonus_data = array(); else $bonus_data = @unserialize($row['bonus_data']);
			if (!is_array($bonus_data)) $bonus_data = array();						
			$bonus_data['cur_reward_role_index'] = $num;
			$res = $db->query("UPDATE `users` SET `bonus_data`='".$db->real_escape_string(serialize($bonus_data))."' WHERE `ID`=".$id);
			if ($db->errno > 0) die('15');
			die('16');
			
		break;

		case 2: 				// Убираем пароль банка
			CheckCostPoints('nullbankpass', $err1, $id);
			$rd = GetRoleData($f['data'][$num]['id']);
			if ($rd['retcode'] !=0 || $rd['packet_check_error'] != 0) die($err2);
			if ($rd['value']['status']['storehousepasswd'] == '') die('45'); // нет пароля на банке
			$rd['value']['status']['storehousepasswd'] = '';
			$rd = PutRoleData($f['data'][$num]['id'], $rd['value']);
			if ($rd['retcode'] != 0) die($err3);
			$desc='Удаление пароля банка персонажа '.$f['data'][$num]['name'];
		break;

		case 5: 				// Обмен на голд монеты			
			if ( !isset($_POST['i']) || CheckNum($_POST['i']) || !isset($_POST['n']) || !isset($_POST['num']) || !isset($_POST['allow_lk_gold_exchange']) || !isset($_POST['gold_itemid']) || !isset($_POST['gold_item_exchange_rate'])) die($err1);
			extract($_POST);
			$gpoint=-$i;
			$spoint=0;
			$cnt = finditem($f['data'][$num]['id'], $gold_itemid);
			if ($cnt < ($i*$gold_item_exchange_rate) ) die('49');		
			if (takeitem($f['data'][$num]['id'], $gold_itemid, $i*$gold_item_exchange_rate) != true) die('50');
			$db->query('set names utf8');
			$it = GetExtItem($gold_itemid);
			$desc = sprintf('%s: обмен %d %s на %s голд монет', $f['data'][$num]['name'], $i*$gold_item_exchange_rate, $it->name, $i);
		break;

		case 6: 				// Обмен на серебрянные монеты
			if ( !isset($_POST['i']) || CheckNum($_POST['i']) || !isset($_POST['n']) || !isset($_POST['num']) || !isset($_POST['allow_lk_silver_exchange']) || !isset($_POST['silver_itemid']) || !isset($_POST['silver_item_exchange_rate'])) die($err1);
			extract($_POST);
			$gpoint=0;
			$spoint=-$i;
			$cnt = finditem($f['data'][$num]['id'], $silver_itemid);
			if ($cnt < ($i*$silver_item_exchange_rate) ) die('49');		
			if (takeitem($f['data'][$num]['id'], $silver_itemid, $i*$silver_item_exchange_rate) != true) die('50');
			$db->query('set names utf8');
			$it = GetExtItem($silver_itemid);
			$desc = sprintf('%s: обмен %d %s на %s серебрянных монет', $f['data'][$num]['name'], $i*$silver_item_exchange_rate, $it->name, $i);
		break;

		case 7:					// Установка значка клана
			CheckCostPoints('klancost', $err1, $id);
			global $master_name;
			$f = GetUserRoles($id, 1);
			if ($f['retcode'] != 0) die('12');
			if (!CheckKlanMaster($id, $num, $f)) die('7');			
			$servid = intval($_POST['servid']);
			if (!is_writable('klan/iconlist_guild.png') || !is_writable('klan/iconlist_guild.txt') || !is_writable('klan/version') || !is_writable('klan/.')) die('94');
			$r = KlanPic($num, $servid);
			if ($r=='ok') echo "8"; else die("9");
			$desc='Значок клана '.$num.', мастер '.$master_name;
		break;	

		case 8: 				// Покупка предмета	
		if (!isset($_POST['sitem']) || !isset($_POST['count']) || !isset($_POST['t']) || CheckNum($_POST['sitem']) || CheckNum($_POST['count'])) die($err1);		
		$sitem = $_POST['sitem']; $count = $_POST['count']; $t = $_POST['t'];		
		if ($count < 1) die($err1);
		$db->query("SET NAMES utf8");
		$query = "select * from `shop_items` WHERE id = ".$sitem;
		$rresult = query2mysql($query);		
		$row = mysqli_fetch_assoc($rresult);
		if ($row['rest']>=0)
			if (($row['rest']-$count) < 0) die('28');
		if ($t=='e') {			
			ShowCost($row['cost_expire']);
		} else 
		if ($t=='t') {
			if (($count*$row['count']) > $row['maxcount']) die($err1);
			ShowCost($row['cost_timeless']);
		} else die($err1);
		$gpoint*=$count;
		$spoint*=$count;
		$discount_data = @unserialize($row['discount_data']);
		if (!is_array($discount_data)) $discount_data = array();
		$curdiscount = 0;
		if (count($discount_data)>0){			
			foreach ($discount_data as $i1 => $val1){
				if ($count >= $i1) 
					$curdiscount = $val1;
			}			
		}
		$discount_txt = '';
		if ($curdiscount>0) {
			$dg = round($gpoint/100*$curdiscount);
			$ds = round($spoint/100*$curdiscount);
			$gpoint -= $dg;
			$spoint -= $ds;
			$discount_txt = ' (скидка '.$curdiscount.'%)';
		}		
		if (($gpoint==0)&&($spoint==0)) die($err1);
		CheckPoints($id,$gpoint,$spoint);	// Проверяем есть ли на аккаунте поинты
		$item = InitItem();
		$item['id'] = $row['itemid'];
		if ($t=='e') $item['count'] = $row['count']; else $item['count'] = $row['count']*$count;
		$item['max_count'] = $row['maxcount'];
		$item['data'] = $row['data'];
		$item['proctype'] = $row['proctype'];
		$expire = $row['expire'];
		if ($t=='e') $item['expire_date'] = time()+$expire*$count; else $item['expire_date'] = 0;
		$item['client_size'] = $row['client_size'];
		$q = SysSendMail(0, 32, 3, $f['data'][$num]['id'], 'Покупка из личного кабинета' ,'Приятной игры', $item, 0);
		if ($q['retcode'] == 217) die("51");
		$it = GetExtItem($row['itemid']);
		echo '29';
		if ($t=='t') $ddd=''; else $ddd=' на '.GetTime($expire*$count, false);
		$desc = $cur_role_name.': покупка '.$item['count'].' x '.$it->name.$ddd.$discount_txt;		
		if ($row['rest']>0){
			$query = "update `shop_items` set rest = rest-".$count." WHERE id = ".$sitem;
			$rresult = $db->query($query);
			if ($rresult==false) {
				//die("15");
			}
		}
		$query = "update `shop_items` set `buycount`=`buycount`+".$count." WHERE id = ".$sitem;
		$rresult = $db->query($query);
		if ($rresult==false) {
			//echo("Ошибка запроса<br>");
		}	
		break;	

		case 10:	// Перевод поинтов на другой акк
			if (isset($_POST['comment'])) $comment = $db->real_escape_string($_POST['comment']); else $comment = '';
			$lk_transfer_min_role_lvl = intval($_POST['lk_transfer_min_role_lvl']);
			$gpoint = intval($_POST['gold']);
			$spoint = 0;				
			$accid1 = intval($_POST['accid']);
			$accid = 16 + $accid1*16;
			if ($id == $accid) die('20'); // Самому себе
			if (!@AssignData(ak)) die('42');
			if ($lk_transfer_min_role_lvl) {
				if (!CheckRoleLvl($id, $lk_transfer_min_role_lvl)) die('111');
			}
			$sql = "SELECT * FROM `users` WHERE `id`=".$id;
			$result = query2mysql($sql, true);
			$row = mysqli_fetch_assoc($result);
			if (!$row['vkid'] && $_POST['lk_transfer_vk_only']) die('112');
			if (!$row['steamid'] && $_POST['lk_transfer_steam_only']) die('113');
			$sql = "SELECT * FROM `users` WHERE `id`=".$accid;
			$result = query2mysql($sql);
			if ($result->num_rows == 0) die('19');			
			CheckPoints($id,$gpoint,$spoint);	// Проверяем есть ли на аккаунте поинты
			$row = mysqli_fetch_assoc($result);			
			$recid = $row['ID'];
			$sacc = $id/16-1;
			GiveGold($recid, $gpoint, $spoint, "Перевод от аккаунта № $sacc $comment", 'Hidden');
			echo '21';
			$desc = 'Перевод монет на счет № '.$accid1." ".$comment;		
		break;

		case 11:	// Вывод голд поинтов в игру
			if (!isset($_POST['goldcount']) || !isset($_POST['lk2game_exchange_rate'])) die($err1);
			$lk2game_exchange_rate = $_POST['lk2game_exchange_rate'];
			$gpoint = $_POST['goldcount'];
			$spoint = 0;
			$zoneid = $_POST['zoneid'];
			$aid = $_POST['aid'];
			CheckPoints($id,$gpoint,$spoint);	// Проверяем есть ли на аккаунте поинты
			$result = UseCash($id, $zoneid, $aid, floor($gpoint*$lk2game_exchange_rate*100));			
			echo '44';
			$desc='Обмен '.$_POST['goldcount'].' ЛК на '.floor($gpoint*$lk2game_exchange_rate).' игрового голда';
		break;

		case 12: 				// Клан арты
			if (!isset($_POST['artid']) || CheckNum($_POST['artid'])) die($err1); else $artid = $_POST['artid'];
			$send_role_id = $f['data'][$num]['id'];	
			$send_txt_addon = '';		
			$db->query("SET NAMES utf8");
			$query = "select * from `klan_items` WHERE id = ".$artid;
			$rresult = query2mysql($query);		
			if ($rresult->num_rows > 0){
				$row = mysqli_fetch_assoc($rresult);
				if (!CheckKlanMaster($id, $row['klanid'], $f)) die('76');
				if ($_POST['name'] != '')
				{
					$name = $_POST['name'];
					$f1 = GetRoleId($name);
					if ($f1['retcode'] == 0 && $f1['roleid'] > 0)
					{
						$rid = $f1['roleid'];
						$role = GetRole($rid, 63);
						if ($role['retcode'] != 0) die('13');
						if ($role['value']['factionid'] != $row['klanid']) die('115');
						$send_role_id = $rid;
						$send_txt_addon = ' для '.$role['value']['name'];
					} else die('114');
				}
				if ($row['cost_item_id'] != 0 && $row['cost_item_count'] != 0) {
					isonline($id, $f['data'][$num]['id']);
					$cnt = finditem($f['data'][$num]['id'], $row['cost_item_id']);					
					if ($cnt < $row['cost_item_count']) die('77');		
					if (!takeitem($f['data'][$num]['id'], $row['cost_item_id'], $row['cost_item_count'])) die('78');
				}
				$gpoint = $row['costgold'];
				$spoint = $row['costsilver'];
				CheckPoints($id, $gpoint, $spoint);	// Проверяем есть ли на аккаунте поинты
				$item = InitItem();
				$item['id'] = $row['itemid'];
				$item['count'] = $row['count'];
				$item['max_count'] = $row['maxcount'];
				$item['data'] = $row['data'];
				$item['proctype'] = $row['proctype'];
				if ($row['expire']>0) $item['expire_date'] = time()+$row['expire']; else $item['expire_date'] = 0;
				$item['client_size'] = $row['client_size'];
				$q = SysSendMail(0, 32, 3, $send_role_id, 'Покупка из личного кабинета', 'Приятной игры', $item, 0);
				if ($q['retcode'] == 217) die('51'); else if ($q['retcode'] != 0) die('52');
				$it = GetExtItem($row['itemid']);
				echo "29";
				if ($row['expire']==0) $ddd=''; else $ddd=' на '.$row['expire'].' сек';
				$desc = $cur_role_name.': покупка клан арта '.$item->count.' '.$it->name.$ddd.$send_txt_addon;				
				$rresult = $db->query($query);
				$query = "update `klan_items` set `buycount`=`buycount`+1 WHERE id = ".$artid;
				$rresult = $db->query($query);
			} else die($err1);			
		break;		

		case 17:				// Получение информации о персонаже
			$id = intval($_POST['i']);
			if ($id < 16) die();
			$r = GetRole($id, 63);
			if ($r['retcode'] != 0) die('Персонаж не найден');
			$k = GetFactionInfo($r['value']['factionid']);
			if ($r['value']['factionid']==0) {
				$k['value']['name'] = 'Нет';
				$r['value']['factionrole'] = '';
			} else $r['value']['factionrole'] = $factrole[$r['value']['title']];
			printf('Ник: <b>%s</b><br>Класс: <b>%s</b><br>Уровень: <b>%d</b><br>Клан: <b>%s</b> %s', $r['value']['name'], GetOccupationName($r['value']['status']['occupation']), $r['value']['status']['level'], $k['value']['name'], $r['value']['factionrole']);			
			die();
		break;
		
		case 19:				// Редактируем шоп итем
			if (!isset($_POST['sitem']) || CheckNum($_POST['sitem'])) die('10');
			extract($_POST);
			$a = explode("\r\n", $discount_data);
			$b = array();
			if (count($a)>0)
			foreach ($a as $i1 => $val1){
				if ($val1=='') continue;
				$c = explode('-',$val1);
				if (count($c)!=2) continue;
				$b[$c[0]] = $c[1];
			}
			$discount_data = @serialize($b);
			$db->query("SET NAMES utf8");
			$sql = sprintf("UPDATE `shop_items` SET `itemid`=%d, `count`=%d, `maxcount`=%d, `data`='%s', `client_size`=%d, `proctype`=%d, `subcat`=%d, `cost_timeless`='%s', `cost_expire`='%s', `expire`=%d, `discount_data`='%s', `desc`='%s', `rest`=%d, `buycount`=%d WHERE `id`=%d", $itemid, $count, $maxcount, $db->real_escape_string($data), $client_size, $proctype, $subcat, $db->real_escape_string($cost_timeless_gold.'|'.$cost_timeless_silver), $db->real_escape_string($cost_expire_gold.'|'.$cost_expire_silver), $expire, $db->real_escape_string($discount_data), $db->real_escape_string($desc), $rest, $buycount, $sitem);
			$res = query2mysql($sql);
			die('16');
		break;	

		case 20:				// Смена пароля от аккаунта
			$gpoint=0;
			$spoint=0;
			extract($_POST);
			$query="UPDATE `users` SET `passwd2`=`passwd` WHERE `name`='$login' AND `ID`=$id";
			$result = query2mysql($query);
			//$query="call changePasswd('$login',$md52)";
			$query = sprintf("UPDATE `users` SET `passwd`='%s' WHERE `name`='%s'", $db->real_escape_string($newpassw), $db->real_escape_string($login));
			$result = query2mysql($query);
			$db->query("INSERT INTO `changepass` (`name`,`ip`,`data`) VALUES ('".$login."','".$ip."',now())");
			echo '43';
			$desc='Смена пароля аккаунта';
		break;		

		case 53:				// Редактируем клан арт итем
			if (!isset($_POST['kitem']) || CheckNum($_POST['kitem'])) die('10');
			extract($_POST);			
			query2mysql("SET NAMES utf8");
			if (isset($remove_no_klan)) $remove_no_klan = 1; else $remove_no_klan = 0;
			$sql = sprintf("UPDATE `klan_items` SET `klanid`=%d, `itemid`=%d, `count`=%d, `maxcount`=%d, `data`='%s', `client_size`=%d, `proctype`=%d, `expire`=%d, `costgold`=%d, `costsilver`=%d, `cost_item_id`=%d, `cost_item_count`=%d, `remove_no_klan`=%d, `desc`='%s', `buycount`=%d WHERE `id`=%d", $klanid, $itemid, $count, $maxcount, $db->real_escape_string($data), $client_size, $proctype, $expire, $costgold, $costsilver, $cost_item_id, $cost_item_count, $remove_no_klan, $db->real_escape_string($desc), $buycount, $kitem);
			$res = query2mysql($sql);
			die('16');
		break;

		case 54:				// Добавляем новый клан арт итем			
			$sql = "INSERT INTO `klan_items` (`klanid`,`itemid`,`count`,`maxcount`,`data`,`client_size`,`proctype`,`expire`,`costgold`,`costsilver`,`cost_item_id`,`cost_item_count`,`desc`,`buycount`) VALUES ('0', '0', '1', '1', '', '0', '0', '0', '0', '0', '0', '0', '', '0');";
			$res = query2mysql($sql);
			die('16');
		break;	

		case 55:				// Удаляем клан арт итем
			if (!isset($_POST['kitem']) || CheckNum($_POST['kitem'])) die('10');
			$kitem = intval($_POST['kitem']);			
			$sql = sprintf("DELETE FROM `klan_items` WHERE `id`=%d", $kitem);
			$res = query2mysql($sql);
			die('16');
		break;

		case 56:				// Добавляем IP в список разрешенных
			if (!isset($_POST['addip']) || strlen($_POST['addip'])>15) die('10');
			$addip = $_POST['addip'];
			$sql = sprintf("SELECT `ipdata` FROM `users` WHERE `ID`=%d", $id);
			$res = query2mysql($sql, true);
			$row = mysqli_fetch_assoc($res);
			$ipdata = @unserialize($row['ipdata']);
			if (!is_array($ipdata)) {
				$ipdata = array();
				$ipdata[0] = false;
				$ipdata[1] = array();
				$ipdata[2] = false;
			}
			if (!isset($ipdata[2])) $ipdata[2] = false;
			array_push($ipdata[1], $addip);
			$sql = sprintf("UPDATE `users` SET `ipdata`='%s' WHERE `ID`=%d", $db->real_escape_string(serialize($ipdata)), $id);
			$res = query2mysql($sql);
			die('16');
		break;

		case 57:				// Удаляем IP из списка разрешенных
			if (!isset($_POST['i']) || CheckNum($_POST['i'])) die('10');
			$i = $_POST['i'];
			$sql = sprintf("SELECT `ipdata` FROM `users` WHERE `ID`=%d", $id);
			$res = query2mysql($sql, true);
			$row = mysqli_fetch_assoc($res);
			$ipdata = @unserialize($row['ipdata']);
			if (!is_array($ipdata)) {
				$ipdata = array();
				$ipdata[0] = false;
				$ipdata[1] = array();
				$ipdata[2] = false;
			}
			if (!isset($ipdata[2])) $ipdata[2] = false;
			if (!isset($ipdata[1][$i])) die('10');
			unset($ipdata[1][$i]);
			// Если нет айпи - снимаем блок со входа в ЛК
			if (count($ipdata[1]) == 0) $ipdata[0] = false;
			$sql = sprintf("UPDATE `users` SET `ipdata`='%s' WHERE `ID`=%d", $db->real_escape_string(serialize($ipdata)), $id);
			$res = query2mysql($sql);
			die('16');
		break;

		case 58:				// Активируем/отключаем режим ограничения по айпи в ЛК
			if (!isset($_POST['mode'])) die('10');
			$mode = intval($_POST['mode']);
			$sql = sprintf("SELECT `ipdata` FROM `users` WHERE `ID`=%d", $id);
			$res = query2mysql($sql, true);
			$row = mysqli_fetch_assoc($res);
			$ipdata = @unserialize($row['ipdata']);
			if (!is_array($ipdata)) {
				$ipdata = array();
				$ipdata[0] = false;
				$ipdata[1] = array();
				$ipdata[2] = false;
			}			
			if (!isset($ipdata[2])) $ipdata[2] = false;
			$ipdata[0] = (bool)$mode;
			$sql = sprintf("UPDATE `users` SET `ipdata`='%s' WHERE `ID`=%d", $db->real_escape_string(serialize($ipdata)), $id);
			$res = query2mysql($sql);
			die('16');
		break;

		case 59:				// Активируем/отключаем режим ограничения по айпи в игре
			if (!isset($_POST['mode'])) die('10');
			$mode = intval($_POST['mode']);
			$sql = sprintf("SELECT `ipdata` FROM `users` WHERE `ID`=%d", $id);
			$res = query2mysql($sql, true);
			$row = mysqli_fetch_assoc($res);
			$ipdata = @unserialize($row['ipdata']);
			if (!is_array($ipdata)) {
				$ipdata = array();
				$ipdata[0] = false;
				$ipdata[1] = array();
				$ipdata[2] = false;
			}			
			$ipdata[2] = (bool)$mode;
			$sql = sprintf("UPDATE `users` SET `ipdata`='%s' WHERE `ID`=%d", $db->real_escape_string(serialize($ipdata)), $id);
			$res = query2mysql($sql);
			die('16');
		break;

		case 69:				// Редактируем промо-код
			if (!isset($_POST['record_id']) || CheckNum($_POST['record_id'])) die('10');
			extract($_POST);	
			if (isset($_POST['promo_multi_user'])) $promo_multi_user = 1; else $promo_multi_user = 0;		
			$db->query("SET NAMES utf8");			
			$sql = sprintf("UPDATE `promo_codes` SET `code`='%s', `expire`=%d, `group`=%d, `bonus_money_gold`=%d, `bonus_money_silver`=%d, `bonus_item_id`=%d, `bonus_item_count`=%d, `bonus_item_max_count`=%d, `bonus_item_data`='%s', `bonus_item_client_size`=%d, `bonus_item_proctype`=%d, `bonus_item_expire`=%d, `multi_user`=%d, `used_userid`=%d, `desc`='%s' WHERE `id`=%d", $promo_code, $promo_expire, $promo_group, $promo_gold, $promo_silver, $promo_item_id, $promo_item_count, $promo_item_maxcount, $db->real_escape_string($promo_item_data), $promo_item_client_size, $promo_item_proctype, $promo_item_expire, $promo_multi_user, $promo_used_userid, $db->real_escape_string($promo_desc), $record_id);
			$res = $db->query($sql);
			if (!$res) die($db->error);			
			die('ok');
		break;

		case 70:				// Удаляем промо-код из базы
			if (!isset($_POST['record_id']) || CheckNum($_POST['record_id'])) die('10');
			$record_id = intval($_POST['record_id']);			
			$sql = sprintf("DELETE FROM `promo_codes` WHERE `id`=%d", $record_id);
			$res = $db->query($sql);
			if (!$res) die($db->error);
			die('16');
		break;

		case 71:				// Удаляем просроченные, но не использованные промо-коды из базы
			$sql = sprintf("DELETE FROM `promo_codes` WHERE `expire`<=%d AND `expire`<>0 AND `used_userid`=0", time());
			$res = $db->query($sql);
			if (!$res) die($db->error);
			die('16');
		break;

		case 72:				// Удаляем использованные промо-коды из базы
			$sql = "DELETE FROM `promo_codes` WHERE `used_userid`>0";
			$res = $db->query($sql);
			if (!$res) die($db->error);
			die('16');
		break;

		case 73:				// Используем промо-код
			if (!isset($_POST['promo_code'])) die('10');
			// Пытаемся рассинхронизировать параллельные потоки для избежания дюпа и багания монет
			usleep(rand(500,90000));
			$db->query("SET NAMES utf8");
			$promo_code = $_POST['promo_code'];
			$sql = "SELECT * FROM `promo_codes` WHERE BINARY `code`='".$db->real_escape_string($promo_code)."'";
			$res = query2mysql($sql);
			if (!$res->num_rows) die('99');
			$row = mysqli_fetch_assoc($res);
			if ($row['used_userid'] > 0 && !$row['multi_user']) die('100');
			if ($row['expire'] > 0 && $row['expire']<=time()) die('101');
			if ($row['multi_user'] || $row['group']) {
				$row['group'] = intval($row['group']);
				$sql = "SELECT `bonus_data` FROM `users` WHERE `ID`=".$id;
				$res = query2mysql($sql, true);
				$rr = mysqli_fetch_assoc($res);
				$bonus_data = @unserialize($rr['bonus_data']);
				if (!is_array($bonus_data)) $bonus_data = array();
				if (!isset($bonus_data['promo']) || !is_array($bonus_data['promo'])) $bonus_data['promo'] = array();
				if (!isset($bonus_data['promo_group']) || !is_array($bonus_data['promo_group'])) $bonus_data['promo_group'] = array();
				if ($row['multi_user']) {
					if (in_array($promo_code, $bonus_data['promo'])) die('100');
					array_push($bonus_data['promo'], $promo_code);
					$row['used_userid']++;
				}
				if ($row['group']) {
					if (in_array($row['group'], $bonus_data['promo_group'])) die('103');
					array_push($bonus_data['promo_group'], $row['group']);
					if (!$row['multi_user']) $row['used_userid'] = $id;
				}
			} else $row['used_userid'] = $id;
			if ($row['bonus_item_id'] > 0 && $row['bonus_item_count'] > 0) {
				$actrole = GetActiveRole($id);
				if ($actrole==0) die('95');
				$item = InitItem();
				$item['id'] = $row['bonus_item_id'];
				$item['count'] = $row['bonus_item_count'];
				$item['max_count'] = $row['bonus_item_max_count'];
				$item['data'] = $row['bonus_item_data'];
				$item['proctype'] = $row['bonus_item_proctype'];
				if ($row['bonus_item_expire']>0) $item['expire_date'] = time() + $row['bonus_item_expire']; else $item['expire_date'] = 0;
				$item['client_size'] = $row['bonus_item_client_size'];
				$q = SysSendMail(0, 32, 3, $actrole, 'Бонус', 'За использование промо-кода '.$promo_code, $item, 0);
				if ($q['retcode'] == 217) die('51'); else if ($q['retcode'] != 0) die('52');
			}
			if ($row['multi_user'] || $row['group']) {
				$res = query2mysql(sprintf("UPDATE `users` SET `bonus_data`='%s' WHERE `ID`=%d", $db->real_escape_string(serialize($bonus_data)), $id));
			}
			$res = query2mysql(sprintf("UPDATE `promo_codes` SET `used_userid`=%d WHERE `id`=%d", $row['used_userid'], $row['id']));
			$gpoint = -$row['bonus_money_gold'];
			$spoint = -$row['bonus_money_silver'];
			$desc = 'Промо-код '.$promo_code;			
		break;

		case 77:			// Админ активация аккаунта
			if (!isset($_POST['ip']) || !isset($_POST['userid'])) die('10');
			$userid = intval($_POST['userid']);
			$query = sprintf("SELECT `passwd` FROM `users` WHERE `ID`=%d", $userid);
			$result = query2mysql($query); 
			if (!$result->num_rows) die('10');
			$row = mysqli_fetch_assoc($result);
			if ($row['passwd']!='confirm') die('104');
			$query = sprintf("UPDATE `users` SET `passwd`=`passwd2` WHERE `ID`=%d", $userid);
			$result = query2mysql($query); 
			die('ok');
		break;

		default : die($err1); break;
	}
	GiveGold($id, -$gpoint, -$spoint, $desc, $ip);
	if ($n!=7 && $n!=8 && $n!=10 && $n!=11 && $n!=12 && $n!=20) echo "16";
	die();
}

function AdminAction($id, $act){
	global $db;
	$db->query("SET NAMES utf8");
	switch ($act){
		case 'addcat':
			$res = query2mysql("INSERT INTO `shop_cat` (`name`) VALUES ('New')");
			die('16');
		break;

		case 'delcat':
			if (!isset($_POST['page'])) die('10');
			$page = intval($_POST['page']);
			$res = $db->query("SELECT * FROM `shop_subcat` WHERE `catid`=".$page);
			if ($res->num_rows>0){
				while ($row = mysqli_fetch_assoc($res)){
					$res1 = query2mysql("DELETE FROM `shop_items` WHERE `subcat`=".$row['id']);
				}
			}
			$res = query2mysql("DELETE FROM `shop_subcat` WHERE `catid`=".$page);
			$res = query2mysql("DELETE FROM `shop_cat` WHERE `id`=".$page);
			die('16');
		break;

		case 'addsubcat':
			if (!isset($_POST['page'])) die('10');
			$page = intval($_POST['page']);
			$res = query2mysql("INSERT INTO `shop_subcat` (`catid`,`name`) VALUES (".$page.",'New')");
			die('16');
		break;

		case 'delsubcat':
			if (!isset($_POST['page'])) die('10');
			$page = intval($_POST['page']);
			if (!isset($_POST['subcat'])) die('10');
			$subcat = intval($_POST['subcat']);			
			$res = query2mysql("DELETE FROM `shop_items` WHERE `subcat`=".$subcat);
			$res = query2mysql("DELETE FROM `shop_subcat` WHERE `id`=".$subcat);
			die('16');
		break;

		case 'rencat':
			if (!isset($_POST['newname'])) die('10');
			$newname = $db->real_escape_string($_POST['newname']);
			if (!isset($_POST['page'])) die('10');
			$page = intval($_POST['page']);
			$res = query2mysql(sprintf("UPDATE `shop_cat` SET `name`='%s' WHERE `id`=%d", $newname, $page));
			die('16');
		break;

		case 'rensubcat':
			if (!isset($_POST['newname'])) die('10');
			$newname = $db->real_escape_string($_POST['newname']);
			if (!isset($_POST['page'])) die('10');
			$page = intval($_POST['page']);
			if (!isset($_POST['subcat'])) die('10');
			$subcat = intval($_POST['subcat']);	
			$res = query2mysql(sprintf("UPDATE `shop_subcat` SET `name`='%s' WHERE `id`=%d AND `catid`=%d", $newname, $subcat, $page));
			die('16');
		break;

		case 'additem':
			if (!isset($_POST['subcat'])) die('10');
			$subcat = intval($_POST['subcat']);	
			$res = query2mysql(sprintf("INSERT INTO `shop_items` (`itemid`, `count`, `maxcount`, `subcat`, `rest`) VALUES (57440, 1, 10000, %d, -1)", $subcat));
			$newid = $db->insert_id;
			die('16|'.$newid);
		break;

		case 'delitem':
			if (!isset($_POST['page'])) die('10');
			$page = intval($_POST['page']);
			if (!isset($_POST['subcat'])) die('10');
			$subcat = intval($_POST['subcat']);	
			if (!isset($_POST['sitem'])) die('10');
			$sitem = intval($_POST['sitem']);			
			$res = query2mysql(sprintf("DELETE FROM `shop_items` WHERE `subcat`=%d AND `id`=%d", $subcat, $sitem));
			die('16');
		break;

		case 'cloneitem':
			if (!isset($_POST['sitem'])) die('10');
			$sitem = intval($_POST['sitem']);	
			$res = query2mysql("SELECT * FROM `shop_items` WHERE `id`=".$sitem, true);
			$row = mysqli_fetch_assoc($res);
			$res = query2mysql(sprintf("INSERT INTO `shop_items` (`itemid`, `count`, `maxcount`, `data`, `client_size`, `proctype`, `subcat`, `cost_timeless`, `cost_expire`, `expire`, `discount_data`, `desc`, `rest`) VALUES (%d, %d, %d, '%s', %d, %d, %d, '%s', '%s', %d, '%s', '%s', %d)", $row['itemid'], $row['count'], $row['maxcount'], $row['data'], $row['client_size'], $row['proctype'], $row['subcat'], $row['cost_timeless'], $row['cost_expire'], $row['expire'], $row['discount_data'], $db->real_escape_string($row['desc']), $row['rest']));
			die('16');
		break;

		case 'itemname':
			if (!isset($_POST['itemid'])) die();
			$itemid = intval($_POST['itemid']);
			if ($itemid == 0) die();
			$item = GetExtItem($itemid);
			$icon = sprintf('<img src="getitemicon.php?i=%s" border="0" class="item_icon"> ', urlencode(base64_encode($item->icon)));
			die($icon.' <span class="it_name">'.$item->name.'</span>');
		break;

		case 'klanname':
			if (!isset($_POST['itemid'])||!isset($_POST['servid'])) die();
			$itemid = intval($_POST['itemid']);
			$servid = intval($_POST['servid']);
			if ($itemid == 0) die();
			$fact = GetFactionInfo($itemid);
			echo '<img src="klan/geticon.php?servid='.$servid.'&klan='.$itemid.'" class="imcnt"> '.$fact['value']['name'];
			die();
		break;

		case 'get_all_users':
			$logins = [];
			$q = $db->query("SELECT `name` FROM users ORDER BY name ASC");
			while ($row = $q->fetch_assoc()) {
				$logins[] = $row['name'];
			}
			die(serialize($logins));
		break;
	}
}

function GetRefData($id){
	global $db;
	$refpers = 0;
	$refacc = 0;
	$sql="SELECT `referal` FROM `users` WHERE `ID`=".$id;
	$result = @$db->query($sql);
	$mt = 0;
	$rb = false;
	if ($result && $result->num_rows) {
		$row = mysqli_fetch_assoc($result);
		$refacc = $row['referal'];	
		if ($refacc != 0){			
			$f = GetUserRoles($refacc);
			$lt = 0;
			if ($f['retcode'] == 0) {						
				foreach ($f['data'] as $i => $val){
					$r = GetRole($val['id'], 63);
					if ($r['value']['status']['reborndata'] != '') $rb = $r['value']['status']['reborndata'];
					$mt += $r['value']['status']['time_used'];
					if ($r['value']['status']['time_used'] > $lt){
						$lt = $r['value']['status']['time_used'];
						$refpers = $val['id'];
					}
				}
			}
		}
	}
	$a = array($refacc, $refpers, $mt, $rb);
	return $a;
}

function GetActiveRole($id){
	global $db;
	$f = false;
	$db->query("SET NAMES utf8");
	$res = $db->query('SELECT `bonus_data` FROM `users` WHERE `ID`='.$id);
	$row = mysqli_fetch_assoc($res);
	if ($row['bonus_data']=='') $bonus_data = array(); else $bonus_data = @unserialize($row['bonus_data']);
	if (!is_array($bonus_data)) $bonus_data = array();
	$cur_reward_role_index = -1;
	if (isset($bonus_data['cur_reward_role_index'])) {
		$cur_reward_role_index = $bonus_data['cur_reward_role_index'];
		$f = GetUserRoles($id);
		if ($cur_reward_role_index >= count($f['data'])) $cur_reward_role_index = -1;
	}
	if ($cur_reward_role_index >= 0) $a = $f['data'][$cur_reward_role_index]['id']; else
	{
		$role = isroleonline($id);
		if ($role > 0) return $role;
		if ($f === false) $f = GetUserRoles($id);
		$lt = 0;
		$a = 0;
		if ($f['retcode']==0) {						
			foreach ($f['data'] as $i => $val){
				$r = GetRole($val['id'], 63);
				if ($r['value']['status']['time_used'] > $lt){
					$lt = $r['value']['status']['time_used'];
					$a = $val['id'];
				}
			}
		}
	}
	return $a;
}

function donate(){
	global $db;	
	if (!isset($_POST['ip'])||!isset($_POST['out_summ'])||!isset($_POST['inv_id'])||!isset($_POST['login'])||!isset($_POST['userid'])||!isset($_POST['moneycount'])||!isset($_POST['bonus'])||!isset($_POST['act_bonus'])||!isset($_POST['don_kurs'])||!isset($_POST['don_system'])) die('2');
	$out_summ = $_POST["out_summ"];
	$don_kurs = $_POST["don_kurs"];
	$don_system = $_POST['don_system'];
	$inv_id = intval($_POST["inv_id"]);
	$p_sys_id = $_POST['p_sys_id'];
	$login = $_POST["login"];
	$userid = $_POST["userid"];
	$moneycount = $_POST["moneycount"];
	$bonus = $_POST["bonus"];
	$act_bonus = $_POST["act_bonus"];
	$item = unserialize($_POST["item"]);
	$send_bonus_item = $_POST["send_bonus_item"];
	$ip = $_POST["ip"];
	$ref_don_bonus_enable = $_POST['ref_don_bonus_enable'];
	$ref_don_bonus = (float)$_POST['ref_don_bonus'];
	$ref_don_bonus_timeused = (int)$_POST['ref_don_bonus_timeused'];
	$dsctxt='Спасибо за пожертвование';
	if ($out_summ < $don_kurs) {
		$moneycount=0;
		$bonus = 0;
		$send_bonus_item = false;
	}
	// Проверяем по номеру заявки
	$sql = "SELECT * FROM `donate` WHERE `inv_id`='$inv_id' AND `status`=1 AND `don_system`='".$db->real_escape_string($don_system)."'";
	$res = $db->query($sql);
	if ($res->num_rows>0) {
		$sql = "SELECT * FROM `donate` WHERE `p_sys_id`='".$db->real_escape_string($p_sys_id)."' AND `p_sys_id`<>'' AND `status`=1 AND `don_system`='".$db->real_escape_string($don_system)."'";
		$res = $db->query($sql);
		if ($res->num_rows > 0) {
			die('101'); // Этот заказ уже обработан
		} else die('NeedNewID'); // Нужна новая заявка
	}
	// Проверяем по номеру платежа
	$sql = "SELECT * FROM `donate` WHERE `p_sys_id`='".$db->real_escape_string($p_sys_id)."' AND `p_sys_id`<>'' AND `status`=1 AND `don_system`='".$db->real_escape_string($don_system)."'";
	$res = $db->query($sql);
	if ($res->num_rows>0) {
		die('101'); // Этот заказ уже обработан
	}
	$sql = "update `users` set lkgold=lkgold+$moneycount+$bonus WHERE `name`='$login'";
	$points = $moneycount+$bonus;
	$res = $db->query($sql);
	if (($db->affected_rows>0)||($points==0)) {
		$sql = "SELECT * FROM `donate` WHERE `inv_id`='$inv_id' AND `status`=0 AND `don_system`='".$db->real_escape_string($don_system)."'";
		$res = $db->query($sql);
		if ($res->num_rows>0) { // Заказ добавлен, но не обработан
			$db->query("UPDATE `donate` SET `status`=1 WHERE `inv_id`='$inv_id' AND `don_system`='".$db->real_escape_string($don_system)."'");
		} else {
			$sql="INSERT INTO `donate` (`inv_id`, `p_sys_id`, `don_system`, `data`, `out_summ`, `don_kurs`, `money`, `act_bonus`, `bonus_money`, `login`, `userid`, `ip`, `status`) VALUES ('$inv_id', '".$db->real_escape_string($p_sys_id)."', '".$db->real_escape_string($don_system)."', NOW(), '$out_summ', '$don_kurs', '$moneycount', '$act_bonus', '$bonus', '$login', '$userid', '$ip', 1)";	
			$db->query($sql);
		}
		$CashLeft = GetCashFromAcc($userid);	
		$db->query("SET NAMES utf8");
		// Лог: Выдача основных монет
		$sql="insert into `lklogs` (`userid` ,`data` , `ip`, `gold` ,`silver`, `gold_rest` ,`silver_rest`, `desc` ) VALUES ($userid, now(), '', -$moneycount, 0, ".($CashLeft[0]-$bonus).", ".$CashLeft[1].", '".$db->real_escape_string($dsctxt)."');";				
		$db->query($sql);		
		// Лог: Выдача бонусных поинтов
		if ($bonus > 0) {
			sleep(1);
			$sql="insert into `lklogs` (`userid` ,`data` , `ip`, `gold` ,`silver`, `gold_rest` ,`silver_rest`, `desc` ) VALUES ($userid, now(), '', -$bonus, 0, ".$CashLeft[0].", ".$CashLeft[1].", 'Бонус');";				
			$db->query($sql);
		}		
		// выдача итема активному персонажу аккаунта
		if ($item['expire_date']) $item['expire_date'] += time();
		if (!$send_bonus_item) $item = InitItem();
		$actrole = GetActiveRole($userid);
		if ($actrole!=0) {
			if ($moneycount > 0) $msgtext = sprintf('Спасибо за пожертвование, вам в ЛК начислено %s монет. ', $moneycount); else
			$msgtext = 'Спасибо за пожертвование. ';
			if ($bonus > 0) $msgtext.=sprintf('Также вы получаете %s бонусных монет! ', $bonus);
			$msgtext.='Приятной игры';			
			$q = SysSendMail(0, 32, 3, $actrole, 'Уведомление', $msgtext, $item, 0);
		}
		if ($ref_don_bonus_enable) {
			// Выдача бонуса рефералу		
			$ref = GetRefData($userid);
			if ($ref[0]!=0 && $moneycount > 0 && $ref[1]!=0 && $ref[2] >= $ref_don_bonus_timeused){		// Только если выданы поинты, и есть нужное время онлайна 
				$ref_bonus = round($moneycount/100*$ref_don_bonus);
				if ($ref_bonus > 0) {
					$sql = "update `users` set lkgold=lkgold+$ref_bonus WHERE `ID`=".$ref[0];
					$res=$db->query($sql);
					$sql = "update `users` set ref_bonus=ref_bonus+$ref_bonus WHERE `ID`=".$userid;
					$res=$db->query($sql);
					$dsctxt = 'Бонус за пожертвование от приглашенного игрока №'.(($userid/16) - 1);
					$CashLeft = GetCashFromAcc($ref[0]);
					$sql="insert into `lklogs` (`userid` ,`data` , `ip`, `gold` ,`silver`, `gold_rest` ,`silver_rest`, `desc` ) VALUES (".$ref[0].", now(), '', -$ref_bonus, 0, ".$CashLeft[0].", ".$CashLeft[1].", '".$db->real_escape_string($dsctxt)."');";
					$db->query($sql);
					$actrole = GetActiveRole($ref[0]);
					if ($actrole!=0) {
						$item = InitItem();
						$q = SysSendMail(0, 32, 3, $actrole, 'Уведомление', sprintf('Вам в ЛК начислено %d монет за приглашенного игрока №%s', $ref_bonus, ($userid/16)-1), $item, 0);
					}
				}
			}
		}
		die('100');
	} else {
		$sql="INSERT INTO `donate` (`inv_id`, `p_sys_id`, `don_system`, `data`, `out_summ`, `don_kurs`, `money`, `act_bonus`, `bonus_money`, `login`, `ip`, `status`) VALUES ('$inv_id', '".$db->real_escape_string($p_sys_id)."', '".$db->real_escape_string($don_system)."', NOW(), '$out_summ', '$don_kurs', '$moneycount', '$act_bonus', '$bonus', '$login', '$ip', 0)";		
		$db->query($sql);
		die('3');
	}
}

function CheckBrut($answ=''){
	global $db;
	// Проверка на брут
	if (!$db->query(sprintf("DELETE FROM `antibrut` WHERE `last_date_fail`<=%d", time()-600))) {
		if ($answ == '') die('errorbase'); // Чистка таблицы антибрута
		else MakeAnswer($answ, 1000, 'errorbase');
	}
	$query = sprintf("SELECT * FROM `antibrut` WHERE `ip`='%s'", $db->real_escape_string($_POST['ip']));
	$result = $db->query($query);
	if ($result->num_rows) {
		$row = mysqli_fetch_assoc($result);
		if (($row['fail_count'])>=5) {
			if ($answ == '') die('time');
			else MakeAnswer($answ, 1000, 'time');
		}
	}
}

function Register(){
	global $db;
	if (!isset($_POST['ip']) || !isset($_POST['login']) || !isset($_POST['pass']) || !isset($_POST['email']) || !isset($_POST['email_confirm']) || !isset($_POST['question']) || !isset($_POST['answer']) || !isset($_POST['realname']) || !isset($_POST['referal']) || !isset($_POST['ip_max_reg']) || !isset($_POST['email_max_reg']) || !isset($_POST['register_gold']) || !isset($_POST['zoneid']) || !isset($_POST['aid'])) die('10');	
	$pass = ($_POST['email_confirm'])?'confirm':base64_decode($_POST['pass']);
	$ip_max_reg = intval($_POST['ip_max_reg']);
	$email_max_reg = intval($_POST['email_max_reg']);
	$ip = $db->real_escape_string($_POST['ip']);
	$referal = intval($_POST['referal']);	
	$register_gold = intval($_POST['register_gold']);
	$login = $db->real_escape_string(trim($_POST['login']));
	$question = $db->real_escape_string(trim($_POST['question']));
	$answer = $db->real_escape_string(trim($_POST['answer']));
	$email = $db->real_escape_string(trim($_POST['email']));
	$realname = $db->real_escape_string(trim($_POST['realname']));
	$vkid = $db->real_escape_string($_POST['vk_id']);
	$vkname = $db->real_escape_string($_POST['vk_name']);
	$vkphoto = $db->real_escape_string($_POST['vk_photo']);
	$steamid = $db->real_escape_string($_POST['steam_id']);
	$steamname = $db->real_escape_string($_POST['steam_name']);
	$steamphoto = $db->real_escape_string($_POST['steam_photo']);
	if ($vkid) $query = "SELECT * FROM `users` WHERE `vkid` = '$vkid'"; else
	if ($steamid) $query = "SELECT * FROM `users` WHERE `steamid` = '$steamid'";
	if ($vkid || $steamid) {
		$rresult = $db->query($query);
		if ($rresult==false) {
			die("errorbase");
		}
		if ($rresult->num_rows) {
			die("needauth");
		}
	}
	$zoneid = $_POST['zoneid'];
	$aid = $_POST['aid'];
	CheckBrut();	
	if ($ip=='') $ip = 'none';
	CheckAntibrutIP();
	// Check if user already exists
	$query = "SELECT * FROM `users` WHERE name = '$login'";
	$rresult = $db->query($query);
	if ($rresult==false) {
		die("errorbase");
	}
	if ($rresult->num_rows) {
		die("exists");
	}	
	if ($ip_max_reg > 0) {
	// Check count IP reg
		$query = "SELECT count(*) FROM `users` WHERE `idnumber` = '$ip'";
		$rresult = $db->query($query);
		if ($rresult==false) {
			die("errorbase");
		}
		$row = mysqli_fetch_array($rresult);
		if (intval($row[0]) >= $ip_max_reg) {
			die("iplimit");
		}	
	}
	if ($email_max_reg > 0) {
	// Check count EMail reg
		$query = "SELECT count(*) FROM `users` WHERE `email` = '$email'";
		$rresult = $db->query($query);
		if ($rresult==false) {
			die("errorbase");
		}
		$row = mysqli_fetch_array($rresult);
		if (intval($row[0]) >= $email_max_reg) {
			die("emaillimit");
		}	
	}
        if ($referal > 0) {
		// Проверяем наличие реф акка
		$result = $db->query('SELECT `ID` FROM `users` WHERE `ID`='.$referal);
		if (!$result) die("errorbase");
		if (!$result->num_rows) {
			$referal = 0;
		}
	}
	$query = "SELECT (IFNULL(MAX(id), 16)+16) from `users`"; 
	$result = $db->query($query); 
	if (!$result) die("errorbase");
	$row = mysqli_fetch_array($result);
	$userid = $row[0];
	if ($referal >= $userid) $referal = 0;
	$query = sprintf("INSERT INTO `users` (`id`,`name`,`passwd`,`Prompt`,`answer`,`truename`,`idnumber`,`email`,`province`,`city`,`address`,`creatime`,`passwd2`,`referal`) VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', now(), '%s', %d)", $userid, $login, $db->real_escape_string($pass), $question, $answer, $realname, $ip, $email, '', '', '', $db->real_escape_string(base64_decode($_POST['pass'])), $referal);
	$rresult = $db->query($query);	
	if (!$rresult) die("errorbase: ");
	$db->query('SET names utf8');
 	$query = sprintf("UPDATE `users` SET `Prompt`='%s', `answer`='%s', `truename`='%s', `vkid`='%s', `vkname`='%s', `vkphoto`='%s', `steamid`='%s', `steamname`='%s', `steamphoto`='%s' WHERE `ID`=%d", $question, $answer, $realname, $vkid, $vkname, $vkphoto, $steamid, $steamname, $steamphoto, $userid);
	$rresult = $db->query($query);	
	if (!$rresult) die("errorbase");
	if ($register_gold > 0) {
		$result = UseCash($userid, $zoneid, $aid, $register_gold*100);
	}
	die("ok".$userid);
}

function ResendMail(){
	global $db;
	$answ = array();
	CheckBrut($answ);
	CheckAntibrutIP($answ);
	if (!isset($_POST['ip']) || !isset($_POST['login'])) MakeAnswer($answ, 1000, 'inputdata');
	$result = $db->query("SELECT `ID`,`passwd`, `email` FROM `users` WHERE `name`='".$db->real_escape_string($_POST['login'])."'");
	if (!$result) MakeAnswer($answ, 1000, 'errorbase');
	$row = mysqli_fetch_assoc($result);
	if ($row['passwd']!='confirm') MakeAnswer($answ, 1000, 'already');	
	$answ['realid'] = $row['ID'];
	$answ['email'] = $row['email'];
	MakeAnswer($answ, 0, 'ok');
}

function ActReg(){
	global $db;
	if (!isset($_POST['ip']) || !isset($_POST['login']) || !isset($_POST['idacc'])) die('10');	
	$query = sprintf("SELECT `passwd` FROM `users` WHERE `ID`=%d AND `name`='%s'", $_POST['idacc'], $db->real_escape_string($_POST['login']));
	$result = $db->query($query); 
	if (!$result) die("errorbase");
	if (!$result->num_rows) die('10');
	$row = mysqli_fetch_assoc($result);
	if ($row['passwd']!='confirm') die('already');
	$query = sprintf("UPDATE `users` SET `passwd`=`passwd2` WHERE `ID`=%d AND `name`='%s'", $_POST['idacc'], $db->real_escape_string($_POST['login']));
	$result = $db->query($query); 
	if (!$result) die("errorbase");
	die('ok');
}

function CheckLogin(){
	global $db;
	CheckBrut();
	CheckAntibrutIP();
	if (isset($_POST['login'])) {
		$login = $db->real_escape_string($_POST['login']);
	} else die();
	if (strlen($login)>20) echo "1"; else {		
		$query = "SELECT * FROM users WHERE name = '$login'";
		$rresult = $db->query($query);	
		if ($rresult->num_rows) echo "denied"; else
		echo "free";
	}
	die();
}

function checker($email){
	if (preg_match("/^[0-9a-z_\-\.]+@[0-9a-z_\-^\.]+\.[a-z]{2,4}$/i", $email)) $h=1; else $h=0;
	return($h);
}

function getdata($s1,$s2) {
	return ('
      <tr>
         <td width="150px" align="right" valign="top"><font color="#aa0000">'.$s1.':</font></td>
         <td align="left" valign="top">'.$s2.'</td>
      </tr>');	
}

function GetHash($servid, $login, $pass, $email, $cookie_pasw, $encoder_Salt, $operation = 0){
	// $operation: 0 - null pass, 1 - disable ip check
	$s = sprintf('%d|%s|%s|%s|%d', $servid, $login, md5($pass), $email, $operation);
	return mycrypt(encode($s, $cookie_pasw, $encoder_Salt));
}    

function ValidateChKey($key, $cookie_pasw, $encoder_Salt){
	if ($key == '' || strlen($key)<8) return 0;
	$d = @encode(decrypt($key), $cookie_pasw, $encoder_Salt);
	if ($d == '' || strlen($d)<8) return 1;
	$str = explode('|', $d);
	if (count($str)!=5) return 2;
	if (intval($str[0]) != $str[0]) return 3;
	//if ($str[0] < 1 || $str[0] > 4) return 4;
	return $str[1];
}

function MakeUpdateAnswer($errorcode, $text, $ttt){
	$answer = array(
		'errorcode' => $errorcode,
		'text' => $text,
		'log' => $ttt
	);
	echo serialize($answer);
	die();
}

function GetAccountTemplateData($row)
{
	$chlink = $_POST['link'];
	$cookie_pasw = $_POST['cookie_pasw'];
	$encoder_Salt = $_POST['encoder_Salt'];
	$rmlink = $chlink.GetHash($_POST['servid'], $row['name'], $row['passwd'], $row['email'], $cookie_pasw, $encoder_Salt);
	$dislink = $chlink.GetHash($_POST['servid'], $row['name'], $row['passwd'], $row['email'], $cookie_pasw, $encoder_Salt, 1);
	$result = array(
		'login' => $row['name'],
		'question' => $row['Prompt'],
		'answer' => $row['answer'],
		'reset_passw_link' => $rmlink,
		'reset_ip_link' => $dislink
	);
	return $result;
}

function NullPass(){
	global $db;
	$answ = array();
	if (!isset($_POST['ip']) || !isset($_POST['cookie_pasw'])|| !isset($_POST['encoder_Salt'])) MakeAnswer($answ, 1000, 'error');
	CheckBrut($answ);
	$ip = $db->real_escape_string($_POST['ip']);
	$db->query("SET NAMES utf8");
	if (!isset($_POST['key'])) {
		CheckAntibrutIP();
		MakeAnswer($answ, 1000, 'wrongkey');
	}
	$cookie_pasw = $_POST['cookie_pasw'];
	$encoder_Salt = $_POST['encoder_Salt'];
	$key = $_POST['key'];
	$login = ValidateChKey($key, $cookie_pasw, $encoder_Salt);
	if (!$login) {
		CheckAntibrutIP();
		MakeAnswer($answ, 1000, 'wrongkey');
	}	
	$sql = "SELECT `ID`, `name`, `passwd`, `email`, `Prompt`, `answer` FROM `users` WHERE `name`='".$db->real_escape_string($login)."';";
	$res = $db->query($sql);
	if ($res==false) MakeAnswer($answ, 1000, "errorbase");	
	if ($res->num_rows) {
		$row = mysqli_fetch_assoc($res);
		$newkey = GetHash($_POST['servid'], $row['name'], $row['passwd'], $row['email'], $cookie_pasw, $encoder_Salt);		
		if ($key != $newkey) MakeAnswer($answ, 1000, 'wrongkey');
		$newpassw = $_POST['newpassw'];
		$newpasswdecoded = $_POST['newpasswdecoded'];		
		$db->query('set names latin1');		
		$query="UPDATE `users` SET `passwd2`=`passwd` WHERE `name`='$login'";
		$rresult = $db->query($query);
		if (!$rresult) MakeAnswer($answ, 1000, 'errorbase');
		$query = sprintf("UPDATE `users` SET `passwd`='%s' WHERE `name`='%s'", $db->real_escape_string($newpassw), $db->real_escape_string($login));
		$rresult = $db->query($query);
		if (!$rresult) MakeAnswer($answ, 1000, 'errorbase');
		$db->query("INSERT INTO `changepass` (`name`,`ip`,`data`,`type`) VALUES ('".$login."','".$_POST['ip']."',now(),1)");
		$answ = array(
			'to' => $row['email'],
			'login' => $login,
			'question' => $row['Prompt'],
			'answer' => $row['answer']
			
		);		
		MakeAnswer($answ, 0, 'ok');
	} else {
		CheckAntibrutIP();
		MakeAnswer($answ, 1000, 'wrongkey');
	}
	CheckAntibrutIP();
	MakeAnswer($answ, 1000, 'no');
}

function DisableIPCheck(){
	global $db;
	$answ = array();
	if (!isset($_POST['ip']) || !isset($_POST['cookie_pasw'])|| !isset($_POST['encoder_Salt'])) MakeAnswer($answ, 1000, 'error');
	CheckBrut($answ);
	$ip = $db->real_escape_string($_POST['ip']);
	$db->query("SET NAMES utf8");
	if (!isset($_POST['key'])) {
		CheckAntibrutIP();
		MakeAnswer($answ, 1000, 'wrongkey');
	}
	$cookie_pasw = $_POST['cookie_pasw'];
	$encoder_Salt = $_POST['encoder_Salt'];
	$key = $_POST['key'];
	$login = ValidateChKey($key, $cookie_pasw, $encoder_Salt);
	if (!$login) {
		CheckAntibrutIP();
		MakeAnswer($answ, 1000, 'wrongkey');
	}	
	$sql = "SELECT `ipdata`, `email` FROM `users` WHERE `name`='".$db->real_escape_string($login)."';";
	$res = $db->query($sql);
	if (!$res || $res->num_rows < 1) MakeAnswer($answ, 1000, "errorbase");	
	$row = mysqli_fetch_assoc($res);
	$ipdata = @unserialize($row['ipdata']);	
	if (!is_array($ipdata)) {
		$ipdata = array();
		$ipdata[0] = false;
		$ipdata[1] = array();
		$ipdata[2] = false;
	}
	if (!isset($ipdata[2])) $ipdata[2] = false;
	$ipdata[0] = false;
	$sql = sprintf("UPDATE `users` SET `ipdata`='%s' WHERE `name`='%s'", $db->real_escape_string(serialize($ipdata)), $db->real_escape_string($login));
	$res = $db->query($sql);
	if (!$res) MakeAnswer($answ, 1000, 'errorbase');
	$answ = array(
		'to' => $row['email'],
		'login' => $login
		
	);	
	MakeAnswer($answ, 0, 'ipcheckdisabled');
}

function ForgetPass(){
	global $db;
	$answ = array();
	if (!isset($_POST['ip']) || !isset($_POST['cookie_pasw'])|| !isset($_POST['encoder_Salt'])) MakeAnswer($answ, 1000, 'error');
	CheckBrut($answ);
	$ip = $db->real_escape_string($_POST['ip']);
	$db->query("SET NAMES utf8");
	if (!isset($_POST['login'])) {
		CheckAntibrutIP();
		MakeAnswer($answ, 1000, 'error1');
	}
	$login = $db->real_escape_string($_POST['login']);
	$sql = "SELECT `ID`, `name`, `passwd`, `email`, `Prompt`, `answer` FROM `users` WHERE `name`='".$login."';";
	$res = $db->query($sql);
	if ($res==false) MakeAnswer($answ, 1000, "errorbase");
	if ($res->num_rows) {
		$row = mysqli_fetch_assoc($res);
		if (!checker($row['email'])) MakeAnswer($answ, 1000, 'bademail');
		$account_data = GetAccountTemplateData($row);
		$answ = array(
			'to' => $row['email'],
			'accounts' => array($account_data)
		);
		MakeAnswer($answ, 0, 'ok');
	} else {
		CheckAntibrutIP();
		MakeAnswer($answ, 1000, 'wrongacc');
	}
	CheckAntibrutIP();
	MakeAnswer($answ, 1000, 'no');
}

function ForgetLogin(){
	global $db;
	$answ = array();
	if (!isset($_POST['ip']) || !isset($_POST['cookie_pasw'])|| !isset($_POST['encoder_Salt'])) MakeAnswer($answ, 1000, 'error');
	CheckBrut($answ);
	$ip = $db->real_escape_string($_POST['ip']);
	$db->query("SET NAMES utf8");
	if (!isset($_POST['email']) || !isset($_POST['ip'])) MakeAnswer($answ, 1000, 'error');
	$email = $db->real_escape_string($_POST['email']);
	if (!checker($email)) {
		CheckAntibrutIP();
		MakeAnswer($answ, 1000, 'bademail');
	}
	$chlink = $_POST['link'];
	$sql = "SELECT `ID`, `name`, `passwd`, `email`, `Prompt`, `answer` FROM `users` WHERE `email`='".$email."';";
	$res = $db->query($sql);
	if ($res==false) MakeAnswer($answ, 1000, "errorbase");
	if ($res->num_rows){
		$accounts_data = array();
		while ($row = mysqli_fetch_assoc($res)){									
			$account_data = GetAccountTemplateData($row);
			array_push($accounts_data, $account_data);
		}
		
		$answ = array(
			'to' => $_POST['email'],
			'accounts' => $accounts_data
		);		
		MakeAnswer($answ, 0, 'ok');
	} else {
		CheckAntibrutIP();
		MakeAnswer($answ, 1000, 'wrongaccemail');
	}
	CheckAntibrutIP();	
	MakeAnswer($answ, 1000, 'no');
}

function OnlineStat(){
	$l = GetFullRoleListOnline();
	$online_acc = 0; $online_pers = 0; $online_world = 0; $online_instance = 0;
	if (count($l)>0) {
		foreach ($l as $i => $val){
			if ($val['userid'] > 0) $online_acc++;
			if ($val['roleid'] > 0) $online_pers++;
			if ($val['gsid'] == 1) $online_world++; else $online_instance++;
		}
	}
	$data = array();
	$data['online_acc'] = $online_acc;
	$data['online_pers'] = $online_pers;
	$data['online_world'] = $online_world;
	$data['online_instance'] = $online_instance;
	echo serialize($data);
	die();
}

function GetTime2($t,$col='#00aa00'){
	$pinkdays = floor($t/86400);
	$pinkhours = floor(($t-$pinkdays*86400)/3600);
	$pinkmin = floor(($t-$pinkdays*86400-$pinkhours*3600)/60);
	$pinksec = round($t-$pinkdays*86400-$pinkhours*3600-$pinkmin*60,0);
	if ($pinkhours<10) $pinkhours='0'.$pinkhours;
	if ($pinkmin<10) $pinkmin='0'.$pinkmin;
	if ($pinksec<10) $pinksec='0'.$pinksec;
	$timeused = '';
	if ($pinkdays>0) $timeused='<font color="#'.$col.'"><b>'.$pinkdays.'</b></font> дн ';
	$timeused .= $pinkhours.':'.$pinkmin.':'.$pinksec;
	return $timeused;
}

function GetTime1($t){
	$pinkhours = floor(($t)/3600);
	$pinkmin = floor(($t-$pinkhours*3600)/60);
	$pinksec = round($t-$pinkhours*3600-$pinkmin*60,0);
	if ($pinkhours<10) $pinkhours='0'.$pinkhours;
	if ($pinkmin<10) $pinkmin='0'.$pinkmin;
	if ($pinksec<10) $pinksec='0'.$pinksec;
	$timeused = $pinkhours.':'.$pinkmin.':'.$pinksec;
	return $timeused;
}

function GetPersInfo($a, $servid, $touserinfo = false){
	global $db, $gender, $factrole;
	$klanname = 'Нет';
	$factrang = '';
	$klanicon = '';
	$reborn = '';
	if ($a['factionid']!=0) {
		$res = $db->query('SELECT `name` FROM `klan` WHERE `id`='.$a['factionid']);
		if ($res->num_rows>0){
			$row = mysqli_fetch_assoc($res);
			$klanname = htmlspecialchars($row['name']);
		} else 
		{
			$fact = GetFactionInfo($a['factionid']);
			$klanname = $fact['value']['name'];
		}
		$klanicon = "<img src='klan/geticon.php?servid=".$servid."&klan=".$a['factionid']."' align='absmiddle'>";
		$factrang = $factrole[$a['factionrole']];				
	}
	if ($touserinfo) $dd = "<p>RoleID: <b><font color='#ffff00'>".$a['roleid']."</font></b></p>\n";
	if ($a['reborn']) $reborn = " <span class='label label-important'>РБ</span>";
	$d = sprintf("<font color='#ffffff'>
	<p>Уровень: <b><font color='#ffff00'>%d</font></b>%s</p>
	<p>Класс: <b><font color='#ffff00'>%s</font></b></p>
	<p>Пол: <b><font color='#ffff00'>%s</font></b></p>
	<p>Клан: %s <span class='label label-info'>%s</span> %s</p>
	<p>HP: <b><font color='#ffff00'>%s</font></b></p>
	<p>MP: <b><font color='#ffff00'>%s</font></b></p>
	<p>Онлайн: <span class='label label-info'>%s</span></p>
	</font>", $a['rolelevel'], $reborn, GetOccupationName($a['roleprof']), $gender[$a['rolegender']], $klanicon, $klanname, $factrang, $a['hp'], $a['mp'], str_replace('"', "'", GetTime2($a['timeused'], 'ffff00')));	
	if ($touserinfo) 
	{
		$t = sprintf('<div class="userinfo_table"><p>RoleID: <b><font color="#50ff50">%d</font></b> <i class="icon icon-color icon-edit role_edit" data-rel="tooltip" data-original-title="Редактировать персонажа" onclick="EditRole(%d, \'%s\')"></i> <i class="icon icon-color icon-cross role_ban" data-rel="tooltip" data-original-title="Забанить персонажа" onclick="BanRole(%d, \'%s\')"></i></p><p>Ник: <b><font color="#ffff00">%s</font></b>%s</div>', $a['roleid'], $a['roleid'], htmlspecialchars($a['rolename']), $a['roleid'], htmlspecialchars($a['rolename']), htmlspecialchars($a['rolename']), $d);
	} else
	$t = sprintf('title="%s" data-content="%s" data-rel="popover"', htmlspecialchars($a['rolename']), $d);
	return $t;
}

function TopHeader($desc){
	$res = '<table class="table table-bordered table-striped bootstrap-datatable datatable">
	<thead>
	<tr>';
	if (is_array($desc))
	foreach ($desc as $i => $val) {
		$res.=sprintf("<th>%s</th>",htmlspecialchars($val));
	}
	$res .= '</tr>
	</thead>
	';
	return $res;
}

function RaceCount($num){
	global $racetop, $db;
	$res = $db->query("SELECT count(id) FROM `top` WHERE `roleprof`=".$num." AND `rolegender`=0");
	if ($db->errno > 0) die($db->error);
	$row = mysqli_fetch_array($res);
	$cnt1 = $row[0];
	$res = $db->query("SELECT count(id) FROM `top` WHERE `roleprof`=".$num." AND `rolegender`=1");
	if ($db->errno > 0) die($db->error);
	$row = mysqli_fetch_array($res);
	$cnt2 = $row[0];
	$racetop[GetOccupationName($num)] = ($cnt1+$cnt2).'|'.$cnt1.'|'.$cnt2.'|'.$num;
}

function RefreshTop(){
	global $db, $ElementsVer, $racetop, $gender, $occ;
	FillKlanData();
	$servid = $_POST['servid'];	
	$top = array();
	$db->query("set names utf8");

	// Топ Уровень
	$topdata = '';
	$res = $db->query("SELECT * FROM `top` where (`userid` not in (select distinct `userid` from `auth`)) ORDER BY `rolelevel` DESC, `reborn` DESC, `timeused` DESC, roleid ASC LIMIT 0,100");
	if ($db->errno > 0) die($db->error);
	if ($res->num_rows > 0) {
		$topdata = TopHeader(array('№', 'Ник', 'Класс', 'Уровень'));
		$n = 1;
		while ($row = mysqli_fetch_assoc($res)){
			$reborn = '';
			if ($row['reborn']) $reborn = " <span class='label label-important'>РБ</span>";
			$topdata.=sprintf('
			<tr>	
				<td>%s</td>			
				<td %s>%s</td>
				<td>%s</td>			
				<td><span class="label label-success">%s</span>%s</td>
			</tr>',$n, GetPersInfo($row,$servid), htmlspecialchars($row['rolename']), GetOccupationName($row['roleprof']), $row['rolelevel'], $reborn);
			$n++;
		}
		$topdata.="</table>\n";
	}
	$top['level'] = $topdata;

	// Топ Онлайн
	$topdata = '';
	$res = $db->query("SELECT * FROM `top` where (`userid` not in (select distinct `userid` from `auth`)) ORDER BY `timeused` DESC, `rolelevel` DESC, `reborn` DESC, roleid ASC LIMIT 0,100");
	if ($db->errno > 0) die($db->error);
	if ($res->num_rows > 0) {
		$topdata = TopHeader(array('№', 'Ник', 'Класс', 'Онлайн'));
		$n = 1;
		while ($row = mysqli_fetch_assoc($res)){
			$pink = GetTime2($row['timeused'], 'ffff00');
			$topdata.=sprintf('
			<tr>	
				<td>%s</td>			
				<td %s>%s</td>	
				<td>%s</td>		
				<td><span class="label label-info">%s</span></td>
			</tr>',$n, GetPersInfo($row,$servid), htmlspecialchars($row['rolename']), GetOccupationName($row['roleprof']), $pink);
			$n++;
		}
		$topdata.="</table>\n";
	}
	$top['online'] = $topdata;

	// Топ ПК
	$topdata = '';
	$res = $db->query("SELECT * FROM `top` where (`userid` not in (select distinct `userid` from `auth`)) ORDER BY `pkvalue` DESC, `roleid` ASC LIMIT 0,100");
	if ($db->errno > 0) die($db->error);
	if ($res->num_rows > 0) {
		$topdata = TopHeader(array('№', 'Ник', 'Класс', 'Часов ПК'));
		$n = 1;
		while ($row = mysqli_fetch_assoc($res)){
			$pink = GetTime1($row['pkvalue']);
			$topdata.=sprintf('
			<tr>	
				<td>%s</td>			
				<td %s>%s</td>			
				<td>'.GetOccupationName($row['roleprof']).'</td>			
				<td><span class="label label-important">%s</span></td>
			</tr>',$n, GetPersInfo($row,$servid), htmlspecialchars($row['rolename']), $pink);
			$n++;
		}
		$topdata.="</table>\n";
	}
	$top['pk'] = $topdata;

	// Топ Кланы
	$topdata = '';
	$res = $db->query("SELECT * FROM `klan` ORDER BY `terr1` DESC, `terr2` DESC, `terr3` DESC, `members` DESC LIMIT 0,100");
	if ($db->errno > 0) die($db->error);
	if ($res->num_rows > 0) {
		$topdata = TopHeader(array('№', 'Название', 'Мастер', 'Игроков', '1ур', '2ур', '3ур'));
		$n = 1;
		while ($row = mysqli_fetch_assoc($res)){			
			$res1 = $db->query('SELECT * FROM `top` WHERE `roleid`='.$row['masterid']);
			if ($res1->num_rows > 0){
				$row1 = mysqli_fetch_assoc($res1);
				$roleinfo = GetPersInfo($row1, $servid);
			} else $roleinfo = '';
			$topdata.=sprintf('
			<tr>	
				<td>%s</td>		
				<td>%s</td>
				<td %s>%s</td>
				<td>%s</td>
				<td>%s</td>
				<td>%s</td>
				<td>%s</td>
			</tr>',
			$n,'<img src="klan/geticon.php?servid='.$servid.'&klan='.$row['id'].'" class="imcnt"> '.htmlspecialchars($row['name']), $roleinfo, htmlspecialchars($row['mastername']), $row['members'], $row['terr1'], $row['terr2'], $row['terr3']);
			$n++;
		}
		$topdata.="</table>\n";
	}
	$top['klan'] = $topdata;

	// Топ классы
	$racetop = array();
	foreach ($occ as $i => $val)
	{
		RaceCount($i);
	}	
	arsort($racetop);
	$topdata = '<table class="table table-bordered table-striped"></tr>';
	$a = 1;
	foreach ($racetop as $i=> $val)	{
		$data = explode('|', $val);
		$topdata.=sprintf('
		<td><i class="class_icon class_%s"></i> <span class="label label-info" data-rel="tooltip" data-original-title="Всего">%d</span> <i class="class_icon class_%s"></i><br><span class="label label-success" data-rel="tooltip" data-original-title="Мужчин">%d</span><span class="label label-inverse">%s</span><span class="label label-important" data-rel="tooltip" data-original-title="Женщин">%d</span></td>
', $data[3], $data[0], $data[3], $data[1], $i, $data[2]);
		$a++;
		if ($a >= 14)
		{
			$topdata.='</tr><tr>';
			$a = 1;
		}
	}
	$topdata.="</tr></table>\n";
	$top['class'] = $topdata;
	echo lll_____('top', serialize($top), 60);
	die();
}

function AddFiltering(&$sWhere, $num, $name, $like = false, $val='', $costval = false){
	global $db;	
	if ( isset($_POST['bSearchable_'.$num]) && $_POST['bSearchable_'.$num] == "true" && $_POST['sSearch_'.$num] != '' ){
		if ( $sWhere == "" ){
			$sWhere = "WHERE ";
		} else {
			$sWhere .= " AND ";
		}
		if ($val == '') $value = $_POST['sSearch_'.$num]; else $value = $val;
		if ($costval) {
			$sWhere .= sprintf("(`gold%s`='%s' or `silver%s`='%s')", $name, $db->real_escape_string($value), $name, $db->real_escape_string($value));
		} else {
			if ($like) $sWhere .= sprintf("`%s` LIKE '%%%s%%'", $name, $db->real_escape_string($value)); else
			$sWhere .= sprintf("`%s`='%s'", $name, $db->real_escape_string($value));
		}
	}
}

function ParseCost($cost, &$gold, &$silver){
	$c = explode('|', $cost);
	$gold = (int)$c[0];
	if (count($c) > 1) $silver = (int)$c[1]; else $silver = 0;
}

function GetShowCost($cost, $shownull = false, $dark = false, $br = false){
	$res = '';
	ParseCost($cost, $gold, $silver);
	if (!$dark) {
		$dg = '';
		$ds = '';
	} else {
		$dg = ' gold_dark';
		$ds = ' gold_silver';
	}
	if ($gold != 0 || $shownull) $res = ' <span class="gold'.$dg.'">'.$gold.'</span>';
	if ($silver != 0) $res .= ' <span class="silver'.$ds.'">'.$silver.'</span>';
	if ($br && $res != '') $res = '<br>'.$res;
	return $res;
}

function GetLKLogs(){
	global $db, $gpoint, $spoint;
	if (!isset($_POST['sEcho'])) die();
	$db->query('set names utf8');
	$aColumns = array( 'id', 'userid', 'name', 'userid', 'data', 'ip', 'gold', 'gold_rest', 'desc' );	
	$sIndexColumn = "id";	
	$sTable = "lklogs";	
	$sLimit = "";
	if ( isset( $_POST['iDisplayStart'] ) && $_POST['iDisplayLength'] != '-1' )
	{
		$sLimit = "LIMIT ".intval( $_POST['iDisplayStart'] ).", ".
			intval( $_POST['iDisplayLength'] );
	}	
	$sOrder = "ORDER by `id` DESC";
	if ( isset( $_POST['iSortCol_0'] ) )
	{
		$sOrder = "ORDER BY  ";
		for ( $i=0 ; $i<intval( $_POST['iSortingCols'] ) ; $i++ )
		{
			if ( $_POST[ 'bSortable_'.intval($_POST['iSortCol_'.$i]) ] == "true" )
			{
				$sOrder .= "`".$aColumns[ intval( $_POST['iSortCol_'.$i] ) ]."` ".
					($_POST['sSortDir_'.$i]==='asc' ? 'asc' : 'desc') .", ";
			}
		}
		
		$sOrder = substr_replace( $sOrder, "", -2 );
		if ( $sOrder == "ORDER BY" )
		{
			$sOrder = "";
		}
	}	
	$sWhere = '';
	AddFiltering($sWhere, 0, 'id');
	AddFiltering($sWhere, 1, 'userid');
	AddFiltering($sWhere, 2, 'name');
	AddFiltering($sWhere, 3, 'userid', false, intval(($_POST['sSearch_3']*16)+16));
	AddFiltering($sWhere, 4, 'data', true);
	AddFiltering($sWhere, 5, 'ip', true);
	AddFiltering($sWhere, 6, '', false, '', true);
	AddFiltering($sWhere, 7, '_rest', false, '', true);
	AddFiltering($sWhere, 8, 'desc', true);

	if (!$sWhere) $sWhere = 'WHERE `userid`>0'; else $sWhere .= ' AND `userid`>0';
	$sQuery = "SELECT `$sTable`.*, `users`.`name` FROM $sTable LEFT JOIN `users` ON `userid`=`users`.`ID` $sWhere $sOrder $sLimit";
	$rResult = $db->query( $sQuery) or die($db->error);
	
	$sQuery = "SELECT count(`$sTable`.`".$sIndexColumn."`) FROM $sTable LEFT JOIN `users` ON `userid`=`users`.`ID` $sWhere";
	$rResultFilterTotal = $db->query( $sQuery) or die($db->error);
	$aResultFilterTotal = mysqli_fetch_array($rResultFilterTotal);
	$iFilteredTotal = $aResultFilterTotal[0];
	
	$sQuery = "SELECT COUNT(`".$sIndexColumn."`) FROM $sTable";
	$rResultTotal = $db->query( $sQuery) or die($db->error);
	$aResultTotal = mysqli_fetch_array($rResultTotal);
	$iTotal = $aResultTotal[0];
	
	$output = array(
		"sEcho" => intval($_POST['sEcho']),
		"iTotalRecords" => $iTotal,
		"iTotalDisplayRecords" => $iFilteredTotal,
		"aaData" => array()
	);
	
	while ( $aRow = mysqli_fetch_array( $rResult ) )
	{		
		$color = '';
		if ($aRow['gold'] > 0 || $aRow['silver'] > 0) $color = 'minus';
		if ($aRow['gold'] < 0 || $aRow['silver'] < 0) $color = 'plus';
		$lkid = intval(($aRow['userid']/16)-1);
		$aRow['desc'] = preg_replace("/№\s+(\d+)\b/u", '<a href="#" onclick="fnlkid(${1})">${0}</a>', $aRow['desc']);
		$row = array(
			$aRow['id'],
			sprintf('<a href="#" onclick="fnuser(%d)">%d</a>',$aRow['userid'],$aRow['userid']),
			'<img src="img/details_open.png"> '.sprintf('<a href="#" onclick="fnlogin(\'%s\')">%s</a>',$aRow['name'],$aRow['name']),
			sprintf('<a href="#" onclick="fnlkid(%d)">%d</a>',$lkid,$lkid),
			$aRow['data'],
			sprintf('<a href="#" onclick="fnip(\'%s\')">%s</a>',$aRow['ip'],$aRow['ip']),
			GetShowCost($aRow['gold'].'|'.$aRow['silver'], true, true),
			GetShowCost($aRow['gold_rest'].'|'.$aRow['silver_rest'], true, true),
			$aRow['desc'],
			$color,
			$aRow['userid']
		);
		$output['aaData'][] = $row;
	}	
	echo json_encode( $output );
	die();
}

function GetAccounts(){
	global $db, $gpoint, $spoint;
	if (!isset($_POST['sEcho'])) die();
	$db->query('set names utf8');
	$aColumns = array( 'ID', 'name', 'ID', 'vkid', 'steamid', 'truename', 'email', 'idnumber', 'creatime', 'lkgold', 'lksilver', 'referal', 'ref_status', 'ref_bonus', 'bonus_data' );	
	$sIndexColumn = "ID";	
	$sTable = "users";	
	$sLimit = "";
	if ( isset( $_POST['iDisplayStart'] ) && $_POST['iDisplayLength'] != '-1' )
	{
		$sLimit = "LIMIT ".intval( $_POST['iDisplayStart'] ).", ".
			intval( $_POST['iDisplayLength'] );
	}	
	$sOrder = "ORDER by `id` DESC";
	if ( isset( $_POST['iSortCol_0'] ) )
	{
		$sOrder = "ORDER BY  ";
		for ( $i=0 ; $i<intval( $_POST['iSortingCols'] ) ; $i++ )
		{
			if ( $_POST[ 'bSortable_'.intval($_POST['iSortCol_'.$i]) ] == "true" )
			{
				$sOrder .= "`".$aColumns[ intval( $_POST['iSortCol_'.$i] ) ]."` ".
					($_POST['sSortDir_'.$i]==='asc' ? 'asc' : 'desc') .", ";
			}
		}
		
		$sOrder = substr_replace( $sOrder, "", -2 );
		if ( $sOrder == "ORDER BY" )
		{
			$sOrder = "";
		}
	}	
	$sWhere = '';
	AddFiltering($sWhere, 0, 'ID');
	AddFiltering($sWhere, 1, 'name');
	AddFiltering($sWhere, 2, 'ID', false, intval(($_POST['sSearch_3']*16)+16));
	AddFiltering($sWhere, 3, 'vkid');
	AddFiltering($sWhere, 4, 'steamid');
	AddFiltering($sWhere, 5, 'truename', true);	
	AddFiltering($sWhere, 6, 'email', true);
	AddFiltering($sWhere, 7, 'idnumber', true);	
	AddFiltering($sWhere, 8, 'creatime', true);
	AddFiltering($sWhere, 9, 'lkgold');
	AddFiltering($sWhere, 10, 'lksilver');
	AddFiltering($sWhere, 11, 'referal');
	AddFiltering($sWhere, 12, 'ref_status');
	AddFiltering($sWhere, 13, 'ref_bonus');
	AddFiltering($sWhere, 14, 'bonus_data');

	$sQuery = "SELECT `$sTable`.* FROM $sTable $sWhere $sOrder $sLimit";
	$rResult = $db->query( $sQuery) or die($db->error);
	
	$sQuery = "SELECT count(`$sTable`.`".$sIndexColumn."`) FROM $sTable $sWhere";
	$rResultFilterTotal = $db->query( $sQuery) or die($db->error);
	$aResultFilterTotal = mysqli_fetch_array($rResultFilterTotal);
	$iFilteredTotal = $aResultFilterTotal[0];
	
	$sQuery = "SELECT COUNT(`".$sIndexColumn."`) FROM $sTable";
	$rResultTotal = $db->query( $sQuery) or die($db->error);
	$aResultTotal = mysqli_fetch_array($rResultTotal);
	$iTotal = $aResultTotal[0];
	
	$output = array(
		"sEcho" => intval($_POST['sEcho']),
		"iTotalRecords" => $iTotal,
		"iTotalDisplayRecords" => $iFilteredTotal,
		"aaData" => array()
	);
	
	while ( $aRow = mysqli_fetch_array( $rResult ) )
	{		
		$color = ''; $act_txt = '';
		if ($aRow['passwd'] == 'confirm') {
			$color = 'minus';
			$act_txt = sprintf('<br><a href="#" class="btn btn-mini btn-inverse" onclick="act_acc(%d)">Активировать</a>', $aRow['ID']);
		}
		$lkid = intval(($aRow['ID']/16)-1);
		if (!$aRow['referal']) $referal = '<i class="icon icon-color icon-close"></i>'; else $referal = sprintf('<a href="#" onclick="fnuser(%d)">%d</a>',$aRow['referal'],$aRow['referal']);
		if (!$aRow['ref_status']) $ref_status = '<i class="icon icon-color icon-close"></i>'; else $ref_status = '<i class="icon icon-color icon-check"></i>';
		$bonus_text = '';
		if ($aRow['bonus_data']=='') $bonus_data = array(); else $bonus_data = @unserialize($aRow['bonus_data']);
		if (!is_array($bonus_data)) $bonus_data = array();
		if (isset($bonus_data['reg_gold'])) $bonus_text.='Рег.голд получен<br>';
		if (!isset($bonus_data['promo'])) $bonus_data['promo'] = array();
		if (!isset($bonus_data['promo_group'])) $bonus_data['promo_group'] = array();
		if (count($bonus_data['promo'])) {
			$bonus_text .= '<b>Промо-коды:</b> '.implode(', ', $bonus_data['promo']).'<br>';
		}
		if (count($bonus_data['promo_group'])) {
			$bonus_text .= '<b>Группы-Промо:</b> '.implode(', ', $bonus_data['promo_group']).'<br>';
		}
		if ($aRow['vkid']) $vk_txt = sprintf('<div style="float: left"><img src="%s"  border="0"></div><font class="accmanage_font"><a href="https://vk.com/id%s" target="_blank">%s</a><br>%s</font>', $aRow['vkphoto'], $aRow['vkid'], $aRow['vkid'], $aRow['vkname']); else $vk_txt = '';
		if ($aRow['steamid']) $steam_txt = sprintf('<div style="float: left"><img src="%s" border="0"></div><font class="accmanage_font"><a href="http://steamcommunity.com/profiles/%s/" target="_blank">%s</a><br>%s</font>', $aRow['steamphoto'], $aRow['steamid'], $aRow['steamid'], $aRow['steamname']); else $steam_txt = '';
		$row = array(			
			sprintf('<a href="#" onclick="fnuser(%d)">%d</a>',$aRow['ID'],$aRow['ID']),
			'<img src="img/details_open.png"> '.$aRow['name'].$act_txt,
			$lkid,
			$vk_txt,
			$steam_txt,
			$aRow['truename'],
			sprintf('<a href="#" onclick="fnemail(\'%s\')">%s</a>',$aRow['email'],$aRow['email']),
			sprintf('<a href="#" onclick="fnip(\'%s\')">%s</a>',$aRow['idnumber'],$aRow['idnumber']),
			$aRow['creatime'],
			'<span class="gold gold_dark">'.$aRow['lkgold'].'</span>',
			'<span class="silver gold_silver">'.$aRow['lksilver'].'</span>',
			$referal,
			$ref_status,
			$aRow['ref_bonus'],
			$bonus_text,
			$color,
			$aRow['ID']
		);
		$output['aaData'][] = $row;
	}	
	echo json_encode( $output );
	die();
}

function GetTOPLogs($qtop = false){
	global $db, $gpoint, $spoint;
	if (!isset($_POST['sEcho'])) die();
	$db->query('set names utf8');
	$aColumns = array( 'id', 'vote_id', 'data', 'ip', 'name', 'login', 'userid', 'userid', 'vote_type', 'points', 'send_item', 'status' );	
	$sIndexColumn = "id";	
	if ($qtop) $sTable = 'qtop_data'; else $sTable = "mmotop_data";	
	$sLimit = "";
	if ( isset( $_POST['iDisplayStart'] ) && $_POST['iDisplayLength'] != '-1' )
	{
		$sLimit = "LIMIT ".intval( $_POST['iDisplayStart'] ).", ".
			intval( $_POST['iDisplayLength'] );
	}	
	$sOrder = "ORDER by `id` DESC";
	if ( isset( $_POST['iSortCol_0'] ) )
	{
		$sOrder = "ORDER BY  ";
		for ( $i=0 ; $i<intval( $_POST['iSortingCols'] ) ; $i++ )
		{
			if ( $_POST[ 'bSortable_'.intval($_POST['iSortCol_'.$i]) ] == "true" )
			{
				$sOrder .= "`".$aColumns[ intval( $_POST['iSortCol_'.$i] ) ]."` ".
					($_POST['sSortDir_'.$i]==='asc' ? 'asc' : 'desc') .", ";
			}
		}
		
		$sOrder = substr_replace( $sOrder, "", -2 );
		if ( $sOrder == "ORDER BY" )
		{
			$sOrder = "";
		}
	}	
	$sWhere = '';
	AddFiltering($sWhere, 0, 'id');
	AddFiltering($sWhere, 1, 'vote_id');
	AddFiltering($sWhere, 2, 'data');
	AddFiltering($sWhere, 3, 'ip', true);
	AddFiltering($sWhere, 4, 'name');
	AddFiltering($sWhere, 5, 'login');
	AddFiltering($sWhere, 6, 'userid');	
	AddFiltering($sWhere, 7, 'userid', false, intval(($_POST['sSearch_6']*16)+16));	
	AddFiltering($sWhere, 8, 'votetype');
	AddFiltering($sWhere, 9, 'points');
	AddFiltering($sWhere, 10, 'send_item');
	AddFiltering($sWhere, 11, 'status');
	$sQuery = "SELECT `$sTable`.* FROM $sTable $sWhere $sOrder $sLimit";
	$rResult = $db->query( $sQuery) or die($db->error);
	
	$sQuery = "SELECT count(`$sTable`.`".$sIndexColumn."`) FROM $sTable $sWhere";
	$rResultFilterTotal = $db->query( $sQuery) or die($db->error);
	$aResultFilterTotal = mysqli_fetch_array($rResultFilterTotal);
	$iFilteredTotal = $aResultFilterTotal[0];
	
	$sQuery = "SELECT COUNT(`".$sIndexColumn."`) FROM $sTable";
	$rResultTotal = $db->query( $sQuery) or die($db->error);
	$aResultTotal = mysqli_fetch_array($rResultTotal);
	$iTotal = $aResultTotal[0];
	
	$output = array(
		"sEcho" => intval($_POST['sEcho']),
		"iTotalRecords" => $iTotal,
		"iTotalDisplayRecords" => $iFilteredTotal,
		"aaData" => array()
	);
	
	while ( $aRow = mysqli_fetch_array( $rResult ) )
	{		
		$userid = ($aRow['userid'] == 0)?'':sprintf('<a href="#" onclick="fnuser(%d)">%d</a>',$aRow['userid'],$aRow['userid']);
		$lkid = intval(($aRow['userid']/16)-1);
		$lktext = ($lkid == -1)?'':sprintf('<a href="#" onclick="fnlkid(%d)">%d</a>',$lkid,$lkid);
		if ($aRow['login'] == '') $plus_btn = ''; else $plus_btn = '<img src="img/details_open.png"> ';
		$color = 'plus';
		if ($aRow['status'] != 1 && $aRow['status'] != 7) $color = 'minus';
		$send_item = '<i class="icon icon-color icon-check"></i>';
		if ($aRow['send_item'] == 0) $send_item = '<i class="icon icon-color icon-close"></i>';
		$row = array(
			$aRow['id'],
			$aRow['vote_id'],
			$aRow['data'],
			sprintf('<a href="#" onclick="fnip(\'%s\')">%s</a>',$aRow['ip'],$aRow['ip']),
			htmlspecialchars($aRow['name']),
			$plus_btn.sprintf('<a href="#" onclick="fnlogin(\'%s\')">%s</a>',$aRow['login'],$aRow['login']),
			$userid,			
			$lktext,
			$aRow['vote_type'],			
			GetShowCost($aRow['points'], true, true),
			$send_item,
			$aRow['status'],
			$color,
			$aRow['userid']
		);
		$output['aaData'][] = $row;
	}	
	echo json_encode( $output );
	die();
}

function GetKlanArts(){
	global $db, $gpoint, $spoint;
	if (!isset($_POST['sEcho'])|| !isset($_POST['servid'])) die();
	$servid = $_POST['servid'];
	$db->query('set names utf8');
	$aColumns = array( 'id', 'klanid', 'itemid', 'count', 'maxcount', 'data', 'client_size', 'proctype', 'expire', 'costgold', 'remove_no_klan', 'buycount', 'klan_items`.`desc' );
	$sIndexColumn = "id";
	$sTable = "klan_items";	
	// Paging
	$sLimit = "";
	if ( isset( $_POST['iDisplayStart'] ) && $_POST['iDisplayLength'] != '-1' )
	{
		$sLimit = "LIMIT ".intval( $_POST['iDisplayStart'] ).", ".
			intval( $_POST['iDisplayLength'] );
	}	
	// Ordering
	$sOrder = "ORDER by `id` DESC";
	if ( isset( $_POST['iSortCol_0'] ) )
	{
		$sOrder = "ORDER BY  ";
		for ( $i=0 ; $i<intval( $_POST['iSortingCols'] ) ; $i++ )
		{
			if ( $_POST[ 'bSortable_'.intval($_POST['iSortCol_'.$i]) ] == "true" )
			{
				$sOrder .= "`".$aColumns[ intval( $_POST['iSortCol_'.$i] ) ]."` ".
					($_POST['sSortDir_'.$i]==='asc' ? 'asc' : 'desc') .", ";
			}
		}
		
		$sOrder = substr_replace( $sOrder, "", -2 );
		if ( $sOrder == "ORDER BY" )
		{
			$sOrder = "";
		}
	}	
	$sWhere = '';
	AddFiltering($sWhere, 0, 'id');
	AddFiltering($sWhere, 1, 'klanid');
	AddFiltering($sWhere, 2, 'itemid');
	AddFiltering($sWhere, 3, 'count');
	AddFiltering($sWhere, 4, 'maxcount');
	AddFiltering($sWhere, 5, 'data', true);
	AddFiltering($sWhere, 6, 'client_size');
	AddFiltering($sWhere, 7, 'proctype');
	AddFiltering($sWhere, 8, 'expire');
	AddFiltering($sWhere, 9, 'costgold');
	AddFiltering($sWhere, 10, 'remove_no_klan');
	AddFiltering($sWhere, 11, 'buycount');
	AddFiltering($sWhere, 12, 'klan_items`.`desc', true);

	$sQuery = "SELECT SQL_CALC_FOUND_ROWS `$sTable`.*, `klan`.`name`, `shop_names`.`name` as `itemname` FROM $sTable LEFT JOIN `klan` ON `klanid`=`klan`.`id` LEFT JOIN `shop_names` ON `itemid`=`shop_names`.`id` $sWhere $sOrder $sLimit";
	$rResult = $db->query( $sQuery) or die($db->error);
	
	/* Data set length after filtering */
	$sQuery = "
		SELECT FOUND_ROWS()
	";
	$rResultFilterTotal = $db->query( $sQuery) or die($db->error);
	$aResultFilterTotal = mysqli_fetch_array($rResultFilterTotal);
	$iFilteredTotal = $aResultFilterTotal[0];
	
	/* Total data set length */
	$sQuery = "
		SELECT COUNT(`".$sIndexColumn."`)
		FROM   $sTable
	";
	$rResultTotal = $db->query( $sQuery) or die($db->error);
	$aResultTotal = mysqli_fetch_array($rResultTotal);
	$iTotal = $aResultTotal[0];
	
	$output = array(
		"sEcho" => intval($_POST['sEcho']),
		"iTotalRecords" => $iTotal,
		"iTotalDisplayRecords" => $iFilteredTotal,
		"aaData" => array()
	);	
	while ( $aRow = mysqli_fetch_array( $rResult ) )
	{		
		$cost = GetShowCost($aRow['costgold'].'|'.$aRow['costsilver'], true, true, false);
		if ($aRow['cost_item_id'] == 0 || $aRow['cost_item_count'] == 0) $costitem = ''; else {
			$it1 = GetExtItem($aRow['cost_item_id']);
			$costitemname = $it1->name;			
			$costitem = '<br>'.$aRow['cost_item_count'].' <img src="getitemicon.php?i='.urlencode(base64_encode($it1->icon)).'" border="0" class="item_icon" style="width: 24px; margin: 0"> '.$costitemname.'<br><span class="label label-important">'.$aRow['cost_item_id'].'</span>';
		}
		if ($aRow['itemid'] == 0 || $aRow['count'] == 0) $item = '0'; else {
			$it = GetExtItem($aRow['itemid']);
			$item = '<img src="getitemicon.php?i='.urlencode(base64_encode($it->icon)).'" border="0" class="item_icon" style="width: 24px; margin: 0"> '.$aRow['itemname'].'<br><span class="label label-success">'.$aRow['itemid'].'</span>';
		}
		$row = array(
			$aRow['id'],
			'<b>'.$aRow['klanid'].'</b> <img src="klan/geticon.php?servid='.$servid.'&klan='.$aRow['klanid'].'" class="imcnt"> '.$aRow['name'],
			$item,
			$aRow['count'],
			$aRow['maxcount'],
			'<div style="width: 100px; overflow: auto">'.$aRow['data'].'</div>',
			$aRow['client_size'],
			$aRow['proctype'],
			$aRow['expire'],
			$cost.$costitem,
			($aRow['remove_no_klan'])?'<i class="icon icon-color icon-check"></i>':'<i class="icon icon-color icon-close"></i>',
			$aRow['buycount'],
			$aRow['desc'].' <a class="icon icon-color icon-trash" data-rel="tooltip" data-original-title="Удалить запись" style="float:right" href="index.php?op=act&n=55&num=0&kitem='.$aRow['id'].'" onclick="return CheckDelete()"></a> <i class="icon icon-color icon-edit" data-rel="tooltip" data-original-title="Редактировать запись" style="float:right"></i>',
			$aRow['id']
		);
		$output['aaData'][] = $row;
	}	
	echo json_encode( $output );
	die();
}

function GetPromoCodes(){
	global $db, $gpoint, $spoint;
	if (!isset($_POST['sEcho'])) die();
	$db->query('set names utf8');
	$aColumns = array( 'id', 'code', 'expire', 'group', 'bonus_money_gold', 'bonus_money_silver', 'bonus_item_id', 'multi_user', 'used_userid', 'desc' );
	$sIndexColumn = "id";
	$sTable = "promo_codes";	
	// Paging
	$sLimit = "";
	if ( isset( $_POST['iDisplayStart'] ) && $_POST['iDisplayLength'] != '-1' )
	{
		$sLimit = "LIMIT ".intval( $_POST['iDisplayStart'] ).", ".
			intval( $_POST['iDisplayLength'] );
	}	
	// Ordering
	$sOrder = "ORDER by `id` DESC";
	if ( isset( $_POST['iSortCol_0'] ) )
	{
		$sOrder = "ORDER BY  ";
		for ( $i=0 ; $i<intval( $_POST['iSortingCols'] ) ; $i++ )
		{
			if ( $_POST[ 'bSortable_'.intval($_POST['iSortCol_'.$i]) ] == "true" )
			{
				$sOrder .= "`".$aColumns[ intval( $_POST['iSortCol_'.$i] ) ]."` ".
					($_POST['sSortDir_'.$i]==='asc' ? 'asc' : 'desc') .", ";
			}
		}
		
		$sOrder = substr_replace( $sOrder, "", -2 );
		if ( $sOrder == "ORDER BY" )
		{
			$sOrder = "";
		}
	}	
	$sWhere = '';
	AddFiltering($sWhere, 0, 'id');
	AddFiltering($sWhere, 1, 'code', true);
	AddFiltering($sWhere, 2, 'expire', true);
	AddFiltering($sWhere, 3, 'group');
	AddFiltering($sWhere, 4, 'bonus_money_gold');
	AddFiltering($sWhere, 5, 'bonus_money_silver');
	AddFiltering($sWhere, 6, 'bonus_item_id');
	AddFiltering($sWhere, 7, 'multi_user');
	AddFiltering($sWhere, 8, 'used_userid');
	AddFiltering($sWhere, 9, 'desc', true);
	/*
	 * SQL queries
	 * Get data to display
	 */
	$sQuery = "SELECT SQL_CALC_FOUND_ROWS * FROM $sTable $sWhere $sOrder $sLimit";
	//echo $sQuery;
	$rResult = $db->query( $sQuery) or die($db->error);
	
	/* Data set length after filtering */
	$sQuery = "
		SELECT FOUND_ROWS()
	";
	$rResultFilterTotal = $db->query( $sQuery) or die($db->error);
	$aResultFilterTotal = mysqli_fetch_array($rResultFilterTotal);
	$iFilteredTotal = $aResultFilterTotal[0];
	
	/* Total data set length */
	$sQuery = "
		SELECT COUNT(`".$sIndexColumn."`)
		FROM   $sTable
	";
	$rResultTotal = $db->query( $sQuery) or die($db->error);
	$aResultTotal = mysqli_fetch_array($rResultTotal);
	$iTotal = $aResultTotal[0];
	
	$output = array(
		"sEcho" => intval($_POST['sEcho']),
		"iTotalRecords" => $iTotal,
		"iTotalDisplayRecords" => $iFilteredTotal,
		"aaData" => array()
	);	
	while ( $aRow = mysqli_fetch_array( $rResult ) )
	{		
		if ($aRow['expire'] > 0) $expire = @date('Y-m-d H:i:s', $aRow['expire']); else $expire = '';
		if ($aRow['bonus_item_id'] == 0 || $aRow['bonus_item_count'] == 0) $item = ''; else {
			$it = GetExtItem($aRow['bonus_item_id']);
			$item = '<b>'.$aRow['bonus_item_count'].'</b> x '.$it->name.'<br><span class="label label-important">'.$aRow['bonus_item_id'].'</span>';
			if ($aRow['bonus_item_expire'] > 0) $item .= ' на '.GetTime($aRow['bonus_item_expire']);
		}
		$color = '';
		if ($aRow['used_userid'] > 0) $color = 'plus';
		if ($aRow['used_userid'] == 0 && $aRow['expire'] > 0 && $aRow['expire'] <= time()) $color = 'minus';
		if ($aRow['multi_user']) {
			if ($aRow['used_userid'] == 0) $userid = ''; else $userid = '<span class="label label-success">'.$aRow['used_userid'].'</span>';
		} else {
			if ($aRow['used_userid'] == 0) $userid = ''; else 
			$userid = '<img src="img/details_open.png"> '.sprintf('<a href="#" onclick="fnuserid(\'%d\')">%d</a>',$aRow['used_userid'],$aRow['used_userid']);
		}
		if ($aRow['bonus_money_gold'] > 0) $gold = '<span class="gold gold_dark">'.$aRow['bonus_money_gold'].'</span>'; else $gold = '';
		if ($aRow['bonus_money_silver'] > 0) $silver = '<span class="silver gold_silver">'.$aRow['bonus_money_silver'].'</span>'; else $silver = '';
		$row = array(
			$aRow['id'],
			$aRow['code'],
			$expire,
			($aRow['group'])?$aRow['group']:'<i class="icon icon-color icon-close"></i>',
			$gold,
			$silver,
			$item,
			($aRow['multi_user'])?'<i class="icon icon-color icon-check"></i>':'<i class="icon icon-color icon-close"></i>',
			$userid,
			$aRow['desc'].' <a class="icon icon-color icon-trash" data-rel="tooltip" data-original-title="Удалить запись" style="float:right" href="#" onclick="return CheckDeletePromo('.$aRow['id'].')"></a> <i class="icon icon-color icon-edit" data-rel="tooltip" data-original-title="Редактировать запись" style="float:right" id="edit_promo_'.$aRow['id'].'"></i>',
			$color,
			$aRow['id'],
			$aRow['used_userid']
		);
		$output['aaData'][] = $row;
	}	
	echo json_encode( $output );
	die();
}

function handleGetPublicPromocodes() {
    global $db;
    
    // Очищаем буфер вывода
    while (ob_get_level()) {
        ob_end_clean();
    }
    
    header('Content-Type: application/json; charset=utf-8');
    header('X-Content-Type-Options: nosniff');
    
    // Устанавливаем кодировку UTF-8
    $db->set_charset('utf8');
    $db->query("SET NAMES utf8");
    
    $current_time = time();
    $promos = array();
    $debug = array();
    
    // 1. Получаем ВСЕ промокоды без условий (для отладки)
    $all_query = "SELECT id, code, multi_user, expire, used_userid, bonus_money_gold, bonus_money_silver, bonus_item_id, bonus_item_count, `desc` 
                  FROM promo_codes 
                  ORDER BY id DESC 
                  LIMIT 50"; // Покажем последние 50 для отладки
    
    $all_result = $db->query($all_query);
    $all_promos = array();
    if ($all_result && $all_result->num_rows > 0) {
        while ($row = $all_result->fetch_assoc()) {
            $all_promos[] = array(
                'id' => $row['id'],
                'code' => $row['code'],
                'multi_user' => $row['multi_user'],
                'expire' => $row['expire'],
                'expire_date' => $row['expire'] > 0 ? date('Y-m-d H:i:s', $row['expire']) : '0',
                'used_userid' => $row['used_userid'],
                'desc' => $row['desc']
            );
        }
    }
    $debug['all_promos'] = $all_promos;
    $debug['total_in_db'] = $all_result ? $all_result->num_rows : 0;
    
    // 2. Теперь получаем ТОЛЬКО с multi_user = 1 (без других условий)
    $multi_query = "SELECT * FROM promo_codes 
                    WHERE multi_user = 1";
    
    $multi_result = $db->query($multi_query);
    $multi_count = $multi_result ? $multi_result->num_rows : 0;
    $debug['multi_user_1_count'] = $multi_count;
    
    // 3. Получаем с multi_user = 1 и (expire > now OR expire = 0)
    $active_query = "SELECT * FROM promo_codes 
                     WHERE multi_user = 1 
                     AND (expire > $current_time OR expire = 0)";
    
    $active_result = $db->query($active_query);
    $active_count = $active_result ? $active_result->num_rows : 0;
    $debug['active_condition_count'] = $active_count;
    
    // 4. Получаем с multi_user = 1 и (expire > now OR expire = 0) и used_userid condition
    $final_query = "SELECT * FROM promo_codes 
                    WHERE multi_user = 1 
                    AND (expire > $current_time OR expire = 0)
                    AND (used_userid = 0 OR used_userid IS NULL)";
    
    $final_result = $db->query($final_query);
    $final_count = $final_result ? $final_result->num_rows : 0;
    $debug['final_condition_count'] = $final_count;
    
    // 5. Основной запрос для вывода (без условий на used_userid)
    $query = "SELECT * FROM promo_codes 
              WHERE multi_user = 1 
              AND (expire > $current_time OR expire = 0)
              ORDER BY 
                  CASE 
                      WHEN expire = 0 THEN 1 
                      ELSE 0 
                  END DESC,
                  expire ASC";
    
    $result = $db->query($query);
    
    if ($result && $result->num_rows > 0) {
        while ($row = $result->fetch_assoc()) {
            $promos[] = array(
                'code' => $row['code'],
                'desc' => $row['desc'],
                'expire' => (int)$row['expire'],
                'bonus_money_gold' => (int)$row['bonus_money_gold'],
                'bonus_money_silver' => (int)$row['bonus_money_silver'],
                'bonus_item_id' => (int)$row['bonus_item_id'],
                'bonus_item_count' => (int)$row['bonus_item_count'],
                'bonus_item_expire' => (int)$row['bonus_item_expire'],
                'used_userid' => (int)$row['used_userid'] // Добавим для отладки
            );
        }
    }
    
    $debug['returned_count'] = count($promos);
    
    // Отправляем ответ с отладкой
    echo json_encode([
        'errorcode' => 0,
        'debug' => $debug,
        'data' => $promos
    ], JSON_UNESCAPED_UNICODE);
    
    exit;
}

function ShowHistory(){
	global $db, $gpoint, $spoint;
	if (!isset($_POST['id'])||!isset($_POST['sEcho'])) die();
	$id = intval($_POST['id']);
	if ($id < 16) die();
	$db->query('set names utf8');
	$aColumns = array( 'id', 'data', 'ip', 'gold', 'gold_rest', 'desc' );	
	/* Indexed column (used for fast and accurate table cardinality) */
	$sIndexColumn = "id";	
	/* DB table to use */
	$sTable = "lklogs";	
	/* 
	 * Paging
	 */
	$sLimit = "";
	if ( isset( $_POST['iDisplayStart'] ) && $_POST['iDisplayLength'] != '-1' )
	{
		$sLimit = "LIMIT ".intval( $_POST['iDisplayStart'] ).", ".
			intval( $_POST['iDisplayLength'] );
	}	
	/*
	 * Ordering
	 */
	$sOrder = "ORDER by `id` DESC";
	if ( isset( $_POST['iSortCol_0'] ) )
	{
		$sOrder = "ORDER BY  ";
		for ( $i=0 ; $i<intval( $_POST['iSortingCols'] ) ; $i++ )
		{
			if ( $_POST[ 'bSortable_'.intval($_POST['iSortCol_'.$i]) ] == "true" )
			{
				$sOrder .= "`".$aColumns[ intval( $_POST['iSortCol_'.$i] ) ]."` ".
					($_POST['sSortDir_'.$i]==='asc' ? 'asc' : 'desc') .", ";
			}
		}
		
		$sOrder = substr_replace( $sOrder, "", -2 );
		if ( $sOrder == "ORDER BY" )
		{
			$sOrder = "";
		}
	}	
	$sWhere = '';
	AddFiltering($sWhere, 0, 'id');	
	AddFiltering($sWhere, 1, 'data', true);
	AddFiltering($sWhere, 2, 'ip', true);
	AddFiltering($sWhere, 3, '', false, '', true);
	AddFiltering($sWhere, 4, '_rest', false, '', true);
	AddFiltering($sWhere, 5, 'desc', true);
	/*
	 * SQL queries
	 * Get data to display
	 */
	if ($sWhere == '') $sWhere = 'WHERE `userid`='.$id; else $sWhere .= 'AND `userid`='.$id;
	//$sQuery = "SELECT SQL_CALC_FOUND_ROWS *	FROM $sTable $sWhere $sOrder $sLimit";
	$sQuery = "SELECT * FROM $sTable $sWhere $sOrder $sLimit";
	$rResult = $db->query( $sQuery) or die($db->error);
	
	/* Data set length after filtering */
	//$sQuery = "SELECT FOUND_ROWS()";
	$sQuery = "SELECT count(`".$sIndexColumn."`) FROM $sTable $sWhere";
	$rResultFilterTotal = $db->query( $sQuery) or die($db->error);
	$aResultFilterTotal = mysqli_fetch_array($rResultFilterTotal);
	$iFilteredTotal = $aResultFilterTotal[0];
	
	/* Total data set length */
	$sQuery = "
		SELECT COUNT(`".$sIndexColumn."`)
		FROM   $sTable
	";
	$rResultTotal = $db->query( $sQuery) or die($db->error);
	$aResultTotal = mysqli_fetch_array($rResultTotal);
	$iTotal = $aResultTotal[0];
	
	$output = array(
		"sEcho" => intval($_POST['sEcho']),
		"iTotalRecords" => $iTotal,
		"iTotalDisplayRecords" => $iFilteredTotal,
		"aaData" => array()
	);
	
	while ( $aRow = mysqli_fetch_array( $rResult ) )
	{		
		$color = '';
		if ($aRow['gold'] > 0 || $aRow['silver'] > 0) $color = 'minus';
		if ($aRow['gold'] < 0 || $aRow['silver'] < 0) $color = 'plus';
		$row = array(
			$aRow['id'],
			$aRow['data'],
			$aRow['ip'],
			GetShowCost($aRow['gold'].'|'.$aRow['silver'], true, true),
			GetShowCost($aRow['gold_rest'].'|'.$aRow['silver_rest'], true, true),
			$aRow['desc'],
			$color
		);
		$output['aaData'][] = $row;
	}
	
	echo json_encode( $output );
	die();
}

function ShowLoginLog(){
	global $db;
	if (!isset($_POST['id'])||!isset($_POST['sEcho'])) die();
	$id = intval($_POST['id']);
	if ($id < 16) die();
	$aColumns = array( 'data', 'ip', 'action' );	
	/* Indexed column (used for fast and accurate table cardinality) */
	$sIndexColumn = "id";	
	/* DB table to use */
	$sTable = "login_log";	
	/* 
	 * Paging
	 */
	$sLimit = "";
	if ( isset( $_POST['iDisplayStart'] ) && $_POST['iDisplayLength'] != '-1' )
	{
		$sLimit = "LIMIT ".intval( $_POST['iDisplayStart'] ).", ".
			intval( $_POST['iDisplayLength'] );
	}	
	/*
	 * Ordering
	 */
	$sOrder = "ORDER by `id` DESC";
	if ( isset( $_POST['iSortCol_0'] ) )
	{
		$sOrder = "ORDER BY  ";
		for ( $i=0 ; $i<intval( $_POST['iSortingCols'] ) ; $i++ )
		{
			if ( $_POST[ 'bSortable_'.intval($_POST['iSortCol_'.$i]) ] == "true" )
			{
				$sOrder .= "`".$aColumns[ intval( $_POST['iSortCol_'.$i] ) ]."` ".
					($_POST['sSortDir_'.$i]==='asc' ? 'asc' : 'desc') .", ";
			}
		}
		
		$sOrder = substr_replace( $sOrder, "", -2 );
		if ( $sOrder == "ORDER BY" )
		{
			$sOrder = "";
		}
	}	
	$sWhere = '';
	AddFiltering($sWhere, 0, 'data', true);
	AddFiltering($sWhere, 1, 'ip', true);
	AddFiltering($sWhere, 2, 'action');
	/*
	 * SQL queries
	 * Get data to display
	 */
	if ($sWhere == '') $sWhere = 'WHERE `userid`='.$id; else $sWhere .= 'AND `userid`='.$id;
	//$sQuery = "SELECT SQL_CALC_FOUND_ROWS *	FROM $sTable $sWhere $sOrder $sLimit";
	$sQuery = "SELECT * FROM $sTable $sWhere $sOrder $sLimit";
	$rResult = $db->query( $sQuery) or die($db->error);
	
	/* Data set length after filtering */
	//$sQuery = "SELECT FOUND_ROWS()";
	$sQuery = "SELECT count(`".$sIndexColumn."`) FROM $sTable $sWhere";
	$rResultFilterTotal = $db->query( $sQuery) or die($db->error);
	$aResultFilterTotal = mysqli_fetch_array($rResultFilterTotal);
	$iFilteredTotal = $aResultFilterTotal[0];
	
	/* Total data set length */
	$sQuery = "
		SELECT COUNT(`".$sIndexColumn."`)
		FROM   $sTable
	";
	$rResultTotal = $db->query( $sQuery) or die($db->error);
	$aResultTotal = mysqli_fetch_array($rResultTotal);
	$iTotal = $aResultTotal[0];
	
	$output = array(
		"sEcho" => intval($_POST['sEcho']),
		"iTotalRecords" => $iTotal,
		"iTotalDisplayRecords" => $iFilteredTotal,
		"aaData" => array()
	);
	
	while ( $aRow = mysqli_fetch_array( $rResult ) )
	{		
		$color = '';
		if ($aRow['action'] == 1 || $aRow['action'] == 3) $color = 'plus'; else
		if ($aRow['action'] == 4 || $aRow['action'] == 5) $color = 'minus';//plus
		$row = array(			
			$aRow['data'],
			$aRow['ip'],			
			$aRow['action'],
			$color
		);
		$output['aaData'][] = $row;
	}
	
	echo json_encode( $output );
	die();
}

function ShowLoginLogAdm(){
	global $db;
	if (!isset($_POST['sEcho'])) die();
	$aColumns = array( 'id', 'data', 'ip', 'userid', 'login', 'userid', 'action' );	
	/* Indexed column (used for fast and accurate table cardinality) */
	$sIndexColumn = "id";	
	/* DB table to use */
	$sTable = "login_log";	
	/* 
	 * Paging
	 */
	$sLimit = "";
	if ( isset( $_POST['iDisplayStart'] ) && $_POST['iDisplayLength'] != '-1' )
	{
		$sLimit = "LIMIT ".intval( $_POST['iDisplayStart'] ).", ".
			intval( $_POST['iDisplayLength'] );
	}	
	/*
	 * Ordering
	 */
	$sOrder = "ORDER by `id` DESC";
	if ( isset( $_POST['iSortCol_0'] ) )
	{
		$sOrder = "ORDER BY  ";
		for ( $i=0 ; $i<intval( $_POST['iSortingCols'] ) ; $i++ )
		{
			if ( $_POST[ 'bSortable_'.intval($_POST['iSortCol_'.$i]) ] == "true" )
			{
				$sOrder .= "`".$aColumns[ intval( $_POST['iSortCol_'.$i] ) ]."` ".
					($_POST['sSortDir_'.$i]==='asc' ? 'asc' : 'desc') .", ";
			}
		}
		
		$sOrder = substr_replace( $sOrder, "", -2 );
		if ( $sOrder == "ORDER BY" )
		{
			$sOrder = "";
		}
	}	
	$sWhere = '';
	AddFiltering($sWhere, 0, 'id');
	AddFiltering($sWhere, 1, 'data', true);
	AddFiltering($sWhere, 2, 'ip', true);
	AddFiltering($sWhere, 3, 'userid');
	AddFiltering($sWhere, 4, 'login');
	AddFiltering($sWhere, 5, 'userid', false, intval(($_POST['sSearch_5']*16)+16));
	AddFiltering($sWhere, 6, 'action');	
	$sQuery = "SELECT * FROM $sTable $sWhere $sOrder $sLimit";
	$rResult = $db->query( $sQuery) or die($db->error);
	$sQuery = "SELECT count(`".$sIndexColumn."`) FROM $sTable $sWhere";
	$rResultFilterTotal = $db->query( $sQuery) or die($db->error);
	$aResultFilterTotal = mysqli_fetch_array($rResultFilterTotal);
	$iFilteredTotal = $aResultFilterTotal[0];	
	/* Total data set length */
	$sQuery = "
		SELECT COUNT(`".$sIndexColumn."`)
		FROM   $sTable
	";
	$rResultTotal = $db->query( $sQuery) or die($db->error);
	$aResultTotal = mysqli_fetch_array($rResultTotal);
	$iTotal = $aResultTotal[0];
	
	$output = array(
		"sEcho" => intval($_POST['sEcho']),
		"iTotalRecords" => $iTotal,
		"iTotalDisplayRecords" => $iFilteredTotal,
		"aaData" => array()
	);
	
	while ( $aRow = mysqli_fetch_array( $rResult ) )
	{		
		$color = '';
		if ($aRow['action'] == 1 || $aRow['action'] == 3) $color = 'plus'; else
		if ($aRow['action'] == 4 || $aRow['action'] == 5) $color = 'minus';
		$lkid = intval(($aRow['userid']/16)-1);
		$row = array(	
			$aRow['id'],		
			$aRow['data'],
			sprintf('<a href="#" onclick="fnip(\'%s\')">%s</a>',$aRow['ip'],$aRow['ip']),
			sprintf('<a href="#" onclick="fnuser(%d)">%d</a>',$aRow['userid'],$aRow['userid']),
			'<img src="img/details_open.png"> '.sprintf('<a href="#" onclick="fnlogin(\'%s\')">%s</a>',$aRow['login'],$aRow['login']),
			sprintf('<a href="#" onclick="fnlkid(%d)">%d</a>',$lkid,$lkid),			
			$aRow['action'],
			$color,
			$aRow['userid']
		);
		$output['aaData'][] = $row;
	}
	
	echo json_encode( $output );
	die();
}

function UserInfo1pld(){
	global $db, $gender; // Добавляем глобальные переменные, если они используются для пола
	if (!isset($_POST['id']) && !isset($_POST['login'])) die();
	
	$servid = isset($_POST['servid']) ? $_POST['servid'] : 1; // Защита если servid не передан
	if (isset($_POST['id'])) $id = $_POST['id']; else $id = '';
	if (isset($_POST['login'])) $login = $_POST['login']; else $login = '';
	
	$db->query('set names utf8');
	if ($id != '') $res = $db->query("select * from `users` where `ID`=$id"); 
	else $res = $db->query("select * from `users` where `name`='".$db->real_escape_string($login)."'");
	
	if (!$res) die($db->error);
	if ($res->num_rows == 0) die('<div class="alert alert-error"><strong>Ошибка!</strong> Аккаунт не найден</div>');
	
	$gold_rest = 0; $silver_rest = 0;	
	if ($res->num_rows > 0) {
		$row = mysqli_fetch_assoc($res);
		$ipdata = @unserialize($row['ipdata']);
		if (!is_array($ipdata)) {
			$ipdata = array();
			$ipdata[0] = false;		// Ограничение на вход в ЛК
			$ipdata[1] = array();	// Список разрешенных IP	
			$ipdata[2] = false;		// Ограничение на вход в игру		
		}
		if (!isset($ipdata[2])) $ipdata[2] = false;
		$id = $row['ID'];
		$gold_rest = $row['lkgold'];
		$silver_rest = $row['lksilver'];
	}
	
	// Подсчет статистики монет
	$res_log = $db->query('select sum(`gold`) as `gold`, sum(`silver`) as `silver` FROM `lklogs` WHERE (`gold`<0 or `silver`<0) AND `userid`='.$id);
	if (!$res_log) die($db->error);
	$row_log = mysqli_fetch_assoc($res_log);
	$gold_add = intval($row_log['gold']*(-1)); 
	$silver_add = intval($row_log['silver']*(-1));
	
	$res_log = $db->query('select sum(`gold`) as `gold`, sum(`silver`) as `silver` FROM `lklogs` WHERE (`gold`>0 or `silver`>0) AND `userid`='.$id);
	if (!$res_log) die($db->error);
	$row_log = mysqli_fetch_assoc($res_log);
	$gold_spend = intval($row_log['gold']); 
	$silver_spend = intval($row_log['silver']);

	// Формирование списка IP для вывода
	$ips_list = (count($ipdata[1])) ? implode(', ', $ipdata[1]) : 'Нет';

	// === НАЧАЛО НОВОЙ ВЕРСТКИ ===
	?>
	<div class="text-admin">
		<h1 class="sub-title-dec">Статистика аккаунта</h1>
		
		<div class="text-admin__container">
			<p>UserID: <span class="decor-text"><?= $row['ID'] ?></span></p>
			<p>Login: <span><?= htmlspecialchars($row['name']) ?></span></p>
			<p>E-mail: <span><?= htmlspecialchars($row['email']) ?></span></p>
			<p>
				Секретный вопрос:
				<span>
					<?= htmlspecialchars($row['Prompt']) ?> 
					( <span class="text-green">ответ: <?= htmlspecialchars($row['answer']) ?></span> )
				</span>
			</p>
			<p>Имя: <span><?= htmlspecialchars($row['truename']) ?></span></p>
			<p>IP регистрации: <span> <?= $row['idnumber'] ?> </span></p>
			<p>
				Список разрешенных IP: <span class="text-green"><?= $ips_list ?></span>
			</p>
			<p>Дата регистрации: <span><?= $row['creatime'] ?></span></p>
            
            <?php if ($ipdata[0]) { ?>
				<p><span class="text-decor-red">Включено ограничение на вход в ЛК по IP</span> (<a href="#" onclick="alert('Функция отключения должна быть здесь'); return false;" class="text-green">инфо</a>)</p>
			<?php } ?>
			<?php if ($ipdata[2]) { ?>
				<p><span class="text-decor-red">Включено ограничение на вход в игру по IP</span></p>
			<?php } ?>
		</div>

		<div class="text-admin__money">
			<p>
				<span class="text-decor-green">Получено монет: </span>
				<span>
					<img src="../assets/images/gold.png" alt="gold" /> <?= $gold_add ?>
				</span>
				<span>
					<img src="img/silver.png" alt="silver" /> <?= $silver_add ?>
				</span>
			</p>
			<p>
				<span class="text-decor-red">Потрачено монет: </span>
				<span>
					<img src="../assets/images/gold.png" alt="gold" /> <?= $gold_spend ?>
				</span>
				<span>
					<img src="img/silver.png" alt="silver" /> <?= $silver_spend ?>
				</span>
			</p>
			<p>
				<span>Остаток монет: </span>
				<span>
					<img src="../assets/images/gold.png" alt="gold" /> <?= $gold_rest ?>
				</span>
				<span>
					<img src="img/silver.png" alt="silver" /> <?= $silver_rest ?>
				</span>
			</p>
 	 </div> 

 	 <div class="text-admin__content">
			 <?php 
			// Обновляем информацию о персонажах
			$db->query('DELETE FROM `top` WHERE `userid`='.$id);
			$r = GetUserRoles($id); // Получаем список ролей
			
			if ($r['retcode'] != 0) {
				echo '<div class="decor-text-input" style="color:red">GetUserRoles error '.$r['retcode'].'</div>';
			} else if (count($r['data']) > 0) {
				
				foreach ($r['data'] as $i => $val){		
					$rid = $val['id'];
					AddRoleToTop($rid); // Добавляем в кэш-таблицу top
					$res_top = $db->query("SELECT * FROM `top` where roleid=".$rid);
					if (!$res_top) continue;
					$char = mysqli_fetch_assoc($res_top);

					// --- Логика получения Клана и Иконки (перенесена из GetPersInfo) ---
					$klanname = 'Нет';
					$klanicon = '';
					if ($char['factionid'] != 0) {
						$found_name = '';
						// Ищем клан в локальной базе
						$res_k = $db->query('SELECT `name` FROM `klan` WHERE `id`='.$char['factionid']);
						if ($res_k && $res_k->num_rows > 0){
							$row_k = mysqli_fetch_assoc($res_k);
							$found_name = trim($row_k['name']);
						}
						
						// Если название в базе нашлось и оно НЕ пустое
						if ($found_name !== '') {
							$klanname = htmlspecialchars($found_name);
						} else {
							// Если в базе пусто, пробуем запросить напрямую из игры
							$fact = GetFactionInfo($char['factionid']);
							if (isset($fact['value']['name']) && trim($fact['value']['name']) !== '') {
								$klanname = htmlspecialchars(trim($fact['value']['name']));
							} else {
								// Если и сервер игры не отдал имя, выводим ID
								$klanname = 'ClanID: ' . $char['factionid'];
							}
						}
						// Путь к иконке клана
						$klanicon = "<img src='klan/geticon.php?servid=".$servid."&klan=".$char['factionid']."' align='absmiddle' style='margin-right:5px;'>";
					}
					
					// Получение названия класса/профессии
					$prof_name = (function_exists('GetOccupationName')) ? GetOccupationName($char['roleprof']) : $char['roleprof'];
					// Получение пола
					$gender_name = (isset($gender[$char['rolegender']])) ? $gender[$char['rolegender']] : $char['rolegender'];
					// Реборн (если есть)
					$reborn = ($char['reborn']) ? " <span style='color:#ff0000; font-weight:bold;'>РБ</span>" : "";
					
					// --- Вывод карточки персонажа ---
					?>
					<div class="admin-card">
						<p>
							RoleID: <span class="decor-blue"><?= $char['roleid'] ?></span>
							<span style="float:right;">
								<i class="icon icon-color icon-edit" style="cursor:pointer;" title="Редактировать" onclick="EditRole(<?= $char['roleid'] ?>, '<?= htmlspecialchars($char['rolename']) ?>')"></i> 
								<i class="icon icon-color icon-cross" style="cursor:pointer;" title="Забанить" onclick="BanRole(<?= $char['roleid'] ?>, '<?= htmlspecialchars($char['rolename']) ?>')"></i>
							</span>
						</p>
						<p>Ник: <span><?= htmlspecialchars($char['rolename']) ?></span></p>
						<p>Уровень: <span><?= $char['rolelevel'] . $reborn ?></span></p>
						<p>Класс: <span><?= $prof_name ?></span></p>
						<p>Пол: <span><?= $gender_name ?></span></p>
						<p>Клан: <span><?= $klanicon . $klanname ?></span></p>
						<p>HP: <span><?= $char['hp'] ?></span></p>
						<p>MP: <span><?= $char['mp'] ?></span></p>
						<p>Онлайн: <span><?= GetTime2($char['timeused'], 'ffff00') ?></span></p>
					</div>
					<?php
				}
			} else {
				echo '<div class="text-admin__container"><p>Персонажи не найдены</p></div>';
			}
			?>
		</div>
	</div>
	<?php
	die();
}
function UserInfo(){
	global $db, $gender; // Добавляем глобальные переменные, если они используются для пола
	if (!isset($_POST['id']) && !isset($_POST['login'])) die();
	
	$servid = isset($_POST['servid']) ? $_POST['servid'] : 1; // Защита если servid не передан
	if (isset($_POST['id'])) $id = $_POST['id']; else $id = '';
	if (isset($_POST['login'])) $login = $_POST['login']; else $login = '';
	
	$db->query('set names utf8');
	if ($id != '') $res = $db->query("select * from `users` where `ID`=$id"); 
	else $res = $db->query("select * from `users` where `name`='".$db->real_escape_string($login)."'");
	
	if (!$res) die($db->error);
	if ($res->num_rows == 0) die('<div class="alert alert-error"><strong>Ошибка!</strong> Аккаунт не найден</div>');
	
	$gold_rest = 0; $silver_rest = 0;	
	if ($res->num_rows > 0) {
		$row = mysqli_fetch_assoc($res);
		$ipdata = @unserialize($row['ipdata']);
		if (!is_array($ipdata)) {
			$ipdata = array();
			$ipdata[0] = false;		// Ограничение на вход в ЛК
			$ipdata[1] = array();	// Список разрешенных IP	
			$ipdata[2] = false;		// Ограничение на вход в игру		
		}
		if (!isset($ipdata[2])) $ipdata[2] = false;
		$id = $row['ID'];
		$gold_rest = $row['lkgold'];
		$silver_rest = $row['lksilver'];
	}
	
	// Подсчет статистики монет
	$res_log = $db->query('select sum(`gold`) as `gold`, sum(`silver`) as `silver` FROM `lklogs` WHERE (`gold`<0 or `silver`<0) AND `userid`='.$id);
	if (!$res_log) die($db->error);
	$row_log = mysqli_fetch_assoc($res_log);
	$gold_add = intval($row_log['gold']*(-1)); 
	$silver_add = intval($row_log['silver']*(-1));
	
	$res_log = $db->query('select sum(`gold`) as `gold`, sum(`silver`) as `silver` FROM `lklogs` WHERE (`gold`>0 or `silver`>0) AND `userid`='.$id);
	if (!$res_log) die($db->error);
	$row_log = mysqli_fetch_assoc($res_log);
	$gold_spend = intval($row_log['gold']); 
	$silver_spend = intval($row_log['silver']);

	// Формирование списка IP для вывода
	$ips_list = (count($ipdata[1])) ? implode(', ', $ipdata[1]) : 'Нет';
	if (isset($_GET['id'])) {
		$postdata = array(
			'op' => 'userinfo',
			'servid' => $servid,
			'id' => intval($_GET['id'])
		);
		$donsumm = GetUserDonateSumm('', $_GET['id'], true);	
	} else {
		if (preg_match($login_filter, $_GET['login']) || strlen($_GET['login']) < $login_min_len || strlen($_GET['login']) > $login_max_len) {
			echo GetErrorTxt(10);
			die();
		}
		$postdata = array(
			'op' => 'userinfo',
			'servid' => $servid,
			'login' => $_GET['login']
		);
		$donsumm = GetUserDonateSumm($_GET['login'], '', true);	
	}
	// === НАЧАЛО НОВОЙ ВЕРСТКИ ===
	?>
	<div class="text-admin">
		<h1 class="sub-title-dec">Статистика аккаунта</h1>
		
		<div class="text-admin__container">
			<p>UserID: <span class="decor-text"><?= $row['ID'] ?></span></p>
			<p>Login: <span><?= htmlspecialchars($row['name']) ?></span></p>
			<p>E-mail: <span><?= htmlspecialchars($row['email']) ?></span></p>
			<p>
				Секретный вопрос:
				<span>
					<?= htmlspecialchars($row['Prompt']) ?> 
					( <span class="text-green">ответ: <?= htmlspecialchars($row['answer']) ?></span> )
				</span>
			</p>
			<p>Имя: <span><?= htmlspecialchars($row['truename']) ?></span></p>
			<p>IP регистрации: <span> <?= $row['idnumber'] ?> </span></p>
			<p>
				Список разрешенных IP: <span class="text-green"><?= $ips_list ?></span>
			</p>
			<p>Дата регистрации: <span><?= $row['creatime'] ?></span></p>
            
            <?php if ($ipdata[0]) { ?>
				<p><span class="text-decor-red">Включено ограничение на вход в ЛК по IP</span> (<a href="#" onclick="alert('Функция отключения должна быть здесь'); return false;" class="text-green">инфо</a>)</p>
			<?php } ?>
			<?php if ($ipdata[2]) { ?>
				<p><span class="text-decor-red">Включено ограничение на вход в игру по IP</span></p>
			<?php } ?>
		</div>

		<div class="text-admin__money">
			<p>Пожертвовано: <span class="text-green"><?= $donsumm ?> руб.</span></p>
			<p>
				<span class="text-decor-green">Получено монет: </span>
				<span>
					<img src="../assets/images/gold.png" alt="gold" /> <?= $gold_add ?>
				</span>
				<span>
					<img src="img/silver.png" alt="silver" /> <?= $silver_add ?>
				</span>
			</p>
			<p>
				<span class="text-decor-red">Потрачено монет: </span>
				<span>
					<img src="../assets/images/gold.png" alt="gold" /> <?= $gold_spend ?>
				</span>
				<span>
					<img src="img/silver.png" alt="silver" /> <?= $silver_spend ?>
				</span>
			</p>
			<p>
				<span>Остаток монет: </span>
				<span>
					<img src="../assets/images/gold.png" alt="gold" /> <?= $gold_rest ?>
				</span>
				<span>
					<img src="img/silver.png" alt="silver" /> <?= $silver_rest ?>
				</span>
			</p>
		</div>

		<div class="text-admin__content">
			<?php
			// Обновляем информацию о персонажах
			$db->query('DELETE FROM `top` WHERE `userid`='.$id);
			$r = GetUserRoles($id); // Получаем список ролей
			
			if ($r['retcode'] != 0) {
				echo '<div class="decor-text-input" style="color:red">GetUserRoles error '.$r['retcode'].'</div>';
			} else if (count($r['data']) > 0) {
				
				foreach ($r['data'] as $i => $val){		
					$rid = $val['id'];
					AddRoleToTop($rid); // Добавляем в кэш-таблицу top
					$res_top = $db->query("SELECT * FROM `top` where roleid=".$rid);
					if (!$res_top) continue;
					$char = mysqli_fetch_assoc($res_top);

					// --- Логика получения Клана и Иконки (перенесена из GetPersInfo) ---
					$klanname = '';
					$klanicon = '';
					if ($char['factionid'] != 0) {
						$res_k = $db->query('SELECT `name` FROM `klan` WHERE `id`='.$char['factionid']);
						if ($res_k && $res_k->num_rows > 0){
							$row_k = mysqli_fetch_assoc($res_k);
							$klanname = htmlspecialchars($row_k['name']);
						} else {
							// Если клана нет в кэше базы, запрашиваем его название напрямую из игры
							$fact = GetFactionInfo($char['factionid']);
							$klanname = htmlspecialchars($fact['value']['name']);
						}
						// Путь к иконке клана
						$klanicon = "<img src='klan/geticon.php?servid=".$servid."&klan=".$char['factionid']."' align='absmiddle' style='margin-right:5px;'>";
					}
					// Получение названия класса/профессии
					$prof_name = (function_exists('GetOccupationName')) ? GetOccupationName($char['roleprof']) : $char['roleprof'];
					// Получение пола
					$gender_name = (isset($gender[$char['rolegender']])) ? $gender[$char['rolegender']] : $char['rolegender'];
					// Реборн (если есть)
					$reborn = ($char['reborn']) ? " <span style='color:#ff0000; font-weight:bold;'>РБ</span>" : "";
					
					// --- Вывод карточки персонажа ---
					?>
					<div class="admin-card">
						<p>
							RoleID: <span class="decor-blue"><?= $char['roleid'] ?></span>
							<span style="float:right;">
								<i class="icon icon-color icon-edit" style="cursor:pointer;" title="Редактировать" onclick="EditRole(<?= $char['roleid'] ?>, '<?= htmlspecialchars($char['rolename']) ?>')"></i> 
								<i class="icon icon-color icon-cross" style="cursor:pointer;" title="Забанить" onclick="BanRole(<?= $char['roleid'] ?>, '<?= htmlspecialchars($char['rolename']) ?>')"></i>
							</span>
						</p>
						<p>Ник: <span><?= htmlspecialchars($char['rolename']) ?></span></p>
						<p>Уровень: <span><?= $char['rolelevel'] . $reborn ?></span></p>
						<p>Класс: <span><?= $prof_name ?></span></p>
						<p>Пол: <span><?= $gender_name ?></span></p>
						<p>Клан: <span><?= $klanicon . $klanname ?></span></p>
						<p>HP: <span><?= $char['hp'] ?></span></p>
						<p>MP: <span><?= $char['mp'] ?></span></p>
						<p>Онлайн: <span><?= GetTime2($char['timeused'], 'ffff00') ?></span></p>
					</div>
					<?php
				}
			} else {
				echo '<div class="text-admin__container"><p>Персонажи не найдены</p></div>';
			}
			?>
		</div>
	</div>
	<?php
	die();
}

function RoleInfo(){
	global $db;
	if (!isset($_POST['roleid'])) die();
	$servid = $_POST['servid'];
	$roleid = $_POST['roleid'];
	$db->query('set names utf8');	
	// Обновляем информацию аккаунта в базе топа
	AddRoleToTop($roleid);	
	$res = $db->query("SELECT * FROM `top` where roleid=".$roleid);
	if (!$res) die($db->error);
	$row = mysqli_fetch_assoc($res);
	echo GetPersInfo($row, $servid, true);
	die();
}

function AddLK(){
	global $db;
	if (!isset($_POST['gold']) || !isset($_POST['silver']) || !isset($_POST['login']) || !isset($_POST['desc']) || !isset($_POST['id'])|| !isset($_POST['ip'])) die('10');
	$login = $db->real_escape_string($_POST['login']);
	$gpoint = intval($_POST['gold']);
	$spoint = intval($_POST['silver']);
	$ip = 'Hidden';
	$desc = $_POST['desc'];
	query2mysql('set names utf8');
	$res = query2mysql("SELECT `ID` FROM `users` WHERE `name`='$login'");
	if ($res->num_rows == 0) die('90'); // Аккаунт не найден
	$row = mysqli_fetch_assoc($res); $id = $row['ID'];
	GiveGold($id, $gpoint, $spoint, $desc, $ip);
	$id = intval($_POST['id']);
	$ip = $_POST['ip'];
	AddLKLogs($id, $ip, 0, 0, "Выдача монет: <span class=\"gold gold_dark\">$gpoint</span> <span class=\"silver gold_silver\">$spoint</span> $login ($desc)");
	die('91');
}

function EditKlanArt(){
	global $db;
	if (!isset($_POST['id'])) die();
	$id = intval($_POST['id']);
	$db->query('set names utf8');
	$res = $db->query("SELECT * FROM `klan_items` WHERE `id`=$id");
	if (!$res) die($db->error);
	if ($res->num_rows == 0) die('<div class="alert alert-error"><strong>Ошибка!</strong> Предмет не найден</div>');
	$row = mysqli_fetch_assoc($res);
	$itemrefresh = "getName('itemid".$id."', 'loaderid".$id."', 'paramid".$id."');";
	$costitemrefresh = "getName('costitemid".$id."', 'costloaderid".$id."', 'costparamid".$id."');";
	$klanrefresh = "getKlanName('klanid".$id."', 'klanloaderid".$id."', 'klanansw".$id."');";
	printf('
	<div class="row-fluid">
			<div class="box span12">
				<div class="box-header well" data-original-title>
					<h2><i class="icon icon-color icon-edit"></i> Редактирование записи № '.$id.'</h2>
				</div>
				<div class="box-content">
					<form name="editform'.$id.'" method="post" action="index.php?op=act&n=53&num=0">
					<input type="hidden" name="kitem" value="'.$id.'">
					<table class="table table-bordered table-striped">
					<tr>
						<td><h3>Klan ID</h3></td>
						<td><input type="text" id="klanid'.$id.'" name="klanid" value="%d" class="config_itemid" onchange="'.$klanrefresh.'"> <span style="display:none" id="klanloaderid'.$id.'"><img src="img/ajax-loaders/ajax-loader-1.gif" border="0" align="absmiddle"/></span> <span id="klanansw'.$id.'"></span></td>
						<td style="text-align:left"><code>ID клана</code></td>
					</tr>
					<tr>
						<td><h3>Item ID</h3></td>
						<td><input type="text" id="itemid'.$id.'" name="itemid" value="%d" class="config_itemid" onchange="'.$itemrefresh.'"> <span style="display:none" id="loaderid'.$id.'"><img src="img/ajax-loaders/ajax-loader-1.gif" border="0" align="absmiddle"/></span> <span id="paramid'.$id.'"></span></td>
						<td style="text-align:left"><code>ID предмета для продажи</code></td>
					</tr>	
					<tr>
						<td><h3>Count</h3></td>
						<td><input type="text" name="count" value="%d"></td>
						<td style="text-align:left"><code>Количество предметов</code></td>
					</tr>
					<tr>
						<td><h3>Max count</h3></td>
						<td><input type="text" name="maxcount" value="%d"></td>
						<td style="text-align:left"><code>Максимальное количество в ячейке</code></td>
					</tr>
					<tr>
						<td><h3>Data</h3></td>
						<td><input type="text" class="span12" name="data" value="%s"></td>
						<td style="text-align:left"><code>Октет предмета</code></td>
					</tr>
					<tr>
						<td><h3>Client size</h3></td>
						<td><input type="text" name="mask" value="%d"></td>
						<td style="text-align:left"><code>Маска предмета</code></td>
					</tr>
					<tr>
						<td><h3>Proctype</h3></td>
						<td><input type="text" name="proctype" value="%d" onkeyup="fillproctype(this.value)" onfocus="showproctype(this);"></td>
						<td style="text-align:left"><code>Привязка предмета</code></td>
					</tr>	
					<tr>
						<td><h3>Expire</h3></td>
						<td><input type="text" name="expire" value="%d"></td>
						<td style="text-align:left"><code>Срок действия временной вещи в секундах</code><br><code>Если 0 - без срока действия</code></td>
					</tr>	
					<tr>
						<td><h3>Стоимость в ЛК монетах</h3></td>
						<td><span class="gold"> <input type="text" name="costgold" value="%d" class="config_cost"></span> <span class="silver"> <input type="text" name="costsilver" value="%d" class="config_cost"></span></td>
						<td style="text-align:left"><code>Стоимость предмета</code></td>
					</tr>
					<tr>
						<td><h3>Стоимость в предметах</h3></td>
						<td>Количество: <input type="text" name="cost_item_count" value="%d" class="config_cost"> ID: <input type="text" name="cost_item_id" value="%d" class="config_itemid" id="costitemid'.$id.'" onchange="'.$costitemrefresh.'"> <span style="display:none" id="costloaderid'.$id.'"><img src="img/ajax-loaders/ajax-loader-1.gif" border="0" align="absmiddle"/></span> <span id="costparamid'.$id.'"></span></td>
						<td style="text-align:left"><code>Стоимость предмета</code></td>
					</tr>
					<tr>
						<td><h3>Remove no klan</h3></td>
						<td><input type="checkbox" name="remove_no_klan" %s></td>
						<td style="text-align:left"><code>Забирать клан-арт у персонажа если он выйдет из клана (изъятие будет совершаться после выхода персонажем из аккаунта)</code><br><code>Стоимость клан-арта будет компенсирована</code></td>
					</tr>
					<tr>
						<td><h3>Desc</h3></td>
						<td><center><textarea class="cleditor" name="desc">%s</textarea></center></td>
						<td style="text-align:left"><code>Описание для администрации</code></td>
					</tr>
					<tr>
						<td><h3>Buy count</h3></td>
						<td><input type="text" name="buycount" value="%d"></td>
						<td style="text-align:left"><code>Счетчик покупок, содержит количество купленных предметов</code><br><code>За единицу берется значение поля <b>Count</b></code></td>
					</tr>
					<tr>
						<td colspan="3"><br><input type="submit" class="btn btn-large btn-primary" value="Сохранить запись"><br><br></td>
					</tr>
					</table>
					</form>
				</div>
			</div>
		</div>
		<script>
		function InitItemID'.$id.'(){
			%s
		}

		setTimeout(InitItemID'.$id.', 100);
		</script>
	',
	$row['klanid'], $row['itemid'], $row['count'], $row['maxcount'], $row['data'], $row['client_size'], $row['proctype'], $row['expire'], $row['costgold'], $row['costsilver'], $row['cost_item_count'], $row['cost_item_id'], ($row['remove_no_klan'])?'checked':'', htmlspecialchars($row['desc']), $row['buycount'], $itemrefresh.$klanrefresh.$costitemrefresh);
}

function EditPromoCode(){
	global $db;
	if (!isset($_POST['id'])) die();
	$id = intval($_POST['id']);
	$db->query('set names utf8');
	$res = $db->query("SELECT * FROM `promo_codes` WHERE `id`=$id");
	if (!$res) die($db->error);
	if ($res->num_rows == 0) die('<div class="alert alert-error"><strong>Ошибка!</strong> Запись не найдена</div>');
	$row = mysqli_fetch_assoc($res);
	$itemrefresh = "getName('promo_item_id".$id."', 'promo_loaderid".$id."', 'promo_paramid".$id."');";	
	echo '<script>
	function SavePromo'.$id.'(){
		var f = document.createElement("form");
		f.name = "editpromoform'.$id.'";
		f.method = "POST";
		f.action = "index.php?op=act&n=69&num=0";
		var E = document.getElementById("promodata'.$id.'");
		var elems = E.getElementsByTagName("input");
		while(elems.length > 0) {			
			f.appendChild(elems[0]);
		}
		elems = E.getElementsByTagName("textarea");
		f.appendChild(elems[0]);
		var formData = new FormData(f);	
		var xhr = new XMLHttpRequest();
		xhr.open("POST", f.action);
		xhr.onreadystatechange = function() {
			if (xhr.readyState == 4) {				
				if(xhr.status == 200) {
					var txt = "error"; var txt1 = xhr.responseText;
					if (txt1 == "ok") {
						txt = "success";
						txt1 = "Запись успешно сохранена";
					}
					noty({"text":txt1,"layout":"top","type":txt});					
				}
				$("#edit_promo_'.$id.'").click();
				oTable_promo._fnAjaxUpdate();
			}
		};
		xhr.send(formData);

	}
	</script>';	
	printf('
	<div class="row-fluid">
			<div class="box span12">
				<div class="box-header well" data-original-title>
					<h2><i class="icon icon-color icon-edit"></i> Редактирование записи № '.$id.'</h2>
				</div>
				<div class="box-content">					
					<table class="table table-bordered table-striped" id="promodata'.$id.'">
					<tr>
						<td><h3>Code</h3></td>
						<td>
							<input type="hidden" name="record_id" value="'.$id.'">
							<input type="text" name="promo_code" value="%s"></td>
						<td style="text-align:left"><code>Промо код</code></td>
					</tr>
					<tr>
						<td><h3>Срок годности</h3></td>
						<td><input type="text" name="promo_expire" value="%d"></td>
						<td style="text-align:left"><code>Дата окончания срока действия кода в Unix формате</code><br><code>Если 0 - без срока действия</code></td>
					</tr>	
					<tr>
						<td><h3>Группа кодов</h3></td>
						<td><input type="text" name="promo_group" value="%d"></td>
						<td style="text-align:left"><code>0 - без группы</code><br><code>Используется для объединения кодов в группы</code><br><code>Для кодов из одной группы действует ограничение использования не более одного кода из группы на аккаунт</code></td>
					</tr>
					<tr>
						<td><h3>Бонус ЛК</h3></td>
						<td><span class="gold"> <input type="text" name="promo_gold" value="%d" class="config_cost"></span> <span class="silver"> <input type="text" name="promo_silver" value="%d" class="config_cost"></span></td>
						<td style="text-align:left"><code>Награда за код в ЛК монетах</code></td>
					</tr>
					<tr>
						<td><h3>Бонус предмет</h3></td>
						<td><input type="text" id="promo_item_id'.$id.'" name="promo_item_id" value="%d" class="config_itemid" onchange="getName(\'promo_item_id'.$id.'\', \'promo_loaderid'.$id.'\', \'promo_paramid'.$id.'\');"> <span style="display:none" id="promo_loaderid'.$id.'"><img src="img/ajax-loaders/ajax-loader-1.gif" border="0" align="absmiddle"/></span> <span id="promo_paramid'.$id.'"></span></td>
						<td style="text-align:left"><code>ID предмета награды за код</code></td>
					</tr>
					<tr>
						<td><h3>Count</h3></td>
						<td><input type="text" name="promo_item_count" value="%d"></td>
						<td style="text-align:left"><code>Количество предметов</code></td>
					</tr>
					<tr>
						<td><h3>Max count</h3></td>
						<td><input type="text" name="promo_item_maxcount" value="%d"></td>
						<td style="text-align:left"><code>Максимальное количество в ячейке</code></td>
					</tr>
					<tr>
						<td><h3>Data</h3></td>
						<td><input type="text" class="span12" name="promo_item_data" value="%s"></td>
						<td style="text-align:left"><code>Октет предмета</code></td>
					</tr>
					<tr>
						<td><h3>Client size</h3></td>
						<td><input type="text" name="promo_item_client_size" value="%d"></td>
						<td style="text-align:left"></td>
					</tr>
					<tr>
						<td><h3>Proctype</h3></td>
						<td><input type="text" name="promo_item_proctype" value="%d" onkeyup="fillproctype(this.value)" onfocus="showproctype(this);"></td>
						<td style="text-align:left"><code>Привязка предмета</code></td>
					</tr>	
					<tr>
						<td><h3>Expire</h3></td>
						<td><input type="text" name="promo_item_expire" value="%d"></td>
						<td style="text-align:left"><code>Срок действия предмета в секундах</code><br><code>Если 0 - без срока действия</code></td>
					</tr>
					<tr>
						<td><h3>Многоразовый</h3></td>
						<td><center><input name="promo_multi_user" type="checkbox" %s data-no-uniform="true" class="iphone-toggle"></center></td>
						<td style="text-align:left"><code>Если включено - код может быть использован несколькими игроками (не более одного использования на аккаунт)</code><br><code>а в поле Used UserID будет записываться количество использований, а не ID аккаунта</code></td>
					</tr>
					<tr>
						<td><h3>Used UserID</h3></td>
						<td><input type="text" name="promo_used_userid" value="%d"></td>
						<td style="text-align:left"><code>UserID использовавшего код</code><br><code>Если многоразовый - количество использований</code></td>
					</tr>
					<tr>
						<td><h3>Desc</h3></td>
						<td><center><textarea name="promo_desc">%s</textarea></center></td>
						<td style="text-align:left"><code>Описание для администрации</code></td>
					</tr>					
					<tr>
						<td colspan="3"><br><input type="button" class="btn btn-large btn-primary" onclick="SavePromo'.$id.'()" value="Сохранить запись"><br><br></td>
					</tr>
					</table>
					<script>
					function InitPromoItemID'.$id.'(){
						%s
					}
			
					setTimeout(InitPromoItemID'.$id.', 100);
					</script>					
				</div>
			</div>
		</div>		
	', 
	$row['code'], $row['expire'], $row['group'], $row['bonus_money_gold'], $row['bonus_money_silver'], $row['bonus_item_id'], $row['bonus_item_count'], $row['bonus_item_max_count'], $row['bonus_item_data'], $row['bonus_item_client_size'], $row['bonus_item_proctype'], $row['bonus_item_expire'], ($row['multi_user'])?'checked':'', $row['used_userid'], htmlspecialchars($row['desc']), $itemrefresh);
	die();
}

function GetUserID($roleid)
{
	//if ($ProtocolVer < 27) return (floor($roleid/16)*16);
	$b = GetRoleBase($roleid);	
	return $b['value']['userid'];
}

function GetVoteFromBase($vote, $tbl)
{
	global $db;
	$sql = sprintf("SELECT * FROM `".$tbl."` WHERE `vote_id`=%d", $vote['vote_id']);
	$res = $db->query($sql);
	if (!$res) die($db->error);
	if ($res->num_rows) return true; else return false;
}

function AddAuto($vote, $login, $userid, $status, $tbl, $points = 0, $send_item = 0)
{
	global $db;
	$sql = sprintf("INSERT INTO `".$tbl."` (`vote_id`,`data`,`ip`,`name`,`login`,`userid`,`vote_type`,`points`,`send_item`,`status`) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')", $db->real_escape_string($vote['vote_id']), $db->real_escape_string($vote['data']), $db->real_escape_string($vote['ip']), $db->real_escape_string($vote['login']), $db->real_escape_string($login), $db->real_escape_string($userid), $db->real_escape_string($vote['vote_type']), $db->real_escape_string($points), $db->real_escape_string($send_item), $db->real_escape_string($status));
	$result = $db->query($sql);
	if (!$result) die($db->error);
}

function GiveMoney($login, $id, $vote, $senditem, $item1, $item2, $item3, $item4, $reason, $tbl, $nickname = false)
{
	// Status 0 - not found, 1 - Success, 2 - Limit IP, 3 - Limit login, 4 - No Active Role, 5 - Error send item, 6 - Mail Limit, 7 - Success NickName, 8 - No VK, 9 - No Steam
	global $cnt, $fp, $q, $gpoint, $spoint, $db;
	ShowCost($vote['points']);
	$CashLeft = GetCashFromAcc($id,$login);
	$i = $CashLeft[2];
	$l = $CashLeft[3];
	$code = -1;
	$send_item = 0;
	$points = 0;
	if ($gpoint > 0 || $spoint > 0) {
		if ($login == '') $sql = "update `users` set lkgold=lkgold+".$gpoint.", lksilver=lksilver+".$spoint." where `ID`='".$db->real_escape_string($id)."'"; else
		$sql = "update `users` set lkgold=lkgold+".$gpoint.", lksilver=lksilver+".$spoint." where `name`='".$db->real_escape_string($login)."'";	
		$result = $db->query($sql);
		if ($db->errno > 0) {
			echo $db->error()." No\n";
			$code = 0;
		} else {
			$points = $vote['points'];
			$CashLeft = GetCashFromAcc($id,$login);
			$i = $CashLeft[2];
			$l = $CashLeft[3];
			$sql = "INSERT INTO `lklogs` (`userid`,`data`,`ip`,`gold`,`silver`, `gold_rest` ,`silver_rest`,`desc`) VALUES (".$CashLeft[2].",now(),'".$db->real_escape_string($vote['ip'])."','-".$gpoint."','-".$spoint."', ".$CashLeft[0].", ".$CashLeft[1].",'Голосование в ".$reason." ".$vote['vote_id']."')";	
			$db->query($sql);
			$code = 1;
			echo " Ok\n";
		}
	}
	if ($senditem) {
		$roleid = GetActiveRole($i);
		if ($roleid) {
			if ($vote['vote_type'] == 1) $q = SysSendMail(0, 32, 3, $roleid, 'Бонус', 'За голосование на '.$reason.'.', $item1, 0); else
			if ($vote['vote_type'] == 2) $q = SysSendMail(0, 32, 3, $roleid, 'Бонус', 'За голосование на '.$reason.'.', $item2, 0); else
			if ($vote['vote_type'] == 3) $q = SysSendMail(0, 32, 3, $roleid, 'Бонус', 'За голосование на '.$reason.'.', $item3, 0); else
			if ($vote['vote_type'] == 4) $q = SysSendMail(0, 32, 3, $roleid, 'Бонус', 'За голосование на '.$reason.'.', $item4, 0);
			if ($q['retcode'] != 0) {
				if ($code == -1) {
					if ($q['retcode'] == 217) $code = 6; else
					$code = 5;
				}
			} else
			$send_item = 1;
			if ($code == -1) $code = 1;
			echo " Send item ok\n";
		} else {
			$code = 4;
			echo " Active role not found\n";
		}
	}
	if ($code == 1 && $nickname) $code = 7;
	AddAuto($vote, $l, $i, $code, $tbl, $points, $send_item);
	$cnt++;
}

function CheckLoginTop($login)
{
	global $db;
	$sql = sprintf("SELECT `ID` FROM `users` WHERE `name`='%s'", $db->real_escape_string($login));
	$result = $db->query($sql);
	if (!$result) die($db->error);
	return $result->num_rows;
}

function CheckVkSteamTop($login, $top_bonus_vk_only, $top_bonus_steam_only)
{
	global $db;
	$sql = sprintf("SELECT `vkid`, `steamid` FROM `users` WHERE `name`='%s'", $db->real_escape_string($login));
	$result = $db->query($sql);
	if (!$result) die($db->error);
	if (!$result->num_rows) return 0;
	$row = mysqli_fetch_assoc($result);
	if (!$row['vkid'] && $top_bonus_vk_only) return 8;
	if (!$row['steamid'] && $top_bonus_steam_only) return 9;
	return 0;
}

function UnpackItem($i)
{
	global $Structures;
	$pp = new Protocols();
	$item = $pp->unmarshal(my_hex2bin($i), $Structures['GRoleInventory']);
	unset($pp);
	if ($item['expire_date']) $item['expire_date'] += time();
	return $item;
}

function TopBonus($is_mmotop)
{
	global $db, $cnt;
	extract($_POST);
	$item1 = InitItem();
	$item2 = InitItem();
	$item3 = InitItem();
	$item4 = InitItem();
	if ($is_mmotop) {
		$statlink = $mmotop_statlink;
		$tbl = 'mmotop_data';
		$sendmsg = $send_mmotop_message;
		$msg = $mmotop_message;
		$senditem = $send_mmotop_bonusitem;
		$item1 = UnpackItem($mmotop1_item); $item2 = UnpackItem($mmotop2_item); $item3 = UnpackItem($mmotop3_item); $item4 = UnpackItem($mmotop4_item);	
		$cost1 = $mmotop_cost1; $cost2 = $mmotop_cost2; $cost3 = $mmotop_cost3; $cost4 = $mmotop_cost4;
		$pattern = '/^(\d+)\s+(\d{2}\.\d{2}\.\d{4})\s+(\d{2}:\d{2}:\d{2})\s+(\d{1,3}(?:\.\d{1,3}){3})\s+(\S+)\s+(\d+)$/u';
		$explodetext = chr(10);
		$reason = 'mmotop';
	} else {		
		$statlink = $qtop_statlink;
		$tbl = 'qtop_data';
		$sendmsg = $send_qtop_message;
		$msg = $qtop_message;
		$senditem = $send_qtop_bonusitem;
		$item1 = UnpackItem($qtop1_item); $item2 = UnpackItem($qtop2_item);		
		$cost1 = $qtop_cost1; $cost2 = $qtop_cost2;
		$pattern = "/(\d+)\|\|(\d+\.\d+\.\d+)\|\|(\d+:\d+:\d+)\|\|(\d+\.\d+\.\d+\.\d+)\|\|(.+)\|\|(\d+)/";
		$explodetext = chr(13).chr(10);
		$reason = 'qtop';
	}
	if ($statlink == '') die('Stat link is empty');
	if (!$db->query('set names utf8')) die($db->error);
	if ($top_log_lifetime > 0){	
		// Чистка старых логов
		$dm = @localtime(time(), true);
		$dt = @mktime($dm['tm_hour'], $dm['tm_min'], $dm['tm_sec'], $dm['tm_mon']+1-$top_log_lifetime, $dm['tm_mday'], 1900 + $dm['tm_year']);
		$sql = sprintf("DELETE FROM `%s` WHERE `data`<='%s'", $tbl, @date('Y-m-d H:i:s', $dt));
		if (!$db->query($sql)) die($db->error);		
	}
	$ch = curl_init();
	if (!$ch) die('Curl init error');
	curl_setopt($ch, CURLOPT_URL, $statlink);
	curl_setopt($ch, CURLOPT_FAILONERROR, 1);  
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_TIMEOUT, 15);
	$data = curl_exec($ch);
	if ($data === false) die(curl_error($ch));
	curl_close($ch);
	if ($data == '') die('Stat is empty');
	$data = explode($explodetext, $data);
	$cnt = 0;
	$vote_ip_array = array();			// Массив с счетчиками айпи
	$vote_login_array = array();			// Массив с счетчиками логинов
	$vote = array();	
	foreach($data as $index => $val){

		if (!preg_match($pattern, $val, $v)) {
			file_put_contents('donate_reward.log', "❌ PATTERN FAIL: [$val]\n", FILE_APPEND);
			continue;
		}
		file_put_contents('donate_reward.log', "✅ PATTERN OK: ID={$v[1]}, login={$v[5]}, vote_type={$v[6]}\n", FILE_APPEND);

		$vote['vote_id'] = $v[1];
		$d = @strtotime($v[2].' '.$v[3]); 	
		$vote['data'] = @date('Y-m-d H:i:s', $d);
		$dm = @localtime(time(), true);
		$dt = @mktime($dm['tm_hour'], $dm['tm_min'], $dm['tm_sec'], $dm['tm_mon']+1, $dm['tm_mday'] - $num_day_process, 1900 + $dm['tm_year']);
		
		if ($d < $dt) {
			file_put_contents('donate_reward.log', "⛔ SKIP: голос устарел vote_id={$v[1]} — дата {$v[2]} {$v[3]} < порог " . date('Y-m-d H:i:s', $dt) . "\n", FILE_APPEND);
			continue;
		}		
		$vote['ip'] = $v[4];
		$vote['login'] = $v[5];
		$vote['vote_type'] = $v[6];
		if ($vote['vote_type']==1) $vote['points'] = $cost1; else
		if ($vote['vote_type']==2) $vote['points'] = $cost2; else
		if ($vote['vote_type']==3) $vote['points'] = $cost3; else
		if ($vote['vote_type']==4) $vote['points'] = $cost4;
		$voteday = substr($v[2], 0, 2);		// день месяца голосования
		$found = false;
		$nickname = false;
		file_put_contents('donate_reward.log', print_r($vote, true), FILE_APPEND);
		$login = mb_strtolower($vote['login']);
		if (!CheckLoginTop($login)) {	// Ищем по нику
			$f = GetRoleId($vote['login']);
			if ($f['roleid'] == -1) {
				$found = false;
			} else {
				$id = GetUserID($f['roleid']);
				$CashLeft = GetCashFromAcc($id, '');
				if ($CashLeft[3]) {
					$login = $CashLeft[3];
					$found = true;
					$nickname = true;
				}
			}			
		} else $found = true;
		if (!isset($vote_ip_array[$voteday])) $vote_ip_array[$voteday] = array();
		if (!isset($vote_ip_array[$voteday][$vote['ip']])) $vote_ip_array[$voteday][$vote['ip']] = 0;
		if (!isset($vote_login_array[$voteday])) $vote_login_array[$voteday] = array();
		if (!isset($vote_login_array[$voteday][$login])) $vote_login_array[$voteday][$login] = 0;
		if ($vote['vote_type'] == 1){		// Если обычное голосование добавляем в статистику лимита			
			$vote_ip_array[$voteday][$vote['ip']] += 1;			
			$vote_login_array[$voteday][$login] += 1;
		}
		if (GetVoteFromBase($vote, $tbl)) {
			file_put_contents('donate_reward.log', "⛔ SKIP: Уже в базе vote_id={$vote['vote_id']}\n", FILE_APPEND);
			continue;
		}
		$stat = 0;
		if ($vote['vote_type'] == 1) {
			if ($vote_ip_array[$voteday][$vote['ip']] > $max_votes_from_ip) $stat = 2;
			if ($vote_login_array[$voteday][$login] > $max_votes_from_login) $stat = 3;		
		}
		if ($stat == 0) $stat = CheckVkSteamTop($login, $top_bonus_vk_only, $top_bonus_steam_only);
		if ($stat > 0) {
			file_put_contents('donate_reward.log', "⛔ SKIP: Ограничение stat=$stat для login=$login, vote_id={$vote['vote_id']}\n", FILE_APPEND);
			$CashLeft = GetCashFromAcc(0, $login);
			AddAuto($vote, $CashLeft[3], $CashLeft[2], $stat, $tbl, 0, 0);
			echo($vote['vote_id']." ".$vote['login']." Limit reached\n");
			continue;
		}		
		echo($vote['vote_id']." ".$vote['login']);
		if (!$found) {	// Ищем по нику
			file_put_contents('donate_reward.log', "⛔ SKIP: Не найден login={$vote['login']} → AddAuto\n", FILE_APPEND);
			AddAuto($vote, '', 0, 0, $tbl, 0, 0);
			echo " Not found\n";			
		} else {

			// Получаем бонус на голосование в процентах (type = 'vote')
			$bonus_percent = 0;
			$sql = sprintf(
				"SELECT percent FROM user_bonus_effects WHERE login = '%s' AND type = 'vote' AND expire_at > NOW() ORDER BY percent DESC LIMIT 1",
				$db->real_escape_string($login)
			);
			$res = $db->query($sql);
			if ($res && $res->num_rows > 0) {
				$row = $res->fetch_assoc();
				$bonus_percent = (int)$row['percent'];
			}

			if (!empty($vote['points']) && $bonus_percent > 0) {
				if (strpos($vote['points'], '|') !== false) {
					// Формат вида "X|Y" (золото|серебро)
					list($gold, $silver) = explode('|', $vote['points']);
					$gold = (int)$gold;
					$silver = (int)$silver;
					$gold += floor($gold * $bonus_percent / 100);
					$vote['points'] = sprintf('%d|%d', $gold, $silver);
				} else {
					// Обычное число
					$vote['points'] = (int)$vote['points'];
					$vote['points'] += floor($vote['points'] * $bonus_percent / 100);
				}
			}

			GiveMoney($login, 0, $vote, $senditem, $item1, $item2, $item3, $item4, $reason, $tbl, $nickname);
		}
	}	
	if ($cnt > 0 && $sendmsg) {
		broadcast(9,0,32,0,sprintf($msg, $cnt));
	}
	die();
}

function AddLoginLog()
{
	global $db;
	extract($_POST);
	$res = $db->query("INSERT INTO `login_log` (`data`,`ip`,`userid`,`login`,`action`) VALUES (now(),'".$db->real_escape_string($ip)."','".$db->real_escape_string($userid)."','".$db->real_escape_string($login)."','".$db->real_escape_string($action)."')");	
	die();
}

function FillKlanData()
{
	global $db, $Structures;
	set_time_limit(490);
	// Инициализируем структуры
	$p = new Protocols();
	unset($p);
	// Обновляем информацию о кланах
	$db->query("SET NAMES utf8");
	$db->query("TRUNCATE TABLE `klan`;");
	if ($db->errno > 0) die($db->error);
	// Получаем список кланов пакетами
	$res = WalkTable('faction', ['val' => 'int'], $Structures['GFactionInfo']);
	foreach ($res as $i => $val){		
		if ($val['value']['master'] != 0) {
			$b = GetRoleBase($val['value']['master']);
			$name = $db->real_escape_string($b['value']['name']);
		} else $name = '';
		$members = 0;
		foreach ($val['value']['member'] as $i1 => $val1)
		{
			$data = DBFamilyGet($val1['fid']);
			$members += count($data['value']['member']);
		}		
		$sql = "INSERT INTO `klan` (`id` ,`name` ,`desc` ,`level` ,`masterid` ,`mastername` ,`members`) VALUES ('".$val['value']['fid']."', '".$db->escape_string($val['value']['name'])."', '".$db->real_escape_string($val['value']['announce'])."', '".$val['value']['level']."', '".$val['value']['master']."', '$name','".$members."');";
		$db->query($sql);
		if ($db->errno > 0) die($db->error);
	}
	
	// Обновляем информацию о территориях
	$t = DBTerritoryListLoad([1]);
	foreach ($t['store']['tlist'] as $i => $val){
		if ($val['owner'] != 0){
			switch ($val['assis_drawn_num']){
				case 2:$sql="update `klan` set terr3=terr3+1 where `id`=".$val['owner'];break;
				case 4:$sql="update `klan` set terr2=terr2+1 where `id`=".$val['owner'];break;
				case 8:$sql="update `klan` set terr1=terr1+1 where `id`=".$val['owner'];break;
				default: die('unknown territory level '.$val['assis_drawn_num']);
			}
			$db->query($sql);
			if ($db->errno > 0) die($db->error);
		}
	}
}

function find_and_remove_items(&$it, $itemsid, $role_faction, $id, $rid, $rname, $desc, $is_banned)
{
	global $db;
	$cnt = 0;
	foreach ($it as $i => $val){
		if (array_key_exists($val['id'], $itemsid)) {
			if ($itemsid[$val['id']]['klanid'] != $role_faction[$rid]) {
				if ($is_banned==0) isonline($id, $rid, 16, false);
				$cnt+=$val['count'];
				unset($it[$i]);
				$item = InitItem();
				if ($itemsid[$val['id']]['cost_item_id'] > 0) {
					$item['id'] = $itemsid[$val['id']]['cost_item_id'];
					$item['count'] = $itemsid[$val['id']]['cost_item_count'];
					$item['max_count'] = $itemsid[$val['id']]['cost_item_count'];
				}
				$gpoint = $itemsid[$val['id']]['costgold']; $spoint = $itemsid[$val['id']]['costsilver'];
				GiveGold($id, $gpoint, $spoint, 'Изъятие клан-арта', $ip);				
				$q = SysSendMail(0, 32, 3, $rid,'Изъятие клан-арта','Вам была возвращена стоимость клан-арта', $item, 0);
				broadcast(9, 0, 40, 0, sprintf("У игрока %s только что был изъят клан-арт", $rname));
				printf("Remove klan-art from %s, Role: %d %s, RemoveItemID: %d, SendItemID: %d\n", $desc, $rid, $rname, $val['id'], $itemsid[$val['id']]['cost_item_id']);
				$cnt++;
			}
		}
	}
	return $cnt;
}

function CheckSimilarIP($userid1, $userid2)
{
	global $db;
	$sql = "SELECT DISTINCT `ip` FROM `login_log` WHERE `ip`<>'Hidden' AND `ip`<>'' AND `userid`=%d";
	$ips = array();
	$res = $db->query(sprintf($sql, $userid1));
	if (!$res) die($db->error);
	while ($row = mysqli_fetch_assoc($res)){
		array_push($ips, $row['ip']);
	}
	$res = $db->query(sprintf($sql, $userid2));
	if (!$res) die($db->error);
	while ($row = mysqli_fetch_assoc($res)){
		if (in_array($row['ip'], $ips)) {
			return $row['ip'];
		}
	}	
	return false;
}

function UpdateUserInfo()
{
	global $db;
	if (!isset($_POST['id'])) die('Wrong user id'); else $id = $_POST['id'];
	if ($id == 0) die('UserID 0');
	$db->query("SET NAMES utf8");	
	$r = GetUserRoles($id);
	if ($r['retcode'] != 0) die('<br>GetUserRoles error '.$r['retcode']);
	$num = 0; $rolenames = array();
	$role_faction = array();
	if (!count($r['data'])) die('No roles found on account');
	foreach ($r['data'] as $i => $val){		
		$rid = $val['id'];			
		$role_faction[$rid] = AddRoleToTop($rid);
		$num++;
		array_push($rolenames, $val['name']);
	}
	echo "$num roles info updated: ".implode(", ", $rolenames)."\n";

	// Проверка выдачи бонуса рефералу	
	$ref_level_bonus_enabled = $_POST['ref_level_bonus_enabled'];
	if ($ref_level_bonus_enabled) {
		$ref_require_rb = $_POST['ref_require_rb'];
		$ref_require_level = $_POST['ref_require_level'];
		$item = unserialize($_POST['ref_item']);		
		if ($item['expire_date']) $item['expire_date'] += time();
		$sql="SELECT `referal`,`ref_status` FROM `users` WHERE `ID`=".$id;
		$result = @$db->query($sql);
		if (!$result) die($db->error);
		if ($result->num_rows){
			$row = mysqli_fetch_assoc($result);
			if ($row['ref_status'] == 0 && $row['referal'] != 0) {
				$a = GetRefData($id);
				$refacc = $a[0];
				$refpers = $a[1];
				$mt = $a[2];
				$rb = $a[3];
				if ($refacc > 0 && $refpers > 0) {			
					$fnd = '';
					foreach ($r['data'] as $i => $val){
						$r1 = GetRole($val['id'], 63);
						if ($r1['value']['status']['level'] >= $ref_require_level)
						{
							if ($ref_require_rb)
							{
								if ($r1['value']['status']['reborndata']) $fnd = $val['name'];
							} else $fnd = $val['name'];
						}
					}
					if ($fnd != ''){
						$checkIp = CheckSimilarIP($id, $refacc);
						if ($checkIp) {
							$db->query('UPDATE `users` SET `ref_status`=2 WHERE `ID`='.$id);
							printf("No bonus send to referal (IP match): %s %s - %s %s (%s)\n", $id, $fnd, $refacc, $refpers, $checkIp);
							$txt = 'Реферальный бонус за реферала №'.($id/16-1).' не начислен, совпадение IP входа '.$checkIp;
							AddLKLogs($id, '', 0, 0, $txt);
							AddLKLogs($refacc, '', 0, 0, $txt);
						} else {
							$q = SysSendMail(0, 32, 3, $refpers, 'Бонус', sprintf('За приглашенного игрока %s, который достиг '.$ref_require_level.' уровня', $fnd), $item, 0);
							$err = '';
							if ($q['retcode'] == 217) $err = ' fail, mailbox is full'; else
							if ($q['retcode'] == 0) $db->query('UPDATE `users` SET `ref_status`=1 WHERE `ID`='.$id); else
							$err = ' fail, retcode '.$q['retcode'];
							printf("\nSend referal bonus%s: %s %s - %s %s\n", $err, $id, $fnd, $refacc, $refpers);
							if (!$err)
							{
								$r1 = GetRole($refpers, 63);
								broadcast(9,0,40,0,sprintf("%s получил бонус за приглашенного игрока %s", $r1['value']['name'], $fnd));
							}
						}
					}
				}
			}
		}		
	}
	// Проверка и изъятие запрещенных клан-артов	
	$cnt = 0;
	$itemsid = array();	
	$sql="SELECT * FROM `klan_items` WHERE `remove_no_klan`=1";
	$result = @$db->query($sql);
	if (!$result) die($db->error);	
	if ($result->num_rows){
		// Строим список артов
		while ($row = mysqli_fetch_assoc($result)){
			$itemsid[$row['itemid']] = $row;
		}		
		foreach ($r['data'] as $i => $val){		
			$rid = $val['id'];
			$rname = $val['name'];
			$rd = GetRoleData($rid);
			if ($rd['retcode'] != 0) die('Read role '.$id.' error');
			$cnt = 0;
			$cnt+=find_and_remove_items($rd['value']['pocket']['items'], $itemsid, $role_faction, $id, $rid, $rname, 'pocket', $cnt);
			$cnt+=find_and_remove_items($rd['value']['pocket']['pocket_items'], $itemsid, $role_faction, $id, $rid, $rname, 'pocket_items', $cnt);
			$cnt+=find_and_remove_items($rd['value']['pocket']['fashion'], $itemsid, $role_faction, $id, $rid, $rname, 'fashion', $cnt);
			$cnt+=find_and_remove_items($rd['value']['storehouse']['items'], $itemsid, $role_faction, $id, $rid, $rname, 'storehouse', $cnt);
			$cnt+=find_and_remove_items($rd['value']['storehouse']['items2'], $itemsid, $role_faction, $id, $rid, $rname, 'storehouse2', $cnt);
			$cnt+=find_and_remove_items($rd['value']['pocket']['equipment'], $itemsid, $role_faction, $id, $rid, $rname, 'equipment', $cnt);
			if ($cnt > 0) {
				$rd = PutRoleData($rid, $rd['value']);
				if ($rd['retcode'] != 0) die('Role '.$rid.' save error '.$rd['retcode']."\n");
			}
		}
	}
	die();
}

function MakeNames()
{
	global $db;
	$fname = 'items_ext.txt';
	if (!file_exists($fname)) die('No input file');
	$handle = @fopen($fname, "r");
	$q = 0; $q1 = 0;
	if (!$handle) die('Error open file');
	$db->query("SET NAMES utf8");
	$res = $db->query('TRUNCATE TABLE `shop_names`');
	if (!$res) die($db->error);
	while (!feof($handle)) {
		$line = fgets($handle);
		if ($line == '') continue;
		$a = explode( "|", $line);
		if (count($a)==4) {
			$sql = sprintf("INSERT INTO `shop_names` (`id`,`name`,`icon`,`list`) VALUES (%d, '%s', '%s', %d)", $a[0], $db->real_escape_string(trim($a[1])), $db->real_escape_string(trim($a[2])), trim($a[3]));
			$db->query($sql);
			if ($db->errno != 0 ) {
				echo $sql.'<br>';
				die($db->error);
			}
			$q++;
		} else {
			printf("Error line: %s<br>", $line);
			$q1++;
		}
	}
	fclose($handle);
	printf("Success lines: %d, Error lines: %d", $q, $q1);
}

function GetErrorTxt($num,$custom='')
{
	$err = '<div class="alert alert-error"><button type="button" class="close" data-dismiss="alert">×</button><strong>Ошибка!</strong> %s</div>';
	$succ = '<div class="alert alert-success"><button type="button" class="close" data-dismiss="alert">×</button><strong>Готово!</strong> %s</div>';
	switch ($num){
		case 88: return sprintf($err, $custom);
		case 89: return sprintf($succ, $custom);
	}
}

function CheckArchive($fn)
{
	global $path, $answer, $ttt;
	$error = '';
	$res = true;
	$error = '';
	$archive = new PclZip($fn);
	$files = $archive->listContent();
	$addmsg = false;
	if (!is_array($files) || count($files)==0 || !file_exists($fn)) {
		$error = 'Ошибка открытия файла '.$fn;
		$res = false;
	} else {
		foreach ($files as $i => $val)
		{
			if (file_exists($val['filename'])) {
				if (!is_writable($val['filename'])) {
					$res = false;
					$error .= 'Нет прав на запись файла <code>'.$val['filename'].'</code><br>';
					$addmsg = true;
				}
			} else {
				$data = pathinfo($val['filename']);			
				$p = (isset($data['dirname']) && $data['dirname'] != '.')?$data['dirname']:'';
				$p = $path.'/'.$p;
				if (!is_writable($p)) {
					$res = false;
					$error .= 'Нет прав на запись в папку <code>'.$p.'</code><br>';
					$addmsg = true;
				}
			}
		}
		if ($addmsg) {
			$userinfo = posix_getpwuid(posix_getuid());
			$error .= '<br>Выполните команду <code>chown -R '.$userinfo['name'].' '.$path.'</code> (или установите права на запись) и попробуйте запустить обновление ещё раз';
		}
	}
	if (!$res) $answer.=GetErrorTxt(88, $error);
	return $res;
}

function TrySave($fn, $data)
{
	global $answer, $ttt;
	$fp = @fopen($fn, "w");
	if (!$fp) {
		$answer.=GetErrorTxt(88, 'Ошибка создания файла '.$fn);
		return false;
	}
	fwrite($fp, $data);
	fclose($fp);
	return true;
}

function Update()
{
	global $path, $db, $answer, $ttt;
	$answer = ''; $ttt = '';
	if (!isset($_POST['server_sql']) || !isset($_POST['server_zip'])) die('Input data fail');	
	$data = pathinfo($_SERVER['SCRIPT_FILENAME']);
	$path = (isset($data['dirname']) && $data['dirname'] != '.')?$data['dirname']:'';
	$state_head = '<div class="clear"></div>
	<div class="box-header well" data-original-title>
		<h2><i class="%s"></i> %s</h2>
		<div class="box-icon">
			<a href="#" class="btn btn-minimize btn-round"><i class="icon-chevron-down"></i></a>
		</div>
	</div>
	<div class="box-content" style="display:none">';
	$state_footer = '</div><div class="clear"></div>';
	if ($_POST['server_zip'] != '') {			
		$fn = $path.'/server.zip';
		if (!TrySave($fn, base64_decode($_POST['server_zip']))) MakeUpdateAnswer(0, $answer, $ttt);
		if (!CheckArchive($fn)) {
			if (!unlink($fn)) $answer.=GetErrorTxt(88, 'Ошибка удаления файла '.$fn);
			MakeUpdateAnswer(0, $answer, $ttt);
		}
		$archive = new PclZip($fn);
		$r = $archive->extract(PCLZIP_OPT_REPLACE_NEWER);
		$succ_files = 0; $fail_files = 0;
		$answer.=sprintf($state_head, 'icon-folder-open', 'Обновление файлов');
		if (!unlink($fn)) $answer.=GetErrorTxt(88, 'Ошибка удаления файла '.$fn);
		if ($r == 0) {
			$answer.=GetErrorTxt(88, $archive->errorInfo(true));				
			MakeUpdateAnswer(0, $answer, $ttt);
		} else {
			$ttt .= "\tОбновление файлов\n";
			foreach ($r as $i1 => $val1){
				if ($val1['status']=='ok' || $val1['status'] == 'already_a_directory') {
					if ($val1['folder'] == 0) $succ_files++; 
					$num = 89;
				} else {
					if ($val1['folder'] == 0) $fail_files++;
					$num = 88;
				}
				if ($val1['folder'] == 1) $f = 'Папка: '; else $f = 'Файл: ';
				$f .= $val1['filename'];
				$ttt .= sprintf("%s Status: %s\n", $f, $val1['status']);
				$answer.=GetErrorTxt($num, sprintf('<code>%s</code> Status: <code>%s</code>', $f, $val1['status']));
			}				
		}
		$answer.=$state_footer;
		if ($fail_files > 0) $answer.=sprintf('<div class="alert alert-error">Обновлено файлов: <span class="label label-inverse">%d</span><span class="label label-success">успешно</span>, <span class="label label-inverse">%d</span><span class="label label-important">с ошибкой</span></div>', $succ_files, $fail_files); else $answer.=sprintf('<div class="alert alert-success">Обновлено файлов: <span class="label label-inverse">%d</span><span class="label label-success">успешно</span></div>', $succ_files);
	}
	if ($_POST['server_sql'] != '') {
		$db->query('set names utf8');
		$answer.=sprintf($state_head, 'icon-hdd', 'SQL запросы в базу данных');
		$ttt .= "\tSQL запросы в базу данных\n";
		$succ_sql = 0; $fail_sql = 0;
		$server_sql = explode("\n", $_POST['server_sql']);
		foreach ($server_sql as $i1 => $val1){
			$res = $db->query($val1);
			if (!$res) {
				$fail_sql++;
				$answer.=GetErrorTxt(88, sprintf('<code>%s</code><br>Запрос: <code>%s</code>', $db->error, $val1));
				$ttt .= sprintf("Ошибка: %s\nЗапрос: %s\n", $db->error, $val1);
			} else {
				$answer.=GetErrorTxt(89, sprintf('<code>%s</code>', $val1));
				$ttt .= sprintf("%s\n", $val1);
				$succ_sql++;
			}
		}
		$answer.=$state_footer;
		if ($fail_sql > 0) $answer.=sprintf('<div class="alert alert-error">Выполнено запросов в базу данных: <span class="label label-inverse">%d</span><span class="label label-success">успешно</span>, <span class="label label-inverse">%d</span><span class="label label-important">с ошибкой</span></div>', $succ_sql, $fail_sql); else $answer.=sprintf('<div class="alert alert-success">Выполнено запросов в базу данных: <span class="label label-inverse">%d</span><span class="label label-success">успешно</span></div>', $succ_sql);		
	}
	MakeUpdateAnswer(1, $answer, $ttt);
}

function TopStat()
{
	global $db;
	$months = array('', 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь');
	if (!isset($_POST['tblname'])) die();
	$tblname = $db->real_escape_string($_POST['tblname']);
	$curmonth = @date('n');
	$curday = @date('j');
	// Дневная статистика
	$d_txt = [];
	$dd = $curday;
	$m = $curmonth;
	$day_sum = array();
	for ($a=$dd; $a>0; $a--){
		$tmp = sprintf("WHERE `data`>='%s' AND `data`<'%s'", @date('Y-m-d H:i:s', mktime(0,0,0,$m,$a,@date('Y'))), @date('Y-m-d H:i:s', mktime(0,0,0,$m,$a+1,@date('Y'))));
		// Total
		$result = $db->query('SELECT count(*) as `cnt` FROM `'.$tblname.' '.$tmp);
		$row = mysqli_fetch_assoc($result);
		$d_txt[$a] = sprintf('["%s", %d]', $a, $row['cnt']);
		$day_sum[$a] = array();
		$day_sum[$a]['total'] = $row['cnt'];
		// Success
		$result = $db->query('SELECT count(*) as `cnt` FROM `'.$tblname.' '.$tmp.' AND `status`=1');
		$row = mysqli_fetch_assoc($result);
		$day_sum[$a]['success'] = $row['cnt'];
		// Fail
		$result = $db->query('SELECT count(*) as `cnt` FROM `'.$tblname.' '.$tmp.' AND `status`<>1');
		$row = mysqli_fetch_assoc($result);
		$day_sum[$a]['fail'] = $row['cnt'];
	}
	$d_txt = array_reverse($d_txt);	
	// Месячная статистика	
	$m_txt = [];
	$month_sum = array();
	for ($a=$curmonth; $a>0; $a--){
		$tmp = sprintf("WHERE `data`>='%s' AND `data`<'%s'", @date('Y-m-d H:i:s', mktime(0,0,0,$a,1,@date('Y'))), @date('Y-m-d H:i:s', mktime(0,0,0,$a+1,1,@date('Y'))));
		// Total
		$result = $db->query('SELECT count(*) as `cnt` FROM `'.$tblname.' '.$tmp);
		$row = mysqli_fetch_assoc($result);
		$m_txt[$a] = sprintf('["%s", %d]', $months[$a], $row['cnt']);
		$month_sum[$a] = array();
		$month_sum[$a]['total'] = $row['cnt'];
		// Success
		$result = $db->query('SELECT count(*) as `cnt` FROM `'.$tblname.' '.$tmp.' AND `status`=1');
		$row = mysqli_fetch_assoc($result);
		$month_sum[$a]['success'] = $row['cnt'];
		// Fail
		$result = $db->query('SELECT count(*) as `cnt` FROM `'.$tblname.' '.$tmp.' AND `status`<>1');
		$row = mysqli_fetch_assoc($result);
		$month_sum[$a]['fail'] = $row['cnt'];
	}
	$m_txt = array_reverse($m_txt);
	$answ = array(
		'day_sum' => $day_sum,
		'month_sum' => $month_sum,
		'd_txt' => $d_txt,
		'm_txt' => $m_txt
	);
	MakeAnswer($answ);
}

function CleanKlanCache()
{
	global $db;
	$res = $db->query('TRUNCATE TABLE `klan_pic`');
	if (!$res) die($db->error); else echo 'ok';
}

function RID2UID()
{
	if (!isset($_POST['roleid'])) die();
	$roleid = intval($_POST['roleid']);
	echo GetUserID($roleid);
	die();
}

function FindRoleName()
{
	if (!isset($_POST['rolename'])) die();
	$rolename = $_POST['rolename'];
	$rid = 0; $uid = 0;
	$f = GetRoleId($rolename);
	if ($f['retcode'] == 0 && $f['roleid'] > 0)
	{
		$rid = $f['roleid'];
		$uid = GetUserID($rid);		
	}
	echo json_encode(array('roleid' => $rid, 'userid' => $uid));
	die();
}

function ShowEditRoleForm()
{
	if (!isset($_POST['roleid'])) die();
	$roleid = intval($_POST['roleid']);
	$rd = GetRoleData($roleid);
	$answ = array('roleid' => $roleid, 'content' => '');
	//if ($rd['retcode'] == 60) $answ['roleid'] = 0; else
	//if ($rd['retcode'] !=0 || $rd['packet_check_error'] != 0) $answ['content'] = 'Ошибка получения персонажа'; else
	//{
		$text = '<form class="edit_role_form" id="edit_role_form"><ul class="nav nav-tabs" id="EditTab">';
		foreach ($rd['value'] as $i => $val)
		{
			$text .= '<li><a href="#'.$i.'">'.$i.'</a></li>';
		}
		$text .= '</ul><div id="EditTabContent" class="tab-content" style="height: 85%; overflow: initial">';
		foreach ($rd['value'] as $i => $val)
		{
			$text .= '<div class="tab-pane" id="'.$i.'"><textarea name="'.$i.'">'.json_encode($val, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE).'</textarea></div>';
		}
		$text .= '</form><center><a href="#" id="save_but" onclick="return SaveRole()" class="btn btn-large btn-inverse">Сохранить</a></center>';
		$answ['content'] = $text;
	//}
	echo json_encode($answ);
	die();
}

function SaveEditRole()
{
	if (!isset($_POST['roleid'])) die();
	$err_templ = '<div class="alert alert-error">%s</div>';
	$roleid = intval($_POST['roleid']);
	$id = intval($_POST['id']);
	$ip = $_POST['ip'];
	$roledata = array();
	foreach ($_POST as $i => $val)
	{
		if ($i == 'op' || $i == 'roleid' || $i == 'lk_ver' || $i == 'id' || $i == 'ip') continue;
		$a = json_decode($val, true);
		if (json_last_error() === JSON_ERROR_NONE) $roledata[$i] = $a; else die(sprintf($err_templ, 'Ошибка парсинга секции '.$i));
	}
	$rd = PutRoleData($roleid, $roledata);
	if ($rd['retcode'] != 0) die(sprintf($err_templ, 'Ошибка сохранения персонажа '.$rd['retcode']));
	AddLKLogs($id, $ip, 0, 0, 'Редактирование персонажа '.$roleid.' '.htmlspecialchars($roledata['base']['name']));
	echo '<div class="alert alert-success">Данные успешно сохранены</div>';
	die();
}

function BanRole()
{
	extract($_POST);
	$err_templ = '<div class="alert alert-error">%s</div>';
	if ($reason == '') $reason = chr(0);
	$t = 'Бан ';
	switch ($type)
	{
		case 'chat':
			$t .= 'чата';
			$res = GMShutupRole(1024, 1, $roleid, $time, $reason);
			if ($res['dstroleid'] != $roleid ) die(sprintf($err_templ, 'Ошибка отправки запроса '.$res['dstroleid']));
		break;

		case 'role':
			$t .= 'персонажа';
			$res = GMKickoutRole(1024, 1, $roleid, $time, $reason);
			if ($res['retcode'] !=0 ) die(sprintf($err_templ, 'Ошибка отправки запроса '.$res['retcode']));
		break;

		default:
			die('Invalid type');
		break;
	}
	$msg = sprintf('%s %s на %s, Причина: %s', strip_tags($rolename), $t, GetTime($time, false), $reason);
	if (isset($broadcast))
	{
		broadcast(9, 0, 32, 0, $msg);		
	}
	AddLKLogs($id, $ip, 0, 0, $msg);	
	echo '<div class="alert alert-success">Бан успешно выдан</div>';
	die();
}

//Смена класса персонажа
function ChangeRoleOccupation()
{
    global $db;
    
    // Очищаем буферы
    while (ob_get_level()) {
        ob_end_clean();
    }
    
    header('Content-Type: application/json; charset=utf-8');
    header('X-Content-Type-Options: nosniff');
    
    // Получаем параметры
    if (!isset($_POST['roleid']) || !isset($_POST['new_occupation'])) {
        echo json_encode(array('error' => 'Отсутствуют необходимые параметры'));
        die();
    }
    
    $roleid = intval($_POST['roleid']);
    $new_occupation = intval($_POST['new_occupation']);
    $id = isset($_POST['id']) ? intval($_POST['id']) : 0;
    $ip = $_SERVER['REMOTE_ADDR'];
    $new_reborndata = isset($_POST['reborndata']) ? $_POST['reborndata'] : '';
    
    if ($id == 0) {
        echo json_encode(array('error' => 'Не авторизован'));
        die();
    }
    
    // ========== ПРОВЕРКА БАЛАНСА ==========
    $cost = 500; // Стоимость смены класса
    
    // Получаем пользователя
    $user_q = mysqli_query($db, "SELECT lkgold FROM users WHERE ID = $id");
    if (!$user_q || mysqli_num_rows($user_q) === 0) {
        echo json_encode(array('error' => 'Пользователь не найден'));
        die();
    }
    $user = mysqli_fetch_assoc($user_q);
    $current_gold = (int)$user['lkgold'];
    
    if ($current_gold < $cost) {
        echo json_encode(array('error' => 'Недостаточно средств. Нужно ' . $cost . ' gold'));
        die();
    }
    
    // Массив допустимых классов
    $valid_classes = array(
        0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,19,20,22,23,
        25,26,27,28,29,33,34,35,36,37,39,40,41,42,43,45,46,47,48,49,
        51,52,53,54,55,56,57,58,59,60,64,65,66,67,68,71,72,73,74,75,
        96,97,98,99,100,102,103,104,105,106,108,109,110,111,112,117,118,119,120,121
    );
    
    if (!in_array($new_occupation, $valid_classes)) {
        echo json_encode(array('error' => 'Некорректное значение класса'));
        die();
    }
    
    // Получаем данные персонажа
    $role_data = GetRoleData($roleid);
    
    if ($role_data['retcode'] != 0) {
        echo json_encode(array('error' => 'Ошибка получения данных персонажа'));
        die();
    }
    
    // Проверка уровня
    $current_level = isset($role_data['value']['status']['level']) ? intval($role_data['value']['status']['level']) : 0;
    if ($current_level < 135) {
        echo json_encode(array('error' => 'Требуется уровень 135+'));
        die();
    }
    
    $old_occupation = isset($role_data['value']['status']['occupation']) ? intval($role_data['value']['status']['occupation']) : 0;
    
    // ========== СПИСЫВАЕМ МОНЕТЫ (как в кейсах) ==========
    $update = mysqli_query($db, "UPDATE users SET lkgold = lkgold - $cost WHERE ID = $id");
    if (!$update) {
        echo json_encode(array('error' => 'Ошибка списания средств'));
        die();
    }
    
    // Бан на 5 секунд
    $ban_result = GMKickoutRole(1024, 1, $roleid, 5, 'Смена класса персонажа');
    if ($ban_result['retcode'] != 0) {
        // Возвращаем деньги если бан не удался
        mysqli_query($db, "UPDATE users SET lkgold = lkgold + $cost WHERE ID = $id");
        echo json_encode(array('error' => 'Ошибка выдачи бана'));
        die();
    }
    
    sleep(5);
    
    // Меняем класс
    $role_data['value']['status']['occupation'] = $new_occupation;
    
    // Меняем реборн если есть
    if (!empty($new_reborndata)) {
        $role_data['value']['status']['reborndata'] = $new_reborndata;
    }
    
    $save_result = PutRoleData($roleid, $role_data['value']);
    if ($save_result['retcode'] != 0) {
        // Возвращаем деньги если сохранение не удалось
        mysqli_query($db, "UPDATE users SET lkgold = lkgold + $cost WHERE ID = $id");
        echo json_encode(array('error' => 'Ошибка сохранения класса'));
        die();
    }
    
    // Массив названий классов для логов
    $class_names = array(
        0 => 'Новичок',
        1 => 'Вим I', 2 => 'Вим II', 3 => 'Вим III',
        4 => 'Хаккан I', 5 => 'Хаккан II', 6 => 'Хаккан III',
        7 => 'Айне I', 8 => 'Айне II', 9 => 'Айне III',
        10 => 'Скайя I', 11 => 'Скайя II', 12 => 'Скайя III',
        13 => 'Вим IV', 14 => 'Вим V',
        16 => 'Хаккан IV', 17 => 'Хаккан V',
        19 => 'Айне IV', 20 => 'Айне V',
        22 => 'Скайя IV', 23 => 'Скайя V',
        25 => 'Морто I', 26 => 'Морто II', 27 => 'Морто III', 28 => 'Морто IV', 29 => 'Морто V',
        33 => 'Титаны I', 34 => 'Титаны II', 35 => 'Титаны III', 36 => 'Титаны IV', 37 => 'Титаны V',
        39 => 'Арден I', 40 => 'Арден II', 41 => 'Арден III', 42 => 'Арден IV', 43 => 'Арден V',
        45 => 'Умбра I', 46 => 'Умбра II', 47 => 'Умбра III', 48 => 'Умбра IV', 49 => 'Умбра V',
        51 => 'Вайлин I', 52 => 'Вайлин II', 53 => 'Вайлин III', 54 => 'Вайлин IV', 55 => 'Вайлин V',
        56 => 'Ниру I', 57 => 'Ниру II', 58 => 'Ниру III', 59 => 'Ниру IV', 60 => 'Ниру V',
        64 => 'Тайо I', 65 => 'Тайо II', 66 => 'Тайо III', 67 => 'Тайо IV', 68 => 'Тайо V',
        71 => 'Сейра I', 72 => 'Сейра II', 73 => 'Сейра III', 74 => 'Сейра IV', 75 => 'Сейра V',
        96 => 'Войда I', 97 => 'Войда II', 98 => 'Войда III', 99 => 'Войда IV', 100 => 'Войда V',
        102 => 'Психея I', 103 => 'Психея II', 104 => 'Психея III', 105 => 'Психея IV', 106 => 'Психея V',
        108 => 'Кентавр I', 109 => 'Кентавр II', 110 => 'Кентавр III', 111 => 'Кентавр IV', 112 => 'Кентавр V',
        117 => 'Гидран I', 118 => 'Гидран II', 119 => 'Гидран III', 120 => 'Гидран IV', 121 => 'Гидран V'
    );
    
    $old_class_name = isset($class_names[$old_occupation]) ? $class_names[$old_occupation] : "Класс $old_occupation";
    $new_class_name = isset($class_names[$new_occupation]) ? $class_names[$new_occupation] : "Класс $new_occupation";
    
    // Получаем имя персонажа
    $char_name_query = mysqli_query($db, "SELECT name FROM base WHERE id = $roleid LIMIT 1");
    $char_name = 'Неизвестно';
    if ($char_name_query && $row = mysqli_fetch_assoc($char_name_query)) {
        $char_name = $row['name'];
    }
    
    $log_message = "Смена класса персонажа $char_name с $old_class_name на $new_class_name (ID: $old_occupation -> $new_occupation)";
    
    // Логируем в БД
    AddLKLogs($id, $ip, 0, 0, $log_message);
    
    echo json_encode(array(
        'success' => true,
        'message' => 'Класс персонажа успешно изменен. Списано ' . $cost . ' gold',
        'new_balance' => $current_gold - $cost
    ));
    die();
} 

// Функция для генерации данных реборна
function generateRebornData($class_id) {
    $reborn_map = array(
        14 => '0e009600', // Вим V
        17 => '11009600', // Хаккан V
        20 => '14009600', // Айне V
        23 => '17009600', // Скайя V
        29 => '1d009600', // Морто V
        37 => '25009600', // Титаны V
        43 => '2b009600', // Арден V
        49 => '2b008a00', // Умбра V
        55 => '', // Вайлин V (нужно добавить)
        60 => '', // Ниру V (нужно добавить)
        68 => '', // Тайо V (нужно добавить)
        96 => ''  // Войда V (нужно добавить)
    );
    
    return isset($reborn_map[$class_id]) ? $reborn_map[$class_id] : '';
}

function GetRoleOccupation()
{
    // Очищаем буферы
    while (ob_get_level()) {
        ob_end_clean();
    }
    
    header('Content-Type: application/json; charset=utf-8');
    header('X-Content-Type-Options: nosniff');
    
    if (!isset($_POST['roleid'])) {
        echo json_encode(array('error' => 'Отсутствует ID персонажа'));
        die();
    }
    
    $roleid = intval($_POST['roleid']);
    
    $role_data = GetRoleData($roleid);
    
    if ($role_data['retcode'] != 0) {
        echo json_encode(array('error' => 'Ошибка получения данных персонажа'));
        die();
    }
    
    $occupation = 0;
    if (isset($role_data['value']['base']['occupation'])) {
        $occupation = intval($role_data['value']['base']['occupation']);
    }
    
    echo json_encode(array(
        'success' => true,
        'roleid' => $roleid,
        'occupation' => $occupation
    ));
    die();
}

function reward_donate_items() {
    global $Structures, $db;

    $logfile = __DIR__ . '/donate_reward.log';

    // Проверка метода
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        echo json_encode(['status' => 'error', 'message' => 'bad_method']);
        return;
    }

    // Чтение и проверка параметров
    $login = isset($_POST['login']) ? trim($_POST['login']) : '';
    $charid = isset($_POST['charid']) ? (int)$_POST['charid'] : 0;
    $itemHex = isset($_POST['item']) ? trim($_POST['item']) : '';
	$added_by = isset($_POST['added_by']) ? trim($_POST['added_by']) : '';
	$source   = isset($_POST['source']) ? trim($_POST['source']) : '';	

	$context = "Выдал: $added_by. Причина: $source";


    if (empty($itemHex) || !ctype_xdigit($itemHex)) {
        file_put_contents($logfile, "[" . date('c') . "] ❌ invalid_hex\n", FILE_APPEND);
        echo json_encode(['status' => 'error', 'message' => 'invalid_hex']);
        return;
    }

    if (empty($login)) {
        file_put_contents($logfile, "[" . date('c') . "] ❌ missing_login\n", FILE_APPEND);
        echo json_encode(['status' => 'error', 'message' => 'missing_login']);
        return;
    }

    // Поиск ID пользователя
    $safeLogin = $db->real_escape_string($login);
    $res = $db->query("SELECT ID FROM users WHERE name = '$safeLogin' LIMIT 1");
    if (!$res || !$res->num_rows) {
        file_put_contents($logfile, "[" . date('c') . "] ❌ user_not_found: $login\n", FILE_APPEND);
        echo json_encode(['status' => 'error', 'message' => 'user_not_found']);
        return;
    }

    $row = $res->fetch_assoc();
    $userid = (int)$row['ID'];

    // Распаковка HEX
    $binary = @hex2bin($itemHex);
    if ($binary === false || strlen($binary) === 0) {
        file_put_contents($logfile, "[" . date('c') . "] ❌ hex_decode_failed\n", FILE_APPEND);
        echo json_encode(['status' => 'error', 'message' => 'hex_decode_failed']);
        return;
    }

    // Парсинг предмета
    try {
        $pp = new Protocols();
        $parsed = $pp->unmarshal($binary, $Structures['GRoleInventory']);
    } catch (Throwable $e) {
        file_put_contents($logfile, "[" . date('c') . "] ❌ Exception in unmarshal: " . $e->getMessage() . "\n", FILE_APPEND);
        echo json_encode(['status' => 'error', 'message' => 'unmarshal_exception']);
        return;
    }

    if (empty($parsed['id'])) {
        file_put_contents($logfile, "[" . date('c') . "] ❌ empty_item after unmarshal\n", FILE_APPEND);
        echo json_encode(['status' => 'error', 'message' => 'empty_item']);
        return;
    }

    // Сборка предмета
    $item = InitItem();
    $item['id']          = $parsed['id'];
    $item['count']       = $parsed['count'];
    $item['max_count']   = $parsed['max_count'];
    $item['data']        = $parsed['data'];
    $item['proctype']    = $parsed['proctype'];
    $item['expire_date'] = $parsed['expire_date'];
    $item['client_size'] = $parsed['client_size'];
    $item['pos']         = $parsed['pos'];
    $item['guid1']       = $parsed['guid1'];
    $item['guid2']       = $parsed['guid2'];

    $mailTitle = "Награда из тайника";

    $success = SysSendMail(0, 32, 3, $charid, $mailTitle, $context, $item, 0);

    file_put_contents($logfile, "[" . date('c') . "] 📤 Отправка предмета {$item['id']} игроку $login (charid: $charid) — " . ($success ? "успех" : "ошибка") . "\n", FILE_APPEND);

    echo json_encode([
        'status' => $success ? 'ok' : 'error',
        'message' => $success ? 'item_sent' : 'send_failed',
        'charid' => $charid,
        'item_id' => $item['id']
    ]);
}

function get_status_data()
{
    global $db, $servid, $factrole;
    if (!isset($_POST['id'])) die();

    $id   = (int)$_POST['id'];
    $answ = getbaninfo($id);

    $f = GetUserRoles($id, 1);
    if ($f['retcode'] != 0) MakeAnswerErrGetRoles($answ);
    if (!@AssignData(ak)) die('13');

    $answ['roles_count'] = count($f['roles']);
    $answ['roles']       = [];
    if (!$answ['roles_count']) MakeAnswer($answ);

    $db->query('SET NAMES utf8');

	foreach ($f['data'] as $i => $v) {
		$roleId   = $v['id'];
		$roleName = $v['name'];

		$r = GetRole($roleId, 0xFFFFFFFF); 
		if ($r['retcode'] == 0) {
			$detail = $r['value'];
			$st = $detail['status']; 

			
			$st['factionid'] = isset($detail['factionid']) ? $detail['factionid'] : 0;
			$st['factionrole'] = isset($detail['title']) ? $detail['title'] : 0;
			
			if ($st['factionid'] > 0) {
				$fact = GetFactionInfo($st['factionid']);
				$st['factionname'] = isset($fact['value']['name']) ? $fact['value']['name'] : 'Неизвестно';
				$st['factionrole_name'] = isset($factrole[$st['factionrole']]) ? $factrole[$st['factionrole']] : 'В клане';
			} else {
				$st['factionname'] = 'Нет клана';
				$st['factionrole_name'] = 'Без клана';
			}

			if (!empty($detail['base2']) && is_array($detail['base2'])) {
				foreach ($detail['base2'] as $k => $v) {
					if (!array_key_exists($k, $st)) {
						$st[$k] = $v;
					}
				}
			}
		
			// Подсчёт завершённых тасков
			$tc_bin = hex2bin($detail['task']['task_complete']);
			$st['completed_tasks'] = strlen($tc_bin) / 4;
		
			// Подсчёт открытых сундуков
			$ti = $detail['status']['treasure_info'];
			if (is_string($ti)) {
				$bin = hex2bin($ti);
				if (strlen($bin) >= 16) {
					$st['opened_chests'] = unpack('V', substr($bin, 12, 4))[1];
				} else {
					$st['opened_chests'] = 0;
				}
			}			
		
			$st['index']  = $i;
			$st['roleid'] = $roleId;
			$st['name']   = $roleName;
		
			$answ['roles'][] = $st;
		}		
	}
	

    if ($answ['bancount'] > 0) MakeAnswer($answ, 81);
    MakeAnswer($answ);
}

function get_user_votes() {
    global $db;

    $userid = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    if ($userid <= 0) {
        MakeAnswer([], 1, 'Invalid userid');
    }

    $votes = [];

    $stmt = $db->prepare("SELECT DATE(`data`) as d, COUNT(*) as c FROM mmotop_data WHERE userid = ? GROUP BY d ORDER BY d ASC");

    if (!$stmt) {
        MakeAnswer([], 2, 'DB prepare error', $db->error);
    }

    $stmt->bind_param('i', $userid);

    if (!$stmt->execute()) {
        $stmt->close();
        MakeAnswer([], 3, 'Execute error', $stmt->error);
    }

    $stmt->store_result();
    $stmt->bind_result($date, $count);

    while ($stmt->fetch()) {
        $votes[$date] = (int)$count;
    }

    $stmt->close();

    MakeAnswer(['votes' => $votes]);
}

function AddVoteBonus() {
	global $db;

	file_put_contents('donate_reward.log', print_r($_POST, true), FILE_APPEND);

	if (!isset($_POST['login'], $_POST['bonus_percent'], $_POST['bonus_duration'])) {
		echo json_encode(['status' => false, 'error' => 'Не указаны обязательные параметры']);
		return;
	}

	$login = trim($_POST['login']);
	$percent = (int)$_POST['bonus_percent'];
	$duration = (int)$_POST['bonus_duration'];
	$source = isset($_POST['source']) ? trim($_POST['source']) : 'achievement';
	$type = 'vote';

	if ($login === '' || $percent <= 0 || $duration <= 0) {
		echo json_encode(['status' => false, 'error' => 'Некорректные значения']);
		return;
	}

	$expire_at = date('Y-m-d H:i:s', time() + $duration);
	$login_esc = $db->real_escape_string($login);
	$source_esc = $db->real_escape_string($source);
	$expire_esc = $db->real_escape_string($expire_at);

	$sql = sprintf(
		"INSERT INTO user_bonus_effects (login, type, percent, source, expire_at)
		VALUES ('%s', '%s', %d, '%s', '%s')",
		$login_esc, $type, $percent, $source_esc, $expire_esc
	);

	if ($db->query($sql)) {
		echo json_encode(['status' => true]);
	} else {
		echo json_encode(['status' => false, 'error' => $db->error]);
	}
}

function add_case() {
    global $db;

    // Получаем данные из POST-запроса
    $name     = isset($_POST['name']) ? trim($_POST['name']) : '';
    $price    = isset($_POST['price']) ? (int)$_POST['price'] : 0;
    $contents = isset($_POST['contents']) ? $_POST['contents'] : '';
    $active   = isset($_POST['active']) ? (int)$_POST['active'] : 1;
    $icon     = isset($_POST['icon']) ? trim($_POST['icon']) : '';

    // Валидация
    if ($name == '' || $price <= 0 || $contents == '') {
        echo json_encode(['status' => 'error', 'message' => 'Недостаточно данных']);
        return;
    }

    // Экранирование
    $name_safe     = mysqli_real_escape_string($db, $name);
    $contents_safe = mysqli_real_escape_string($db, $contents);
    $icon_safe     = mysqli_real_escape_string($db, $icon);

    // SQL-запрос
    $sql = "INSERT INTO cases (name, price, contents, active, icon, created)
            VALUES ('$name_safe', $price, '$contents_safe', $active, '$icon_safe', NOW())";

    $res = mysqli_query($db, $sql);

    if ($res) {
        echo json_encode([
            'status' => 'success',
            'message' => 'Кейс добавлен',
            'errorcode' => 0
        ]);
    } else {
        echo json_encode([
            'status' => 'error',
            'message' => 'Ошибка БД: ' . mysqli_error($db),
            'errorcode' => 1
        ]);
    }
}


function get_case_by_id() {
    global $db;

    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;

    if ($id <= 0) {
        echo json_encode(['status' => 'error', 'message' => 'Некорректный ID']);
        return;
    }

    $sql = "SELECT * FROM cases WHERE id = $id LIMIT 1";
    $res = mysqli_query($db, $sql);

    if (!$res || mysqli_num_rows($res) === 0) {
        echo json_encode(['status' => 'error', 'message' => 'Кейс не найден']);
        return;
    }

    $case = mysqli_fetch_assoc($res);

    // Если contents — JSON, декодируем для удобства
    $case['contents'] = json_decode($case['contents'], true);

    echo json_encode(['status' => 'success', 'data' => $case]);
}

function open_case() {
    global $db;

    $userid  = isset($_POST['userid']) ? (int)$_POST['userid'] : 0;
    $case_id = isset($_POST['case_id']) ? (int)$_POST['case_id'] : 0;

    if ($userid <= 0 || $case_id <= 0) {
        echo json_encode(['status' => 'error', 'message' => 'Некорректные данные']);
        return;
    }

    // Получаем пользователя
    $user_q = mysqli_query($db, "SELECT lkgold FROM users WHERE ID = $userid");
    if (!$user_q || mysqli_num_rows($user_q) === 0) {
        echo json_encode(['status' => 'error', 'message' => 'Пользователь не найден']);
        return;
    }
    $user = mysqli_fetch_assoc($user_q);

    // Получаем кейс
    $case_q = mysqli_query($db, "SELECT * FROM cases WHERE id = $case_id");
    if (!$case_q || mysqli_num_rows($case_q) === 0) {
        echo json_encode(['status' => 'error', 'message' => 'Кейс не найден']);
        return;
    }
    $case = mysqli_fetch_assoc($case_q);
    $price = (int)$case['price'];

    if ((int)$user['lkgold'] < $price) {
        echo json_encode(['status' => 'error', 'message' => 'Недостаточно средств']);
        return;
    }

    // Декодируем содержимое кейса
    $items = json_decode($case['contents'], true);
    if (!is_array($items)) {
        echo json_encode(['status' => 'error', 'message' => 'Неверный формат содержимого кейса']);
        return;
    }

    // Выбор предмета по шансу
	$rolled = mt_rand(1, 10000);
	$accum = 0;
	$selected = null;

	foreach ($items as $item) {
		$baseChance = floatval($item['chance']); // в %
		$adjustedChance = pow($baseChance, 1); // снижает шанс для маленьких значений

		// переводим в сотые доли процента
		$chance = $adjustedChance * 100; // 0.5% → 0.353 → 35.3
		$accum += $chance;

		if ($rolled <= $accum) {
			$selected = $item;
			break;
		}
	}


    if (!$selected) {
        echo json_encode(['status' => 'error', 'message' => 'Не удалось выбрать предмет']);
        return;
    }

    // Списываем lkgold
    $update = mysqli_query($db, "UPDATE users SET lkgold = lkgold - $price WHERE ID = $userid");
    if (!$update) {
        echo json_encode(['status' => 'error', 'message' => 'Ошибка списания средств']);
        return;
    }

    // Успешно
    echo json_encode([
        'status' => 'success',
        'ocet' => $selected['hex'],
        'message' => 'Вы получили предмет из кейса'
    ]);
}

function get_cases_list() {
    global $db;

    $cases = [];
    $q = mysqli_query($db, "SELECT * FROM cases ORDER BY id DESC");
    while ($row = mysqli_fetch_assoc($q)) {
        $row['contents'] = json_decode($row['contents'], true); // преобразуем сразу
        $cases[] = $row;
    }

    echo json_encode([
        'errorcode' => 0,
        'cases' => $cases
    ], JSON_UNESCAPED_UNICODE);
}

function manage_case() {
    global $db;

    $caseId = isset($_POST['case_id']) ? (int)$_POST['case_id'] : 0;
    $action = isset($_POST['action']) ? $_POST['action'] : '';

    if ($caseId <= 0) {
        echo json_encode(['status' => 'error', 'message' => 'Неверный ID кейса']);
        exit;
    }

    if ($action === 'toggle') {
        $q = $db->query("UPDATE cases SET active = NOT active WHERE id = $caseId LIMIT 1");
        if ($q) {
            echo json_encode(['status' => 'success', 'message' => 'Статус обновлён']);
        } else {
            echo json_encode(['status' => 'error', 'message' => 'Ошибка базы данных']);
        }
        exit;
    }

    if ($action === 'delete') {
        $q = $db->query("DELETE FROM cases WHERE id = $caseId LIMIT 1");
        if ($q) {
            echo json_encode(['status' => 'success', 'message' => 'Кейс удалён']);
        } else {
            echo json_encode(['status' => 'error', 'message' => 'Не удалось удалить кейс']);
        }
        exit;
    }

    echo json_encode(['status' => 'error', 'message' => 'Неизвестное действие']);
    exit;
}

function readFileReverse($filename) {
    $fp = fopen($filename, 'r');
    $position = -1;
    $lines = [];
    $line = '';

    fseek($fp, $position, SEEK_END);
    $char = fgetc($fp);

    while ($char !== false) {
        if ($char === "\n") {
            $lines[] = strrev($line);
            $line = '';
        } else {
            $line .= $char;
        }

        $position--;
        if (fseek($fp, $position, SEEK_END) === -1) break;
        $char = fgetc($fp);
    }

    if ($line !== '') {
        $lines[] = strrev($line);
    }

    fclose($fp);
    return $lines;
}


function GetChatLogs() {
    // Получаем параметры из POST
    $chatTypes = isset($_POST['types']) ? $_POST['types'] : array();
    if (!is_array($chatTypes)) {
        $chatTypes = array($chatTypes); // если передан как строка
    }
	$dateFilter = isset($_POST['date']) ? $_POST['date'] : '';
	$targetDate = $dateFilter ? date('Y-m-d', strtotime($dateFilter)) : '';

    $filterRoleId = isset($_POST['roleid']) ? intval($_POST['roleid']) : 0;
    $limit  = isset($_POST['limit']) ? intval($_POST['limit']) : 20;
    $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
	$keyword = isset($_POST['keyword']) ? trim($_POST['keyword']) : '';
	$encodedKeyword = '';
	if ($keyword !== '') {
		$encodedKeyword = base64_encode(mb_convert_encoding($keyword, 'UTF-16LE', 'UTF-8'));
	}

    $typesMap = array(
        'whisper' => array('Whisper', array()),
        'guild'   => array('Guild', array()),
        'local'   => array('Chat', array(0)),
        'party'   => array('Chat', array(2)),
        'world'   => array('Chat', array(1)),
    );

    $selectedTypes = array();
    $selectedChannels = array();

    foreach ($chatTypes as $t) {
        if (isset($typesMap[$t])) {
            if (!in_array($typesMap[$t][0], $selectedTypes)) {
                $selectedTypes[] = $typesMap[$t][0];
            }
            foreach ($typesMap[$t][1] as $ch) {
                if (!in_array($ch, $selectedChannels)) {
                    $selectedChannels[] = $ch;
                }
            }
        }
    }

    $logPath = '/root/logs/world2.chat';
    if (!file_exists($logPath)) {
        echo json_encode(array(
            'status' => 'error',
            'message' => 'Файл чата не найден',
            'debug' => array(
                'input_post' => $_POST,
                'selectedTypes' => $selectedTypes,
                'selectedChannels' => $selectedChannels
            )
        ));
        return;
    }

    $lines = readFileReverse($logPath);
    if (!$lines) {
        echo json_encode(array('status' => 'error', 'message' => 'Не удалось открыть файл'));
        return;
    }

    $result = array();
    $matchedCount = 0;

    foreach ($lines as $line) {
        if (strpos($line, 'msg=') === false) continue;
		$lineDate = substr($line, 0, 10); // строка начинается с даты

		if ($targetDate && $lineDate !== $targetDate) continue;

        if (preg_match('/(Chat|Whisper|Guild):/', $line, $m)) {
            $type = $m[1];
            if (!empty($selectedTypes) && !in_array($type, $selectedTypes)) continue;
        } else {
            continue;
        }

        if (preg_match('/chl=(\d+)/', $line, $m)) {
            $channel = intval($m[1]);
            if (!empty($selectedChannels) && !in_array($channel, $selectedChannels)) continue;
        } else {
            $channel = null;
        }

        if (!preg_match('/msg=([A-Za-z0-9+\/=]+)/', $line, $m)) continue;
        // Получаем отправителя (src=)
        $srcId = null;
        if (preg_match('/src=(\-?\d+)/', $line, $srcMatch)) {
            $srcId = (int)$srcMatch[1];
			if ($srcId < 0) continue;
        }

        // Фильтр по отправителю
        if ($filterRoleId > 0 && $srcId !== $filterRoleId) continue;

        // Строка подходит по фильтру
        $matchedCount++;
        if ($matchedCount <= $offset) continue;
        if (count($result) >= $limit) break;

        $rawmsg = $m[1];
		if ($encodedKeyword !== '' && strpos($rawmsg, $encodedKeyword) === false) {
			continue;
		}
        $decoded = base64_decode($rawmsg);
        $text = iconv('UTF-16LE', 'UTF-8//IGNORE', $decoded);

        $entry = array(
            'line' => trim($line),
            'text' => $text,
            'type' => $type,
            'channel' => $channel,
            'srcId' => $srcId
        );

		// Подтягиваем имя отправителя
		if ($srcId && function_exists('GetRole')) {
			$srcRole = GetRole($srcId, 63);
			$entry['srcName'] = "[" . $srcId . "]";
			if (!empty($srcRole['value']['name'])) {
				$entry['srcName'] .= " " . $srcRole['value']['name'];
			}
		}

		// Если шепот — добавим получателя
		if ($type === 'Whisper') {
			preg_match('/dst=(\d+)/', $line, $destMatch);
			$destId = isset($destMatch[1]) ? (int)$destMatch[1] : null;

			if (function_exists('GetRole')) {
				$destRole = $destId ? GetRole($destId, 63) : null;

				$entry['from'] = isset($entry['srcName']) ? $entry['srcName'] : "[" . $srcId . "]";
				$entry['to'] = (!empty($destRole['value']['name']))
					? "[" . $destId . "] " . $destRole['value']['name']
					: "[" . $destId . "]";
			} else {
				$entry['from'] = "[" . $srcId . "]";
				$entry['to'] = "[" . $destId . "]";
			}
		}


        $result[] = $entry;
    }

    fclose($handle);

    echo json_encode(array(
        'status' => 'success',
        'total' => $matchedCount,
        'offset' => $offset,
        'limit' => $limit,
        'data' => $result,
        'debug' => array(
            'input_post' => $_POST,
            'selectedTypes' => $selectedTypes,
            'selectedChannels' => $selectedChannels
        )
    ));
}


function GetTradeLogs() {
    $logPath = '/root/logs/world2.formatlog';
    $itemsPath = __DIR__ . '/items_ext.txt';

    $dateFilter = isset($_POST['date']) ? $_POST['date'] : '';
    $targetDate = $dateFilter ? date('Y-m-d', strtotime($dateFilter)) : '';

    $limit = isset($_POST['limit']) ? intval($_POST['limit']) : 20;
    $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
    $filterRoleId = isset($_POST['roleid']) ? intval($_POST['roleid']) : 0;
	$itemIdFilter = isset($_POST['itemid']) ? intval($_POST['itemid']) : 0;

    if (!file_exists($logPath) || !file_exists($itemsPath)) {
        echo json_encode(array('status' => 'error', 'message' => 'Файл лога или предметов не найден'));
        return;
    }

    $itemNames = loadItemNames($itemsPath);
    $result = [];
    $matchedCount = 0;

    $fp = fopen($logPath, 'rb');
    if (!$fp) {
        echo json_encode(array('status' => 'error', 'message' => 'Не удалось открыть лог'));
        return;
    }

    $bufferSize = 65536; // 64 KB
    $pos = -1;
    $partial = '';
    fseek($fp, 0, SEEK_END);
    $fileSize = ftell($fp);

    while (abs($pos) < $fileSize) {
        $readSize = min($bufferSize, $fileSize + $pos);
        fseek($fp, $pos - $readSize + 1, SEEK_END);
        $chunk = fread($fp, $readSize);
        $pos -= $readSize;

        $chunk .= $partial;
        $lines = explode("\n", $chunk);
        $partial = array_shift($lines); // последняя неполная строка на следующую итерацию

        $lines = array_reverse($lines); // чтобы новые строки были первыми

        foreach ($lines as $line) {
            if (strpos($line, 'formatlog:trade:') === false) continue;
            if (strpos($line, 'trade_debug') !== false) continue;

            $lineDate = substr($line, 0, 10);
            if ($targetDate && strpos($line, $targetDate) !== 0) continue;

            $data = parseTradeLogLine($line, $itemNames, $itemIdFilter);
            if (!is_array($data)) continue;

            if ($filterRoleId > 0) {
                $aMatch = isset($data['roleidA']) && $data['roleidA'] == $filterRoleId;
                $bMatch = isset($data['roleidB']) && $data['roleidB'] == $filterRoleId;
                if (!$aMatch && !$bMatch) continue;
            }

            if (!isset($data['roleidA']) && !isset($data['roleidB'])) {
                error_log("⚠️ Пропущена строка: нет roleidA/roleidB в " . $line);
            }

            $matchedCount++;
            if ($matchedCount <= $offset) continue;
            if (count($result) >= $limit) break 2;

            $result[] = $data;
        }
    }

    fclose($fp);

    echo json_encode(array(
        'status' => 'success',
        'debug' => array(
            'rawDateInput' => $dateFilter,
            'parsedTargetDate' => $targetDate,
        ),
        'total' => $matchedCount,
        'offset' => $offset,
        'limit' => $limit,
        'data' => $result
    ));
}


function parseTradeLogLine($line, $itemNames, $filterItemId = 0) {
    static $namesCache = []; // Кэш имён по roleid

    if (strpos($line, 'formatlog:trade:') === false) return false;

    $out = array(
        'type' => 'Trade',
        'line' => trim($line),
        'roleidA' => null,
        'roleidB' => null,
        'nameA' => null,
        'nameB' => null,
        'goldA' => 0,
        'goldB' => 0,
        'itemsA' => array(),
        'itemsB' => array()
    );

    preg_match('/roleidA=(\d+)/', $line, $m);
    if (isset($m[1])) {
        $out['roleidA'] = (int)$m[1];
        $out['nameA'] = "[" . $out['roleidA'] . "]";

        if (function_exists('GetRole')) {
            $rid = $out['roleidA'];
            if (!isset($namesCache[$rid])) {
                $rA = @GetRole($rid, 63);
                $namesCache[$rid] = (is_array($rA) && isset($rA['value']['name']) && is_string($rA['value']['name']))
                    ? $rA['value']['name'] : null;
            }
            if (!empty($namesCache[$rid])) {
                $out['nameA'] .= " " . $namesCache[$rid];
            }
        }
    }

    preg_match('/roleidB=(\d+)/', $line, $m);
    if (isset($m[1])) {
        $out['roleidB'] = (int)$m[1];
        $out['nameB'] = "[" . $out['roleidB'] . "]";

        if (function_exists('GetRole')) {
            $rid = $out['roleidB'];
            if (!isset($namesCache[$rid])) {
                $rB = @GetRole($rid, 63);
                $namesCache[$rid] = (is_array($rB) && isset($rB['value']['name']) && is_string($rB['value']['name']))
                    ? $rB['value']['name'] : null;
            }
            if (!empty($namesCache[$rid])) {
                $out['nameB'] .= " " . $namesCache[$rid];
            }
        }
    }

    preg_match('/moneyA=(\d+)/', $line, $m);
    if (isset($m[1])) {
        $out['goldA'] = floor($m[1] / 10000);
    }

    preg_match('/moneyB=(\d+)/', $line, $m);
    if (isset($m[1])) {
        $out['goldB'] = floor($m[1] / 10000);
    }

    preg_match('/objectsA=([^:]*):/', $line, $m);
    if (isset($m[1])) {
        $out['itemsA'] = parseTradeItems($m[1], $itemNames);
    }

    preg_match('/objectsB=([^:]*);?$/', $line, $m);
    if (isset($m[1])) {
        $out['itemsB'] = parseTradeItems($m[1], $itemNames);
    }

	if ($filterItemId > 0) {
		$hasItem = false;
		foreach (['itemsA', 'itemsB'] as $side) {
			foreach ($out[$side] as $item) {
				if (isset($item['item_id']) && $item['item_id'] === $filterItemId) {
					$hasItem = true;
					break 2;
				}
			}
		}
		if (!$hasItem) return false;
	}

    return $out;
}


function parseTradeItems($chunk, $itemNames) {
    $items = array();
    $parts = explode(';', $chunk);
    foreach ($parts as $p) {
        if (strlen($p) < 3) continue;
        $data = explode(',', $p);
        if (count($data) !== 3) continue;
        $id = (int)$data[0];
        $ref = (int)$data[1];
        $count = (int)$data[2];
        $items[] = array(
            'item_id' => $id,
            'refine' => $ref,
            'count' => $count,
            'item_name' => isset($itemNames[$id]) ? $itemNames[$id] : "Item #{$id}"
        );
    }
    return $items;
}

function loadItemNames($file) {
    $items = array();
    if (!file_exists($file)) return $items;
    $lines = file($file);
    foreach ($lines as $line) {
        $line = trim($line);
        if (!$line || strpos($line, '|') === false) continue;
        $parts = explode('|', $line);
        if (count($parts) >= 2) {
            $id = intval($parts[0]);
            $name = $parts[1];
            $items[$id] = $name;
        }
    }
    return $items;
}

function GetReinforceLogs() {
    $logPath = '/root/logs/world2.formatlog';
    $itemsPath = __DIR__ . '/items_ext.txt';

    $dateFilter = isset($_POST['date']) ? $_POST['date'] : '';
    $targetDate = $dateFilter ? date('Y-m-d', strtotime($dateFilter)) : '';

    $filterUserId = isset($_POST['roleid']) ? intval($_POST['roleid']) : 0;
    $limit  = isset($_POST['limit']) ? intval($_POST['limit']) : 10;
    $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;

    if (!file_exists($logPath) || !file_exists($itemsPath)) {
        echo json_encode(array('status' => 'error', 'message' => 'Файл лога или предметов не найден'));
        return;
    }

    $itemNames = loadItemNames($itemsPath);
    $result = array();
    $matchedCount = 0;

    $fp = fopen($logPath, 'rb');
    if (!$fp) {
        echo json_encode(array('status' => 'error', 'message' => 'Не удалось открыть лог'));
        return;
    }

    $bufferSize = 65536; // 64 KB
    $pos = -1;
    $partial = '';
    fseek($fp, 0, SEEK_END);
    $fileSize = ftell($fp);

    while (abs($pos) < $fileSize) {
        $readSize = min($bufferSize, $fileSize + $pos);
        fseek($fp, $pos - $readSize + 1, SEEK_END);
        $chunk = fread($fp, $readSize);
        $pos -= $readSize;

        $chunk .= $partial;
        $lines = explode("\n", $chunk);
        $partial = array_shift($lines); // последняя неполная строка станет частью следующего блока

        $lines = array_reverse($lines); // обрабатываем снизу вверх

        foreach ($lines as $line) {
            if (strpos($line, 'formatlog:reinforce:') === false) continue;

            $lineDate = substr($line, 0, 10);
            if ($targetDate && strpos($line, $targetDate) !== 0) continue;

            if (!preg_match('/formatlog:reinforce:userid=(\d+):item=(\d+):level=(\d+):stone=(\d+):result=(\d+):luckyroll=(\d+):level_before=(\d+)/', $line, $m)) {
                continue;
            }

            $userId      = (int)$m[1];
            $itemId      = (int)$m[2];
            $levelAfter  = (int)$m[3];
            $stoneId     = (int)$m[4];
            $resultFlag  = (int)$m[5];
            $luckyRollId = (int)$m[6];
            $levelBefore = (int)$m[7];

            if ($filterUserId > 0 && $userId !== $filterUserId) continue;

            $matchedCount++;
            if ($matchedCount <= $offset) continue;
            if (count($result) >= $limit) break 2;

            $userName = "[" . $userId . "]";
            if (function_exists('GetRole')) {
                $role = GetRole($userId, 63);
                if (!empty($role['value']['name'])) {
                    $userName .= " " . $role['value']['name'];
                }
            }

            $typeMap = array(
                0 => 'success',
                2 => 'fail_down',
                3 => 'fail_zero',
                4 => 'fail_nochange'
            );

            $result[] = array(
                'type' => 'Reinforce',
                'userid' => $userId,
                'username' => $userName,
                'item' => array(
                    'id' => $itemId,
                    'name' => isset($itemNames[$itemId]) ? $itemNames[$itemId] : "Item #$itemId"
                ),
                'stone' => array(
                    'id' => $stoneId,
                    'name' => isset($itemNames[$stoneId]) ? $itemNames[$stoneId] : "Item #$stoneId"
                ),
                'luckyroll' => $luckyRollId,
                'level_before' => $levelBefore,
                'level_after' => $levelAfter,
                'result_type' => isset($typeMap[$resultFlag]) ? $typeMap[$resultFlag] : 'unknown',
                'line' => trim($line)
            );
        }
    }

    fclose($fp);

    echo json_encode(array(
        'status' => 'success',
        'total' => $matchedCount,
        'offset' => $offset,
        'limit' => $limit,
        'data' => $result
    ));
}

function GetGShopTradeLogs() {
    $logPath = '/root/logs/world2.formatlog';
    $itemsPath = __DIR__ . '/items_ext.txt';

    $dateFilter = isset($_POST['date']) ? $_POST['date'] : '';
    $targetDate = $dateFilter ? date('Y-m-d', strtotime($dateFilter)) : '';

    $limit = isset($_POST['limit']) ? intval($_POST['limit']) : 20;
    $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
    $itemIdFilter = isset($_POST['itemid']) ? intval($_POST['itemid']) : 0;
    $filterUserId = isset($_POST['roleid']) ? intval($_POST['roleid']) : 0;

    if (!file_exists($logPath) || !file_exists($itemsPath)) {
        echo json_encode(array('status' => 'error', 'message' => 'Файл лога или предметов не найден'));
        return;
    }

    $itemNames = loadItemNames($itemsPath);
    $result = [];
    $matchedCount = 0;

    $fp = fopen($logPath, 'rb');
    if (!$fp) {
        echo json_encode(array('status' => 'error', 'message' => 'Не удалось открыть лог'));
        return;
    }

    $bufferSize = 65536;
    $pos = -1;
    $partial = '';
    fseek($fp, 0, SEEK_END);
    $fileSize = ftell($fp);

    while (abs($pos) < $fileSize) {
        $readSize = min($bufferSize, $fileSize + $pos);
        fseek($fp, $pos - $readSize + 1, SEEK_END);
        $chunk = fread($fp, $readSize);
        $pos -= $readSize;

        $chunk .= $partial;
        $lines = explode("\n", $chunk);
        $partial = array_shift($lines);
        $lines = array_reverse($lines);

        foreach ($lines as $line) {
            if (strpos($line, 'formatlog:gshop_trade:') === false) continue;
            $lineDate = substr($line, 0, 10);
            if ($targetDate && strpos($line, $targetDate) !== 0) continue;

            $data = parseGShopTradeLogLine($line, $itemNames);
            if (!is_array($data)) continue;

            // фильтр по item_id
            if ($itemIdFilter > 0 && $data['item_id'] !== $itemIdFilter) continue;

            // фильтр по roleid
			if ($filterUserId > 0 && (!isset($data['userid']) || (int)$data['userid'] !== $filterUserId)) continue;


            $matchedCount++;
            if ($matchedCount <= $offset) continue;
            if (count($result) >= $limit) break 2;

            $result[] = $data;
        }
    }

    fclose($fp);

    echo json_encode(array(
        'status' => 'success',
		'debug' => array(
			'rawDateInput' => $dateFilter,
			'parsedTargetDate' => $targetDate,
			'itemIdFilter' => $itemIdFilter,
			'filterUserId' => $filterUserId,
			'post_raw' => $_POST,
		),
        'total' => $matchedCount,
        'offset' => $offset,
        'limit' => $limit,
        'data' => $result
    ));
}

function parseGShopTradeLogLine($line, $itemNames) {
    static $namesCache = [];

    $data = array(
        'line' => trim($line),
        'timestamp' => substr($line, 0, 19),
        'userid' => null,
        'username' => null,
        'item_id' => null,
        'item_name' => null,
        'item_count' => null,
        'cash_need' => null,
        'cash_left' => null,
        'order_id' => null,
    );

    preg_match('/userid=(\d+)/', $line, $m);
    if (isset($m[1])) {
        $data['userid'] = (int)$m[1];

        if (function_exists('GetRole')) {
            $rid = $data['userid'];
            if (!isset($namesCache[$rid])) {
                $r = @GetRole($rid, 63);
                $namesCache[$rid] = (is_array($r) && isset($r['value']['name']) && is_string($r['value']['name']))
                    ? $r['value']['name'] : null;
            }
            if (!empty($namesCache[$rid])) {
                $data['username'] = $namesCache[$rid];
            }
        }
    }

    preg_match('/item_id=(\d+)/', $line, $m);
    if (isset($m[1])) {
        $data['item_id'] = (int)$m[1];
        $data['item_name'] = isset($itemNames[$data['item_id']]) ? $itemNames[$data['item_id']] : "Item #{$data['item_id']}";
    }

    preg_match('/item_count=(\d+)/', $line, $m);
    if (isset($m[1])) $data['item_count'] = (int)$m[1];

    preg_match('/cash_need=([\d.]+)/', $line, $m);
    if (isset($m[1])) $data['cash_need'] = (float)$m[1];

    preg_match('/cash_left=([\d.]+)/', $line, $m);
    if (isset($m[1])) $data['cash_left'] = (float)$m[1];

    preg_match('/order_id=(\d+)/', $line, $m);
    if (isset($m[1])) $data['order_id'] = (int)$m[1];

    return $data;
}

function GetNpcTradeLogs() {
    $logFile = '/tmp/npc_trade_debug.log';
    file_put_contents($logFile, "--- START LOG ---\n");

    $logPath = '/root/logs/world2.log';
    $itemsPath = dirname(__FILE__) . '/items_ext.txt'; // для PHP 5.3

    $itemNames = loadItemNames($itemsPath);
    file_put_contents($logFile, "Loaded itemNames: " . count($itemNames) . "\n", FILE_APPEND);

    $dateFilter = isset($_POST['date']) ? $_POST['date'] : '';
    $targetDate = $dateFilter ? date('Y-m-d', strtotime($dateFilter)) : '';
    $limit = isset($_POST['limit']) ? intval($_POST['limit']) : 20;
    $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
    $filterUserId = isset($_POST['roleid']) ? intval($_POST['roleid']) : 0;
    $filterItemId = isset($_POST['itemid']) ? intval($_POST['itemid']) : 0;

    file_put_contents($logFile, "Input filters: date=$targetDate, offset=$offset, limit=$limit, roleid=$filterUserId, itemid=$filterItemId\n", FILE_APPEND);

    if (!file_exists($logPath)) {
        file_put_contents($logFile, "ERROR: Log file not found\n", FILE_APPEND);
        echo json_encode(array('status' => 'error', 'message' => 'Файл world2.log не найден'));
        return;
    }

    $result = array();
	$matchedCount = 0;
	$exit = false;

	$fp = fopen($logPath, 'rb');
	if (!$fp) {
		file_put_contents($logFile, "ERROR: Failed to open log file\n", FILE_APPEND);
		echo json_encode(array('status' => 'error', 'message' => 'Не удалось открыть лог'));
		return;
	}

	file_put_contents($logFile, "Log file opened\n", FILE_APPEND);

	$bufferSize = 65536;
	$pos = -1;
	$partial = '';
	fseek($fp, 0, SEEK_END);
	$fileSize = ftell($fp);

	while (abs($pos) < $fileSize && !$exit) {
		$readSize = min($bufferSize, $fileSize + $pos);
		fseek($fp, $pos - $readSize + 1, SEEK_END);
		$chunk = fread($fp, $readSize);
		$pos -= $readSize;

		$chunk .= $partial;
		$lines = explode("\n", $chunk);
		$partial = array_shift($lines);
		$lines = array_reverse($lines);

		foreach ($lines as $line) {
			$lineRaw = $line;
			$line = @iconv('GBK', 'UTF-8//IGNORE', $line);
			if ($line === false || $line === '') {
				$line = @mb_convert_encoding($lineRaw, 'UTF-8', 'GBK');
				if ($line === false || $line === '') {
					file_put_contents($logFile, "BAD ENCODING LINE: " . bin2hex($lineRaw) . "\n", FILE_APPEND);
					continue;
				}
			}

			if ($line === false || strlen($line) < 19) continue;

			$lineDate = substr($line, 0, 19);
			if ($targetDate && strpos($lineDate, $targetDate) !== 0) continue;

			$username = '';
			static $namesCache = array();

			// === TRADE ===
			if (strpos($line, 'item_trade') !== false) {
				preg_match('/用户 (\d+)/u', $line, $matchUser);
				$userid = isset($matchUser[1]) ? intval($matchUser[1]) : 0;
				if (!$userid) continue;
				if ($filterUserId && $filterUserId !== $userid) continue;

				preg_match('/扣除物品（itemID:(\d+)itemCount:(\d+)）/u', $line, $matchFrom);
				$itemFromId = isset($matchFrom[1]) ? intval($matchFrom[1]) : 0;
				$itemFromCount = isset($matchFrom[2]) ? intval($matchFrom[2]) : 0;

				preg_match('/添加物品（itemID:(\d+)itemCount:(\d+)）/u', $line, $matchTo);
				$itemToId = isset($matchTo[1]) ? intval($matchTo[1]) : 0;
				$itemToCount = isset($matchTo[2]) ? intval($matchTo[2]) : 0;

				if ($filterItemId && $filterItemId !== $itemFromId && $filterItemId !== $itemToId) continue;

				if (!isset($namesCache[$userid])) {
					$r = @GetRole($userid, 63);
					$namesCache[$userid] = (is_array($r) && isset($r['value']['name']) && is_string($r['value']['name']))
						? $r['value']['name'] : '';
				}
				$username = $namesCache[$userid];

				$itemFromName = isset($itemNames[$itemFromId]) ? $itemNames[$itemFromId] : "Item #$itemFromId";
				$itemToName = isset($itemNames[$itemToId]) ? $itemNames[$itemToId] : "Item #$itemToId";

				$matchedCount++;
				file_put_contents($logFile, "Matched TRADE: user=$userid, from=$itemFromId, to=$itemToId, match=$matchedCount\n", FILE_APPEND);

				if ($matchedCount <= $offset) continue;
				if (count($result) >= $limit) {
					$exit = true;
					break;
				}

				$result[] = array(
					'type' => 'trade',
					'date' => $lineDate,
					'userid' => $userid,
					'username' => $username,
					'item_from_id' => $itemFromId,
					'item_from_count' => $itemFromCount,
					'item_from_name' => $itemFromName,
					'item_to_id' => $itemToId,
					'item_to_count' => $itemToCount,
					'item_to_name' => $itemToName,
					'raw' => trim($line),
				);
				continue;
			}

			// === NPC BUY ===
			if (strpos($line, '从NPC购买了') !== false) {
				if (preg_match('/用户(\d+)从NPC购买了(\d+)个(\d+)/u', $line, $m)) {
					if (count($m) < 4) continue;

					$userid = intval($m[1]);
					$itemCount = intval($m[2]);
					$itemId = intval($m[3]);

					if (!$userid) continue;
					if ($filterItemId && $filterItemId != $itemId) continue;

					if (!isset($namesCache[$userid])) {
						$r = @GetRole($userid, 63);
						$namesCache[$userid] = (is_array($r) && isset($r['value']['name']) && is_string($r['value']['name']))
							? $r['value']['name'] : '';
					}
					$username = $namesCache[$userid];

					$itemName = isset($itemNames[$itemId]) ? $itemNames[$itemId] : "Item #$itemId";

					$matchedCount++;
					file_put_contents($logFile, "Matched NPC: user=$userid item=$itemId count=$itemCount match=$matchedCount\n", FILE_APPEND);

					if ($matchedCount <= $offset) continue;
					if (count($result) >= $limit) {
						$exit = true;
						break;
					}

					$lineDate = substr($line, 0, 19);
					if (!mb_check_encoding($lineDate, 'UTF-8')) {
						file_put_contents($logFile, "BAD UTF in lineDate (NPC): " . bin2hex($lineDate) . "\n", FILE_APPEND);
						$lineDate = '[INVALID DATE]';
					}

					$result[] = array(
						'type' => 'buy',
						'date' => $lineDate,
						'userid' => $userid,
						'username' => $username,
						'item_id' => $itemId,
						'item_count' => $itemCount,
						'item_name' => $itemName,
						'raw' => is_string($line) ? trim($line) : '[INVALID RAW]',
					);
					continue;
				}
			}
		}
	}

	fclose($fp);
	file_put_contents($logFile, "--- EXITED WHILE ---\n", FILE_APPEND);


    file_put_contents($logFile, "--- BEFORE JSON ENCODE ---\n", FILE_APPEND);
    file_put_contents($logFile, "Result count: " . count($result) . "\n", FILE_APPEND);

    foreach ($result as $i => $row) {
        file_put_contents($logFile, "#$i: " . print_r($row, true), FILE_APPEND);
    }

    // В случае фатального падения
    register_shutdown_function(function () {
        $err = error_get_last();
        if ($err) {
            file_put_contents('/tmp/npc_trade_debug.log', "--- FATAL ERROR ---\n", FILE_APPEND);
            file_put_contents('/tmp/npc_trade_debug.log', print_r($err, true), FILE_APPEND);
        }
    });

$response = array(
    'status' => 'success',
    'debug' => array(
        'rawDateInput' => $dateFilter,
        'parsedTargetDate' => $targetDate,
        'itemIdFilter' => $filterItemId,
        'filterUserId' => $filterUserId,
        'post_raw' => $_POST,
    ),
    'total' => $matchedCount,
    'offset' => $offset,
    'limit' => $limit,
    'data' => $result,
);

$json = json_encode($response);
if ($json === false || $json === null) {
    file_put_contents($logFile, "--- JSON ENCODE ERROR ---\n", FILE_APPEND);
    file_put_contents($logFile, print_r($response, true), FILE_APPEND);
    echo json_encode(array(
        'status' => 'error',
        'message' => 'Ошибка при формировании JSON',
        'debug' => array(
            'json_last_error' => json_last_error(),
            'json_last_error_msg' => function_exists('json_last_error_msg') ? json_last_error_msg() : 'no msg'
        )
    ));
    return;
}

file_put_contents($logFile, "--- JSON ENCODED OK ---\n", FILE_APPEND);
echo $json;

}

/*
function parseItemTradeLineSafe($line) {
    $line = iconv('ISO-8859-1', 'UTF-8//IGNORE', $line);

    $matches = [];

    preg_match('/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/', $line, $matches);
    $datetime = $matches[1] ?? '';

    preg_match('/ÓÃ»§ (\d+)/u', $line, $matches);
    $userid = isset($matches[1]) ? (int)$matches[1] : 0;

    preg_match('/itemID:(\d+)itemCount:(\d+)/', $line, $matches);
    $item_from_id = isset($matches[1]) ? (int)$matches[1] : 0;
    $item_from_count = isset($matches[2]) ? (int)$matches[2] : 0;

    preg_match('/repuID:(\d+)repuCount:(\d+)/', $line, $matches);
    $repu_id = isset($matches[1]) ? (int)$matches[1] : 0;
    $repu_count = isset($matches[2]) ? (int)$matches[2] : 0;

    // последняя пара itemID:itemCount — это to-предмет
    preg_match_all('/itemID:(\d+)itemCount:(\d+)/', $line, $matches);
    $lastItem = count($matches[0]) > 1 ? count($matches[0]) - 1 : 0;
    $item_to_id = isset($matches[1][$lastItem]) ? (int)$matches[1][$lastItem] : 0;
    $item_to_count = isset($matches[2][$lastItem]) ? (int)$matches[2][$lastItem] : 0;

    return [
        'datetime' => $datetime,
        'userid' => $userid,
        'item_from_id' => $item_from_id,
        'item_from_count' => $item_from_count,
        'repu_id' => $repu_id,
        'repu_count' => $repu_count,
        'item_to_id' => $item_to_id,
        'item_to_count' => $item_to_count
    ];
}
*/


function GetEloStatsByUserId() {
    if (!isset($_POST['userid'])) {
        echo json_encode(array('status' => 'error', 'message' => 'Missing userid'));
        return;
    }

    $userid = (int)$_POST['userid']; // безопасное приведение
    global $db;

    $userid_escaped = $db->real_escape_string($userid);
    $res = $db->query("SELECT player, IFNULL(stats, 0) AS elo FROM players WHERE userid = $userid_escaped");

    $data = array();
    if ($res) {
        while ($row = $res->fetch_assoc()) {
            $data[] = "roleid={$row['player']} elo={$row['elo']}";
        }

        echo json_encode(array(
            'status' => 'success',
            'userid' => $userid,
            'data' => $data
        ));
    } else {
        echo json_encode(array(
            'status' => 'error',
            'message' => 'DB query failed: ' . $db->error
        ));
    }
}

function GetEloTop() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $limit = isset($_POST['limit']) ? max(1, min(100, intval($_POST['limit']))) : 10;
    $result = [
        'user_top'    => [],
        'player_top'  => []
    ];

    // --- Топ пользователей ---
    $query = "
        SELECT 
            p.userid, 
            u.name, 
            SUM(p.stats) AS total_elo
        FROM 
            players p
        JOIN 
            users u ON u.ID = p.userid
        WHERE 
            p.stats > 0
        GROUP BY 
            p.userid
        ORDER BY 
            total_elo DESC
        LIMIT $limit
    ";
    $res = $db->query($query);
    if ($res) {
        while ($row = $res->fetch_assoc()) {
            $result['user_top'][] = [
                'userid'    => $row['userid'],
                'name'      => $row['name'],
                'total_elo' => (int)$row['total_elo']
            ];
        }
    }

    // --- Топ персонажей с class_id ---
    $query2 = "
        SELECT 
            player, 
            userid, 
            stats
        FROM 
            players
        WHERE 
            stats > 0
        ORDER BY 
            stats DESC
        LIMIT $limit
    ";
    $res2 = $db->query($query2);
    if ($res2) {
        while ($row = $res2->fetch_assoc()) {
            $role = GetRole($row['player'], 63);
            $name = !empty($role['value']['name']) ? $role['value']['name'] : 'Unknown';
            
            // Получаем class_id из статуса
            $class_id = 0;
            $level = 0;
            if (isset($role['value']['status'])) {
                $level = isset($role['value']['status']['level']) ? intval($role['value']['status']['level']) : 0;
                $class_id = isset($role['value']['status']['occupation']) ? intval($role['value']['status']['occupation']) : 0;
            }

            $result['player_top'][] = [
                'player'    => $row['player'],
                'userid'    => $row['userid'],
                'name'      => $name,
                'level'     => $level,
                'class_id'  => $class_id,
                'elo'       => (int)$row['stats']
            ];
        }
    }

    echo serialize($result);
}

function GetPveTop() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку для соединения с БД
    $db->set_charset("utf8mb4");
    
    $limit = isset($_POST['limit']) ? max(1, min(500, intval($_POST['limit']))) : 100;
    $page = isset($_POST['page']) ? intval($_POST['page']) : 1;
    if ($page < 1) $page = 1;
    
    $offset = ($page - 1) * $limit;
    
    // Получаем общее количество записей
    $count_query = "SELECT COUNT(*) as total FROM pve_top";
    $count_result = $db->query($count_query);
    $total = 0;
    if ($count_result && $count_result->num_rows > 0) {
        $row = $count_result->fetch_assoc();
        $total = $row['total'];
    }
    
    // Получаем данные с пагинацией
    $query = "SELECT * FROM pve_top ORDER BY rank ASC LIMIT $offset, $limit";
    $result = $db->query($query);
    $players = array();
    
    if ($result && $result->num_rows > 0) {
        while ($row = $result->fetch_assoc()) {
            // Принудительно конвертируем имя в UTF-8 если нужно
            $name = $row['name'];
            if (!mb_check_encoding($name, 'UTF-8')) {
                $name = utf8_encode($name);
            }
            
            // Получаем class_id через GetRole для каждого персонажа
            $class_id = 0;
            $actual_level = intval($row['level']);
            
            // Проверяем наличие функции GetRole
            if (function_exists('GetRole') && $row['player_id'] > 0) {
                $role = GetRole($row['player_id'], 63);
                if (isset($role['retcode']) && $role['retcode'] == 0 && isset($role['value']['status'])) {
                    // Если есть статус, берем актуальные данные
                    $actual_level = isset($role['value']['status']['level']) ? intval($role['value']['status']['level']) : $actual_level;
                    $class_id = isset($role['value']['status']['occupation']) ? intval($role['value']['status']['occupation']) : 0;
                }
            }
            
            $players[] = array(
                'id' => intval($row['id']),
                'rank' => intval($row['rank']),
                'player_id' => intval($row['player_id']),
                'name' => $name, // Используем имя с правильной кодировкой
                'level' => $actual_level,
                'class_id' => $class_id,
                'pve_points' => intval($row['pve_points']),
                'updated_at' => $row['updated_at']
            );
        }
    }
	
    
    echo json_encode(array(
        'status' => 'success',
        'data' => $players,
        'total' => $total,
        'page' => $page,
        'limit' => $limit,
        'pages' => ceil($total / $limit)
    ), JSON_UNESCAPED_UNICODE);

}

function GetPlayerPveInfo() {
    global $db, $pp;
    global $bossNames, $bossPoints, $achievementPoints, $achievements, $taskNames, $questPoints;

    header('Content-Type: application/json; charset=utf-8');

    if (!isset($_POST['id']) || !is_numeric($_POST['id'])) {
        echo json_encode(['status' => 'error', 'message' => 'invalid_id']);
        return;
    }

    $player_id = (int)$_POST['id'];

    $role = GetRoleData($player_id);
    if (!is_array($role) || !isset($role['retcode']) || $role['retcode'] != 0) {
        echo json_encode(['status' => 'error', 'message' => 'GetRoleData failed', 'debug' => $role]);
        return;
    }

    $val = $role['value'];
    $status = isset($val['status']) ? $val['status'] : [];
    $base   = isset($val['base']) ? $val['base'] : [];
    $base2  = isset($val['base2']) ? $val['base2'] : [];
    $pocket = isset($val['pocket']) ? $val['pocket'] : [];
	$storehouse = isset($val['storehouse']) ? $val['storehouse'] : [];
    $task   = isset($val['task']) ? $val['task'] : [];
    $ach    = isset($val['achievement']) ? $val['achievement'] : [];

    // Основные данные
    $name = isset($base['name']) ? $base['name'] : 'Unknown';
    $level = isset($status['level']) ? (int)$status['level'] : 0;
    $occupation = isset($status['occupation']) ? (int)$status['occupation'] : 0;
    $cultivation = isset($status['cultivation']) ? (int)$status['cultivation'] : 0;
    $deity_level = isset($base2['deity_level']) ? (int)$base2['deity_level'] : 0;
    $reborn_raw = '';
    if (isset($status['reborndata'])) {
        $reborn_raw = $status['reborndata'];
    } elseif (isset($val['reborndata'])) {
        $reborn_raw = $val['reborndata'];
    }

    $reborn = (!empty($reborn_raw) ? 1 : 0);

    $exp = isset($status['exp']) ? (int)$status['exp'] : 0;
    if ($level == 160 && !empty($reborn_raw)) {
        $exp = 2353727877046;
    }
    $time_used = isset($status['time_used']) ? (int)$status['time_used'] : 0;
    $battlescore = isset($status['battlescore']) ? (int)$status['battlescore'] : 0;
    $reputation = isset($status['reputation']) ? (int)$status['reputation'] : 0;
    $runescore = isset($base2['runescore']) ? (int)$base2['runescore'] : 0;
    $talismanscore = isset($status['talismanscore']) ? (int)$status['talismanscore'] : 0;
    $money = isset($pocket['money']) ? (int)$pocket['money'] : 0;

    // Форматирование времени и денег
    $money_formatted = sprintf('%d зол %d сер %d мед', floor($money / 10000), floor(($money % 10000) / 100), $money % 100);
    $days = floor($time_used / 86400);
    $hours = floor(($time_used % 86400) / 3600);
    $minutes = floor(($time_used % 3600) / 60);
    $seconds = $time_used % 60;
    $time_formatted = sprintf('%dд %02d:%02d:%02d', $days, $hours, $minutes, $seconds);

    // Вклад (credit)
    $credit_values = [];
    if (!empty($status['credit'])) {
        $bin = @hex2bin(str_replace(' ', '', $status['credit']));
        for ($i = 0; $i < strlen($bin); $i += 4) {
            $v = unpack('V', substr($bin, $i, 4));
            $credit_values[] = $v[1];
        }
    }
    
    $field_names = [
        0  => 'Очки школы Морто',
        1  => 'Очки школы Айне',
        2  => 'Очки школы Скайа',
        3  => 'Очки школы Вим',
        4  => 'Очки школы Хаккан',
        5  => 'Верность',
        6  => 'Очки романтики',
        7  => 'Интеллект',
        8  => 'Очки мастерства',
        9  => 'Очки мастерства',
        10 => 'Очки родства Эйдос',
        11 => 'Очки родства Малеус',
        12 => 'Очки родства Феор',
        13 => 'Очки школы Титан',
        14 => 'Очки школы Арден',
        15 => 'Очки школы Ниру',
        16 => 'Очки школы Войда',
        17 => 'Очки школы Умбра',
        18 => 'Очки школы Вайлин',
        19 => 'Очки школы Тайо',
        20 => 'Заслуги',
        21 => 'Подвиги',
        22 => 'Текущее значение Небо (Архая)',
        23 => 'Очки Земли (Архая)',
        24 => 'Очки Жизни (Архая)',
        25 => 'Очки Небо (Архая)',
        26 => 'Текущее значение Земля (Архая)',
        27 => 'Текущее значение Жизнь (Архая)',
        28 => 'Суверенная честь',
        29 => 'Вклад Альянса',
        30 => 'Очки школы Психея',
        31 => 'Очки школы Китос',
        32 => 'Очки школы Гидра',
        33 => '????',
        34 => 'Очки школы Сейра',
        35 => 'Очки школы Гервин',
    ];

    $credit_sum = 0;
    $credit_details = [];
    foreach ($credit_values as $i => $val) {
        if ($val > 0) {
            $label = isset($field_names[$i]) ? $field_names[$i] : "Поле $i";
            $credit_details[] = [
                'index' => $i,
                'label' => $label,
                'value' => $val
            ];
            $credit_sum += $val;
        }
    }

    // Убийства
    $kills = [];
    $q = $db->query("SELECT boss_id, MAX(date) as last_date, COUNT(*) as kills FROM killsboss WHERE player_id = $player_id GROUP BY boss_id");
    if ($q) {
        while ($r = $q->fetch_assoc()) {
            $bid = (int)$r['boss_id'];
            $kills[] = [
                'boss_id' => $bid,
                'boss' => isset($bossNames[$bid]) ? $bossNames[$bid] : "Boss #$bid",
                'kills' => (int)$r['kills'],
                'points' => isset($bossPoints[$bid]) ? $bossPoints[$bid] : 0,
                'last_date' => $r['last_date']
            ];
        }
    }

    // ============= ИСПРАВЛЕННОЕ ДЕКОДИРОВАНИЕ КВЕСТОВ =============
    
    // Функция для безопасного hex2bin
    function safeHex2Bin($hexString) {
        if (empty($hexString)) return false;
        $hexString = preg_replace('/\s+/', '', $hexString);
        if (!preg_match('/^[0-9a-fA-F]+$/', $hexString)) return false;
        return @hex2bin($hexString);
    }
    
    $complete_ids = [];
    $quest_points_total = 0;
    
    // Декодируем task_complete - смешанный формат (первый ID 4 байта, остальные 2 байта)
    if (!empty($task['task_complete'])) {
        $bin = safeHex2Bin($task['task_complete']);
        
        if ($bin !== false) {
            $offset = 0;
            $bin_len = strlen($bin);
            $isFirst = true;
            
            while ($offset < $bin_len) {
                if ($isFirst && $offset + 4 <= $bin_len) {
                    // Первый ID - 4 байта
                    $data = unpack('V', substr($bin, $offset, 4));
                    $quest_id = $data[1];
                    $offset += 4;
                    $isFirst = false;
                } else if ($offset + 2 <= $bin_len) {
                    // Остальные ID - 2 байта
                    $data = unpack('v', substr($bin, $offset, 2));
                    $quest_id = $data[1];
                    $offset += 2;
                } else {
                    break;
                }
                
                // Проверяем, что ID реалистичный
                if ($quest_id > 0 && $quest_id < 2000000) {
                    $complete_ids[] = $quest_id;
                }
            }
        }
    }
    
    // Формируем массив квестов без времени
    $quests_full = [];
    foreach ($complete_ids as $id) {
        $quests_full[] = [
            'id' => $id,
            'name' => isset($taskNames[$id]) ? $taskNames[$id] : "Квест #$id",
            'points' => isset($questPoints[$id]) ? $questPoints[$id] : 0
        ];
    }
    
    // Декодируем время завершения квестов (task_finishtime)
    $finish_times = [];
    
    if (!empty($task['task_finishtime']) && !empty($complete_ids)) {
        $bin = safeHex2Bin($task['task_finishtime']);
        
        if ($bin !== false) {
            $offset = 0;
            $bin_len = strlen($bin);
            
            // Для каждого завершенного квеста читаем временную метку (4 байта)
            foreach ($complete_ids as $quest_id) {
                if ($offset + 4 > $bin_len) break;
                
                $time_data = unpack('V', substr($bin, $offset, 4));
                $timestamp = $time_data[1];
                $offset += 4;
                
                // Проверяем, что timestamp реалистичный (между 2010 и 2030 годом)
                if ($timestamp > 1262304000 && $timestamp < 1893456000) {
                    $finish_times[] = [
                        'id' => $quest_id,
                        'name' => isset($taskNames[$quest_id]) ? $taskNames[$quest_id] : "Квест #$quest_id",
                        'timestamp' => $timestamp,
                        'date' => gmdate('Y-m-d H:i:s', $timestamp),
                        'points' => isset($questPoints[$quest_id]) ? $questPoints[$quest_id] : 0
                    ];
                }
            }
            
            // Сортируем по времени от новых к старым
            usort($finish_times, function($a, $b) {
                return $b['timestamp'] - $a['timestamp'];
            });
        }
    }
    
    // ============= ДЕКОДИРОВАНИЕ ДОСТИЖЕНИЙ =============
    
    $ach_ids = [];
    if (!empty($ach['achieve_map'])) {
        $bin = safeHex2Bin($ach['achieve_map']);
        
        if ($bin !== false) {
            for ($i = 0; $i < strlen($bin); $i++) {
                $byte = ord($bin[$i]);
                for ($b = 0; $b < 8; $b++) {
                    if ($byte & (1 << $b)) {
                        $ach_id = $i * 8 + $b + 1;
                        $ach_ids[] = $ach_id;
                    }
                }
            }
        }
    }
    
    $achievements_full = [];
    foreach ($ach_ids as $id) {
        $achievements_full[] = [
            'id' => $id,
            'name' => isset($achievements[$id]) ? $achievements[$id] : "Достижение #$id",
            'points' => isset($achievementPoints[$id]) ? $achievementPoints[$id] : 0
        ];
    }
    
    // ============= ДЕКОДИРОВАНИЕ ЭКИПИРОВКИ =============
    
    $equip_slots = [];
    if (isset($pocket['equipment']) && is_array($pocket['equipment']) && count($pocket['equipment']) > 0) {
        // Загружаем список предметов
        $items = [];
        if (($handle = fopen("items_ext.txt", "r")) !== FALSE) {
            while (($data = fgetcsv($handle, 1000, "|")) !== FALSE) {
                if (count($data) >= 2) {
                    $items[(int)$data[0]] = trim($data[1]);
                }
            }
            fclose($handle);
        }

        // Функция парсинга предметов
        if (!function_exists('parseItemHex')) {
            function parseItemHex($hexDump) {
                $hexDump = preg_replace('/\s+/', '', $hexDump);
                $bin = @pack("H*", $hexDump);
                if (strlen($bin) < 16) {
                    return ['objectId' => 0, 'itemId' => 0, 'enchant' => 0, 'augment' => 0];
                }
                $objectId = unpack("V", substr($bin, 0, 4)); $objectId = $objectId[1];
                $itemId   = unpack("V", substr($bin, 4, 4)); $itemId   = $itemId[1];
                $enchant  = unpack("v", substr($bin, 8, 2)); $enchant  = $enchant[1];
                $augment  = unpack("v", substr($bin, 14, 2)); $augment  = $augment[1];
                return [
                    'objectId' => $objectId,
                    'itemId'   => $itemId,
                    'enchant'  => $enchant,
                    'augment'  => $augment
                ];
            }
        }

        foreach ($pocket['equipment'] as $item) {
            if (!isset($item['id']) || !isset($item['pos'])) continue;

            $pos = (int)$item['pos'];
            $item_id = (int)$item['id'];
            $data_hex = isset($item['data']) ? $item['data'] : '';

            $title = isset($items[$item_id]) ? $items[$item_id] : 'Предмет #' . $item_id;

            if ($data_hex) {
                $parsed = parseItemHex($data_hex);
                if ($parsed['enchant'] > 0) $title .= ' (+' . $parsed['enchant'] . ')';
                if ($parsed['augment'] > 0) $title .= ' [Модификатор ' . $parsed['augment'] . ']';
            }

            $equip_slots[$pos] = [
                'id'    => $item_id,
                'title' => $title,
            ];
        }
    }

    // ============= ПОДГОТОВКА ДАННЫХ ДЛЯ РАСЧЕТА ОЧКОВ =============
    
    foreach ($kills as &$k) {
        $bid = (int)$k['boss_id'];
        $k['kill_count'] = isset($k['kills']) ? (int)$k['kills'] : 0;
        $k['points'] = isset($bossPoints[$bid]) ? (int)$bossPoints[$bid] : 0;
    }
    unset($k);

    $completed_count = count($complete_ids);
    
    $dataForScore = [
        'all_kills' => $kills,
        'complete_ids' => $complete_ids,
        'achieved_ids' => $ach_ids,
        'time_used' => $time_used,
        'exp' => $exp,
        'money' => isset($pocket['money']) ? (int)$pocket['money'] : 0,
        'battlescore' => $battlescore,
        'credit_summary' => $credit_sum,
        'reputation' => $reputation,
        'runescore' => $runescore,
        'talismanscore' => $talismanscore,
        'credit_details' => $credit_details,
        'level' => $level,
        'reborn' => !empty($reborn_raw) ? 1 : 0,
        'occupation_id' => isset($status['occupation']) ? (int)$status['occupation'] : 0,
        'cultivation_id' => $cultivation,
        'deity_level' => $deity_level,
        'completed_count' => $completed_count,
    ];
    
    global $creditFieldConfigs;
    $total_points = (int) calculateTotalPoints($dataForScore);
if ($player_id == ID_НИЕВЫ) { // замените на реальный ID
    error_log("=== PROFILE DEBUG ===");
    error_log("Player: $name (ID: $player_id)");
    error_log("Total points: $total_points");
    
    // Подсчет по категориям
    $quest_sum = 0;
    foreach ($complete_ids as $qid) {
        $quest_sum += isset($questPoints[$qid]) ? $questPoints[$qid] : 0;
    }
    error_log("Quests: $quest_sum");
    
    $ach_sum = 0;
    foreach ($ach_ids as $aid) {
        $ach_sum += isset($achievementPoints[$aid]) ? $achievementPoints[$aid] : 0;
    }
    error_log("Achievements: $ach_sum");
    
    $boss_sum = 0;
    foreach ($kills as $kill) {
        $bid = $kill['boss_id'];
        $boss_sum += $kill['kills'] * (isset($bossPoints[$bid]) ? $bossPoints[$bid] : 0);
    }
    error_log("Bosses: $boss_sum");
    
    $credit_sum_calc = 0;
    foreach ($credit_details as $detail) {
        $idx = $detail['index'];
        $val = $detail['value'];
        if (isset($creditFieldConfigs[$idx])) {
            $conf = $creditFieldConfigs[$idx];
            $credit_sum_calc += (($val * $conf['multiplier']) + $conf['add']) / $conf['divider'] - $conf['sub'];
        }
    }
    error_log("Credits: " . round($credit_sum_calc, 2));
    
    $metrics_sum = $total_points - $quest_sum - $ach_sum - $boss_sum - $credit_sum_calc;
    error_log("Metrics: " . round($metrics_sum, 2));
    
    // Сохраняем ID квестов
    error_log("Quest IDs: " . implode(',', $complete_ids));
}
    // ============= ФОРМИРОВАНИЕ ОТВЕТА =============
    
    $response = [
        'status' => 'success',
        'player' => [
            'id' => $player_id,
            'name' => $name,
            'level' => $level,
            'reborn' => $reborn,
            'occupation_id' => $occupation,
            'cultivation_id' => $cultivation,
            'deity_level' => $deity_level,
            'exp' => $exp,
            'money' => $money,
            'money_formatted' => $money_formatted,
            'time_used' => $time_used,
            'time_formatted' => $time_formatted
        ],
        'scores' => [
            'battlescore' => $battlescore,
            'reputation' => $reputation,
            'runescore' => $runescore,
            'talismanscore' => $talismanscore,
            'credit_summary' => $credit_sum,
            'pve_points' => $total_points,
            'completed_count' => $completed_count
        ],
        'credit_details' => $credit_details,
        'quests_completed' => count($quests_full),
        'quests' => $quests_full,
        'quests_with_time' => $finish_times,
        'achievements_completed' => count($achievements_full),
        'achievements' => $achievements_full,
        'kills' => $kills,
        'equipment' => $equip_slots,
        'updated_at' => date('Y-m-d H:i:s'),
		'pocket' => $pocket,
		'storehouse' => $storehouse,
		'skills' => isset($status['skills']) ? $status['skills'] : ''
    ];

    echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}

function GetPlayerPvpInfo()
{
    global $db, $pp;


    header('Content-Type: application/json; charset=utf-8');
    if (!isset($_POST['id']) || !is_numeric($_POST['id'])) {
        echo json_encode(['status' => 'error', 'message' => 'invalid_id']);
        exit;
    }

    $playerId = (int)$_POST['id'];

    $playerName = getRoleName($playerId);
    $role = GetRoleData($playerId);

    $playerDeityLevel = 0;
    $playerLevel = 0;
    $playerOccupation = 0;
    $playerCultivation = 0;
    $reborn = 0;

    if (is_array($role) && isset($role['retcode']) && $role['retcode'] == 0) {
        $val = $role['value'];
        $status = isset($val['status']) ? $val['status'] : [];
        $base2 = isset($val['base2']) ? $val['base2'] : [];

        $playerLevel = isset($status['level']) ? (int)$status['level'] : 0;
        $playerOccupation = isset($status['occupation']) ? (int)$status['occupation'] : 0;
        $playerCultivation = isset($status['cultivation']) ? (int)$status['cultivation'] : 0;
        $playerDeityLevel = isset($base2['deity_level']) ? (int)$base2['deity_level'] : 0;
        $reborn_raw = isset($status['reborndata']) ? $status['reborndata'] : (isset($val['reborndata']) ? $val['reborndata'] : '');
        $reborn = !empty($reborn_raw) ? 1 : 0;
    }

// Получаем PVP рейтинг и позицию
    $res = $db->query("SELECT stats FROM players WHERE player = $playerId");
    $playerElo = 0;
    $playerRank = '-';
    if ($res && $res->num_rows > 0) {
        $playerElo = (int)$res->fetch_assoc()['stats'];
        $playerRank = getEloRank($playerElo);
        if ($playerElo == 0 || $playerRank > 200) $playerRank = '-';
    }

// Подсчёт всех боёв
    $wins = $losses = 0;
    $q = $db->query("SELECT attacker_id FROM pvp_kills WHERE attacker_id = $playerId OR victim_id = $playerId");
    if ($q) {
        while ($r = $q->fetch_assoc()) {
            if ($r['attacker_id'] == $playerId) $wins++;
            else $losses++;
        }
    }
    $total_matches = $wins + $losses;
    $kd = $losses > 0 ? round($wins / $losses, 2) : ($wins > 0 ? '∞' : '0');

// Считаем streak
    $current_streak = $max_streak = 0;
    $q = $db->query("SELECT attacker_id FROM pvp_kills WHERE attacker_id = $playerId OR victim_id = $playerId ORDER BY timestamp ASC");
    if ($q) {
        while ($r = $q->fetch_assoc()) {
            if ($r['attacker_id'] == $playerId) {
                $current_streak++;
                if ($current_streak > $max_streak) $max_streak = $current_streak;
            } else $current_streak = 0;
        }
    }
    $win_streak = $current_streak;

// Подсчёт очков
    $total_net_gain = $last10_net_gain = 0;
    $q_all = $db->query("SELECT gain, loss, attacker_id FROM pvp_kills WHERE attacker_id = $playerId OR victim_id = $playerId");
    if ($q_all) while ($r = $q_all->fetch_assoc()) {
        $total_net_gain += ($r['attacker_id'] == $playerId) ? (int)$r['gain'] : -(int)$r['loss'];
    }

    $q_last10 = $db->query("SELECT gain, loss, attacker_id FROM pvp_kills WHERE attacker_id = $playerId OR victim_id = $playerId ORDER BY timestamp DESC LIMIT 10");
    if ($q_last10) while ($r = $q_last10->fetch_assoc()) {
        $last10_net_gain += ($r['attacker_id'] == $playerId) ? (int)$r['gain'] : -(int)$r['loss'];
    }

// Weekly matches
    $q_week = $db->query("SELECT COUNT(*) as cnt FROM pvp_kills WHERE (attacker_id = $playerId OR victim_id = $playerId) AND DATE(timestamp) >= CURDATE() - INTERVAL 7 DAY");
    $weekly_matches = ($q_week && $q_week->num_rows > 0) ? (int)$q_week->fetch_assoc()['cnt'] : 0;

// История событий
    $events = [];
    $q_events = $db->query("SELECT timestamp, attacker_id, victim_id, gain, loss, att_elo_before, vic_elo_before 
                        FROM pvp_kills 
                        WHERE attacker_id = $playerId OR victim_id = $playerId 
                        ORDER BY timestamp DESC");
    if ($q_events) {
        while ($r = $q_events->fetch_assoc()) {
            $isKill = ($r['attacker_id'] == $playerId);
            $oppId = $isKill ? $r['victim_id'] : $r['attacker_id'];
            $oppName = getRoleName($oppId);
            $oppElo = getElo($oppId);
            $oppRank = getEloRank($oppElo);
            if ($oppElo == 0 || $oppRank > 200) $oppRank = '-';
            $events[] = [
                'timestamp' => date('Y-m-d H:i:s', strtotime($r['timestamp'])),
                'event_type' => $isKill ? 'kill' : 'death',
                'opponent_id' => $oppId,
                'opponent_name' => $oppName,
                'gain' => (int)$r['gain'],
                'loss' => (int)$r['loss'],
                'player_delta' => $isKill ? '+' . $r['gain'] : '-' . $r['loss'],
                'opponent_delta' => $isKill ? '-' . $r['loss'] : '+' . $r['gain'],
                'hist_player_elo' => $isKill ? $r['att_elo_before'] : $r['vic_elo_before'],
                'hist_opponent_elo' => $isKill ? $r['vic_elo_before'] : $r['att_elo_before']
            ];
        }
    }

    $response = [
        'status' => 'success',
        'player' => [
            'id' => $playerId,
            'name' => $playerName,
            'level' => $playerLevel,
            'reborn' => $reborn,
            'occupation_id' => $playerOccupation,
            'cultivation_id' => $playerCultivation,
            'deity_level' => $playerDeityLevel,
            'elo' => $playerElo,
            'rank' => $playerRank
        ],
        'stats' => [
            'matches_total' => $total_matches,
            'wins' => $wins,
            'losses' => $losses,
            'kd' => $kd,
            'win_streak' => $win_streak,
            'max_streak' => $max_streak,
            'elo_total_gain' => $total_net_gain,
            'elo_last10_gain' => $last10_net_gain,
            'weekly_matches' => $weekly_matches
        ],
        'events' => $events,
        'updated_at' => date('Y-m-d H:i:s')
    ];

    echo json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}

function GetAllianceInfo() {
    global $db, $occ, $factrole, $territory_names;

    header('Content-Type: application/json; charset=utf-8');

    if (!isset($_POST['id']) || !is_numeric($_POST['id'])) {
        echo json_encode(array('status' => 'error', 'message' => 'invalid_id'));
        return;
    }

    $faction_id = intval($_POST['id']);
    if ($faction_id <= 0) {
        echo json_encode(array('status' => 'error', 'message' => 'invalid_faction_id'));
        return;
    }

    $pp = new Protocols();

    // ==== 1. Данные альянса (4604) ====
    $res = $pp->SendToGameDB(
        4604,
        array('id' => $faction_id),
        array('id' => 'unsigned int'),
        array('retcode' => 'int', 'cachesize' => 'int', 'value' => 'GFactionInfo', 'activity' => 'int', 'act_uptime' => 'int', 'base_status' => 'int')
    );

    if (!is_array($res) || !isset($res['retcode']) || $res['retcode'] != 0) {
        echo json_encode(array('status' => 'error', 'message' => 'faction_not_found', 'debug' => $res));
        return;
    }

    $faction = $res['value'];

    // ==== 2. Базовые поля ====
    $faction_name     = isset($faction['name']) ? $faction['name'] : '';
    $faction_announce = isset($faction['announce']) ? decodeOctets($faction['announce']) : '';
    $faction_level    = isset($faction['level']) ? ($faction['level'] + 1) : 0;

    // Дата создания
    $created_at = null;
    if (isset($faction['createtime']) && $faction['createtime'] > 0) {
        $created_at = date('Y-m-d H:i:s', $faction['createtime']);
    }

    // Мастер
    $master_id = isset($faction['master']) ? intval($faction['master']) : 0;
    $master_name = ($master_id > 0) ? GetRoleName($master_id) : 'Unknown';

    // ==== 3. Территории ====
    $terr_res = DBTerritoryListLoad(array(), 0);
    $territories = array();

    if (isset($terr_res['retcode']) && $terr_res['retcode'] == 0 && isset($terr_res['store']['tlist'])) {
        foreach ($terr_res['store']['tlist'] as $t) {

            if (!isset($t['owner']) || $t['owner'] != $faction_id) continue;

            $tid = isset($t['id']) ? intval($t['id']) : 0;

            // ---- НОРМАЛИЗАЦИЯ ТЕРРИТОРИИ ----
            $territories[] = array(
                'id'        => $tid,
                'name' => isset($territory_names[$tid])
                    ? $territory_names[$tid]
                    : (isset($t['name']) ? $t['name'] : 'Территория #' . $tid),
                'status'    => isset($t['status']) ? intval($t['status']) : 0,
                'status_text' => (isset($t['status']) && $t['status'] == 0) ? 'Захвачена' : 'Под атакой',

                'occupy_time_raw' => isset($t['occupy_time']) ? $t['occupy_time'] : 0,
                'occupy_time'     => (isset($t['occupy_time']) && $t['occupy_time'] > 0)
                    ? date('d.m.Y H:i', $t['occupy_time'])
                    : null,

                // опциональные поля
                'defender'      => isset($t['defender']) ? $t['defender'] : null,
                'owner_zoneid'  => isset($t['owner_zoneid']) ? $t['owner_zoneid'] : null,
                'produce'       => isset($t['produce']) ? $t['produce'] : array()
            );
        }
    }

    // ==== 4. Гильдии ====
    $guilds = array();

    if (isset($faction['member']) && is_array($faction['member'])) {
        foreach ($faction['member'] as $fam) {

            if (!isset($fam['fid'])) continue;
            $gid = intval($fam['fid']);
            if ($gid <= 0) continue;

            $ginfo = $pp->SendToGameDB(
                4613,
                array('fid' => $gid),
                array('fid' => 'unsigned int'),
                array('retcode' => 'int', 'value' => 'GFamily')
            );

            if (!isset($ginfo['retcode']) || $ginfo['retcode'] != 0 || !isset($ginfo['value'])) {
                continue;
            }

            $g = $ginfo['value'];

            // Декод объявления гильдии
            $g_announce = '';
            if (isset($g['announce'])) $g_announce = decodeOctets($g['announce']);

            // Лидер гильдии
            $g_master_id = isset($g['master']) ? intval($g['master']) : 0;
            $g_master_name = ($g_master_id > 0) ? GetRoleName($g_master_id) : 'Unknown';

            // Члены
            $members = array();
          if (isset($g['member']) && is_array($g['member'])) {
                foreach ($g['member'] as $m) {

                    // Создаем временный массив для участника
                    $m_arr = array(
                        'rid'         => isset($m['rid']) ? intval($m['rid']) : 0,
                        'name'        => isset($m['name']) ? $m['name'] : '',
                        'nickname'    => isset($m['nickname']) ? $m['nickname'] : '',
                        'level'       => isset($m['level']) ? intval($m['level']) : 0,
                        'occupation'  => isset($m['occupation']) ? GetOccupationName($m['occupation']) : '',
                        'occupation_id'=> isset($m['occupation']) ? $m['occupation'] : 0,
                        'title_id'    => isset($m['title']) ? $m['title'] : 999,
                        'title'       => isset($m['title'], $factrole[$m['title']]) ? $factrole[$m['title']] : 'Неизвестно',
                        'contribution'=> isset($m['contribution']) ? intval($m['contribution']) : 0,
                        'devotion'    => isset($m['devotion']) ? intval($m['devotion']) : 0,
                        'jointime_ts' => isset($m['jointime']) ? intval($m['jointime']) : 0,
                        'jointime'    => (isset($m['jointime']) && $m['jointime'] > 0)
                            ? date('Y-m-d H:i:s', $m['jointime'])
                            : null
                    );
                    
                    // Если это мастер, делаем запрос и получаем время
                    if ($m_arr['name'] === $master_name && function_exists('GetRole')) {
                        $r = GetRole($m_arr['rid'], 63);
                        if ($r['retcode'] == 0 && isset($r['value']['status']['time_used'])) {
                            $m_arr['time_used'] = $r['value']['status']['time_used'];
                        }
                    }
                    
                    // Добавляем сформированный массив в итоговый список
                    $members[] = $m_arr;
                }
            }

            // Сортировка (PHP 5.4)
            usort($members, function($a, $b) {
                if ($a['title_id'] != $b['title_id'])
                    return ($a['title_id'] < $b['title_id']) ? -1 : 1;

                if ($a['level'] != $b['level'])
                    return ($a['level'] > $b['level']) ? -1 : 1;

                if ($a['jointime_ts'] == $b['jointime_ts']) return 0;
                return ($a['jointime_ts'] < $b['jointime_ts']) ? -1 : 1;
            });

            // Гильдия
            $guilds[] = array(
                'id'        => $gid,
                'name'      => isset($g['name']) ? $g['name'] : '',
                'master_id' => $g_master_id,
                'master'    => $g_master_name,
                'announce'  => $g_announce,
                'createtime'=> (isset($g['createtime']) && $g['createtime'] > 0)
                    ? date('Y-m-d H:i:s', $g['createtime'])
                    : null,
                'members'   => $members,
                'member_count' => count($members)
            );
        }
    }

    // ==== 5. Очки со склада ====
    $points = array();
    if ($db) {
        $id_esc = intval($faction_id);
        $qr = $db->query("SELECT * FROM alliance_points WHERE alliance_id = $id_esc LIMIT 1");
        if ($qr && $qr->num_rows > 0) {
            $row = $qr->fetch_assoc();
            $points = $row;
        }
    }

    // ==== 6. Финальный JSON ====
    echo json_encode(
        array(
            'status' => 'success',
            'alliance' => array(
                'id'          => $faction_id,
                'name'        => $faction_name,
                'announce'    => $faction_announce,
                'level'       => $faction_level,
                'master_id'   => $master_id,
                'master_name' => $master_name,
                'created_at'  => $created_at,
                'prosperity'  => isset($faction['prosperity']) ? $faction['prosperity'] : 0,
                'population'  => isset($faction['population']) ? $faction['population'] : 0,
                'status'      => isset($faction['status']) ? $faction['status'] : 0,
                'contribution'=> isset($faction['contribution']) ? $faction['contribution'] : 0,
                'nimbus'      => isset($faction['nimbus']) ? $faction['nimbus'] : 0,
                'charm'       => isset($faction['charm']) ? $faction['charm'] : 0,
                'name_history'=> isset($faction['namehis']) ? decodeOctets($faction['namehis']) : '',
                'territories' => $territories,
                'guilds'      => $guilds,
                'points'      => $points
            )
        ),
        JSON_UNESCAPED_UNICODE
    );
}

function getClanInfo()
{
    global $occ, $factrole;

    if (!isset($_POST['id'])) {
        return array("error" => "No clan id");
    }

    $clan_id = intval($_POST['id']);
    if ($clan_id <= 0) {
        return array("error" => "Invalid clan id");
    }

    $pp = new Protocols();

    $family_arg = array('id' => $clan_id);
    $family_res = $pp->SendToGameDB(
        4613,
        $family_arg,
        array('id' => 'unsigned int'),
        array('retcode' => 'int', 'value' => 'GFamily')
    );

    if ($family_res['retcode'] != 0 || !isset($family_res['value'])) {
        return array("error" => "Clan not found");
    }

    $clan = $family_res['value'];

    // Сбор базовой информации
    $data = array(
        "id"        => $clan_id,
        "name"      => isset($clan['name']) ? $clan['name'] : "",
        "created"   => isset($clan['createtime']) ? date("d.m.y H:i", $clan['createtime']) : "",
        "faction"   => array(
            "id" => isset($clan['factionid']) ? intval($clan['factionid']) : 0,
            "name" => null
        ),
        "leader" => null,
        "members" => array(),
        "skills"  => array()
    );

    // --- Получаем альянс ---
    if ($data["faction"]["id"] > 0) {
        $f_res = $pp->SendToGameDB(
            4604,
            array("id" => $data["faction"]["id"]),
            array("id" => "unsigned int"),
            array(
                "retcode"    => "int",
                "cachesize"  => "int",
                "value"      => "GFactionInfo",
                "activity"   => "int",
                "act_uptime" => "int",
                "base_status"=> "int"
            )
        );

        if ($f_res["retcode"] == 0 && isset($f_res["value"]["name"])) {
            $data["faction"]["name"] = $f_res["value"]["name"];
        } else {
            $data["faction"]["name"] = "Альянс #" . $data["faction"]["id"];
        }
    }


    // --- Члены клана ---
    if (isset($clan['member']) && is_array($clan['member'])) {

        $leader = null;

        foreach ($clan['member'] as $m) {

            $occ_name = isset($m['occupation']) && isset($occ[$m['occupation']])
                ? $occ[$m['occupation']]
                : "Неизвестно";

            $role = isset($factrole[$m['title']])
                ? $factrole[$m['title']]
                : "";

            $member = array(
                "rid"         => $m['rid'],
                "name"        => $m['name'],
                "level"       => $m['level'],
                "class"       => $occ_name,
                "title"       => $role,
                "jointime_ts" => isset($m['jointime']) ? intval($m['jointime']) : 0,
                "title_id"    => isset($m['title']) ? intval($m['title']) : 999,
                "contribution"=> isset($m['contribution']) ? $m['contribution'] : 0,
                "jointime"    => isset($m['jointime']) ? date("d.m.y H:i", $m['jointime']) : "",
                "devotion"    => isset($m['devotion']) ? $m['devotion'] : 0
            );
			if ($member['title_id'] == 2 && function_exists('GetRole')) {
                $r = GetRole($m['rid'], 63);
                if ($r['retcode'] == 0 && isset($r['value']['status']['time_used'])) {
                    $member['time_used'] = $r['value']['status']['time_used'];
                }
            }		

            $data["members"][] = $member;
        }
    }
    usort($data["members"], function($a, $b) {
        if ($a['title_id'] != $b['title_id'])
            return ($a['title_id'] < $b['title_id']) ? -1 : 1;

        if ($a['level'] != $b['level'])
            return ($a['level'] > $b['level']) ? -1 : 1;

        if ($a['jointime_ts'] == $b['jointime_ts']) return 0;
        return ($a['jointime_ts'] < $b['jointime_ts']) ? -1 : 1;
    });
    $data["leader"] = isset($data["members"][0]) ? $data["members"][0] : null;

    // --- Навыки ---
    if (isset($clan['skills']) && is_array($clan['skills'])) {
        foreach ($clan['skills'] as $s) {
            $data["skills"][] = array(
                "id"      => $s['id'],
                "level"   => $s['level'],
                "ability" => $s['ability']
            );
        }
    }

    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
}

// ============= АУКЦИОННЫЕ ФУНКЦИИ =============

function auction_admin() {
global $db;
    
    error_log("=== auction_admin() called ===");
    
    $subop = isset($_POST['subop']) ? trim($_POST['subop']) : '';
    
    error_log("Subop: $subop");
    
    switch ($subop) {
        case 'add_auction':
            error_log("Processing add_auction in auction_admin()");
            
            // Проверяем наличие всех обязательных полей
            $required_fields = array('category_id', 'name', 'item_type', 'start_price', 'min_bid_step');
            foreach ($required_fields as $field) {
                if (!isset($_POST[$field]) || empty($_POST[$field])) {
                    $error_msg = "Не заполнено обязательное поле: $field";
                    error_log($error_msg);
                    echo serialize(array('errorcode' => 1, 'errortxt' => $error_msg));
                    return;
                }
            }
            
            $category_id = intval($_POST['category_id']);
            $name = trim($_POST['name']);
            $item_type = trim($_POST['item_type']);
            $image_url = isset($_POST['image_url']) ? trim($_POST['image_url']) : '';
            $description = isset($_POST['description']) ? trim($_POST['description']) : '';
            $source = isset($_POST['source']) ? trim($_POST['source']) : '';
            $start_price = intval($_POST['start_price']);
            $min_bid_step = intval($_POST['min_bid_step']);
            
            // Проверяем, это шаблон или обычный аукцион
            $is_template = isset($_POST['is_template']) ? intval($_POST['is_template']) : 0;
            $duration_minutes = isset($_POST['duration_minutes']) ? intval($_POST['duration_minutes']) : 1440;
            
            // Получаем предметы из JSON
            $items_json = isset($_POST['items']) ? $_POST['items'] : '[]';
            
            // Для PHP 5.4 используем json_decode с флагом
            $items = json_decode($items_json, true);
            
            // Проверка JSON для PHP 5.4
            if (function_exists('json_last_error')) {
                $json_error = json_last_error();
                if ($json_error !== JSON_ERROR_NONE) {
                    $error_msg = 'Ошибка формата JSON предметов: ' . $json_error;
                    error_log($error_msg);
                    echo serialize(array('errorcode' => 1, 'errortxt' => $error_msg));
                    return;
                }
            }
            
            if (!$items || !is_array($items)) {
                $error_msg = 'Ошибка формата данных предметов';
                error_log($error_msg);
                echo serialize(array('errorcode' => 1, 'errortxt' => $error_msg));
                return;
            }
            
            // Проверяем, есть ли хотя бы один предмет
            $hasValidItem = false;
            $firstItem = null;
            
            foreach ($items as $item) {
                if (isset($item['item_id']) && intval($item['item_id']) > 0) {
                    $hasValidItem = true;
                    $firstItem = $item;
                    break;
                }
            }
            
            if (!$hasValidItem) {
                $error_msg = 'Добавьте хотя бы один предмет с валидным ID';
                error_log($error_msg);
                echo serialize(array('errorcode' => 1, 'errortxt' => $error_msg));
                return;
            }
            
            if ($is_template) {
                // Сохраняем как шаблон
                $all_items_json = json_encode($items);
                if ($all_items_json === false) {
                    $all_items_json = '[]';
                }
                $all_items_json_escaped = $db->real_escape_string($all_items_json);
                
                // Сохраняем шаблон
                $sql = "INSERT INTO auction_templates (
                    category_id, name, item_type, image_url, description, source, 
                    start_price, min_bid_step, duration_minutes, items_json, is_active
                ) VALUES (
                    $category_id,
                    '" . $db->real_escape_string($name) . "',
                    '" . $db->real_escape_string($item_type) . "',
                    '" . $db->real_escape_string($image_url) . "',
                    '" . $db->real_escape_string($description) . "',
                    '" . $db->real_escape_string($source) . "',
                    $start_price,
                    $min_bid_step,
                    $duration_minutes,
                    '$all_items_json_escaped',
                    1
                )";
                
                error_log("Template SQL: $sql");
                
                if ($db->query($sql)) {
                    $template_id = $db->insert_id;
                    
                    // Проверяем, нужно ли создать аукцион сейчас
                    $auction_id = 0;
                    
                    // Получаем информацию о категории
                    $category_result = $db->query("SELECT auto_repeat FROM auction_categories WHERE id = $category_id");
                    if ($category_result && $category_result->num_rows > 0) {
                        $category = $category_result->fetch_assoc();
                        
                        // Если категория не имеет авто-повтора, создаем аукцион сразу
                        if (isset($category['auto_repeat']) && !$category['auto_repeat']) {
                            $auction_id = create_auction_from_template($template_id, $category_id);
                        }
                    }
                    
                    echo serialize(array(
                        'errorcode' => 0, 
                        'id' => $template_id, 
                        'is_template' => 1,
                        'auction_id' => $auction_id
                    ));
                } else {
                    echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $db->error));
                }
            } else {
                // Создаем обычный аукцион
                $start_time = isset($_POST['start_time']) ? trim($_POST['start_time']) : date('Y-m-d H:i:s');
                $end_time = isset($_POST['end_time']) ? trim($_POST['end_time']) : date('Y-m-d H:i:s', strtotime("+$duration_minutes minutes"));
                
                // Используем первый предмет как основной для аукциона
                $item_id = isset($firstItem['item_id']) ? intval($firstItem['item_id']) : 0;
                $count = isset($firstItem['count']) ? intval($firstItem['count']) : 1;
                $client_size = isset($firstItem['client_size']) ? intval($firstItem['client_size']) : 0;
                $max_count = isset($firstItem['max_count']) ? intval($firstItem['max_count']) : 1;
                $data = isset($firstItem['data']) ? $firstItem['data'] : '';
                $proctype = isset($firstItem['proctype']) ? intval($firstItem['proctype']) : 0;
                $expire_date = isset($firstItem['expire_date']) ? intval($firstItem['expire_date']) : 0;
                
                // Экранируем строки для БД
                $name_escaped = $db->real_escape_string($name);
                $item_type_escaped = $db->real_escape_string($item_type);
                $image_url_escaped = $db->real_escape_string($image_url);
                $description_escaped = $db->real_escape_string($description);
                $source_escaped = $db->real_escape_string($source);
                $data_escaped = $db->real_escape_string($data);
                
                // Сохраняем все предметы как JSON
                $all_items_json = json_encode($items);
                if ($all_items_json === false) {
                    $all_items_json = '[]';
                }
                $all_items_json_escaped = $db->real_escape_string($all_items_json);
                
                // Добавляем created_by
                $created_by = isset($_POST['admin_id']) ? intval($_POST['admin_id']) : 0;
                if ($created_by <= 0) {
                    $created_by = 1; // Дефолтный администратор
                }
                
                // Формируем SQL запрос
                $sql = "INSERT INTO auction_items (
                    category_id, name, item_type, item_id, count, client_size, max_count, 
                    data, proctype, expire_date, source, image_url, description, 
                    start_price, min_bid_step, start_time, end_time, status, all_items_json, created_by,
                    current_bid, current_bidder_id, current_bidder_name
                ) VALUES (
                    $category_id, 
                    '$name_escaped', 
                    '$item_type_escaped', 
                    $item_id, 
                    $count, 
                    $client_size, 
                    $max_count,
                    '$data_escaped', 
                    $proctype, 
                    $expire_date, 
                    '$source_escaped', 
                    '$image_url_escaped', 
                    '$description_escaped',
                    $start_price, 
                    $min_bid_step, 
                    '$start_time', 
                    '$end_time', 
                    'active', 
                    '$all_items_json_escaped', 
                    $created_by,
                    0, 0, ''
                )";
                
                error_log("SQL query: $sql");
                
                if ($db->query($sql)) {
                    $auction_id = $db->insert_id;
                    error_log("Auction created successfully! ID: $auction_id");
                    echo serialize(array('errorcode' => 0, 'id' => $auction_id, 'message' => 'Аукцион создан'));
                } else {
                    $error = $db->error;
                    error_log("Database error: $error");
                    echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $error));
                }
            }
            break;
            
        case 'add_template':
            $category_id = intval($_POST['category_id']);
            $name = $db->real_escape_string(trim($_POST['name']));
            $item_type = $db->real_escape_string(trim($_POST['item_type']));
            $image_url = isset($_POST['image_url']) ? $db->real_escape_string(trim($_POST['image_url'])) : '';
            $description = isset($_POST['description']) ? $db->real_escape_string(trim($_POST['description'])) : '';
            $source = isset($_POST['source']) ? $db->real_escape_string(trim($_POST['source'])) : '';
            $start_price = intval($_POST['start_price']);
            $min_bid_step = intval($_POST['min_bid_step']);
            $duration_minutes = intval($_POST['duration_minutes']);
            
            $items_json = isset($_POST['items']) ? $_POST['items'] : '[]';
            $all_items_json_escaped = $db->real_escape_string($items_json);
            
            $sql = "INSERT INTO auction_templates (
                category_id, name, item_type, image_url, description, source, 
                start_price, min_bid_step, duration_minutes, items_json, is_active
            ) VALUES (
                $category_id,
                '$name',
                '$item_type',
                '$image_url',
                '$description',
                '$source',
                $start_price,
                $min_bid_step,
                $duration_minutes,
                '$all_items_json_escaped',
                1
            )";
            
            if ($db->query($sql)) {
                echo serialize(array('errorcode' => 0, 'id' => $db->insert_id));
            } else {
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $db->error));
            }
            break;
			
			case 'edit_template':
    error_log("=== edit_template called ===");
    error_log("POST data: " . print_r($_POST, true));
    
    $template_id = isset($_POST['id']) ? intval($_POST['id']) : 0;
    $category_id = isset($_POST['category_id']) ? intval($_POST['category_id']) : 0;
    $name = isset($_POST['name']) ? trim($_POST['name']) : '';
    $item_type = isset($_POST['item_type']) ? trim($_POST['item_type']) : '';
    $image_url = isset($_POST['image_url']) ? trim($_POST['image_url']) : '';
    $description = isset($_POST['description']) ? trim($_POST['description']) : '';
    $source = isset($_POST['source']) ? trim($_POST['source']) : '';
    $start_price = isset($_POST['start_price']) ? intval($_POST['start_price']) : 100;
    $min_bid_step = isset($_POST['min_bid_step']) ? intval($_POST['min_bid_step']) : 100;
    $duration_minutes = isset($_POST['duration_minutes']) ? intval($_POST['duration_minutes']) : 1440;
    
    // Экранирование
    $name_escaped = $db->real_escape_string($name);
    $item_type_escaped = $db->real_escape_string($item_type);
    $image_url_escaped = $db->real_escape_string($image_url);
    $description_escaped = $db->real_escape_string($description);
    $source_escaped = $db->real_escape_string($source);
    
    // Получаем предметы из JSON
    $items_json = isset($_POST['items']) ? $_POST['items'] : '[]';
    $all_items_json_escaped = $db->real_escape_string($items_json);
    
    if ($template_id <= 0) {
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Неверный ID шаблона'));
        return;
    }
    
    if (empty($name) || empty($item_type) || $category_id <= 0) {
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Заполните обязательные поля'));
        return;
    }
    
    $sql = "UPDATE auction_templates SET 
        category_id = $category_id,
        name = '$name_escaped',
        item_type = '$item_type_escaped',
        image_url = '$image_url_escaped',
        description = '$description_escaped',
        source = '$source_escaped',
        start_price = $start_price,
        min_bid_step = $min_bid_step,
        duration_minutes = $duration_minutes,
        items_json = '$all_items_json_escaped'
        WHERE id = $template_id";
    
    error_log("Edit template SQL: $sql");
    
    if ($db->query($sql)) {
        echo serialize(array('errorcode' => 0, 'id' => $template_id));
    } else {
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $db->error));
    }
    break;
            
        case 'create_auction_from_template':
    $template_id = intval($_POST['template_id']);
    
    if ($template_id <= 0) {
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Неверный ID шаблона'));
        return;
    }
    
    // Получаем данные шаблона
    $template_result = $db->query("SELECT * FROM auction_templates WHERE id = $template_id");
    if (!$template_result || $template_result->num_rows == 0) {
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Шаблон не найден'));
        return;
    }
    
    $template = $template_result->fetch_assoc();
    
    // Получаем категорию шаблона
    $category_id = $template['category_id'];
    
    // Определяем время начала и окончания
    $now = time();
    $start_time = date('Y-m-d H:i:s', $now);
    
    // Используем длительность из шаблона (по умолчанию 1440 минут = 24 часа)
    $duration_minutes = isset($template['duration_minutes']) ? $template['duration_minutes'] : 1440;
    $end_time = date('Y-m-d H:i:s', $now + ($duration_minutes * 60));
    
    // Получаем предметы из JSON
    $items_json = isset($template['items_json']) ? $template['items_json'] : '[]';
    $items = json_decode($items_json, true);
    
    if (!$items || !is_array($items) || empty($items)) {
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка данных предметов в шаблоне'));
        return;
    }
    
    // Используем первый предмет
    $firstItem = $items[0];
    $item_id = isset($firstItem['item_id']) ? intval($firstItem['item_id']) : 0;
    $count = isset($firstItem['count']) ? intval($firstItem['count']) : 1;
    $client_size = isset($firstItem['client_size']) ? intval($firstItem['client_size']) : 0;
    $max_count = isset($firstItem['max_count']) ? intval($firstItem['max_count']) : 1;
    $data = isset($firstItem['data']) ? $firstItem['data'] : '';
    $proctype = isset($firstItem['proctype']) ? intval($firstItem['proctype']) : 0;
    $expire_date = isset($firstItem['expire_date']) ? intval($firstItem['expire_date']) : 0;
    
    // Экранируем строки для БД
    $name_escaped = $db->real_escape_string($template['name']);
    $item_type_escaped = $db->real_escape_string($template['item_type']);
    $image_url_escaped = isset($template['image_url']) ? $db->real_escape_string($template['image_url']) : '';
    $description_escaped = isset($template['description']) ? $db->real_escape_string($template['description']) : '';
    $source_escaped = isset($template['source']) ? $db->real_escape_string($template['source']) : '';
    $data_escaped = $db->real_escape_string($data);
    $all_items_json_escaped = $db->real_escape_string($items_json);
    
    // Проверяем данные
    if ($item_id <= 0) {
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Неверный ID предмета в шаблоне'));
        return;
    }
    
    // Формируем SQL запрос для создания аукциона
    $sql = "INSERT INTO auction_items (
        category_id, name, item_type, item_id, count, client_size, max_count, 
        data, proctype, expire_date, source, image_url, description, 
        start_price, min_bid_step, start_time, end_time, status, all_items_json, created_by,
        current_bid, current_bidder_id, current_bidder_name, template_id
    ) VALUES (
        $category_id, 
        '$name_escaped', 
        '$item_type_escaped', 
        $item_id, 
        $count, 
        $client_size, 
        $max_count,
        '$data_escaped', 
        $proctype, 
        $expire_date, 
        '$source_escaped', 
        '$image_url_escaped', 
        '$description_escaped',
        " . (isset($template['start_price']) ? $template['start_price'] : 100) . ", 
        " . (isset($template['min_bid_step']) ? $template['min_bid_step'] : 100) . ", 
        '$start_time', 
        '$end_time', 
        'active', 
        '$all_items_json_escaped', 
        1,
        0, 0, '',
        $template_id
    )";
    
    error_log("Creating auction from template SQL: $sql");
    
    if ($db->query($sql)) {
        $auction_id = $db->insert_id;
        error_log("Auction created from template ID: $auction_id");
        echo serialize(array('errorcode' => 0, 'auction_id' => $auction_id));
    } else {
        $error = $db->error;
        error_log("Database error when creating auction from template: $error");
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $error));
    }
    break;
            
        case 'toggle_template':
            $template_id = intval($_POST['id']);
            $sql = "UPDATE auction_templates SET is_active = NOT is_active WHERE id = $template_id";
            
            if ($db->query($sql)) {
                echo serialize(array('errorcode' => 0));
            } else {
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $db->error));
            }
            break;
            
        case 'delete_template':
            $template_id = intval($_POST['id']);
            $sql = "DELETE FROM auction_templates WHERE id = $template_id";
            
            if ($db->query($sql)) {
                echo serialize(array('errorcode' => 0));
            } else {
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $db->error));
            }
            break;
            
        case 'get_template':
    error_log("=== get_template START ===");
    error_log("POST data: " . print_r($_POST, true));
    
    // Устанавливаем заголовок для JSON ответа
    header('Content-Type: application/json');
    
    // Используем разные возможные имена параметра
    $template_id = 0;
    if (isset($_POST['template_id'])) {
        $template_id = intval($_POST['template_id']);
    } elseif (isset($_POST['id'])) {
        $template_id = intval($_POST['id']);
    }
    
    error_log("Template ID received: " . $template_id);
    
    if ($template_id <= 0) {
        error_log("Invalid template ID");
        echo json_encode(array('errorcode' => 1, 'errortxt' => 'Неверный ID шаблона'));
        exit;
    }
    
    // Получаем данные шаблона
    $result = $db->query("SELECT * FROM auction_templates WHERE id = $template_id");
    
    if ($result && $result->num_rows > 0) {
        $template = $result->fetch_assoc();
        
        // Декодируем JSON для удобства клиента
        if (isset($template['items_json'])) {
            $items = json_decode($template['items_json'], true);
            if ($items && is_array($items) && count($items) > 0) {
                // Добавляем первый предмет в корень шаблона для удобства
                $firstItem = $items[0];
                $template['item_id'] = isset($firstItem['item_id']) ? $firstItem['item_id'] : 0;
                $template['count'] = isset($firstItem['count']) ? $firstItem['count'] : 1;
                $template['client_size'] = isset($firstItem['client_size']) ? $firstItem['client_size'] : 0;
                $template['max_count'] = isset($firstItem['max_count']) ? $firstItem['max_count'] : 1;
                $template['data'] = isset($firstItem['data']) ? $firstItem['data'] : '';
                $template['proctype'] = isset($firstItem['proctype']) ? $firstItem['proctype'] : 0;
                $template['expire_date'] = isset($firstItem['expire_date']) ? $firstItem['expire_date'] : 0;
            }
        }
        
        error_log("Template found, returning JSON");
        echo json_encode(array('errorcode' => 0, 'template' => $template));
    } else {
        error_log("Template not found");
        echo json_encode(array('errorcode' => 1, 'errortxt' => 'Шаблон не найден'));
    }
    exit;
	break;
	
	case 'get_templates':
    error_log("=== get_templates called ===");
    
    $category_id = isset($_POST['category_id']) ? intval($_POST['category_id']) : 0;
    $where = "WHERE t.is_active = 1";
    
    if ($category_id > 0) {
        $where .= " AND t.category_id = $category_id";
    }
    
    $sql = "SELECT t.*, c.name as category_name 
            FROM auction_templates t 
            LEFT JOIN auction_categories c ON t.category_id = c.id 
            $where 
            ORDER BY t.id DESC";
    
    error_log("SQL query: " . $sql);
    
    $result = $db->query($sql);
    
    if (!$result) {
        error_log("Database error: " . $db->error);
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $db->error));
        return;
    }
    
    $templates = array();
    
    if ($result->num_rows > 0) {
        while ($row = $result->fetch_assoc()) {
            $templates[] = $row;
        }
    }
    
    error_log("Found " . count($templates) . " templates");
    echo serialize(array('errorcode' => 0, 'templates' => $templates));
    break;
            
        case 'cancel_auction':
            $id = intval($_POST['id']);
            $sql = "UPDATE auction_items SET status = 'cancelled' WHERE id = $id";
            
            if ($db->query($sql)) {
                $db->query("DELETE FROM auction_bids WHERE auction_id = $id");
                echo serialize(array('errorcode' => 0, 'message' => 'Аукцион отменен'));
            } else {
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $db->error));
            }
            break;
            
        case 'add_category':
            $name = $db->real_escape_string($_POST['name']);
            $duration_type = $db->real_escape_string($_POST['duration_type']);
            $is_active = intval($_POST['is_active']);
            $auto_repeat = isset($_POST['auto_repeat']) ? intval($_POST['auto_repeat']) : 0;
            $repeat_type = isset($_POST['repeat_type']) ? $db->real_escape_string($_POST['repeat_type']) : 'daily';
            $repeat_dow = isset($_POST['repeat_dow']) ? $db->real_escape_string($_POST['repeat_dow']) : '';
            $repeat_hour = isset($_POST['repeat_hour']) ? $db->real_escape_string($_POST['repeat_hour']) : '00:00:00';
            
            $sql = "INSERT INTO auction_categories (name, duration_type, is_active, auto_repeat, repeat_type, repeat_dow, repeat_hour) 
                    VALUES ('$name', '$duration_type', $is_active, $auto_repeat, '$repeat_type', '$repeat_dow', '$repeat_hour')";
            
            if ($db->query($sql)) {
                echo serialize(array('errorcode' => 0, 'id' => $db->insert_id, 'message' => 'Категория добавлена'));
            } else {
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $db->error));
            }
            break;
            
        case 'edit_category':
            $id = intval($_POST['id']);
            $name = $db->real_escape_string($_POST['name']);
            $duration_type = isset($_POST['duration_type']) ? $db->real_escape_string($_POST['duration_type']) : 'daily';
            $is_active = intval($_POST['is_active']);
            $auto_repeat = isset($_POST['auto_repeat']) ? intval($_POST['auto_repeat']) : 0;
            $repeat_type = isset($_POST['repeat_type']) ? $db->real_escape_string($_POST['repeat_type']) : 'daily';
            $repeat_dow = isset($_POST['repeat_dow']) ? $db->real_escape_string($_POST['repeat_dow']) : '';
            $repeat_hour = isset($_POST['repeat_hour']) ? $db->real_escape_string($_POST['repeat_hour']) : '00:00:00';
            
            $sql = "UPDATE auction_categories SET 
                    name = '$name', 
                    duration_type = '$duration_type',
                    is_active = $is_active,
                    auto_repeat = $auto_repeat,
                    repeat_type = '$repeat_type',
                    repeat_dow = '$repeat_dow',
                    repeat_hour = '$repeat_hour'
                    WHERE id = $id";
            
            if ($db->query($sql)) {
                echo serialize(array('errorcode' => 0, 'message' => 'Категория обновлена'));
            } else {
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $db->error));
            }
            break;
            
        case 'update_category_repeat':
            $category_id = intval($_POST['category_id']);
            $auto_repeat = intval($_POST['auto_repeat']);
            $repeat_type = $db->real_escape_string($_POST['repeat_type']);
            $repeat_dow = isset($_POST['repeat_dow']) ? $db->real_escape_string($_POST['repeat_dow']) : '';
            $repeat_hour = isset($_POST['repeat_hour']) ? $db->real_escape_string($_POST['repeat_hour']) : '00:00:00';
            
            $sql = "UPDATE auction_categories SET 
                auto_repeat = $auto_repeat,
                repeat_type = '$repeat_type',
                repeat_dow = '$repeat_dow',
                repeat_hour = '$repeat_hour'
                WHERE id = $category_id";
            
            if ($db->query($sql)) {
                echo serialize(array('errorcode' => 0));
            } else {
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $db->error));
            }
            break;
            
        case 'delete_category':
            $id = intval($_POST['id']);
            
            // Проверка активных аукционов
            $check = $db->query("SELECT COUNT(*) as cnt FROM auction_items WHERE category_id = $id AND status = 'active'");
            if ($check && $row = $check->fetch_assoc()) {
                if ($row['cnt'] > 0) {
                    echo serialize(array('errorcode' => 1, 'errortxt' => 'Невозможно удалить категорию с активными аукционами'));
                    return;
                }
            }
            
            $sql = "DELETE FROM auction_categories WHERE id = $id";
            
            if ($db->query($sql)) {
                $db->query("DELETE FROM auction_items WHERE category_id = $id");
                $db->query("DELETE FROM auction_templates WHERE category_id = $id");
                echo serialize(array('errorcode' => 0, 'message' => 'Категория удалена'));
            } else {
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка БД: ' . $db->error));
            }
            break;
            
        case 'get_categories':
            $result = $db->query("SELECT * FROM auction_categories ORDER BY id");
            $categories = array();
            
            if ($result && $result->num_rows > 0) {
                while ($row = $result->fetch_assoc()) {
                    $categories[] = $row;
                }
            }
            
            echo serialize(array('errorcode' => 0, 'categories' => $categories));
            break;
            
        case 'get_active_auctions':
            $category_id = isset($_POST['category_id']) ? intval($_POST['category_id']) : 0;
            $where = "WHERE status = 'active'";
            
            if ($category_id > 0) {
                $where .= " AND category_id = $category_id";
            }
            
            $result = $db->query("
                SELECT a.*, c.name as category_name, c.duration_type 
                FROM auction_items a 
                LEFT JOIN auction_categories c ON a.category_id = c.id 
                $where 
                ORDER BY end_time ASC
            ");
            
            $auctions = array();
            if ($result && $result->num_rows > 0) {
                while ($row = $result->fetch_assoc()) {
                    $auctions[] = $row;
                }
            }
            
            echo serialize(array('errorcode' => 0, 'auctions' => $auctions));
            break;
            
        case 'get_ended_auctions':
            $limit = isset($_POST['limit']) ? intval($_POST['limit']) : 20;
            
            // Пытаемся получить из auction_history
            $result = $db->query("
                SELECT * FROM auction_history 
                ORDER BY end_time DESC 
                LIMIT $limit
            ");
            
            // Если нет записей в auction_history, смотрим в auction_items с status='completed'
            if (!$result || $result->num_rows == 0) {
                $result = $db->query("
                    SELECT 
                        id as auction_id,
                        name,
                        item_type,
                        category_id,
                        0 as winner_id,
                        '' as winner_name,
                        current_bid as final_price,
                        start_price,
                        start_time,
                        end_time,
                        all_items_json as items_json,
                        template_id
                    FROM auction_items 
                    WHERE status = 'completed'
                    ORDER BY end_time DESC 
                    LIMIT $limit
                ");
            }
            
            $auctions = array();
            if ($result && $result->num_rows > 0) {
                while ($row = $result->fetch_assoc()) {
                    $auctions[] = $row;
                }
            }
            
            error_log("Found " . count($auctions) . " ended auctions");
            echo serialize(array('errorcode' => 0, 'auctions' => $auctions));
            break;
            
        case 'get_category_info':
            $category_id = intval($_POST['category_id']);
            $result = $db->query("SELECT * FROM auction_categories WHERE id = $category_id");
            
            if ($result && $result->num_rows > 0) {
                $category = $result->fetch_assoc();
                $info = "Тип: " . (isset($category['duration_type']) ? $category['duration_type'] : 'daily') . " | ";
                $info .= "Авто-повтор: " . (isset($category['auto_repeat']) && $category['auto_repeat'] ? 'Да' : 'Нет');
                
                if (isset($category['auto_repeat']) && $category['auto_repeat']) {
                    $info .= " (" . (isset($category['repeat_type']) ? $category['repeat_type'] : 'daily');
                    if (isset($category['repeat_hour']) && $category['repeat_hour']) {
                        $info .= " в " . substr($category['repeat_hour'], 0, 5);
                    }
                    $info .= ")";
                }
                
                echo $info;
            } else {
                echo "Информация не найдена";
            }
            break;
            
        default:
            echo serialize(array('errorcode' => 1, 'errortxt' => 'Неизвестный subop'));
            break;
    }
}

// Функция создания аукциона из шаблона
function create_auction_from_template($template_id, $category_id = null) {
    global $db;
    
    $template_result = $db->query("SELECT * FROM auction_templates WHERE id = $template_id AND is_active = 1");
    if (!$template_result || $template_result->num_rows == 0) {
        return false;
    }
    
    $template = $template_result->fetch_assoc();
    
    if (!$category_id) {
        $category_id = $template['category_id'];
    }
    
    // Получаем настройки категории
    $category_result = $db->query("SELECT * FROM auction_categories WHERE id = $category_id");
    $category = $category_result->fetch_assoc();
    
    // Определяем время начала и окончания
    $now = time();
    $start_time = date('Y-m-d H:i:s', $now);
    
    // Вычисляем время окончания на основе длительности из шаблона
    $duration_minutes = isset($template['duration_minutes']) ? $template['duration_minutes'] : 1440;
    $end_time = date('Y-m-d H:i:s', $now + ($duration_minutes * 60));
    
    // Используем первый предмет из JSON
    $items_json = isset($template['items_json']) ? $template['items_json'] : '[]';
    $items = json_decode($items_json, true);
    
    if (!$items || !is_array($items) || empty($items)) {
        return false;
    }
    
    $firstItem = $items[0];
    $item_id = isset($firstItem['item_id']) ? intval($firstItem['item_id']) : 0;
    $count = isset($firstItem['count']) ? intval($firstItem['count']) : 1;
    $data = isset($firstItem['data']) ? $db->real_escape_string($firstItem['data']) : '';
    
    // Создаем аукцион
    $name_escaped = $db->real_escape_string($template['name']);
    $item_type_escaped = $db->real_escape_string($template['item_type']);
    $image_url_escaped = isset($template['image_url']) ? $db->real_escape_string($template['image_url']) : '';
    $description_escaped = isset($template['description']) ? $db->real_escape_string($template['description']) : '';
    $source_escaped = isset($template['source']) ? $db->real_escape_string($template['source']) : '';
    $all_items_json_escaped = $db->real_escape_string($items_json);
    
    $sql = "INSERT INTO auction_items (
        category_id, name, item_type, item_id, count, data, source, image_url, description,
        start_price, min_bid_step, start_time, end_time, status, all_items_json, created_by,
        current_bid, current_bidder_id, current_bidder_name, template_id
    ) VALUES (
        $category_id, 
        '$name_escaped', 
        '$item_type_escaped', 
        $item_id, 
        $count, 
        '$data', 
        '$source_escaped', 
        '$image_url_escaped', 
        '$description_escaped',
        " . (isset($template['start_price']) ? $template['start_price'] : 100) . ", 
        " . (isset($template['min_bid_step']) ? $template['min_bid_step'] : 100) . ", 
        '$start_time', 
        '$end_time', 
        'active', 
        '$all_items_json_escaped', 
        1,
        0, 0, '',
        $template_id
    )";
    
    if ($db->query($sql)) {
        return $db->insert_id;
    }
    
    return false;
}

function auction_user() {
    global $db;
    
    error_log("=== auction_user() called ===");
    error_log("Subop: " . (isset($_POST['subop']) ? $_POST['subop'] : 'none'));
    
    // Проверяем подключение к базе данных
    if (!$db || $db->connect_errno) {
        error_log("Database connection error: " . ($db ? $db->connect_error : 'No DB connection'));
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка подключения к базе данных'));
        return;
    }
    
    $subop = isset($_POST['subop']) ? $_POST['subop'] : '';
    
    switch ($subop) {
        case 'get_categories':
            error_log("get_categories called");
            $result = $db->query("SELECT * FROM auction_categories WHERE is_active = 1 ORDER BY id");
            $categories = array();
            
            if ($result && $result->num_rows > 0) {
                error_log("Categories found: " . $result->num_rows);
                while ($row = $result->fetch_assoc()) {
                    $categories[] = $row;
                }
            } else {
                error_log("No categories found or error: " . $db->error);
            }
            
            echo serialize(array('errorcode' => 0, 'categories' => $categories));
            break;
            
        case 'get_auctions':
            error_log("get_auctions called");
            
            $category_id = isset($_POST['category_id']) ? intval($_POST['category_id']) : 0;
            $where = "WHERE a.status = 'active' AND a.end_time > NOW()";
            
            if ($category_id > 0) {
                $where .= " AND a.category_id = $category_id";
            }
            
            error_log("Category ID: $category_id, WHERE: $where");
            
            $sql = "
                SELECT a.*, c.name as category_name, c.duration_type 
                FROM auction_items a 
                LEFT JOIN auction_categories c ON a.category_id = c.id 
                $where 
                ORDER BY a.end_time ASC
            ";
            
            error_log("SQL: " . $sql);
            
            $result = $db->query($sql);
            
            if (!$result) {
                error_log("Query error: " . $db->error);
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка запроса к базе данных'));
                return;
            }
            
            $auctions = array();
            if ($result && $result->num_rows > 0) {
                error_log("Found auctions: " . $result->num_rows);
                while ($row = $result->fetch_assoc()) {
                    // Получаем количество ставок
                    $bid_result = $db->query("SELECT COUNT(*) as cnt FROM auction_bids WHERE auction_id = " . $row['id']);
                    $row['bid_count'] = ($bid_result && $bid_row = $bid_result->fetch_assoc()) ? $bid_row['cnt'] : 0;
                    
                    // Если current_bidder_character пуст, но есть current_bidder_id, пытаемся получить имя персонажа
                    if (empty($row['current_bidder_character']) && !empty($row['current_bidder_id'])) {
                        $char_query = "SELECT name FROM characters WHERE account_id = " . $row['current_bidder_id'] . " ORDER BY hours_online DESC LIMIT 1";
                        $char_result = $db->query($char_query);
                        if ($char_result && $char_result->num_rows > 0) {
                            $char_data = $char_result->fetch_assoc();
                            $row['current_bidder_character'] = $char_data['name'];
                        }
                    }
                    
                    $auctions[] = $row;
                }
            } else {
                error_log("No auctions found");
            }
            
            echo serialize(array('errorcode' => 0, 'auctions' => $auctions));
            break;
            
        case 'get_auction_details':
            error_log("get_auction_details called");
            
            $auction_id = isset($_POST['id']) ? intval($_POST['id']) : 0;
            if ($auction_id <= 0) {
                error_log("Invalid auction ID: $auction_id");
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Неверный ID аукциона'));
                return;
            }
            
            error_log("Requested auction ID: $auction_id");
            
            $sql = "
                SELECT a.*, c.name as category_name, c.duration_type 
                FROM auction_items a 
                LEFT JOIN auction_categories c ON a.category_id = c.id 
                WHERE a.id = $auction_id
            ";
            
            $result = $db->query($sql);
            
            if ($result && $result->num_rows > 0) {
                $auction = $result->fetch_assoc();
                error_log("Auction found: ID=" . $auction['id'] . ", Name=" . $auction['name']);
                
                // Получаем количество ставок
                $bid_result = $db->query("SELECT COUNT(*) as cnt FROM auction_bids WHERE auction_id = $auction_id");
                $auction['bid_count'] = ($bid_result && $bid_row = $bid_result->fetch_assoc()) ? $bid_row['cnt'] : 0;
                
                // Если current_bidder_character пуст, но есть current_bidder_id, пытаемся получить имя персонажа
                if (empty($auction['current_bidder_character']) && !empty($auction['current_bidder_id'])) {
                    $char_query = "SELECT name FROM characters WHERE account_id = " . $auction['current_bidder_id'] . " ORDER BY hours_online DESC LIMIT 1";
                    $char_result = $db->query($char_query);
                    if ($char_result && $char_result->num_rows > 0) {
                        $char_data = $char_result->fetch_assoc();
                        $auction['current_bidder_character'] = $char_data['name'];
                    }
                }
                
                echo serialize(array('errorcode' => 0, 'auction' => $auction));
            } else {
                error_log("Auction not found: ID=$auction_id, error: " . $db->error);
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Аукцион не найден'));
            }
            break;
            
        case 'place_bid':
            error_log("place_bid called");
            
            if (!isset($_POST['player_id']) || !isset($_POST['player_name'])) {
                error_log("Missing player_id or player_name");
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Требуется авторизация'));
                return;
            }
            
            $auction_id = isset($_POST['auction_id']) ? intval($_POST['auction_id']) : 0;
            $bid_amount = isset($_POST['bid_amount']) ? intval($_POST['bid_amount']) : 0;
            $player_id = intval($_POST['player_id']);
            $player_name = isset($_POST['player_name']) ? $db->real_escape_string($_POST['player_name']) : '';
            $character_name = isset($_POST['character_name']) ? $db->real_escape_string($_POST['character_name']) : '';
            
            if ($auction_id <= 0 || $bid_amount <= 0 || $player_id <= 0 || empty($player_name)) {
                error_log("Invalid parameters: auction_id=$auction_id, bid_amount=$bid_amount, player_id=$player_id");
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Неверные параметры запроса'));
                return;
            }
            
            error_log("Bid params: auction=$auction_id, amount=$bid_amount, player=$player_id ($player_name), character=$character_name");
            
            // Получаем аукцион
            $result = $db->query("
                SELECT * FROM auction_items 
                WHERE id = $auction_id 
                AND status = 'active' 
                AND end_time > NOW()
            ");
            
            if (!$result || $result->num_rows == 0) {
                error_log("Auction not found or expired: ID=$auction_id");
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Аукцион не найден или завершен'));
                return;
            }
            
            $auction = $result->fetch_assoc();
            error_log("Auction found: current_bid=" . $auction['current_bid'] . ", min_step=" . $auction['min_bid_step']);
            
            // Проверяем минимальную сумму
            $min_bid = $auction['current_bid'] ? 
                       $auction['current_bid'] + $auction['min_bid_step'] : 
                       $auction['start_price'];
            
            error_log("Min bid required: $min_bid");
            
            if ($bid_amount < $min_bid) {
                error_log("Bid too low: $bid_amount < $min_bid");
                echo serialize(array(
                    'errorcode' => 1, 
                    'errortxt' => 'Ставка слишком низкая. Минимум: ' . number_format($min_bid) . ' золота'
                ));
                return;
            }
            
            // Проверяем, не является ли игрок текущим лидером
            if ($auction['current_bidder_id'] == $player_id) {
                error_log("Player is already the leader: $player_id");
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Вы уже являетесь лидером ставок'));
                return;
            }
            
            // Транзакция
            $db->autocommit(FALSE);
            error_log("Starting transaction");
            
            try {
                // Добавляем ставку
                $sql1 = "INSERT INTO auction_bids (auction_id, player_id, player_name, character_name, bid_amount, is_winning) 
                         VALUES ($auction_id, $player_id, '$player_name', '$character_name', $bid_amount, 1)";
                
                error_log("Insert bid SQL: $sql1");
                
                if (!$db->query($sql1)) {
                    error_log("Insert bid error: " . $db->error);
                    throw new Exception('Ошибка добавления ставки: ' . $db->error);
                }
                
                $bid_id = $db->insert_id;
                error_log("Bid inserted, ID: $bid_id");
                
                // Сбрасываем флаг is_winning
                $sql2 = "UPDATE auction_bids SET is_winning = 0 WHERE auction_id = $auction_id AND id != $bid_id";
                error_log("Reset winning bids SQL: $sql2");
                
                if (!$db->query($sql2)) {
                    error_log("Reset bids error: " . $db->error);
                    throw new Exception('Ошибка обновления ставки: ' . $db->error);
                }
                
                // Обновляем аукцион с сохранением имени персонажа
                $sql3 = "UPDATE auction_items 
                         SET current_bid = $bid_amount, 
                             current_bidder_id = $player_id, 
                             current_bidder_name = '$player_name', 
                             current_bidder_character = '$character_name' 
                         WHERE id = $auction_id";
                
                error_log("Update auction SQL: $sql3");
                
                if (!$db->query($sql3)) {
                    error_log("Update auction error: " . $db->error);
                    throw new Exception('Ошибка обновления аукциона: ' . $db->error);
                }
                
                $db->commit();
                error_log("Transaction committed successfully");
                
                echo serialize(array(
                    'errorcode' => 0, 
                    'message' => 'Ваша ставка ' . number_format($bid_amount) . ' золота принята!'
                ));
                
            } catch (Exception $e) {
                $db->rollback();
                error_log("Transaction rolled back: " . $e->getMessage());
                echo serialize(array('errorcode' => 1, 'errortxt' => $e->getMessage()));
            }
            
            $db->autocommit(TRUE);
            break;
            
        case 'get_player_bids':
    error_log("=== get_player_bids START ===");
    
    $player_id = isset($_POST['player_id']) ? intval($_POST['player_id']) : 0;
    if (!$player_id) {
        error_log("Missing player_id");
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Не указан ID игрока'));
        return;
    }
    
    error_log("Getting bids for player_id: $player_id");
    
    // Получаем список отправленных предметов для этого игрока
    $delivered_sql = "SELECT auction_id FROM auction_delivered_items WHERE user_id = $player_id AND status = 1";
    $delivered_result = $db->query($delivered_sql);
    $delivered_auctions = array();
    
    if ($delivered_result && $delivered_result->num_rows > 0) {
        while ($delivered_row = $delivered_result->fetch_assoc()) {
            $delivered_auctions[$delivered_row['auction_id']] = true;
        }
    }
    error_log("Delivered auctions: " . json_encode($delivered_auctions));
    
    $sql = "
        SELECT 
            ab.*,
            ai.name as auction_name,
            ai.item_id,
            ai.count as item_count,
            ai.start_price,
            ai.current_bid,
            ai.end_time as auction_end_time,
            ai.status as auction_status,
            ai.current_bidder_id,
            ai.current_bidder_name,
            ai.current_bidder_character,
            (SELECT MAX(bid_amount) FROM auction_bids WHERE auction_id = ai.id) as current_max_bid
        FROM auction_bids ab
        INNER JOIN auction_items ai ON ab.auction_id = ai.id
        WHERE ab.player_id = $player_id
        ORDER BY ab.created_at DESC
        LIMIT 100
    ";
    
    error_log("SQL for player bids: " . str_replace(array("\n", "  "), " ", $sql));
    
    $result = $db->query($sql);
    
    if (!$result) {
        error_log("Query error: " . $db->error);
        echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка запроса к базе данных: ' . $db->error));
        return;
    }
    
    $bids = array();
    if ($result && $result->num_rows > 0) {
        error_log("Found " . $result->num_rows . " bids for player $player_id");
        while ($row = $result->fetch_assoc()) {
            // Определяем статус ставки
            $status = 'active';
            $is_leading = false;
            $auction_ended = false;
            $can_send_to_game = false;
            $already_sent = isset($delivered_auctions[$row['auction_id']]) ? true : false;
            
            if (isset($row['auction_end_time']) && $row['auction_end_time']) {
                $auction_ended = strtotime($row['auction_end_time']) < time();
            }
            
            // Проверяем статус аукциона
            if ($row['auction_status'] == 'ended' || $auction_ended || $already_sent) {
                if ($row['is_winning'] == 1 && !$already_sent) {
                    $status = 'won';
                    // Проверяем можно ли отправить в игру
                    if ($auction_ended && $row['player_id'] == $row['current_bidder_id']) {
                        $can_send_to_game = true;
                    }
                } elseif ($already_sent) {
                    $status = 'sent';
                } else {
                    $status = 'lost';
                }
            } else {
                // Если аукцион еще активен
                if ($row['bid_amount'] == $row['current_max_bid']) {
                    $is_leading = true;
                    $status = 'active';
                } else {
                    $status = 'outbid';
                }
            }
            
            $bids[] = array(
                'id' => $row['id'],
                'auction_id' => $row['auction_id'],
                'auction_name' => $row['auction_name'] ? $row['auction_name'] : 'Неизвестный аукцион',
                'item_id' => $row['item_id'] ? $row['item_id'] : 0,
                'item_count' => $row['item_count'] ? $row['item_count'] : 1,
                'bid_amount' => $row['bid_amount'],
                'bid_time' => $row['created_at'],
                'is_winning' => $row['is_winning'],
                'is_leading' => $is_leading,
                'status' => $status,
                'character_name' => $row['character_name'],
                'auction_end_time' => $row['auction_end_time'],
                'auction_status' => $row['auction_status'],
                'current_bidder_id' => $row['current_bidder_id'],
                'current_bidder_name' => $row['current_bidder_name'],
                'current_bidder_character' => $row['current_bidder_character'],
                'can_send_to_game' => $can_send_to_game,
                'already_sent' => $already_sent
            );
        }
    } else {
        error_log("No bids found for player $player_id");
    }
    
    error_log("Returning " . count($bids) . " bids to client");
    error_log("=== get_player_bids END ===");
    
    echo serialize(array('errorcode' => 0, 'bids' => $bids));
    break;
            
        case 'get_trade_history':
            error_log("get_trade_history called");
            
            $hours = isset($_POST['hours']) ? intval($_POST['hours']) : 24;
            $limit = isset($_POST['limit']) ? intval($_POST['limit']) : 50;
            
            if ($hours <= 0 || $hours > 720) {
                $hours = 24;
            }
            if ($limit <= 0 || $limit > 1000) {
                $limit = 50;
            }
            
            error_log("Getting trade history for last $hours hours, limit $limit");
            
            $time_threshold = date('Y-m-d H:i:s', time() - ($hours * 3600));
            
            $sql = "
                SELECT 
                    ai.*,
                    ab.player_name as winner_name,
                    ab.character_name as winner_character,
                    ab.bid_amount as final_price,
                    (SELECT COUNT(*) FROM auction_bids WHERE auction_id = ai.id) as bid_count
                FROM auction_items ai
                LEFT JOIN auction_bids ab ON ai.id = ab.auction_id AND ab.is_winning = 1
                WHERE (ai.status = 'ended' OR ai.end_time < NOW())
                  AND ai.end_time >= '$time_threshold'
                ORDER BY ai.end_time DESC
                LIMIT $limit
            ";
            
            error_log("SQL for trade history: $sql");
            
            $result = $db->query($sql);
            
            if (!$result) {
                error_log("Query error: " . $db->error);
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Ошибка запроса к базе данных'));
                return;
            }
            
            $history = array();
            if ($result && $result->num_rows > 0) {
                error_log("Found " . $result->num_rows . " historical trades");
                while ($row = $result->fetch_assoc()) {
                    $history[] = array(
                        'auction_id' => $row['id'],
                        'auction_name' => $row['name'],
                        'item_id' => $row['item_id'],
                        'item_count' => $row['count'],
                        'description' => $row['description'],
                        'start_price' => $row['start_price'],
                        'final_price' => $row['final_price'] ? $row['final_price'] : 0,
                        'winner_name' => $row['winner_name'] ? $row['winner_name'] : '',
                        'winner_character' => $row['winner_character'] ? $row['winner_character'] : '',
                        'end_time' => $row['end_time'],
                        'bid_count' => $row['bid_count'] ? $row['bid_count'] : 0
                    );
                }
            } else {
                error_log("No trade history found for period: $time_threshold to now");
            }
            
            echo serialize(array('errorcode' => 0, 'history' => $history));
            break;
            
case 'send_to_game':
    session_start();
    
    // Получаем параметры
    $auction_id = isset($_POST['auction_id']) ? intval($_POST['auction_id']) : 0;
    $character_name = isset($_POST['character_name']) ? trim($_POST['character_name']) : '';
    $player_id = isset($_POST['player_id']) ? intval($_POST['player_id']) : 0;
    $player_name = isset($_POST['player_name']) ? trim($_POST['player_name']) : '';
    $character_id = isset($_POST['character_id']) ? intval($_POST['character_id']) : 0;
    $ip_address = $_SERVER['REMOTE_ADDR'];
    $no_wait = isset($_POST['no_wait']) && $_POST['no_wait'] == 1;
    
    // Логируем входящие данные
    error_log("send_to_game called with: " . json_encode([
        'auction_id' => $auction_id,
        'character_name' => $character_name,
        'player_id' => $player_id,
        'player_name' => $player_name,
        'character_id' => $character_id,
        'ip' => $ip_address,
        'no_wait' => $no_wait
    ]));
    
    // Проверяем параметры
    if ($character_id == 0 || $auction_id == 0 || empty($character_name) || $player_id == 0) {
        echo serialize(array(
            'errorcode' => 1, 
            'errortxt' => 'Не указаны обязательные параметры'
        ));
        break;
    }
    
    // Проверяем в сессии, не отправлен ли уже
    $session_key = 'auction_sent_' . $auction_id;
    if (isset($_SESSION[$session_key]) && $_SESSION[$session_key] === true) {
        echo serialize(array(
            'errorcode' => 1,
            'errortxt' => 'Этот предмет уже был отправлен в текущей сессии'
        ));
        break;
    }
    
    // Проверяем в базе данных
    $check_sql = "SELECT status, delivered_at, item_id, count, name FROM auction_items WHERE id = $auction_id";
    $check_result = $db->query($check_sql);
    
    if (!$check_result || $check_result->num_rows == 0) {
        echo serialize(array(
            'errorcode' => 1,
            'errortxt' => 'Аукцион не найден'
        ));
        break;
    }
    
    $auction = $check_result->fetch_assoc();
    
    if ($auction['status'] == 'delivered' || $auction['delivered_at'] !== null) {
        $_SESSION[$session_key] = true;
        echo serialize(array(
            'errorcode' => 1,
            'errortxt' => 'Этот предмет уже был отправлен ранее'
        ));
        break;
    }
    
    // Проверяем, является ли игрок победителем
    $winner_check_sql = "SELECT 
        ab.id as bid_id,
        ab.player_id,
        ab.is_winning
    FROM auction_bids ab
    WHERE ab.auction_id = $auction_id 
    AND ab.is_winning = 1 
    AND ab.player_id = $player_id
    LIMIT 1";
    
    $winner_result = $db->query($winner_check_sql);
    if (!$winner_result || $winner_result->num_rows == 0) {
        echo serialize(array(
            'errorcode' => 1,
            'errortxt' => 'Вы не являетесь победителем этого аукциона'
        ));
        break;
    }
    
    $winner_data = $winner_result->fetch_assoc();
    
    // Получаем данные о предмете
    $item_sql = "SELECT item_id, count FROM auction_items WHERE id = $auction_id";
    $item_result = $db->query($item_sql);
    $item_id = 0;
    $item_count = 1;
    
    if ($item_result && $item_result->num_rows > 0) {
        $item_row = $item_result->fetch_assoc();
        $item_id = $item_row['item_id'];
        $item_count = $item_row['count'];
    }
    
    // Создаем JSON
    $item_data_array = array(
        array(
            'item_id' => $item_id,
            'count' => $item_count,
            'client_size' => 0,
            'max_count' => $item_count,
            'data' => '',
            'proctype' => 0,
            'expire_date' => 0
        )
    );
    $item_json = json_encode($item_data_array, JSON_UNESCAPED_UNICODE);
    
    // Если это no_wait запрос, отправляем быстрый ответ и продолжаем в фоне
    if ($no_wait) {
        // Отправляем клиенту, что запрос принят
        if (function_exists('fastcgi_finish_request')) {
            echo serialize(array(
                'errorcode' => 0, 
                'message' => 'Запрос принят, предмет отправляется'
            ));
            fastcgi_finish_request();
            error_log("fastcgi_finish_request called - continuing in background");
        } else {
            // Если fastcgi_finish_request не доступен, используем ob_flush
            echo serialize(array(
                'errorcode' => 0, 
                'message' => 'Запрос принят, предмет отправляется'
            ));
            
            // Отправляем заголовки и закрываем соединение
            if (ob_get_level()) {
                ob_end_flush();
            }
            ob_flush();
            flush();
            
            // Закрываем соединение с клиентом
            if (function_exists('session_write_close')) {
                session_write_close();
            }
            
            error_log("Connection closed - continuing in background");
        }
    }
    
    // Начинаем транзакцию
    $db->autocommit(FALSE);
    $success = false;
    $message = '';
    
    try {
        // Проверяем, может уже отправляли
        $check_delivered_sql = "SELECT id FROM auction_delivered_items WHERE auction_id = $auction_id AND user_id = $player_id";
        $check_delivered_result = $db->query($check_delivered_sql);
        
        if (!$check_delivered_result || $check_delivered_result->num_rows == 0) {
            // Сохраняем в БД
            $user_login_esc = $db->real_escape_string($player_name);
            $character_name_esc = $db->real_escape_string($character_name);
            $item_json_esc = $db->real_escape_string($item_json);
            $ip_address_esc = $db->real_escape_string($ip_address);
            
            $insert_sql = "INSERT INTO auction_delivered_items 
                (auction_id, user_id, user_login, character_id, character_name, 
                 item_id, item_count, item_json, ip_address, delivered_at) 
                VALUES (
                    $auction_id, 
                    $player_id, 
                    '$user_login_esc', 
                    $character_id, 
                    '$character_name_esc',
                    $item_id, 
                    $item_count, 
                    '$item_json_esc', 
                    '$ip_address_esc',
                    NOW()
                )";
            
            if (!$db->query($insert_sql)) {
                throw new Exception('Ошибка сохранения в базу данных: ' . $db->error);
            }
            error_log("Saved to auction_delivered_items, ID: " . $db->insert_id);
        } else {
            error_log("Item already exists in auction_delivered_items");
        }
        
        // СОЗДАЕМ СТРУКТУРУ ПРЕДМЕТА ДЛЯ ОТПРАВКИ
        $item = array(
            'id' => intval($auction['item_id']),
            'pos' => 0,
            'count' => intval($auction['count']),
            'client_size' => 0,
            'max_count' => intval($auction['count']),
            'data' => str_repeat('0', 64),
            'proctype' => 0,
            'expire_date' => 0,
            'guid1' => 0,
            'guid2' => 0
        );
        
        // Формируем тему и текст письма
        $subject = "Выигрыш на аукционе: " . $auction['name'];
        $context = "Поздравляем, " . $character_name . "!\n\n";
        $context .= "Вы выиграли на аукционе предмет: " . $auction['name'] . "\n";
        $context .= "Количество: " . $auction['count'] . " шт.\n\n";
        $context .= "Предмет отправлен на почту вашего персонажа.\n";
        $context .= "Спасибо за участие в аукционе!";
        
        $tid = time() . rand(1000, 9999);
        
        // Отправляем через SysSendMail
        $item_sent = false;
        $send_error = '';
        
        if (function_exists('SysSendMail')) {
            error_log("Sending mail via SysSendMail to character_id: $character_id");
            
            $mail_result = SysSendMail(
                $tid,
                0,
                3,
                $character_id,
                $subject,
                $context,
                $item,
                0
            );
            
            error_log("SysSendMail result: " . json_encode($mail_result));
            
            // Проверяем результат
            if (is_array($mail_result)) {
                if (isset($mail_result['retcode']) && $mail_result['retcode'] == 0) {
                    $item_sent = true;
                } elseif (isset($mail_result['success']) && $mail_result['success'] === true) {
                    $item_sent = true;
                } else {
                    $send_error = is_array($mail_result) && isset($mail_result['retcode']) 
                        ? "Код ошибки: {$mail_result['retcode']}" 
                        : "Неизвестная ошибка";
                    
                    if (is_array($mail_result) && isset($mail_result['retcode']) && $mail_result['retcode'] == 217) {
                        $send_error = "Почтовый ящик персонажа переполнен";
                    }
                }
            } elseif ($mail_result === true) {
                $item_sent = true;
            } else {
                $send_error = "Неизвестный ответ от SysSendMail";
            }
        } else {
            error_log("SysSendMail function not found - simulating success");
            $item_sent = true;
        }
        
        if (!$item_sent) {
            throw new Exception('Ошибка отправки предмета в игру: ' . $send_error);
        }
        
        // Обновляем статус аукциона
        $update_auction_sql = "UPDATE auction_items SET 
            status = 'delivered',
            delivered_at = NOW(),
            delivered_to = '" . $db->real_escape_string($character_name) . "',
            delivered_to_id = $character_id
            WHERE id = $auction_id";
        
        if (!$db->query($update_auction_sql)) {
            throw new Exception('Ошибка обновления статуса аукциона: ' . $db->error);
        }
        
        // Обновляем статус ставки на 'sent'
        $update_bid_sql = "UPDATE auction_bids SET 
            status = 'sent',
            sent_at = NOW()
            WHERE auction_id = $auction_id 
            AND player_id = $player_id 
            AND is_winning = 1";
        
        if (!$db->query($update_bid_sql)) {
            throw new Exception('Ошибка обновления статуса ставки: ' . $db->error);
        }
        
        // Коммитим транзакцию
        $db->commit();
        $success = true;
        $message = 'Предмет успешно отправлен персонажу ' . $character_name;
        
        error_log("Transaction completed successfully");
        
    } catch (Exception $e) {
        $db->rollback();
        error_log("Transaction error in send_to_game: " . $e->getMessage());
        
        // Если это no_wait запрос, мы уже отправили ответ, просто логируем ошибку
        if (!$no_wait) {
            echo serialize(array(
                'errorcode' => 1,
                'errortxt' => $e->getMessage()
            ));
        }
        $db->autocommit(TRUE);
        break;
    }
    
    $db->autocommit(TRUE);
    
    // Отправляем успешный ответ (только если не no_wait)
    if ($success) {
        $_SESSION[$session_key] = true;
        $_SESSION['auction_sent_' . $auction_id] = true;
        
        if (!$no_wait) {
            echo serialize(array(
                'errorcode' => 0,
                'message' => $message,
                'auction_id' => $auction_id,
                'character_name' => $character_name,
                'character_id' => $character_id,
                'status' => 'sent',
                'timestamp' => time()
            ));
        } else {
            error_log("Background processing completed for auction $auction_id");
        }
    }
    
    break;
            
        default:
            error_log("Unknown subop: $subop");
            echo serialize(array('errorcode' => 1, 'errortxt' => 'Неизвестная операция'));
    }
    
    error_log("=== auction_user() finished ===");
}

//ПОЛЬЗОВАТЕЛЬСКАЯ ЧАСТЬ БАТЛ ПАССА НАЧАЛАСЬ
function createBattlePassLogTables() {
    global $db;
    
    // Таблица для логов отправленных наград
    $db->query("CREATE TABLE IF NOT EXISTS battlepass_rewards_log (
        id INT AUTO_INCREMENT PRIMARY KEY,
        user_id INT NOT NULL,
        login VARCHAR(50) NOT NULL,
        season_id INT NOT NULL,
        level INT NOT NULL,
        reward_type ENUM('free','premium','premium_plus') NOT NULL,
        item_id INT NOT NULL,
        item_count INT NOT NULL DEFAULT 1,
        item_data TEXT,
        target_player INT NOT NULL,
        target_player_name VARCHAR(50),
        send_method VARCHAR(20) DEFAULT 'mail',
        send_result TEXT,
        status ENUM('success','error') DEFAULT 'success',
        error_message TEXT,
        ip_address VARCHAR(45),
        request_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        INDEX idx_user (user_id),
        INDEX idx_season (season_id),
        INDEX idx_time (request_time)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
    
    // Таблица для логов покупок пропусков
    $db->query("CREATE TABLE IF NOT EXISTS battlepass_purchases_log (
        id INT AUTO_INCREMENT PRIMARY KEY,
        user_id INT NOT NULL,
        login VARCHAR(50) NOT NULL,
        season_id INT NOT NULL,
        pass_type VARCHAR(20) NOT NULL,
        price INT NOT NULL,
        old_balance INT NOT NULL,
        new_balance INT NOT NULL,
        ip_address VARCHAR(45),
        request_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        INDEX idx_user (user_id),
        INDEX idx_time (request_time)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
    
    // Таблица для логов покупок в магазине
    $db->query("CREATE TABLE IF NOT EXISTS battlepass_shop_purchases_log (
        id INT AUTO_INCREMENT PRIMARY KEY,
        user_id INT NOT NULL,
        login VARCHAR(50) NOT NULL,
        shop_item_id INT NOT NULL,
        item_name VARCHAR(255),
        price_gold INT NOT NULL,
        price_silver INT NOT NULL,
        item_data TEXT,
        target_player INT NOT NULL,
        target_player_name VARCHAR(50),
        send_method VARCHAR(20) DEFAULT 'mail',
        send_result TEXT,
        status ENUM('success','error') DEFAULT 'success',
        error_message TEXT,
        ip_address VARCHAR(45),
        request_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        INDEX idx_user (user_id),
        INDEX idx_item (shop_item_id)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
    
    // Таблица для логов всех действий (аудит)
    $db->query("CREATE TABLE IF NOT EXISTS battlepass_audit_log (
        id INT AUTO_INCREMENT PRIMARY KEY,
        user_id INT NOT NULL,
        login VARCHAR(50) NOT NULL,
        action VARCHAR(50) NOT NULL,
        details TEXT,
        ip_address VARCHAR(45),
        user_agent TEXT,
        request_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        INDEX idx_user (user_id),
        INDEX idx_action (action),
        INDEX idx_time (request_time)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
}

// Вызываем функцию создания таблиц при загрузке
createBattlePassLogTables();

/**
 * Логирование отправки награды
 */
function logBattlePassReward($user_id, $login, $season_id, $level, $reward_type, $item_data, $target_player, $target_player_name, $send_result, $status = 'success', $error_message = '') {
    global $db;
    
    // Создаем таблицу если её нет
    $db->query("CREATE TABLE IF NOT EXISTS battlepass_rewards_log (
        id INT AUTO_INCREMENT PRIMARY KEY,
        user_id INT NOT NULL,
        login VARCHAR(50) NOT NULL,
        season_id INT NOT NULL,
        level INT NOT NULL,
        reward_type ENUM('free','premium','premium_plus') NOT NULL,
        item_id INT NOT NULL,
        item_count INT NOT NULL DEFAULT 1,
        item_data TEXT,
        target_player INT NOT NULL,
        target_player_name VARCHAR(50),
        send_method VARCHAR(20) DEFAULT 'mail',
        send_result TEXT,
        status ENUM('success','error') DEFAULT 'success',
        error_message TEXT,
        ip_address VARCHAR(45),
        request_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        INDEX idx_user (user_id),
        INDEX idx_season (season_id),
        INDEX idx_time (request_time)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
    
    $login_esc = $db->real_escape_string($login);
    $item_data_esc = $db->real_escape_string(json_encode($item_data, JSON_UNESCAPED_UNICODE));
    $send_result_esc = $db->real_escape_string(json_encode($send_result, JSON_UNESCAPED_UNICODE));
    $error_message_esc = $db->real_escape_string($error_message);
    $ip = $_SERVER['REMOTE_ADDR'];
    $target_player_name_esc = $db->real_escape_string($target_player_name);
    
    $item_id = isset($item_data['item_id']) ? (int)$item_data['item_id'] : 0;
    $item_count = isset($item_data['count']) ? (int)$item_data['count'] : 1;
    
    $query = "INSERT INTO battlepass_rewards_log 
              (user_id, login, season_id, level, reward_type, item_id, item_count, item_data, 
               target_player, target_player_name, send_method, send_result, status, error_message, ip_address) 
              VALUES (
                  {$user_id}, 
                  '{$login_esc}', 
                  {$season_id}, 
                  {$level}, 
                  '{$reward_type}', 
                  {$item_id}, 
                  {$item_count}, 
                  '{$item_data_esc}', 
                  {$target_player}, 
                  '{$target_player_name_esc}', 
                  'mail', 
                  '{$send_result_esc}', 
                  '{$status}', 
                  '{$error_message_esc}', 
                  '{$ip}'
              )";
    
    $result = $db->query($query);
    if (!$result) {
        error_log("Failed to log battlepass reward: " . $db->error);
    }
}

/**
 * Логирование покупки пропуска
 */
function logBattlePassPurchase($user_id, $login, $season_id, $pass_type, $price, $old_balance, $new_balance) {
    global $db;
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    // Проверяем существование таблицы и создаем если нет
    $check = $db->query("SHOW TABLES LIKE 'battlepass_purchases_log'");
    if ($check->num_rows == 0) {
        $db->query("CREATE TABLE IF NOT EXISTS battlepass_purchases_log (
            id INT AUTO_INCREMENT PRIMARY KEY,
            user_id INT NOT NULL,
            login VARCHAR(50) COLLATE utf8_unicode_ci NOT NULL,
            season_id INT NOT NULL,
            pass_type VARCHAR(20) COLLATE utf8_unicode_ci NOT NULL,
            price INT NOT NULL,
            old_balance INT NOT NULL,
            new_balance INT NOT NULL,
            ip_address VARCHAR(45) COLLATE utf8_unicode_ci,
            request_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            INDEX idx_user (user_id),
            INDEX idx_time (request_time)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci");
    }
    
    // Экранируем данные
    $login_esc = $db->real_escape_string($login);
    $pass_type_esc = $db->real_escape_string($pass_type);
    $ip = $_SERVER['REMOTE_ADDR'];
    
    $query = "INSERT INTO battlepass_purchases_log 
              (user_id, login, season_id, pass_type, price, old_balance, new_balance, ip_address) 
              VALUES (
                  {$user_id}, 
                  '{$login_esc}', 
                  {$season_id}, 
                  '{$pass_type_esc}', 
                  {$price}, 
                  {$old_balance}, 
                  {$new_balance}, 
                  '{$ip}'
              )";
    
    error_log("Purchase log query: " . $query);
    
    $result = $db->query($query);
    if (!$result) {
        error_log("Failed to log battlepass purchase: " . $db->error);
    } else {
        error_log("Purchase logged successfully for user {$user_id}, pass_type {$pass_type}");
    }
    
    // Также логируем в аудит
    logBattlePassAudit($user_id, $login, 'purchase_pass', "Тип: {$pass_type}, Цена: {$price}, Баланс: {$old_balance} -> {$new_balance}");
}

/**
 * Логирование покупки в магазине
 */
function logBattlePassShopPurchase($user_id, $login, $shop_item_id, $item_name, $price_gold, $price_silver, $item_data, $target_player, $target_player_name, $send_result, $status = 'success', $error_message = '') {
    global $db;
    
    $login_esc = $db->real_escape_string($login);
    $item_name_esc = $db->real_escape_string($item_name);
    $item_data_esc = $db->real_escape_string(json_encode($item_data, JSON_UNESCAPED_UNICODE));
    $send_result_esc = $db->real_escape_string(json_encode($send_result, JSON_UNESCAPED_UNICODE));
    $error_message_esc = $db->real_escape_string($error_message);
    $ip = $_SERVER['REMOTE_ADDR'];
    $target_player_name_esc = $db->real_escape_string($target_player_name);
    
    $db->query("INSERT INTO battlepass_shop_purchases_log 
                (user_id, login, shop_item_id, item_name, price_gold, price_silver, item_data, 
                 target_player, target_player_name, send_method, send_result, status, error_message, ip_address) 
                VALUES (
                    {$user_id}, 
                    '{$login_esc}', 
                    {$shop_item_id}, 
                    '{$item_name_esc}', 
                    {$price_gold}, 
                    {$price_silver}, 
                    '{$item_data_esc}', 
                    {$target_player}, 
                    '{$target_player_name_esc}', 
                    'mail', 
                    '{$send_result_esc}', 
                    '{$status}', 
                    '{$error_message_esc}', 
                    '{$ip}'
                )");
    
    // Также логируем в аудит
    $details = "Shop purchase: Item ID {$shop_item_id}, Price: {$price_gold} gold, to player {$target_player}";
    logBattlePassAudit($user_id, $login, 'purchase_shop', $details);
}

/**
 * Логирование всех действий (аудит)
 */
function logBattlePassAudit($user_id, $login, $action, $details = '') {
    global $db;
    
    // Создаем таблицу если её нет
    $db->query("CREATE TABLE IF NOT EXISTS battlepass_audit_log (
        id INT AUTO_INCREMENT PRIMARY KEY,
        user_id INT NOT NULL,
        login VARCHAR(50) NOT NULL,
        action VARCHAR(50) NOT NULL,
        details TEXT,
        ip_address VARCHAR(45),
        user_agent TEXT,
        request_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        INDEX idx_user (user_id),
        INDEX idx_action (action),
        INDEX idx_time (request_time)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8");
    
    $login_esc = $db->real_escape_string($login);
    $action_esc = $db->real_escape_string($action);
    $details_esc = $db->real_escape_string($details);
    $ip = $_SERVER['REMOTE_ADDR'];
    $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $db->real_escape_string($_SERVER['HTTP_USER_AGENT']) : '';
    
    $query = "INSERT INTO battlepass_audit_log 
              (user_id, login, action, details, ip_address, user_agent) 
              VALUES (
                  {$user_id}, 
                  '{$login_esc}', 
                  '{$action_esc}', 
                  '{$details_esc}', 
                  '{$ip}', 
                  '{$user_agent}'
              )";
    
    $result = $db->query($query);
    if (!$result) {
        error_log("Failed to log battlepass audit: " . $db->error);
    }
}

/**
 * Получение логов выполнения квестов (task_log)
 */
/**
 * Получение логов выполнения квестов (task_log)
 */
function BpAdminGetTaskLogs() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $player_id = isset($_POST['player_id']) ? (int)$_POST['player_id'] : 0;
    $quest_id = isset($_POST['quest_id']) ? (int)$_POST['quest_id'] : 0;
    $date_from = isset($_POST['date_from']) ? $_POST['date_from'] : '';
    $date_to = isset($_POST['date_to']) ? $_POST['date_to'] : '';
    $limit = isset($_POST['limit']) ? (int)$_POST['limit'] : 100;
    $offset = isset($_POST['offset']) ? (int)$_POST['offset'] : 0;
    
    // Подключаем названия квестов
    $taskNames = array();
    if (file_exists('task_name.php')) {
        include('task_name.php');
    }
    
    // Проверяем существование таблицы
    $check = $db->query("SHOW TABLES LIKE 'task_log'");
    if ($check->num_rows == 0) {
        echo json_encode([
            'errorcode' => 1,
            'message' => 'Таблица task_log не существует',
            'logs' => [],
            'total' => 0,
            'stats' => []
        ]);
        return;
    }
    
    $where = array("1=1");
    
    if ($player_id > 0) {
        $where[] = "id_player = {$player_id}";
    }
    
    if ($quest_id > 0) {
        $where[] = "id_quest = {$quest_id}";
    }
    
    if (!empty($date_from)) {
        $date_from_esc = $db->real_escape_string($date_from . ' 00:00:00');
        $where[] = "date >= '{$date_from_esc}'";
    }
    
    if (!empty($date_to)) {
        $date_to_esc = $db->real_escape_string($date_to . ' 23:59:59');
        $where[] = "date <= '{$date_to_esc}'";
    }
    
    $where_clause = implode(" AND ", $where);
    
    // Получаем общее количество
    $count_query = "SELECT COUNT(*) as cnt FROM task_log WHERE {$where_clause}";
    $count_result = $db->query($count_query);
    $total = 0;
    if ($count_result) {
        $row = $count_result->fetch_assoc();
        $total = $row['cnt'];
    }
    
    // Получаем данные
    $query = "SELECT 
                id,
                id_player,
                id_quest,
                date
              FROM task_log
              WHERE {$where_clause}
              ORDER BY date DESC
              LIMIT {$offset}, {$limit}";
    
    $q = $db->query($query);
    $logs = array();
    
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            // Получаем имя персонажа
            if (!empty($row['id_player'])) {
                $p_q = $db->query("SELECT name FROM players WHERE id = {$row['id_player']} LIMIT 1");
                if ($p_q && $p_q->num_rows > 0) {
                    $p = $p_q->fetch_assoc();
                    $row['player_name'] = $p['name'];
                } else {
                    $row['player_name'] = 'Неизвестно';
                }
            }
            
            // Добавляем название квеста из task_name.php
            $quest_id_val = (int)$row['id_quest'];
            if (isset($taskNames[$quest_id_val]) && !empty($taskNames[$quest_id_val])) {
                $row['quest_name'] = $taskNames[$quest_id_val];
            } else {
                $row['quest_name'] = 'Нет описания';
            }
            
            // Ищем связанные задания Battle Pass (БЕЗ ИЗМЕНЕНИЙ)
            $bp_quests = array();
            $bpq = $db->query("
                SELECT id, title, quest_type, target_count
                FROM battlepass_quests 
                WHERE target_type = 'complete_quest' 
                AND (
                    target_id = " . (int)$row['id_quest'] . " 
                    OR target_params LIKE '%\"game_quest_id\":" . (int)$row['id_quest'] . "%'
                    OR target_params IS NULL
                )
                AND is_active = 1
                ORDER BY quest_type, id
                LIMIT 5
            ");
            if ($bpq && $bpq->num_rows > 0) {
                while ($bp = $bpq->fetch_assoc()) {
                    // Добавляем информацию о типе задания
                    $type_label = '';
                    if ($bp['quest_type'] == 'daily') {
                        $type_label = 'ежедн';
                    } elseif ($bp['quest_type'] == 'weekly') {
                        $type_label = 'нед';
                    } elseif ($bp['quest_type'] == 'seasonal') {
                        $type_label = 'сезон';
                    }
                    $bp['type_label'] = $type_label;
                    $bp_quests[] = $bp;
                }
            }
            $row['bp_quests'] = $bp_quests;
            
            $logs[] = $row;
        }
    }
    
    // Получаем статистику
    $stats_query = "SELECT 
                    COUNT(*) as total,
                    COUNT(DISTINCT id_player) as unique_players,
                    COUNT(DISTINCT id_quest) as unique_quests,
                    MIN(date) as first_date,
                    MAX(date) as last_date
                   FROM task_log
                   WHERE {$where_clause}";
    $stats_result = $db->query($stats_query);
    $stats = array();
    if ($stats_result) {
        $stats = $stats_result->fetch_assoc();
    }
    
    echo json_encode([
        'errorcode' => 0,
        'logs' => $logs,
        'total' => $total,
        'stats' => $stats,
        'debug' => [
            'where' => $where_clause,
            'count_logs' => count($logs),
            'total_in_period' => $total,
            'query' => $query
        ]
    ]);
}

function BpGetData() {
    global $db;
    
    // Очищаем все буферы перед отправкой JSON
    while (ob_get_level()) {
        ob_end_clean();
    }
    
    header('Content-Type: application/json; charset=utf-8');
    header('X-Content-Type-Options: nosniff');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    $db->set_charset("utf8");
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $login = isset($_POST['login']) ? trim($_POST['login']) : '';
    $servid = isset($_POST['servid']) ? (int)$_POST['servid'] : 1;
    $page = isset($_POST['page']) ? (int)$_POST['page'] : 1;
    
    if (!$user_id) {
        $response = ['errorcode' => 1, 'message' => 'invalid_user'];
        echo json_encode($response);
        return;
    }
    
    // Получаем активный сезон
    $q = $db->query("SELECT * FROM battlepass_seasons WHERE is_active = 1 ORDER BY id DESC LIMIT 1");
    
    if (!$q) {
        error_log("DB Error (seasons query): " . $db->error);
        $response = ['errorcode' => 1, 'message' => 'db_error_seasons'];
        echo json_encode($response);
        return;
    }
    
    if ($q->num_rows == 0) {
        $response = [
            'errorcode' => 0,
            'active' => false,
            'message' => 'no_active_season'
        ];
        echo json_encode($response);
        return;
    }
    
    $season = $q->fetch_assoc();
    $season_id = $season['id'];
    
    // Получаем прогресс игрока
    $q = $db->query("SELECT * FROM battlepass_progress WHERE user_id = {$user_id} AND season_id = {$season_id} LIMIT 1");
    
    $progress = null;
    
    if ($q && $q->num_rows > 0) {
        $progress = $q->fetch_assoc();
    } else {
        // Создаем прогресс для нового игрока
        $login_esc = $db->real_escape_string($login);
        $insert_query = "INSERT INTO battlepass_progress (user_id, login, season_id, current_level, current_exp, total_exp, pass_type, purchased, notification_enabled) 
                         VALUES ({$user_id}, '{$login_esc}', {$season_id}, 1, 0, 0, 'free', 0, 1)";
        
        if ($db->query($insert_query)) {
            $progress = [
                'user_id' => $user_id,
                'login' => $login,
                'season_id' => $season_id,
                'current_level' => 1,
                'current_exp' => 0,
                'total_exp' => 0,
                'pass_type' => 'free',
                'purchased' => 0,
                'notification_enabled' => 1
            ];
        } else {
            $progress = [
                'user_id' => $user_id,
                'login' => $login,
                'season_id' => $season_id,
                'current_level' => 1,
                'current_exp' => 0,
                'total_exp' => 0,
                'pass_type' => 'free',
                'purchased' => 0,
                'notification_enabled' => 1
            ];
        }
    }
    
    // Рассчитываем прогресс до следующего уровня
    $exp_needed = (int)$season['exp_per_level'];
    $progress['level_progress'] = ($exp_needed > 0) ? ($progress['current_exp'] / $exp_needed) * 100 : 0;
    
    // Получаем награды для уровней
    $q = $db->query("SELECT * FROM battlepass_levels WHERE season_id = {$season_id} ORDER BY level ASC");
    $rewards = array();
    
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            $level_rewards = [
                'level' => (int)$row['level'],
                'free_reward' => null,
                'premium_reward' => null,
                'premium_plus_reward' => null
            ];
            
            // Декодируем FREE награду
            if (!empty($row['free_reward_data'])) {
                $free_data = json_decode($row['free_reward_data'], true);
                if (is_array($free_data)) {
                    $check = $db->query("SELECT id FROM battlepass_rewards WHERE user_id = {$user_id} AND season_id = {$season_id} AND level = {$row['level']} AND reward_type = 'free' LIMIT 1");
                    $received = ($check && $check->num_rows > 0);
                    
                    $free_data['received'] = $received;
                    $free_data['count'] = (int)$row['free_reward_count'];
                    $level_rewards['free_reward'] = $free_data;
                }
            }
            
            // Декодируем PREMIUM награду
            if (!empty($row['premium_reward_data'])) {
                $premium_data = json_decode($row['premium_reward_data'], true);
                if (is_array($premium_data)) {
                    $check = $db->query("SELECT id FROM battlepass_rewards WHERE user_id = {$user_id} AND season_id = {$season_id} AND level = {$row['level']} AND reward_type = 'premium' LIMIT 1");
                    $received = ($check && $check->num_rows > 0);
                    
                    $premium_data['received'] = $received;
                    $premium_data['count'] = (int)$row['premium_reward_count'];
                    $level_rewards['premium_reward'] = $premium_data;
                }
            }
            
            // Декодируем PREMIUM+ награду
            if (!empty($row['premium_plus_reward_data'])) {
                $plus_data = json_decode($row['premium_plus_reward_data'], true);
                if (is_array($plus_data)) {
                    $check = $db->query("SELECT id FROM battlepass_rewards WHERE user_id = {$user_id} AND season_id = {$season_id} AND level = {$row['level']} AND reward_type = 'premium_plus' LIMIT 1");
                    $received = ($check && $check->num_rows > 0);
                    
                    $plus_data['received'] = $received;
                    $plus_data['count'] = (int)$row['premium_plus_reward_count'];
                    $level_rewards['premium_plus_reward'] = $plus_data;
                }
            }
            
            $rewards[] = $level_rewards;
        }
    }
    
    // Получаем персонажей для подсчета квестов
    $characters = getAccountCharacters($user_id);
    $role_ids = array();
    foreach ($characters as $char) {
        $role_ids[] = $char['roleid'];
    }
    $role_ids_str = !empty($role_ids) ? implode(',', $role_ids) : '0';
    
    // Получаем задания
    $q = $db->query("SELECT * FROM battlepass_quests WHERE season_id = {$season_id} AND is_active = 1");
    $quests = [
        'daily' => [],
        'weekly' => [],
        'seasonal' => []
    ];
    
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            // Получаем прогресс выполнения задания
            $q2 = $db->query("SELECT current_count, completed, completed_at FROM battlepass_quest_completions WHERE user_id = {$user_id} AND quest_id = {$row['id']} LIMIT 1");
            
            $current_count = 0;
            $completed = 0;
            $completed_at = null;
            
            if ($q2 && $q2->num_rows > 0) {
                $progress_quest = $q2->fetch_assoc();
                $current_count = (int)$progress_quest['current_count'];
                $completed = (int)$progress_quest['completed'];
                $completed_at = $progress_quest['completed_at'];
            }
            
            // ===== ОБРАБОТКА ЗАДАНИЙ НА ВХОД =====
            if ($row['target_type'] == 'login') {
                if ($completed && $completed_at) {
                    $completion_date = date('Y-m-d', strtotime($completed_at));
                    $today = date('Y-m-d');
                    
                    if ($completion_date != $today) {
                        $completed = 0;
                        $current_count = 0;
                        $db->query("DELETE FROM battlepass_quest_completions WHERE user_id = {$user_id} AND quest_id = {$row['id']}");
                    }
                }
                
                if (!$completed) {
                    $today_start = date('Y-m-d 00:00:00');
                    $log_q = $db->query("SELECT COUNT(*) as cnt FROM login_log 
                                         WHERE userid = {$user_id} 
                                         AND action = 3
                                         AND data >= '{$today_start}'");
                    
                    $login_count = 0;
                    if ($log_q && $log_q->num_rows > 0) {
                        $log_row = $log_q->fetch_assoc();
                        $login_count = (int)$log_row['cnt'];
                    }
                    
                    if ($login_count != $current_count) {
                        $current_count = $login_count;
                        $new_completed = ($current_count >= (int)$row['target_count']) ? 1 : 0;
                        
                        if ($q2 && $q2->num_rows > 0) {
                            $update_query = "UPDATE battlepass_quest_completions 
                                             SET current_count = {$current_count}, 
                                                 completed = {$new_completed},
                                                 completed_at = " . ($new_completed ? "NOW()" : "NULL") . "
                                             WHERE user_id = {$user_id} AND quest_id = {$row['id']}";
                            $db->query($update_query);
                        } else {
                            $login_esc = $db->real_escape_string($login);
                            $insert_query = "INSERT INTO battlepass_quest_completions 
                                             (user_id, login, quest_id, current_count, completed, completed_at) 
                                             VALUES ({$user_id}, '{$login_esc}', {$row['id']}, {$current_count}, {$new_completed}, " . ($new_completed ? "NOW()" : "NULL") . ")";
                            $db->query($insert_query);
                        }
                        
                        if ($new_completed && !$completed) {
                            AddBattlePassExp($user_id, $season_id, (int)$row['reward_exp'], $login);
                            $completed = 1;
                        } else {
                            $completed = $new_completed;
                        }
                    }
                }
            }
            
            // ===== ОБРАБОТКА ЗАДАНИЙ НА УБИЙСТВА =====
            elseif ($row['target_type'] == 'kill_player') {
                $total_kills_today = countTodayKillsForAccount($user_id);
                
                if ($total_kills_today != $current_count) {
                    $current_count = $total_kills_today;
                    $new_completed = ($current_count >= (int)$row['target_count']) ? 1 : 0;
                    
                    if ($q2 && $q2->num_rows > 0) {
                        $update_query = "UPDATE battlepass_quest_completions 
                                         SET current_count = {$current_count}, 
                                             completed = {$new_completed},
                                             completed_at = " . ($new_completed ? "NOW()" : "NULL") . "
                                         WHERE user_id = {$user_id} AND quest_id = {$row['id']}";
                        $db->query($update_query);
                    } else {
                        $login_esc = $db->real_escape_string($login);
                        $insert_query = "INSERT INTO battlepass_quest_completions 
                                         (user_id, login, quest_id, current_count, completed, completed_at) 
                                         VALUES ({$user_id}, '{$login_esc}', {$row['id']}, {$current_count}, {$new_completed}, " . ($new_completed ? "NOW()" : "NULL") . ")";
                        $db->query($insert_query);
                    }
                    
                    if ($new_completed && !$completed) {
                        AddBattlePassExp($user_id, $season_id, (int)$row['reward_exp'], $login);
                        $completed = 1;
                    } else {
                        $completed = $new_completed;
                    }
                } else {
                    if ($total_kills_today >= (int)$row['target_count'] && !$completed) {
                        $completed = 1;
                        
                        if ($q2 && $q2->num_rows > 0) {
                            $db->query("UPDATE battlepass_quest_completions 
                                        SET completed = 1, completed_at = NOW() 
                                        WHERE user_id = {$user_id} AND quest_id = {$row['id']}");
                            AddBattlePassExp($user_id, $season_id, (int)$row['reward_exp'], $login);
                        }
                    }
                }
            }
            
            // ===== ОБРАБОТКА ЗАДАНИЙ НА ВЫПОЛНЕНИЕ КВЕСТОВ (complete_quest) =====
            elseif ($row['target_type'] == 'complete_quest' && !empty($role_ids)) {
                // Получаем целевой ID квеста
                $target_quest_id = 0;
                
                if (!empty($row['target_params'])) {
                    $params = json_decode($row['target_params'], true);
                    if (is_array($params) && isset($params['game_quest_id'])) {
                        $target_quest_id = (int)$params['game_quest_id'];
                    }
                }
                
                if ($target_quest_id == 0 && isset($row['target_id']) && $row['target_id'] > 0) {
                    $target_quest_id = (int)$row['target_id'];
                }
                
                // Определяем период в зависимости от типа задания
                $date_condition = "";
                
                if ($row['quest_type'] == 'daily') {
                    $today_start = date('Y-m-d 00:00:00');
                    $today_end = date('Y-m-d 23:59:59');
                    $date_condition = "AND date >= '{$today_start}' AND date <= '{$today_end}'";
                } elseif ($row['quest_type'] == 'weekly') {
                    $week_start = date('Y-m-d 00:00:00', strtotime('monday this week'));
                    $week_end = date('Y-m-d 23:59:59', strtotime('sunday this week'));
                    $date_condition = "AND date >= '{$week_start}' AND date <= '{$week_end}'";
                } elseif ($row['quest_type'] == 'seasonal') {
                    $date_condition = "AND date >= '{$season['date_start']}' AND date <= '{$season['date_end']}'";
                }
                
                // Считаем количество выполненных квестов
                $count_query = "
                    SELECT COUNT(*) as cnt
                    FROM task_log
                    WHERE id_player IN ({$role_ids_str})
                    {$date_condition}
                ";
                
                if ($target_quest_id > 0) {
                    $count_query .= " AND id_quest = {$target_quest_id}";
                }
                
                $count_result = $db->query($count_query);
                if ($count_result) {
                    $row_cnt = $count_result->fetch_assoc();
                    $total_quests = (int)$row_cnt['cnt'];
                    
                    if ($total_quests != $current_count) {
                        $current_count = $total_quests;
                        $new_completed = ($current_count >= (int)$row['target_count']) ? 1 : 0;
                        
                        if ($q2 && $q2->num_rows > 0) {
                            $update_query = "UPDATE battlepass_quest_completions 
                                             SET current_count = {$current_count}, 
                                                 completed = {$new_completed},
                                                 completed_at = " . ($new_completed ? "NOW()" : "NULL") . "
                                             WHERE user_id = {$user_id} AND quest_id = {$row['id']}";
                            $db->query($update_query);
                        } else {
                            $login_esc = $db->real_escape_string($login);
                            $insert_query = "INSERT INTO battlepass_quest_completions 
                                             (user_id, login, quest_id, current_count, completed, completed_at) 
                                             VALUES ({$user_id}, '{$login_esc}', {$row['id']}, {$current_count}, {$new_completed}, " . ($new_completed ? "NOW()" : "NULL") . ")";
                            $db->query($insert_query);
                        }
                        
                        if ($new_completed && !$completed) {
                            AddBattlePassExp($user_id, $season_id, (int)$row['reward_exp'], $login);
                            $completed = 1;
                        } else {
                            $completed = $new_completed;
                        }
                    }
                }
            }
            
            // ===== ОБРАБОТКА ЗАДАНИЙ НА ГОЛОСОВАНИЕ (vote) =====
            elseif ($row['target_type'] == 'vote') {
                // Определяем период в зависимости от типа задания
                $date_condition = "";
                
                if ($row['quest_type'] == 'daily') {
                    $today_start = date('Y-m-d 00:00:00');
                    $today_end = date('Y-m-d 23:59:59');
                    $date_condition = "AND data >= '{$today_start}' AND data <= '{$today_end}'";
                } elseif ($row['quest_type'] == 'weekly') {
                    $week_start = date('Y-m-d 00:00:00', strtotime('monday this week'));
                    $week_end = date('Y-m-d 23:59:59', strtotime('sunday this week'));
                    $date_condition = "AND data >= '{$week_start}' AND data <= '{$week_end}'";
                } elseif ($row['quest_type'] == 'seasonal') {
                    $date_condition = "AND data >= '{$season['date_start']}' AND data <= '{$season['date_end']}'";
                }
                
                // Учитываем статус голосования (status = 1,7 - успешное)
                $status_condition = "AND status IN (1, 7)";
                
                // Учитываем конкретный vote_id если указан
                $vote_condition = "";
                if (!empty($row['target_id']) && $row['target_id'] > 0) {
                    $vote_condition = "AND vote_id = " . (int)$row['target_id'];
                }
                
                // Считаем количество голосований
                $vote_query = "
                    SELECT COUNT(*) as cnt
                    FROM mmotop_data
                    WHERE userid = {$user_id}
                    {$date_condition}
                    {$status_condition}
                    {$vote_condition}
                ";
                
                $vote_result = $db->query($vote_query);
                $total_votes = 0;
                
                if ($vote_result && $vote_result->num_rows > 0) {
                    $vote_row = $vote_result->fetch_assoc();
                    $total_votes = (int)$vote_row['cnt'];
                }
                
                if ($total_votes != $current_count) {
                    $current_count = $total_votes;
                    $new_completed = ($current_count >= (int)$row['target_count']) ? 1 : 0;
                    
                    if ($q2 && $q2->num_rows > 0) {
                        $update_query = "UPDATE battlepass_quest_completions 
                                         SET current_count = {$current_count}, 
                                             completed = {$new_completed},
                                             completed_at = " . ($new_completed ? "NOW()" : "NULL") . "
                                         WHERE user_id = {$user_id} AND quest_id = {$row['id']}";
                        $db->query($update_query);
                    } else {
                        $login_esc = $db->real_escape_string($login);
                        $insert_query = "INSERT INTO battlepass_quest_completions 
                                         (user_id, login, quest_id, current_count, completed, completed_at) 
                                         VALUES ({$user_id}, '{$login_esc}', {$row['id']}, {$current_count}, {$new_completed}, " . ($new_completed ? "NOW()" : "NULL") . ")";
                        $db->query($insert_query);
                    }
                    
                    if ($new_completed && !$completed) {
                        AddBattlePassExp($user_id, $season_id, (int)$row['reward_exp'], $login);
                        $completed = 1;
                    } else {
                        $completed = $new_completed;
                    }
                } else {
                    if ($total_votes >= (int)$row['target_count'] && !$completed) {
                        $completed = 1;
                        
                        if ($q2 && $q2->num_rows > 0) {
                            $db->query("UPDATE battlepass_quest_completions 
                                        SET completed = 1, completed_at = NOW() 
                                        WHERE user_id = {$user_id} AND quest_id = {$row['id']}");
                            AddBattlePassExp($user_id, $season_id, (int)$row['reward_exp'], $login);
                        }
                    }
                }
            }
            
            $row['current_count'] = $current_count;
            $row['completed'] = $completed;
            $row['progress_percent'] = ((int)$row['target_count'] > 0) ? ($current_count / (int)$row['target_count']) * 100 : 0;
            
            // Декодируем target_params для передачи на клиент
            if (!empty($row['target_params'])) {
                $row['target_params'] = json_decode($row['target_params'], true);
            }
            
            if (isset($quests[$row['quest_type']])) {
                $quests[$row['quest_type']][] = $row;
            }
        }
    }
    
    // Получаем товары магазина
    $q = $db->query("SELECT * FROM battlepass_shop WHERE season_id = {$season_id} AND is_active = 1 ORDER BY sort_order");
    $shop_items = [
        'ingots' => [],
        'tokens' => []
    ];
    
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            $q2 = $db->query("SELECT COUNT(*) as cnt FROM battlepass_purchases WHERE shop_item_id = {$row['id']}");
            if ($q2 && $q2->num_rows > 0) {
                $purchases = $q2->fetch_assoc();
                $row['purchased_count'] = (int)$purchases['cnt'];
            } else {
                $row['purchased_count'] = 0;
            }
            
            if ($row['discount_percent'] > 0) {
                $row['current_price_gold'] = (int)($row['price_gold'] * (100 - $row['discount_percent']) / 100);
                $row['current_price_silver'] = (int)($row['price_silver'] * (100 - $row['discount_percent']) / 100);
            } else {
                $row['current_price_gold'] = (int)$row['price_gold'];
                $row['current_price_silver'] = (int)$row['price_silver'];
            }
            
            if (!empty($row['item_data'])) {
                $item_data = json_decode($row['item_data'], true);
                if (is_array($item_data)) {
                    $row['item_data_array'] = $item_data;
                }
            }
            
            $shop_items[$row['category']][] = $row;
        }
    }
    
    // Рассчитываем оставшиеся дни
    $date_end = strtotime($season['date_end']);
    $now = time();
    $days_left = max(0, ceil(($date_end - $now) / (60 * 60 * 24)));
    $season['days_left'] = $days_left;
    
    // Формируем ответ
    $response = [
        'errorcode' => 0,
        'active' => true,
        'season' => $season,
        'progress' => $progress,
        'rewards' => $rewards,
        'quests' => $quests,
        'shop' => $shop_items,
        'characters' => $characters
    ];
    
    $json = json_encode($response, JSON_UNESCAPED_UNICODE);
    if ($json === false) {
        $error = json_last_error_msg();
        error_log("JSON encode error: " . $error);
        
        $response = [
            'errorcode' => 1,
            'message' => 'json_encode_error: ' . $error
        ];
        echo json_encode($response);
        return;
    }
    
    echo $json;
}

/**
 * Получение награды
 */

function BpClaimReward() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $login = isset($_POST['login']) ? $_POST['login'] : '';
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    $level = isset($_POST['level']) ? (int)$_POST['level'] : 0;
    $reward_type = isset($_POST['reward_type']) ? $_POST['reward_type'] : '';
    $target_player = isset($_POST['target_player']) ? (int)$_POST['target_player'] : 0;
    
    // Логируем входящие данные
    error_log("BpClaimReward called with: " . json_encode([
        'user_id' => $user_id,
        'login' => $login,
        'season_id' => $season_id,
        'level' => $level,
        'reward_type' => $reward_type,
        'target_player' => $target_player
    ]));
    
    if (!$user_id || !$season_id || !$level || !$reward_type) {
        echo json_encode(['errorcode' => 1, 'message' => 'Не указаны обязательные параметры']);
        return;
    }
    
    if (!$target_player) {
        echo json_encode(['errorcode' => 1, 'message' => 'Не выбран персонаж для получения награды']);
        return;
    }
    
    // Проверяем сессию на дубликат отправки
    $session_key = 'bp_sent_' . $season_id . '_' . $level . '_' . $reward_type;
    if (isset($_SESSION[$session_key]) && $_SESSION[$session_key] === true) {
        echo json_encode(['errorcode' => 1, 'message' => 'Эта награда уже была отправлена в текущей сессии']);
        return;
    }
    
    // Проверяем, что уровень доступен
    $q = $db->query("SELECT current_level FROM battlepass_progress WHERE user_id = {$user_id} AND season_id = {$season_id} LIMIT 1");
    if (!$q) {
        error_log("BpClaimReward DB Error (progress query): " . $db->error);
        echo json_encode(['errorcode' => 1, 'message' => 'Ошибка при проверке прогресса']);
        return;
    }
    
    if ($q->num_rows == 0) {
        echo json_encode(['errorcode' => 1, 'message' => 'Прогресс не найден']);
        return;
    }
    
    $progress = $q->fetch_assoc();
    
    if ($level > $progress['current_level']) {
        echo json_encode(['errorcode' => 1, 'message' => 'Уровень еще не достигнут']);
        return;
    }
    
    // Проверяем, не получена ли уже награда
    $check_query = "SELECT id FROM battlepass_rewards WHERE user_id = {$user_id} AND season_id = {$season_id} AND level = {$level} AND reward_type = '{$reward_type}' LIMIT 1";
    $q = $db->query($check_query);
    
    if (!$q) {
        // Если таблицы нет, создаем её по структуре из дампа
        $db->query("CREATE TABLE IF NOT EXISTS battlepass_rewards (
            id INT AUTO_INCREMENT PRIMARY KEY,
            user_id INT NOT NULL,
            login VARCHAR(50) NOT NULL,
            season_id INT NOT NULL,
            level INT NOT NULL,
            reward_type ENUM('free','premium','premium_plus') DEFAULT 'free',
            received_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            UNIQUE KEY user_level_reward (user_id, season_id, level, reward_type)
        ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci");
    } else {
        if ($q->num_rows > 0) {
            echo json_encode(['errorcode' => 1, 'message' => 'Награда уже получена']);
            return;
        }
    }
    
    // Получаем данные награды
    $q = $db->query("SELECT * FROM battlepass_levels WHERE season_id = {$season_id} AND level = {$level} LIMIT 1");
    if (!$q) {
        error_log("BpClaimReward DB Error (levels query): " . $db->error);
        echo json_encode(['errorcode' => 1, 'message' => 'Ошибка при получении данных награды']);
        return;
    }
    
    if ($q->num_rows == 0) {
        echo json_encode(['errorcode' => 1, 'message' => 'Награда не найдена']);
        return;
    }
    
    $level_data = $q->fetch_assoc();
    
    // Получаем данные награды в зависимости от типа
    $reward_json = '';
    
    if ($reward_type == 'free') {
        $reward_json = $level_data['free_reward_data'];
    } elseif ($reward_type == 'premium') {
        $reward_json = $level_data['premium_reward_data'];
    } elseif ($reward_type == 'premium_plus') {
        $reward_json = $level_data['premium_plus_reward_data'];
    }
    
    if (empty($reward_json)) {
        echo json_encode(['errorcode' => 1, 'message' => 'Нет данных о награде']);
        return;
    }
    
    error_log("Reward data: " . $reward_json);
    
    // 🔥 ОТПРАВЛЯЕМ НАГРАДУ ЧЕРЕЗ SysSendMail
    $send_result = BpSendItemByMail(
        $target_player,
        $reward_json,
        $level,
        $reward_type,
        $season_id,
        $user_id,      // Добавляем user_id
        $login         // Добавляем login
    );
    
    if ($send_result['errorcode'] !== 0) {
        error_log("BpSendItemByMail error: " . json_encode($send_result));
        echo json_encode($send_result);
        return;
    }
    
    // Отмечаем награду как полученную - ТОЛЬКО ПОЛЯ, КОТОРЫЕ ЕСТЬ В ТАБЛИЦЕ
    $login_esc = $db->real_escape_string($login);
    
    $insert_query = "INSERT INTO battlepass_rewards 
                    (user_id, login, season_id, level, reward_type, received_at) 
                    VALUES (
                        {$user_id}, 
                        '{$login_esc}', 
                        {$season_id}, 
                        {$level}, 
                        '{$reward_type}', 
                        NOW()
                    )";
    
    error_log("BpClaimReward insert query: " . $insert_query);
    $insert_result = $db->query($insert_query);
    
    if (!$insert_result) {
        error_log("BpClaimReward insert error: " . $db->error);
        // Не возвращаем ошибку, так как предмет уже отправлен
    }
    
    $_SESSION[$session_key] = true;
    
    echo json_encode([
        'errorcode' => 0,
        'message' => 'Награда отправлена в игру!',
        'level' => $level,
        'reward_type' => $reward_type,
        'character_id' => $target_player
    ]);
}

/**
 * Отправка предмета в игру
 */
function BpSendItemByMail($roleid, $reward_json, $level, $reward_type, $season_id, $user_id, $login) {
    global $Structures, $pp, $db;
    
    // Получаем имя персонажа
    $character_name = GetCharacterName($roleid);
    
    // Декодируем JSON с наградой
    $item_data = json_decode($reward_json, true);
    if (!$item_data || !isset($item_data['item_id'])) {
        logBattlePassReward(
            $user_id, $login, $season_id, $level, $reward_type, 
            $item_data, $roleid, $character_name, 
            ['error' => 'Invalid reward data'], 'error', 'Неверный формат награды'
        );
        return ['errorcode' => 1, 'message' => 'Неверный формат награды'];
    }
    
    // Создаем структуру предмета
    if (!isset($pp)) $pp = new Protocols();
    
    $item = [
        'id' => (int)$item_data['item_id'],
        'pos' => 0,
        'count' => isset($item_data['count']) ? (int)$item_data['count'] : 1,
        'client_size' => isset($item_data['client_size']) ? (int)$item_data['client_size'] : 0,
        'max_count' => isset($item_data['max_count']) ? (int)$item_data['max_count'] : (isset($item_data['count']) ? (int)$item_data['count'] : 1),
        'data' => isset($item_data['data']) ? $item_data['data'] : '',
        'proctype' => isset($item_data['proctype']) ? (int)$item_data['proctype'] : 0,
        'expire_date' => isset($item_data['expire_date']) ? (int)$item_data['expire_date'] : 0,
        'guid1' => 0,
        'guid2' => 0
    ];
    
    // Обработка данных предмета если есть
    if (!empty($item['data']) && is_string($item['data']) && ctype_xdigit(str_replace(' ', '', $item['data']))) {
        $item['data'] = hex2bin(str_replace(' ', '', $item['data']));
        $marshaled = $pp->marshal($item['data'], $Structures['Octets']);
        $item['data'] = $marshaled ?: '';
    }
    
    // Определяем заголовок и текст письма в зависимости от типа
    if ($reward_type == 'shop') {
        $title = "Покупка в магазине Battle Pass";
        $context = "Вы совершили покупку в магазине Боевого пропуска.\n\n";
        $context .= " Спасибо за покупку!";
    } else {
        $title = "Награда Battle Pass";
        $context = "Вы получили награду за {$level} уровень Боевого пропуска!\n\n";
        $context .= " Удачи в игре!";
    }
    
    // Отправляем через SysSendMail
    $result = SysSendMail(0, 32, 3, $roleid, $title, $context, $item, 0);
    
    // Логируем результат
    if (is_array($result) && isset($result['retcode']) && $result['retcode'] == 0) {
        logBattlePassReward(
            $user_id, $login, $season_id, $level, $reward_type,
            $item_data, $roleid, $character_name,
            $result, 'success', ''
        );
        return ['errorcode' => 0, 'message' => 'Предмет отправлен'];
    } else {
        $error_msg = is_array($result) && isset($result['retcode']) 
            ? "Код ошибки: {$result['retcode']}" 
            : "Неизвестная ошибка";
        
        if (isset($result['retcode']) && $result['retcode'] == 217) {
            $error_msg = "Почтовый ящик переполнен";
        }
        
        logBattlePassReward(
            $user_id, $login, $season_id, $level, $reward_type,
            $item_data, $roleid, $character_name,
            $result, 'error', $error_msg
        );
        
        return ['errorcode' => 1, 'message' => 'Ошибка отправки: ' . $error_msg];
    }
}

/**
 * Получение названия предмета по ID
 */
function GetItemName($item_id) {
    $postdata = array(
        'op' => 'adm',
        'act' => 'itemname',
        'itemid' => $item_id
    );
    
    $result = CurlPage($postdata, 3);
    
    // Очищаем ответ от скриптов
    $clean = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $result);
    
    // Парсим HTML
    $doc = new DOMDocument();
    @$doc->loadHTML('<?xml encoding="UTF-8">' . $clean);
    
    // Получаем название
    $name = trim($doc->textContent);
    
    // Очищаем от лишних пробелов
    $name = preg_replace('/\s+/', ' ', $name);
    $name = trim($name);
    
    // Если название пустое или содержит подозрительный код
    if (empty($name) || strlen($name) > 100 || strpos($name, 'var ') !== false || strpos($name, 'license') !== false) {
        // Пробуем получить название из кеша предметов
        global $item_cache;
        if (isset($item_cache[$item_id]) && !empty($item_cache[$item_id]['name'])) {
            return $item_cache[$item_id]['name'];
        }
        return "Предмет #{$item_id}";
    }
    
    return $name;
}
/**
 * Получение имени персонажа по ID
 */
function GetCharacterName($roleid) {
    global $db;
    
    $q = $db->query("SELECT name FROM players WHERE id = {$roleid} LIMIT 1");
    if ($q && $q->num_rows > 0) {
        $row = $q->fetch_assoc();
        return $row['name'];
    }
    return 'Unknown';
}


/**
 * Покупка премиум пропуска
 */
function BpPurchase() {
    global $db;
    
    // Очищаем все буферы
    while (ob_get_level()) {
        ob_end_clean();
    }
    
    header('Content-Type: application/json; charset=utf-8');
    header('X-Content-Type-Options: nosniff');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $login = isset($_POST['login']) ? trim($_POST['login']) : '';
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    $pass_type = isset($_POST['pass_type']) ? trim($_POST['pass_type']) : '';
    $target_player = isset($_POST['target_player']) ? (int)$_POST['target_player'] : 0;
    
    // Проверяем обязательные параметры
    if (!$user_id || !$season_id || !$pass_type) {
        echo json_encode(['errorcode' => 1, 'message' => 'Не указаны обязательные параметры']);
        exit;
    }
    
    if (!$target_player) {
        echo json_encode(['errorcode' => 1, 'message' => 'Не выбран персонаж']);
        exit;
    }
    
    // Получаем данные сезона
    $q = $db->query("SELECT * FROM battlepass_seasons WHERE id = {$season_id} LIMIT 1");
    if (!$q || $q->num_rows == 0) {
        echo json_encode(['errorcode' => 1, 'message' => 'Сезон не найден']);
        exit;
    }
    $season = $q->fetch_assoc();
    
    // Получаем прогресс игрока
    $q = $db->query("SELECT * FROM battlepass_progress WHERE user_id = {$user_id} AND season_id = {$season_id} LIMIT 1");
    if (!$q || $q->num_rows == 0) {
        // Создаем прогресс если его нет
        $login_esc = $db->real_escape_string($login);
        $db->query("INSERT INTO battlepass_progress (user_id, login, season_id, current_level, current_exp, total_exp, pass_type, purchased, notification_enabled) 
                    VALUES ({$user_id}, '{$login_esc}', {$season_id}, 1, 0, 0, 'free', 0, 1)");
        
        $q = $db->query("SELECT * FROM battlepass_progress WHERE user_id = {$user_id} AND season_id = {$season_id} LIMIT 1");
        if (!$q || $q->num_rows == 0) {
            echo json_encode(['errorcode' => 1, 'message' => 'Не удалось создать прогресс']);
            exit;
        }
    }
    
    $progress = $q->fetch_assoc();
    
    // Определяем цену
    $price = 0;
    if ($pass_type == 'premium') {
        $price = (int)$season['price_premium'];
    } elseif ($pass_type == 'premium_plus') {
        $price = (int)$season['price_premium_plus'];
    } else {
        echo json_encode(['errorcode' => 1, 'message' => 'Неверный тип пропуска']);
        exit;
    }
    
    // Проверяем возможность покупки/улучшения
    $is_upgrade = false;
    if ($progress['purchased']) {
        if ($progress['pass_type'] == 'premium' && $pass_type == 'premium_plus') {
            // Улучшение - списываем разницу
            $price = max(0, (int)$season['price_premium_plus'] - (int)$season['price_premium']);
            $is_upgrade = true;
        } elseif ($progress['pass_type'] == 'premium_plus') {
            echo json_encode(['errorcode' => 1, 'message' => 'У вас уже активирован максимальный пропуск']);
            exit;
        } elseif ($progress['pass_type'] == $pass_type) {
            echo json_encode(['errorcode' => 1, 'message' => 'Этот пропуск уже активирован']);
            exit;
        }
    }
    
    // ПОЛУЧАЕМ ТЕКУЩИЙ БАЛАНС ПОЛЬЗОВАТЕЛЯ ИЗ ТАБЛИЦЫ server.users
    $q = $db->query("SELECT lkgold FROM server.users WHERE id = {$user_id} LIMIT 1");
    if (!$q) {
        error_log("Balance query error: " . $db->error);
        echo json_encode(['errorcode' => 1, 'message' => 'Ошибка при проверке баланса: ' . $db->error]);
        exit;
    }
    if ($q->num_rows == 0) {
        error_log("User not found, ID={$user_id}");
        echo json_encode(['errorcode' => 1, 'message' => 'Пользователь не найден']);
        exit;
    }
    $user_data = $q->fetch_assoc();
    $current_balance = (int)$user_data['lkgold'];
    
    // ПРОВЕРЯЕМ ДОСТАТОЧНО ЛИ СРЕДСТВ
    if ($current_balance < $price) {
        echo json_encode([
            'errorcode' => 1, 
            'message' => "Недостаточно средств. Нужно: {$price} золота, у вас: {$current_balance} золота"
        ]);
        exit;
    }
    
    // СПИСЫВАЕМ СРЕДСТВА
    $new_balance = $current_balance - $price;
    $update_balance = $db->query("UPDATE server.users SET lkgold = {$new_balance} WHERE id = {$user_id}");
    
    if (!$update_balance) {
        error_log("Balance update error: " . $db->error);
        echo json_encode(['errorcode' => 1, 'message' => 'Ошибка при списании средств: ' . $db->error]);
        exit;
    }
    
    // Обновляем прогресс Battle Pass
    $new_pass_type = $pass_type;
    $new_purchased = 1;
    
    $update_query = "UPDATE battlepass_progress 
                     SET pass_type = '{$new_pass_type}', 
                         purchased = 1,
                         last_update = NOW()
                     WHERE id = {$progress['id']}";
    
    if (!$db->query($update_query)) {
        error_log("Progress update error: " . $db->error);
    }
    
    // ЛОГИРУЕМ ПОКУПКУ
    logBattlePassPurchase($user_id, $login, $season_id, $pass_type, $price, $current_balance, $new_balance);
    
    // Формируем сообщение
    $message = '';
    if ($pass_type == 'premium') {
        $message = "Премиум пропуск успешно активирован! Списано {$price} золота.";
    } elseif ($pass_type == 'premium_plus') {
        if ($is_upgrade) {
            $message = "Пропуск улучшен до Премиум+! Списано {$price} золота.";
        } else {
            $message = "Премиум+ пропуск успешно активирован! Списано {$price} золота.";
        }
    }
    
    echo json_encode([
        'errorcode' => 0, 
        'message' => $message,
        'pass_type' => $new_pass_type,
        'purchased' => 1,
        'balance' => $new_balance
    ]);
    exit;
}

/**
 * Покупка в магазине
 */
function BpPurchaseShop() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Логируем входящие данные
    error_log("=== BpPurchaseShop ===");
    error_log("POST data: " . print_r($_POST, true));
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $login = isset($_POST['login']) ? trim($_POST['login']) : '';
    $shop_item_id = isset($_POST['shop_item_id']) ? (int)$_POST['shop_item_id'] : 0;
    $target_player = isset($_POST['target_player']) ? (int)$_POST['target_player'] : 0;
    $request_id = isset($_POST['request_id']) ? $_POST['request_id'] : '';
    
    if (!$user_id || !$shop_item_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'Не указаны обязательные параметры']);
        return;
    }
    
    if (!$target_player) {
        echo json_encode(['errorcode' => 1, 'message' => 'Не выбран персонаж']);
        return;
    }
    
    // Получаем товар
    $q = $db->query("SELECT * FROM battlepass_shop WHERE id = {$shop_item_id} LIMIT 1");
    if (!$q || $q->num_rows == 0) {
        echo json_encode(['errorcode' => 1, 'message' => 'Товар не найден']);
        return;
    }
    
    $item = $q->fetch_assoc();
    
    // Проверяем лимит
    if ($item['stock_limit'] > 0 && $item['purchased_count'] >= $item['stock_limit']) {
        echo json_encode(['errorcode' => 1, 'message' => 'Лимит товара исчерпан']);
        return;
    }
    
    // Проверяем баланс пользователя
    $q = $db->query("SELECT lkgold FROM server.users WHERE id = {$user_id} LIMIT 1");
    if (!$q || $q->num_rows == 0) {
        echo json_encode(['errorcode' => 1, 'message' => 'Пользователь не найден']);
        return;
    }
    
    $user = $q->fetch_assoc();
    $balance = (int)$user['lkgold'];
    
    // Рассчитываем цену со скидкой
    $price = $item['price_gold'];
    if ($item['discount_percent'] > 0) {
        $price = $price * (100 - $item['discount_percent']) / 100;
    }
    
    if ($balance < $price) {
        echo json_encode(['errorcode' => 1, 'message' => 'Недостаточно средств']);
        return;
    }
    
    // Получаем данные предмета из JSON
    $item_data = json_decode($item['item_data'], true);
    if (!$item_data) {
        echo json_encode(['errorcode' => 1, 'message' => 'Ошибка в данных предмета']);
        return;
    }
    
    // Отправляем предмет в игру (передаем уровень = 0 для магазина)
    $send_result = BpSendItemByMail(
        $target_player,
        json_encode($item_data),
        0, // уровень 0 для магазина
        'shop', // тип shop
        $item['season_id'],
        $user_id,
        $login
    );
    
    if ($send_result['errorcode'] !== 0) {
        echo json_encode($send_result);
        return;
    }
    
    // Списываем средства
    $new_balance = $balance - $price;
    $db->query("UPDATE server.users SET lkgold = {$new_balance} WHERE id = {$user_id}");
    
    // Обновляем счетчик покупок
    $db->query("UPDATE battlepass_shop SET purchased_count = purchased_count + 1 WHERE id = {$shop_item_id}");
    
    // Логируем покупку
    logBattlePassShopPurchase(
        $user_id, $login, $shop_item_id, $item['item_name'],
        $price, 0, $item_data, $target_player, '', $send_result
    );
    
    echo json_encode([
        'errorcode' => 0,
        'message' => 'Покупка успешно совершена! Предмет отправлен в игру.',
        'new_balance' => $new_balance
    ]);
}

/**
 * Обновление уведомлений
 */
function BpUpdateNotification() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    $enabled = isset($_POST['enabled']) ? (int)$_POST['enabled'] : 0;
    
    if (!$user_id || !$season_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_data']);
        return;
    }
    
    $db->query("UPDATE battlepass_progress SET notification_enabled = {$enabled} WHERE user_id = {$user_id} AND season_id = {$season_id}");
    
    echo json_encode(['errorcode' => 0]);
}

function AddBattlePassExp($user_id, $season_id, $exp_amount, $login = '') {
    global $db;
    
    if ($exp_amount <= 0) {
        return false;
    }
    
    // Получаем текущий прогресс
    $q = $db->query("SELECT * FROM battlepass_progress 
                     WHERE user_id = {$user_id} AND season_id = {$season_id} 
                     LIMIT 1");
    
    if (!$q || $q->num_rows == 0) {
        return false;
    }
    
    $progress = $q->fetch_assoc();
    
    // Получаем сезон
    $q = $db->query("SELECT * FROM battlepass_seasons WHERE id = {$season_id} LIMIT 1");
    if (!$q || $q->num_rows == 0) {
        return false;
    }
    
    $season = $q->fetch_assoc();
    
    $new_exp = $progress['current_exp'] + $exp_amount;
    $new_total = $progress['total_exp'] + $exp_amount;
    $new_level = $progress['current_level'];
    
    // Проверяем повышение уровня
    $exp_needed = (int)$season['exp_per_level'];
    $max_level = (int)$season['max_level'];
    
    while ($new_exp >= $exp_needed && $new_level < $max_level) {
        $new_exp -= $exp_needed;
        $new_level++;
    }
    
    // Обновляем прогресс
    $update_query = "UPDATE battlepass_progress 
                    SET current_exp = {$new_exp}, 
                        current_level = {$new_level},
                        total_exp = {$new_total}
                    WHERE id = {$progress['id']}";
    
    $db->query($update_query);
    
    error_log("Exp added: user={$user_id}, amount={$exp_amount}, new_level={$new_level}");
    
    return true;
}

function GetBattlePassDaysLeft($season) {
    $now = time();
    $end = strtotime($season['date_end']);
    $days = ceil(($end - $now) / 86400);
    return max(0, $days);
}

/**
 * Получение текущей недели сезона
 */
function GetBattlePassCurrentWeek($season) {
    $start = strtotime($season['date_start']);
    $now = time();
    $days = floor(($now - $start) / 86400);
    $week = floor($days / 7) + 1;
    return max(1, min($week, 52));
}

/**
 * Проверка выполнения задания при входе
 */
function BpCheckLoginQuest() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $login = isset($_POST['login']) ? $_POST['login'] : '';
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    
    if (!$user_id || !$season_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'Не указаны параметры']);
        return;
    }
    
    // Получаем задание "Вход в игру"
    $q = $db->query("SELECT * FROM battlepass_quests 
                     WHERE season_id = {$season_id} 
                     AND target_type = 'login' 
                     AND is_active = 1 
                     LIMIT 1");
    
    if (!$q || $q->num_rows == 0) {
        echo json_encode(['errorcode' => 0, 'message' => 'Нет задания на вход']);
        return;
    }
    
    $quest = $q->fetch_assoc();
    $quest_id = $quest['id'];
    $target_count = (int)$quest['target_count']; // Сколько входов нужно (обычно 1)
    
    // Получаем текущий прогресс
    $progress_q = $db->query("SELECT * FROM battlepass_quest_completions 
                              WHERE user_id = {$user_id} 
                              AND quest_id = {$quest_id}
                              LIMIT 1");
    
    $current_count = 0;
    $completed = false;
    $progress_id = 0;
    
    if ($progress_q && $progress_q->num_rows > 0) {
        $progress = $progress_q->fetch_assoc();
        $current_count = (int)$progress['current_count'];
        $completed = (bool)$progress['completed'];
        $progress_id = $progress['id'];
        
        // Проверяем, не устарела ли запись (для ежедневных заданий)
        if ($progress['completed_at']) {
            $completed_date = date('Y-m-d', strtotime($progress['completed_at']));
            $today = date('Y-m-d');
            
            // Если задание было выполнено вчера или раньше - сбрасываем
            if ($completed_date != $today) {
                $current_count = 0;
                $completed = false;
                
                // Обновляем запись или удалим её для чистоты
                $db->query("UPDATE battlepass_quest_completions 
                            SET current_count = 0, 
                                completed = 0, 
                                completed_at = NULL,
                                exp_received = 0
                            WHERE id = {$progress_id}");
            }
        }
    }
    
    // Если уже выполнили норму сегодня - выходим
    if ($completed) {
        echo json_encode([
            'errorcode' => 0,
            'completed' => true,
            'message' => 'Задание уже выполнено сегодня'
        ]);
        return;
    }
    
    // Проверяем логи входа за сегодня
    $today_start = date('Y-m-d 00:00:00');
    $today_end = date('Y-m-d 23:59:59');
    
    $log_q = $db->query("SELECT COUNT(*) as cnt FROM login_log 
                         WHERE userid = {$user_id} 
                         AND action = 3
                         AND data >= '{$today_start}' 
                         AND data <= '{$today_end}'");
    
    if (!$log_q) {
        echo json_encode(['errorcode' => 1, 'message' => 'Ошибка проверки логов']);
        return;
    }
    
    $row = $log_q->fetch_assoc();
    $login_count = (int)$row['cnt'];
    
    // Если сегодня не заходил - выходим
    if ($login_count == 0) {
        echo json_encode([
            'errorcode' => 0,
            'completed' => false,
            'message' => 'Вход не найден'
        ]);
        return;
    }
    
    // Обновляем прогресс
    $new_completed = ($login_count >= $target_count) ? 1 : 0;
    
    if ($progress_id > 0) {
        // Обновляем существующую запись
        $update = $db->query("UPDATE battlepass_quest_completions 
                              SET current_count = {$login_count}, 
                                  completed = {$new_completed},
                                  completed_at = " . ($new_completed ? "NOW()" : "NULL") . "
                              WHERE id = {$progress_id}");
    } else {
        // Создаем новую запись
        $login_esc = $db->real_escape_string($login);
        $insert = $db->query("INSERT INTO battlepass_quest_completions 
                              (user_id, login, quest_id, current_count, completed, completed_at) 
                              VALUES ({$user_id}, '{$login_esc}', {$quest_id}, {$login_count}, {$new_completed}, " . ($new_completed ? "NOW()" : "NULL") . ")");
    }
    
    // Если задание только что выполнилось, начисляем опыт
    if ($new_completed && !$completed) {
        AddBattlePassExp($user_id, $season_id, $quest['reward_exp']);
        
        echo json_encode([
            'errorcode' => 0,
            'completed' => true,
            'reward_exp' => $quest['reward_exp'],
            'message' => 'Задание выполнено!',
            'current_count' => $login_count,
            'target_count' => $target_count
        ]);
    } else {
        echo json_encode([
            'errorcode' => 0,
            'completed' => false,
            'message' => 'Прогресс обновлен',
            'current_count' => $login_count,
            'target_count' => $target_count
        ]);
    }
}


/**
 * Получение списка персонажей аккаунта
 */
function getAccountCharacters($user_id) {
    global $db, $servid;
    
    $characters = array();
    
    // Используем существующую логику из get_status_data
    $f = GetUserRoles($user_id, 1);
    
    if ($f['retcode'] == 0 && !empty($f['roles'])) {
        foreach ($f['data'] as $i => $v) {
            $roleId = (int)$v['id'];
            $roleName = $v['name'];
            
            $characters[] = array(
                'roleid' => $roleId,
                'name' => $roleName
            );
        }
    }
    
    return $characters;
}


/**
 * Подсчет убийств за сегодня для всех персонажей аккаунта
 */
function countTodayKillsForAccount($user_id) {
    global $db;
    
    // 1. Получаем всех персонажей аккаунта через проверенную функцию
    $characters = getAccountCharacters($user_id);
    
    if (empty($characters)) {
        error_log("countTodayKillsForAccount: No characters for user {$user_id}");
        return 0;
    }
    
    // 2. Собираем все roleid
    $role_ids = array();
    foreach ($characters as $char) {
        $role_ids[] = $char['roleid'];
    }
    $role_ids_str = implode(',', $role_ids);
    
    error_log("countTodayKillsForAccount: User {$user_id} roles: {$role_ids_str}");
    
    // 3. Получаем начало и конец сегодняшнего дня
    $today_start = date('Y-m-d 00:00:00');
    $today_end = date('Y-m-d 23:59:59');
    
    // 4. Подсчет убийств
    $query = "
        SELECT COUNT(*) as kill_count
        FROM pvp_kills
        WHERE timestamp >= '{$today_start}'
        AND timestamp <= '{$today_end}'
        AND attacker_id IN ({$role_ids_str})
    ";
    
    $result = $db->query($query);
    if ($result && $result->num_rows > 0) {
        $row = $result->fetch_assoc();
        return (int)$row['kill_count'];
    }
    
    return 0;
}

function diagnoseKills($user_id) {
    global $db;
    
    $output = [];
    $output[] = "=== ДИАГНОСТИКА УБИЙСТВ ===";
    $output[] = "User ID: {$user_id}";
    $output[] = "Текущее время сервера: " . date('Y-m-d H:i:s');
    
    // 1. Персонажи
    $chars = getAccountCharacters($user_id);
    $output[] = "\nПерсонажи (" . count($chars) . "):";
    foreach ($chars as $char) {
        $output[] = "  • roleid: {$char['roleid']}, name: {$char['name']}";
    }
    
    if (empty($chars)) {
        $output[] = "❌ Нет персонажей!";
        return implode("\n", $output);
    }
    
    $role_ids = array_column($chars, 'roleid');
    $role_ids_str = implode(',', $role_ids);
    
    // 2. Проверяем таблицу pvp_kills
    $output[] = "\n=== СТРУКТУРА ТАБЛИЦЫ PVp_KILLS ===";
    $cols = $db->query("SHOW COLUMNS FROM pvp_kills");
    while ($col = $cols->fetch_assoc()) {
        $output[] = "  • {$col['Field']} - {$col['Type']}";
    }
    
    // 3. Общее количество записей
    $total = $db->query("SELECT COUNT(*) as cnt FROM pvp_kills");
    $total_cnt = $total->fetch_assoc()['cnt'];
    $output[] = "\nВсего записей в pvp_kills: {$total_cnt}";
    
    // 4. Диапазон дат
    $date_range = $db->query("SELECT MIN(timestamp) as min_date, MAX(timestamp) as max_date FROM pvp_kills");
    $range = $date_range->fetch_assoc();
    if ($range['min_date']) {
        $output[] = "Минимальная дата: {$range['min_date']}";
        $output[] = "Максимальная дата: {$range['max_date']}";
    }
    
    // 5. Сегодняшний диапазон
    $today_start = date('Y-m-d 00:00:00');
    $today_end = date('Y-m-d 23:59:59');
    $output[] = "\nСегодняшний диапазон: {$today_start} - {$today_end}";
    
    // 6. Записи за сегодня (ВСЕ)
    $today_all = $db->query("
        SELECT COUNT(*) as cnt 
        FROM pvp_kills 
        WHERE timestamp >= '{$today_start}' 
        AND timestamp <= '{$today_end}'
    ");
    $today_all_cnt = $today_all->fetch_assoc()['cnt'];
    $output[] = "\nУбийств сегодня всего: {$today_all_cnt}";
    
    if ($today_all_cnt > 0) {
        // Показываем последние 5 убийств
        $examples = $db->query("
            SELECT k.*, 
                   a.name as attacker_name,
                   v.name as victim_name
            FROM pvp_kills k
            LEFT JOIN players a ON k.attacker_id = a.id
            LEFT JOIN players v ON k.victim_id = v.id
            WHERE k.timestamp >= '{$today_start}' 
            AND k.timestamp <= '{$today_end}'
            ORDER BY k.timestamp DESC
            LIMIT 5
        ");
        
        $output[] = "Последние убийства за сегодня:";
        while ($ex = $examples->fetch_assoc()) {
            $attacker = $ex['attacker_name'] ?: 'ID:'.$ex['attacker_id'];
            $victim = $ex['victim_name'] ?: 'ID:'.$ex['victim_id'];
            $output[] = "  • {$ex['timestamp']} | {$attacker} -> {$victim}";
        }
    }
    
    // 7. Убийства ВАШИМИ персонажами сегодня
    $my_kills = $db->query("
        SELECT COUNT(*) as cnt
        FROM pvp_kills
        WHERE timestamp >= '{$today_start}'
        AND timestamp <= '{$today_end}'
        AND attacker_id IN ({$role_ids_str})
    ");
    $my_kills_cnt = $my_kills->fetch_assoc()['cnt'];
    $output[] = "\nУбийств ВАШИМИ персонажами сегодня: {$my_kills_cnt}";
    
    if ($my_kills_cnt > 0) {
        $my_details = $db->query("
            SELECT k.*, a.name as attacker_name, v.name as victim_name
            FROM pvp_kills k
            LEFT JOIN players a ON k.attacker_id = a.id
            LEFT JOIN players v ON k.victim_id = v.id
            WHERE k.timestamp >= '{$today_start}'
            AND k.timestamp <= '{$today_end}'
            AND k.attacker_id IN ({$role_ids_str})
            ORDER BY k.timestamp DESC
        ");
        
        while ($kill = $my_details->fetch_assoc()) {
            $output[] = "  ✓ {$kill['timestamp']} - {$kill['attacker_name']} убил {$kill['victim_name']}";
        }
    } else {
        // Проверяем есть ли вообще убийства этими персонажами
        $any_kills = $db->query("
            SELECT COUNT(*) as cnt
            FROM pvp_kills
            WHERE attacker_id IN ({$role_ids_str})
        ");
        $any_kills_cnt = $any_kills->fetch_assoc()['cnt'];
        $output[] = "Убийств вашими персонажами ЗА ВСЕ ВРЕМЯ: {$any_kills_cnt}";
        
        if ($any_kills_cnt > 0) {
            $last = $db->query("
                SELECT MAX(timestamp) as last_date
                FROM pvp_kills
                WHERE attacker_id IN ({$role_ids_str})
            ");
            $last_date = $last->fetch_assoc()['last_date'];
            $output[] = "Последнее убийство было: {$last_date}";
            
            // Проверяем, было ли сегодня
            $last_day = substr($last_date, 0, 10);
            $today = date('Y-m-d');
            
            if ($last_day == $today) {
                $output[] = "✅ ЕСТЬ убийства сегодня, но запрос их не находит!";
                $output[] = "   Возможно проблема в поле timestamp (проверьте формат)";
            } else {
                $output[] = "❌ Последнее убийство было {$last_day}, не сегодня";
            }
        }
    }
    
    // 8. Проверка конкретных записей из вашей БД
    $output[] = "\n=== ПРОВЕРКА КОНКРЕТНЫХ ЗАПИСЕЙ ===";
    $specific = $db->query("
        SELECT id, timestamp, attacker_id, victim_id 
        FROM pvp_kills 
        WHERE id IN (66, 67)
        ORDER BY id DESC
    ");
    
    while ($row = $specific->fetch_assoc()) {
        $output[] = "Запись ID={$row['id']}: timestamp={$row['timestamp']}, attacker_id={$row['attacker_id']}";
        
        // Проверяем, принадлежит ли атакующий нашему пользователю
        if (in_array($row['attacker_id'], $role_ids)) {
            $output[] = "  ✅ Этот attacker_id ({$row['attacker_id']}) принадлежит вам!";
        } else {
            $output[] = "  ❌ Этот attacker_id ({$row['attacker_id']}) НЕ принадлежит вам";
        }
    }
    
    return implode("\n", $output);
}
/**
 * Обновление прогресса задания на убийства
 */
function updateKillQuestProgress($user_id, $quest_id, $login, $season_id) {
    global $db;
    
    // Получаем задание
    $q = $db->query("SELECT * FROM battlepass_quests WHERE id = {$quest_id} LIMIT 1");
    if (!$q || $q->num_rows == 0) {
        return false;
    }
    $quest = $q->fetch_assoc();
    
    // Подсчитываем убийства
    $kills_today = countTodayKillsForAccount($user_id);
    
    // Получаем текущий прогресс
    $q2 = $db->query("SELECT * FROM battlepass_quest_completions 
                      WHERE user_id = {$user_id} AND quest_id = {$quest_id} LIMIT 1");
    
    $target_count = (int)$quest['target_count'];
    $completed = ($kills_today >= $target_count) ? 1 : 0;
    
    if ($q2 && $q2->num_rows > 0) {
        $progress = $q2->fetch_assoc();
        $old_completed = $progress['completed'];
        
        // Обновляем запись
        $update_query = "UPDATE battlepass_quest_completions 
                         SET current_count = {$kills_today}, 
                             completed = {$completed},
                             completed_at = " . ($completed ? "NOW()" : "NULL") . "
                         WHERE id = {$progress['id']}";
        $db->query($update_query);
        
        // Если задание только что выполнилось, начисляем опыт
        if ($completed && !$old_completed) {
            AddBattlePassExp($user_id, $season_id, (int)$quest['reward_exp'], $login);
        }
    } else {
        // Создаем новую запись
        $login_esc = $db->real_escape_string($login);
        $insert_query = "INSERT INTO battlepass_quest_completions 
                         (user_id, login, quest_id, current_count, completed, completed_at) 
                         VALUES ({$user_id}, '{$login_esc}', {$quest_id}, {$kills_today}, {$completed}, " . ($completed ? "NOW()" : "NULL") . ")";
        $db->query($insert_query);
        
        if ($completed) {
            AddBattlePassExp($user_id, $season_id, (int)$quest['reward_exp'], $login);
        }
    }
    
    return $kills_today;
}

function BpRefreshQuestProgress() {
    global $db;
    
    while (ob_get_level()) {
        ob_end_clean();
    }
    
    header('Content-Type: application/json; charset=utf-8');
    header('X-Content-Type-Options: nosniff');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $login = isset($_POST['login']) ? trim($_POST['login']) : '';
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    
    if (!$user_id || !$season_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_data']);
        exit;
    }
    
    // Получаем сезон
    $q = $db->query("SELECT * FROM battlepass_seasons WHERE id = {$season_id} LIMIT 1");
    if (!$q || $q->num_rows == 0) {
        echo json_encode(['errorcode' => 1, 'message' => 'season_not_found']);
        exit;
    }
    $season = $q->fetch_assoc();
    
    // Получаем персонажей
    $characters = getAccountCharacters($user_id);
    $role_ids = array();
    foreach ($characters as $char) {
        $role_ids[] = $char['roleid'];
    }
    $role_ids_str = !empty($role_ids) ? implode(',', $role_ids) : '0';
    
    $updated_quests = [];
    
    // Получаем все активные задания
    $q = $db->query("SELECT * FROM battlepass_quests 
                     WHERE season_id = {$season_id} 
                     AND is_active = 1");
    
    if ($q && $q->num_rows > 0) {
        while ($quest = $q->fetch_assoc()) {
            $quest_id = (int)$quest['id'];
            $target_count = (int)$quest['target_count'];
            $current_count = 0;
            
            // ===== УБИЙСТВА ИГРОКОВ =====
            if ($quest['target_type'] == 'kill_player') {
                $current_count = countTodayKillsForAccount($user_id);
            }
            
            // ===== ВЫПОЛНЕНИЕ КВЕСТОВ =====
            elseif ($quest['target_type'] == 'complete_quest') {
                // Получаем целевой ID квеста
                $target_quest_id = 0;
                
                if (!empty($quest['target_params'])) {
                    $params = json_decode($quest['target_params'], true);
                    if (is_array($params) && isset($params['game_quest_id'])) {
                        $target_quest_id = (int)$params['game_quest_id'];
                    }
                }
                
                if ($target_quest_id == 0 && isset($quest['target_id']) && $quest['target_id'] > 0) {
                    $target_quest_id = (int)$quest['target_id'];
                }
                
                // Определяем период
                $date_condition = "";
                
                if ($quest['quest_type'] == 'daily') {
                    $today_start = date('Y-m-d 00:00:00');
                    $today_end = date('Y-m-d 23:59:59');
                    $date_condition = "AND date >= '{$today_start}' AND date <= '{$today_end}'";
                } elseif ($quest['quest_type'] == 'weekly') {
                    $week_start = date('Y-m-d 00:00:00', strtotime('monday this week'));
                    $week_end = date('Y-m-d 23:59:59', strtotime('sunday this week'));
                    $date_condition = "AND date >= '{$week_start}' AND date <= '{$week_end}'";
                } elseif ($quest['quest_type'] == 'seasonal') {
                    $date_condition = "AND date >= '{$season['date_start']}' AND date <= '{$season['date_end']}'";
                }
                
                $count_query = "
                    SELECT COUNT(*) as cnt
                    FROM task_log
                    WHERE id_player IN ({$role_ids_str})
                    {$date_condition}
                ";
                
                if ($target_quest_id > 0) {
                    $count_query .= " AND id_quest = {$target_quest_id}";
                }
                
                $count_result = $db->query($count_query);
                if ($count_result) {
                    $row_cnt = $count_result->fetch_assoc();
                    $current_count = (int)$row_cnt['cnt'];
                }
            }
            
            // ===== ГОЛОСОВАНИЯ =====
            elseif ($quest['target_type'] == 'vote') {
                // Определяем период
                $date_condition = "";
                
                if ($quest['quest_type'] == 'daily') {
                    $today_start = date('Y-m-d 00:00:00');
                    $today_end = date('Y-m-d 23:59:59');
                    $date_condition = "AND data >= '{$today_start}' AND data <= '{$today_end}'";
                } elseif ($quest['quest_type'] == 'weekly') {
                    $week_start = date('Y-m-d 00:00:00', strtotime('monday this week'));
                    $week_end = date('Y-m-d 23:59:59', strtotime('sunday this week'));
                    $date_condition = "AND data >= '{$week_start}' AND data <= '{$week_end}'";
                } elseif ($quest['quest_type'] == 'seasonal') {
                    $date_condition = "AND data >= '{$season['date_start']}' AND data <= '{$season['date_end']}'";
                }
                
                // Учитываем статус и vote_id
                $vote_query = "
                    SELECT COUNT(*) as cnt
                    FROM mmotop_data
                    WHERE userid = {$user_id}
                    AND status IN (1, 7)
                    {$date_condition}
                ";
                
                if (!empty($quest['target_id']) && $quest['target_id'] > 0) {
                    $vote_query .= " AND vote_id = " . (int)$quest['target_id'];
                }
                
                $vote_result = $db->query($vote_query);
                if ($vote_result && $vote_result->num_rows > 0) {
                    $vote_row = $vote_result->fetch_assoc();
                    $current_count = (int)$vote_row['cnt'];
                }
            }
            
            // ===== ВХОД В ИГРУ =====
            elseif ($quest['target_type'] == 'login') {
                $today_start = date('Y-m-d 00:00:00');
                $log_q = $db->query("SELECT COUNT(*) as cnt FROM login_log 
                                     WHERE userid = {$user_id} 
                                     AND action = 3
                                     AND data >= '{$today_start}'");
                
                if ($log_q && $log_q->num_rows > 0) {
                    $log_row = $log_q->fetch_assoc();
                    $current_count = (int)$log_row['cnt'];
                }
            }
            
            $completed = ($current_count >= $target_count) ? 1 : 0;
            
            // Получаем текущий прогресс
            $q2 = $db->query("SELECT * FROM battlepass_quest_completions 
                              WHERE user_id = {$user_id} AND quest_id = {$quest_id} LIMIT 1");
            
            if ($q2 && $q2->num_rows > 0) {
                $progress = $q2->fetch_assoc();
                $old_completed = $progress['completed'];
                
                // Обновляем запись
                $update_query = "UPDATE battlepass_quest_completions 
                                 SET current_count = {$current_count}, 
                                     completed = {$completed},
                                     completed_at = " . ($completed ? "NOW()" : "NULL") . "
                                 WHERE id = {$progress['id']}";
                $db->query($update_query);
                
                if ($completed && !$old_completed) {
                    AddBattlePassExp($user_id, $season_id, (int)$quest['reward_exp'], $login);
                }
            } else {
                // Создаем новую запись
                $login_esc = $db->real_escape_string($login);
                $insert_query = "INSERT INTO battlepass_quest_completions 
                                 (user_id, login, quest_id, current_count, completed, completed_at) 
                                 VALUES ({$user_id}, '{$login_esc}', {$quest_id}, {$current_count}, {$completed}, " . ($completed ? "NOW()" : "NULL") . ")";
                $db->query($insert_query);
                
                if ($completed) {
                    AddBattlePassExp($user_id, $season_id, (int)$quest['reward_exp'], $login);
                }
            }
            
            $updated_quests[] = [
                'quest_id' => $quest_id,
                'current_count' => $current_count,
                'target_count' => $target_count,
                'completed' => $completed
            ];
        }
    }
    
    echo json_encode([
        'errorcode' => 0,
        'updated_quests' => $updated_quests
    ]);
    exit;
}

/**
 * Подсчет выполненных квестов за сегодня для всех персонажей аккаунта
 */
function countCompletedQuestsForAccount($user_id, $quest, $season_id) {
    global $db;
    
    // 1. Получаем всех персонажей аккаунта
    $characters = getAccountCharacters($user_id);
    
    if (empty($characters)) {
        error_log("countCompletedQuestsForAccount: No characters for user {$user_id}");
        return 0;
    }
    
    // 2. Собираем все roleid
    $role_ids = array();
    foreach ($characters as $char) {
        $role_ids[] = $char['roleid'];
    }
    $role_ids_str = implode(',', $role_ids);
    
    error_log("countCompletedQuestsForAccount: User {$user_id} roles: {$role_ids_str}");
    
    // 3. Определяем период в зависимости от типа задания
    $date_condition = "";
    
    if ($quest['quest_type'] == 'daily') {
        // За сегодня
        $today_start = date('Y-m-d 00:00:00');
        $today_end = date('Y-m-d 23:59:59');
        $date_condition = "AND tl.date >= '{$today_start}' AND tl.date <= '{$today_end}'";
        
    } elseif ($quest['quest_type'] == 'weekly') {
        // За текущую неделю
        $week_number = (int)$quest['week_number'];
        if ($week_number > 0) {
            // Если указан конкретный номер недели сезона
            $season_dates = getSeasonDates($season_id);
            if ($season_dates) {
                $week_start = date('Y-m-d H:i:s', strtotime($season_dates['start'] . " + " . (($week_number - 1) * 7) . " days"));
                $week_end = date('Y-m-d H:i:s', strtotime($week_start . " + 7 days - 1 second"));
                $date_condition = "AND tl.date >= '{$week_start}' AND tl.date <= '{$week_end}'";
            }
        } else {
            // Текущая неделя
            $week_start = date('Y-m-d 00:00:00', strtotime('monday this week'));
            $week_end = date('Y-m-d 23:59:59', strtotime('sunday this week'));
            $date_condition = "AND tl.date >= '{$week_start}' AND tl.date <= '{$week_end}'";
        }
        
    } elseif ($quest['quest_type'] == 'seasonal') {
        // За весь сезон
        $season_dates = getSeasonDates($season_id);
        if ($season_dates) {
            $date_condition = "AND tl.date >= '{$season_dates['start']}' AND tl.date <= '{$season_dates['end']}'";
        }
    }
    
   // 4. Условие для ID квеста
$quest_condition = "";

// Получаем целевой ID квеста
$target_quest_id = 0;

// Сначала проверяем target_params (новый формат)
if (!empty($quest['target_params'])) {
    $params = json_decode($quest['target_params'], true);
    if (is_array($params) && isset($params['game_quest_id'])) {
        $target_quest_id = (int)$params['game_quest_id'];
    }
}

// Если не нашли в target_params, используем target_id (старый формат)
if ($target_quest_id == 0 && isset($quest['target_id']) && $quest['target_id'] > 0) {
    $target_quest_id = (int)$quest['target_id'];
}

// ВАЖНО: Если target_quest_id == 0 - считаем ВСЕ квесты (без условия)
// Если target_quest_id > 0 - считаем только конкретный квест
if ($target_quest_id > 0) {
    $quest_condition = "AND tl.id_quest = " . $target_quest_id;
    error_log("countCompletedQuestsForAccount: Counting ONLY quest ID {$target_quest_id}");
} else {
    error_log("countCompletedQuestsForAccount: Counting ALL quests (target_id = 0)");
    // НЕ добавляем условие на id_quest - считаем все квесты
}
    
    // 5. Подсчет выполненных квестов
    $query = "
        SELECT COUNT(*) as quest_count
        FROM task_log tl
        WHERE tl.id_player IN ({$role_ids_str})
        {$date_condition}
        {$quest_condition}
    ";
    
    error_log("countCompletedQuestsForAccount query: " . $query);
    
    $result = $db->query($query);
    if ($result && $result->num_rows > 0) {
        $row = $result->fetch_assoc();
        return (int)$row['quest_count'];
    }
    
    return 0;
}

/**
 * Получение дат начала и окончания сезона
 */
function getSeasonDates($season_id) {
    global $db;
    
    $q = $db->query("SELECT date_start, date_end FROM battlepass_seasons WHERE id = {$season_id} LIMIT 1");
    if ($q && $q->num_rows > 0) {
        return $q->fetch_assoc();
    }
    
    return null;
}

/**
 * Диагностика выполнения квестов для отладки
 */
function diagnoseQuestCompletion($user_id, $quest_id) {
    global $db;
    
    $output = array();
    $output[] = "=== ДИАГНОСТИКА ВЫПОЛНЕНИЯ КВЕСТОВ ===";
    $output[] = "User ID: " . $user_id;
    $output[] = "Quest ID: " . $quest_id;
    $output[] = "Время: " . date('Y-m-d H:i:s');
    
    // Проверяем соединение с БД
    if (!$db) {
        $output[] = "❌ Нет соединения с БД!";
        return implode("\n", $output);
    }
    
    // Получаем информацию о задании
    $quest_id_esc = (int)$quest_id;
    $q = $db->query("SELECT * FROM battlepass_quests WHERE id = " . $quest_id_esc . " LIMIT 1");
    if (!$q) {
        $output[] = "❌ Ошибка запроса к battlepass_quests: " . $db->error;
        return implode("\n", $output);
    }
    
    if ($q->num_rows == 0) {
        $output[] = "❌ Задание не найдено!";
        return implode("\n", $output);
    }
    
    $quest = $q->fetch_assoc();
    $output[] = "Quest type: " . (isset($quest['quest_type']) ? $quest['quest_type'] : 'не указан');
    $output[] = "Target type: " . (isset($quest['target_type']) ? $quest['target_type'] : 'не указан');
    $output[] = "Target count: " . (isset($quest['target_count']) ? $quest['target_count'] : 0);
    
    $target_type = isset($quest['target_type']) ? $quest['target_type'] : '';
    
    if ($target_type == 'complete_quest') {
        // Получаем персонажей через вашу функцию
        $characters = getAccountCharacters($user_id);
        $output[] = "\nПерсонажи (" . count($characters) . "):";
        
        if (empty($characters)) {
            $output[] = "❌ Нет персонажей!";
        } else {
            foreach ($characters as $char) {
                $roleid = isset($char['roleid']) ? $char['roleid'] : '?';
                $name = isset($char['name']) ? $char['name'] : 'без имени';
                $output[] = "  • roleid: " . $roleid . ", name: " . $name;
            }
            
            $role_ids = array();
            foreach ($characters as $char) {
                if (isset($char['roleid']) && $char['roleid'] > 0) {
                    $role_ids[] = (int)$char['roleid'];
                }
            }
            
            if (empty($role_ids)) {
                $output[] = "❌ Не удалось получить ID персонажей!";
                return implode("\n", $output);
            }
            
            $role_ids_str = implode(',', $role_ids);
            
            // Проверяем существование таблицы task_log
            $table_check = $db->query("SHOW TABLES LIKE 'task_log'");
            if ($table_check->num_rows == 0) {
                $output[] = "\n❌ Таблица task_log не существует!";
                return implode("\n", $output);
            }
            
            // Проверяем task_log
            $output[] = "\n=== ТАБЛИЦА TASK_LOG ===";
            
            // Общее количество записей
            $total = $db->query("SELECT COUNT(*) as cnt FROM task_log");
            if ($total) {
                $row = $total->fetch_assoc();
                $total_cnt = isset($row['cnt']) ? $row['cnt'] : 0;
                $output[] = "Всего записей в task_log: " . $total_cnt;
            } else {
                $output[] = "❌ Ошибка при подсчете записей: " . $db->error;
            }
            
            // Записи для персонажей пользователя
            $user_logs = $db->query("
                SELECT tl.*, p.name as player_name
                FROM task_log tl
                LEFT JOIN players p ON tl.id_player = p.id
                WHERE tl.id_player IN (" . $role_ids_str . ")
                ORDER BY tl.date DESC
                LIMIT 20
            ");
            
            $output[] = "\nПоследние записи для ваших персонажей:";
            if ($user_logs && $user_logs->num_rows > 0) {
                while ($log = $user_logs->fetch_assoc()) {
                    $date = isset($log['date']) ? $log['date'] : 'неизвестно';
                    $player_name = isset($log['player_name']) ? $log['player_name'] : 'неизвестно';
                    $id_player = isset($log['id_player']) ? $log['id_player'] : '?';
                    $id_quest = isset($log['id_quest']) ? $log['id_quest'] : '?';
                    $output[] = "  • " . $date . " | " . $player_name . " (ID: " . $id_player . ") | Квест ID: " . $id_quest;
                }
            } else {
                $output[] = "  ❌ Нет записей в task_log для ваших персонажей!";
                if ($db->error) {
                    $output[] = "  Ошибка запроса: " . $db->error;
                }
            }
            
            // Параметры квеста
            $target_quest_id = 0;
if (!empty($quest['target_params'])) {
    $params = json_decode($quest['target_params'], true);
    if (is_array($params) && isset($params['game_quest_id'])) {
        $target_quest_id = (int)$params['game_quest_id'];
    }
    $output[] = "\nПараметры target_params: " . print_r($params, true);
}

if ($target_quest_id == 0 && isset($quest['target_id']) && $quest['target_id'] > 0) {
    $target_quest_id = (int)$quest['target_id'];
}

$output[] = "\nЦелевой ID квеста: " . ($target_quest_id ? $target_quest_id : '0 (ВСЕ КВЕСТЫ)');

// Подсчет за сегодня
$today_start = date('Y-m-d 00:00:00');
$today_end = date('Y-m-d 23:59:59');

$count_query = "
    SELECT COUNT(*) as cnt
    FROM task_log
    WHERE id_player IN (" . $role_ids_str . ")
    AND date >= '" . $today_start . "'
    AND date <= '" . $today_end . "'
";

if ($target_quest_id > 0) {
    $count_query .= " AND id_quest = " . $target_quest_id;
    $output[] = "\n(Подсчет только для квеста ID: " . $target_quest_id . ")";
} else {
    $output[] = "\n(Подсчет для ВСЕХ квестов)";
}
            
            $count_today = $db->query($count_query);
            
            if ($count_today) {
                $row = $count_today->fetch_assoc();
                $count = isset($row['cnt']) ? $row['cnt'] : 0;
                $output[] = "\nВыполнено сегодня: " . $count . " / " . (isset($quest['target_count']) ? $quest['target_count'] : 0);
                
                // Проверяем, есть ли запись в battlepass_quest_completions
                $completion_check = $db->query("
                    SELECT * FROM battlepass_quest_completions 
                    WHERE user_id = " . $user_id . " AND quest_id = " . $quest_id . "
                    LIMIT 1
                ");
                
                if ($completion_check && $completion_check->num_rows > 0) {
                    $completion = $completion_check->fetch_assoc();
                    $current = isset($completion['current_count']) ? $completion['current_count'] : 0;
                    $completed = isset($completion['completed']) ? ($completion['completed'] ? 'да' : 'нет') : 'нет';
                    $output[] = "\nПрогресс в БД: " . $current . " / " . (isset($quest['target_count']) ? $quest['target_count'] : 0) . ", выполнено: " . $completed;
                } else {
                    $output[] = "\nНет записи о прогрессе в battlepass_quest_completions";
                }
            } else {
                $output[] = "\n❌ Ошибка при подсчете за сегодня: " . $db->error;
            }
        }
    }
    
    return implode("\n", $output);
}

/**
 * Обработка события убийства (вызывается при PvP)
 */
function BpProcessKillEvent() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $attacker_id = isset($_POST['attacker_id']) ? (int)$_POST['attacker_id'] : 0;
    $victim_id = isset($_POST['victim_id']) ? (int)$_POST['victim_id'] : 0;
    
    if (!$attacker_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'No attacker specified']);
        return;
    }
    
    // Получаем user_id атакующего по его персонажу
    $q = $db->query("SELECT userid FROM players WHERE id = {$attacker_id} LIMIT 1");
    if (!$q || $q->num_rows == 0) {
        echo json_encode(['errorcode' => 1, 'message' => 'Attacker not found']);
        return;
    }
    $user_data = $q->fetch_assoc();
    $user_id = (int)$user_data['userid'];
    
    // Получаем логин пользователя
    $q = $db->query("SELECT login FROM users WHERE id = {$user_id} LIMIT 1");
    $login = '';
    if ($q && $q->num_rows > 0) {
        $login = $q->fetch_assoc()['login'];
    }
    
    // Получаем активный сезон
    $q = $db->query("SELECT id FROM battlepass_seasons WHERE is_active = 1 ORDER BY id DESC LIMIT 1");
    if (!$q || $q->num_rows == 0) {
        echo json_encode(['errorcode' => 0, 'message' => 'No active season']);
        return;
    }
    $season_id = $q->fetch_assoc()['id'];
    
    // Получаем все активные задания на убийства
    $q = $db->query("SELECT * FROM battlepass_quests 
                     WHERE season_id = {$season_id} 
                     AND is_active = 1 
                     AND target_type = 'kill_player'");
    
    if (!$q || $q->num_rows == 0) {
        echo json_encode(['errorcode' => 0, 'message' => 'No kill quests']);
        return;
    }
    
    $results = [];
    while ($quest = $q->fetch_assoc()) {
        $quest_id = (int)$quest['id'];
        $kills_today = updateKillQuestProgress($user_id, $quest_id, $login, $season_id);
        
        $results[] = [
            'quest_id' => $quest_id,
            'kills_today' => $kills_today,
            'target' => (int)$quest['target_count']
        ];
    }
    
    echo json_encode([
        'errorcode' => 0,
        'results' => $results
    ]);
}


/**
 * Обработка события для заданий
 */
function BpProcessQuestEvent() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $event_type = isset($_POST['event_type']) ? $_POST['event_type'] : '';
    $event_data = isset($_POST['event_data']) ? json_decode($_POST['event_data'], true) : [];
    
    if (!$user_id || !$event_type) {
        echo json_encode(['errorcode' => 1, 'message' => 'Не указаны параметры']);
        return;
    }
    
    // Получаем активный сезон
    $q = $db->query("SELECT id FROM battlepass_seasons WHERE is_active = 1 LIMIT 1");
    if (!$q || $q->num_rows == 0) {
        echo json_encode(['errorcode' => 0, 'message' => 'Нет активного сезона']);
        return;
    }
    $season_id = $q->fetch_assoc()['id'];
    
    // Получаем все активные задания этого типа
    $q = $db->query("SELECT * FROM battlepass_quests 
                     WHERE season_id = {$season_id} 
                     AND is_active = 1 
                     AND target_type = '{$event_type}'");
    
    if (!$q || $q->num_rows == 0) {
        echo json_encode(['errorcode' => 0, 'message' => 'Нет заданий этого типа']);
        return;
    }
    
    $results = [];
    
    while ($quest = $q->fetch_assoc()) {
        // Проверяем, не выполнено ли уже задание
        $check = $db->query("SELECT * FROM battlepass_quest_completions 
                            WHERE user_id = {$user_id} AND quest_id = {$quest['id']} 
                            LIMIT 1");
        
        if ($check && $check->num_rows > 0) {
            $progress = $check->fetch_assoc();
            
            if ($progress['completed']) {
                // Для ежедневных заданий проверяем дату
                if ($quest['quest_type'] == 'daily') {
                    if (date('Y-m-d', strtotime($progress['completed_at'])) != date('Y-m-d')) {
                        // Сбрасываем задание
                        $db->query("DELETE FROM battlepass_quest_completions 
                                   WHERE id = {$progress['id']}");
                    } else {
                        $results[] = [
                            'quest_id' => $quest['id'],
                            'completed' => true,
                            'message' => 'Уже выполнено'
                        ];
                        continue;
                    }
                } else {
                    $results[] = [
                        'quest_id' => $quest['id'],
                        'completed' => true,
                        'message' => 'Уже выполнено'
                    ];
                    continue;
                }
            }
            
            // Увеличиваем счетчик
            $new_count = $progress['current_count'] + 1;
            $completed = ($new_count >= $quest['target_count']) ? 1 : 0;
            
            $update = "UPDATE battlepass_quest_completions 
                       SET current_count = {$new_count}, 
                           completed = {$completed}";
            
            if ($completed) {
                $update .= ", completed_at = NOW()";
            }
            
            $update .= " WHERE id = {$progress['id']}";
            $db->query($update);
            
            if ($completed) {
                AddBattlePassExp($user_id, $season_id, $quest['reward_exp']);
            }
            
            $results[] = [
                'quest_id' => $quest['id'],
                'completed' => $completed,
                'current_count' => $new_count,
                'target_count' => $quest['target_count'],
                'reward_exp' => $completed ? $quest['reward_exp'] : 0
            ];
            
        } else {
            // Создаем новую запись
            $db->query("INSERT INTO battlepass_quest_completions 
                       (user_id, quest_id, current_count, completed) 
                       VALUES ({$user_id}, {$quest['id']}, 1, 0)");
            
            $results[] = [
                'quest_id' => $quest['id'],
                'completed' => false,
                'current_count' => 1,
                'target_count' => $quest['target_count'],
                'reward_exp' => 0
            ];
        }
    }
    
    echo json_encode([
        'errorcode' => 0,
        'results' => $results
    ]);
}

/**
 * Получение заданий пользователя
 */
function BpGetUserQuests() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    
    if (!$user_id || !$season_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'Не указаны параметры']);
        return;
    }
    
    $q = $db->query("SELECT q.*, 
                            COALESCE(c.current_count, 0) as current_count,
                            COALESCE(c.completed, 0) as completed,
                            c.completed_at
                     FROM battlepass_quests q
                     LEFT JOIN battlepass_quest_completions c 
                         ON q.id = c.quest_id AND c.user_id = {$user_id}
                     WHERE q.season_id = {$season_id} 
                         AND q.is_active = 1
                     ORDER BY 
                         CASE q.quest_type 
                             WHEN 'daily' THEN 1 
                             WHEN 'weekly' THEN 2 
                             ELSE 3 
                         END, 
                         q.week_number, 
                         q.id");
    
    $quests = [
        'daily' => [],
        'weekly' => [],
        'seasonal' => []
    ];
    
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            // Для ежедневных заданий проверяем актуальность
            if ($row['quest_type'] == 'daily' && $row['completed']) {
                if (date('Y-m-d', strtotime($row['completed_at'])) != date('Y-m-d')) {
                    $row['current_count'] = 0;
                    $row['completed'] = 0;
                }
            }
            
            $row['progress_percent'] = ($row['current_count'] / $row['target_count']) * 100;
            $quests[$row['quest_type']][] = $row;
        }
    }
    
    echo json_encode([
        'errorcode' => 0,
        'quests' => $quests
    ]);
}

/**
 * Сброс ежедневных заданий (вызывается по крону)
 */
function BpResetDailyQuests() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Удаляем записи о выполнении заданий за предыдущие дни
    $db->query("DELETE FROM battlepass_quest_completions 
                WHERE quest_id IN (SELECT id FROM battlepass_quests WHERE quest_type = 'daily')
                AND DATE(completed_at) < CURDATE()");
    
    echo json_encode([
        'errorcode' => 0,
        'message' => 'Ежедневные задания сброшены'
    ]);
}


function GetAllianceTop() {
    global $db;
    
    // ВАЖНО: устанавливаем кодировку соединения с БД
    $db->set_charset('utf8mb4');
    $db->query("SET NAMES 'utf8mb4'");
    $db->query("SET CHARACTER SET 'utf8mb4'");
    
    // ВАЖНО: указываем клиенту, что отдаём JSON в UTF-8
    header('Content-Type: application/json; charset=utf-8');
    
    // Получаем параметры
    $page = isset($_POST['page']) ? intval($_POST['page']) : 1;
    $per_page = isset($_POST['per_page']) ? intval($_POST['per_page']) : 20;
    $sort = isset($_POST['sort']) ? $_POST['sort'] : 'total_score';
    $order = isset($_POST['order']) && $_POST['order'] == 'asc' ? 'ASC' : 'DESC';
    
    // Допустимые поля для сортировки
    $allowed_sort = ['total_score', 'level', 'member_count', 'guild_count', 
                     'territory_count', 'contribution', 'avg_elo', 'fort_wins', 'imperial_wins'];
    
    if (!in_array($sort, $allowed_sort)) {
        $sort = 'total_score';
    }
    
    // Экранирование для безопасности
    $sort = mysqli_real_escape_string($db, $sort);
    $order = mysqli_real_escape_string($db, $order);
    
    $offset = ($page - 1) * $per_page;
    
    // Получаем общее количество альянсов
    $count_query = "SELECT COUNT(*) as total FROM alliance_rating";
    $count_result = $db->query($count_query);
    $total = 0;
    if ($count_result && $count_result->num_rows > 0) {
        $total = $count_result->fetch_assoc()['total'];
    }
    
    // Получаем данные с пагинацией и сортировкой
    $query = "SELECT * FROM alliance_rating 
              ORDER BY $sort $order 
              LIMIT $offset, $per_page";
    
    $result = $db->query($query);
    $alliances = array();
    
    if ($result && $result->num_rows > 0) {
        while ($row = $result->fetch_assoc()) {
            // Данные уже в UTF-8, ничего не конвертируем
            $alliances[] = $row;
        }
    }
    
    echo json_encode(array(
        'status' => 'success',
        'alliances' => $alliances,
        'total' => intval($total),
        'page' => $page,
        'per_page' => $per_page,
        'sort' => $sort,
        'order' => $order
    ), JSON_UNESCAPED_UNICODE); // JSON_UNESCAPED_UNICODE сохраняет русские буквы
}

/**
 * Получение логов отправленных наград
 */
function BpAdminGetRewardsLog() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $limit = isset($_POST['limit']) ? (int)$_POST['limit'] : 100;
    $offset = isset($_POST['offset']) ? (int)$_POST['offset'] : 0;
    
    $where = array();
    if ($season_id > 0) {
        $where[] = "season_id = {$season_id}";
    }
    if ($user_id > 0) {
        $where[] = "user_id = {$user_id}";
    }
    
    $where_clause = empty($where) ? "1" : implode(" AND ", $where);
    
    $q = $db->query("SELECT * FROM battlepass_rewards_log 
                     WHERE {$where_clause} 
                     ORDER BY request_time DESC 
                     LIMIT {$offset}, {$limit}");
    
    $logs = array();
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            $logs[] = $row;
        }
    }
    
    // Получаем общее количество
    $q = $db->query("SELECT COUNT(*) as cnt FROM battlepass_rewards_log WHERE {$where_clause}");
    $total = 0;
    if ($q) {
        $row = $q->fetch_assoc();
        $total = $row['cnt'];
    }
    
    echo json_encode([
        'errorcode' => 0,
        'logs' => $logs,
        'total' => $total,
        'debug' => [
            'where' => $where_clause,
            'count' => count($logs)
        ]
    ]);
}

/**
 * Получение логов покупок пропусков
 */
function BpAdminGetPurchasesLog() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $limit = isset($_POST['limit']) ? (int)$_POST['limit'] : 100;
    $offset = isset($_POST['offset']) ? (int)$_POST['offset'] : 0;
    
    // Проверяем существование таблицы
    $check = $db->query("SHOW TABLES LIKE 'battlepass_purchases_log'");
    if ($check->num_rows == 0) {
        echo json_encode([
            'errorcode' => 0,
            'logs' => [],
            'debug' => 'Table does not exist'
        ]);
        return;
    }
    
    $where = array();
    if ($season_id > 0) {
        $where[] = "season_id = {$season_id}";
    }
    if ($user_id > 0) {
        $where[] = "user_id = {$user_id}";
    }
    
    $where_clause = empty($where) ? "1" : implode(" AND ", $where);
    
    $query = "SELECT * FROM battlepass_purchases_log 
              WHERE {$where_clause} 
              ORDER BY request_time DESC 
              LIMIT {$offset}, {$limit}";
    
    $q = $db->query($query);
    
    $logs = array();
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            $logs[] = $row;
        }
    }
    
    // Получаем общее количество
    $count_query = "SELECT COUNT(*) as cnt FROM battlepass_purchases_log WHERE {$where_clause}";
    $count_result = $db->query($count_query);
    $total = 0;
    if ($count_result) {
        $row = $count_result->fetch_assoc();
        $total = $row['cnt'];
    }
    
    echo json_encode([
        'errorcode' => 0,
        'logs' => $logs,
        'total' => $total,
        'debug' => [
            'query' => $query,
            'count' => count($logs)
        ]
    ]);
}

/**
 * Получение логов покупок в магазине
 */
function BpAdminGetShopPurchasesLog() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $limit = isset($_POST['limit']) ? (int)$_POST['limit'] : 100;
    $offset = isset($_POST['offset']) ? (int)$_POST['offset'] : 0;
    
    $where = array();
    if ($season_id > 0) {
        $where[] = "season_id = {$season_id}";
    }
    if ($user_id > 0) {
        $where[] = "user_id = {$user_id}";
    }
    
    $where_clause = empty($where) ? "1" : implode(" AND ", $where);
    
    $q = $db->query("SELECT * FROM battlepass_shop_purchases_log 
                     WHERE {$where_clause} 
                     ORDER BY request_time DESC 
                     LIMIT {$offset}, {$limit}");
    
    $logs = array();
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            $logs[] = $row;
        }
    }
    
    echo json_encode([
        'errorcode' => 0,
        'logs' => $logs
    ]);
}

/**
 * Получение логов аудита
 */
function BpAdminGetAuditLog() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $action = isset($_POST['action']) ? $_POST['action'] : '';
    $limit = isset($_POST['limit']) ? (int)$_POST['limit'] : 100;
    $offset = isset($_POST['offset']) ? (int)$_POST['offset'] : 0;
    
    $where = array("1=1");
    if ($user_id > 0) {
        $where[] = "user_id = {$user_id}";
    }
    if (!empty($action)) {
        $action_esc = $db->real_escape_string($action);
        $where[] = "action = '{$action_esc}'";
    }
    
    $where_clause = implode(" AND ", $where);
    
    // Получаем общее количество
    $count_query = "SELECT COUNT(*) as cnt FROM battlepass_audit_log WHERE {$where_clause}";
    $count_result = $db->query($count_query);
    $total = 0;
    if ($count_result) {
        $row = $count_result->fetch_assoc();
        $total = $row['cnt'];
    }
    
    // Получаем данные
    $query = "SELECT * FROM battlepass_audit_log 
              WHERE {$where_clause} 
              ORDER BY request_time DESC 
              LIMIT {$offset}, {$limit}";
    
    $q = $db->query($query);
    $logs = array();
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            $logs[] = $row;
        }
    }
    
    echo json_encode([
        'errorcode' => 0,
        'logs' => $logs,
        'total' => $total
    ]);
}



/**
 * Тест соединения с БД
 */
function BpAdminTestConnection() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    // Проверяем подключение к БД
    $db_status = $db ? 'connected' : 'disconnected';
    
    // Получаем информацию о кодировке
    $charset = array();
    $q = $db->query("SHOW VARIABLES LIKE 'character_set%'");
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            $charset[$row['Variable_name']] = $row['Value'];
        }
    }
    
    // Проверяем наличие таблиц
    $tables = array();
    $q = $db->query("SHOW TABLES LIKE 'battlepass_%'");
    if ($q) {
        while ($row = $q->fetch_array()) {
            $tables[] = $row[0];
        }
    }
    
    echo json_encode([
        'errorcode' => 0,
        'message' => 'connection_ok',
        'debug' => [
            'db_status' => $db_status,
            'charset' => $charset,
            'tables_found' => $tables,
            'php_version' => phpversion(),
            'server_time' => date('Y-m-d H:i:s')
        ]
    ]);
}

/**
 * Получение списка сезонов
 */
function BpAdminGetSeasons() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    // Простой запрос без ORDER BY для начала
    $query = "SELECT * FROM battlepass_seasons";
    $q = $db->query($query);
    
    $seasons = array();
    $error = null;
    
    if (!$q) {
        $error = $db->error;
        echo json_encode([
            'errorcode' => 1,
            'message' => 'Database error',
            'debug' => [
                'query' => $query,
                'error' => $error
            ]
        ]);
        return;
    }
    
    while ($row = $q->fetch_assoc()) {
        // Принудительно конвертируем в UTF-8
        foreach ($row as $key => $value) {
            if (is_string($value) && !mb_check_encoding($value, 'UTF-8')) {
                $row[$key] = utf8_encode($value);
            }
        }
        $seasons[] = $row;
    }
    
    echo json_encode([
        'errorcode' => 0,
        'seasons' => $seasons,
        'debug' => [
            'count' => count($seasons),
            'query' => $query
        ]
    ]);
}


/**
 * Получение данных конкретного сезона
 */
function BpAdminGetSeason() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    
    if (!$season_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_season']);
        return;
    }
    
    $q = $db->query("SELECT * FROM battlepass_seasons WHERE id = {$season_id} LIMIT 1");
    
    if ($q && $q->num_rows > 0) {
        $season = $q->fetch_assoc();
        echo json_encode([
            'errorcode' => 0,
            'season' => $season
        ]);
    } else {
        echo json_encode(['errorcode' => 1, 'message' => 'season_not_found']);
    }
}

/**
 * Функция для формирования HEX из полей предмета
 */
function BuildItemHex($data) {
    // Формируем HEX строку из полей
    // Формат может отличаться в зависимости от игры
    // Это пример, нужно подставить вашу реальную структуру
    
    $item_id = isset($data['item_id']) ? (int)$data['item_id'] : 0;
    $count = isset($data['count']) ? (int)$data['count'] : 1;
    $client_size = isset($data['client_size']) ? (int)$data['client_size'] : 0;
    $max_count = isset($data['max_count']) ? (int)$data['max_count'] : 1;
    $data_str = isset($data['data']) ? $data['data'] : '';
    $proctype = isset($data['proctype']) ? (int)$data['proctype'] : 0;
    $expire_date = isset($data['expire_date']) ? (int)$data['expire_date'] : 0;
    $source = isset($data['source']) ? $data['source'] : '';
    
    // Простой пример формирования HEX (замените на вашу реальную логику)
    $hex = sprintf("%08x", $item_id) . 
           sprintf("%08x", $count) . 
           sprintf("%08x", $client_size) . 
           sprintf("%08x", $max_count) . 
           bin2hex($data_str) . 
           sprintf("%08x", $proctype) . 
           sprintf("%08x", $expire_date) . 
           bin2hex($source);
    
    return $hex;
}

/**
 * Сохранение сезона
 */
function BpAdminSaveSeason() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку соединения
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    $db->set_charset("utf8");
    
    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    $name = isset($_POST['name']) ? trim($_POST['name']) : '';
    $description = isset($_POST['description']) ? trim($_POST['description']) : '';
    $date_start = isset($_POST['date_start']) ? $_POST['date_start'] : '';
    $date_end = isset($_POST['date_end']) ? $_POST['date_end'] : '';
    $max_level = isset($_POST['max_level']) ? (int)$_POST['max_level'] : 50;
    $exp_per_level = isset($_POST['exp_per_level']) ? (int)$_POST['exp_per_level'] : 1000;
    $price_premium = isset($_POST['price_premium']) ? (int)$_POST['price_premium'] : 0;
    $price_premium_plus = isset($_POST['price_premium_plus']) ? (int)$_POST['price_premium_plus'] : 0;
    $is_active = isset($_POST['is_active']) ? (int)$_POST['is_active'] : 0;
    
    // Логируем входящие данные для отладки
    error_log("BpAdminSaveSeason - raw name: " . bin2hex($name));
    error_log("BpAdminSaveSeason - raw description: " . bin2hex($description));
    
    // ПРИНУДИТЕЛЬНАЯ КОНВЕРТАЦИЯ
    // Если строка похожа на кракозябры, пробуем восстановить
    if (preg_match('/[^\x20-\x7E]/', $name) === 0 && strlen($name) > 0) {
        // Строка состоит только из ASCII - возможно это кракозябры
        $name = mb_convert_encoding($name, 'UTF-8', 'CP1251');
    }
    
    if (preg_match('/[^\x20-\x7E]/', $description) === 0 && strlen($description) > 0) {
        $description = mb_convert_encoding($description, 'UTF-8', 'CP1251');
    }
    
    error_log("BpAdminSaveSeason - converted name: " . bin2hex($name));
    
    if (empty($name)) {
        echo json_encode(['errorcode' => 1, 'message' => 'empty_name']);
        return;
    }
    
    if (empty($date_start) || empty($date_end)) {
        echo json_encode(['errorcode' => 1, 'message' => 'empty_dates']);
        return;
    }
    
    // Экранируем
    $name_esc = $db->real_escape_string($name);
    $description_esc = $db->real_escape_string($description);
    $date_start_esc = $db->real_escape_string($date_start);
    $date_end_esc = $db->real_escape_string($date_end);
    
    if ($id > 0) {
        $query = "UPDATE battlepass_seasons SET 
                  name = '{$name_esc}',
                  description = '{$description_esc}',
                  date_start = '{$date_start_esc}',
                  date_end = '{$date_end_esc}',
                  max_level = {$max_level},
                  exp_per_level = {$exp_per_level},
                  price_premium = {$price_premium},
                  price_premium_plus = {$price_premium_plus},
                  is_active = {$is_active}
                  WHERE id = {$id}";
    } else {
        $query = "INSERT INTO battlepass_seasons 
                  (name, description, date_start, date_end, max_level, exp_per_level, price_premium, price_premium_plus, is_active) 
                  VALUES ('{$name_esc}', '{$description_esc}', '{$date_start_esc}', '{$date_end_esc}', {$max_level}, {$exp_per_level}, {$price_premium}, {$price_premium_plus}, {$is_active})";
    }
    
    if ($db->query($query)) {
        $new_id = $id > 0 ? $id : $db->insert_id;
        
        // Проверяем что сохранилось
        $check = $db->query("SELECT * FROM battlepass_seasons WHERE id = {$new_id}");
        $saved = $check->fetch_assoc();
        
        // Логируем сохраненные данные
        error_log("BpAdminSaveSeason - saved name: " . bin2hex($saved['name']));
        
        echo json_encode([
            'errorcode' => 0,
            'id' => $new_id,
            'message' => 'season_saved',
            'debug' => [
                'query' => $query,
                'saved_data' => $saved,
                'name_hex' => bin2hex($name),
                'name_original' => $name
            ]
        ]);
    } else {
        echo json_encode([
            'errorcode' => 1,
            'message' => 'db_error: ' . $db->error,
            'debug' => [
                'query' => $query,
                'error' => $db->error
            ]
        ]);
    }
}

/**
 * Удаление сезона
 */
function BpAdminDeleteSeason() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    
    if (!$id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_id']);
        return;
    }
    
    // Удаляем связанные данные
    $db->query("DELETE FROM battlepass_levels WHERE season_id = {$id}");
    $db->query("DELETE FROM battlepass_quests WHERE season_id = {$id}");
    $db->query("DELETE FROM battlepass_shop WHERE season_id = {$id}");
    $db->query("DELETE FROM battlepass_progress WHERE season_id = {$id}");
    $db->query("DELETE FROM battlepass_rewards WHERE season_id = {$id}");
    $db->query("DELETE FROM battlepass_quest_completions WHERE quest_id IN (SELECT id FROM battlepass_quests WHERE season_id = {$id})");
    $db->query("DELETE FROM battlepass_purchases WHERE shop_item_id IN (SELECT id FROM battlepass_shop WHERE season_id = {$id})");
    
    // Удаляем сезон
    $result = $db->query("DELETE FROM battlepass_seasons WHERE id = {$id}");
    
    if ($result) {
        echo json_encode(['errorcode' => 0, 'message' => 'season_deleted']);
    } else {
        echo json_encode(['errorcode' => 1, 'message' => 'delete_failed']);
    }
}

/**
 * Получение уровней сезона
 */
function BpAdminGetLevels() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    
    if (!$season_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_season']);
        return;
    }
    
    $q = $db->query("SELECT * FROM battlepass_levels WHERE season_id = {$season_id} ORDER BY level ASC");
    
    $levels = array();
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            // Декодируем JSON данные для FREE награды
            if (!empty($row['free_reward_data'])) {
                $free_data = json_decode($row['free_reward_data'], true);
                if (is_array($free_data)) {
                    $row['free_reward'] = $free_data;
                } else {
                    $row['free_reward'] = null;
                }
            } else {
                $row['free_reward'] = null;
            }
            
            // Декодируем JSON данные для PREMIUM награды
            if (!empty($row['premium_reward_data'])) {
                $premium_data = json_decode($row['premium_reward_data'], true);
                if (is_array($premium_data)) {
                    $row['premium_reward'] = $premium_data;
                } else {
                    $row['premium_reward'] = null;
                }
            } else {
                $row['premium_reward'] = null;
            }
            
            // Декодируем JSON данные для PREMIUM+ награды
            if (!empty($row['premium_plus_reward_data'])) {
                $plus_data = json_decode($row['premium_plus_reward_data'], true);
                if (is_array($plus_data)) {
                    $row['premium_plus_reward'] = $plus_data;
                } else {
                    $row['premium_plus_reward'] = null;
                }
            } else {
                $row['premium_plus_reward'] = null;
            }
            
            $levels[] = $row;
        }
    }
    
    echo json_encode([
        'errorcode' => 0,
        'levels' => $levels
    ]);
}



/**
 * Сохранение уровней
 */
function BpAdminSaveLevels() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    // Логируем все входные данные
    error_log("========== BpAdminSaveLevels ==========");
    error_log("POST: " . print_r($_POST, true));
    
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    
    // Проверяем, пришли ли данные в JSON формате
    if (isset($_POST['levels_json']) && isset($_POST['levels_format']) && $_POST['levels_format'] === 'json') {
        $levels_json = $_POST['levels_json'];
        $levels = json_decode($levels_json, true);
        error_log("Decoded levels from JSON: " . print_r($levels, true));
    } else {
        $levels = isset($_POST['levels']) ? $_POST['levels'] : array();
    }
    
    error_log("season_id: " . $season_id);
    error_log("levels type: " . gettype($levels));
    error_log("levels count: " . (is_array($levels) ? count($levels) : 'not array'));
    
    if (!$season_id) {
        error_log("ERROR: invalid_season");
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_season']);
        return;
    }
    
    if (!is_array($levels)) {
        error_log("ERROR: levels is not array, it's " . gettype($levels));
        echo json_encode(['errorcode' => 1, 'message' => 'no_levels_data']);
        return;
    }
    
    if (empty($levels)) {
        error_log("ERROR: levels is empty array");
        echo json_encode(['errorcode' => 1, 'message' => 'no_levels_data']);
        return;
    }
    
    // Очищаем существующие уровни
    $delete_query = "DELETE FROM battlepass_levels WHERE season_id = {$season_id}";
    error_log("DELETE query: " . $delete_query);
    $db->query($delete_query);
    
    // Сохраняем новые уровни
    $saved_count = 0;
    $errors = array();
    
    foreach ($levels as $level_num => $level_data) {
        error_log("Processing level index: " . $level_num);
        error_log("Level data: " . print_r($level_data, true));
        
        // Получаем номер уровня
        $level = isset($level_data['level']) ? (int)$level_data['level'] : (int)$level_num;
        error_log("Level number: " . $level);
        
        // ИНИЦИАЛИЗИРУЕМ ПЕРЕМЕННЫЕ
        $free_data = array();
        $premium_data = array();
        $plus_data = array();
        
        // Формируем данные для FREE награды
        if (isset($level_data['free_data']) && is_array($level_data['free_data']) && isset($level_data['free_data']['item_id']) && $level_data['free_data']['item_id'] > 0) {
            $free_data = $level_data['free_data'];
            error_log("FREE data from array: " . print_r($free_data, true));
        } elseif (isset($level_data['free_item_id']) && $level_data['free_item_id'] > 0) {
            // Старый формат
            $free_data = array(
                'item_id' => (int)$level_data['free_item_id'],
                'count' => isset($level_data['free_count']) ? (int)$level_data['free_count'] : 1,
                'client_size' => isset($level_data['free_client_size']) ? (int)$level_data['free_client_size'] : 0,
                'max_count' => isset($level_data['free_max_count']) ? (int)$level_data['free_max_count'] : 1,
                'data' => isset($level_data['free_data']) ? $level_data['free_data'] : '',
                'proctype' => isset($level_data['free_proctype']) ? (int)$level_data['free_proctype'] : 0,
                'expire_date' => isset($level_data['free_expire_date']) ? (int)$level_data['free_expire_date'] : 0,
                'source' => isset($level_data['free_source']) ? $level_data['free_source'] : 'battlepass_free'
            );
            error_log("FREE data from old format: " . print_r($free_data, true));
        } else {
            error_log("FREE: item_id not set or <= 0");
        }
        
        // Формируем данные для PREMIUM награды
        if (isset($level_data['premium_data']) && is_array($level_data['premium_data']) && isset($level_data['premium_data']['item_id']) && $level_data['premium_data']['item_id'] > 0) {
            $premium_data = $level_data['premium_data'];
            error_log("PREMIUM data from array: " . print_r($premium_data, true));
        } elseif (isset($level_data['premium_item_id']) && $level_data['premium_item_id'] > 0) {
            // Старый формат
            $premium_data = array(
                'item_id' => (int)$level_data['premium_item_id'],
                'count' => isset($level_data['premium_count']) ? (int)$level_data['premium_count'] : 1,
                'client_size' => isset($level_data['premium_client_size']) ? (int)$level_data['premium_client_size'] : 0,
                'max_count' => isset($level_data['premium_max_count']) ? (int)$level_data['premium_max_count'] : 1,
                'data' => isset($level_data['premium_data']) ? $level_data['premium_data'] : '',
                'proctype' => isset($level_data['premium_proctype']) ? (int)$level_data['premium_proctype'] : 0,
                'expire_date' => isset($level_data['premium_expire_date']) ? (int)$level_data['premium_expire_date'] : 0,
                'source' => isset($level_data['premium_source']) ? $level_data['premium_source'] : 'battlepass_premium'
            );
            error_log("PREMIUM data from old format: " . print_r($premium_data, true));
        } else {
            error_log("PREMIUM: item_id not set or <= 0");
        }
        
        // Формируем данные для PREMIUM+ награды
        if (isset($level_data['plus_data']) && is_array($level_data['plus_data']) && isset($level_data['plus_data']['item_id']) && $level_data['plus_data']['item_id'] > 0) {
            $plus_data = $level_data['plus_data'];
            error_log("PLUS data from array: " . print_r($plus_data, true));
        } elseif (isset($level_data['plus_item_id']) && $level_data['plus_item_id'] > 0) {
            // Старый формат
            $plus_data = array(
                'item_id' => (int)$level_data['plus_item_id'],
                'count' => isset($level_data['plus_count']) ? (int)$level_data['plus_count'] : 1,
                'client_size' => isset($level_data['plus_client_size']) ? (int)$level_data['plus_client_size'] : 0,
                'max_count' => isset($level_data['plus_max_count']) ? (int)$level_data['plus_max_count'] : 1,
                'data' => isset($level_data['plus_data']) ? $level_data['plus_data'] : '',
                'proctype' => isset($level_data['plus_proctype']) ? (int)$level_data['plus_proctype'] : 0,
                'expire_date' => isset($level_data['plus_expire_date']) ? (int)$level_data['plus_expire_date'] : 0,
                'source' => isset($level_data['plus_source']) ? $level_data['plus_source'] : 'battlepass_plus'
            );
            error_log("PLUS data from old format: " . print_r($plus_data, true));
        } else {
            error_log("PLUS: item_id not set or <= 0");
        }
        
        // Кодируем в JSON
        $free_json = !empty($free_data) ? json_encode($free_data, JSON_UNESCAPED_UNICODE) : '';
        $premium_json = !empty($premium_data) ? json_encode($premium_data, JSON_UNESCAPED_UNICODE) : '';
        $plus_json = !empty($plus_data) ? json_encode($plus_data, JSON_UNESCAPED_UNICODE) : '';
        
        error_log("free_json: " . $free_json);
        error_log("premium_json: " . $premium_json);
        error_log("plus_json: " . $plus_json);
        
        // Количество предметов
        $free_count = !empty($free_data) ? (int)$free_data['count'] : 0;
        $premium_count = !empty($premium_data) ? (int)$premium_data['count'] : 0;
        $plus_count = !empty($plus_data) ? (int)$plus_data['count'] : 0;
        
        // Экранируем JSON строки
        $free_json_esc = $db->real_escape_string($free_json);
        $premium_json_esc = $db->real_escape_string($premium_json);
        $plus_json_esc = $db->real_escape_string($plus_json);
        
        // Вставляем запись
        $query = "INSERT INTO battlepass_levels 
                  (season_id, level, free_reward_data, free_reward_count, premium_reward_data, premium_reward_count, premium_plus_reward_data, premium_plus_reward_count) 
                  VALUES ({$season_id}, {$level}, '{$free_json_esc}', {$free_count}, '{$premium_json_esc}', {$premium_count}, '{$plus_json_esc}', {$plus_count})";
        
        error_log("INSERT query: " . $query);
        
        if ($db->query($query)) {
            $saved_count++;
            error_log("SUCCESS: saved level {$level}");
        } else {
            $db_error = $db->error;
            error_log("ERROR: " . $db_error);
            $errors[] = "Level {$level}: " . $db_error;
        }
    }
    
    error_log("FINISHED: saved_count={$saved_count}, total_levels=" . count($levels));
    error_log("========== BpAdminSaveLevels END ==========");
    
    echo json_encode([
        'errorcode' => 0, 
        'message' => 'levels_saved',
        'debug' => [
            'saved_count' => $saved_count,
            'total_levels' => count($levels),
            'errors' => $errors
        ]
    ]);
}


/**
 * Копирование уровней из одного сезона в другой
 */
function BpAdminCopyLevels() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $from_season = isset($_POST['from_season']) ? (int)$_POST['from_season'] : 0;
    $to_season = isset($_POST['to_season']) ? (int)$_POST['to_season'] : 0;
    
    if (!$from_season || !$to_season) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_seasons']);
        return;
    }
    
    if ($from_season == $to_season) {
        echo json_encode(['errorcode' => 1, 'message' => 'same_season']);
        return;
    }
    
    // Получаем уровни из источника
    $q = $db->query("SELECT * FROM battlepass_levels WHERE season_id = {$from_season}");
    
    if (!$q) {
        echo json_encode(['errorcode' => 1, 'message' => 'source_query_failed: ' . $db->error]);
        return;
    }
    
    if ($q->num_rows == 0) {
        echo json_encode(['errorcode' => 0, 'message' => 'no_levels_to_copy']);
        return;
    }
    
    // Очищаем целевой сезон
    $db->query("DELETE FROM battlepass_levels WHERE season_id = {$to_season}");
    
    // Копируем
    $success = true;
    $error_msg = '';
    while ($row = $q->fetch_assoc()) {
        // Убираем id, чтобы автоинкремент сработал корректно
        unset($row['id']);
        // Меняем season_id на целевой
        $row['season_id'] = $to_season;
        
        // Формируем список полей и значений динамически
        $fields = array_keys($row);
        $values = array();
        foreach ($row as $val) {
            if (is_null($val)) {
                $values[] = 'NULL';
            } else {
                $values[] = "'" . $db->real_escape_string((string)$val) . "'";
            }
        }
        
        $query = "INSERT INTO battlepass_levels (`" . implode('`, `', $fields) . "`) VALUES (" . implode(', ', $values) . ")";
        
        if (!$db->query($query)) {
            $success = false;
            $error_msg = $db->error; // Сохраняем текст ошибки MySQL
            break;
        }
    }
    
    if ($success) {
        echo json_encode(['errorcode' => 0, 'message' => 'levels_copied']);
    } else {
        // Возвращаем текст ошибки, чтобы было проще дебажить
        echo json_encode(['errorcode' => 1, 'message' => 'copy_failed: ' . $error_msg]);
    }
}

function BpAdminGetQuest() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    
    if (!$id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_id']);
        return;
    }
    
    $q = $db->query("SELECT * FROM battlepass_quests WHERE id = {$id} LIMIT 1");
    
    if ($q && $q->num_rows > 0) {
        $quest = $q->fetch_assoc();
        // Конвертируем в UTF-8
        array_walk_recursive($quest, function(&$item) {
            if (is_string($item) && !mb_check_encoding($item, 'UTF-8')) {
                $item = utf8_encode($item);
            }
        });
        echo json_encode([
            'errorcode' => 0,
            'quest' => $quest
        ]);
    } else {
        echo json_encode(['errorcode' => 1, 'message' => 'quest_not_found']);
    }
}

/**
 * Получение заданий сезона
 */
function BpAdminGetQuests() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    $db->set_charset("utf8");
    
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    
    if (!$season_id) {
        echo json_encode([
            'errorcode' => 1, 
            'message' => 'invalid_season',
            'debug' => ['received_season_id' => $season_id]
        ]);
        return;
    }
    
    $query = "SELECT * FROM battlepass_quests WHERE season_id = {$season_id} ORDER BY 
              CASE quest_type 
                  WHEN 'daily' THEN 1 
                  WHEN 'weekly' THEN 2 
                  ELSE 3 
              END, week_number, id";
    
    $q = $db->query($query);
    
    $quests = array();
    $db_error = null;
    
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            // Принудительно конвертируем в UTF-8 для JSON
            foreach ($row as $key => $value) {
                if (is_string($value) && !mb_check_encoding($value, 'UTF-8')) {
                    $row[$key] = mb_convert_encoding($value, 'UTF-8', 'auto');
                }
            }
            $quests[] = $row;
        }
    } else {
        $db_error = $db->error;
    }
    
    echo json_encode([
        'errorcode' => 0,
        'quests' => $quests,
        'debug' => [
            'season_id' => $season_id,
            'count' => count($quests)
        ]
    ]);
}


/**
 * Сохранение задания
 */
function BpAdminSaveQuest() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку соединения
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    $db->set_charset("utf8");
    
    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    $quest_type = isset($_POST['quest_type']) ? $_POST['quest_type'] : '';
    $week_number = isset($_POST['week_number']) && $_POST['week_number'] !== '' ? (int)$_POST['week_number'] : 0;
    $title = isset($_POST['title']) ? trim($_POST['title']) : '';
    $description = isset($_POST['description']) ? trim($_POST['description']) : '';
    $target_type = isset($_POST['target_type']) ? $_POST['target_type'] : '';
    $target_id = isset($_POST['target_id']) && $_POST['target_id'] !== '' ? (int)$_POST['target_id'] : 0;
    $target_count = isset($_POST['target_count']) ? (int)$_POST['target_count'] : 1;
    $reward_exp = isset($_POST['reward_exp']) ? (int)$_POST['reward_exp'] : 100;
    $log_pattern = isset($_POST['log_pattern']) ? trim($_POST['log_pattern']) : '';
    $is_active = isset($_POST['is_active']) ? (int)$_POST['is_active'] : 1;
    
    // Логируем входящие данные для отладки
    error_log("BpAdminSaveQuest POST data: " . print_r($_POST, true));
    
    // ===== ОБРАБОТКА target_params для complete_quest =====
    $target_params = null;
    
    if ($target_type == 'complete_quest') {
        // Вариант 1: Поле name="target_params[game_quest_id]" (как в вашей админке)
        if (isset($_POST['target_params']) && is_array($_POST['target_params']) && isset($_POST['target_params']['game_quest_id'])) {
            $game_quest_id = (int)$_POST['target_params']['game_quest_id'];
            if ($game_quest_id > 0) {
                $target_params = json_encode([
                    'game_quest_id' => $game_quest_id
                ], JSON_UNESCAPED_UNICODE);
                
                // Также обновляем target_id для совместимости со старым кодом
                if ($target_id == 0) {
                    $target_id = $game_quest_id;
                }
                error_log("BpAdminSaveQuest: Using target_params[game_quest_id] = {$game_quest_id}");
            } else {
                // Если ID = 0, значит "любой квест"
                $target_params = json_encode([
                    'game_quest_id' => 0
                ], JSON_UNESCAPED_UNICODE);
                error_log("BpAdminSaveQuest: Any quest (game_quest_id = 0)");
            }
        }
        
        // Вариант 2: Поле name="game_quest_id" (альтернативный формат)
        elseif (isset($_POST['game_quest_id']) && $_POST['game_quest_id'] !== '') {
            $game_quest_id = (int)$_POST['game_quest_id'];
            if ($game_quest_id > 0) {
                $target_params = json_encode([
                    'game_quest_id' => $game_quest_id
                ], JSON_UNESCAPED_UNICODE);
                
                if ($target_id == 0) {
                    $target_id = $game_quest_id;
                }
                error_log("BpAdminSaveQuest: Using game_quest_id = {$game_quest_id}");
            } else {
                $target_params = json_encode([
                    'game_quest_id' => 0
                ], JSON_UNESCAPED_UNICODE);
            }
        }
        
        // Вариант 3: Уже готовый JSON в target_params
        elseif (isset($_POST['target_params']) && is_string($_POST['target_params']) && !empty($_POST['target_params'])) {
            $target_params = $_POST['target_params'];
            // Пробуем извлечь game_quest_id из JSON
            $params = json_decode($target_params, true);
            if (is_array($params) && isset($params['game_quest_id']) && $params['game_quest_id'] > 0 && $target_id == 0) {
                $target_id = (int)$params['game_quest_id'];
            }
            error_log("BpAdminSaveQuest: Using target_params string");
        }
    }
    
    // Экранируем для БД
    $title_esc = $db->real_escape_string($title);
    $description_esc = $db->real_escape_string($description);
    $log_pattern_esc = $db->real_escape_string($log_pattern);
    $target_params_esc = $target_params ? "'" . $db->real_escape_string($target_params) . "'" : 'NULL';
    
    $week_sql = $week_number > 0 ? $week_number : 'NULL';
    $target_id_sql = $target_id > 0 ? $target_id : 'NULL';
    
    if ($id > 0) {
        $query = "UPDATE battlepass_quests SET 
                  quest_type = '{$quest_type}',
                  week_number = {$week_sql},
                  title = '{$title_esc}',
                  description = '{$description_esc}',
                  target_type = '{$target_type}',
                  target_id = {$target_id_sql},
                  target_count = {$target_count},
                  target_params = {$target_params_esc},
                  reward_exp = {$reward_exp},
                  log_pattern = '{$log_pattern_esc}',
                  is_active = {$is_active}
                  WHERE id = {$id}";
    } else {
        $query = "INSERT INTO battlepass_quests 
                  (season_id, quest_type, week_number, title, description, target_type, target_id, target_count, target_params, reward_exp, log_pattern, is_active) 
                  VALUES ({$season_id}, '{$quest_type}', {$week_sql}, '{$title_esc}', '{$description_esc}', '{$target_type}', {$target_id_sql}, {$target_count}, {$target_params_esc}, {$reward_exp}, '{$log_pattern_esc}', {$is_active})";
    }
    
    error_log("BpAdminSaveQuest query: " . $query);
    
    if ($db->query($query)) {
        $new_id = $id > 0 ? $id : $db->insert_id;
        
        // Проверяем что сохранилось
        $check = $db->query("SELECT id, title, target_id, target_params FROM battlepass_quests WHERE id = {$new_id}");
        if ($check && $check->num_rows > 0) {
            $saved = $check->fetch_assoc();
            error_log("BpAdminSaveQuest - saved: ID={$saved['id']}, target_id={$saved['target_id']}, target_params={$saved['target_params']}");
        }
        
        echo json_encode(['errorcode' => 0, 'message' => 'quest_saved', 'id' => $new_id]);
    } else {
        error_log("BpAdminSaveQuest error: " . $db->error);
        echo json_encode(['errorcode' => 1, 'message' => 'db_error: ' . $db->error]);
    }
}


/**
 * Удаление задания
 */
function BpAdminDeleteQuest() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    
    if (!$id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_id']);
        return;
    }
    
    $db->query("DELETE FROM battlepass_quest_completions WHERE quest_id = {$id}");
    $result = $db->query("DELETE FROM battlepass_quests WHERE id = {$id}");
    
    if ($result) {
        echo json_encode(['errorcode' => 0, 'message' => 'quest_deleted']);
    } else {
        echo json_encode(['errorcode' => 1, 'message' => 'delete_failed']);
    }
}

/**
 * Получение товаров магазина
 */
function BpAdminGetShop() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    
    if (!$season_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_season']);
        return;
    }
    
    $q = $db->query("SELECT * FROM battlepass_shop WHERE season_id = {$season_id} ORDER BY category, sort_order");
    
    $items = array();
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            // Декодируем JSON данные предмета
            if (!empty($row['item_data'])) {
                $item_data = json_decode($row['item_data'], true);
                if (is_array($item_data)) {
                    $row['item_data_array'] = $item_data;
                }
            }
            $items[] = $row;
        }
    }
    
    echo json_encode([
        'errorcode' => 0,
        'items' => $items
    ]);
}

/**
 * Сохранение товара магазина
 */
function BpAdminSaveShopItem() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Логируем все входящие данные
    error_log("=== BpAdminSaveShopItem ===");
    error_log("POST data: " . print_r($_POST, true));
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    $category = isset($_POST['category']) ? $_POST['category'] : '';
    $price_gold = isset($_POST['price_gold']) ? (int)$_POST['price_gold'] : 0;
    $price_silver = isset($_POST['price_silver']) ? (int)$_POST['price_silver'] : 0;
    $discount_percent = isset($_POST['discount_percent']) ? (int)$_POST['discount_percent'] : 0;
    $original_price_gold = isset($_POST['original_price_gold']) ? (int)$_POST['original_price_gold'] : 0;
    $original_price_silver = isset($_POST['original_price_silver']) ? (int)$_POST['original_price_silver'] : 0;
    $stock_limit = isset($_POST['stock_limit']) ? (int)$_POST['stock_limit'] : 0;
    $is_active = isset($_POST['is_active']) ? 1 : 0;
    $sort_order = isset($_POST['sort_order']) ? (int)$_POST['sort_order'] : 0;
    
    // Собираем данные предмета (как в наградах)
    $item_data = array(
        'item_id' => isset($_POST['item_id']) ? (int)$_POST['item_id'] : 0,
        'count' => isset($_POST['item_count']) ? (int)$_POST['item_count'] : 1,
        'client_size' => isset($_POST['item_client_size']) ? (int)$_POST['item_client_size'] : 0,
        'max_count' => isset($_POST['item_max_count']) ? (int)$_POST['item_max_count'] : 1,
        'data' => isset($_POST['item_data']) ? $_POST['item_data'] : '',
        'proctype' => isset($_POST['item_proctype']) ? (int)$_POST['item_proctype'] : 0,
        'expire_date' => isset($_POST['item_expire_date']) ? (int)$_POST['item_expire_date'] : 0,
        'source' => isset($_POST['item_source']) ? $_POST['item_source'] : 'shop'
    );
    
    if (!$season_id || $item_data['item_id'] <= 0) {
        echo json_encode(['errorcode' => 1, 'message' => 'Не указан ID предмета']);
        return;
    }
    
    // Кодируем данные предмета в JSON (как в наградах)
    $item_data_json = json_encode($item_data, JSON_UNESCAPED_UNICODE);
    
    // Экранируем
    $item_data_json_esc = $db->real_escape_string($item_data_json);
    $category_esc = $db->real_escape_string($category);
    
    if ($id > 0) {
        $query = "UPDATE battlepass_shop SET 
                  category = '{$category_esc}',
                  item_data = '{$item_data_json_esc}',
                  price_gold = {$price_gold},
                  price_silver = {$price_silver},
                  discount_percent = {$discount_percent},
                  original_price_gold = {$original_price_gold},
                  original_price_silver = {$original_price_silver},
                  stock_limit = {$stock_limit},
                  is_active = {$is_active},
                  sort_order = {$sort_order}
                  WHERE id = {$id}";
    } else {
        $query = "INSERT INTO battlepass_shop 
                  (season_id, category, item_data, price_gold, price_silver, discount_percent, original_price_gold, original_price_silver, stock_limit, is_active, sort_order, purchased_count) 
                  VALUES ({$season_id}, '{$category_esc}', '{$item_data_json_esc}', {$price_gold}, {$price_silver}, {$discount_percent}, {$original_price_gold}, {$original_price_silver}, {$stock_limit}, {$is_active}, {$sort_order}, 0)";
    }
    
    error_log("Shop item save query: " . $query);
    
    if ($db->query($query)) {
        echo json_encode(['errorcode' => 0, 'message' => 'Товар успешно сохранен']);
    } else {
        error_log("Shop item save error: " . $db->error);
        echo json_encode(['errorcode' => 1, 'message' => 'Ошибка сохранения: ' . $db->error]);
    }
}

/**
 * Удаление товара магазина
 */
function BpAdminDeleteShopItem() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    
    if (!$id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_id']);
        return;
    }
    
    $db->query("DELETE FROM battlepass_purchases WHERE shop_item_id = {$id}");
    $result = $db->query("DELETE FROM battlepass_shop WHERE id = {$id}");
    
    if ($result) {
        echo json_encode(['errorcode' => 0, 'message' => 'shop_item_deleted']);
    } else {
        echo json_encode(['errorcode' => 1, 'message' => 'delete_failed']);
    }
}

/**
 * Получение статистики игроков
 */
function BpAdminGetStats() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    
    if (!$season_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_season']);
        return;
    }
    
    $q = $db->query("SELECT p.*, u.name as user_name, u.email 
                     FROM battlepass_progress p
                     LEFT JOIN users u ON p.user_id = u.id
                     WHERE p.season_id = {$season_id}
                     ORDER BY p.current_level DESC, p.total_exp DESC
                     LIMIT 100");
    
    $players = array();
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            // Получаем количество полученных наград
            $r = $db->query("SELECT COUNT(*) as cnt FROM battlepass_rewards WHERE user_id = {$row['user_id']} AND season_id = {$season_id}");
            if ($r) {
                $rcnt = $r->fetch_assoc();
                $row['rewards_count'] = $rcnt['cnt'];
            } else {
                $row['rewards_count'] = 0;
            }
            
            // Получаем количество выполненных заданий
            $r = $db->query("SELECT COUNT(*) as cnt FROM battlepass_quest_completions WHERE user_id = {$row['user_id']} AND completed = 1");
            if ($r) {
                $qcnt = $r->fetch_assoc();
                $row['quests_completed'] = $qcnt['cnt'];
            } else {
                $row['quests_completed'] = 0;
            }
            
            $players[] = $row;
        }
    }
    
    echo json_encode([
        'errorcode' => 0,
        'players' => $players
    ]);
}

/**
 * Получение общей статистики
 */
function BpAdminGetOverallStats() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    
    if (!$season_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_season']);
        return;
    }
    
    $stats = array(
        'total_players' => 0,
        'total_purchases' => 0,
        'total_rewards' => 0,
        'avg_level' => 0,
        'max_level' => 0
    );
    
    $q = $db->query("SELECT COUNT(*) as cnt FROM battlepass_progress WHERE season_id = {$season_id}");
    if ($q) {
        $row = $q->fetch_assoc();
        $stats['total_players'] = $row['cnt'];
    }
    
    $q = $db->query("SELECT COUNT(*) as cnt FROM battlepass_purchases WHERE shop_item_id IN (SELECT id FROM battlepass_shop WHERE season_id = {$season_id})");
    if ($q) {
        $row = $q->fetch_assoc();
        $stats['total_purchases'] = $row['cnt'];
    }
    
    $q = $db->query("SELECT COUNT(*) as cnt FROM battlepass_rewards WHERE season_id = {$season_id}");
    if ($q) {
        $row = $q->fetch_assoc();
        $stats['total_rewards'] = $row['cnt'];
    }
    
    $q = $db->query("SELECT AVG(current_level) as avg, MAX(current_level) as max FROM battlepass_progress WHERE season_id = {$season_id}");
    if ($q) {
        $row = $q->fetch_assoc();
        $stats['avg_level'] = round($row['avg'], 1);
        $stats['max_level'] = (int)$row['max'];
    }
    
    echo json_encode([
        'errorcode' => 0,
        'stats' => $stats
    ]);
}

/**
 * Сброс прогресса игрока
 */
function BpAdminResetPlayer() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    // Устанавливаем кодировку
    $db->query("SET NAMES 'utf8'");
    $db->query("SET CHARACTER SET 'utf8'");
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $season_id = isset($_POST['season_id']) ? (int)$_POST['season_id'] : 0;
    
    if (!$user_id || !$season_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'invalid_data']);
        return;
    }
    
    $db->query("DELETE FROM battlepass_rewards WHERE user_id = {$user_id} AND season_id = {$season_id}");
    $db->query("DELETE FROM battlepass_quest_completions WHERE user_id = {$user_id} AND quest_id IN (SELECT id FROM battlepass_quests WHERE season_id = {$season_id})");
    $db->query("DELETE FROM battlepass_purchases WHERE user_id = {$user_id} AND shop_item_id IN (SELECT id FROM battlepass_shop WHERE season_id = {$season_id})");
    $db->query("DELETE FROM battlepass_progress WHERE user_id = {$user_id} AND season_id = {$season_id}");
    
    echo json_encode(['errorcode' => 0, 'message' => 'player_reset']);
}

/**
 * Функция логирования
 */
function calendarLog($action, $user_id, $login, $data = array(), $status = 'info') {
    global $db;
    
    $ip = $_SERVER['REMOTE_ADDR'];
    $data_json = json_encode($data, JSON_UNESCAPED_UNICODE);
    $login_esc = $db->real_escape_string($login);
    
    $sql = "INSERT INTO calendar_logs (user_id, login, action, request_data, ip, status) 
            VALUES ({$user_id}, '{$login_esc}', '{$action}', '{$data_json}', '{$ip}', '{$status}')";
    
    $result = $db->query($sql);
    
    if (!$result) {
        error_log("Calendar log error: " . $db->error);
    }
    
    return $result;
}

/**
 * Проверка CSRF-токена
 */
function verifyCalendarCSRFToken($token) {
    return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}

/**
 * Rate limiting для календаря
 */
function checkCalendarRateLimit($user_id, $action, $limit = 20, $period = 3600) {
    global $db;
    
    $ip = $_SERVER['REMOTE_ADDR'];
    $time_limit = time() - $period;
    
    // Проверяем количество запросов за период
    $q = $db->query("SELECT COUNT(*) as cnt FROM calendar_logs 
                     WHERE user_id = {$user_id} AND action = '{$action}' 
                     AND request_time > FROM_UNIXTIME({$time_limit})");
    
    if ($q && $q->num_rows > 0) {
        $row = $q->fetch_assoc();
        if ($row['cnt'] >= $limit) {
            return false;
        }
    }
    
    return true;
}
/**
 * Отправка предмета в игру
 */
function CalendarSendItem($roleid, $item_data, $day, $month, $year) {
    global $Structures, $pp;
    
    if (!isset($pp)) $pp = new Protocols();
    
    // Проверяем обязательные поля
    if (!isset($item_data['item_id']) || !$item_data['item_id']) {
        return array(
            'errorcode' => 1, 
            'message' => 'Не указан ID предмета',
            'details' => $item_data
        );
    }
    
    // Создаем структуру предмета
    $item = array(
        'id' => (int)$item_data['item_id'],
        'pos' => 0,
        'count' => isset($item_data['count']) ? (int)$item_data['count'] : 1,
        'client_size' => isset($item_data['client_size']) ? (int)$item_data['client_size'] : 0,
        'max_count' => isset($item_data['max_count']) ? (int)$item_data['max_count'] : (isset($item_data['count']) ? (int)$item_data['count'] : 1),
        'data' => isset($item_data['data']) ? $item_data['data'] : '',
        'proctype' => isset($item_data['proctype']) ? (int)$item_data['proctype'] : 0,
        'expire_date' => isset($item_data['expire_date']) ? (int)$item_data['expire_date'] : 0,
        'guid1' => 0,
        'guid2' => 0
    );
    
    // Обработка HEX данных если есть
    if (!empty($item['data']) && is_string($item['data']) && ctype_xdigit(str_replace(' ', '', $item['data']))) {
        $item['data'] = hex2bin(str_replace(' ', '', $item['data']));
        $marshaled = $pp->marshal($item['data'], $Structures['Octets']);
        $item['data'] = $marshaled ?: '';
    }
    
    $title = "Награда за {$day} день календаря";
    $text = "Вы получили награду за {$day} день в календаре ({$month}.{$year})!";
    
    $result = SysSendMail(0, 32, 3, $roleid, $title, $text, $item, 0);
    
    $log_details = array(
        'item' => $item,
        'result' => $result,
        'target' => $roleid,
        'day' => $day
    );
    
    if (is_array($result) && isset($result['retcode']) && $result['retcode'] == 0) {
        return array(
            'errorcode' => 0, 
            'message' => 'Предмет отправлен',
            'details' => $log_details
        );
    } else {
        $error_msg = is_array($result) && isset($result['retcode']) 
            ? "Код ошибки: {$result['retcode']}" 
            : "Неизвестная ошибка";
        
        if (isset($result['retcode']) && $result['retcode'] == 217) {
            $error_msg = "Почтовый ящик переполнен";
        }
        
        return array(
            'errorcode' => 1, 
            'message' => $error_msg,
            'details' => $log_details
        );
    }
}

function logRewardSend($user_id, $login, $day, $character_id, $character_name, $item_data, $status, $message = '') {
    global $db;
    
    $ip = $_SERVER['REMOTE_ADDR'];
    $reward_data = json_encode($item_data, JSON_UNESCAPED_UNICODE);
    $login_esc = $db->real_escape_string($login);
    $character_name_esc = $db->real_escape_string($character_name);
    $message_esc = $db->real_escape_string($message);
    
    $sql = "INSERT INTO calendar_logs 
        (user_id, login, action, day, character_id, character_name, reward_data, request_data, ip, status) 
        VALUES (
            {$user_id}, 
            '{$login_esc}', 
            'user_claim', 
            {$day}, 
            {$character_id}, 
            '{$character_name_esc}', 
            '{$reward_data}', 
            '{$message_esc}', 
            '{$ip}', 
            '{$status}'
        )";
    
    $result = $db->query($sql);
    
    if (!$result) {
        error_log("Calendar log error: " . $db->error);
        error_log("SQL: " . $sql);
    }
    
    return $result;
}

/**
 * Логирование пропуска дня
 */
function logResignDay($user_id, $login, $old_day, $new_day, $character_id, $character_name, $status, $message = '') {
    global $db;
    
    $ip = $_SERVER['REMOTE_ADDR'];
    $login_esc = $db->real_escape_string($login);
    $character_name_esc = $db->real_escape_string($character_name);
    $data = json_encode([
        'old_day' => $old_day,
        'new_day' => $new_day
    ], JSON_UNESCAPED_UNICODE);
    
    $sql = "INSERT INTO calendar_logs 
        (user_id, login, action, day, character_id, character_name, request_data, ip, status, request_time) 
        VALUES (
            {$user_id}, 
            '{$login_esc}', 
            'user_resign', 
            {$old_day}, 
            {$character_id}, 
            '{$character_name_esc}', 
            '{$data}', 
            '{$ip}', 
            '{$status}',
            NOW()
        )";
    
    @$db->query($sql);
    error_log("Calendar resign log: User {$user_id}, Day {$old_day} -> {$new_day}, Status {$status}");
}

/**
 * Обработка пользовательских запросов календаря
 */
function CalendarUserDayBonus() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    header('X-Content-Type-Options: nosniff');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $login = isset($_POST['login']) ? $_POST['login'] : '';
    $sub_action = isset($_POST['sub_action']) ? $_POST['sub_action'] : '';
    
    if (!$user_id) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Не авторизован'));
        return;
    }
    
    switch ($sub_action) {
        
        // ===== ПОЛУЧЕНИЕ ДАННЫХ КАЛЕНДАРЯ =====
        case 'get_data':
            
            $month = isset($_POST['month']) ? (int)$_POST['month'] : (int)date('n');
            $year = isset($_POST['year']) ? (int)$_POST['year'] : (int)date('Y');
            
            // Проверяем активен ли календарь
            $q = $db->query("SELECT * FROM calendar_settings WHERE month = {$month} AND year = {$year} LIMIT 1");
            if ($q && $q->num_rows > 0) {
                $settings = $q->fetch_assoc();
                if (!$settings['is_active']) {
                    echo json_encode(array('errorcode' => 1, 'message' => 'Календарь за этот месяц неактивен'));
                    return;
                }
            }
            
            // Получаем дни с наградами
            $rewards = array();
            $q = $db->query("SELECT * FROM calendar_days WHERE month = {$month} AND year = {$year} ORDER BY day");
            if ($q) {
                while ($row = $q->fetch_assoc()) {
                    $rewards[$row['day']] = array(
                        'item_id' => (int)$row['item_id'],
                        'count' => (int)$row['item_count'],
                        'client_size' => (int)$row['client_size'],
                        'max_count' => (int)$row['max_count'],
                        'data' => $row['item_data'],
                        'proctype' => (int)$row['proctype'],
                        'expire_date' => (int)$row['expire_date'],
                        'is_special' => (bool)$row['is_special']
                    );
                }
            }
            
            // Получаем прогресс пользователя
            $progress = array(
                'received' => array(),
                'resign_count' => 4
            );
            
            $q = $db->query("SELECT * FROM calendar_user_progress WHERE user_id = {$user_id} AND month = {$month} AND year = {$year} LIMIT 1");
            if ($q && $q->num_rows > 0) {
                $row = $q->fetch_assoc();
                $progress['received'] = !empty($row['received_days']) ? explode(',', $row['received_days']) : array();
                $progress['resign_count'] = (int)$row['resign_count'];
            } else {
                // Создаем прогресс для нового пользователя
                $db->query("INSERT INTO calendar_user_progress (user_id, month, year, received_days, resign_count) 
                            VALUES ({$user_id}, {$month}, {$year}, '', 4)");
            }
            
            // Определяем первый день месяца
            $first_day = date('N', strtotime("{$year}-{$month}-01"));
            
            $response_data = array(
                'month' => $month,
                'year' => $year,
                'days_in_month' => (int)date('t', strtotime("{$year}-{$month}-01")),
                'first_day' => $first_day,
                'received' => $progress['received'],
                'resign_count' => $progress['resign_count'],
                'rewards' => $rewards
            );
            
            echo json_encode(array(
                'errorcode' => 0,
                'data' => $response_data
            ), JSON_UNESCAPED_UNICODE);
            break;
        
        // ===== ПОЛУЧЕНИЕ НАГРАДЫ =====
        case 'claim_reward':
    
            $day = isset($_POST['day']) ? (int)$_POST['day'] : 0;
            $target_player = isset($_POST['target_player']) ? (int)$_POST['target_player'] : 0;
            
            if (!$day || !$target_player) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Не указаны обязательные параметры'));
                return;
            }
            
            $month = (int)date('n');
            $year = (int)date('Y');
            $today = (int)date('j');
            
            // Проверка дня
            if ($day < 1 || $day > 31) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Некорректный день'));
                return;
            }
            
            // Получаем прогресс пользователя
            $q = $db->query("SELECT * FROM calendar_user_progress WHERE user_id = {$user_id} AND month = {$month} AND year = {$year} LIMIT 1");
            if (!$q || $q->num_rows == 0) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Прогресс не найден'));
                return;
            }
            
            $progress = $q->fetch_assoc();
            $received = !empty($progress['received_days']) ? explode(',', $progress['received_days']) : array();
            
            // Проверки
            if (in_array($day, $received)) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Награда за этот день уже получена'));
                return;
            }
            
            if ($day > $today) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Этот день еще не наступил'));
                return;
            }
            
            // Получаем данные награды
            $q = $db->query("SELECT * FROM calendar_days WHERE month = {$month} AND year = {$year} AND day = {$day} LIMIT 1");
            if (!$q || $q->num_rows == 0) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Награда не найдена'));
                return;
            }
            
            $reward = $q->fetch_assoc();
            
            // Формируем данные предмета
            $item_data = array(
                'item_id' => (int)$reward['item_id'],
                'count' => (int)$reward['item_count'],
                'client_size' => (int)$reward['client_size'],
                'max_count' => (int)$reward['max_count'],
                'data' => $reward['item_data'],
                'proctype' => (int)$reward['proctype'],
                'expire_date' => (int)$reward['expire_date']
            );
            
            // Получаем имя персонажа
            $character_name = getCharacterName($target_player);
            
            // Отправляем награду
            $send_result = CalendarSendItem($target_player, $item_data, $day, $month, $year);
            
            if ($send_result['errorcode'] !== 0) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Ошибка отправки награды: ' . $send_result['message']));
                return;
            }
            
            // Обновляем прогресс - добавляем полученный день
            $received[] = $day;
            $received_str = implode(',', $received);
            
            $db->query("UPDATE calendar_user_progress SET received_days = '{$received_str}' 
                        WHERE user_id = {$user_id} AND month = {$month} AND year = {$year}");
            
            echo json_encode(array(
                'errorcode' => 0,
                'message' => '✅ Награда отправлена в игру!'
            ), JSON_UNESCAPED_UNICODE);
            break;
        
        // ===== ПРОПУСК ДНЯ =====
        case 'resign_day':
    
            $day = isset($_POST['day']) ? (int)$_POST['day'] : 0;
            $target_player = isset($_POST['target_player']) ? (int)$_POST['target_player'] : 0;
            
            if (!$day || !$target_player) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Не указаны обязательные параметры'));
                return;
            }
            
            $month = (int)date('n');
            $year = (int)date('Y');
            $today = (int)date('j');
            
            // Получаем прогресс пользователя
            $q = $db->query("SELECT * FROM calendar_user_progress WHERE user_id = {$user_id} AND month = {$month} AND year = {$year} LIMIT 1");
            if (!$q || $q->num_rows == 0) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Прогресс не найден'));
                return;
            }
            
            $progress = $q->fetch_assoc();
            $received = !empty($progress['received_days']) ? explode(',', $progress['received_days']) : array();
            
            // Проверки
            if ($progress['resign_count'] <= 0) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Нет доступных пропусков'));
                return;
            }
            
            if (in_array($day, $received)) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Этот день уже получен'));
                return;
            }
            
            if ($day >= $today) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Нельзя пропустить текущий или будущий день'));
                return;
            }
            
            // Получаем данные награды
            $q = $db->query("SELECT * FROM calendar_days WHERE month = {$month} AND year = {$year} AND day = {$day} LIMIT 1");
            if (!$q || $q->num_rows == 0) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Награда не найдена'));
                return;
            }
            
            $reward = $q->fetch_assoc();
            
            // Формируем данные предмета
            $item_data = array(
                'item_id' => (int)$reward['item_id'],
                'count' => (int)$reward['item_count'],
                'client_size' => (int)$reward['client_size'],
                'max_count' => (int)$reward['max_count'],
                'data' => $reward['item_data'],
                'proctype' => (int)$reward['proctype'],
                'expire_date' => (int)$reward['expire_date']
            );
            
            // Получаем имя персонажа
            $character_name = getCharacterName($target_player);
            
            // Отправляем награду
            $send_result = CalendarSendItem($target_player, $item_data, $day, $month, $year);
            
            if ($send_result['errorcode'] !== 0) {
                echo json_encode(array('errorcode' => 1, 'message' => 'Ошибка отправки награды: ' . $send_result['message']));
                return;
            }
            
            // Обновляем прогресс
            $received[] = $day;
            $received_str = implode(',', $received);
            $new_resign_count = $progress['resign_count'] - 1;
            
            $db->query("UPDATE calendar_user_progress SET 
                        received_days = '{$received_str}',
                        resign_count = {$new_resign_count}
                        WHERE user_id = {$user_id} AND month = {$month} AND year = {$year}");
            
            echo json_encode(array(
                'errorcode' => 0,
                'message' => "✅ День пропущен, награда отправлена в игру!"
            ), JSON_UNESCAPED_UNICODE);
            break;
            
        default:
            echo json_encode(array('errorcode' => 1, 'message' => 'Неизвестное действие'));
    }
}
/**
 * Обработка админских запросов календаря
 */
function CalendarAdminDayBonus() {
    global $db;
    
    header('Content-Type: application/json; charset=utf-8');
    
    $admin_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $login = isset($_POST['login']) ? $_POST['login'] : '';
    $sub_action = isset($_POST['sub_action']) ? $_POST['sub_action'] : '';
    
    if (!$admin_id) {
        echo json_encode(['errorcode' => 1, 'message' => 'Не авторизован']);
        return;
    }
    
    // Логируем только успешные запросы или ошибки, но не каждый вход
    $start_time = microtime(true);
    
    switch ($sub_action) {
        
        // ===== ТЕСТ СОЕДИНЕНИЯ =====
        case 'test_connection':
            echo json_encode([
                'errorcode' => 0,
                'message' => 'Соединение работает',
                'time' => date('Y-m-d H:i:s')
            ]);
            break;
        
        // ===== ПОЛУЧЕНИЕ ВСЕХ КАЛЕНДАРЕЙ =====
        case 'get_all':
            
            // Получаем все уникальные месяцы/годы из calendar_days
            $days_q = $db->query("SELECT DISTINCT month, year FROM calendar_days ORDER BY year DESC, month DESC");
            
            $calendars = [];
            if ($days_q) {
                while ($row = $days_q->fetch_assoc()) {
                    $month = $row['month'];
                    $year = $row['year'];
                    
                    // Проверяем есть ли настройки для этого месяца
                    $settings_q = $db->query("SELECT * FROM calendar_settings WHERE month = {$month} AND year = {$year} LIMIT 1");
                    if ($settings_q && $settings_q->num_rows > 0) {
                        $settings = $settings_q->fetch_assoc();
                        $is_active = $settings['is_active'];
                    } else {
                        // Если нет настроек, создаем их по умолчанию (активные)
                        $db->query("INSERT IGNORE INTO calendar_settings (month, year, is_active) VALUES ({$month}, {$year}, 1)");
                        $is_active = 1;
                    }
                    
                    // Получаем количество дней с наградами
                    $count_q = $db->query("SELECT COUNT(*) as cnt FROM calendar_days WHERE month = {$month} AND year = {$year}");
                    $count = $count_q->fetch_assoc();
                    
                    $calendars[] = [
                        'month' => $month,
                        'year' => $year,
                        'days_count' => (int)$count['cnt'],
                        'is_active' => (int)$is_active
                    ];
                }
            }
            
            // Если нет ни одного календаря, создаем тестовый для текущего месяца
            if (empty($calendars)) {
                $month = (int)date('n');
                $year = (int)date('Y');
                
                // Создаем настройки
                $db->query("INSERT IGNORE INTO calendar_settings (month, year, is_active) VALUES ({$month}, {$year}, 1)");
                
                // Проверяем есть ли дни
                $count_q = $db->query("SELECT COUNT(*) as cnt FROM calendar_days WHERE month = {$month} AND year = {$year}");
                $count = $count_q->fetch_assoc();
                
                $calendars[] = [
                    'month' => $month,
                    'year' => $year,
                    'days_count' => (int)$count['cnt'],
                    'is_active' => 1
                ];
            }
            
            calendarLog('admin_get_all', $admin_id, $login, ['count' => count($calendars)], 'success');
            
            echo json_encode([
                'errorcode' => 0,
                'calendars' => $calendars
            ]);
            break;
        
        // ===== СОХРАНЕНИЕ НАСТРОЕК КАЛЕНДАРЯ =====
        case 'save_settings':
            
            $month = isset($_POST['month']) ? (int)$_POST['month'] : 0;
            $year = isset($_POST['year']) ? (int)$_POST['year'] : 0;
            $is_active = isset($_POST['is_active']) ? (int)$_POST['is_active'] : 1;
            
            if (!$month || !$year) {
                echo json_encode(['errorcode' => 1, 'message' => 'Не указаны месяц и год']);
                return;
            }
            
            // Проверяем существование
            $q = $db->query("SELECT id FROM calendar_settings WHERE month = {$month} AND year = {$year} LIMIT 1");
            if ($q && $q->num_rows > 0) {
                // Обновляем
                $db->query("UPDATE calendar_settings SET is_active = {$is_active} WHERE month = {$month} AND year = {$year}");
                $action_type = 'update';
            } else {
                // Создаем
                $db->query("INSERT INTO calendar_settings (month, year, is_active) VALUES ({$month}, {$year}, {$is_active})");
                $action_type = 'create';
            }
            
            calendarLog('admin_save_settings', $admin_id, $login, [
                'month' => $month,
                'year' => $year,
                'is_active' => $is_active,
                'action' => $action_type
            ], 'success');
            
            echo json_encode([
                'errorcode' => 0,
                'message' => 'Настройки сохранены',
                'month' => $month,
                'year' => $year
            ]);
            break;
        
        // ===== ПОЛУЧЕНИЕ ДАННЫХ ДЛЯ РЕДАКТИРОВАНИЯ =====
        case 'get_edit_data':
            
            $month = isset($_POST['month']) ? (int)$_POST['month'] : 0;
            $year = isset($_POST['year']) ? (int)$_POST['year'] : 0;
            
            if (!$month || !$year) {
                echo json_encode(['errorcode' => 1, 'message' => 'Не указаны месяц и год']);
                return;
            }
            
            // Получаем настройки
            $settings = null;
            $q = $db->query("SELECT * FROM calendar_settings WHERE month = {$month} AND year = {$year} LIMIT 1");
            if ($q && $q->num_rows > 0) {
                $settings = $q->fetch_assoc();
            }
            
            // Получаем дни
            $days = [];
            $q = $db->query("SELECT * FROM calendar_days WHERE month = {$month} AND year = {$year} ORDER BY day");
            if ($q) {
                while ($row = $q->fetch_assoc()) {
                    $days[$row['day']] = [
                        'item_id' => (int)$row['item_id'],
                        'count' => (int)$row['item_count'],
                        'client_size' => (int)$row['client_size'],
                        'max_count' => (int)$row['max_count'],
                        'data' => $row['item_data'],
                        'proctype' => (int)$row['proctype'],
                        'expire_date' => (int)$row['expire_date'],
                        'is_special' => (bool)$row['is_special']
                    ];
                }
            }
            
            // Получаем статистику
            $stats = [];
            $stats_q = $db->query("SELECT 
                COUNT(DISTINCT user_id) as total_users,
                SUM(CASE WHEN status = 'success' AND action = 'user_claim' THEN 1 ELSE 0 END) as total_claims,
                SUM(CASE WHEN status = 'success' AND action = 'user_resign' THEN 1 ELSE 0 END) as total_resigns
                FROM calendar_logs 
                WHERE (request_data LIKE '%\"month\":{$month},\"year\":{$year}%' OR (day BETWEEN 1 AND 31 AND request_time LIKE '{$year}-{$month}%'))");
            if ($stats_q) {
                $stats = $stats_q->fetch_assoc();
            }
            
            calendarLog('admin_get_edit_data', $admin_id, $login, ['month' => $month, 'year' => $year], 'success');
            
            echo json_encode([
                'errorcode' => 0,
                'settings' => $settings,
                'days' => $days,
                'stats' => $stats,
                'days_in_month' => (int)date('t', strtotime("{$year}-{$month}-01"))
            ], JSON_UNESCAPED_UNICODE);
            break;
        
        // ===== СОХРАНЕНИЕ ДНЕЙ =====
        case 'save_days':
            
            $month = isset($_POST['month']) ? (int)$_POST['month'] : 0;
            $year = isset($_POST['year']) ? (int)$_POST['year'] : 0;
            $days_json = isset($_POST['days']) ? $_POST['days'] : '';
            
            if (!$month || !$year || !$days_json) {
                echo json_encode(['errorcode' => 1, 'message' => 'Не указаны обязательные параметры']);
                return;
            }
            
            $days = json_decode($days_json, true);
            if (!is_array($days)) {
                echo json_encode(['errorcode' => 1, 'message' => 'Неверный формат данных дней']);
                return;
            }
            
            // Удаляем старые записи
            $db->query("DELETE FROM calendar_days WHERE month = {$month} AND year = {$year}");
            
            // Добавляем новые
            $inserted = 0;
            foreach ($days as $day => $data) {
                $day_num = (int)$day;
                $item_id = isset($data['item_id']) ? (int)$data['item_id'] : 0;
                $item_count = isset($data['count']) ? (int)$data['count'] : 1;
                $client_size = isset($data['client_size']) ? (int)$data['client_size'] : 0;
                $max_count = isset($data['max_count']) ? (int)$data['max_count'] : $item_count;
                $item_data = isset($data['data']) ? $db->real_escape_string($data['data']) : '';
                $proctype = isset($data['proctype']) ? (int)$data['proctype'] : 0;
                $expire_date = isset($data['expire_date']) ? (int)$data['expire_date'] : 0;
                $is_special = isset($data['is_special']) ? (int)$data['is_special'] : 0;
                
                $sql = "INSERT INTO calendar_days 
                    (day, month, year, item_id, item_count, client_size, max_count, item_data, proctype, expire_date, is_special) 
                    VALUES (
                        {$day_num}, {$month}, {$year}, {$item_id}, {$item_count}, 
                        {$client_size}, {$max_count}, '{$item_data}', {$proctype}, {$expire_date}, {$is_special}
                    )";
                
                if ($db->query($sql)) {
                    $inserted++;
                } else {
                    error_log("Calendar save_days error: " . $db->error);
                }
            }
            
            calendarLog('admin_save_days', $admin_id, $login, [
                'month' => $month,
                'year' => $year,
                'days_saved' => $inserted
            ], 'success');
            
            echo json_encode([
                'errorcode' => 0,
                'message' => "Сохранено {$inserted} дней",
                'saved' => $inserted
            ]);
            break;
        
        // ===== ПОЛУЧЕНИЕ ЛОГОВ =====
        case 'get_logs':
    
    $month = isset($_POST['month']) ? (int)$_POST['month'] : 0;
    $year = isset($_POST['year']) ? (int)$_POST['year'] : 0;
    $limit = isset($_POST['limit']) ? (int)$_POST['limit'] : 100;
    $offset = isset($_POST['offset']) ? (int)$_POST['offset'] : 0;
    
    // Базовый запрос - ПОЛУЧАЕМ ВСЕ ЛОГИ
    $sql = "SELECT * FROM calendar_logs";
    $count_sql = "SELECT COUNT(*) as cnt FROM calendar_logs";
    
    $where_conditions = [];
    
    // Добавляем фильтр по месяцу/году если указаны
    if ($month > 0 && $year > 0) {
        $where_conditions[] = "(request_data LIKE '%\"month\":{$month},\"year\":{$year}%' 
                              OR request_data LIKE '%\"month\":{$month}%' 
                              OR request_data LIKE '%\"year\":{$year}%'
                              OR (request_time LIKE '{$year}-{$month}%')
                              OR (action = 'user_claim' AND day > 0))";
    }
    
    // Добавляем WHERE если есть условия
    if (!empty($where_conditions)) {
        $sql .= " WHERE " . implode(' AND ', $where_conditions);
        $count_sql .= " WHERE " . implode(' AND ', $where_conditions);
    }
    
    $sql .= " ORDER BY request_time DESC LIMIT {$offset}, {$limit}";
    
    // Получаем логи
    $logs = [];
    $q = $db->query($sql);
    if ($q) {
        while ($row = $q->fetch_assoc()) {
            // Декодируем JSON данные если нужно
            if (!empty($row['reward_data'])) {
                $row['reward_data'] = json_decode($row['reward_data'], true);
            }
            if (!empty($row['request_data'])) {
                $row['request_data'] = json_decode($row['request_data'], true);
            }
            $logs[] = $row;
        }
    }
    
    // Получаем общее количество
    $count_q = $db->query($count_sql);
    $total = $count_q ? $count_q->fetch_assoc()['cnt'] : 0;
    
    calendarLog('admin_get_logs', $admin_id, $login, [
        'month' => $month,
        'year' => $year,
        'returned' => count($logs),
        'total' => $total
    ], 'success');
    
    echo json_encode([
        'errorcode' => 0,
        'logs' => $logs,
        'total' => $total
    ], JSON_UNESCAPED_UNICODE);
    break;
        
        // ===== ОЧИСТКА ЛОГОВ =====
        case 'clear_logs':
    
    $days = isset($_POST['days']) ? (int)$_POST['days'] : 30;
    
    if ($days <= 0) {
        echo json_encode(['errorcode' => 1, 'message' => 'Неверное количество дней']);
        return;
    }
    
    // Рассчитываем дату
    $date_limit = date('Y-m-d H:i:s', strtotime("-{$days} days"));
    
    // Удаляем старые записи
    $sql = "DELETE FROM calendar_logs WHERE request_time < '{$date_limit}'";
    $result = $db->query($sql);
    
    if (!$result) {
        error_log("Calendar clear_logs error: " . $db->error);
        echo json_encode(['errorcode' => 1, 'message' => 'Ошибка при очистке логов']);
        return;
    }
    
    $deleted = $db->affected_rows;
    
    // Логируем очистку
    $ip = $_SERVER['REMOTE_ADDR'];
    $login_esc = $db->real_escape_string($login);
    $request_data = json_encode(['days' => $days, 'deleted' => $deleted], JSON_UNESCAPED_UNICODE);
    
    $log_sql = "INSERT INTO calendar_logs 
        (user_id, login, action, request_data, ip, status) 
        VALUES (
            {$admin_id}, 
            '{$login_esc}', 
            'admin_clear_logs', 
            '{$request_data}', 
            '{$ip}', 
            'success'
        )";
    $db->query($log_sql);
    
    echo json_encode([
        'errorcode' => 0,
        'message' => "Удалено {$deleted} старых записей"
    ]);
    break;
        
        // ===== СБРОС ПРОГРЕССА ПОЛЬЗОВАТЕЛЯ =====
        case 'reset_user':
            
            $target_user = isset($_POST['target_user']) ? (int)$_POST['target_user'] : 0;
            $month = isset($_POST['month']) ? (int)$_POST['month'] : (int)date('n');
            $year = isset($_POST['year']) ? (int)$_POST['year'] : (int)date('Y');
            
            if (!$target_user) {
                echo json_encode(['errorcode' => 1, 'message' => 'Не указан пользователь']);
                return;
            }
            
            $db->query("DELETE FROM calendar_user_progress WHERE user_id = {$target_user} AND month = {$month} AND year = {$year}");
            
            calendarLog('admin_reset_user', $admin_id, $login, [
                'target_user' => $target_user,
                'month' => $month,
                'year' => $year
            ], 'success');
            
            echo json_encode([
                'errorcode' => 0,
                'message' => 'Прогресс пользователя сброшен'
            ]);
            break;
        
        // ===== НЕИЗВЕСТНОЕ ДЕЙСТВИЕ =====
        default:
            $execution_time = microtime(true) - $start_time;
            calendarLog('admin_error', $admin_id, $login, [
                'action' => $sub_action,
                'error' => 'Unknown action',
                'time' => round($execution_time, 4)
            ], 'error');
            
            echo json_encode([
                'errorcode' => 1, 
                'message' => 'Неизвестное действие: ' . $sub_action
            ]);
    }
}

// ==========================================
// МОДУЛЬ МАРАФОНА КВЕСТОВ (AUTH.PHP) - PRODUCTION
// ==========================================

function getMarathonCharacterName($db, $roleid) {
    if (!$roleid) return 'Unknown';
    
    $queries = [
        "SELECT rolename FROM top WHERE roleid = $roleid LIMIT 1",
        "SELECT name FROM characters WHERE id = $roleid LIMIT 1",
        "SELECT nickname FROM characters WHERE id = $roleid LIMIT 1",
        "SELECT name FROM players WHERE id = $roleid LIMIT 1",
        "SELECT nickname FROM players WHERE id = $roleid LIMIT 1"
    ];
    
    foreach ($queries as $sql) {
        $res = @$db->query($sql);
        if ($res && $res->num_rows > 0) {
            $row = $res->fetch_assoc();
            $name = reset($row);
            if (!empty($name) && trim($name) !== '') return $name;
        }
    }
    
    return "ID:$roleid";
}

function MarathonAdminGetEvents() {
    global $db;
    $db->set_charset('utf8');
    $events = array();
    $res = $db->query("SELECT * FROM marathon_events ORDER BY id DESC");
    if ($res) {
        while ($row = $res->fetch_assoc()) {
            $events[] = $row;
        }
    }
    echo json_encode(array('errorcode' => 0, 'events' => $events), JSON_UNESCAPED_UNICODE);
    exit;
}

function MarathonAdminGetRewards() {
    global $db;
    $db->set_charset('utf8');
    $rewards = array();
    $res = $db->query("SELECT r.*, e.title as event_title, e.mode as event_mode FROM marathon_rewards r LEFT JOIN marathon_events e ON r.event_id = e.id ORDER BY r.event_id DESC, r.condition_value ASC");
    if ($res) {
        while ($row = $res->fetch_assoc()) {
            $rewards[] = $row;
        }
    }
    echo json_encode(array('errorcode' => 0, 'rewards' => $rewards), JSON_UNESCAPED_UNICODE);
    exit;
}

function MarathonAdminSaveEvent() {
    global $db;
    $db->set_charset('utf8');
    
    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    $title = isset($_POST['title']) ? trim($_POST['title']) : '';
    $description = isset($_POST['description']) ? trim($_POST['description']) : '';
    $start_date = isset($_POST['start_date']) ? $_POST['start_date'] : '';
    $end_date = isset($_POST['end_date']) ? $_POST['end_date'] : '';
    $mode = isset($_POST['mode']) ? $_POST['mode'] : 'fixed';
    $target_quest_id = isset($_POST['target_quest_id']) ? (int)$_POST['target_quest_id'] : 0;
    $excluded_quests = isset($_POST['excluded_quests']) ? trim($_POST['excluded_quests']) : '';
    $is_active = isset($_POST['is_active']) ? 1 : 0;
    
    if (empty($title) || empty($start_date) || empty($end_date)) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Заполните обязательные поля'), JSON_UNESCAPED_UNICODE);
        exit;
    }
    
    $title_esc = $db->real_escape_string($title);
    $desc_esc = $db->real_escape_string($description);
    $start_esc = $db->real_escape_string($start_date);
    $end_esc = $db->real_escape_string($end_date);
    $mode_esc = $db->real_escape_string($mode);
    $excl_esc = $db->real_escape_string($excluded_quests);
    
    if ($id > 0) {
        $query = "UPDATE marathon_events SET 
            title='$title_esc', description='$desc_esc', start_date='$start_esc', 
            end_date='$end_esc', mode='$mode_esc', target_quest_id=$target_quest_id, 
            excluded_quests='$excl_esc', is_active=$is_active WHERE id=$id";
    } else {
        $query = "INSERT INTO marathon_events 
            (title, description, start_date, end_date, mode, target_quest_id, excluded_quests, is_active) 
            VALUES ('$title_esc', '$desc_esc', '$start_esc', '$end_esc', '$mode_esc', 
            $target_quest_id, '$excl_esc', $is_active)";
    }
    
    if ($db->query($query)) {
        echo json_encode(array('errorcode' => 0, 'message' => 'Марафон сохранен', 'id' => $id > 0 ? $id : $db->insert_id), JSON_UNESCAPED_UNICODE);
    } else {
        echo json_encode(array('errorcode' => 1, 'message' => 'Ошибка БД: ' . $db->error), JSON_UNESCAPED_UNICODE);
    }
    exit;
}

function MarathonAdminSaveReward() {
    global $db, $Structures;
    $db->set_charset('utf8');
    
    $event_id = isset($_POST['event_id']) ? (int)$_POST['event_id'] : 0;
    $condition_type = isset($_POST['condition_type']) ? $_POST['condition_type'] : 'count';
    $delivery_type = isset($_POST['delivery_type']) ? $_POST['delivery_type'] : 'mail';
    
    if ($condition_type === 'rank') {
        $min = isset($_POST['rank_min']) ? (int)$_POST['rank_min'] : 1;
        $max = isset($_POST['rank_max']) ? (int)$_POST['rank_max'] : $min;
        if ($max < $min) $max = $min;
        $condition_value = $min . '-' . $max;
    } else {
        $condition_value = (string)(isset($_POST['condition_value']) ? (int)$_POST['condition_value'] : 0);
    }
    
    $item_data = array(
        'id' => isset($_POST['item_id']) ? (int)$_POST['item_id'] : 0,
        'count' => isset($_POST['count']) ? (int)$_POST['count'] : 1,
        'client_size' => isset($_POST['client_size']) ? (int)$_POST['client_size'] : 0,
        'max_count' => isset($_POST['max_count']) ? (int)$_POST['max_count'] : 1,
        'data' => isset($_POST['data']) ? $_POST['data'] : '',
        'proctype' => isset($_POST['proctype']) ? (int)$_POST['proctype'] : 0,
        'expire_date' => isset($_POST['expire_date']) ? (int)$_POST['expire_date'] : 0,
    );
    
    $item_hex = '';
    if ($item_data['id'] > 0 && isset($Structures['GRoleInventory'])) {
        try {
            $pp = new Protocols();
            $item_hex = strtoupper(bin2hex($pp->marshal($item_data, $Structures['GRoleInventory'])));
        } catch (Exception $e) {}
    }
    
    $item_data_json = json_encode($item_data, JSON_UNESCAPED_UNICODE);
    $json_esc = $db->real_escape_string($item_data_json);
    $hex_esc = $db->real_escape_string($item_hex);
    $ct_esc = $db->real_escape_string($condition_type);
    $cv_esc = $db->real_escape_string($condition_value);
    $dt_esc = $db->real_escape_string($delivery_type);
    
    if ($event_id > 0 && $condition_value !== '0') {
        $query = "INSERT INTO marathon_rewards 
            (event_id, condition_type, condition_value, delivery_type, item_data_json, item_hex) 
            VALUES ($event_id, '$ct_esc', '$cv_esc', '$dt_esc', '$json_esc', '$hex_esc')";
        
        if ($db->query($query)) {
            echo json_encode(array('errorcode' => 0, 'message' => 'Награда добавлена', 'id' => $db->insert_id), JSON_UNESCAPED_UNICODE);
        } else {
            echo json_encode(array('errorcode' => 1, 'message' => 'Ошибка БД: ' . $db->error), JSON_UNESCAPED_UNICODE);
        }
    } else {
        echo json_encode(array('errorcode' => 1, 'message' => 'Ошибка: не выбран марафон или условие'), JSON_UNESCAPED_UNICODE);
    }
    exit;
}

function MarathonAdminDeleteEvent() {
    global $db;
    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    if ($id > 0) {
        $db->query("DELETE FROM marathon_rewards WHERE event_id = $id");
        $db->query("DELETE FROM marathon_events WHERE id = $id");
        echo json_encode(array('errorcode' => 0, 'message' => 'Удалено'), JSON_UNESCAPED_UNICODE);
    } else {
        echo json_encode(array('errorcode' => 1, 'message' => 'Неверный ID'), JSON_UNESCAPED_UNICODE);
    }
    exit;
}

function MarathonAdminDeleteReward() {
    global $db;
    $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
    if ($id > 0) {
        $db->query("DELETE FROM marathon_rewards WHERE id = $id");
        echo json_encode(array('errorcode' => 0, 'message' => 'Удалено'), JSON_UNESCAPED_UNICODE);
    } else {
        echo json_encode(array('errorcode' => 1, 'message' => 'Неверный ID'), JSON_UNESCAPED_UNICODE);
    }
    exit;
}

function MarathonGetData() {
    global $db;
    $db->set_charset('utf8');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    if (!$user_id) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Не авторизован'));
        exit;
    }

    $now = date('Y-m-d H:i:s');
    $q = $db->query("SELECT * FROM marathon_events WHERE is_active = 1 AND start_date <= '$now' AND end_date >= '$now' ORDER BY id DESC");
    
    if (!$q || $q->num_rows == 0) {
        echo json_encode(array('errorcode' => 0, 'active' => false, 'message' => 'Активных марафонов нет'));
        exit;
    }
    
    $events = array();
    while ($event = $q->fetch_assoc()) {
        $event_id = $event['id'];
        $rewards_q = $db->query("SELECT * FROM marathon_rewards WHERE event_id = {$event_id} ORDER BY condition_value ASC");
        $rewards = array();
        if ($rewards_q) {
            while ($r = $rewards_q->fetch_assoc()) {
                $item_preview = array('item_id' => 0, 'count' => 1);
                $decoded = json_decode($r['item_data_json'], true);
                if (is_array($decoded) && isset($decoded['id'])) {
                    $item_preview = array(
                        'item_id' => (int)$decoded['id'],
                        'count' => isset($decoded['count']) ? (int)$decoded['count'] : 1
                    );
                }
                $rewards[] = array(
                    'id' => $r['id'],
                    'condition_type' => $r['condition_type'],
                    'condition_value' => $r['condition_value'],
                    'delivery_type' => $r['delivery_type'],
                    'item_preview' => $item_preview
                );
            }
        }
        $events[] = array(
            'id' => $event['id'],
            'title' => $event['title'],
            'description' => $event['description'],
            'start_date' => $event['start_date'],
            'end_date' => $event['end_date'],
            'mode' => $event['mode'],
            'rewards' => $rewards
        );
    }
    
    echo json_encode(array('errorcode' => 0, 'active' => true, 'events' => $events), JSON_UNESCAPED_UNICODE);
    exit;
}

function MarathonGetPlayerStats() {
    global $db;
    $db->set_charset('utf8');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $player_id = isset($_POST['player_id']) ? (int)$_POST['player_id'] : 0;
    $event_id = isset($_POST['event_id']) ? (int)$_POST['event_id'] : 0;
    
    if (!$user_id || !$player_id || !$event_id) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Неверные параметры'));
        exit;
    }
    
    $event_q = $db->query("SELECT * FROM marathon_events WHERE id = {$event_id} LIMIT 1");
    if (!$event_q || $event_q->num_rows == 0) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Марафон не найден'));
        exit;
    }
    $event = $event_q->fetch_assoc();
    
    $quest_filter = $event['target_quest_id'] > 0 ? "AND id_quest = {$event['target_quest_id']}" : "";
    $exclude_filter = "";
    if (!empty($event['excluded_quests'])) {
        $ex = array_map('intval', explode(',', $event['excluded_quests']));
        $exclude_filter = "AND id_quest NOT IN (" . implode(',', $ex) . ")";
    }
    
    $player_count = 0;
    $progress_q = $db->query("SELECT COUNT(*) as cnt FROM task_log 
                              WHERE id_player = {$player_id} 
                              AND date >= '{$event['start_date']}' AND date <= '{$event['end_date']}' 
                              {$quest_filter} {$exclude_filter}");
    if ($progress_q) {
        $row = $progress_q->fetch_assoc();
        $player_count = (int)$row['cnt'];
    }

    $player_rank = 0;
    if ($event['mode'] == 'top') {
        $rank_q = $db->query("SELECT COUNT(*) + 1 as rank FROM (
            SELECT id_player, COUNT(*) as cnt 
            FROM task_log 
            WHERE date >= '{$event['start_date']}' AND date <= '{$event['end_date']}' 
            {$quest_filter} {$exclude_filter}
            GROUP BY id_player 
            HAVING cnt > {$player_count}
        ) as t");
        if ($rank_q) {
            $row = $rank_q->fetch_assoc();
            $player_rank = (int)$row['rank'];
        }
    }
    
    $rewards_q = $db->query("SELECT * FROM marathon_rewards WHERE event_id = {$event_id} ORDER BY condition_value ASC");
    $rewards = array();
    if ($rewards_q) {
        while ($r = $rewards_q->fetch_assoc()) {
            $claim_q = $db->query("SELECT id FROM marathon_claims WHERE reward_id = {$r['id']} AND id_player = {$user_id} LIMIT 1");
            $is_claimed = ($claim_q && $claim_q->num_rows > 0);
            
            $can_claim = false;
            if (!$is_claimed) {
                if ($r['condition_type'] == 'count' && $player_count >= (int)$r['condition_value']) {
                    $can_claim = true;
                } elseif ($r['condition_type'] == 'rank' && $player_rank > 0) {
                    $range = explode('-', $r['condition_value']);
                    $min = (int)$range[0];
                    $max = isset($range[1]) ? (int)$range[1] : $min;
                    if ($player_rank >= $min && $player_rank <= $max) {
                        $can_claim = true;
                    }
                }
            }
            
            $item_preview = array('item_id' => 0, 'count' => 1);
            $decoded = json_decode($r['item_data_json'], true);
            if (is_array($decoded) && isset($decoded['id'])) {
                $item_preview = array(
                    'item_id' => (int)$decoded['id'],
                    'count' => isset($decoded['count']) ? (int)$decoded['count'] : 1
                );
            }
            
            $rewards[] = array(
                'id' => $r['id'],
                'condition_type' => $r['condition_type'],
                'condition_value' => $r['condition_value'],
                'delivery_type' => $r['delivery_type'],
                'item_preview' => $item_preview,
                'is_claimed' => $is_claimed,
                'can_claim' => $can_claim
            );
        }
    }
    
    echo json_encode(array(
        'errorcode' => 0,
        'stats' => array('count' => $player_count, 'rank' => $player_rank),
        'rewards' => $rewards
    ), JSON_UNESCAPED_UNICODE);
    exit;
}

function MarathonGetTopStats() {
    global $db;
    $db->set_charset('utf8');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $event_id = isset($_POST['event_id']) ? (int)$_POST['event_id'] : 0;
    
    if (!$user_id || !$event_id) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Неверные параметры'));
        exit;
    }
    
    $event_q = $db->query("SELECT * FROM marathon_events WHERE id = {$event_id} LIMIT 1");
    if (!$event_q || $event_q->num_rows == 0) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Марафон не найден'));
        exit;
    }
    $event = $event_q->fetch_assoc();
    
    $quest_filter = $event['target_quest_id'] > 0 ? "AND id_quest = {$event['target_quest_id']}" : "";
    $exclude_filter = "";
    if (!empty($event['excluded_quests'])) {
        $ex = array_map('intval', explode(',', $event['excluded_quests']));
        $exclude_filter = "AND id_quest NOT IN (" . implode(',', $ex) . ")";
    }
    
    $top_q = $db->query("SELECT id_player, COUNT(*) as cnt 
                         FROM task_log 
                         WHERE date >= '{$event['start_date']}' AND date <= '{$event['end_date']}' 
                         {$quest_filter} {$exclude_filter}
                         GROUP BY id_player 
                         ORDER BY cnt DESC 
                         LIMIT 6");
    
    $top = array();
    if ($top_q) {
        while ($row = $top_q->fetch_assoc()) {
            $top[] = array(
                'player_id' => $row['id_player'],
                'name' => getMarathonCharacterName($db, $row['id_player']),
                'count' => (int)$row['cnt']
            );
        }
    }
    
    echo json_encode(array('errorcode' => 0, 'top' => $top), JSON_UNESCAPED_UNICODE);
    exit;
}

function MarathonGetFullTop() {
    global $db;
    $db->set_charset('utf8');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $event_id = isset($_POST['event_id']) ? (int)$_POST['event_id'] : 0;
    
    if (!$user_id || !$event_id) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Неверные параметры'));
        exit;
    }
    
    $event_q = $db->query("SELECT * FROM marathon_events WHERE id = {$event_id} LIMIT 1");
    if (!$event_q || $event_q->num_rows == 0) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Марафон не найден'));
        exit;
    }
    $event = $event_q->fetch_assoc();
    
    $quest_filter = $event['target_quest_id'] > 0 ? "AND id_quest = {$event['target_quest_id']}" : "";
    $exclude_filter = "";
    if (!empty($event['excluded_quests'])) {
        $ex = array_map('intval', explode(',', $event['excluded_quests']));
        $exclude_filter = "AND id_quest NOT IN (" . implode(',', $ex) . ")";
    }
    
    $top_q = $db->query("SELECT l.id_player, t.rolename, COUNT(l.id) as cnt 
                         FROM task_log l
                         LEFT JOIN top t ON l.id_player = t.roleid
                         WHERE l.date >= '{$event['start_date']}' AND l.date <= '{$event['end_date']}' 
                         {$quest_filter} {$exclude_filter}
                         GROUP BY l.id_player 
                         ORDER BY cnt DESC 
                         LIMIT 100");
    
    $top = array();
    if ($top_q) {
        while ($row = $top_q->fetch_assoc()) {
            $name = !empty($row['rolename']) ? $row['rolename'] : "ID:" . $row['id_player'];
            $top[] = array(
                'player_id' => $row['id_player'],
                'name' => $name,
                'count' => (int)$row['cnt']
            );
        }
    }
    
    echo json_encode(array('errorcode' => 0, 'top' => $top), JSON_UNESCAPED_UNICODE);
    exit;
}

function MarathonClaimReward() {
    global $db, $Structures;
    $db->set_charset('utf8');
    
    $user_id = isset($_POST['user_id']) ? (int)$_POST['user_id'] : 0;
    $reward_id = isset($_POST['reward_id']) ? (int)$_POST['reward_id'] : 0;
    $target_player = isset($_POST['target_player']) ? (int)$_POST['target_player'] : 0;
    
    if (!$user_id || !$reward_id || !$target_player) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Ошибка параметров'));
        exit;
    }

    $owner_check = $db->query("SELECT userid FROM top WHERE roleid = {$target_player} LIMIT 1");
    $is_owner = false;
    if ($owner_check && $owner_check->num_rows > 0) {
        $owner_data = $owner_check->fetch_assoc();
        if ($owner_data['userid'] == $user_id) {
            $is_owner = true;
        }
    }
    
    // Если персонаж существует в базе и принадлежит другому аккаунту — блокируем
    if ($owner_check && $owner_check->num_rows > 0 && !$is_owner) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Персонаж не принадлежит вашему аккаунту'));
        exit;
    }

    $q = $db->query("SELECT r.*, e.start_date, e.end_date, e.target_quest_id, e.excluded_quests, e.title as event_title, e.mode 
                     FROM marathon_rewards r JOIN marathon_events e ON r.event_id = e.id WHERE r.id = {$reward_id} LIMIT 1");
    if (!$q || $q->num_rows == 0) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Награда не найдена'));
        exit;
    }
    $reward = $q->fetch_assoc();
    
    $check_q = $db->query("SELECT id FROM marathon_claims WHERE reward_id = {$reward_id} AND id_player = {$user_id} LIMIT 1");
    if ($check_q && $check_q->num_rows > 0) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Уже получено'));
        exit;
    }

    $quest_filter = $reward['target_quest_id'] > 0 ? "AND id_quest = {$reward['target_quest_id']}" : "";
    $exclude_filter = "";
    if (!empty($reward['excluded_quests'])) {
        $ex = array_map('intval', explode(',', $reward['excluded_quests']));
        $exclude_filter = "AND id_quest NOT IN (" . implode(',', $ex) . ")";
    }
    
    $player_count = 0;
    $pq = $db->query("SELECT COUNT(*) as cnt FROM task_log 
                      WHERE id_player = {$target_player} 
                      AND date >= '{$reward['start_date']}' AND date <= '{$reward['end_date']}' 
                      {$quest_filter} {$exclude_filter}");
    if ($pq) {
        $row = $pq->fetch_assoc();
        $player_count = (int)$row['cnt'];
    }

    $player_rank = 0;
    if ($reward['mode'] == 'top') {
        $rq = $db->query("SELECT COUNT(*) + 1 as rank FROM (
            SELECT id_player, COUNT(*) as cnt 
            FROM task_log 
            WHERE date >= '{$reward['start_date']}' AND date <= '{$reward['end_date']}' 
            {$quest_filter} {$exclude_filter}
            GROUP BY id_player 
            HAVING cnt > {$player_count}
        ) as t");
        if ($rq) {
            $row = $rq->fetch_assoc();
            $player_rank = (int)$row['rank'];
        }
    }

    $ok = false;
    if ($reward['condition_type'] == 'count' && $player_count >= (int)$reward['condition_value']) {
        $ok = true;
    }
    if ($reward['condition_type'] == 'rank' && $player_rank > 0) {
        $range = explode('-', $reward['condition_value']);
        $min = (int)$range[0];
        $max = isset($range[1]) ? (int)$range[1] : $min;
        if ($player_rank >= $min && $player_rank <= $max) {
            $ok = true;
        }
    }

    if (!$ok) {
        echo json_encode(array('errorcode' => 1, 'message' => 'Условия не выполнены'));
        exit;
    }

    $db->query("INSERT INTO marathon_claims (event_id, reward_id, id_player) VALUES ({$reward['event_id']}, {$reward_id}, {$user_id})");

    if ($reward['delivery_type'] == 'mail') {
        $item_data = json_decode($reward['item_data_json'], true);
        if (!$item_data || !isset($item_data['id'])) {
            echo json_encode(array('errorcode' => 1, 'message' => 'Ошибка данных предмета'));
            exit;
        }
        
        if (function_exists('InitItem')) {
            $item = InitItem();
        } else {
            $item = array();
        }
        
        $item['id'] = (int)$item_data['id'];
        $item['count'] = isset($item_data['count']) ? (int)$item_data['count'] : 1;
        $item['max_count'] = isset($item_data['max_count']) ? (int)$item_data['max_count'] : 1;
        $item['client_size'] = isset($item_data['client_size']) ? (int)$item_data['client_size'] : 0;
        $item['pos'] = 0;
        $item['guid1'] = 0;
        $item['guid2'] = 0;
        $item['proctype'] = isset($item_data['proctype']) ? (int)$item_data['proctype'] : 0;
        $item['expire_date'] = isset($item_data['expire_date']) ? (int)$item_data['expire_date'] : 0;
        
        $raw_data = isset($item_data['data']) ? $item_data['data'] : '';
        if (!empty($raw_data) && is_string($raw_data) && ctype_xdigit(str_replace(' ', '', $raw_data))) {
            $binary_data = @hex2bin(str_replace(' ', '', $raw_data));
            if ($binary_data !== false && isset($Structures['Octets'])) {
                $pp = new Protocols();
                $item['data'] = $pp->marshal($binary_data, $Structures['Octets']);
            } else {
                $item['data'] = $raw_data;
            }
        } else {
            $item['data'] = $raw_data;
        }
        
        $mailTitle = "Награда Марафона";
        $context = "Поздравляем с успехом в марафоне: {$reward['event_title']}!";
        
        $success = SysSendMail(0, 32, 3, $target_player, $mailTitle, $context, $item, 0);
        
        if ($success) {
            echo json_encode(array('errorcode' => 0, 'message' => 'Отправлено на почту', 'delivery' => 'mail'));
        } else {
            echo json_encode(array('errorcode' => 1, 'message' => 'Ошибка отправки SysSendMail'));
        }
    } else {
        echo json_encode(array('errorcode' => 0, 'message' => 'Добавлено в тайник', 'delivery' => 'web_tainik', 'item_hex' => $reward['item_hex'], 'event_title' => $reward['event_title']));
    }
    exit;
}

function pvp_events() {
    global $db;
    $subop = isset($_POST['subop']) ? $_POST['subop'] : '';
    $is_public = in_array($subop, ['list_public', 'participants']); // JSON для публичных
    $is_json = $is_public || isset($_POST['format']) && $_POST['format'] == 'json'; // Или по параметру
    if ($is_json) {
        header('Content-Type: application/json; charset=utf-8');
    }
    $db->set_charset("utf8"); // UTF-8 для БД
    switch ($subop) {
        case 'list':
    $sql = "SELECT * FROM pvp_events ORDER BY start_date DESC";
    $res = $db->query($sql);
    if (!$res) {
        if ($is_json) {
            echo json_encode(array('status' => 'error', 'message' => $db->error));
        } else {
            echo serialize(array('errorcode' => 1, 'errortxt' => $db->error));
        }
        return;
    }
    $events = array();
    while ($row = $res->fetch_assoc()) {
        // Поскольку таблица pvp_events теперь в utf8mb4, конвертация не нужна
        // if (isset($row['name'])) $row['name'] = mb_convert_encoding($row['name'], 'UTF-8', 'cp1251');
        // if (isset($row['description'])) $row['description'] = mb_convert_encoding($row['description'], 'UTF-8', 'cp1251');
        $events[] = $row;
    }
    $response = array('events' => $events);
    if ($is_json) {
        echo json_encode(array('status' => 'success') + $response);
    } else {
        echo serialize(array('errorcode' => 0) + $response);
    }
    break;
	
	case 'list_public':
    // ФИКС: Переключение charset перед SELECT из pvp_events (если таблица в CP1251)
    $db->query("SET NAMES cp1251 COLLATE cp1251_general_ci");
    $res = $db->query("SELECT * FROM pvp_events ORDER BY start_date DESC");
    // Вернуть charset обратно
    $db->query("SET NAMES utf8");
    
    if (!$res) {
        if ($is_json) {
            echo json_encode(array('status' => 'error', 'message' => $db->error));
        } else {
            echo serialize(array('errorcode' => 1, 'errortxt' => $db->error));
        }
        break;
    }
    
    $events = array();
    while ($row = $res->fetch_assoc()) {
        // Конвертация полей с именами/описаниями из CP1251 в UTF-8
        if (isset($row['name'])) $row['name'] = mb_convert_encoding($row['name'], 'UTF-8', 'CP1251');
        if (isset($row['description'])) $row['description'] = mb_convert_encoding($row['description'], 'UTF-8', 'CP1251');
        $events[] = $row;
    }
    
    $response = array('status' => 'success', 'events' => $events);
    if ($is_json) {
        echo json_encode($response);
    } else {
        echo serialize(array('errorcode' => 0) + $response);
    }
    break;

        case 'create':
    $location = intval($_POST['location']);
    $gs_id = intval($_POST['gs_id']);
    $start_date = isset($_POST['start_date']) ? $db->real_escape_string($_POST['start_date']) : '';
    $end_date = isset($_POST['end_date']) ? $db->real_escape_string($_POST['end_date']) : '';
    $name = isset($_POST['name']) ? $db->real_escape_string($_POST['name']) : '';
    $description = isset($_POST['description']) ? $db->real_escape_string($_POST['description']) : '';
    $rewards = isset($_POST['rewards']) ? $db->real_escape_string($_POST['rewards']) : '';
    
    $sql = "INSERT INTO pvp_events (location, gs_id, start_date, end_date, name, description, rewards) 
            VALUES ($location, $gs_id, '$start_date', '$end_date', '$name', '$description', '$rewards')";
    
    error_log("SQL Create: " . $sql);
    
    if ($db->query($sql)) {
        if ($is_json) {
            echo json_encode(array('status' => 'success', 'id' => $db->insert_id, 'message' => 'Событие создано'));
        } else {
            echo serialize(array('errorcode' => 0, 'id' => $db->insert_id, 'errortxt' => 'Событие создано'));
        }
    } else {
        error_log("SQL Error: " . $db->error);
        if ($is_json) {
            echo json_encode(array('status' => 'error', 'message' => $db->error));
        } else {
            echo serialize(array('errorcode' => 1, 'errortxt' => $db->error));
        }
    }
    break;

case 'update':
    $id = intval(isset($_POST['id']) ? $_POST['id'] : (isset($_POST['event_id']) ? $_POST['event_id'] : 0));
    $location = intval($_POST['location']);
    $gs_id = intval($_POST['gs_id']);
    $start_date = isset($_POST['start_date']) ? $db->real_escape_string($_POST['start_date']) : '';
    $end_date = isset($_POST['end_date']) ? $db->real_escape_string($_POST['end_date']) : '';
    $name = isset($_POST['name']) ? $db->real_escape_string($_POST['name']) : '';
    $description = isset($_POST['description']) ? $db->real_escape_string($_POST['description']) : '';
    $rewards = isset($_POST['rewards']) ? $db->real_escape_string($_POST['rewards']) : '';
    
    $sql = "UPDATE pvp_events SET 
            location = $location, 
            gs_id = $gs_id, 
            start_date = '$start_date', 
            end_date = '$end_date', 
            name = '$name', 
            description = '$description',
            rewards = '$rewards'
            WHERE id = $id";
    
    error_log("SQL Update: " . $sql);
    
    if ($db->query($sql)) {
        if ($is_json) {
            echo json_encode(array('status' => 'success', 'message' => 'Событие обновлено'));
        } else {
            echo serialize(array('errorcode' => 0, 'errortxt' => 'Событие обновлено'));
        }
    } else {
        error_log("SQL Error: " . $db->error);
        if ($is_json) {
            echo json_encode(array('status' => 'error', 'message' => $db->error));
        } else {
            echo serialize(array('errorcode' => 1, 'errortxt' => $db->error));
        }
    }
    break;

        case 'delete':
            $id = intval($_POST['id']);
            $sql = "DELETE FROM pvp_events WHERE id=$id";
            if ($db->query($sql)) {
                if ($is_json) {
                    echo json_encode(array('status' => 'success', 'message' => 'Событие удалено'));
                } else {
                    echo serialize(array('errorcode' => 0, 'errortxt' => 'Событие удалено'));
                }
            } else {
                if ($is_json) {
                    echo json_encode(array('status' => 'error', 'message' => $db->error));
                } else {
                    echo serialize(array('errorcode' => 1, 'errortxt' => $db->error));
                }
            }
            break;

        case 'stats':
    $id = intval($_POST['event_id']);
    $event_res = $db->query("SELECT * FROM pvp_events WHERE id=$id");
    if (!$event_res) {
        if ($is_json) {
            echo json_encode(array('status' => 'error', 'message' => $db->error));
        } else {
            echo serialize(array('errorcode' => 1, 'errortxt' => $db->error));
        }
        return;
    }
    $event = $event_res->fetch_assoc();
    if (!$event) {
        if ($is_json) {
            echo json_encode(array('status' => 'error', 'message' => 'Событие не найдено'));
        } else {
            echo serialize(array('errorcode' => 1, 'errortxt' => 'Событие не найдено'));
        }
        break;
    }
    // Конвертация event
    if (isset($event['name'])) $event['name'] = mb_convert_encoding($event['name'], 'UTF-8', 'CP1251');
    if (isset($event['description'])) $event['description'] = mb_convert_encoding($event['description'], 'UTF-8', 'CP1251');
    
    $sql = "SELECT attacker_id, victim_id, timestamp, location, gs_id FROM pvp_kills WHERE timestamp >= '{$event['start_date']}' AND timestamp <= '{$event['end_date']}' AND location = {$event['location']} AND gs_id = {$event['gs_id']} ORDER BY timestamp DESC LIMIT 1000";
    $res = $db->query($sql);
    if (!$res) {
        if ($is_json) {
            echo json_encode(array('status' => 'error', 'message' => $db->error));
        } else {
            echo serialize(array('errorcode' => 1, 'errortxt' => $db->error));
        }
        return;
    }
    $kills = array();
    $attacker_stats = array();
    $victim_stats = array();
    while ($row = $res->fetch_assoc()) {
        $kills[] = $row;
        $attacker_id = $row['attacker_id'];
        $victim_id = $row['victim_id'];
        $attacker_stats[$attacker_id] = isset($attacker_stats[$attacker_id]) ? $attacker_stats[$attacker_id] + 1 : 1;
        $victim_stats[$victim_id] = isset($victim_stats[$victim_id]) ? $victim_stats[$victim_id] + 1 : 1;
    }
    arsort($attacker_stats);
    arsort($victim_stats);
    $top_killers_mapped = array();
    $top_victims_mapped = array();
    $player_ids = array_unique(array_merge(array_keys($attacker_stats), array_keys($victim_stats)));
    $players = array();
    if (!empty($player_ids)) {
        $ids_str = implode(',', $player_ids);
        // ФИКС: Переключение charset перед SELECT из top
        $db->query("SET NAMES cp1251 COLLATE cp1251_general_ci");
        $p_res = $db->query("SELECT roleid as player, rolename as name FROM top WHERE roleid IN ($ids_str)");
        // Вернуть charset обратно
        $db->query("SET NAMES utf8");
        
        if ($p_res) {
            while ($p_row = $p_res->fetch_assoc()) {
                // Конвертация имени из CP1251 в UTF-8
                $p_row['name'] = mb_convert_encoding($p_row['name'], 'UTF-8', 'CP1251');
                $players[$p_row['player']] = $p_row['name'];
            }
        }
    }
    $count = 0;
    foreach ($attacker_stats as $id => $kills_count) {
        if ($count >= 10) break;
        $name = isset($players[$id]) ? $players[$id] : 'Unknown';
        $top_killers_mapped[] = array('id' => $id, 'name' => $name, 'kills' => $kills_count);
        $count++;
    }
    $count = 0;
    foreach ($victim_stats as $id => $deaths_count) {
        if ($count >= 10) break;
        $name = isset($players[$id]) ? $players[$id] : 'Unknown';
        $top_victims_mapped[] = array('id' => $id, 'name' => $name, 'deaths' => $deaths_count);
        $count++;
    }
    $response = array(
        'event' => $event,
        'total_kills' => count($kills),
        'top_killers' => $top_killers_mapped,
        'top_victims' => $top_victims_mapped
    );
    if ($is_json) {
        echo json_encode(array('status' => 'success') + $response);
    } else {
        echo serialize(array('errorcode' => 0) + $response);
    }
    break;

        case 'participants':
    $id = intval($_POST['event_id']);
    
    // Проверяем структуру таблицы
    $columns_res = $db->query("SHOW COLUMNS FROM pvp_events LIKE 'rewards'");
    $has_rewards = $columns_res && $columns_res->num_rows > 0;
    
    if (!$has_rewards) {
        error_log("ВНИМАНИЕ: В таблице pvp_events нет колонки rewards");
    }
    
    // Получаем событие
    $event_res = $db->query("SELECT * FROM pvp_events WHERE id = $id");
    if (!$event_res) {
        echo json_encode(array('status' => 'error', 'message' => 'Ошибка запроса события: ' . $db->error));
        break;
    }
    $event = $event_res->fetch_assoc();
    if (!$event) {
        echo json_encode(array('status' => 'error', 'message' => 'Событие не найдено'));
        break;
    }
    
    // Добавляем поле rewards если его нет
    if (!isset($event['rewards'])) {
        $event['rewards'] = '';
    }
    
    // Отладка: логируем награды
    error_log("Награды для события #$id: " . $event['rewards']);

    // Получаем статистику по убийствам и смертям
    $sql_stats = "
        SELECT player_id, SUM(kills) as kills, SUM(deaths) as deaths FROM (
            SELECT attacker_id as player_id, COUNT(*) as kills, 0 as deaths 
            FROM pvp_kills 
            WHERE timestamp >= '{$event['start_date']}' 
                AND timestamp <= '{$event['end_date']}' 
                AND location = {$event['location']} 
                AND gs_id = {$event['gs_id']}
            GROUP BY attacker_id
            
            UNION ALL
            
            SELECT victim_id as player_id, 0 as kills, COUNT(*) as deaths 
            FROM pvp_kills 
            WHERE timestamp >= '{$event['start_date']}' 
                AND timestamp <= '{$event['end_date']}' 
                AND location = {$event['location']} 
                AND gs_id = {$event['gs_id']}
            GROUP BY victim_id
        ) combined
        GROUP BY player_id
    ";
    
    $stats_res = $db->query($sql_stats);
    $stats = array();
    if ($stats_res) {
        while ($stat_row = $stats_res->fetch_assoc()) {
            $stats[$stat_row['player_id']] = array(
                'kills' => intval($stat_row['kills']),
                'deaths' => intval($stat_row['deaths'])
            );
        }
    }

    // Получаем уникальных игроков
    $sql_unique = "SELECT DISTINCT attacker_id as player_id FROM pvp_kills 
                   WHERE timestamp >= '{$event['start_date']}' 
                   AND timestamp <= '{$event['end_date']}' 
                   AND location = {$event['location']} 
                   AND gs_id = {$event['gs_id']}
                   UNION
                   SELECT DISTINCT victim_id as player_id FROM pvp_kills 
                   WHERE timestamp >= '{$event['start_date']}' 
                   AND timestamp <= '{$event['end_date']}' 
                   AND location = {$event['location']} 
                   AND gs_id = {$event['gs_id']}";
    
    $unique_res = $db->query($sql_unique);
    $player_ids = array();
    while ($u_row = $unique_res->fetch_assoc()) {
        $player_ids[] = $u_row['player_id'];
    }
    
    $participants = array();
    if (!empty($player_ids)) {
        $ids_str = implode(',', array_unique($player_ids));
        
        $db->query("SET NAMES cp1251 COLLATE cp1251_general_ci");
        $p_res = $db->query("SELECT roleid, rolename, rolelevel, roleprof FROM top WHERE roleid IN ($ids_str)");
        
        if ($p_res) {
            while ($p_row = $p_res->fetch_assoc()) {
                $name = mb_convert_encoding($p_row['rolename'], 'UTF-8', 'CP1251');
                $class_name = GetOccupationName($p_row['roleprof']);
                
                $player_id = $p_row['roleid'];
                $kills = isset($stats[$player_id]['kills']) ? $stats[$player_id]['kills'] : 0;
                $deaths = isset($stats[$player_id]['deaths']) ? $stats[$player_id]['deaths'] : 0;
                
                $participants[] = array(
                    'id' => $player_id,
                    'name' => $name,
                    'level' => $p_row['rolelevel'],
                    'class' => $class_name,
                    'kills' => $kills,
                    'deaths' => $deaths
                );
            }
        }
        
        $db->query("SET NAMES utf8");
    }
    
    // Сортируем участников по убийствам (для теста)
    usort($participants, function($a, $b) {
        return $b['kills'] - $a['kills'];
    });
    
    $total_participants = count($participants);
    
    $response = array(
        'event' => $event,
        'total_participants' => $total_participants,
        'participants' => $participants
    );
    
    // Отладка: логируем полный ответ
    error_log("Ответ для события #$id: " . json_encode($response, JSON_UNESCAPED_UNICODE));
    
    echo json_encode(array('status' => 'success') + $response, JSON_UNESCAPED_UNICODE);
    break;

        default:
            if ($is_json) {
                echo json_encode(array('status' => 'error', 'message' => 'Неизвестный subop'));
            } else {
                echo serialize(array('errorcode' => 1, 'errortxt' => 'Неизвестный subop'));
            }
            break;
    }
}

if (!isset($_POST['op'])) die('');
$op = $_POST['op'];
if (isset($_POST['id'])) $_POST['id'] = intval($_POST['id']);

switch ($op) {
	    case 'elo_top': GetEloTop(); break;
        case 'elo_by_userid': GetEloStatsByUserId(); break;
        case 'gshop_trade_logs': GetGShopTradeLogs(); break;
        case 'npc_trade_logs': GetNpcTradeLogs(); break;
        case 'auth': auth(); break;
        case 'vkauth': vkauth(); break;
        case 'steamauth': steamauth(); break;
        case 'clean_klan_cache': CleanKlanCache(); break;
        case 'topstat': TopStat(); break;
        case 'accmanage': GetAccounts(); break;
        case 'AdminGenPromo': AdminGenPromo(); break;
        case 'update': Update(); break;
        case 'AddLoginLog': AddLoginLog(); break;
        case 'reg': Register(); break;
        case 'addvk': AddVK(); break;
        case 'addsteam': AddSteam(); break;
        case 'resendmail': ResendMail(); break;
        case 'auth_data': auth_data(); break;
        case 'actreg': ActReg(); break;
        case 'checklogin': CheckLogin(); break;
        case 'nullpass': NullPass(); break;
        case 'forgetpass': ForgetPass(); break;
        case 'forgetlogin': ForgetLogin(); break;
        case 'disableipcheck': DisableIPCheck(); break;
        case 'online_stat': OnlineStat(); break;
        case 'lklogs': GetLKLogs(); break;
        case 'mmotoplogs': GetTOPLogs(); break;
        case 'qtoplogs': GetTOPLogs(true); break;
        case 'klanart': GetKlanArts(); break;
        case 'promo_codes': GetPromoCodes(); break;
        case 'userinfo': UserInfo(); break;
        case 'roleinfo': RoleInfo(); break;
        case 'edit_klanart': EditKlanArt(); break;
        case 'edit_promo_code': EditPromoCode(); break;
        case 'addlk': AddLK(); break;
        case 'make_names': MakeNames(); break;
        case 'history': ShowHistory(); break;
        case 'loginlog_adm': ShowLoginLogAdm(); break;
        case 'adm': AdminAction($_POST['id'], $_POST['act']); break;
        case 'loginlog': ShowLoginLog(); break;
        case 'GetRegGold': GetRegGold($_POST['id']); break;
        case 'refresh_top': RefreshTop(); break;
        case 'update_user_info': UpdateUserInfo(); break;
        case 'qtop_bonus': TopBonus(false); break;
        case 'mmotop_bonus': TopBonus(true); break;
        case 'checkban': checkban((int)$_POST['id']); break;
        case 'persklan': persklan(); break;
        case 'act': act((int)$_POST['id'], (int)$_POST['n'], (int)$_POST['num'], $_POST['ip']); break;
        case 'persuah': persuah(); break;
        case 'pers': pers(); break;
        case 'shopheaders': shopheaders($_POST['id']); break;
        case 'don': donate(); break;
        case 'rid2uid': RID2UID(); break;
        case 'findrolename': FindRoleName(); break;
        case 'editrole': ShowEditRoleForm(); break;
        case 'saveeditrole': SaveEditRole(); break;
        case 'reward_donate_items': reward_donate_items(); break;
        case 'get_status_data': get_status_data(); break;
        case 'get_user_votes': get_user_votes(); break;
        case 'add_vote_bonus': AddVoteBonus(); break;
        case 'add_case': add_case(); break;
        case 'get_case': get_case_by_id(); break;
        case 'open_case': open_case(); break;
        case 'get_cases_list': get_cases_list(); break;
        case 'banrole': BanRole(); break;
        case 'manage_case': manage_case(); break;
        case 'chat_logs': GetChatLogs(); break;
        case 'trade_logs': GetTradeLogs(); break;
        case 'reinforce_logs': GetReinforceLogs(); break;
        case 'player_info':
            GetPlayerPveInfo();
            break;
        case 'player_pvp_info':
            GetPlayerPvpInfo();
            break;
        case 'alliance_info':
            GetAllianceInfo();
            break;
        case 'clan_info':
            getClanInfo();
            break;
		case 'pvp_events':
			pvp_events();
			break;
		case 'auction_admin':
			auction_admin();
			break;
		case 'auction_user':
			auction_user();
			break;
		case 'alliance_top':
			GetAllianceTop();
			break;
		case 'pve_top':
			GetPveTop();
			break;	
    case 'bp_admin_get_seasons':
        BpAdminGetSeasons();
        break;
        
    case 'bp_admin_get_season':
        BpAdminGetSeason();
        break;
        
    case 'bp_admin_save_season':
        BpAdminSaveSeason();
        break;
        
    case 'bp_admin_delete_season':
        BpAdminDeleteSeason();
        break;
        
    case 'bp_admin_get_levels':
        BpAdminGetLevels();
        break;
        
    case 'bp_admin_save_levels':
        BpAdminSaveLevels();
        break;
        
    case 'bp_admin_copy_levels':
        BpAdminCopyLevels();
        break;
        
    case 'bp_admin_get_quests':
        BpAdminGetQuests();
        break;
		
	case 'bp_admin_get_quest':
    BpAdminGetQuest();
    break;
        
    case 'bp_admin_save_quest':
        BpAdminSaveQuest();
        break;
        
    case 'bp_admin_delete_quest':
        BpAdminDeleteQuest();
        break;
        
    case 'bp_admin_get_shop':
        BpAdminGetShop();
        break;
        
    case 'bp_admin_save_shop_item':
        BpAdminSaveShopItem();
        break;
        
    case 'bp_admin_delete_shop_item':
        BpAdminDeleteShopItem();
        break;
        
    case 'bp_admin_get_stats':
        BpAdminGetStats();
        break;
        
    case 'bp_admin_get_overall_stats':
        BpAdminGetOverallStats();
        break;
        
    case 'bp_admin_reset_player':
        BpAdminResetPlayer();
        break;
		
		case 'bp_admin_test_connection':
    BpAdminTestConnection();
    break;
	
case 'bp_get_data':
    BpGetData();
    break;
    
case 'bp_claim_reward':
    BpClaimReward();
    break;
    
case 'bp_purchase':
    BpPurchase();
    break;
    
case 'bp_purchase_shop':
    BpPurchaseShop();
    break;
    
	    case 'bp_admin_get_task_logs':
        BpAdminGetTaskLogs();
        break;
	
case 'bp_update_notification':
    BpUpdateNotification();
    break;
	
	case 'bp_admin_get_rewards_log':
    BpAdminGetRewardsLog();
    break;
    
case 'bp_admin_get_purchases_log':
    BpAdminGetPurchasesLog();
    break;
    
case 'bp_admin_get_shop_purchases_log':
    BpAdminGetShopPurchasesLog();
    break;
    
case 'bp_admin_get_audit_log':
    BpAdminGetAuditLog();
    break;
	
	    case 'bp_refresh_quest_progress':
        BpRefreshQuestProgress();
        break;
        
    case 'bp_process_kill':
        BpProcessKillEvent();
        break;
	
	case 'bp_check_login_quest':
    BpCheckLoginQuest();
    break;
    
case 'bp_process_quest_event':
    BpProcessQuestEvent();
    break;
    
case 'bp_get_user_quests':
    BpGetUserQuests();
    break;
    
case 'bp_reset_daily_quests':
    BpResetDailyQuests();
    break;
	
	case 'calendar_user':
    CalendarUserDayBonus();
    break;
    
case 'calendar_admin':
    CalendarAdminDayBonus();
    break;
	
	case 'get_public_promocodes':
    handleGetPublicPromocodes();
    break;
	
	case 'change_role_occupation':
    ChangeRoleOccupation();
    break;
	
	case 'get_role_occupation':
    GetRoleOccupation();
    break;
	
case 'marathon_get_data':
    MarathonGetData();
    break;

case 'marathon_claim':
    MarathonClaimReward();
    break;

case 'marathon_admin_get_events':
    MarathonAdminGetEvents();
    break;

case 'marathon_admin_get_rewards':
    MarathonAdminGetRewards();
    break;

case 'marathon_admin_save_event':
    MarathonAdminSaveEvent();
    break;

case 'marathon_admin_save_reward':
    MarathonAdminSaveReward();
    break;

case 'marathon_admin_delete_event':
    MarathonAdminDeleteEvent();
    break;

case 'marathon_admin_delete_reward':
    MarathonAdminDeleteReward();
    break;
	
	case 'marathon_get_player_stats':
    MarathonGetPlayerStats();
    break;
	
	case 'marathon_get_top_stats':
    MarathonGetTopStats();
    break;
	
	case 'marathon_get_full_top':
    MarathonGetFullTop();
    break;
	
	


    default:
        echo json_encode(['status' => 'error', 'message' => 'Неизвестный op']);
        break;
}
