Файл: pages/economy/rings.php
Строк: 725
<?php
// Physical module map v50.6: public URL stays clean while code lives in /pages.
if (!defined('GAME_ROOT')) define('GAME_ROOT', dirname(__DIR__, 2));
chdir(GAME_ROOT);
if (PHP_SAPI !== 'cli') { $_SERVER['PHP_SELF']='/rings.php'; $_SERVER['SCRIPT_NAME']='/rings.php'; }
include './system/common.php';
include './system/functions.php';
include './system/user.php';
include './system/menu.php';
if(empty($user['id'])){ header('Location:/'); exit; }
$uid=(int)$user['id'];
$viewUid=(int)($_GET['user']??$uid);
if($viewUid<=0) $viewUid=$uid;
$canManage=$viewUid===$uid;
game_ring_normalize_user_slots($viewUid);
$redirectRing = static function(int $slot,string $manage='') use ($uid): never {
$url='/rings/?slot='.max(1,min(3,$slot));
if($manage!=='') $url.='&manage='.rawurlencode($manage);
header('Location:'.$url);
exit;
};
if($_SERVER['REQUEST_METHOD']==='POST'){
security_require_csrf();
if(!$canManage){ http_response_code(403); exit('Недостаточно прав.'); }
$rid=(int)($_POST['ring_id']??0);
$act=(string)($_POST['act']??'');
$ring=db_fetch_assoc(db_query('SELECT * FROM `ring_catalog` WHERE `id`='.$rid.' LIMIT 1'));
if(!$ring){ $_SESSION['err']='Кольцо не найдено.'; header('Location:/rings/'); exit; }
$owned=db_fetch_assoc(db_query('SELECT * FROM `user_rings` WHERE `user`='.$uid.' AND `ring_id`='.$rid.' LIMIT 1'));
$slot=game_ring_slot_for_catalog($ring);
try{
if($act==='buy'){
if($owned) throw new RuntimeException('Это кольцо уже куплено.');
$price=max(0,(int)$ring['price']);
db_pdo()->beginTransaction();
$pay=db_prepare(
'UPDATE `users` SET `g`=`g`-:price WHERE `id`=:uid AND `g`>=:min_price',
['price'=>$price,'uid'=>$uid,'min_price'=>$price]
);
if($pay->rowCount()<1) throw new RuntimeException('Недостаточно золота.');
db_execute('UPDATE `user_rings` SET `slot`=0 WHERE `user`=:uid AND `slot`=:slot',['uid'=>$uid,'slot'=>$slot]);
db_execute(
'INSERT INTO `user_rings` (`user`,`ring_id`,`slot`,`level`,`rune`,`bonus`,`stars`,`smith`) VALUES (:uid,:rid,:slot,1,0,0,0,0)',
['uid'=>$uid,'rid'=>$rid,'slot'=>$slot]
);
db_pdo()->commit();
$_SESSION['ok']='Кольцо куплено и установлено.';
$redirectRing($slot);
}
if(!$owned) throw new RuntimeException('Сначала купите это кольцо.');
if($act==='upgrade'){
$level=max(1,(int)$owned['level']);
if($level>=80) throw new RuntimeException('Кольцо уже максимально прокачано.');
$cost=(int)$ring['price']+250*$level;
db_pdo()->beginTransaction();
$pay=db_prepare(
'UPDATE `users` SET `g`=`g`-:cost WHERE `id`=:uid AND `g`>=:min_cost',
['cost'=>$cost,'uid'=>$uid,'min_cost'=>$cost]
);
if($pay->rowCount()<1) throw new RuntimeException('Недостаточно золота.');
$up=db_prepare(
'UPDATE `user_rings` SET `level`=`level`+1 WHERE `user`=:uid AND `ring_id`=:rid AND `level`=:old_level',
['uid'=>$uid,'rid'=>$rid,'old_level'=>$level]
);
if($up->rowCount()<1) throw new RuntimeException('Кольцо уже изменилось. Обновите страницу.');
db_pdo()->commit();
$_SESSION['ok']='Уровень кольца повышен до '.($level+1).'.';
$redirectRing($slot);
}
if($act==='ring_rune'){
$target=max(1,min(6,(int)($_POST['rune']??0)));
$values=game_ring_rune_values();
$costs=game_ring_rune_costs();
$current=max(0,min(6,(int)($owned['rune']??0)));
if($target<=$current) throw new RuntimeException('Выберите руну качеством выше текущей.');
$cost=game_ring_discounted_cost((int)$costs[$target],3);
$delta=(int)$values[$target]-(int)$values[$current];
$stat=game_ring_rune_stat((string)$ring['kind']);
db_pdo()->beginTransaction();
$pay=db_prepare(
'UPDATE `users` SET `g`=`g`-:cost WHERE `id`=:uid AND `g`>=:min_cost',
['cost'=>$cost,'uid'=>$uid,'min_cost'=>$cost]
);
if($pay->rowCount()<1) throw new RuntimeException('Недостаточно золота для руны.');
$up=db_prepare(
'UPDATE `user_rings` SET `rune`=:new_rune WHERE `user`=:uid AND `ring_id`=:rid AND `rune`=:old_rune',
['new_rune'=>$target,'uid'=>$uid,'rid'=>$rid,'old_rune'=>$current]
);
if($up->rowCount()<1) throw new RuntimeException('Руна уже изменилась. Обновите страницу.');
game_ring_add_stats($uid,[$stat=>$delta]);
db_pdo()->commit();
$_SESSION['ok']='Руна установлена: +'.$values[$target].' к '.game_ring_rune_stat_label((string)$ring['kind']).'.';
$redirectRing($slot,'rune');
}
if($act==='ring_bonus'){
$current=max(0,(int)($owned['bonus']??0));
$max=game_ring_bonus_max((int)$owned['level']);
if($current>=$max) throw new RuntimeException('Бонус кольца уже максимальный.');
$base=$current<10 ? 5+$current*5 : 100+($current-10)*100;
$cost=game_ring_discounted_cost(max(5,$base),5);
$per=game_ring_bonus_per_stat();
db_pdo()->beginTransaction();
$pay=db_prepare(
'UPDATE `users` SET `g`=`g`-:cost WHERE `id`=:uid AND `g`>=:min_cost',
['cost'=>$cost,'uid'=>$uid,'min_cost'=>$cost]
);
if($pay->rowCount()<1) throw new RuntimeException('Недостаточно золота для повышения бонуса.');
$up=db_prepare(
'UPDATE `user_rings` SET `bonus`=`bonus`+1 WHERE `user`=:uid AND `ring_id`=:rid AND `bonus`=:old_bonus',
['uid'=>$uid,'rid'=>$rid,'old_bonus'=>$current]
);
if($up->rowCount()<1) throw new RuntimeException('Бонус уже изменился. Обновите страницу.');
game_ring_add_stats($uid,['str'=>$per,'vit'=>$per,'agi'=>$per,'def'=>$per]);
db_pdo()->commit();
$_SESSION['ok']='Бонус кольца повышен до '.($current+1).'/'.$max.'.';
$redirectRing($slot,'bonus');
}
if($act==='ring_star'){
$current=max(0,min(5,(int)($owned['stars']??0)));
if($current>=5) throw new RuntimeException('Все пять звезд уже открыты.');
$costs=game_ring_star_costs();
$cost=(int)$costs[$current];
game_star_chest_state($uid);
$per=game_ring_star_per_stat();
db_pdo()->beginTransaction();
$dust=db_prepare(
'UPDATE `stars_dust_users` SET `dust`=`dust`-:cost WHERE `user`=:uid AND `dust`>=:min_cost',
['cost'=>$cost,'uid'=>$uid,'min_cost'=>$cost]
);
if($dust->rowCount()<1) throw new RuntimeException('Недостаточно звездной пыли.');
$up=db_prepare(
'UPDATE `user_rings` SET `stars`=`stars`+1 WHERE `user`=:uid AND `ring_id`=:rid AND `stars`=:old_stars',
['uid'=>$uid,'rid'=>$rid,'old_stars'=>$current]
);
if($up->rowCount()<1) throw new RuntimeException('Звезды уже изменились. Обновите страницу.');
game_ring_add_stats($uid,['str'=>$per,'vit'=>$per,'agi'=>$per,'def'=>$per]);
db_pdo()->commit();
$_SESSION['ok']='Открыта '.($current+1).'-я звезда кольца.';
$redirectRing($slot,'stars');
}
if($act==='ring_smith'){
$current=max(0,min(50,(int)($owned['smith']??0)));
if($current>=50) throw new RuntimeException('Заточка кольца уже +50.');
$need1=1+$current;
$need=2+$current*2;
$silver=1000+1000*$current;
$per=game_ring_smith_per_stat();
db_pdo()->beginTransaction();
$sack=db_fetch_one('SELECT * FROM `sack` WHERE `user`=:uid FOR UPDATE',['uid'=>$uid]);
if(!$sack || (int)($sack['1']??0)<$need1 || (int)($sack['2']??0)<$need || (int)($sack['3']??0)<$need || (int)($sack['4']??0)<$need || (int)($sack['5']??0)<$need){
throw new RuntimeException('Недостаточно ресурсов для заточки.');
}
$pay=db_prepare(
'UPDATE `users` SET `s`=`s`-:silver WHERE `id`=:uid AND `s`>=:min_silver',
['silver'=>$silver,'uid'=>$uid,'min_silver'=>$silver]
);
if($pay->rowCount()<1) throw new RuntimeException('Недостаточно серебра для заточки.');
db_execute(
'UPDATE `sack` SET `1`=`1`-:r1,`2`=`2`-:r2,`3`=`3`-:r3,`4`=`4`-:r4,`5`=`5`-:r5 WHERE `user`=:uid',
['r1'=>$need1,'r2'=>$need,'r3'=>$need,'r4'=>$need,'r5'=>$need,'uid'=>$uid]
);
$up=db_prepare(
'UPDATE `user_rings` SET `smith`=`smith`+1 WHERE `user`=:uid AND `ring_id`=:rid AND `smith`=:old_smith',
['uid'=>$uid,'rid'=>$rid,'old_smith'=>$current]
);
if($up->rowCount()<1) throw new RuntimeException('Заточка уже изменилась. Обновите страницу.');
game_ring_add_stats($uid,['str'=>$per,'vit'=>$per,'agi'=>$per,'def'=>$per]);
db_pdo()->commit();
$_SESSION['ok']='Кольцо заточено до +'.($current+1).'.';
$redirectRing($slot,'smith');
}
} catch(Throwable $e){
if(db_pdo()->inTransaction()) db_pdo()->rollBack();
error_log('[RING_ACTION] '.$e->getMessage());
$_SESSION['err']=$e->getMessage();
if($owned) $redirectRing($slot,(string)($_POST['manage']??''));
header('Location:/rings/'); exit;
}
}
$slot=max(0,(int)($_GET['slot']??0));
$manage=(string)($_GET['manage']??'');
$slotRows=game_ring_slot_details($viewUid);
$detail=($slot>=1&&$slot<=3)?($slotRows[$slot]??null):null;
include './system/h.php';
if(!empty($_SESSION['ok'])){ echo '<div class="ok center">'.security_html((string)$_SESSION['ok']).'</div>'; unset($_SESSION['ok']); }
if(!empty($_SESSION['err'])){ echo '<div class="error center">'.security_html((string)$_SESSION['err']).'</div>'; unset($_SESSION['err']); }
?>
<div class="title">Магические кольца</div>
<?php if($detail):
$catalog=db_fetch_assoc(db_query('SELECT * FROM `ring_catalog` WHERE `id`='.(int)$detail['ring_id'].' LIMIT 1')) ?: ['name'=>'Кольцо','description'=>'','kind'=>'rage'];
$level=max(1,(int)$detail['level']);
$effects=game_ring_effects($level,(string)$catalog['kind']);
$quality=$detail['quality'];
$rune=max(0,min(6,(int)$detail['rune']));
$bonus=max(0,(int)$detail['bonus']);
$bonusMax=game_ring_bonus_max($level);
$stars=max(0,min(5,(int)$detail['stars']));
$smith=max(0,min(50,(int)$detail['smith']));
$runeValues=game_ring_rune_values();
$runeValue=(int)$runeValues[$rune];
$smithTotal=$smith*game_ring_smith_per_stat()*4;
$bonusTotal=$bonus*game_ring_bonus_per_stat()*4;
$starTotal=$stars*game_ring_star_per_stat()*4;
?>
<div class="ring-detail-page ring-classic-detail">
<div class="ring-detail-head">
<div class="ring-detail-icon-wrap" style="border-color:<?=htmlspecialchars($quality['color'],ENT_QUOTES,'UTF-8')?>">
<img class="ring-detail-icon" src="<?=htmlspecialchars($quality['image'],ENT_QUOTES,'UTF-8')?>" alt="">
<?php if($smith>0):?><span class="slot-smith">+<?=$smith?></span><?php endif;?>
<?php if($rune>0):?><img class="slot-rune" src="/images/icon/quality/<?=$rune?>.png" alt=""><?php endif;?>
<?php if($stars>0):?><span class="slot-stars"><?=str_repeat('★',$stars)?></span><?php endif;?>
</div>
<div class="ring-detail-main">
<div class="ring-detail-title" style="color:<?=htmlspecialchars($quality['color'],ENT_QUOTES,'UTF-8')?>"><?=htmlspecialchars($catalog['name'],ENT_QUOTES,'UTF-8')?></div>
<div class="ring-special-line"><?=htmlspecialchars($effects['label'],ENT_QUOTES,'UTF-8')?>: <b><?=$effects['bonus']?> <?=htmlspecialchars($effects['unit'],ENT_QUOTES,'UTF-8')?></b></div>
<div class="ring-special-line"><?=htmlspecialchars($effects['proc_label'],ENT_QUOTES,'UTF-8')?>: <b><?=$effects['chance']?>%</b></div>
</div>
</div>
<div class="mini-line"></div>
<div class="ring-upgrade-row">
<div><img src="/images/icon/rune.png" alt=""> <b>Руна:</b>
<?php if($rune>0):?><span class="green">+<?=$runeValue?> к <?=game_ring_rune_stat_label((string)$catalog['kind'])?></span><?php else:?><span class="grey">не установлена</span><?php endif;?><br>
<span class="white">Руна — мощное усиление вещи!</span>
</div>
<?php if($canManage):?><a class="old-btn" href="/rings/?slot=<?=$slot?>&manage=rune">Магазин рун</a><?php endif;?>
</div>
<div class="mini-line"></div>
<div class="ring-upgrade-row">
<div><img src="/images/icon/smith.png" alt=""> <b>Заточка:</b> <span class="green">+<?=$smith?></span><br><span class="green">+<?=n_f($smithTotal)?> к параметрам</span></div>
<?php if($canManage && $smith<50):?><a class="old-btn" href="/rings/?slot=<?=$slot?>&manage=smith">Заточка</a><?php endif;?>
</div>
<div class="mini-line"></div>
<div class="ring-upgrade-row">
<div><img src="/images/icon/quality/<?=max(1,min(6,(int)$quality['tier']))?>.png" alt=""> <b>Бонус:</b> <span class="green"><?=$bonus?> из <?=$bonusMax?></span><br><span class="green">+<?=n_f($bonusTotal)?> к параметрам</span></div>
<?php if($canManage && $bonus<$bonusMax):?><a class="old-btn" href="/rings/?slot=<?=$slot?>&manage=bonus">Бонус</a><?php endif;?>
</div>
<div class="mini-line"></div>
<div class="ring-upgrade-row">
<div><span class="ring-star-symbol">★</span> <b>Звезды:</b> <span class="green"><?=$stars?> из 5</span><br><span class="green">+<?=n_f($starTotal)?> к параметрам</span></div>
<?php if($canManage && $stars<5):?><a class="old-btn" href="/rings/?slot=<?=$slot?>&manage=stars">Звезды</a><?php endif;?>
</div>
<?php if($canManage && $manage==='rune'):
$costs=game_ring_rune_costs(); $vals=game_ring_rune_values(); $discount=game_ring_discount_percent(3); ?>
<div class="ring-manage-panel">
<div class="center blue"><b>Установка руны</b><?php if($discount>0):?> · скидка <?=$discount?>%<?php endif;?></div>
<?php for($r=$rune+1;$r<=6;$r++): $cost=game_ring_discounted_cost((int)$costs[$r],3); ?>
<form method="post" class="ring-rune-choice">
<?=security_csrf_field()?><input type="hidden" name="ring_id" value="<?=$detail['ring_id']?>"><input type="hidden" name="act" value="ring_rune"><input type="hidden" name="rune" value="<?=$r?>"><input type="hidden" name="manage" value="rune">
<img src="/images/runes/<?=htmlspecialchars(game_ring_rune_stat((string)$catalog['kind']),ENT_QUOTES,'UTF-8')?>.png" alt="">
<span><img src="/images/icon/quality/<?=$r?>.png" alt=""> +<?=$vals[$r]?> к <?=game_ring_rune_stat_label((string)$catalog['kind'])?></span>
<button class="old-btn" type="submit">Купить за <img src="/images/icon/gold.png" alt=""> <?=n_f($cost)?></button>
</form>
<?php endfor; ?>
</div>
<?php elseif($canManage && $manage==='bonus'):
$base=$bonus<10?5+$bonus*5:100+($bonus-10)*100; $cost=game_ring_discounted_cost(max(5,$base),5); ?>
<div class="ring-manage-panel center">
<div class="blue">Каждая единица бонуса даёт <b>+24</b> к сумме основных параметров кольца.</div>
<div>Текущий бонус: <b><?=$bonus?>/<?=$bonusMax?></b></div>
<form method="post"><?=security_csrf_field()?><input type="hidden" name="ring_id" value="<?=$detail['ring_id']?>"><input type="hidden" name="act" value="ring_bonus"><input type="hidden" name="manage" value="bonus"><button class="old-btn" type="submit">Улучшить за <img src="/images/icon/gold.png" alt=""> <?=n_f($cost)?></button></form>
</div>
<?php elseif($canManage && $manage==='stars'):
$dust=game_star_chest_state($uid); $costs=game_ring_star_costs(); $starCost=(int)$costs[$stars]; ?>
<div class="ring-manage-panel center">
<div class="blue">Звезды улучшаются только за звездную пыль.</div>
<div>Звездная пыль: <b><?=n_f((int)$dust['dust'])?></b> · следующая звезда: <b><?=n_f($starCost)?></b></div>
<form method="post"><?=security_csrf_field()?><input type="hidden" name="ring_id" value="<?=$detail['ring_id']?>"><input type="hidden" name="act" value="ring_star"><input type="hidden" name="manage" value="stars"><button class="old-btn" type="submit">Прокачать звезду</button></form>
</div>
<?php elseif($canManage && $manage==='smith'):
$need1=1+$smith; $need=2+$smith*2; $silver=1000+1000*$smith; ?>
<div class="ring-manage-panel center">
<div class="blue">Заточка кольца использует ресурсы пещеры и серебро.</div>
<div>Нужно: ресурс 1 — <b><?=$need1?></b>, ресурсы 2–5 — по <b><?=$need?></b>, серебро — <b><?=n_f($silver)?></b>.</div>
<form method="post"><?=security_csrf_field()?><input type="hidden" name="ring_id" value="<?=$detail['ring_id']?>"><input type="hidden" name="act" value="ring_smith"><input type="hidden" name="manage" value="smith"><button class="old-btn" type="submit">Заточить до +<?=($smith+1)?></button></form>
</div>
<?php endif; ?>
<div class="mini-line"></div>
<div class="center"><a class="old-btn" href="/rings/">Кольца</a></div>
</div>
<?php else: ?>
<div class="rings-v50-list rings-classic-list">
<?php
$q=db_query('SELECT * FROM `ring_catalog` ORDER BY `id`');
while($r=db_fetch_assoc($q)):
$owned=db_fetch_assoc(db_query('SELECT * FROM `user_rings` WHERE `user`='.$uid.' AND `ring_id`='.(int)$r['id'].' LIMIT 1'));
$level=$owned?max(1,(int)($owned['level']??1)):1;
$quality=game_ring_quality($level,(string)($r['kind']??'rage'));
$nextCost=(int)$r['price']+250*$level;
$effects=game_ring_effects($level,(string)($r['kind']??'rage'));
$selected=$owned && (int)($owned['slot']??0)>0;
$pct=min(100,(int)round($level/80*100));
?>
<div class="rings-v50-row">
<div class="rings-v50-left"><img class="rings-v50-icon" src="<?=htmlspecialchars($quality['image'],ENT_QUOTES,'UTF-8')?>" alt=""></div>
<div class="rings-v50-mid">
<div class="rings-v50-title" style="color:<?=htmlspecialchars($quality['color'],ENT_QUOTES,'UTF-8')?>"><?=htmlspecialchars($r['name'],ENT_QUOTES,'UTF-8')?><?php if($selected):?>, <span class="green">✔ Выбрано</span><?php endif;?></div>
<div class="white"><?=htmlspecialchars($r['description'],ENT_QUOTES,'UTF-8')?></div>
<div class="rings-v50-stats"><?=htmlspecialchars($effects['label'],ENT_QUOTES,'UTF-8')?>: <span class="green"><?=$effects['bonus']?> <?=htmlspecialchars($effects['unit'],ENT_QUOTES,'UTF-8')?></span></div>
<div class="rings-v50-stats"><?=htmlspecialchars($effects['proc_label'],ENT_QUOTES,'UTF-8')?>: <span class="green"><?=$effects['chance']?>%</span></div>
<div class="ring-level-progress"><span style="width:<?=$pct?>%"></span></div>
<?php if(!$owned):?>
<form method="post" class="center"><?=security_csrf_field()?><input type="hidden" name="ring_id" value="<?=$r['id']?>"><button class="old-btn" name="act" value="buy">Купить за <img src="/images/icon/gold.png" alt=""> <?=n_f((int)$r['price'])?></button></form>
<?php else:?>
<form method="post" class="center"><?=security_csrf_field()?><input type="hidden" name="ring_id" value="<?=$r['id']?>"><button class="old-btn" name="act" value="upgrade">Улучшить за <img src="/images/icon/gold.png" alt=""> <?=n_f($nextCost)?></button></form>
<div class="center"><a class="old-btn" href="/rings/?slot=<?=game_ring_slot_for_catalog($r)?>">Выбрать</a></div>
<?php endif;?>
</div>
</div>
<div class="mini-line"></div>
<?php endwhile; ?>
<div class="block_zero"><a href="/smith/"><img src="/images/icon/arrow.png" alt=""> Вернуться в кузницу</a></div>
</div>
<?php endif; include './system/f.php'; ?>