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

Список мы собираем руками на основе анкет, но генерируется он динамически.
Возраст считается по дате рождения, зависит от последнего числа текущего игрового периода.
Если что-то не нравится или хотите/надо поменять — пишите в связь с администрацией, поправим.
Здесь еще и фильтры будут, мамой клянусь.
Код:
<!-- HTML -->
<style>
.hehe-charlist.grid-col-3 {
gap: var(--size-sm);
.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-sm);
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-sm);
right: var(--size-xs);
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) => {
if (id) {
return `<a href="/profile.php?id=${id}" target="_blank" rel="noopener noreferrer">${name}</a>`;
}
return `<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 monthDifference = GAME_LATEST_DATE.getMonth() - birthDate.getMonth();
if (
monthDifference < 0 ||
(monthDifference === 0 &&
GAME_LATEST_DATE.getDate() < birthDate.getDate())
) {
age--;
}
return `, ${age}`;
};
const generateCharList = (characters) => {
const container = document.getElementById("hehe-target");
if (!container) {
return;
}
Object.values(characters)
.sort((first, second) => first.ru.localeCompare(second.ru))
.forEach((character) => {
const { ru, id, dob, desc } = character;
container.insertAdjacentHTML(
"beforeend",
`<div class="char">
<strong>${charTemplate(id, ru)}${handleAgeFromDOB(dob)}</strong>
<small class="who"><em>${window.teh.describeCharacter(character)}</em></small>
${desc ? `<small class="desc">${desc}</small>` : ""}
</div>`
);
});
};
const hash = +new Date();
const script = document.createElement("script");
script.charset = "windows-1251";
script.src = `https://forumstatic.ru/files/001c/ab/7e/61137.js?v=${hash}`;
script.addEventListener(
"load",
async () => {
try {
const characters = await window.teh.loadCharacters();
generateCharList(characters);
} catch (error) {
console.error("Could not generate character list.", error);
}
},
{ once: true }
);
script.addEventListener(
"error",
() => {
console.error("Character-vault script failed to load.");
},
{ once: true }
);
document.body.append(script);
})();
</script>