39514-vm/sccharacter.php
2026-04-16 00:12:54 +00:00

429 lines
19 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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: radial-gradient(circle at top right, #1d2234 0%, #0a0d13 55%, #050608 100%);
color: var(--text-main);
font-family: Arial, Helvetica, sans-serif;
}
.page {
max-width: 1180px;
margin: 0 auto;
padding: 2rem 1rem 3rem;
}
.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: 1rem;
}
.equipment-section {
display: flex;
flex-direction: column;
gap: 0.8rem;
}
.equipment-section-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.8rem;
}
.equipment-section-title {
margin: 0;
color: var(--primary);
letter-spacing: 0.08em;
text-transform: uppercase;
font-size: 1rem;
}
.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; }
.stat {
padding: 0.32rem 0.55rem;
border-radius: 999px;
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.08);
font-size: 0.78rem;
}
.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 nest 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>
<h2 class="section-title">Équipement</h2>
<?php if ($character_items === []): ?>
<div class="message">
<p class="muted">Ce personnage na 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>
<span class="tag muted"><?php echo count($category_items); ?> objet(s)</span>
</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
);
$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($name, ENT_QUOTES, 'UTF-8'); ?></h3>
<div class="tags">
<span class="tag <?php echo $is_custom ? 'tag-primary' : ''; ?>"><?php echo $is_custom ? 'Objet perso.' : 'Base dobjets'; ?></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>