488 lines
21 KiB
PHP
488 lines
21 KiB
PHP
<?php
|
||
|
||
require_once __DIR__ . '/db/auth.php';
|
||
require_once __DIR__ . '/db/scstatsitem.php';
|
||
require_once __DIR__ . '/db/scitemcustom.php';
|
||
require_once __DIR__ . '/db/sccharacters.php';
|
||
|
||
auth_start_session();
|
||
auth_bootstrap();
|
||
scstatsitem_bootstrap();
|
||
scitemcustom_bootstrap();
|
||
sccharacters_bootstrap();
|
||
|
||
$db = db();
|
||
$share_token = trim((string) ($_GET['share'] ?? ''));
|
||
$character = null;
|
||
$character_items = [];
|
||
$custom_stats_by_itemcustom = [];
|
||
|
||
if ($share_token !== '') {
|
||
$stmt_character = $db->prepare(
|
||
'SELECT c.*, COALESCE(NULLIF(TRIM(a.cl_auth_user), \'\'), \'Inconnu\') AS cl_sccharacter_creator_name
|
||
FROM tbl_sccharacters c
|
||
LEFT JOIN tbl_auth a ON a.cl_auth_id = c.cl_sccharacter_owner_auth_id
|
||
WHERE c.cl_sccharacter_share_token = :share_token
|
||
AND c.cl_sccharacter_share_enabled = 1
|
||
LIMIT 1'
|
||
);
|
||
$stmt_character->execute(['share_token' => $share_token]);
|
||
$character = $stmt_character->fetch() ?: null;
|
||
}
|
||
|
||
if ($character) {
|
||
sccharacters_reindex_character_items($db, (int) $character['cl_sccharacter_id']);
|
||
|
||
$stmt_items = $db->prepare(
|
||
"SELECT
|
||
ci.*,
|
||
bo.cl_scobjs_name AS cl_sccharacteritem_base_name,
|
||
bo.cl_scobjs_type AS cl_sccharacteritem_base_type,
|
||
bo.cl_scobjs_subtype AS cl_sccharacteritem_base_subtype,
|
||
bo.cl_scobjs_uuid AS cl_sccharacteritem_base_uuid,
|
||
oo.cl_scobjs_name AS cl_sccharacteritem_custom_name,
|
||
oo.cl_scobjs_type AS cl_sccharacteritem_custom_type,
|
||
oo.cl_scobjs_subtype AS cl_sccharacteritem_custom_subtype,
|
||
oo.cl_scobjs_uuid AS cl_sccharacteritem_custom_uuid
|
||
FROM tbl_sccharacteritems ci
|
||
LEFT JOIN tbl_scobjs bo ON bo.cl_scobjs_id = ci.cl_sccharacteritem_scobjs_id
|
||
LEFT JOIN tbl_scitemcustom co ON co.cl_scitemcustom_id = ci.cl_sccharacteritem_scitemcustom_id
|
||
LEFT JOIN tbl_scobjs oo ON oo.cl_scobjs_id = co.cl_scitemcustom_obj_id
|
||
WHERE ci.cl_sccharacteritem_character_id = :character_id
|
||
ORDER BY ci.cl_sccharacteritem_sort_order ASC, ci.cl_sccharacteritem_id ASC"
|
||
);
|
||
$stmt_items->execute(['character_id' => (int) $character['cl_sccharacter_id']]);
|
||
$character_items = $stmt_items->fetchAll();
|
||
|
||
$custom_item_ids = [];
|
||
foreach ($character_items as $row) {
|
||
if (($row['cl_sccharacteritem_source'] ?? '') === 'custom' && !empty($row['cl_sccharacteritem_scitemcustom_id'])) {
|
||
$custom_item_ids[] = (int) $row['cl_sccharacteritem_scitemcustom_id'];
|
||
}
|
||
}
|
||
$custom_item_ids = array_values(array_unique(array_filter($custom_item_ids)));
|
||
|
||
if ($custom_item_ids !== []) {
|
||
$placeholders = implode(',', array_fill(0, count($custom_item_ids), '?'));
|
||
$stmt_stats = $db->prepare(
|
||
"SELECT
|
||
cs.cl_scitemcustomstat_itemcustom_id,
|
||
st.cl_scstatsitem_name,
|
||
st.cl_scstatsitem_unit,
|
||
cs.cl_scitemcustomstat_sign,
|
||
cs.cl_scitemcustomstat_value
|
||
FROM tbl_scitemcustomstat cs
|
||
INNER JOIN tbl_scstatsitem st ON st.cl_scstatsitem_id = cs.cl_scitemcustomstat_stat_id
|
||
WHERE cs.cl_scitemcustomstat_itemcustom_id IN ({$placeholders})
|
||
ORDER BY st.cl_scstatsitem_name ASC, cs.cl_scitemcustomstat_id ASC"
|
||
);
|
||
$stmt_stats->execute($custom_item_ids);
|
||
|
||
foreach ($stmt_stats->fetchAll() as $stat_row) {
|
||
$itemcustom_id = (int) $stat_row['cl_scitemcustomstat_itemcustom_id'];
|
||
if (!isset($custom_stats_by_itemcustom[$itemcustom_id])) {
|
||
$custom_stats_by_itemcustom[$itemcustom_id] = [];
|
||
}
|
||
$custom_stats_by_itemcustom[$itemcustom_id][] = $stat_row;
|
||
}
|
||
}
|
||
} else {
|
||
http_response_code(404);
|
||
}
|
||
|
||
$item_category_options = sccharacters_item_category_options();
|
||
$character_category_order = $character
|
||
? sccharacters_character_category_order($character)
|
||
: sccharacters_default_category_order();
|
||
$character_items_by_category = [];
|
||
foreach ($character_items as $item_row) {
|
||
$is_custom = ($item_row['cl_sccharacteritem_source'] ?? '') === 'custom';
|
||
$type = $is_custom
|
||
? (string) ($item_row['cl_sccharacteritem_custom_type'] ?? '')
|
||
: (string) ($item_row['cl_sccharacteritem_base_type'] ?? '');
|
||
$subtype = $is_custom
|
||
? (string) ($item_row['cl_sccharacteritem_custom_subtype'] ?? '')
|
||
: (string) ($item_row['cl_sccharacteritem_base_subtype'] ?? '');
|
||
$category_key = sccharacters_resolve_item_category(
|
||
(string) ($item_row['cl_sccharacteritem_slot'] ?? ''),
|
||
$type,
|
||
$subtype
|
||
);
|
||
if (!isset($character_items_by_category[$category_key])) {
|
||
$character_items_by_category[$category_key] = [];
|
||
}
|
||
$character_items_by_category[$category_key][] = $item_row;
|
||
}
|
||
$character_items_by_category = sccharacters_sort_items_by_category_order(
|
||
$character_items_by_category,
|
||
$character_category_order
|
||
);
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title><?php echo htmlspecialchars($character ? ((string) $character['cl_sccharacter_name'] . ' | Personnage partagé') : 'Personnage introuvable', ENT_QUOTES, 'UTF-8'); ?></title>
|
||
<style>
|
||
:root {
|
||
--primary: #a29b78;
|
||
--primary-soft: rgba(162, 155, 120, 0.18);
|
||
--primary-border: rgba(162, 155, 120, 0.3);
|
||
--bg: #080a0f;
|
||
--card: rgba(20, 24, 33, 0.88);
|
||
--text-main: #ece9df;
|
||
--text-soft: rgba(236, 233, 223, 0.72);
|
||
}
|
||
|
||
* { box-sizing: border-box; }
|
||
|
||
body {
|
||
margin: 0;
|
||
min-height: 100vh;
|
||
background-color: #050608;
|
||
color: var(--text-main);
|
||
font-family: Arial, Helvetica, sans-serif;
|
||
position: relative;
|
||
}
|
||
|
||
body::before {
|
||
content: '';
|
||
position: fixed;
|
||
inset: 0;
|
||
background-image:
|
||
linear-gradient(rgba(5, 8, 12, 0.58), rgba(5, 8, 12, 0.72)),
|
||
url('https://robertsspaceindustries.com/media/1vllgn95062syr/background_blur/REACT-Background.jpg');
|
||
background-position: center center;
|
||
background-repeat: no-repeat;
|
||
background-size: cover;
|
||
z-index: -2;
|
||
transform: scale(1.08);
|
||
transform-origin: center center;
|
||
}
|
||
|
||
body::after {
|
||
content: '';
|
||
position: fixed;
|
||
inset: 0;
|
||
background: radial-gradient(circle at top right, rgba(29, 34, 52, 0.22) 0%, rgba(10, 13, 19, 0.18) 55%, rgba(5, 6, 8, 0.1) 100%);
|
||
z-index: -1;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.page {
|
||
max-width: 1180px;
|
||
margin: 0 auto;
|
||
padding: 2rem 1rem 3rem;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.hero,
|
||
.card,
|
||
.message {
|
||
background: var(--card);
|
||
border: 1px solid var(--primary-border);
|
||
border-radius: 20px;
|
||
box-shadow: 0 22px 50px rgba(0,0,0,0.28);
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.hero {
|
||
padding: 1.4rem;
|
||
display: grid;
|
||
grid-template-columns: 120px minmax(0, 1fr);
|
||
gap: 1.2rem;
|
||
margin-bottom: 1.3rem;
|
||
}
|
||
|
||
.avatar,
|
||
.avatar-fallback {
|
||
width: 120px;
|
||
height: 120px;
|
||
border-radius: 28px;
|
||
object-fit: cover;
|
||
background: linear-gradient(145deg, rgba(162, 155, 120, 0.3), rgba(255,255,255,0.08));
|
||
border: 1px solid rgba(255,255,255,0.08);
|
||
}
|
||
|
||
.avatar-fallback {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 2.2rem;
|
||
color: var(--primary);
|
||
font-weight: 700;
|
||
}
|
||
|
||
h1 { margin: 0 0 0.65rem; font-size: 2rem; }
|
||
p { line-height: 1.6; }
|
||
|
||
.meta,
|
||
.stats,
|
||
.tags {
|
||
display: flex;
|
||
gap: 0.55rem;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
}
|
||
|
||
.tag {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 0.34rem 0.62rem;
|
||
border-radius: 999px;
|
||
background: rgba(255,255,255,0.06);
|
||
border: 1px solid rgba(255,255,255,0.08);
|
||
font-size: 0.84rem;
|
||
}
|
||
|
||
.tag-primary {
|
||
background: var(--primary-soft);
|
||
border-color: rgba(162, 155, 120, 0.35);
|
||
color: #f6eebf;
|
||
}
|
||
|
||
.muted { color: var(--text-soft); }
|
||
|
||
.equipment-sections {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2.2rem;
|
||
}
|
||
|
||
.equipment-section {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.8rem;
|
||
}
|
||
|
||
.equipment-section-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.8rem;
|
||
}
|
||
|
||
.equipment-section-title {
|
||
margin: 0;
|
||
color: var(--primary);
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
font-size: 1rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.85rem;
|
||
width: 100%;
|
||
}
|
||
|
||
.equipment-section-title::after {
|
||
content: '';
|
||
flex: 1 1 auto;
|
||
height: 2px;
|
||
background: currentColor;
|
||
border-radius: 999px;
|
||
opacity: 0.95;
|
||
}
|
||
|
||
.grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 1rem;
|
||
}
|
||
|
||
.card {
|
||
padding: 1rem;
|
||
display: flex;
|
||
gap: 0.95rem;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.thumb,
|
||
.thumb-fallback {
|
||
width: 68px;
|
||
height: 68px;
|
||
border-radius: 18px;
|
||
object-fit: cover;
|
||
flex: 0 0 68px;
|
||
background: rgba(255,255,255,0.06);
|
||
border: 1px solid rgba(255,255,255,0.08);
|
||
}
|
||
|
||
.thumb-fallback {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: var(--primary);
|
||
font-size: 1.2rem;
|
||
}
|
||
|
||
.card-body { min-width: 0; }
|
||
.card-body h3 { margin: 0 0 0.45rem; font-size: 1.05rem; }
|
||
.stats {
|
||
margin-top: 0.7rem;
|
||
display: flex;
|
||
gap: 0.55rem;
|
||
flex-wrap: wrap;
|
||
align-items: stretch;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
box-shadow: none;
|
||
backdrop-filter: none;
|
||
-webkit-backdrop-filter: none;
|
||
}
|
||
|
||
.stat {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
min-height: 36px;
|
||
padding: 0.46rem 0.76rem;
|
||
border-radius: 12px;
|
||
background: linear-gradient(135deg, rgba(26, 74, 42, 0.9), rgba(18, 46, 28, 0.82));
|
||
border: 1px solid rgba(90, 255, 150, 0.85);
|
||
box-shadow: inset 0 0 0 1px rgba(140, 255, 188, 0.18), 0 0 0 1px rgba(34, 110, 58, 0.28), 0 10px 22px rgba(0,0,0,0.18);
|
||
font-size: 0.8rem;
|
||
font-weight: 700;
|
||
color: #dcffe9;
|
||
}
|
||
|
||
.section-title {
|
||
margin: 1.2rem 0 0.8rem;
|
||
color: var(--primary);
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.message {
|
||
padding: 1.4rem;
|
||
text-align: center;
|
||
}
|
||
|
||
@media (max-width: 860px) {
|
||
.hero,
|
||
.grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="page">
|
||
<?php if (!$character): ?>
|
||
<div class="message">
|
||
<h1>Personnage introuvable</h1>
|
||
<p class="muted">Ce lien public est invalide, désactivé ou n’est plus disponible.</p>
|
||
</div>
|
||
<?php else: ?>
|
||
<?php
|
||
$avatar = trim((string) ($character['cl_sccharacter_avatar_url'] ?? ''));
|
||
$initial = function_exists('mb_substr')
|
||
? mb_strtoupper(mb_substr((string) $character['cl_sccharacter_name'], 0, 1, 'UTF-8'), 'UTF-8')
|
||
: strtoupper(substr((string) $character['cl_sccharacter_name'], 0, 1));
|
||
?>
|
||
<section class="hero">
|
||
<?php if ($avatar !== ''): ?>
|
||
<img class="avatar" src="<?php echo htmlspecialchars($avatar, ENT_QUOTES, 'UTF-8'); ?>" alt="Avatar de <?php echo htmlspecialchars((string) $character['cl_sccharacter_name'], ENT_QUOTES, 'UTF-8'); ?>" loading="lazy" onerror="this.replaceWith(Object.assign(document.createElement('div'), {className:'avatar-fallback', textContent:'<?php echo htmlspecialchars($initial, ENT_QUOTES, 'UTF-8'); ?>'}));">
|
||
<?php else: ?>
|
||
<div class="avatar-fallback"><?php echo htmlspecialchars($initial, ENT_QUOTES, 'UTF-8'); ?></div>
|
||
<?php endif; ?>
|
||
<div>
|
||
<h1><?php echo htmlspecialchars((string) $character['cl_sccharacter_name'], ENT_QUOTES, 'UTF-8'); ?></h1>
|
||
<div class="meta">
|
||
<?php if (trim((string) $character['cl_sccharacter_role']) !== ''): ?>
|
||
<span class="tag tag-primary"><?php echo htmlspecialchars((string) $character['cl_sccharacter_role'], ENT_QUOTES, 'UTF-8'); ?></span>
|
||
<?php endif; ?>
|
||
<?php if (trim((string) $character['cl_sccharacter_faction']) !== ''): ?>
|
||
<span class="tag"><?php echo htmlspecialchars((string) $character['cl_sccharacter_faction'], ENT_QUOTES, 'UTF-8'); ?></span>
|
||
<?php endif; ?>
|
||
<span class="tag muted">Créé par <?php echo htmlspecialchars((string) $character['cl_sccharacter_creator_name'], ENT_QUOTES, 'UTF-8'); ?></span>
|
||
<span class="tag muted"><?php echo count($character_items); ?> équipement(s)</span>
|
||
</div>
|
||
<p><?php echo nl2br(htmlspecialchars(trim((string) $character['cl_sccharacter_description']) !== '' ? (string) $character['cl_sccharacter_description'] : 'Aucune description publique fournie pour ce personnage.', ENT_QUOTES, 'UTF-8')); ?></p>
|
||
</div>
|
||
</section>
|
||
|
||
<?php if ($character_items === []): ?>
|
||
<div class="message">
|
||
<p class="muted">Ce personnage n’a pas encore d’équipement attribué.</p>
|
||
</div>
|
||
<?php else: ?>
|
||
<div class="equipment-sections">
|
||
<?php foreach ($character_items_by_category as $category_key => $category_items): ?>
|
||
<section class="equipment-section">
|
||
<div class="equipment-section-head">
|
||
<h3 class="equipment-section-title"><?php echo htmlspecialchars(sccharacters_item_category_label((string) $category_key), ENT_QUOTES, 'UTF-8'); ?></h3>
|
||
</div>
|
||
<div class="grid">
|
||
<?php foreach ($category_items as $item_row): ?>
|
||
<?php
|
||
$is_custom = ($item_row['cl_sccharacteritem_source'] ?? '') === 'custom';
|
||
$name = $is_custom
|
||
? (string) ($item_row['cl_sccharacteritem_custom_name'] ?? 'Objet personnalisé indisponible')
|
||
: (string) ($item_row['cl_sccharacteritem_base_name'] ?? 'Objet indisponible');
|
||
$type = $is_custom
|
||
? (string) ($item_row['cl_sccharacteritem_custom_type'] ?? '')
|
||
: (string) ($item_row['cl_sccharacteritem_base_type'] ?? '');
|
||
$subtype = $is_custom
|
||
? (string) ($item_row['cl_sccharacteritem_custom_subtype'] ?? '')
|
||
: (string) ($item_row['cl_sccharacteritem_base_subtype'] ?? '');
|
||
$uuid = $is_custom
|
||
? (string) ($item_row['cl_sccharacteritem_custom_uuid'] ?? '')
|
||
: (string) ($item_row['cl_sccharacteritem_base_uuid'] ?? '');
|
||
$category = sccharacters_resolve_item_category(
|
||
(string) ($item_row['cl_sccharacteritem_slot'] ?? ''),
|
||
$type,
|
||
$subtype
|
||
);
|
||
$quantity = isset($item_row['cl_sccharacteritem_quantity']) && (int) $item_row['cl_sccharacteritem_quantity'] > 0
|
||
? (int) $item_row['cl_sccharacteritem_quantity']
|
||
: null;
|
||
$title = $quantity !== null ? $quantity . 'x ' . $name : $name;
|
||
$note = trim((string) ($item_row['cl_sccharacteritem_note'] ?? ''));
|
||
$stats = $is_custom ? ($custom_stats_by_itemcustom[(int) ($item_row['cl_sccharacteritem_scitemcustom_id'] ?? 0)] ?? []) : [];
|
||
?>
|
||
<article class="card">
|
||
<?php if ($uuid !== ''): ?>
|
||
<img class="thumb" src="https://cstone.space/uifimages/<?php echo htmlspecialchars($uuid, ENT_QUOTES, 'UTF-8'); ?>.png" alt="Aperçu de <?php echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8'); ?>" loading="lazy" onerror="this.replaceWith(Object.assign(document.createElement('div'), {className:'thumb-fallback', textContent:'◈'}));">
|
||
<?php else: ?>
|
||
<div class="thumb-fallback">◈</div>
|
||
<?php endif; ?>
|
||
<div class="card-body">
|
||
<h3><?php echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); ?></h3>
|
||
<div class="tags">
|
||
<span class="tag <?php echo $is_custom ? 'tag-primary' : ''; ?>"><?php echo $is_custom ? 'Objet perso.' : 'Base d’objets'; ?></span>
|
||
<span class="tag"><?php echo htmlspecialchars(sccharacters_item_category_label($category), ENT_QUOTES, 'UTF-8'); ?></span>
|
||
<?php if ($type !== ''): ?><span class="tag muted"><?php echo htmlspecialchars($type, ENT_QUOTES, 'UTF-8'); ?></span><?php endif; ?>
|
||
<?php if ($subtype !== ''): ?><span class="tag muted"><?php echo htmlspecialchars($subtype, ENT_QUOTES, 'UTF-8'); ?></span><?php endif; ?>
|
||
</div>
|
||
<?php if ($stats !== []): ?>
|
||
<div class="stats">
|
||
<?php foreach ($stats as $stat_row): ?>
|
||
<?php
|
||
$sign = (string) ($stat_row['cl_scitemcustomstat_sign'] ?? '');
|
||
$value = rtrim(rtrim(number_format((float) ($stat_row['cl_scitemcustomstat_value'] ?? 0), 2, '.', ''), '0'), '.');
|
||
if ($value === '') {
|
||
$value = '0';
|
||
}
|
||
?>
|
||
<span class="stat"><?php echo htmlspecialchars((string) $stat_row['cl_scstatsitem_name'] . ' : ' . $sign . $value . ' ' . (string) $stat_row['cl_scstatsitem_unit'], ENT_QUOTES, 'UTF-8'); ?></span>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php if ($note !== ''): ?>
|
||
<p class="muted"><?php echo nl2br(htmlspecialchars($note, ENT_QUOTES, 'UTF-8')); ?></p>
|
||
<?php endif; ?>
|
||
</div>
|
||
</article>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</section>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
</body>
|
||
</html>
|