Персонажи в игре

Список мы собираем руками на основе анкет, но генерируется он динамически.
Возраст считается по дате рождения, зависит от последнего числа текущего игрового периода.
Если что-то не нравится или хотите/надо поменять — пишите в связь с администрацией, поправим.
Здесь еще и фильтры будут, мамой клянусь.
[html]<style>
:root {
--dynamic-font-size: 1.2rem;
}
.hehe-charlist.grid-col-3 {
gap: var(--size-xs);
.char {
position: relative;
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: min-content min-content auto;
gap: var(--size-xs);
padding: var(--size-xs);
border: 1px solid var(--color-neutral-300);
border-radius: var(--size-xs);
background-color: var(--color-white);
&:has(.npc) {
background-color: var(--color-neutral-100);
}
}
.npc sup {
position: absolute;
top: var(--size-xs);
right: var(--size-2xs);
font-family: var(--font-accent);
text-transform: uppercase;
color: var(--color-neutral-500);
}
.who {
grid-column: 1 / -1;
grid-row: 2;
margin-block-start: calc(-1 * var(--size-xs));
}
.desc {
grid-column: 1 / -1;
grid-row: 3;
}
}
</style>
<div id="hehe-target" class="hehe-charlist grid-col-3"></div>
<script>
const charTemplate = (id, name) => id
? `<a href="/profile.php?id=${id}"target=_blank>${name}</a>`
: `<span class="npc">${name} <sup>npc</sup></span>`;
const GAME_LATEST_DATE = new Date('2025-10-31');
const handleAgeFromDOB = (dob) => {
if (!dob) {
return '';
}
const [charDay, charMonth, charYear] = dob.split('.');
const birthDate = new Date(`${charYear}-${charMonth}-${charDay}`);
let age = GAME_LATEST_DATE.getFullYear() - birthDate.getFullYear();
const mm = GAME_LATEST_DATE.getMonth() - birthDate.getMonth();
if (mm < 0 || (mm === 0 && GAME_LATEST_DATE.getDate() < birthDate.getDate())) {
age--;
}
return `, ${age}`;
}
const handleWhoDis = (who, gender) => {
if (Array.isArray(who)) {
return who.map(whoDis => { // todo: икон очки
switch (whoDis) {
case 'magician':
return gender === 'f' ? 'волшебница' : 'волшебник';
case 'hedgewitch':
return 'хедж-ведьма';
case 'hybrid':
return 'полукровка';
case 'creature':
return 'существо';
case 'human':
return 'человек';
case 'other':
return '???';
default:
return whoDis;
}
}).join(', ');
}
}
const handleCursed = (cursed, gender) => {
if (!cursed) {
return '';
}
let marker = '';
if (gender === 'f') {
marker = 'а';
}
return `; проклят${marker}`;
}
const generateFCList = (characters) => {
const chars = Object.keys(characters).map(key => ({
...characters[key],
name: key,
}));
const container = document.getElementById("hehe-target");
chars.sort((a, b) => a.ru.localeCompare(b.ru)).forEach(({ ru, id, who, dob, cursed, gender, desc }) => {
container.innerHTML += `
<div class="char">
<strong>${charTemplate(id, ru)}${handleAgeFromDOB(dob)}</strong>
<small class="who"><em>${handleWhoDis(who, gender)}${handleCursed(cursed, gender)}</em></small>
${desc ? `<small class="desc">${desc}</small>` : ''}
</div>
`;
});
}
const vHash = +new Date();
const script = document.createElement('script');
script.charset = 'windows-1251';
script.src = `https://forumstatic.ru/files/001c/ab/7e/10010.js?v=${vHash}`;
script.addEventListener('load', () => {
generateFCList(window.characters);
});
script.addEventListener('error', () => {
console.error('Script failed to load.');
});
document.body.appendChild(script);
</script>[/html]