[html]<!-- Морской бой на базе Deff-лотереи -->
<div id="battleship"></div>
<script>
(function(){
const topicId = 3; // 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]
e=t.value,e instanceof o?e:new o((function(t){t(e)}))).then(s,u)}a((r=r.apply(t,e||[])).next())}))},n=this&&this.__generator||function(t,e){var o,r,n,i,s={label:0,sent:function(){if(1&n[0])throw n[1];return n[1]},trys:[],ops:[]};return i={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function u(i){return function(u){return function(i){if(o)throw new TypeError("Generator is already executing.");for(;s;)try{if(o=1,r&&(n=2&i[0]?r.return:i[0]?r.throw||((n=r.return)&&n.call(r),0):r.next)&&!(n=n.call(r,i[1])).done)return n;switch(r=0,n&&(i=[2&i[0],n.value]),i[0]){case 0:case 1:n=i;break;case 4:return s.label++,{value:i[1],done:!1};case 5:s.label++,r=i[1],i=[0];continue;case 7:i=s.ops.pop(),s.trys.pop();continue;default:if(!((n=(n=s.trys).length>0&&n[n.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!n||i[1]>n[0]&&i[1]<n[3])){s.label=i[1];break}if(6===i[0]&&s.label<n[1]){s.label=n[1],n=i;break}if(n&&s.label<n[2]){s.label=n[2],s.ops.push(i);break}n[2]&&s.ops.pop(),s.trys.pop();continue}i=e.call(t,s)}catch(t){i=[6,t],r=0}finally{o=n=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,u])}}},i=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(e,"__esModule",{value:!0});var s=i(o(831)),u=function(){function t(t,e){this.originalHost=t,!t&&void 0!==typeof window&&document.location.origin&&(this.originalHost=document.location.origin),this.path="/api.php",this.format=e&&e.format||"json",this.charset=e&&e.charset||"utf-8",this.init()}return t.prototype.init=function(){this.parseUrl(this.originalHost)},t.prototype.parseUrl=function(t){if(!t)throw new Error("Hostname not specified and should be a string");var e=/^(((([^:\/#\?]+
?(?