БЕСТИАРИЙ
Кроме широко известных каждому животных в Тенебрии существуют Твари - существа, чье происхождение так или иначе касается сверхестественных сил.
Существа, доступные для того, чтобы сопровождать вас в приключениях по Тенебрии, имеют указанную цену и, в отдельных случаях, условия вербовки. Если ваш персонаж является не магом речь идет только о приручении. Если же вы маг, то Тварь может стать вашим фамильяром. Оплата проводится в банке после чего питомец будет добавлен на вашу личную страницу.
Для того, чтобы предложить дополнение к бестиарию, пожалуйста обратитесь в тему связи с администрацией.
Внимание! При выборе питомца пожалуйста учитывайте региональные особенности отношения к проявлениям магии. В Валонии и Каррамане любые признаки колдовства воспринимаются как угроза, а потому без надлежащего объяснения могут повлечь за собой серьезные последствия.
[size=12][b]→ тварь:[/b] название артефакта(ов) [b]→ сумма позиций:[/b] цифра + цифра = итог [b]→ итого в профиле:[/b] сколько монет после всех манипуляций[/size]
[html]
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: #827f78;
}
:root {
--bestiary-region-bg: var(--quote-input-bg);
--bestiary-region-text: #312704;
--bestiary-danger-low: #31531b;
--bestiary-danger-medium: #764300;
--bestiary-danger-high: #882424;
--bestiary-danger-extra-high: #4b0c0c;
}
/* ── section wrapper ── */
.bestiary-section { margin: 0 auto calc(5 * var(--base-spacing)); width: 1012px; }
/* ── grid: narrower cards ── */
.codex-page__grid {
grid-template-columns: repeat(auto-fill, minmax(155px, 1fr));
gap: calc(1.5 * var(--base-spacing));
margin-bottom: 0;
}
/* ── card: interactive ── */
.codex-page__card { cursor: pointer; }
.codex-page__card.is-active { border-color: rgba(184,160,86,0.6); }
/* ── card-img: cover photo override ── */
.codex-page__card-img {
height: calc(18.5 * var(--base-spacing));
display: block;
overflow: hidden;
flex-shrink: 0;
}
.codex-page__card-img img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: top center;
display: block;
}
/* ── danger (new) ── */
.bestiary-card__danger {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.02em;
display: flex;
align-items: center;
gap: calc(0.5 * var(--base-spacing));
}
.bestiary-card__danger::before {
content: '';
display: inline-block;
width: var(--base-spacing);
height: var(--base-spacing);
border-radius: 50%;
background: currentColor;
flex-shrink: 0;
}
.bestiary-card__danger--low { color: var(--bestiary-danger-low); }
.bestiary-card__danger--medium { color: var(--bestiary-danger-medium); }
.bestiary-card__danger--high { color: var(--bestiary-danger-high); }
.bestiary-card__danger--extreme { color: var(--bestiary-danger-extra-high); }
/* ── regions (new) ── */
.bestiary-card__regions {
display: flex;
flex-wrap: wrap;
gap: calc(0.5 * var(--base-spacing));
flex-grow: 1;
align-content: flex-start;
}
.bestiary-card__region {
font-size: 9px;
background: var(--bestiary-region-bg);
border: 1px solid rgba(86,77,40,0.42);
border-radius: calc(0.5 * var(--base-spacing));
padding: 0 calc(0.5 * var(--base-spacing));
color: var(--bestiary-region-text);
}
/* ── mage only ── */
.bestiary-card__mage-only {
position: relative;
color: var(--codex-price-color);
font-size: 11px;
cursor: default;
flex-shrink: 0;
display: flex;
align-items: center;
}
.bestiary-card__mage-only::after {
content: attr(data-tooltip);
position: absolute;
bottom: calc(100% + calc(0.5 * var(--base-spacing)));
right: 0;
background: #1a1508;
color: #b89840;
font-size: 9px;
font-weight: 600;
white-space: nowrap;
padding: calc(0.5 * var(--base-spacing)) var(--base-spacing);
border-radius: calc(0.5 * var(--base-spacing));
pointer-events: none;
opacity: 0;
transition: opacity 0.15s ease;
z-index: 10;
}
.bestiary-card__mage-only:hover::after { opacity: 1; }
/* ── card footer: flex to push mage icon right ── */
.codex-page__card-footer {
display: flex !important;
align-items: center;
justify-content: space-between;
}
/* ── detail price row ── */
.bestiary-detail__price-row {
display: flex;
align-items: center;
justify-content: space-between;
}
/* ── price unavailable ── */
.codex-page__price--unavailable {
font-size: 10px;
color: var(--codex-text-muted);
font-style: italic;
}
/* ── inline detail panel (new) ── */
.bestiary-detail {
grid-column: 1 / -1;
display: grid;
grid-template-columns: calc(27.5 * var(--base-spacing)) 1fr;
background: var(--codex-card-bg);
border: 1px solid var(--codex-card-border);
border-radius: var(--base-spacing);
overflow: hidden;
position: relative;
opacity: 0;
transform: translateY(calc(-1 * var(--base-spacing)));
transition: opacity 0.22s ease, transform 0.22s ease;
}
.bestiary-detail.is-open {
opacity: 1;
transform: translateY(0);
}
.bestiary-detail__close {
position: absolute;
top: var(--base-spacing);
right: calc(2 * var(--base-spacing));
background: none;
border: 1px solid var(--codex-card-border);
color: var(--codex-text-muted);
width: calc(3 * var(--base-spacing));
height: calc(3 * var(--base-spacing));
border-radius: calc(0.5 * var(--base-spacing));
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
line-height: 1;
flex-shrink: 0;
}
.bestiary-detail__img {
overflow: hidden;
background: var(--codex-card-img-bg);
min-height: calc(32.5 * var(--base-spacing));
}
.bestiary-detail__img img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: top center;
display: block;
}
.bestiary-detail__body {
padding: calc(2 * var(--base-spacing)) calc(4.5 * var(--base-spacing)) calc(2 * var(--base-spacing)) calc(2 * var(--base-spacing));
overflow-y: auto;
max-height: calc(40 * var(--base-spacing));
text-align: justify;
}
.bestiary-detail__name {
font-family: "Throne", Georgia, serif;
font-size: 15px;
font-weight: 700;
color: var(--codex-text-primary);
letter-spacing: 0.06em;
line-height: 1.25;
margin-bottom: var(--base-spacing);
}
.bestiary-detail__meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--base-spacing);
margin-bottom: calc(1.5 * var(--base-spacing));
}
.bestiary-detail__divider {
height: 1px;
background: linear-gradient(90deg, var(--codex-text-divider), transparent);
margin: calc(1.5 * var(--base-spacing)) 0;
}
.bestiary-detail__price {
font-family: "Throne", Georgia, serif;
font-size: 12px;
font-weight: 600;
color: var(--codex-price-color);
display: flex;
align-items: center;
gap: var(--base-spacing);
}
.bestiary-detail__price--unavailable {
font-size: 11px;
color: var(--codex-text-muted);
font-style: italic;
}
.bestiary-detail__description {
font-size: 11px;
color: var(--codex-text-muted);
line-height: 1.75;
}
.bestiary-detail__fields {
margin-top: calc(2 * var(--base-spacing));
display: flex;
flex-direction: column;
gap: calc(1.5 * var(--base-spacing));
}
.bestiary-detail__field-label {
font-family: "Throne", Georgia, serif;
font-size: .9rem;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--codex-text-primary);
margin-bottom: calc(0.5 * var(--base-spacing));
}
.bestiary-detail__field-value {
font-size: 11px;
color: var(--codex-text-muted);
line-height: 1.6;
}
</style>
<div class="codex-page" id="bestiary-root"></div>
<script>
const renderBestiary = () => {
const dangerLabels = {
low: 'низкий',
medium: 'средний',
high: 'высокий',
extreme: 'экстремальный',
};
let activeCard = null;
const findLastInRow = (cardEl, grid) => {
const cards = [...grid.querySelectorAll('.codex-page__card')];
const top = cardEl.offsetTop;
let last = cardEl;
for (const c of cards) {
if (Math.abs(c.offsetTop - top) < 5) last = c;
}
return last;
};
const closeDetail = () => {
const panel = document.getElementById('bestiary-detail');
if (panel) panel.remove();
if (activeCard) { activeCard.classList.remove('is-active'); activeCard = null; }
};
const openDetail = (item, cardEl) => {
const alreadyOpen = activeCard === cardEl && document.getElementById('bestiary-detail');
closeDetail();
if (alreadyOpen) return;
activeCard = cardEl;
cardEl.classList.add('is-active');
const grid = cardEl.closest('.codex-page__grid');
const lastInRow = findLastInRow(cardEl, grid);
const priceInner = item.price
? `<div class="bestiary-detail__price"><i class="fa-solid fa-coins" style="opacity:.75;font-size:10px"></i> ${item.price.toLocaleString('ru-RU')}</div>`
: `<div class="bestiary-detail__price--unavailable">приобретение невозможно</div>`;
const priceHtml = `<div class="bestiary-detail__price-row">${priceInner}${item.mageOnly ? `<span class="bestiary-card__mage-only" data-tooltip="только для магов"><i class="fa-solid fa-wand-magic-sparkles"></i></span>` : ''}</div>`;
const panel = document.createElement('div');
panel.className = 'bestiary-detail';
panel.id = 'bestiary-detail';
panel.innerHTML = `
<button class="bestiary-detail__close" aria-label="Закрыть">✕</button>
<div class="bestiary-detail__img" style="background-image: url(${item.image})">
</div>
<div class="bestiary-detail__body">
<div class="bestiary-detail__name">${item.name}</div>
<div class="bestiary-detail__meta">
<span class="bestiary-card__danger bestiary-card__danger--${item.dangerLevel}">${dangerLabels[item.dangerLevel]}</span>
${[...item.regions].sort((a, b) => a.localeCompare(b, 'ru')).map(r => `<span class="bestiary-card__region">${r}</span>`).join('')}
</div>
<div class="bestiary-detail__divider"></div>
${priceHtml}
<div class="bestiary-detail__divider"></div>
<div class="bestiary-detail__description">${item.description}</div>
<div class="bestiary-detail__fields">
<div>
<div class="bestiary-detail__field-label">Продолжительность жизни</div>
<div class="bestiary-detail__field-value">${item.lifespan}</div>
</div>
<div>
<div class="bestiary-detail__field-label">Приручаемость</div>
<div class="bestiary-detail__field-value">${item.tamability}</div>
</div>
<div>
<div class="bestiary-detail__field-label">Практическая польза</div>
<div class="bestiary-detail__field-value">${item.utility}</div>
</div>
</div>
</div>`;
lastInRow.after(panel);
requestAnimationFrame(() => {
panel.classList.add('is-open');
panel.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
});
panel.querySelector('.bestiary-detail__close').addEventListener('click', closeDetail);
};
const render = () => {
const root = document.getElementById('bestiary-root');
root.innerHTML = document.BESTIARY_DATA.sections.map(section => `
<div class="bestiary-section">
<div class="codex-page__header">${section.title}</div>
<div class="codex-page__grid">${[...section.items].sort((a, b) => a.name.localeCompare(b.name, 'ru')).map(item => {
const priceHtml = item.price
? `<div class="codex-page__price"><i class="fa-solid fa-coins codex-page__price-icon" style="font-size:10px"></i> ${item.price.toLocaleString('ru-RU')}</div>`
: `<div class="codex-page__price--unavailable">недоступно</div>`;
return `
<div class="codex-page__card" data-id="${item.id}" tabindex="0" role="button" aria-label="${item.name}">
<div class="codex-page__card-img" style="background-image: url(${item.image})">
</div>
<div class="codex-page__card-body">
<div class="codex-page__card-name">${item.name}</div>
<div class="bestiary-card__danger bestiary-card__danger--${item.dangerLevel}">${dangerLabels[item.dangerLevel]}</div>
<div class="bestiary-card__regions">
${[...item.regions].sort((a, b) => a.localeCompare(b, 'ru')).map(r => `<span class="bestiary-card__region">${r}</span>`).join('')}
</div>
<div class="codex-page__card-footer">
${priceHtml}
${item.mageOnly ? `<span class="bestiary-card__mage-only" data-tooltip="только для магов"><i class="fa-solid fa-wand-magic-sparkles"></i></span>` : ''}
</div>
</div>
</div>`;
}).join('')}</div>
</div>`).join('');
const allItems = document.BESTIARY_DATA.sections.flatMap(s => s.items);
root.querySelectorAll('.codex-page__card').forEach(card => {
const handler = () => {
const item = allItems.find(i => i.id === card.dataset.id);
if (item) openDetail(item, card);
};
card.addEventListener('click', handler);
card.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') handler(); });
});
};
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeDetail(); });
render();
};
const s = document.createElement('script');
s.src = 'https://forumstatic.ru/files/001c/77/b6/81176.js';
s.onload = renderBestiary;
document.head.appendChild(s);
</script>
[/html]
[hideprofile]



































