Файл: system/v50_features.php
Строк: 457
<?php
declare(strict_types=1);
/* v50: кольца, 16 достижений, новости турниров, перерасчёты. by Метриум (Стэлп) */
function game_v50_bootstrap(): void {
static $done=false; if($done) return; $done=true;
$ver=(int)(function_exists('game_meta_get')?game_meta_get('v50_schema','0'):0);
if($ver>=2) return;
db_query("CREATE TABLE IF NOT EXISTS `achievement_v50_bonus` (`user` int NOT NULL,`grades` int NOT NULL DEFAULT 0,PRIMARY KEY(`user`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
db_query("CREATE TABLE IF NOT EXISTS `automation_log` (`code` varchar(64) NOT NULL,`period_key` varchar(32) NOT NULL,`created_at` int NOT NULL,PRIMARY KEY(`code`,`period_key`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
if(function_exists('game_meta_set')) game_meta_set('v50_schema','2');
}
function game_ring_effects(int $level,string $kind='rage'): array {
$level=max(1,$level);
$chance=min(40, 10 + (int)floor($level/3));
switch($kind){
case 'guard':
$bonus=min(90, 18 + (int)floor($level*1.45));
return ['label'=>'Макс. бонус','unit'=>'% к броне','bonus'=>$bonus,'chance'=>$chance,'proc_label'=>'Шанс срабатывания'];
case 'revive':
$bonus=min(25, 5 + (int)floor($level/6));
return ['label'=>'Бонус','unit'=>'% жизни','bonus'=>$bonus,'chance'=>min(30,10 + (int)floor($level/8)),'proc_label'=>'Шанс срабатывания'];
case 'rage':
default:
$bonus=min(90, 22 + (int)floor($level*1.35));
return ['label'=>'Макс. бонус','unit'=>'% к урону крита','bonus'=>$bonus,'chance'=>$chance,'proc_label'=>'Шанс срабатывания'];
}
}
/** Реальные улучшения магических колец. */
function game_ring_rune_values(): array { return [0=>0,1=>75,2=>150,3=>250,4=>600,5=>1000,6=>2000]; }
function game_ring_rune_costs(): array { return [1=>175,2=>1400,3=>16000,4=>20000,5=>35000,6=>70000]; }
function game_ring_rune_stat(string $kind): string { return match($kind){'guard'=>'def','revive'=>'vit',default=>'str'}; }
function game_ring_rune_stat_label(string $kind): string { return match($kind){'guard'=>'броне','revive'=>'жизни',default=>'силе'}; }
function game_ring_bonus_max(int $level): int { return 90; }
function game_ring_bonus_per_stat(): int { return 6; } // 4 x 6 = +24 к сумме параметров за единицу бонуса.
function game_ring_smith_per_stat(): int { return 23; } // +35 = +3220 к сумме параметров, как в референсе.
function game_ring_star_per_stat(): int { return 100; } // 5 звезд = +2000 к сумме параметров.
function game_ring_star_costs(): array { return [0=>10,1=>25,2=>50,3=>100,4=>200]; }
function game_ring_discount_percent(int $id): int {
if(function_exists('game_active_discount_percent')) return min(90,game_active_discount_percent($id));
$row=db_fetch_assoc(db_query('SELECT `zena`,`time` FROM `skilka` WHERE `id`='.(int)$id.' LIMIT 1'));
if(!$row || (int)($row['time']??0)<=time()) return 0;
return max(0,min(90,(int)($row['zena']??0)));
}
function game_ring_discounted_cost(int $base,int $discountId): int {
$base=max(0,$base); $pct=game_ring_discount_percent($discountId);
return max(0,$base-(int)round($base*$pct/100));
}
function game_ring_add_stats(int $uid,array $delta): void {
$allowed=['str','vit','agi','def']; $sets=[]; $params=['uid'=>$uid];
foreach($allowed as $stat){
$d=(int)($delta[$stat]??0); if($d===0) continue;
$key='d_'.$stat; $sets[]='`'.$stat.'`=`'.$stat.'`+:'.$key; $params[$key]=$d;
}
if(!$sets) return;
db_execute('UPDATE `users` SET '.implode(',',$sets).' WHERE `id`=:uid',$params);
}
function game_ring_total_upgrade_bonus(array $ring): int {
$values=game_ring_rune_values();
$rune=(int)($values[(int)($ring['rune']??0)]??0);
$bonus=max(0,(int)($ring['bonus']??0))*game_ring_bonus_per_stat()*4;
$smith=max(0,(int)($ring['smith']??0))*game_ring_smith_per_stat()*4;
$stars=max(0,min(5,(int)($ring['stars']??0)))*game_ring_star_per_stat()*4;
return $rune+$bonus+$smith+$stars;
}
function game_achievement_catalog_v50(array $user): array {
$uid=(int)($user['id']??0);
$collector=db_fetch_assoc(db_query("SELECT * FROM `collector` WHERE `user`=$uid LIMIT 1")) ?: ['coll'=>1,'stats'=>0];
$trophies=0; for($i=1;$i<=9;$i++){ if((int)($user['troph'.$i]??0)===1) $trophies++; }
$smithCount=(int)(db_result(db_query("SELECT COUNT(*) FROM `inv` WHERE `user`=$uid AND `equip`=1 AND `smith`>=15"),0) ?: 0);
$smithCount+=(int)(db_result(db_query("SELECT COUNT(*) FROM `user_rings` WHERE `user`=$uid AND `slot`>0 AND `smith`>=15"),0) ?: 0);
$runeCount=(int)(db_result(db_query("SELECT COUNT(*) FROM `inv` WHERE `user`=$uid AND `equip`=1 AND `rune`>0"),0) ?: 0);
$runeCount+=(int)(db_result(db_query("SELECT COUNT(*) FROM `user_rings` WHERE `user`=$uid AND `slot`>0 AND `rune`>0"),0) ?: 0);
$bonusSum=(int)(db_result(db_query("SELECT COALESCE(SUM(`bonus`),0) FROM `inv` WHERE `user`=$uid AND `equip`=1"),0) ?: 0);
$bonusSum+=(int)(db_result(db_query("SELECT COALESCE(SUM(`bonus`),0) FROM `user_rings` WHERE `user`=$uid AND `slot`>0"),0) ?: 0);
$sackSum=(int)(db_result(db_query("SELECT COALESCE(`1`,0)+COALESCE(`2`,0)+COALESCE(`3`,0)+COALESCE(`4`,0)+COALESCE(`5`,0)+COALESCE(`6`,0)+COALESCE(`7`,0)+COALESCE(`8`,0)+COALESCE(`9`,0) FROM `sack` WHERE `user`=$uid LIMIT 1"),0) ?: 0);
$petLevel=(int)(db_result(db_query("SELECT COALESCE(MAX(GREATEST(`lvl_str`,`lvl_vit`,`lvl_agi`,`lvl_def`)),0) FROM `pet_u` WHERE `user_id`=$uid"),0) ?: (int)($user['pet_lvl']??0));
$ach=[
['key'=>'lvl','name'=>'Путь героя','link'=>'/user/'.$uid,'icon'=>'/images/icon/exp.png','progress'=>(int)($user['level']??0),'levels'=>[5,10,20,35,50,70],'condition'=>'Достигни %s уровня'],
['key'=>'train','name'=>'Мастер тренировки','link'=>'/train/','icon'=>'/images/icon/train.png','progress'=>(int)($user['skill']??0),'levels'=>[5,15,35,70,120,200],'condition'=>'Получи %s мастерства'],
['key'=>'quests','name'=>'Хранитель поручений','link'=>'/quest/','icon'=>'/images/icon/quest.png','progress'=>(int)($user['quests']??0),'levels'=>[5,15,40,90,180,320],'condition'=>'Выполни %s заданий'],
['key'=>'arena_fights','name'=>'Боец арены','link'=>'/arena/','icon'=>'/images/icon/fights.png','progress'=>(int)($user['boy_arena']??0),'levels'=>[10,30,80,180,450,1000],'condition'=>'Проведи %s боёв на арене'],
['key'=>'arena_wins','name'=>'Победитель арены','link'=>'/arena/','icon'=>'/images/icon/fights.png','progress'=>(int)($user['arena_pobed']??0),'levels'=>[5,15,40,100,260,600],'condition'=>'Победи %s раз на арене'],
['key'=>'duel_fights','name'=>'Дуэлянт','link'=>'/duel/','icon'=>'/images/icon/fights.png','progress'=>(int)($user['duel_fights']??0),'levels'=>[5,15,35,80,160,300],'condition'=>'Проведи %s дуэлей'],
['key'=>'duel_rating','name'=>'Вершитель судеб','link'=>'/duel/','icon'=>'/images/icon/fights.png','progress'=>(int)($user['duel_rating']??0),'levels'=>[20,70,160,320,600,1000],'condition'=>'Набери %s рейтинга дуэлей'],
['key'=>'undying','name'=>'Страх короля бессмертных','link'=>'/undying/','icon'=>'/images/icon/heart.png','progress'=>(int)($user['undying']??0),'levels'=>[3,10,25,60,140,300],'condition'=>'Победи %s бессмертных'],
['key'=>'cave','name'=>'Собиратель ресурсов','link'=>'/cave/','icon'=>'/images/icon/silver.png','progress'=>$sackSum,'levels'=>[5,20,60,150,320,650],'condition'=>'Добудь %s ресурсов в пещере'],
['key'=>'collections','name'=>'Легенда коллекций','link'=>'/collector/'.$uid.'/','icon'=>'/images/icon/quest.png','progress'=>max(0,(int)($collector['coll']??1)-1),'levels'=>[1,2,3,4,5,6],'condition'=>'Собери %s коллекций'],
['key'=>'trophies','name'=>'Охотник за трофеями','link'=>'/medal/','icon'=>'/images/icon/valor.png','progress'=>$trophies,'levels'=>[1,2,3,4,6,9],'condition'=>'Получи %s трофеев'],
['key'=>'smith','name'=>'Кузнечный мастер','link'=>'/smith/smith/','icon'=>'/images/icon/hammer.png','progress'=>$smithCount,'levels'=>[1,2,4,6,7,8],'condition'=>'Заточи до +15 минимум %s предметов'],
['key'=>'runes','name'=>'Рунописец','link'=>'/smith/runes/','icon'=>'/images/icon/hammer.png','progress'=>$runeCount,'levels'=>[1,2,3,4,6,8],'condition'=>'Установи руны на %s предметов'],
['key'=>'bonus','name'=>'Усиления','link'=>'/smith/bonus/','icon'=>'/images/icon/exp.png','progress'=>$bonusSum,'levels'=>[10,30,70,120,220,360],'condition'=>'Получи суммарно %s бонуса экипировки'],
['key'=>'pet','name'=>'Повелитель питомцев','link'=>'/petshop/','icon'=>'/images/icon/heart.png','progress'=>$petLevel,'levels'=>[5,10,18,28,40,55],'condition'=>'Прокачай питомца до %s уровня'],
['key'=>'valor','name'=>'Слава доблести','link'=>'/olimp/','icon'=>'/images/icon/valor.png','progress'=>(int)($user['lvl_dobl']??0),'levels'=>[1,3,7,15,30,50],'condition'=>'Достигни %s уровня доблести'],
];
$qNames=['нет','Обычное','Улучшенное','Редкое','Эпическое','Легендарное','Мифическое'];
$qColors=['#d5c7a5','#75d65c','#70b7ff','#d184ff','#ff9a42','#ffe16d','#ff6b6b'];
foreach($ach as &$a){
$grade=0; foreach($a['levels'] as $target){ if((int)$a['progress'] >= (int)$target) $grade++; }
$a['grade']=$grade; $a['max_grade']=count($a['levels']);
$next=$a['levels'][$grade] ?? end($a['levels']);
$a['next_target']=(int)$next;
$a['percent']=$grade>=$a['max_grade'] ? 100 : (int)max(0,min(100,round(((int)$a['progress']/max(1,(int)$next))*100)));
$a['quality_name']=$qNames[$grade] ?? 'Мифическое';
$a['quality_color']=$qColors[$grade] ?? '#ff6b6b';
$a['condition_text']=$grade>=$a['max_grade'] ? 'Все качества достижения получены' : sprintf((string)$a['condition'],n_f((int)$next));
}
unset($a);
return $ach;
}
function game_achievement_total_grades_v50(array $user): int {
$sum=0; foreach(game_achievement_catalog_v50($user) as $a){ $sum += (int)$a['grade']; } return $sum;
}
function game_achievement_summary_v50(array $user): array {
$list=game_achievement_catalog_v50($user);
$started=0; $fullyCompleted=0; $totalBonus=0; $maxBonus=0; $grades=0; $maxGrades=0;
foreach($list as $a){
$grade=(int)($a['grade'] ?? 0);
$maxGrade=(int)($a['max_grade'] ?? 0);
if($grade>0) $started++;
if($maxGrade>0 && $grade>=$maxGrade) $fullyCompleted++;
$grades += $grade;
$maxGrades += $maxGrade;
$totalBonus += $grade*2;
$maxBonus += $maxGrade*2;
}
return [
'started'=>$started,
'completed'=>$fullyCompleted,
'total'=>count($list),
'bonus'=>$totalBonus,
'max_bonus'=>$maxBonus,
'grades'=>$grades,
'max_grades'=>$maxGrades,
];
}
function game_achievement_v50_sync(array &$user): void {
if(empty($user['id'])) return;
game_v50_bootstrap();
$uid=(int)$user['id'];
$total=game_achievement_total_grades_v50($user);
db_query("INSERT IGNORE INTO `achievement_v50_bonus` (`user`,`grades`) VALUES ($uid,0)");
$stored=(int)(db_result(db_query("SELECT `grades` FROM `achievement_v50_bonus` WHERE `user`=$uid LIMIT 1"),0) ?: 0);
// Достижение является постоянным: уже полученные ступени не отнимаем,
// даже если игрок снял предмет/руну и текущий вычисляемый прогресс уменьшился.
// Это также исключает циклическое снятие/возврат параметров.
if($total <= $stored) return;
$delta=$total-$stored;
$statDelta=$delta*2;
// При отключённых эмулируемых prepared statements PDO/MySQL нельзя повторно
// использовать один и тот же именованный placeholder в одном запросе.
db_execute(
'UPDATE `users` SET '
.'`str`=`str`+:d_str,'
.'`vit`=`vit`+:d_vit,'
.'`agi`=`agi`+:d_agi,'
.'`def`=`def`+:d_def,'
.'`mana`=`mana`+:d_mana,'
.'`mp`=`mp`+:d_mp,'
.'`hp`=`hp`+:d_hp '
.'WHERE `id`=:u',
[
'd_str'=>$statDelta,
'd_vit'=>$statDelta,
'd_agi'=>$statDelta,
'd_def'=>$statDelta,
'd_mana'=>$statDelta,
'd_mp'=>$statDelta,
'd_hp'=>$statDelta,
'u'=>$uid,
]
);
db_execute('UPDATE `achievement_v50_bonus` SET `grades`=:g WHERE `user`=:u',["g"=>$total,"u"=>$uid]);
foreach(['str','vit','agi','def','mana','mp','hp'] as $k){ $user[$k]=(int)($user[$k]??0)+$statDelta; }
}
function game_forum_news_post(string $title,string $text,int $authorId=2,int $sub=1): void {
$title=trim($title); $text=trim($text); if($title===''||$text==='') return;
db_execute('INSERT INTO `forum_topic` (`sub`,`name`,`stick`,`user`,`text`,`time`) VALUES (:s,:n,0,:u,:t,:tm)',[
's'=>$sub,'n'=>mb_substr($title,0,200,'UTF-8'),'u'=>$authorId,'t'=>$text,'tm'=>time()
]);
}