[html]<!-- =====================================================
FOG OF WAR MAP - IMAGE VERSION (FINAL FIXED)
Исправленная версия с подсказками и правильной логикой видимости
===================================================== -->
<!-- Место для размещения карты на форуме -->
<div id="fow-map-container"></div>
<style>
/* --- Основной контейнер карты --- */
#fow-map-container {
display: inline-block;
background: #f0f0f0;
padding: 20px;
border-radius: 10px;
font-family: Arial, Helvetica, sans-serif;
border: 3px solid #888888;
}
/* --- Сетка карты --- */
#fow-grid {
display: grid;
background: #ffffff;
border: 2px solid #333333;
}
/* --- Ячейка карты --- */
.fow-cell {
width: 44px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #bbbbbb;
background: #fafafa;
box-sizing: border-box;
}
/* Стена */
.fow-cell.fow-wall {
background: #c0c0c0;
border: 1px solid #999999;
}
.fow-cell.fow-wall img {
width: 36px;
height: 36px;
}
/* Открытая ячейка (пустая) */
.fow-cell.fow-revealed {
background: #ffffff;
}
/* Враг */
.fow-cell.fow-enemy {
cursor: help;
background: #fff0f0;
}
.fow-cell.fow-enemy img {
width: 36px;
height: 36px;
}
/* Предмет */
.fow-cell.fow-item {
cursor: help;
background: #f0fff0;
}
.fow-cell.fow-item img {
width: 36px;
height: 36px;
}
/* Скрытая ячейка (туман) */
.fow-cell.fow-hidden {
background: #e0e0e0;
}
.fow-cell.fow-hidden img {
width: 30px;
height: 30px;
opacity: 0.6;
}
/* --- Подсказка (tooltip) --- */
#fow-tooltip {
position: fixed;
z-index: 999999;
background: #ffffff;
color: #333333;
padding: 12px 16px;
border-radius: 8px;
font-size: 13px;
max-width: 260px;
box-shadow: 0 4px 25px rgba(0, 0, 0, 0.4);
border: 2px solid #333333;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s, visibility 0.2s;
pointer-events: none;
}
#fow-tooltip.fow-visible {
opacity: 1;
visibility: visible;
}
#fow-tooltip .fow-title {
font-weight: bold;
font-size: 15px;
margin-bottom: 8px;
color: #cc0000;
padding-bottom: 6px;
border-bottom: 1px solid #dddddd;
}
#fow-tooltip .fow-desc {
line-height: 1.45;
color: #555555;
}
</style>
<!-- Подсказка -->
<div id="fow-tooltip">
<div class="fow-title"></div>
<div class="fow-desc"></div>
</div>
<script>
(function() {
// ========================================
// КОНФИГУРАЦИЯ КАРТЫ
// ========================================
var fowConfig = {
// Размер карты (NxN клеток)
gridSize: 10,
// Открытые клетки (координаты: буква столбца + цифра строки)
// ТОЛЬКО эти клетки будут показаны (кроме стен рядом с ними)
openedCells: ['a1', 'b2', 'e9'],
// Стена (какие клетки являются стенами)
wallCells: ['a2', 'b5', 'c5', 'd5', 'e5', 'f5', 'g5', 'h5', 'i5', 'j5'],
// Враги на карте
enemyCells: {
'b6': { name: 'Гоблин', desc: 'Маленький, но коварный противник. Живёт в норах и нападает из засады.' },
'h8': { name: 'Скелет-воин', desc: 'Бывший страж подземелья. Опасен вблизи, но медлителен.' }
},
// Предметы на карте
itemCells: {
'e9': { name: 'Серебряный ключ', desc: 'Старый ключ, покрытый патиной. Открывает потайные двери.' },
'c3': { name: 'Зелье здоровья', desc: 'Восстанавливает 50 единиц здоровья.' }
},
// URL изображений
images: {
wall: 'https://cdn-icons-png.flaticon.com/512/18753/18753489.png',
enemy: 'https://cdn-icons-png.flaticon.com/512/3643/3643383.png',
item: 'https://cdn-icons-png.flaticon.com/512/3410/3410548.png',
fog: 'https://cdn-icons-png.flaticon.com/512/2675/2675962.png'
}
};
// ========================================
// СИСТЕМНЫЙ КОД
// ========================================
function makeCoord(col, row) {
return String.fromCharCode(97 + col) + (row + 1);
}
function parseCoord(coord) {
return {
col: coord.charCodeAt(0) - 97,
row: parseInt(coord.slice(1)) - 1
};
}
function getNeighborCoords(coord) {
var p = parseCoord(coord);
var neighbors = [];
for (var dc = -1; dc <= 1; dc++) {
for (var dr = -1; dr <= 1; dr++) {
if (dc === 0 && dr === 0) continue;
var nc = p.col + dc;
var nr = p.row + dr;
if (nc >= 0 && nc < fowConfig.gridSize && nr >= 0 && nr < fowConfig.gridSize) {
neighbors.push(makeCoord(nc, nr));
}
}
}
return neighbors;
}
function isWallCell(coord) {
return fowConfig.wallCells.indexOf(coord) !== -1;
}
function isOpenedCell(coord) {
return fowConfig.openedCells.indexOf(coord) !== -1;
}
function hasEnemy(coord) {
return fowConfig.enemyCells.hasOwnProperty(coord);
}
function hasItem(coord) {
return fowConfig.itemCells.hasOwnProperty(coord);
}
// Проверка: должна ли клетка быть видимой
function shouldBeVisible(coord) {
// Явно открытая клетка - видна
if (isOpenedCell(coord)) return true;
// Враги и предметы видны ТОЛЬКО если их клетка явно открыта
if (hasEnemy(coord) || hasItem(coord)) return false;
// Стена видна, если она рядом с открытой клеткой
if (isWallCell(coord)) {
var neighbors = getNeighborCoords(coord);
for (var i = 0; i < neighbors.length; i++) {
if (isOpenedCell(neighbors[i])) return true;
}
}
return false;
}
function createImg(src, alt) {
var img = document.createElement('img');
img.src = src;
img.alt = alt;
img.style.display = 'block';
img.style.width = '100%';
img.style.height = 'auto';
return img;
}
function showTooltip(title, desc, event) {
var tip = document.getElementById('fow-tooltip');
tip.querySelector('.fow-title').textContent = title;
tip.querySelector('.fow-desc').textContent = desc;
tip.classList.add('fow-visible');
var padding = 15;
var x = event.clientX + padding;
var y = event.clientY + padding;
var rect = tip.getBoundingClientRect();
if (x + rect.width > window.innerWidth - 10) {
x = event.clientX - rect.width - padding;
}
if (y + rect.height > window.innerHeight - 10) {
y = event.clientY - rect.height - padding;
}
tip.style.left = x + 'px';
tip.style.top = y + 'px';
}
function hideTooltip() {
document.getElementById('fow-tooltip').classList.remove('fow-visible');
}
function updateTooltipPosition(event) {
var tip = document.getElementById('fow-tooltip');
if (!tip.classList.contains('fow-visible')) return;
var padding = 15;
var x = event.clientX + padding;
var y = event.clientY + padding;
var rect = tip.getBoundingClientRect();
if (x + rect.width > window.innerWidth - 10) {
x = event.clientX - rect.width - padding;
}
if (y + rect.height > window.innerHeight - 10) {
y = event.clientY - rect.height - padding;
}
tip.style.left = x + 'px';
tip.style.top = y + 'px';
}
function createMap() {
var root = document.getElementById('fow-map-container');
if (!root) return;
var oldGrid = document.getElementById('fow-grid');
if (oldGrid) oldGrid.remove();
var grid = document.createElement('div');
grid.id = 'fow-grid';
grid.style.gridTemplateColumns = 'repeat(' + fowConfig.gridSize + ', 44px)';
grid.style.gridTemplateRows = 'repeat(' + fowConfig.gridSize + ', 44px)';
for (var row = 0; row < fowConfig.gridSize; row++) {
for (var col = 0; col < fowConfig.gridSize; col++) {
var coord = makeCoord(col, row);
var cell = document.createElement('div');
cell.className = 'fow-cell';
cell.setAttribute('data-coord', coord);
var isWall = isWallCell(coord);
var isOpened = isOpenedCell(coord);
var isVisible = shouldBeVisible(coord);
var enemy = fowConfig.enemyCells[coord];
var item = fowConfig.itemCells[coord];
if (isWall) {
if (isVisible) {
cell.classList.add('fow-wall');
cell.appendChild(createImg(fowConfig.images.wall, 'Стена'));
} else {
cell.classList.add('fow-hidden');
cell.appendChild(createImg(fowConfig.images.fog, 'Туман'));
}
} else if (isVisible) {
cell.classList.add('fow-revealed');
if (enemy) {
cell.classList.add('fow-enemy');
cell.appendChild(createImg(fowConfig.images.enemy, 'Враг'));
cell.setAttribute('data-name', enemy.name);
cell.setAttribute('data-desc', enemy.desc);
} else if (item) {
cell.classList.add('fow-item');
cell.appendChild(createImg(fowConfig.images.item, 'Предмет'));
cell.setAttribute('data-name', item.name);
cell.setAttribute('data-desc', item.desc);
}
} else {
cell.classList.add('fow-hidden');
cell.appendChild(createImg(fowConfig.images.fog, 'Туман'));
}
grid.appendChild(cell);
}
}
root.appendChild(grid);
}
// Получить родительскую ячейку от элемента (для обработки img внутри ячейки)
function getCellFromTarget(target) {
// Проверяем сам элемент
if (target.classList && target.classList.contains('fow-cell')) {
return target;
}
// Проверяем родителя (если это img внутри ячейки)
if (target.parentNode && target.parentNode.classList) {
if (target.parentNode.classList.contains('fow-cell')) {
return target.parentNode;
}
// Проверяем бабушку (вдруг img вложен глубже)
if (target.parentNode.parentNode && target.parentNode.parentNode.classList) {
if (target.parentNode.parentNode.classList.contains('fow-cell')) {
return target.parentNode.parentNode;
}
}
}
return null;
}
function setupEventListeners() {
document.addEventListener('mouseover', function(event) {
var cell = getCellFromTarget(event.target);
if (!cell) return;
if (cell.classList.contains('fow-enemy')) {
showTooltip(cell.getAttribute('data-name'), cell.getAttribute('data-desc'), event);
} else if (cell.classList.contains('fow-item')) {
showTooltip(cell.getAttribute('data-name'), cell.getAttribute('data-desc'), event);
}
});
document.addEventListener('mousemove', updateTooltipPosition);
document.addEventListener('mouseout', function(event) {
var cell = getCellFromTarget(event.target);
if (!cell) return;
if (cell.classList.contains('fow-enemy') || cell.classList.contains('fow-item')) {
hideTooltip();
}
});
}
function init() {
createMap();
setupEventListeners();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
[/html]
















