Файл: system/starter.php
Строк: 125
<?php
/* Обновление игры под PHP 8.4 / PDO, стартовое снаряжение и защита регистрации: by Метриум (Стэлп) */
declare(strict_types=1);
/**
* Стартовые комплекты. Используются только реальные предметы из items/shop.
* Все восемь слотов выдаются и надеваются атомарно при создании персонажа.
*/
function starter_kits(): array {
return [
0 => ['name' => 'Комплект воина', 'items' => [9,10,11,12,13,14,15,16]],
1 => ['name' => 'Комплект охотника', 'items' => [25,26,27,28,29,30,31,32]],
];
}
function starter_state(): array {
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
$kits = starter_kits();
$state = $_SESSION['_starter'] ?? [];
if (!is_array($state)) $state = [];
$sex = (int)($state['sex'] ?? 0);
if (!in_array($sex, [0,1], true)) $sex = 0;
$kit = (int)($state['kit'] ?? 0);
if (!isset($kits[$kit])) $kit = 0;
$ref = max(0, (int)($state['ref'] ?? 0));
$_SESSION['_starter'] = ['sex'=>$sex, 'kit'=>$kit, 'ref'=>$ref, 'updated'=>time()];
return $_SESSION['_starter'];
}
function starter_set_ref(int $ref): void {
$state = starter_state();
$state['ref'] = max(0, $ref);
$state['updated'] = time();
$_SESSION['_starter'] = $state;
}
function starter_set_sex(int $sex): void {
$state = starter_state();
$state['sex'] = $sex === 1 ? 1 : 0;
$state['updated'] = time();
$_SESSION['_starter'] = $state;
}
function starter_next_kit(): void {
$state = starter_state();
$kits = starter_kits();
$keys = array_keys($kits);
$pos = array_search((int)$state['kit'], $keys, true);
$pos = $pos === false ? 0 : (($pos + 1) % count($keys));
$state['kit'] = (int)$keys[$pos];
$state['updated'] = time();
$_SESSION['_starter'] = $state;
}
function starter_selected_kit(): array {
$state = starter_state();
return starter_kits()[(int)$state['kit']];
}
function starter_maneken_url(): string {
$state = starter_state();
$kit = starter_selected_kit();
$items = array_map('intval', $kit['items']);
return '/manekenImage/'.(int)$state['sex'].'/'.implode('/', $items).'/0/';
}
/**
* Загружает параметры стартовых вещей из БД, а не доверяет данным формы/сессии.
*/
function starter_load_items(): array {
$kit = starter_selected_kit();
$ids = array_map('intval', $kit['items']);
if (count($ids) !== 8) throw new RuntimeException('Starter kit is incomplete');
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$stmt = db_prepare(
"SELECT i.id,i.w,i.name,i.quality,i.skill,s.bonus,s._str,s._vit,s._agi,s._def
FROM items i INNER JOIN shop s ON s.id=i.id
WHERE i.id IN ($placeholders)",
$ids
);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$bySlot = [];
foreach ($rows as $row) {
$slot = (int)$row['w'];
if ($slot >= 1 && $slot <= 8) $bySlot[$slot] = $row;
}
for ($slot=1; $slot<=8; $slot++) {
if (!isset($bySlot[$slot])) throw new RuntimeException('Starter item missing for slot '.$slot);
}
ksort($bySlot);
return $bySlot;
}
function starter_stats(): array {
$stats = ['str'=>0,'vit'=>0,'agi'=>0,'def'=>0];
foreach (starter_load_items() as $row) {
$stats['str'] += (int)$row['_str'];
$stats['vit'] += (int)$row['_vit'];
$stats['agi'] += (int)$row['_agi'];
$stats['def'] += (int)$row['_def'];
}
return $stats;
}
/**
* Выдаёт и сразу надевает выбранный комплект.
* Должно вызываться внутри транзакции сразу после INSERT users.
*/
function starter_grant_and_equip(PDO $pdo, int $userId): array {
if ($userId <= 0) throw new InvalidArgumentException('Invalid user id');
$items = starter_load_items();
$worn = [];
$sum = ['str'=>0,'vit'=>0,'agi'=>0,'def'=>0];
$insert = $pdo->prepare(
'INSERT INTO `inv` (`user`,`item`,`quality`,`bonus`,`smith`,`equip`,`new`,`rune`,`_str`,`_vit`,`_agi`,`_def`,`place`)
VALUES (:user,:item,:quality,:bonus,0,1,1,0,:str,:vit,:agi,:def,0)'
);
foreach ($items as $slot => $row) {
$insert->execute([
'user'=>$userId,
'item'=>(int)$row['id'],
'quality'=>(int)$row['quality'],
'bonus'=>(int)$row['bonus'],
'str'=>(int)$row['_str'],
'vit'=>(int)$row['_vit'],
'agi'=>(int)$row['_agi'],
'def'=>(int)$row['_def'],
]);
$invId = (int)$pdo->lastInsertId();
if ($invId <= 0) throw new RuntimeException('Cannot create starter inventory row');
$worn[$slot] = $invId;
$sum['str'] += (int)$row['_str'];
$sum['vit'] += (int)$row['_vit'];
$sum['agi'] += (int)$row['_agi'];
$sum['def'] += (int)$row['_def'];
}
$sql = 'UPDATE `users` SET '
. '`sex`=:sex, '
. '`str`=`str`+:str,`vit`=`vit`+:vit,`agi`=`agi`+:agi,`def`=`def`+:def, '
. '`w_1`=:w1,`w_2`=:w2,`w_3`=:w3,`w_4`=:w4,`w_5`=:w5,`w_6`=:w6,`w_7`=:w7,`w_8`=:w8 '
. 'WHERE `id`=:id';
$state = starter_state();
$stmt = $pdo->prepare($sql);
$stmt->execute([
'sex'=>(int)$state['sex'],
'str'=>$sum['str'],'vit'=>$sum['vit'],'agi'=>$sum['agi'],'def'=>$sum['def'],
'w1'=>$worn[1],'w2'=>$worn[2],'w3'=>$worn[3],'w4'=>$worn[4],
'w5'=>$worn[5],'w6'=>$worn[6],'w7'=>$worn[7],'w8'=>$worn[8],
'id'=>$userId,
]);
// Текущее HP соответствует уже итоговой выносливости, MP — текущей мане.
$pdo->prepare('UPDATE `users` SET `hp`=`vit`*2, `mp`=`mana` WHERE `id`=?')->execute([$userId]);
return ['worn'=>$worn,'stats'=>$sum];
}
function starter_clear(): void {
unset($_SESSION['_starter']);
}