Здесь делается вжух 🪄

test

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » test » новый не новый » морской бой


морской бой

Сообщений 1 страница 11 из 11

1

[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]

0

2

https://upforme.ru/uploads/001c/84/76/2/680908.gif

[html]<h2 style="text-align:center;">⚓ Морской бой</h2>
<p style="text-align:center;">Сделайте выстрел! (1 раз в 2 минуты)</p>

<div id="battlefield"></div>
<div id="status" style="text-align:center; margin:10px; font-size:16px;"></div>

<style>
#battlefield {
  display: grid;
  grid-template-columns: repeat(9, 50px);
  grid-template-rows: repeat(9, 50px);
  gap: 4px;
  justify-content: center;
  margin: 20px auto;
}
.cell {
  width: 50px;
  height: 50px;
  background: #9bb7d4;
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  color: white;
  font-weight: bold;
  user-select: none;
}
.cell.hit { background: #e63946; }
.cell.miss { background: #457b9d; }
</style>

<script>
(async function() {
  const topicId = 3; // id темы игры
  const gridSize = 9;
  const cooldown = 2 * 60 * 1000; // 2 минуты
  const storageKey = "sea_last_shot_time";
  const ships = new Set(["0,0","0,1","2,5","3,5","5,3","7,8"]); // только ведущий знает

  const field = document.getElementById("battlefield");
  const status = document.getElementById("status");

  // Создаем пустую сетку
  const cells = [];
  for (let y = 0; y < gridSize; y++) {
    for (let x = 0; x < gridSize; x++) {
      const div = document.createElement("div");
      div.className = "cell";
      div.dataset.x = x;
      div.dataset.y = y;
      field.appendChild(div);
      cells.push(div);
    }
  }

  // Загружаем ходы из темы
  async function loadShots() {
    if (typeof FORUM === "undefined" || !FORUM.post) return;
    const posts = await FORUM.post.get(topicId);
    posts.forEach(p => {
      const match = p.message.match(/x=(\d+),y=(\d+),(мимо|попадание)/);
      if (match) {
        const [_, x, y, res] = match;
        const cell = cells.find(c => c.dataset.x == x && c.dataset.y == y);
        if (cell) cell.classList.add(res === "попадание" ? "hit" : "miss");
      }
    });
  }

  await loadShots();

  function canShoot() {
    const last = +localStorage.getItem(storageKey) || 0;
    return Date.now() - last > cooldown;
  }

  async function shoot(cell) {
    if (!canShoot()) {
      const wait = Math.ceil((cooldown - (Date.now() - +localStorage.getItem(storageKey))) / 60000);
      status.textContent = `⏳ Стрельнуть можно через ${wait} мин.`;
      return;
    }

    const pos = `${cell.dataset.x},${cell.dataset.y}`;
    const hit = ships.has(pos);
    cell.classList.add(hit ? "hit" : "miss");

    const text = hit ? "💥 попадание" : "💧 мимо";
    status.textContent = `${text} (${pos})`;
    localStorage.setItem(storageKey, Date.now());

    // Публикуем результат в теме
    if (typeof FORUM !== "undefined" && FORUM.post) {
      await FORUM.post.add(topicId, `x=${cell.dataset.x},y=${cell.dataset.y},${hit ? "попадание" : "мимо"}`);
    }
  }

  field.addEventListener("click", e => {
    if (!e.target.classList.contains("cell")) return;
    shoot(e.target);
  });
})();
</script>[/html]

0

3

[html]<h2 style="text-align:center;">⚓ Морской бой</h2>
<p style="text-align:center;">Сделайте выстрел! (1 раз в 2 минуты)</p>

<div id="battlefield"></div>
<div id="status" style="text-align:center; margin:10px; font-size:16px;"></div>

<style>
#battlefield {
  display: grid;
  grid-template-columns: repeat(9, 50px);
  grid-template-rows: repeat(9, 50px);
  gap: 4px;
  justify-content: center;
  margin: 20px auto;
}
.cell {
  width: 50px;
  height: 50px;
  background: #9bb7d4;
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  color: white;
  font-weight: bold;
  user-select: none;
}
.cell.hit { background: #e63946; }
.cell.miss { background: #457b9d; }
</style>

<script>
(async function() {
  const topicId = 3; // ID темы, где публикуются выстрелы
  const gridSize = 9;
  const cooldown = 2 * 60 * 1000; // 2 минуты
  const storageKey = "sea_last_shot_time";
  const ships = new Set(["0,0","0,1","2,5","3,5","5,3","7,8"]);

  const field = document.getElementById("battlefield");
  const status = document.getElementById("status");
  const cells = [];

  // 1️⃣ Создаем сетку
  for (let y = 0; y < gridSize; y++) {
    for (let x = 0; x < gridSize; x++) {
      const div = document.createElement("div");
      div.className = "cell";
      div.dataset.x = x;
      div.dataset.y = y;
      field.appendChild(div);
      cells.push(div);
    }
  }

  // 2️⃣ Загружаем выстрелы из темы (BB-код постов)
  async function loadShots() {
    try {
      const res = await fetch(`/viewtopic.php?id=${topicId}`);
      const text = await res.text();

      const matches = [...text.matchAll(/x=(\d+),y=(\d+),(мимо|попадание)/g)];
      matches.forEach(m => {
        const [_, x, y, res] = m;
        const cell = cells.find(c => c.dataset.x == x && c.dataset.y == y);
        if (cell) cell.classList.add(res === "попадание" ? "hit" : "miss");
      });
    } catch(e) {
      console.log("Ошибка загрузки постов", e);
    }
  }

  await loadShots();
  setInterval(loadShots, 10000); // обновляем каждые 10 сек

  // 3️⃣ Проверка кулдауна
  function canShoot() {
    const last = +localStorage.getItem(storageKey) || 0;
    return Date.now() - last > cooldown;
  }

  // 4️⃣ Отправка поста в тему
  async function postToTopic(message) {
    const formData = new FormData();
    formData.append("form_sent", "1");
    formData.append("csrf_token", FORUM.csrf_token || "");
    formData.append("req_message", message);
    formData.append("submit", "Отправить");

    await fetch(`/post.php?tid=${topicId}`, {
      method: "POST",
      body: formData,
      credentials: "same-origin"
    });
  }

  // 5️⃣ Выстрел
  async function shoot(cell) {
    if (!canShoot()) {
      const wait = Math.ceil((cooldown - (Date.now() - +localStorage.getItem(storageKey))) / 60000);
      status.textContent = `⏳ Стрельнуть можно через ${wait} мин.`;
      return;
    }

    const pos = `${cell.dataset.x},${cell.dataset.y}`;
    if (cell.classList.contains("hit") || cell.classList.contains("miss")) {
      status.textContent = "🚫 Уже стреляли сюда.";
      return;
    }

    const hit = ships.has(pos);
    cell.classList.add(hit ? "hit" : "miss");
    const result = hit ? "💥 попадание" : "💧 мимо";
    status.textContent = `${result} (${pos})`;
    localStorage.setItem(storageKey, Date.now());

    // 6️⃣ Автоматическая публикация
    await postToTopic(`x=${cell.dataset.x},y=${cell.dataset.y},${hit ? "попадание" : "мимо"}`);
    status.textContent += " — результат отправлен в тему!";
  }

  // 7️⃣ Обработчик клика
  field.addEventListener("click", e => {
    if (!e.target.classList.contains("cell")) return;
    shoot(e.target);
  });
})();
</script>[/html]

0

4

[html]<h2 style="text-align:center;">⚓ Морской бой</h2>
<p style="text-align:center;">Сделайте выстрел! (1 раз в 2 минуты)</p>

<div id="battlefield"></div>
<div id="status" style="text-align:center; margin:10px; font-size:16px;"></div>

<style>
#battlefield {
  display: grid;
  grid-template-columns: repeat(9, 50px);
  grid-template-rows: repeat(9, 50px);
  gap: 4px;
  justify-content: center;
  margin: 20px auto;
}
.cell {
  width: 50px;
  height: 50px;
  background: #9bb7d4;
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  color: white;
  font-weight: bold;
  user-select: none;
}
.cell.hit { background: #e63946; }
.cell.miss { background: #457b9d; }
</style>

<script>
(async function() {
  const topicId = 3; // ID темы с результатами
  const gridSize = 9;
  const cooldown = 2 * 60 * 1000; // 2 минуты
  const storageKey = "sea_last_shot_time";
  const ships = new Set(["0,0","0,1","2,5","3,5","5,3","7,8"]);

  const field = document.getElementById("battlefield");
  const status = document.getElementById("status");
  const cells = [];

  // 1️⃣ Создаём сетку
  for (let y = 0; y < gridSize; y++) {
    for (let x = 0; x < gridSize; x++) {
      const div = document.createElement("div");
      div.className = "cell";
      div.dataset.x = x;
      div.dataset.y = y;
      field.appendChild(div);
      cells.push(div);
    }
  }

  // 2️⃣ Загружаем все выстрелы из темы
  async function loadShots() {
    try {
      const res = await fetch(`/viewtopic.php?id=${topicId}`);
      const html = await res.text();
      const shots = [...html.matchAll(/x=(\d+),y=(\d+),(мимо|попадание)/g)];
      cells.forEach(c => c.classList.remove("hit","miss"));
      for (const match of shots) {
        const [_, x, y, result] = match;
        const cell = cells.find(c => c.dataset.x == x && c.dataset.y == y);
        if (cell) cell.classList.add(result === "попадание" ? "hit" : "miss");
      }
    } catch(e) {
      console.error("Ошибка загрузки темы:", e);
    }
  }

  // 3️⃣ Вызываем загрузку при открытии страницы
  await loadShots();

  // 4️⃣ Обновляем поле каждые 10 секунд (чтобы видеть выстрелы других)
  setInterval(loadShots, 10000);

  // 5️⃣ Проверка кулдауна
  function canShoot() {
    const last = +localStorage.getItem(storageKey) || 0;
    return Date.now() - last > cooldown;
  }

  // 6️⃣ Публикация поста в теме (имитация формы)
  async function postToTopic(message) {
    const formData = new FormData();
    formData.append("form_sent", "1");
    formData.append("csrf_token", FORUM.csrf_token || "");
    formData.append("req_message", message);
    formData.append("submit", "Отправить");

    await fetch(`/post.php?tid=${topicId}`, {
      method: "POST",
      body: formData,
      credentials: "same-origin"
    });
  }

  // 7️⃣ Действие при выстреле
  async function shoot(cell) {
    if (!canShoot()) {
      const wait = Math.ceil((cooldown - (Date.now() - +localStorage.getItem(storageKey))) / 60000);
      status.textContent = `⏳ Стрельнуть можно через ${wait} мин.`;
      return;
    }

    const pos = `${cell.dataset.x},${cell.dataset.y}`;
    if (cell.classList.contains("hit") || cell.classList.contains("miss")) {
      status.textContent = "🚫 Уже стреляли сюда.";
      return;
    }

    const hit = ships.has(pos);
    cell.classList.add(hit ? "hit" : "miss");
    const result = hit ? "💥 попадание" : "💧 мимо";
    status.textContent = `${result} (${pos})`;
    localStorage.setItem(storageKey, Date.now());

    // публикуем результат в теме
    await postToTopic(`x=${cell.dataset.x},y=${cell.dataset.y},${hit ? "попадание" : "мимо"}`);
    await loadShots(); // обновляем поле после публикации
  }

  // 8️⃣ Обработчик кликов
  field.addEventListener("click", e => {
    if (!e.target.classList.contains("cell")) return;
    shoot(e.target);
  });
})();
</script>[/html]

0

5

[html]<h2 style="text-align:center;">⚓ Морской бой</h2>
<p style="text-align:center;">Сделайте выстрел! (1 раз в 2 минуты)</p>

<div id="battlefield"></div>
<div id="status" style="text-align:center; margin:10px; font-size:16px;"></div>

<style>
#battlefield {
  display: grid;
  grid-template-columns: repeat(9, 50px);
  grid-template-rows: repeat(9, 50px);
  gap: 4px;
  justify-content: center;
  margin: 20px auto;
}
.cell {
  width: 50px;
  height: 50px;
  background: #9bb7d4;
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  color: white;
  font-weight: bold;
  user-select: none;
}
.cell.hit { background: #e63946; }
.cell.miss { background: #457b9d; }
</style>

<script src="https://cdn.jsdelivr.net/npm/@quadrosystems/mybb-sdk"></script>
<script>
(async function() {
  const topicId = 3; // ID темы с результатами
  const gridSize = 9;
  const cooldown = 2 * 60 * 1000; // 2 минуты
  const storageKey = "sea_last_shot_time";
  const ships = new Set(["0,0","0,1","2,5","3,5","5,3","7,8"]);

  const field = document.getElementById("battlefield");
  const status = document.getElementById("status");
  const cells = [];

  // Подключение MyBB SDK
  const mybb = new MyBBSDK({
    forumUrl: "https://lalamo.rusff.me", // замени на свой форум
    apiKey: "testtest0"
  });

  // Создаём сетку
  for (let y = 0; y < gridSize; y++) {
    for (let x = 0; x < gridSize; x++) {
      const div = document.createElement("div");
      div.className = "cell";
      div.dataset.x = x;
      div.dataset.y = y;
      field.appendChild(div);
      cells.push(div);
    }
  }

  // Загрузка выстрелов из темы через API (кастомное поле)
  async function loadShots() {
    try {
      const custom = await mybb.topic.getCustomField("sea_battles"); // ключ хранения
      const shots = custom ? JSON.parse(custom) : [];
      cells.forEach(c => c.classList.remove("hit","miss"));
      for (const s of shots) {
        const cell = cells.find(c => c.dataset.x==s.x && c.dataset.y==s.y);
        if(cell) cell.classList.add(s.hit?"hit":"miss");
      }
    } catch(e) {
      console.error("Ошибка загрузки:", e);
    }
  }

  await loadShots();
  setInterval(loadShots, 10000); // обновление каждые 10 секунд

  function canShoot() {
    const last = +localStorage.getItem(storageKey) || 0;
    return Date.now() - last > cooldown;
  }

  async function shoot(cell) {
    if(!canShoot()) {
      const wait = Math.ceil((cooldown - (Date.now() - +localStorage.getItem(storageKey))) / 60000);
      status.textContent = `⏳ Стрельнуть можно через ${wait} мин.`;
      return;
    }

    const x = +cell.dataset.x;
    const y = +cell.dataset.y;
    if(cell.classList.contains("hit") || cell.classList.contains("miss")) {
      status.textContent = "�� Уже стреляли сюда.";
      return;
    }

    const hit = ships.has(`${x},${y}`);
    cell.classList.add(hit?"hit":"miss");
    status.textContent = hit ? `�� попадание (${x},${y})` : `�� мимо (${x},${y})`;
    localStorage.setItem(storageKey, Date.now());

    // Сохраняем ход через кастомное поле
    try {
      const custom = await mybb.topic.getCustomField("sea_battles");
      const shots = custom ? JSON.parse(custom) : [];
      shots.push({x, y, hit});
      await mybb.topic.setCustomField("sea_battles", JSON.stringify(shots));

      // Автоматически публикуем сообщение
      await mybb.topic.postReply(`x=${x},y=${y},${hit?"попадание":"мимо"}`);
    } catch(e) { console.error("Ошибка записи:", e); }

    await loadShots();
  }

  field.addEventListener("click", e=>{
    if(!e.target.classList.contains("cell")) return;
    shoot(e.target);
  });
})();
</script>[/html]

0

6

[html]
<script>/**
* Skipped minification because the original files appears to be already minified.
* Original file: /npm/@quadrosystems/mybb-sdk@0.9.7/lib/index.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("cross-fetch")):"function"==typeof define&&define.amd?define(["cross-fetch"],e):"object"==typeof exports?exports.MybbSDK=e(require("cross-fetch")):t.MybbSDK=e(t.fetch)}(this,(function(t){return(()=>{"use strict";var e={607:function(t,e,o){var r=this&&this.__awaiter||function(t,e,o,r){return new(o||(o=Promise))((function(n,i){function s(t){try{a(r.next(t))}catch(t){i(t)}}function u(t){try{a(r.throw(t))}catch(t){i(t)}}function a(t){var e;t.done?n(t.value):(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=/^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/.exec(t);if(this.hostname=e[11],this.protocol=e[4],!this.hostname)throw new Error("Cannot get hostname from arguments")},t.prototype.setToken=function(t){return this.token=t,this},t.prototype.withHash=function(t){return this.hash=t,this},t.prototype.getBoard=function(t){return this.call("board.get",t)},t.prototype.getForums=function(t){return this.call("board.getForums",t)},t.prototype.getFunds=function(){return this.call("board.getFunds")},t.prototype.getSubscriptions=function(t){return this.call("board.getSubscriptions",t)},t.prototype.getFileMetadata=function(t){return this.call("board.getFileMetadata",t)},t.prototype.auth=function(t,e){return this.call("board.auth",{login:t,password:e})},t.prototype.getUsers=function(t){return this.call("users.get",t)},t.prototype.getOnlineUsers=function(t){return this.call("users.getOnline",t)},t.prototype.getRespect=function(t){return this.call("users.getRespect",t)},t.prototype.getTopics=function(t){return this.call("topic.get",t)},t.prototype.getRecent=function(t){return this.call("topic.getRecent",t)},t.prototype.getPosts=function(t){return this.call("post.get",t)},t.prototype.getPostVotes=function(t){return this.call("post.getVotes",t)},t.prototype.storageSet=function(t,e,o){return this.call("storage.set",{token:this.token,key:t,value:e,action:o})},t.prototype.storageGet=function(t){return this.call("storage.get",{token:this.token,key:t})},t.prototype.storageKeys=function(){return this.call("storage.keys")},t.prototype.storageDelete=function(t){return this.call("storage.delete",{token:this.token,key:t})},t.prototype.storageFlush=function(){return this.call("storage.flush",{token:this.token})},t.prototype.call=function(t,e,o,i){return r(this,void 0,void 0,(function(){var r,s;return n(this,(function(n){switch(n.label){case 0:return r={host:this.hostname,port:"http:"===this.protocol?80:443,path:this.path,apiMethod:t,method:"GET"},[4,this.request(r,e)];case 1:if((s=n.sent()).response)return"function"==typeof o&&o(s.response),[2,s.response];if(s.error){if(console.error(s),"function"!=typeof i)throw new Error(s.error);i(s.error)}return[2]}}))}))},t.prototype.request=function(t,e){return r(this,void 0,void 0,(function(){var o,r,i,u,a=this;return n(this,(function(n){for(r in(o=[]).push("method="+t.apiMethod),o.push("format="+this.format),o.push("charset="+this.charset),e)if(e.hasOwnProperty(r)){if(void 0===e[r])continue;i=Array.isArray(e[r])?e[r].map(encodeURIComponent).join(","):encodeURIComponent(e[r]),o.push(r+"="+i)}return u=[t.protocol,"//",t.host,t.path,"?",o.join("&")].join(""),[2,(0,s.default)(u,{method:t.method||"GET",headers:{Cookie:this.hash?"mybb_ru="+this.hash:void 0},mode:"cors",credentials:"same-origin"}).then((function(t){return"json"===a.format?t.json():t.text()}))]}))}))},t}();e.default=u},831:e=>{e.exports=t}},o={},r=function t(r){var n=o[r];if(void 0!==n)return n.exports;var i=o[r]={exports:{}};return e[r].call(i.exports,i,i.exports,t),i.exports}(607);return r.default})()}));</script>

<div style="margin: 20px; padding: 15px; border: 2px dashed #00bfff; background: #f0f8ff; text-align: center;">
    <p style="font-weight: bold;">Тест Функции Постинга</p>
    <button id="testPostButton" style="padding: 10px 20px; background: #00bfff; color: white; border: none; cursor: pointer; border-radius: 5px;" disabled>
        Нажать для Отправки Тестового Сообщения
    </button>
    <p id="testStatus" style="margin-top: 10px; min-height: 20px; color: #333;">Ожидание загрузки MyBB SDK...</p>
</div>

<script>

window.onload = function() {
    let testThreadID = null;
    const statusDisplay = document.getElementById("testStatus");
    const testButton = document.getElementById("testPostButton");

    // 1. Асинхронная функция инициализации для получения ID темы
    async function initializeTest() {
        statusDisplay.textContent = "Инициализация...";

        // *** ГЛАВНАЯ ПРОВЕРКА ***
        if (typeof MYBB === 'undefined' || typeof MYBB.thread === 'undefined') {
             statusDisplay.textContent = "Критическая ошибка: MyBB SDK не загружен или не определен.";
             console.error("SDK Error: MYBB object not available.");
             return;
        }

        try {
            const threadDetails = await MYBB.thread.getDetails();
            testThreadID = threadDetails.tid;
           
            if (testThreadID) {
                statusDisplay.textContent = `Готово к тесту! ID Темы: ${testThreadID}.`;
                testButton.disabled = false;
            } else {
                statusDisplay.textContent = "Ошибка: Не удалось получить ID темы.";
            }

        } catch (e) {
            statusDisplay.textContent = "Ошибка SDK: Не удалось получить детали темы (проверьте авторизацию).";
            console.error("SDK Error:", e);
        }
    }

    // 2. Функция отправки сообщения
    testButton.onclick = async function() {
        // ... (остальная логика постинга без изменений) ...
        if (!testThreadID) {
            statusDisplay.textContent = "Ошибка: ID темы не установлен.";
            return;
        }
       
        testButton.disabled = true;
        statusDisplay.textContent = "Отправка сообщения...";
       
        const testContent = `
[center]✅ АВТОМАТИЧЕСКИЙ ТЕСТ ПОСТИНГА УСПЕШЕН!
Сообщение отправлено скриптом в ${new Date().toLocaleTimeString()}
[/center]
`;

        try {
            await MYBB.thread.reply(testThreadID, testContent);
            statusDisplay.style.color = 'green';
            statusDisplay.textContent = "УСПЕХ! Тестовое сообщение отправлено на форум.";
           
        } catch (error) {
            statusDisplay.style.color = 'red';
            statusDisplay.textContent = "СБОЙ! Проверьте, авторизованы ли вы и консоль F12.";
            console.error("Постинг СБОЙ:", error);
        } finally {
            testButton.disabled = false;
        }
    };

    // Запуск инициализации после загрузки всех ресурсов
    initializeTest();
};
</script>
[/html]

---

**Если и этот код выдает ту же ошибку:** Это означает, что **администраторы вашего форума (RusFF)** должны разрешить использование внешнего скрипта `cdn.jsdelivr.net/...`. Без загрузки этого файла постинг через SDK **невозможен**.

0

7

[html]
<div id="slotMachine" style="width:90%; max-width:600px; background:#1c1b1b; color:#fff; border-radius:10px; padding:2%; text-align:center; font-family:Georgia; margin:2% auto; box-shadow:0 0 10px rgba(0,0,0,0.6); position:relative;">
    <div style="position:absolute; top:8px; left:8px; font-size:0.9em; color:#ffdd8a; text-align:left;">
        �� <span id="userName">Гость</span><br>
        �� <span id="userCoins">0</span>
    </div>
    <b style="font-size:1.3em;">�� Слот-машина</b>
    <div style="margin-top:1%; font-size:0.9em; color:#d9c7a1;">
        ⏳ Новый спин через: <span id="timer" style="font-weight:bold; color:#ffdd8a;"></span>
    </div>

    <div style="display:flex; justify-content:center; gap:2%; margin:3% 0;">
        <div class="slotCell" id="slot1" style="flex:1; aspect-ratio:1; background:#000; border:0.3% solid #7c653f; border-radius:8px; display:flex; align-items:center; justify-content:center;"></div>
        <div class="slotCell" id="slot2" style="flex:1; aspect-ratio:1; background:#000; border:0.3% solid #7c653f; border-radius:8px; display:flex; align-items:center; justify-content:center;"></div>
        <div class="slotCell" id="slot3" style="flex:1; aspect-ratio:1; background:#000; border:0.3% solid #7c653f; border-radius:8px; display:flex; align-items:center; justify-content:center;"></div>
    </div>

    <button id="spinBtn" style="padding:1% 3%; cursor:pointer; background:#b8863b; color:#fff; border:0.3% solid #000; font-weight:bold; border-radius:6px; box-shadow:0 0 5px rgba(0,0,0,0.6);">
        Крутить ��
    </button>

    <div id="winDisplay" style="margin-top:12px; font-size:1.1em; font-weight:bold; color:#ffdd00; min-height:2.2em;"></div>
</div>

<style>
.winGlow { animation: glowPulse 1s infinite alternate; }
@keyframes glowPulse {
  from { text-shadow: 0 0 6px #ffd700; color:#fff2b8; }
  to { text-shadow: 0 0 18px #ffd700, 0 0 36px #ffb700; color:#fff9db; }
}
</style>

<script>
/* --- НАСТРОЙКИ --- */
const COOLDOWN_MINUTES = 0; // <-- УСТАНОВИТЬ ВРУЧНУЮ
const COOLDOWN_MS = COOLDOWN_MINUTES * 60000;

/* Символы */
const symbols = [
  "https://upforme.ru/uploads/001c/84/76/2/793146.png", // ��
  "https://upforme.ru/uploads/001c/84/76/2/635838.png", // ��
  "https://upforme.ru/uploads/001c/84/76/2/604915.png", // ��
  "https://upforme.ru/uploads/001c/84/76/2/703897.png"  // ❤️
];
/* ----------------- */

const ids = ["slot1", "slot2", "slot3"];
const timer = document.getElementById("timer");
const spinBtn = document.getElementById("spinBtn");
const winDisplay = document.getElementById("winDisplay");
let threadID = null;
let isUserLoggedIn = false; // Новый флаг
let userName = localStorage.getItem("slotUserName");
let userCoins = parseInt(localStorage.getItem("slotCoins") || "0");

const displayResult = (id, url) => {
    document.getElementById(id).innerHTML = `<img src="${url}" style="max-width:80%;max-height:80%;">`;
};

// Асинхронная инициализация пользователя и получение ID темы
async function initialize() {
  if (typeof MYBB !== 'undefined' && MYBB.user && MYBB.thread) {
    try {
        const userDetails = await MYBB.user.getDetails();
       
        // Проверяем, авторизован ли пользователь (uid > 0)
        isUserLoggedIn = userDetails.uid && userDetails.uid > 0;
       
        // Устанавливаем ник
        userName = userDetails.username || userName || (isUserLoggedIn ? "Игрок" : "Гость");
       
        // Получаем ID темы
        const threadDetails = await MYBB.thread.getDetails();
        threadID = threadDetails.tid;
       
        console.log(`[СЛОТ] User logged in: ${isUserLoggedIn}, Thread ID: ${threadID}`);
    } catch (e) {
        console.warn("[СЛОТ] MyBB SDK: Не удалось получить детали пользователя/темы.", e);
    }
  }

  // Запрашиваем ник у гостя, если не удалось получить его с форума и он не был сохранен
  if (!userName || userName === "Гость") {
      userName = prompt("Введите ваш ник:") || "Игрок";
  }
 
  localStorage.setItem("slotUserName", userName);
  document.getElementById("userName").textContent = userName;
  document.getElementById("userCoins").textContent = userCoins;
 
  updateTimer();
}

/* --- Функции Кулдауна (без изменений) --- */
const canSpin = () => {
  if (COOLDOWN_MINUTES === 0) return true;
  return Date.now() - (localStorage.getItem("slotLastSpin") || 0) >= COOLDOWN_MS;
};

const updateTimer = () => {
  let last = localStorage.getItem("slotLastSpin") || 0;
  let diff = COOLDOWN_MS - (Date.now() - last);
 
  if (diff <= 0 || COOLDOWN_MINUTES === 0) {
    timer.textContent = "готово ✅";
    spinBtn.disabled = false;
    return;
  }
 
  let minutes = Math.floor(diff/60000);
  let seconds = Math.floor((diff%60000)/1000);
  timer.textContent = `${minutes}м ${seconds}с`;
  spinBtn.disabled = true;
};
setInterval(updateTimer, 1000);

/* --- Функция Спина --- */
spinBtn.onclick = async function() {
  if(!canSpin()){
    alert("Подождите, ваш следующий ход будет через: " + timer.textContent);
    return;
  }

  // 1. Рандомизация
  let results = ids.map(() => symbols[Math.floor(Math.random() * symbols.length)]);
  ids.forEach((id, i) => displayResult(id, results[i]));

  // 2. Установка кулдауна
  localStorage.setItem("slotLastSpin", Date.now());
  updateTimer();

  // 3. Анализ и логика выигрыша
  let counts = {};
  results.forEach(s => counts[s] = (counts[s] || 0) + 1);
  let reward = 0;
  let winText = "Попробуй еще";
  let color = "#7c653f";
  let winDescription = "";
  const [s0, s1, s2, s3] = symbols;

  // (Логика выигрыша - сохранена без изменений)
  if (Object.values(counts).includes(3)) {
    reward = 500;
    winText = "�� ДЖЕКПОТ! Три одинаковых символа!";
    color = "#ffd700";
    winDescription = `�� ДЖЕКПОТ! Тройка! Выпало: -- -- -- — ВЫИГРЫШ: ${reward}!`;
  } else if (Object.values(counts).includes(2)) {
    reward = 100;
    winText = "✨ Пара! Получи бонус!";
    color = "#c0a060";
    winDescription = `�� Пара! Выпало: -- -- -- — ВЫИГРЫШ: ${reward}.`;
  } else if (results.includes(s0) && results.includes(s1) && results.includes(s3)) {
    reward = 300;
    winText = "�� КОМБО! Кристалл, Корона, Сердце!";
    color = "#ff6a9a";
    winDescription = `�� Комбо (0, 1, 3)! Выпало: -- -- -- — ВЫИГРЫШ: ${reward}.`;
  } else if (!results.includes(s2)) {
    reward = 50;
    winText = "�� Удача! Ни одного Черепа!";
    color = "#32cd32";
    winDescription = `�� Удача! Выпало: -- -- -- — ВЫИГРЫШ: ${reward}.`;
  }

  // 4. Обновление баланса и интерфейса
  userCoins += reward;
  localStorage.setItem("slotCoins", userCoins);
  document.getElementById("userCoins").textContent = userCoins;
  winDisplay.innerHTML = reward > 0 ? `<span class="winGlow">Победа!</span><br>${winText}` : winText;

  // 5. Автоматическая отправка сообщения на форум при выигрыше
  if (reward > 0) {
    if (!isUserLoggedIn) {
        winDisplay.innerHTML += `<br><span style="color:#f00; font-size:0.8em;">(Для постинга на форуме нужно авторизоваться!)</span>`;
        console.warn("[СЛОТ] Постинг невозможен: Пользователь не авторизован.");
        return;
    }
   
    if (!threadID) {
        winDisplay.innerHTML += `<br><span style="color:#f00; font-size:0.8em;">(Ошибка ID темы. Постинг невозможен.)</span>`;
        console.error("[СЛОТ] Постинг невозможен: Не удалось получить ID темы.");
        return;
    }

    const postContent = `
[center]Я только что крутил Слот-Машину и выиграл ${reward} монет!
${winDescription}
Мой текущий баланс: ${userCoins}
[/center]
`;
    try {
      // ПРОВЕРКА: Здесь происходит попытка отправки
      await MYBB.thread.reply(threadID, postContent);
      winDisplay.innerHTML += `<br><span style="color:#0f0; font-size:0.8em;">Сообщение успешно отправлено!</span>`;
     
    } catch (error) {
      console.error("[СЛОТ] Критическая ошибка при отправке сообщения:", error);
      winDisplay.innerHTML += `<br><span style="color:#f00; font-size:0.8em;">(Сбой постинга. Проверьте консоль F12!)</span>`;
    }
  }
};

// Запуск инициализации
initialize();
</script>
[/html]

После этих шагов ваш код слота должен наконец-то увидеть объект `MYBB` и выполнить функцию отправки сообщения без конфликтов.

0

8

[html]<div id="battle-container" style="text-align:center;">
  <h3>⚓ Морской бой — общее поле</h3>
  <p>Каждый игрок может стрелять 1 раз в 2 минуты.</p>
  <div id="battlefield"></div>
  <div id="battle-status" style="margin-top:8px;font-size:14px;"></div>
  <textarea id="battle-json" style="display:none;">[]</textarea>
</div>

<style>
#battlefield {
  display: grid;
  grid-template-columns: repeat(8, 48px);
  grid-template-rows: repeat(8, 48px);
  gap: 4px;
  justify-content: center;
  margin: 20px auto;
}
.cell {
  width: 48px; height: 48px;
  background: #9bb7d4;
  border-radius: 6px;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; color: #fff; font-weight: bold;
  user-select: none;
  transition: 0.3s;
}
.cell.hit { background: #d9534f; }
.cell.miss { background: #457b9d; }
</style>

<script>
(function(){
  const gridSize = 8;
  const cooldown = 120000; // 2 минуты
  const field = document.getElementById("battlefield");
  const status = document.getElementById("battle-status");
  const jsonField = document.getElementById("battle-json");

  let data = [];
  try { data = JSON.parse(jsonField.value) } catch(e){ data = [] }

  const cells = [];
  for (let y=0; y<gridSize; y++) {
    for (let x=0; x<gridSize; x++) {
      const div = document.createElement("div");
      div.className = "cell";
      div.dataset.x = x;
      div.dataset.y = y;
      field.appendChild(div);
      cells.push(div);
    }
  }

  function render() {
    cells.forEach(c => c.className = "cell");
    data.forEach(s => {
      const cell = cells.find(c => c.dataset.x==s.x && c.dataset.y==s.y);
      if(cell){
        cell.classList.add(s.result);
        cell.textContent = s.result === "hit" ? "✖" : "•";
        cell.title = `${s.user}: ${s.result === "hit" ? "попадание" : "промах"}`;
      }
    });
  }

  function canShoot() {
    const last = localStorage.getItem("sea_last_shot");
    return !last || Date.now() - last > cooldown;
  }

  field.addEventListener("click", e => {
    const cell = e.target.closest(".cell");
    if(!cell){
      return;
    }
    if(!canShoot()){
      status.textContent = "Вы можете стрелять 1 раз в 2 минуты.";
      return;
    }
    const x = +cell.dataset.x, y = +cell.dataset.y;
    if(data.some(s=>s.x==x && s.y==y)){
      status.textContent = "Уже стреляли сюда!";
      return;
    }

    // случайный результат
    const hit = Math.random() < 0.3;
    const shot = { user: "Игрок", x, y, result: hit ? "hit" : "miss" };
    data.push(shot);

    // сохраняем в скрытом поле JSON
    jsonField.value = JSON.stringify(data);

    // обновляем поле
    render();
    status.textContent = hit ? "Попадание!" : "Промах!";
    localStorage.setItem("sea_last_shot", Date.now());
  });

  render();
})();
</script>[/html]

0

9

[html]<div id="battlefield-container" style="text-align:center;">
  <h3>⚓ Морской бой — поле 8×8</h3>
  <p>Сделайте выстрел! (1 раз в 2 минуты)</p>
  <div id="battlefield"></div>
  <div id="status" style="margin:10px; font-size:16px;"></div>
</div>

<style>
#battlefield {
  display: grid;
  grid-template-columns: repeat(8, 50px);
  grid-template-rows: repeat(8, 50px);
  gap: 4px;
  justify-content: center;
  margin: 20px auto;
}
.cell {
  width: 50px;
  height: 50px;
  background: #9bb7d4;
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  color: white;
  font-weight: bold;
  user-select: none;
}
.cell.hit { background: #e63946; }
.cell.miss { background: #457b9d; }
</style>

<script>
window.addEventListener("load", async function(){
  const field = document.getElementById("battlefield");
  const status = document.getElementById("status");
  const gridSize = 8;
  const cooldown = 2 * 60 * 1000;

  const cells = [];
  let lastShotTime = 0;

  // создаем поле
  for (let y=0; y<gridSize; y++){
    for (let x=0; x<gridSize; x++){
      const div = document.createElement("div");
      div.className = "cell";
      div.dataset.x = x;
      div.dataset.y = y;
      field.appendChild(div);
      cells.push(div);
    }
  }

  // пример локальных ходов
  const shots = []; // сюда будем добавлять {x,y,result}

  function renderShots(){
    cells.forEach(c=>c.classList.remove("hit","miss"));
    for(const s of shots){
      const cell = cells.find(c=>c.dataset.x==s.x && c.dataset.y==s.y);
      if(cell) cell.classList.add(s.result==="попадание"?"hit":"miss");
    }
  }

  // проверка кулдауна
  function canShoot(){
    return Date.now() - lastShotTime > cooldown;
  }

  // обработка клика
  async function shoot(cell){
    if(!canShoot()){
      status.textContent = "⏳ Подождите 2 минуты между ходами.";
      return;
    }

    const x = +cell.dataset.x;
    const y = +cell.dataset.y;

    if(cell.classList.contains("hit") || cell.classList.contains("miss")){
      status.textContent = "🚫 Уже стреляли сюда.";
      return;
    }

    // случайное попадание для примера
    const hit = Math.random()<0.3;
    const result = hit?"попадание":"мимо";
    shots.push({x,y,result});
    renderShots();
    lastShotTime = Date.now();
    status.textContent = `${hit?"💥 Попадание":"💧 Мимо"} (${x},${y})`;
  }

  // клик по клетке
  field.addEventListener("click", e=>{
    if(!e.target.classList.contains("cell")) return;
    shoot(e.target);
  });

  renderShots();
});
</script>
[/html]

0

10

[html]<div id="battlefield-container" style="text-align:center;">
  <h2>⚓ Морской бой — 8×8</h2>
  <p>Сделайте выстрел! (1 раз в 2 минуты)</p>
  <div id="battlefield"></div>
  <div id="status" style="margin:10px; font-size:16px;"></div>
</div>

<style>
#battlefield {
  display: grid;
  grid-template-columns: repeat(8, 50px);
  grid-template-rows: repeat(8, 50px);
  gap: 4px;
  justify-content: center;
  margin: 20px auto;
}
.cell {
  width: 50px;
  height: 50px;
  background: #9bb7d4;
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  font-weight: bold;
  user-select: none;
}
.cell.hit { background: #e63946; color:white; }
.cell.miss { background: #457b9d; color:white; }
</style>

<script>
(async function() {
  const topicPostId = 3; // ID поста, где хранится JSON
  const gridSize = 8;
  const cooldown = 2 * 60 * 1000; // 2 минуты
  const storageKey = "sea_last_shot_time";

  const field = document.getElementById("battlefield");
  const status = document.getElementById("status");
  const cells = [];

  // 1️⃣ Создаём сетку
  for (let y=0; y<gridSize; y++) {
    for (let x=0; x<gridSize; x++) {
      const div = document.createElement("div");
      div.className = "cell";
      div.dataset.x = x;
      div.dataset.y = y;
      field.appendChild(div);
      cells.push(div);
    }
  }

  // 2️⃣ Получаем JSON с поста (имитация через fetch, для теста можно через локальный файл)
  async function loadJSON() {
    try {
      const res = await fetch(`/viewtopic.php?id=${topicPostId}`);
      const html = await res.text();
      // Находим JSON в посте по специальной метке: <!--SEA_BATTLE_JSON--> ... <!--/SEA_BATTLE_JSON-->
      const match = html.match(/<!--SEA_BATTLE_JSON-->([\s\S]*?)<!--\/SEA_BATTLE_JSON-->/);
      let data;
      if(match) {
        data = JSON.parse(match[1]);
      } else {
        // первый запуск — расставляем случайно
        const ships = [];
        const shipSizes = [4,3,3,2,2,2,1,1,1,1];
        const occupied = {};
        function placeShip(size){
          for(let attempt=0; attempt<200; attempt++){
            const dir = Math.random()<0.5 ? 'h':'v';
            const r = Math.floor(Math.random()*gridSize);
            const c = Math.floor(Math.random()*gridSize);
            const cells = [];
            let ok = true;
            for(let k=0;k<size;k++){
              const rr = dir==='h'?r:r+k;
              const cc = dir==='h'?c+k:c;
              if(rr>=gridSize || cc>=gridSize || occupied[rr+'_'+cc]) { ok=false; break; }
              cells.push({r:rr,c:cc});
            }
            if(ok) { cells.forEach(s=>occupied[s.r+'_'+s.c]=true); return cells; }
          }
          return placeShip(size); // рекурсивно
        }
        shipSizes.forEach(sz=>ships.push(placeShip(sz)));
        data = {ships: ships, shots: []};
      }
      return data;
    } catch(e) {
      console.error("Ошибка загрузки JSON:", e);
      return {ships:[], shots:[]};
    }
  }

  // 3️⃣ Отображение выстрелов
  function render(data) {
    cells.forEach(c=>c.classList.remove("hit","miss"));
    data.shots.forEach(s=>{
      const cell = cells.find(c=>c.dataset.x==s.x && c.dataset.y==s.y);
      if(cell) cell.classList.add(s.hit?"hit":"miss");
    });
  }

  const gameData = await loadJSON();
  render(gameData);

  // 4️⃣ Проверка кулдауна
  function canShoot(){
    const last = +localStorage.getItem(storageKey)||0;
    return Date.now()-last>cooldown;
  }

  // 5️⃣ Действие при выстреле
  function shoot(cell) {
    if(!canShoot()){
      const wait = Math.ceil((cooldown-(Date.now()-(+localStorage.getItem(storageKey))))/60000);
      status.textContent = `⏳ Стрельнуть можно через ${wait} мин.`;
      return;
    }
    const pos = {x:+cell.dataset.x, y:+cell.dataset.y};
    if(gameData.shots.some(s=>s.x===pos.x && s.y===pos.y)){
      status.textContent = "🚫 Уже стреляли сюда.";
      return;
    }
    const hit = gameData.ships.some(ship=>ship.some(c=>c.r===pos.y && c.c===pos.x));
    gameData.shots.push({...pos, hit});
    cell.classList.add(hit?"hit":"miss");
    status.textContent = hit?"💥 Попадание!":"💧 Мимо";
    localStorage.setItem(storageKey, Date.now());

    // 6️⃣ Публикация нового JSON в пост (здесь нужно вручную или через админку заменить содержимое поста)
    console.log("Новый ход", JSON.stringify(gameData));
  }

  // 7️⃣ Обработчик кликов
  field.addEventListener("click", e=>{
    if(!e.target.classList.contains("cell")) return;
    shoot(e.target);
  });

  // 8️⃣ Автообновление каждые 10 сек
  setInterval(async ()=>{
    const data = await loadJSON();
    render(data);
  },10000);

})();
</script>[/html]

0

11

[html]<div id="tree-wrapper"></div>

<div id="gift-form">
  <input type="text" id="username" placeholder="Ваш ник">
  <textarea id="message" rows="3" placeholder="Ваше поздравление..."></textarea>
  <select id="icon-select">
    <option value="https://forumstatic.ru/files/001a/fc/23/70482.png">Подарок 1</option>
    <option value="https://forumstatic.ru/files/001a/fc/23/70483.png">Подарок 2</option>
    <option value="https://forumstatic.ru/files/001a/fc/23/70484.png">Подарок 3</option>
  </select>
  <button id="send-gift" title="Отправить подарок"></button>
</div>

<script type="text/javascript" src="https://forumstatic.ru/files/0014/cc/0a/58042.js"></script>
<script type="text/javascript" src="https://forumstatic.ru/files/0014/cc/0a/54653.js"></script>
<script type="text/javascript" src="https://forumstatic.ru/files/0013/65/ed/76531.js"></script>

<script>
const tree = document.getElementById('tree-wrapper');
const sendButton = document.getElementById('send-gift');

async function loadGifts() {
  if (typeof Christmas_Tree.getGifts !== 'function') return;
  const giftsFromTopic = await Christmas_Tree.getGifts();
  tree.innerHTML = '';
  giftsFromTopic.forEach(g => {
    const el = document.createElement('div');
    el.className = 'gift';
    el.style.left = g.x + 'px';
    el.style.top = g.y + 'px';
    el.innerHTML = `
      <img src="${g.icon}" width="32" height="32">
      <div class="gift-note">🎁 ${g.user}: ${g.msg}</div>
    `;
    tree.appendChild(el);
  });
}

// Отправка нового подарка
sendButton.addEventListener('click', async () => {
  const name = document.getElementById('username').value.trim();
  const msg = document.getElementById('message').value.trim();
  const icon = document.getElementById('icon-select').value;
  if (!name || !msg) return alert('Введите ник и поздравление!');

  // Рандомная позиция на ёлке
  const x = Math.random() * (tree.clientWidth - 32);
  const y = Math.random() * (tree.clientHeight - 32);

  // Отправка в тему через скрипт форума
  if (typeof Christmas_Tree.sendGift === 'function') {
    await Christmas_Tree.sendGift(name, msg, icon, x, y);
    document.getElementById('message').value = '';
    loadGifts(); // перерисовать все подарки, включая новый
  } else {
    alert('Ошибка: скрипт ёлки не подключен.');
  }
});

// Загрузка подарков при открытии страницы
loadGifts();
</script>[/html]

0


Вы здесь » test » новый не новый » морской бой


Рейтинг форумов | Создать форум бесплатно