[html]<!-- Морской бой на базе Deff-лотереи -->
<div id="battleship"></div>
<script>
(function(){
const topicId = 2; // ID темы Rusff
const gameMaster = 'admin'; // Автор поста
const size = 10;
const letters = 'ABCDEFGHIJ'.split('');
const turnDelay = 2*60*1000; // 2 минуты
const wrap = document.getElementById('battleship');
wrap.innerHTML = `
<style>
#battleship table {border-collapse: collapse; margin:0 auto;}
#battleship td {width:42px;height:42px;border:1px solid #444;text-align:center;vertical-align:middle;cursor:pointer;background:#b0d4ff;font-weight:bold;}
#battleship td.hit {background:#f00 url(https://forumstatic.ru/files/0014/cc/0a/39265.png) no-repeat center/70%;}
#battleship td.miss {background:#9bb;opacity:0.7;}
#battleship th {background:#e0f0ff;padding:5px;}
#reset-game {margin-top:10px;display:none;}
</style>
<h2>⚓ Морской бой</h2>
<div id="battle-field"></div>
<button id="reset-game">Сбросить поле</button>
<p id="game-status" style="margin-top:10px;font-weight:bold;"></p>
`;
// ---------------- Создаем поле ----------------
function createField(){
let html = '<table><tr><th></th>';
for(let i=1;i<=size;i++) html+='<th>'+i+'</th>';
html+='</tr>';
for(let r=0;r<size;r++){
html+='<tr><th>'+letters[r]+'</th>';
for(let c=1;c<=size;c++){
html+='<td data-cell="'+letters[r]+c+'"></td>';
}
html+='</tr>';
}
html+='</table>';
document.getElementById('battle-field').innerHTML = html;
}
// ---------------- Расставляем корабли случайно ----------------
function placeShips(){
const shipsLen = [4,3,3,2,2,2,1,1,1,1];
const taken = new Set();
function randomDir(){return Math.random()<0.5?'h':'v';}
function canPlace(r,c,len,dir){
for(let i=0;i<len;i++){
let rr=r+(dir==='v'?i:0), cc=c+(dir==='h'?i:0);
if(rr>=size||cc>=size) return false;
const key=rr+'_'+cc;
if(taken.has(key)) return false;
for(let dr=-1;dr<=1;dr++)
for(let dc=-1;dc<=1;dc++)
if(taken.has((rr+dr)+'_'+(cc+dc))) return false;
}
return true;
}
const ships=[];
shipsLen.forEach(len=>{
let placed=false;
while(!placed){
let r=Math.floor(Math.random()*size);
let c=Math.floor(Math.random()*size);
let dir=randomDir();
if(canPlace(r,c,len,dir)){
for(let i=0;i<len;i++){
let rr=r+(dir==='v'?i:0), cc=c+(dir==='h'?i:0);
taken.add(rr+'_'+cc);
ships.push(rr+'_'+cc);
}
placed=true;
}
}
});
return ships;
}
// ---------------- Состояние игры ----------------
let state = {
ships: [], // клетки кораблей
shots: {}, // все выстрелы
lastShot:{} // время последнего выстрела игроков
};
// ---------------- Конвертируем ячейку ----------------
function convertCell(cell){
const row = letters.indexOf(cell[0]);
const col = parseInt(cell.slice(1))-1;
return row+'_'+col;
}
// ---------------- Отправляем сообщение в тему ----------------
function postToTopic(msg, hit){
$.post('/misc.php?item=ajax_lottery', {
a:'message',
id: topicId,
msg: ''+msg+'',
color: hit?'red':'blue'
});
document.getElementById('game-status').textContent = msg;
}
// ---------------- Рендер ----------------
function render(){
document.querySelectorAll('#battleship td[data-cell]').forEach(td=>{
const cell=td.dataset.cell;
td.className='';
if(state.shots[cell]==='miss') td.classList.add('miss');
else if(state.shots[cell]==='hit') td.classList.add('hit');
});
}
// ---------------- Обработка кликов ----------------
function activate(){
document.querySelectorAll('#battleship td[data-cell]').forEach(td=>{
td.addEventListener('click', e=>{
const user = FORUM.user.name;
const now = Date.now();
if(state.lastShot[user] && now - state.lastShot[user]<turnDelay){
alert('Можно стрелять раз в 2 минуты.');
return;
}
const cell = e.target.dataset.cell;
if(state.shots[cell]) return;
const hit = state.ships.includes(convertCell(cell));
state.shots[cell] = hit ? 'hit':'miss';
state.lastShot[user]=now;
render();
saveState();
postToTopic('Игрок '+user+' выстрелил в '+cell+' — '+(hit?'Попал!':'Мимо.'), hit);
});
});
if(FORUM.user.name===gameMaster){
const btn=document.getElementById('reset-game');
btn.style.display='inline-block';
btn.onclick=function(){
if(confirm('Сбросить поле и расставить корабли заново?')){
state.ships=[]; state.shots={}; state.lastShot={};
state.ships=placeShips();
render(); saveState();
postToTopic('⚓ Поле сброшено администратором!', false);
}
};
}
}
// ---------------- Сохраняем и загружаем ----------------
function saveState(){
$.post('/misc.php?item=ajax_lottery', {
a:'save',
id: topicId,
data: JSON.stringify(state)
});
}
function loadState(cb){
$.get('/misc.php?item=ajax_lottery&id='+topicId+'&action=get', function(res){
try { state=JSON.parse(res); } catch(e){ state={ships:[],shots:{},lastShot:{}}; }
if(!state.ships.length) state.ships=placeShips();
if(cb) cb();
});
}
// ---------------- Инициализация ----------------
createField();
loadState(function(){
render();
activate();
});
})();
</script>[/html]