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

https://external-content.duckduckgo.com/iu/?u=https://i.imgur.com/N39Cozd.png

  • Список мы собираем руками на основе анкет, но генерируется он динамически.

  • Возраст считается по дате рождения, зависит от последнего числа текущего игрового периода.

  • Если что-то не нравится или хотите/надо поменять — пишите в связь с администрацией, поправим.

  • Здесь еще и фильтры будут, мамой клянусь.


Код:
<!-- 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>