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

Тест оформления

Объявление

Katherine
Кэтрин
лает и кусает, ответственная за беспорядки в Своре. изучает личные дела, помогает разобраться в матчасти, отвечает на вопросы о вопросах
Ruby
Руби
(временно мало доступна) маленькая рыбка с большими амбициями, может и плавником шлёпнуть и влажно чмокнуть, и объяснить что к чему, админ-универсал многозадачник
Hardy
Харди
Отвечает по вопросам Альянса. По остальным вопросам не отвечает
Robert
Робби
ответственный за фермы, шизофрению и несмешные шутки, не спит по ночам. гейм-мастер. считает деньги и нервные клетки, ответит на любой вопрос
Correy
Корри
Корри, Корица, Кориандр, Коррор. Работает за пятерых, ещё и бесплатно. Вездесущий помогатор
Jamie
Джейми
На страже флуда топового проекта и ментального состояния других членов амс
Это Джейд. И он выживает здесь один. А теперь вот с ней, с Эбботт. Слова до сих пор все еще не укладывались в голове, ведь они не знали друг о друге ничего. А выживание вместе - это не просто вместе в душ сходить, потрахаться, помыться, это даже не то же самое, что решиться завести совместный быт или семью. Это совершенно изнаночное решение - довериться.
...если я открою спальный район, он будет называться Район Госуслуг

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

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


Вы здесь » Тест оформления » Короткие зарисовки » морской бой


морской бой

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

1

.

0

2

[html]

<script src="https://cdn.jsdelivr.net/npm/@quadrosystems/mybb-sdk"></script>
<script>
(async function(){
  const mybb = new MyBBSDK({
    forumUrl: "https://lalamo.rusff.me",
    apiKey: "testtest0"
  });

  const GAME_KEY = "battleship-field"; // ключ хранения поля в теме
  const container = document.getElementById("battleship");
  const resetBtn = document.getElementById("reset-field");
  const user = mybb.user.username || "Guest";

  // Настройки
  const GRID_SIZE = 10;
  const SHIPS = [4,3,3,2,2,2,1,1,1,1]; // длины кораблей
  const FIRE_COOLDOWN = 2*60*1000; // 2 минуты

  // Проверка админа
  const isAdmin = mybb.user.uid == 1; // можно менять на нужного пользователя
  if(isAdmin) resetBtn.style.display="inline-block";

  // Создать поле
  let field = Array(GRID_SIZE).fill(0).map(()=>Array(GRID_SIZE).fill(0));
  let lastFire = {};

  // --- функции для игры ---
  function renderField() {
    container.innerHTML = "";
    const table = document.createElement("table");
    table.style.borderCollapse="collapse";
    for(let y=0;y<GRID_SIZE;y++){
      const tr=document.createElement("tr");
      for(let x=0;x<GRID_SIZE;x++){
        const td=document.createElement("td");
        td.style.width="32px";
        td.style.height="32px";
        td.style.border="1px solid #000";
        td.style.textAlign="center";
        td.style.verticalAlign="middle";
        td.style.cursor="pointer";
        td.dataset.x=x;
        td.dataset.y=y;

        // покраска клеток
        if(field[y][x]==1) td.style.background="#555"; // корабль (скрыт)
        else if(field[y][x]==2) td.style.background="red"; // попадание
        else if(field[y][x]==3) td.style.background="lightblue"; // промах

        tr.appendChild(td);
      }
      table.appendChild(tr);
    }
    container.appendChild(table);
  }

  function placeShipsRandom() {
    field = Array(GRID_SIZE).fill(0).map(()=>Array(GRID_SIZE).fill(0));
    for(const len of SHIPS){
      let placed=false;
      while(!placed){
        const dir = Math.random()<0.5?"H":"V";
        const x=Math.floor(Math.random()*(GRID_SIZE-(dir=="H"?len:0)));
        const y=Math.floor(Math.random()*(GRID_SIZE-(dir=="V"?len:0)));
        let canPlace=true;
        for(let i=0;i<len;i++){
          if(dir=="H" && field[y][x+i]==1) canPlace=false;
          if(dir=="V" && field[y+i][x]==1) canPlace=false;
        }
        if(canPlace){
          for(let i=0;i<len;i++){
            if(dir=="H") field[y][x+i]=1;
            if(dir=="V") field[y+i][x]=1;
          }
          placed=true;
        }
      }
    }
  }

  async function saveField() {
    try {
      await mybb.topic.setCustomField(GAME_KEY, JSON.stringify(field));
    } catch(e){ console.error("Ошибка сохранения:", e);}
  }

  async function loadField() {
    try {
      const saved = await mybb.topic.getCustomField(GAME_KEY);
      if(saved){
        field = JSON.parse(saved);
      } else {
        if(isAdmin) { placeShipsRandom(); await saveField(); }
      }
    } catch(e){ console.error("Ошибка загрузки:", e);}
  }

  container.addEventListener("click", async (e)=>{
    if(!e.target.dataset.x) return;
    const x=+e.target.dataset.x;
    const y=+e.target.dataset.y;
    const now=Date.now();
    if(lastFire[user] && now-lastFire[user]<FIRE_COOLDOWN) {
      alert("Можно стрелять раз в 2 минуты");
      return;
    }

    if(field[y][x]==0){ field[y][x]=3; alert("Промах!"); }
    else if(field[y][x]==1){ field[y][x]=2; alert("Попадание!"); }
    else{ return; }

    lastFire[user]=now;
    await saveField();
    renderField();

    // Автоматически отправляем сообщение в тему
    try{
      const msg = `${user} стрелял по (${x+1},${y+1}) и ${field[y][x]==2?"попал!":"промах!"}`;
      await mybb.topic.postReply(msg);
    }catch(e){ console.error("Ошибка отправки сообщения:", e);}
  });

  resetBtn.addEventListener("click", async ()=>{
    if(!isAdmin) return;
    placeShipsRandom();
    await saveField();
    renderField();
  });

  await loadField();
  renderField();

})();
</script>

<!-- Морской бой для MyBB -->
<div id="battleship-container" style="margin:20px auto; text-align:center;">
  <h3>Морской бой</h3>
  <div id="battleship"></div>
  <button id="reset-field" style="margin-top:10px; display:none;">Сбросить поле (только админ)</button>
</div>

[/html]

0

3

[html]

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

[/html]

0

4

[html]<!-- === Морской бой для Rusff — вставь в сообщение темы === -->
<style>
/* Минимальные стили поля — вписаны в твой стиль (.lot-table) */
.battle-wrap { max-width:520px; margin:10px auto; text-align:center; font-family: Arial, sans-serif; }
.battle-grid { display:inline-block; border-collapse:collapse; margin:10px 0; }
.battle-grid td { width:48px; height:48px; border:1px solid #222; box-sizing:border-box;
  vertical-align:middle; text-align:center; font-weight:700; font-size:14px; cursor:pointer;
  background: rgba(255,255,255,0.03); background-size:70% auto; background-position:center; }
.battle-grid td.header { background:transparent; cursor:default; font-weight:700; font-size:13px; }
.battle-grid td.coord { background:transparent; cursor:default; font-weight:600; font-size:13px; color:#000; opacity:0.7; }
.battle-grid td.miss { background-image: none; color:#2b6ea3; opacity:0.9; }
.battle-grid td.hit { background-image: none; color:#fff; background: #cc3a3a; }
.battle-grid td.ship { /* показывать корабль при reveal */
  background: url('https://forumstatic.ru/files/0014/cc/0a/39265.png') no-repeat center;
  background-size:60%;
}
.battle-controls { margin:6px 0; }
.battle-controls button { margin:0 6px; padding:6px 10px; cursor:pointer; }
.battle-legend { font-size:13px; margin-top:6px; opacity:0.95; }
.battle-info { margin-top:8px; font-size:13px; opacity:0.9; }
</style>

<div id="battleship" class="battle-wrap">
  <h3 style="margin:6px 0;">⚓ Морской бой — поле 8×8</h3>

  <div class="battle-controls">
    <button id="bs-reset">Сброс и новая расстановка</button>
    <button id="bs-toggle-reveal">Показать корабли</button>
    <button id="bs-export">Экспорт состояния (json)</button>
  </div>

  <div id="bs-board-container" style="overflow:auto;">
    <table id="bs-board" class="battle-grid lot-table" style="table-layout:fixed;">
      <!-- генерируется скриптом -->
    </table>
  </div>

  <div class="battle-info">
    <span id="bs-status">Попаданий: <b id="bs-hits">0</b> / <span id="bs-total-cells">0</span></span>
  </div>

  <div class="battle-legend">
    <span style="display:inline-block;margin-right:12px;">🔴 — попадание</span>
    <span style="display:inline-block;margin-right:12px;">🔵 — промах</span>
    <span style="display:inline-block;">🟩 — корабль (только при показе)</span>
  </div>
</div>

<script type="text/javascript">
/* Battleship для Rusff — автономный, сохраняет состояние в localStorage.
   Ключ сохранения: 'battleship_topic_' + (FORUM.topic.id || hash(location.href))
   Автор: адаптация под твой шаблон.
*/
(function(){
  // безопасные имена в глобальных пространствах от твоих скриптов
  if (typeof FORUM === 'undefined') window.FORUM = window.FORUM || {};
  FORUM.battleship = FORUM.battleship || {};

  // Конфигурация
  var ROWS = 8, COLS = 8;
  var LETTERS = ['A','B','C','D','E','F','G','H'];
  var FLEET = [4,3,3,2,2,2,1,1,1,1]; // клетки каждого корабля (1×4,2×3,3×2,4×1)
  var storageKey = (function(){
    try {
      if (window.FORUM && FORUM.topic && FORUM.topic.id) return 'battleship_topic_' + FORUM.topic.id;
    } catch(e){}
    // fallback: hash URL
    function simpleHash(s){
      var h=0; for(var i=0;i<s.length;i++){h=(h<<5)-h + s.charCodeAt(i); h|=0;}
      return Math.abs(h);
    }
    return 'battleship_topic_' + simpleHash(location.href);
  })();

  // state: {ships: [[{r,c},...],...], cells: { "r_c": "miss"|"hit" }, revealed:bool}
  var state = null;

  // DOM refs
  var boardEl = null, hitsEl=null, totalCellsEl=null, btnReset=null, btnReveal=null, btnExport=null;

  // helpers
  function saveState(){
    try { localStorage.setItem(storageKey, JSON.stringify(state)); } catch(e){ console.warn(e); }
  }
  function loadState(){
    try {
      var s = localStorage.getItem(storageKey);
      if (s) return JSON.parse(s);
    } catch(e){}
    return null;
  }
  function clearState(){
    state = null;
    localStorage.removeItem(storageKey);
  }

  // попытка разместить флот случайно без пересечений и в пределах поля
  function placeFleetRandom(){
    var ships = [];
    var occupied = {}; // key "r_c"
    function tryPlace(len){
      var dir = Math.random() < 0.5 ? 'h' : 'v';
      var r = Math.floor(Math.random()*ROWS);
      var c = Math.floor(Math.random()*COLS);
      var cells = [];
      for (var k=0;k<len;k++){
        var rr = dir==='h' ? r : r+k;
        var cc = dir==='h' ? c+k : c;
        if (rr<0 || rr>=ROWS || cc<0 || cc>=COLS) return null;
        if (occupied[rr+'_'+cc]) return null;
        cells.push({r:rr,c:cc});
      }
      // also prevent adjacent placement (optional rule) — keep gap 1 cell around ship
      for (var i=0;i<cells.length;i++){
        for(var dr=-1;dr<=1;dr++){
          for(var dc=-1;dc<=1;dc++){
            var nr=cells[i].r+dr, nc=cells[i].c+dc;
            if (nr>=0 && nr<ROWS && nc>=0 && nc<COLS && occupied[nr+'_'+nc]) return null;
          }
        }
      }
      return cells;
    }

    for (var i=0;i<FLEET.length;i++){
      var len = FLEET[i];
      var attempts = 0, placed = null;
      while(attempts < 200){
        placed = tryPlace(len);
        if (placed) break;
        attempts++;
      }
      if (!placed){
        // если не смогли разместить (очень маловероятно), начинаем заново
        return placeFleetRandom();
      }
      ships.push(placed);
      for (var j=0;j<placed.length;j++){
        occupied[placed[j].r+'_'+placed[j].c] = true;
      }
    }
    return ships;
  }

  // init state (создать или загрузить)
  function initState(){
    var s = loadState();
    if (s && s.ships && s.cells) {
      state = s;
    } else {
      state = { ships: placeFleetRandom(), cells: {}, revealed: false };
      saveState();
    }
  }

  // отрисовка таблицы (с буквами/цифрами)
  function renderBoard(){
    if (!boardEl) return;
    boardEl.innerHTML = '';
    var tbody = document.createElement('tbody');

    // первая строка: пустая клетка + заголовки столбцов A..H
    var trHead = document.createElement('tr');
    trHead.appendChild(td('', 'header')); // пустая
    for (var c=0;c<COLS;c++){
      trHead.appendChild(td(LETTERS[c], 'header'));
    }
    tbody.appendChild(trHead);

    for (var r=0;r<ROWS;r++){
      var tr = document.createElement('tr');
      // номер строки слева
      tr.appendChild(td(String(r+1), 'coord'));
      for (var c=0;c<COLS;c++){
        var cell = td('', '');
        cell.dataset.r = r; cell.dataset.c = c;
        var key = r+'_'+c;
        // determine css based on state.cells
        if (state.cells[key] === 'miss') {
          cell.classList.add('miss');
          cell.textContent = '•';
          cell.title = 'Промах';
        } else if (state.cells[key] === 'hit') {
          cell.classList.add('hit');
          cell.textContent = '✖';
          cell.title = 'Попадание';
        } else {
          cell.textContent = '';
        }

        // show ship if revealed or (optionally) if hit
        if (state.revealed && isShipAt(r,c)) {
          cell.classList.add('ship');
          cell.title = cell.title || 'Корабль';
        }

        // bind click handler only for untouched cells
        (function(rr,cc,el){
          el.addEventListener('click', function(e){
            handleCellClick(rr,cc,el);
          });
        })(r,c,cell);

        tr.appendChild(cell);
      }
      tbody.appendChild(tr);
    }
    boardEl.appendChild(tbody);

    // update counters
    var totalShipCells = countTotalShipCells();
    totalCellsEl.textContent = totalShipCells;
    hitsEl.textContent = countHits();
  }

  function td(text, cls){
    var el = document.createElement('td');
    if (cls) el.className = cls;
    el.textContent = text;
    return el;
  }

  function isShipAt(r,c){
    for (var i=0;i<state.ships.length;i++){
      for (var j=0;j<state.ships[i].length;j++){
        if (state.ships[i][j].r===r && state.ships[i][j].c===c) return true;
      }
    }
    return false;
  }

  function countTotalShipCells(){
    var s=0;
    for (var i=0;i<state.ships.length;i++) s += state.ships[i].length;
    return s;
  }

  function countHits(){
    var cnt=0;
    for (var k in state.cells) if (state.cells[k]==='hit') cnt++;
    return cnt;
  }

  // клик по клетке
  function handleCellClick(r,c,el){
    var key = r+'_'+c;
    if (state.cells[key] === 'hit' || state.cells[key] === 'miss') {
      // уже кликали — ничего не делаем
      return;
    }
    if (isShipAt(r,c)){
      state.cells[key] = 'hit';
      el.classList.add('hit');
      el.textContent = '✖';
      el.title = 'Попадание';
    } else {
      state.cells[key] = 'miss';
      el.classList.add('miss');
      el.textContent = '•';
      el.title = 'Промах';
    }
    saveState();
    hitsEl.textContent = countHits();

    // optional: если все потоплены — показать уведомление
    if (countHits() >= countTotalShipCells()){
      setTimeout(function(){
        alert('Поздравляем! Все корабли потоплены.');
      }, 50);
    }
  }

  // UI: кнопки
  function attachUI(){
    btnReset = document.getElementById('bs-reset');
    btnReveal = document.getElementById('bs-toggle-reveal');
    btnExport = document.getElementById('bs-export');
    hitsEl = document.getElementById('bs-hits');
    totalCellsEl = document.getElementById('bs-total-cells');

    btnReset && btnReset.addEventListener('click', function(){
      if (!confirm('Сбросить поле и заново расставить корабли?')) return;
      state = { ships: placeFleetRandom(), cells: {}, revealed: false };
      saveState();
      renderBoard();
      btnReveal.textContent = 'Показать корабли';
    });

    btnReveal && btnReveal.addEventListener('click', function(){
      state.revealed = !state.revealed;
      saveState();
      renderBoard();
      btnReveal.textContent = state.revealed ? 'Скрыть корабли' : 'Показать корабли';
    });

    btnExport && btnExport.addEventListener('click', function(){
      var data = JSON.stringify(state);
      // простое окно с json — можно скопировать
      var w = window.open('', '_blank', 'width=700,height=400');
      w.document.write('<pre style="white-space:pre-wrap;word-wrap:break-word;font-family:monospace;">' + escapeHtml(data) + '</pre>');
      w.document.title = 'battleship state export';
    });
  }

  function escapeHtml(s){ return (s+'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }

  // init everything
  function init(){
    boardEl = document.getElementById('bs-board');
    if (!boardEl) return;
    attachUI();
    initState();
    renderBoard();
    // ensure reveal button label correct
    var rb = document.getElementById('bs-toggle-reveal');
    if (rb) rb.textContent = state.revealed ? 'Скрыть корабли' : 'Показать корабли';
  }

  // run on DOM ready
  if (document.readyState === 'loading'){
    document.addEventListener('DOMContentLoaded', init);
  } else init();

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

0

5

[html]
<div id="battlefield-container" style="text-align:center;">
  <h2>⚓ Морской бой</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;
  color: white;
  font-weight: bold;
  user-select: none;
}
.cell.hit { background: #e63946; }
.cell.miss { background: #457b9d; }
</style>

<script>
(async function(){
  if (!window.SeaBattle) {
    document.getElementById("battlefield-container").innerHTML =
      "<p style='color:red;'>Ошибка: нет подключения к API.</p>";
    return;
  }

  const { sdk, topicId, gridSize } = window.SeaBattle;
  const field = document.getElementById("battlefield");
  const status = document.getElementById("status");

  let cells = [];
  let gameState = await loadGame();

  // создаём поле
  renderGrid();

  function renderGrid() {
    field.innerHTML = "";
    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;
        const key = x + "_" + y;
        if (gameState[key] === "hit") div.classList.add("hit");
        else if (gameState[key] === "miss") div.classList.add("miss");

        div.addEventListener("click", () => shoot(x, y, div));
        field.appendChild(div);
        cells.push(div);
      }
    }
  }

  async function shoot(x, y, div) {
    const key = x + "_" + y;
    if (gameState[key]) return;

    const hit = Math.random() < 0.3; // вероятность попадания
    gameState[key] = hit ? "hit" : "miss";

    div.classList.add(hit ? "hit" : "miss");
    div.textContent = hit ? "✖" : "•";

    await saveGame();
  }

  async function loadGame() {
    try {
      const data = await sdk.getVariable(topicId, "sea_battle_state");
      return data ? JSON.parse(data) : {};
    } catch (e) {
      console.warn(e);
      return {};
    }
  }

  async function saveGame() {
    try {
      await sdk.setVariable(topicId, "sea_battle_state", JSON.stringify(gameState));
    } catch (e) {
      console.error("Ошибка сохранения:", e);
    }
  }
})();
</script>
[/html]

0

6

[html]<div id="battle-container" style="text-align:center;">
  <h3>⚓ Морской бой — 8Ч8</h3>
  <table id="battle-grid" style="margin:10px auto; border-collapse:collapse;"></table>
  <div style="margin-top:6px;">
    <button id="bs-reveal">Показать/Скрыть корабли</button>
    <button id="bs-reset">Сбросить (только автор темы)</button>
  </div>
  <div id="battle-status" style="margin-top:8px;">Попаданий: <span id="hits">0</span></div>
</div>

<script>
(async function(){
  if (!window.FORUM || !FORUM.seaBattle) {
    document.getElementById('battle-container').innerHTML = '<p style="color:red;">Ошибка: нет подключения к API.</p>';
    return;
  }

  const { mybb, storageKey, ROWS, COLS, topicId, loadState, placeFleetRandom } = FORUM.seaBattle;
  const table = document.getElementById('battle-grid');
  const hitsEl = document.getElementById('hits');
  const btnReveal = document.getElementById('bs-reveal');
  const btnReset = document.getElementById('bs-reset');

  let state = await loadState();

  function render() {
    table.innerHTML='';
    const tbody = document.createElement('tbody');
    for (let r=0;r<ROWS;r++){
      const tr=document.createElement('tr');
      for (let c=0;c<COLS;c++){
        const td=document.createElement('td');
        td.style.width='48px';
        td.style.height='48px';
        td.style.border='1px solid #222';
        td.style.textAlign='center';
        td.style.cursor='pointer';
        const key=r+'_'+c;
        if(state.cells[key]==='hit'){ td.style.background='#cc3a3a'; td.textContent='✖'; }
        else if(state.cells[key]==='miss'){ td.style.color='#2b6ea3'; td.textContent='•'; }
        else td.textContent='';

        if(state.revealed && state.ships.some(ship=>ship.some(s=>s.r===r&&s.c===c))){
          td.style.background='green';
        }

        td.addEventListener('click',()=>handleClick(r,c));
        tr.appendChild(td);
      }
      tbody.appendChild(tr);
    }
    table.appendChild(tbody);
    hitsEl.textContent=Object.values(state.cells).filter(v=>v==='hit').length;
  }

  async function handleClick(r,c){
    const key=r+'_'+c;
    if(state.cells[key]) return; // уже кликнули
    const isShip=state.ships.some(ship=>ship.some(s=>s.r===r&&s.c===c));
    state.cells[key]=isShip?'hit':'miss';
    await mybb.storage.set(storageKey,state);
    state=await loadState();
    render();
  }

  btnReveal.addEventListener('click',async()=>{
    state.revealed=!state.revealed;
    await mybb.storage.set(storageKey,state);
    render();
  });

  btnReset.addEventListener('click',async()=>{
    const isAuthor=(FORUM.user.id===FORUM.topic.userId);
    if(!isAuthor){ alert('Только автор темы может сбросить поле'); return; }
    if(!confirm('Сбросить поле и расставить заново?')) return;
    state={ships:placeFleetRandom(),cells:{},revealed:false};
    await mybb.storage.set(storageKey,state);
    render();
  });

  render();

  // автообновление каждые 10 сек, чтобы видеть выстрелы других игроков
  setInterval(async()=>{ state=await loadState(); render(); },10000);

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

0

7

[html]<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Локальный Морской Бой</title>
    <!-- Подключение Tailwind CSS для стилей -->
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
        /* Стиль для сетки, чтобы обеспечить квадратные ячейки */
        .grid-container {
            display: grid;
            grid-template-columns: repeat(11, 1fr);
            gap: 2px;
            aspect-ratio: 1 / 1;
        }
        .cell {
            width: 100%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            user-select: none;
            transition: all 0.1s;
            border-radius: 4px;
        }
        .header-cell {
            background-color: #e5e7eb; /* gray-200 */
            font-weight: 600;
            color: #4b5563; /* gray-600 */
        }
    </style>
</head>
<body class="bg-gray-100 p-4 font-sans antialiased min-h-screen flex flex-col items-center">

    <div id="game-container" class="w-full max-w-4xl bg-white shadow-xl rounded-2xl p-6 md:p-8">
        <header class="text-center mb-6">
            <h1 class="text-3xl font-extrabold text-indigo-700">Морской Бой (Локальный режим)</h1>
            <p id="message" class="mt-2 text-lg font-semibold p-2 rounded-lg bg-yellow-100 text-yellow-800 transition duration-300">
                Нажмите "Начать Новую Игру", чтобы начать.
            </p>
            <button id="startButton" class="mt-4 px-6 py-3 bg-green-600 text-white font-bold rounded-xl shadow-lg hover:bg-green-700 transition transform hover:scale-105">
                Начать Новую Игру
            </button>
        </header>

        <div id="gameboards" class="grid grid-cols-1 lg:grid-cols-2 gap-6 lg:gap-10 mt-8">
            <!-- Поле Игрока 1 -->
            <div id="player1BoardContainer" class="p-4 border-2 border-indigo-200 rounded-lg bg-indigo-50">
                <h2 class="text-xl font-bold text-center mb-4 text-indigo-700">Игрок 1: Ваше Поле</h2>
                <div id="player1Grid" class="grid-container w-full max-w-md mx-auto"></div>
            </div>

            <!-- Поле Игрока 2 -->
            <div id="player2BoardContainer" class="p-4 border-2 border-red-200 rounded-lg bg-red-50">
                <h2 class="text-xl font-bold text-center mb-4 text-red-700">Игрок 2: Ваше Поле</h2>
                <div id="player2Grid" class="grid-container w-full max-w-md mx-auto"></div>
            </div>
        </div>
       
        <div id="turn-overlay" class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center hidden z-10">
            <div class="bg-white p-8 rounded-xl shadow-2xl text-center">
                <h3 id="overlay-text" class="text-2xl font-bold mb-4">Начинает Игрок 1!</h3>
                <p class="text-gray-600 mb-6">Игрок <span id="current-player-name" class="font-extrabold text-3xl text-indigo-600">1</span>, отвернитесь, пока <span id="other-player-name">Игрок 2</span> совершает ход!</p>
                <button id="hideButton" class="px-6 py-3 bg-blue-600 text-white font-bold rounded-lg hover:bg-blue-700 transition">
                    Продолжить
                </button>
            </div>
        </div>
    </div>

    <script>
        // --- КОНСТАНТЫ И ИНИЦИАЛИЗАЦИЯ ---
        const GRID_SIZE = 10;
        const INITIAL_SHIPS = [
            { size: 4, count: 1 }, // Линкор
            { size: 3, count: 2 }, // Крейсер
            { size: 2, count: 3 }, // Эсминец
            { size: 1, count: 4 }  // Катер
        ];
        const TOTAL_SHIP_CELLS = INITIAL_SHIPS.reduce((sum, s) => sum + s.size * s.count, 0);

        let grid1 = []; // 0: пустая, 1: корабль
        let grid2 = [];
        let shots1 = []; // Попадания Игрока 1 по Игроку 2 (состояние поля 2 для P1)
        let shots2 = []; // Попадания Игрока 2 по Игроку 1 (состояние поля 1 для P2)
        let currentTurn = 1; // 1 или 2
        let hits1 = 0; // Попадания Игрока 1 (по полю 2)
        let hits2 = 0; // Попадания Игрока 2 (по полю 1)
        let gameActive = false;

        const messageEl = document.getElementById('message');
        const startButton = document.getElementById('startButton');
        const board1Container = document.getElementById('player1Grid');
        const board2Container = document.getElementById('player2Grid');
        const overlay = document.getElementById('turn-overlay');

        // --- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ---

        // Создание пустой сетки
        const createEmptyGrid = () => Array(GRID_SIZE).fill(0).map(() => Array(GRID_SIZE).fill(0));
       
        // Генерация случайной сетки (УПРОЩЕННО, просто расставляет 20 случайных кораблей)
        const placeShipsRandomly = () => {
            const grid = createEmptyGrid();
            let placedCellsCount = 0;

            // Расчет общего количества ячеек, которые нужно заполнить (как в оригинальном React-коде)
            const cellsToPlace = INITIAL_SHIPS.reduce((sum, s) => sum + s.count, 0) * 2;
           
            while (placedCellsCount < cellsToPlace) {
                const r = Math.floor(Math.random() * GRID_SIZE);
                const c = Math.floor(Math.random() * GRID_SIZE);

                if (grid[r][c] === 0) {
                    grid[r][c] = 1; // 1 = Корабль
                    placedCellsCount++;
                }
            }
            return grid;
        };

        // Конвертация координат (0, 0) -> A1
        const coordsToLabel = (r, c) => `${String.fromCharCode(65 + c)}${r + 1}`;

        // --- ИНИЦИАЛИЗАЦИЯ ИГРЫ ---

        const initializeGame = () => {
            grid1 = placeShipsRandomly();
            grid2 = placeShipsRandomly();
            shots1 = createEmptyGrid(); // P1 shots on P2 grid
            shots2 = createEmptyGrid(); // P2 shots on P1 grid
            currentTurn = 1;
            hits1 = 0;
            hits2 = 0;
            gameActive = true;
           
            renderBoards();
            updateMessage(`Игра началась! Ход Игрока 1 (синий).`);
            startButton.textContent = 'Новая Игра (Сброс)';
           
            // Показываем оверлей для начала игры
            showOverlay(1, 2);
        };
       
        // --- ОСНОВНАЯ ЛОГИКА ХОДА ---

        const handleShot = (r, c, targetPlayer) => {
            if (!gameActive || targetPlayer !== currentTurn) {
                // Если стреляет не тот игрок или игра неактивна
                return;
            }

            const shooterShots = targetPlayer === 1 ? shots1 : shots2;
            const targetGrid = targetPlayer === 1 ? grid2 : grid1;
            const targetHits = targetPlayer === 1 ? hits1 : hits2;

            if (shooterShots[r][c] !== 0) {
                updateMessage(`Игрок ${currentTurn}: Вы уже стреляли в клетку ${coordsToLabel(r, c)}!`);
                return;
            }

            const isHit = targetGrid[r][c] === 1;

            if (isHit) {
                shooterShots[r][c] = 2; // 2 = Попадание
                if (targetPlayer === 1) { hits1++; } else { hits2++; }

                updateMessage(`Игрок ${currentTurn}: 🎉 ПОПАДАНИЕ! Продолжайте стрелять.`);
                renderBoards();
                checkWinCondition(targetPlayer);
               
            } else {
                shooterShots[r][c] = 3; // 3 = Промах
                updateMessage(`Игрок ${currentTurn}: 🌊 ПРОМАХ! Ход переходит к Игроку ${targetPlayer === 1 ? 2 : 1}.`);
                renderBoards();
               
                // Передача хода
                setTimeout(() => {
                    showOverlay(targetPlayer === 1 ? 2 : 1, targetPlayer);
                }, 1500);
            }
        };

        const checkWinCondition = (shooter) => {
            const currentHits = shooter === 1 ? hits1 : hits2;
            const winnerName = `Игрок ${shooter}`;
           
            if (currentHits >= TOTAL_SHIP_CELLS) {
                gameActive = false;
                updateMessage(`🏆 ${winnerName} Победил! Все корабли противника потоплены! 🏆`);
                startButton.textContent = 'Начать Снова';
                document.getElementById('turn-overlay').classList.add('hidden'); // Убрать оверлей
            }
        };

        // --- РЕНДЕРИНГ UI ---

        const getCellColorAndContent = (r, c, isOwnBoard) => {
            const grid = isOwnBoard ? (currentTurn === 1 ? grid1 : grid2) : (currentTurn === 1 ? grid2 : grid1);
            const shots = isOwnBoard ? (currentTurn === 1 ? shots2 : shots1) : (currentTurn === 1 ? shots1 : shots2);
           
            const cellValue = grid[r][c];
            const shotStatus = shots[r][c];
           
            let bgColor = 'bg-gray-200 hover:bg-gray-300';
            let content = '';
            let cursor = isOwnBoard ? 'cursor-default' : 'cursor-pointer';
            let handler = null;
           
            if (isOwnBoard) {
                // Отрисовка своего поля (показываем корабли)
                if (cellValue === 1) {
                    bgColor = 'bg-gray-700'; // Корабль
                    if (shotStatus === 2) {
                        bgColor = 'bg-red-700'; // Корабль подбит
                        content = '💥';
                    } else if (shotStatus === 3) {
                        bgColor = 'bg-blue-300'; // Промах по нам
                        content = '💧';
                    }
                } else if (shotStatus === 3) {
                    bgColor = 'bg-blue-300'; // Промах по нам
                    content = '💧';
                }
            } else {
                // Отрисовка поля противника (для стрельбы)
                if (shotStatus === 2) {
                    bgColor = 'bg-red-500'; // Попадание
                    content = '🔥';
                    cursor = 'cursor-default';
                } else if (shotStatus === 3) {
                    bgColor = 'bg-blue-300'; // Промах
                    content = '💧';
                    cursor = 'cursor-default';
                } else {
                    // Клетка, по которой можно стрелять
                    bgColor = 'bg-cyan-100 hover:bg-cyan-300';
                    cursor = 'cursor-pointer';
                    handler = () => handleShot(r, c, currentTurn);
                }
            }
           
            return { bgColor, content, cursor, handler };
        };

        const renderGrid = (container, isOwnBoard) => {
            container.innerHTML = '';
           
            // 1. Заголовки столбцов (A-J)
            const emptyCell = document.createElement('div');
            emptyCell.className = 'cell header-cell';
            container.appendChild(emptyCell);
           
            for (let c = 0; c < GRID_SIZE; c++) {
                const header = document.createElement('div');
                header.className = 'cell header-cell';
                header.textContent = String.fromCharCode(65 + c);
                container.appendChild(header);
            }
           
            // 2. Ячейки с данными
            for (let r = 0; r < GRID_SIZE; r++) {
                // Заголовок строки (1-10)
                const header = document.createElement('div');
                header.className = 'cell header-cell';
                header.textContent = r + 1;
                container.appendChild(header);
               
                for (let c = 0; c < GRID_SIZE; c++) {
                    const { bgColor, content, cursor, handler } = getCellColorAndContent(r, c, isOwnBoard);
                   
                    const cell = document.createElement('div');
                    cell.className = `cell text-lg font-bold ${bgColor} ${cursor}`;
                    cell.textContent = content;
                    cell.title = coordsToLabel(r, c);
                   
                    if (handler) {
                        cell.addEventListener('click', handler);
                    }
                    container.appendChild(cell);
                }
            }
        };
       
        const renderBoards = () => {
            const otherPlayer = currentTurn === 1 ? 2 : 1;

            // Рендерим поле текущего игрока (isOwnBoard = true)
            document.getElementById(`player${currentTurn}BoardContainer`).style.display = 'block';
            renderGrid(document.getElementById(`player${currentTurn}Grid`), true);
           
            // Рендерим поле противника (isOwnBoard = false)
            document.getElementById(`player${otherPlayer}BoardContainer`).style.display = 'block';
            renderGrid(document.getElementById(`player${otherPlayer}Grid`), false);
           
            // Прячем поле противника и показываем только активное поле и поле, по которому стреляют
            document.getElementById(`player${currentTurn}BoardContainer`).style.borderColor = '#4f46e5'; /* indigo-600 */
            document.getElementById(`player${otherPlayer}BoardContainer`).style.borderColor = '#10b981'; /* emerald-500 */
           
            document.getElementById(`player${currentTurn}BoardContainer`).classList.add('order-2');
            document.getElementById(`player${otherPlayer}BoardContainer`).classList.remove('order-2');
           
            document.getElementById(`player${currentTurn}BoardContainer`).querySelector('h2').textContent = `Ваше Поле (Попаданий по Вам: ${currentTurn === 1 ? hits2 : hits1}/${TOTAL_SHIP_CELLS})`;
            document.getElementById(`player${otherPlayer}BoardContainer`).querySelector('h2').textContent = `Поле Противника (Ваших попаданий: ${currentTurn === 1 ? hits1 : hits2}/${TOTAL_SHIP_CELLS})`;
        };

        const updateMessage = (text) => {
            messageEl.textContent = text;
            messageEl.className = `mt-2 text-lg font-semibold p-2 rounded-lg transition duration-300 ${gameActive ? 'bg-green-100 text-green-800' : 'bg-yellow-200 text-red-700'}`;
        };

        // --- ЛОГИКА ОВЕРЛЕЯ (СМЕНА ИГРОКОВ) ---
       
        const showOverlay = (nextPlayer, previousPlayer) => {
            currentTurn = nextPlayer;
           
            document.getElementById('overlay-text').textContent = `Ход переходит к Игроку ${nextPlayer}!`;
            document.getElementById('current-player-name').textContent = nextPlayer;
            document.getElementById('other-player-name').textContent = `Игрок ${previousPlayer}`;
           
            overlay.classList.remove('hidden');
            document.getElementById('gameboards').classList.add('hidden');
           
            // Чтобы изменить цвет кнопки оверлея
            const hideButton = document.getElementById('hideButton');
            hideButton.textContent = `Игрок ${nextPlayer}, нажмите, чтобы начать свой ход`;
            hideButton.style.backgroundColor = nextPlayer === 1 ? '#4f46e5' : '#ef4444'; // Indigo vs Red

            hideButton.onclick = () => {
                overlay.classList.add('hidden');
                document.getElementById('gameboards').classList.remove('hidden');
                renderBoards();
                updateMessage(`Ход Игрока ${currentTurn}. Стреляйте по полю противника.`);
            };
        };

        // --- ОБРАБОТЧИК КНОПКИ СТАРТА ---
        startButton.addEventListener('click', initializeGame);

        // Инициализация при загрузке страницы
        window.onload = initializeGame;

    </script>
</body>
</html>[/html]

0

8

[html]<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Морской Бой - Пошаговая версия</title>
    <script src="https://cdn.jsdelivr.net/npm/@quadrosystems/mybb-sdk@0.9.7/lib/index.min.js"></script>
    <style>
        /* Стиль взят из предыдущего автономного примера */
        body { font-family: Arial, sans-serif; background-color: #f7f9fb; padding: 15px; }
        .container { max-width: 450px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); }
        h1 { text-align: center; color: #3b82f6; margin-bottom: 20px; font-size: 1.8rem; }
        .bs-grid-container {
            display: grid;
            grid-template-columns: repeat(11, 1fr);
            gap: 1px;
            max-width: 400px;
            margin: 10px auto;
            border: 3px solid #3b82f6;
            border-radius: 8px;
            overflow: hidden;
        }
        .bs-cell {
            height: 35px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 0.9rem;
            cursor: pointer;
            background-color: #dbeafe;
            transition: background-color 0.2s, transform 0.1s;
            font-weight: bold;
            color: #1f2937;
        }
        .bs-header { background-color: #e5e7eb; cursor: default; color: #4b5563; font-size: 0.8rem; }
        .bs-target-cell:hover { background-color: #93c5fd; transform: scale(1.05); }
        .bs-hit { background-color: #f87171 !important; color: white !important; font-size: 1.5rem !important; cursor: default; }
        .bs-miss { background-color: #60a5fa !important; color: white !important; font-size: 1.5rem !important; cursor: default; }
        #message-box { text-align: center; padding: 10px; margin: 15px auto; border-radius: 6px; font-weight: bold; background-color: #fef3c7; color: #92400e; }
        #refresh-button, #reset-button {
            display: block;
            width: 80%;
            margin: 10px auto 0;
            padding: 10px;
            background-color: #10b981;
            color: white;
            border: none;
            border-radius: 20px;
            font-size: 1rem;
            cursor: pointer;
            box-shadow: 0 3px 6px rgba(0, 0, 0, 0.2);
            transition: background-color 0.2s;
        }
        #refresh-button { background-color: #f97316; } /* orange-500 */
    </style>
</head>
<body>
    <div class="container">
        <h1>Морской Бой (Пошаговый)</h1>
        <div id="status-info" class="text-sm text-center mb-2">
            <p>ID Игры: <span id="game-id">LOADING...</span></p>
            <p>Ваш ID: <span id="user-id">LOADING...</span></p>
        </div>
        <div id="message-box">Ожидание загрузки SDK...</div>
        <div id="battleship-container" class="bs-grid-container">
            <!-- Сетка будет сгенерирована здесь -->
        </div>
        <button id="refresh-button">Обновить Ход Соперника</button>
        <button id="reset-button">Начать Новую Игру</button>
    </div>

<script>
    // Инициализация MyBB SDK (предполагаем, что оно успешно загружено)
    const mybb = window.MyBB;
    console.log("MyBB SDK загружен:", !!mybb);
   
    // --- ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ И НАСТРОЙКИ ---
    const GRID_SIZE = 10;
    const MAX_HITS = 20;
    const GAME_ID_PLACEHOLDER = 'battleship_match_123'; // Должно быть уникальным для каждой игры
   
    let gameState = null;
    let currentUserId = 'User_Unknown'; // Будет заменено на ID из SDK
    let isMyTurn = false;

    // --- ФУНКЦИИ ИГРОВОЙ ЛОГИКИ ---

    function createEmptyGrid() { return Array(GRID_SIZE).fill(0).map(() => Array(GRID_SIZE).fill(0)); }

    function placeShipsRandomly() {
        const grid = createEmptyGrid();
        let placedCellsCount = 0;
        while (placedCellsCount < MAX_HITS) {
            const r = Math.floor(Math.random() * GRID_SIZE);
            const c = Math.floor(Math.random() * GRID_SIZE);
            if (grid[r][c] === 0) {
                grid[r][c] = 1; // 1 = Корабль
                placedCellsCount++;
            }
        }
        return grid;
    }
   
    function coordsToLabel(r, c) { return `${String.fromCharCode(65 + c)}${r + 1}`; }
   
    function getInitialState(p1Id, p2Id) {
        return {
            gameId: GAME_ID_PLACEHOLDER,
            players: [
                { id: p1Id, name: "Игрок 1", hits: 0 },
                { id: p2Id, name: "Игрок 2", hits: 0 }
            ],
            // Храним только сетку ходов (Grid of Moves) - 0: не стрелял, 1: промах, 2: попадание
            // Нам нужно 2 сетки: одна для P1 (для P2) и одна для P2 (для P1)
            p1Moves: createEmptyGrid(),
            p2Moves: createEmptyGrid(),
            // Это сетка, где расположены корабли (должна быть скрыта от противника)
            hiddenShipsGrid: placeShipsRandomly(),
            currentTurnId: p1Id,
            status: 'active',
            message: 'Игра началась. Ход Игрока 1.'
        };
    }

    // --- ФУНКЦИИ SDK (ЗАГЛУШКИ) ---

    async function loadUserId() {
        // Здесь должен быть реальный вызов SDK для получения ID пользователя
        if (mybb && mybb.user) {
            // ПРИМЕР: Предполагаем, что SDK имеет объект user с полем uid
            currentUserId = mybb.user.uid || 'User_' + Math.random().toString(36).substring(2, 9);
        } else {
             // Имитация ID, если SDK не работает или не предоставляет его
            currentUserId = 'User_' + Math.random().toString(36).substring(2, 9);
        }
        document.getElementById('user-id').textContent = currentUserId.substring(0, 10) + '...';
        return currentUserId;
    }
   
    async function saveGameStateToForum(state) {
        const stateJson = JSON.stringify(state);
        console.log("Сохранение состояния (JSON) в MyBB:", stateJson);
       
        // !!! ВАЖНО !!!
        // Здесь должен быть реальный вызов mybb-sdk для сохранения JSON
        // Пример (НЕ ПРОВЕРЕНО): await mybb.post.updateMeta(POST_ID, { gameData: stateJson });
       
        document.getElementById('message-box').textContent = "СОХРАНЕНО. Попросите соперника обновить страницу!";
        return true;
    }
   
    async function loadGameStateFromForum() {
        console.log("Чтение состояния (JSON) из MyBB...");
       
        // !!! ВАЖНО !!!
        // Здесь должен быть реальный вызов mybb-sdk для чтения JSON
        // Пример (НЕ ПРОВЕРЕНО): const meta = await mybb.post.getMeta(POST_ID);
        // let stateJson = meta.gameData;

        // Заглушка:
        let stateJson = localStorage.getItem(GAME_ID_PLACEHOLDER);
       
        if (stateJson) {
            const newState = JSON.parse(stateJson);
            gameState = newState;
            updateGameUI();
            return true;
        } else {
            console.warn("Состояние игры не найдено. Начните новую игру.");
            return false;
        }
    }

    // --- ОСНОВНАЯ ЛОГИКА ИГРЫ ---

    function handleShot(r, c, cellElement) {
        if (!gameState || gameState.status !== 'active') return;

        if (gameState.currentTurnId !== currentUserId) {
            document.getElementById('message-box').textContent = "Сейчас ход вашего соперника. Нажмите 'Обновить Ход Соперника'.";
            return;
        }
       
        const myIndex = gameState.players[0].id === currentUserId ? 0 : 1;
        const opponentIndex = 1 - myIndex;
        const opponentMoves = myIndex === 0 ? gameState.p1Moves : gameState.p2Moves;
       
        if (opponentMoves[r][c] !== 0) {
            document.getElementById('message-box').textContent = `Вы уже стреляли в ${coordsToLabel(r, c)}!`;
            return;
        }

        // Логика выстрела: всегда стреляем в скрытую сетку соперника
        const isHit = gameState.hiddenShipsGrid[r][c] === 1; // Упрощенно: оба игрока стреляют в одну и ту же сетку
        const result = isHit ? 2 : 1; // 1: промах, 2: попадание

        // Обновляем сетку ходов
        opponentMoves[r][c] = result;
       
        if (isHit) {
            gameState.players[myIndex].hits++;
            gameState.message = `🎉 ПОПАДАНИЕ в ${coordsToLabel(r, c)}! Ваш ход продолжается.`;
        } else {
            gameState.currentTurnId = gameState.players[opponentIndex].id; // Передаем ход
            gameState.message = `🌊 ПРОМАХ в ${coordsToLabel(r, c)}. Ход переходит к ${gameState.players[opponentIndex].name}.`;
        }
       
        if (gameState.players[myIndex].hits >= MAX_HITS) {
            gameState.status = 'finished';
            gameState.message = `🏆 ПОБЕДА! ${gameState.players[myIndex].name} выиграл! 🏆`;
        }

        // Сохраняем и обновляем UI
        saveGameStateToForum(gameState);
        updateGameUI();
    }
   
    // --- РЕНДЕРИНГ ИНТЕРФЕЙСА ---
   
    function updateGameUI() {
        if (!gameState) return;
       
        const myIndex = gameState.players[0].id === currentUserId ? 0 : 1;
        const opponentIndex = 1 - myIndex;
        const myMoves = myIndex === 0 ? gameState.p2Moves : gameState.p1Moves;
        const turnPlayer = gameState.players.find(p => p.id === gameState.currentTurnId);

        isMyTurn = gameState.currentTurnId === currentUserId;
       
        document.getElementById('message-box').textContent = gameState.message;
       
        const container = document.getElementById('battleship-container');
        container.innerHTML = '';

        container.appendChild(createCell('bs-header', ''));

        // Заголовки столбцов
        for (let c = 0; c < GRID_SIZE; c++) {
            container.appendChild(createCell('bs-header', coordsToLabel(0, c)[0]));
        }

        // Ячейки
        for (let r = 0; r < GRID_SIZE; r++) {
            container.appendChild(createCell('bs-header', r + 1));
           
            for (let c = 0; c < GRID_SIZE; c++) {
                const cell = createCell('bs-cell', '');
                const moveStatus = myMoves[r][c];
               
                let isTargetable = gameState.status === 'active' && isMyTurn && moveStatus === 0;

                if (moveStatus === 2) {
                    cell.textContent = '💥';
                    cell.classList.add('bs-hit');
                } else if (moveStatus === 1) {
                    cell.textContent = '💧';
                    cell.classList.add('bs-miss');
                } else if (isTargetable) {
                    cell.textContent = coordsToLabel(r, c);
                    cell.classList.add('bs-target-cell');
                    cell.onclick = () => handleShot(r, c, cell);
                } else {
                    cell.textContent = ' ';
                    cell.style.cursor = 'default';
                }

                container.appendChild(cell);
            }
        }
    }
   
    function createCell(className, content) {
        const cell = document.createElement('div');
        cell.className = 'bs-cell ' + className;
        cell.textContent = content;
        return cell;
    }

    async function initializeGame() {
        document.getElementById('game-id').textContent = GAME_ID_PLACEHOLDER;
        await loadUserId();
       
        // 1. Попытка загрузить существующую игру
        const loaded = await loadGameStateFromForum();

        // 2. Если игра не загружена или нет P2, начать новую
        if (!loaded || !gameState.players.some(p => p.id === currentUserId && p.id !== 'WAITING_FOR_PLAYER_2')) {
            const p1Id = currentUserId;
            const p2Id = 'WAITING_FOR_PLAYER_2';
            gameState = getInitialState(p1Id, p2Id);
            document.getElementById('message-box').textContent = "Новая игра создана. Ждем соперника (с таким же GAME ID).";
            // Заглушка для сохранения:
            localStorage.setItem(GAME_ID_PLACEHOLDER, JSON.stringify(gameState));
        }
       
        updateGameUI();
    }

    // --- СЛУШАТЕЛИ СОБЫТИЙ ---

    document.getElementById('reset-button').onclick = async () => {
        if (confirm("Вы уверены, что хотите начать новую игру?")) {
            const p1Id = currentUserId;
            const p2Id = 'WAITING_FOR_PLAYER_2';
            gameState = getInitialState(p1Id, p2Id);
            // Заглушка для сохранения:
            localStorage.setItem(GAME_ID_PLACEHOLDER, JSON.stringify(gameState));
            await saveGameStateToForum(gameState);
            updateGameUI();
        }
    };
   
    document.getElementById('refresh-button').onclick = async () => {
        await loadGameStateFromForum();
        updateGameUI();
        if (isMyTurn) {
             document.getElementById('message-box').textContent = "Ваш ход!";
        } else {
             document.getElementById('message-box').textContent = "Ход соперника. Обновлено.";
        }
    };

    window.onload = initializeGame;

</script>
</body>
</html>[/html]

0

9

[html]<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Морской Бой - Пошаговая версия</title>
    <style>
        /* Общий стиль контейнера, чтобы избежать конфликта с body форума */
        .game-wrapper {
            font-family: 'Inter', sans-serif;
            background-color: #f7f9fb;
            padding: 20px; /* Увеличил отступы */
            margin: 0 auto;
            max-width: 500px;
            box-sizing: border-box;
            border-radius: 12px;
            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); /* Более сильная тень */
        }
        .container {
            max-width: 450px;
            margin: 0 auto;
            background: white;
            padding: 20px;
            border-radius: 10px;
        }
        h1 { text-align: center; color: #3b82f6; margin-bottom: 20px; font-size: 1.8rem; }
       
        .bs-grid-container {
            display: grid;
            grid-template-columns: repeat(11, 1fr);
            gap: 1px;
            max-width: 400px;
            margin: 10px auto;
            border: 3px solid #3b82f6;
            border-radius: 8px;
            overflow: hidden;
            box-sizing: content-box;
        }
        .bs-cell {
            height: 35px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 0.9rem;
            cursor: pointer;
            background-color: #dbeafe;
            transition: background-color 0.2s, transform 0.1s;
            font-weight: bold;
            color: #1f2937;
        }
        .bs-header {
            background-color: #e5e7eb;
            cursor: default;
            color: #4b5563;
            font-size: 0.8rem;
        }
        .bs-target-cell:hover { background-color: #93c5fd; transform: scale(1.05); }
        .bs-hit { background-color: #f87171 !important; color: white !important; font-size: 1.5rem !important; cursor: default; }
        .bs-miss { background-color: #60a5fa !important; color: white !important; font-size: 1.5rem !important; cursor: default; }
        #message-box {
            text-align: center;
            padding: 10px;
            margin: 15px auto;
            border-radius: 6px;
            font-weight: bold;
            background-color: #fef3c7;
            color: #92400e;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }
        .game-button {
            display: block;
            width: 80%;
            margin: 10px auto 0;
            padding: 12px;
            color: white;
            border: none;
            border-radius: 25px;
            font-size: 1rem;
            cursor: pointer;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            transition: background-color 0.2s, transform 0.1s;
        }
        .game-button:active { transform: translateY(1px); }
        #refresh-button { background-color: #f97316; }
        #reset-button { background-color: #10b981; }
    </style>
</head>
<body>
    <div class="game-wrapper">
        <div class="container">
            <h1>Морской Бой (Пошаговый)</h1>
            <div id="status-info" class="text-sm text-center mb-2">
                <p>ID Игры: <span id="game-id">Инициализация...</span></p>
                <p>Ваш ID: <span id="user-id">Инициализация...</span></p>
            </div>
            <div id="message-box">Запуск игры...</div>
            <div id="battleship-container" class="bs-grid-container">
                <!-- Сетка будет сгенерирована здесь -->
            </div>
            <button id="refresh-button" class="game-button">Обновить Ход Соперника</button>
            <button id="reset-button" class="game-button">Начать Новую Игру</button>
        </div>
    </div>

<script>
    // --- ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ И НАСТРОЙКИ ---
    const GRID_SIZE = 10;
    const MAX_HITS = 20;
    const GAME_ID_PLACEHOLDER = 'battleship_match_123';
   
    let gameState = null;
    let currentUserId = 'User_Unknown';
    let isMyTurn = false;

    /**
     * Вычисляет или загружает ID пользователя синхронно, используя sessionStorage.
     * Это гарантирует, что ID будет установлен немедленно.
     */
    function setupUserIdSynchronously() {
        // Убеждаемся, что window.sessionStorage доступен
        if (typeof window.sessionStorage === 'undefined') {
            currentUserId = 'Fallback_' + Math.random().toString(36).substring(2, 6);
            console.error("SessionStorage недоступен. Использование временного ID.");
        } else {
            const storedId = sessionStorage.getItem('bsUserId');
            if (storedId) {
                currentUserId = storedId;
            } else {
                // Генерируем новый ID, если он не найден
                currentUserId = 'User_' + Math.random().toString(36).substring(2, 9);
                sessionStorage.setItem('bsUserId', currentUserId);
            }
        }
       
        // Немедленно обновляем UI
        const userIdElement = document.getElementById('user-id');
        if(userIdElement) {
            // Показываем первые 10 символов и троеточие для краткости
            userIdElement.textContent = currentUserId.substring(0, 10) + '...';
        }
        console.log("�� ID пользователя установлен синхронно:", currentUserId);
    }
   
    // --- ФУНКЦИИ ИГРОВОЙ ЛОГИКИ ---

    function createEmptyGrid() { return Array(GRID_SIZE).fill(0).map(() => Array(GRID_SIZE).fill(0)); }

    function placeShipsRandomly() {
        const grid = createEmptyGrid();
        let placedCellsCount = 0;
        while (placedCellsCount < MAX_HITS) {
            const r = Math.floor(Math.random() * GRID_SIZE);
            const c = Math.floor(Math.random() * GRID_SIZE);
            if (grid[r][c] === 0) {
                grid[r][c] = 1; // 1 = Корабль
                placedCellsCount++;
            }
        }
        return grid;
    }
   
    function coordsToLabel(r, c) { return `${String.fromCharCode(65 + c)}${r + 1}`; }
   
    function getInitialState(p1Id, p2Id) {
        return {
            gameId: GAME_ID_PLACEHOLDER,
            players: [
                { id: p1Id, name: "Игрок 1", hits: 0 },
                { id: p2Id, name: "Игрок 2", hits: 0 }
            ],
            // Сетки ходов
            p1Moves: createEmptyGrid(),
            p2Moves: createEmptyGrid(),
            // Сетка расположения кораблей (цель)
            hiddenShipsGrid: placeShipsRandomly(),
            currentTurnId: p1Id,
            status: 'active',
            message: 'Игра началась. Ход Игрока 1.'
        };
    }

    // --- ФУНКЦИИ ХРАНЕНИЯ (LocalStorage Заглушка) ---
   
    async function saveGameStateToForum(state) {
        // Проверяем, что LocalStorage доступен
        if (typeof window.localStorage === 'undefined') {
            console.error("LocalStorage недоступен. Состояние игры не будет сохранено.");
            return false;
        }
       
        const stateJson = JSON.stringify(state);
        localStorage.setItem(GAME_ID_PLACEHOLDER, stateJson);
       
        document.getElementById('message-box').textContent = "СОХРАНЕНО. Ход передан. Нажмите 'Обновить Ход Соперника' для проверки.";
        return true;
    }
   
    async function loadGameStateFromForum() {
        // Проверяем, что LocalStorage доступен
        if (typeof window.localStorage === 'undefined') {
            console.error("LocalStorage недоступен. Невозможно загрузить состояние.");
            return false;
        }
       
        let stateJson = localStorage.getItem(GAME_ID_PLACEHOLDER);
       
        if (stateJson) {
            const newState = JSON.parse(stateJson);
           
            // Логика присоединения второго игрока
            if (newState.players[1].id === 'WAITING_FOR_PLAYER_2' && currentUserId !== newState.players[0].id) {
                newState.players[1].id = currentUserId;
                newState.players[1].name = "Игрок 2";
                await saveGameStateToForum(newState); // Сохраняем изменение
            }
           
            gameState = newState;
            updateGameUI();
            return true;
        } else {
            console.warn("Состояние игры не найдено. Начните новую игру.");
            return false;
        }
    }

    // --- ОСНОВНАЯ ЛОГИКА ИГРЫ ---

    function handleShot(r, c) {
        if (!gameState || gameState.status !== 'active') {
            document.getElementById('message-box').textContent = "Игра не активна.";
            return;
        }

        if (gameState.currentTurnId !== currentUserId) {
            document.getElementById('message-box').textContent = "Сейчас ход вашего соперника. Нажмите 'Обновить Ход Соперника'.";
            return;
        }
       
        const myIndex = gameState.players.findIndex(p => p.id === currentUserId);
        if (myIndex === -1) {
            document.getElementById('message-box').textContent = "Ошибка: Ваш ID не найден в игре.";
            return;
        }
       
        const opponentIndex = 1 - myIndex;
        const myMovesGrid = myIndex === 0 ? gameState.p1Moves : gameState.p2Moves;
       
        if (myMovesGrid[r][c] !== 0) {
            document.getElementById('message-box').textContent = `Вы уже стреляли в ${coordsToLabel(r, c)}!`;
            return;
        }

        const isHit = gameState.hiddenShipsGrid[r][c] === 1;
        const result = isHit ? 2 : 1; // 1: промах, 2: попадание

        myMovesGrid[r][c] = result;
       
        if (isHit) {
            gameState.players[myIndex].hits++;
            gameState.message = `�� ПОПАДАНИЕ в ${coordsToLabel(r, c)}! Ваш ход продолжается.`;
        } else {
            // Передаем ход
            gameState.currentTurnId = gameState.players[opponentIndex].id;
            gameState.message = `�� ПРОМАХ в ${coordsToLabel(r, c)}. Ход переходит к ${gameState.players[opponentIndex].name}.`;
        }
       
        if (gameState.players[myIndex].hits >= MAX_HITS) {
            gameState.status = 'finished';
            gameState.message = `�� ПОБЕДА! ${gameState.players[myIndex].name} выиграл! ��`;
        }

        // Сохраняем и обновляем UI
        saveGameStateToForum(gameState);
        updateGameUI();
    }
   
    // --- РЕНДЕРИНГ ИНТЕРФЕЙСА ---
   
    function updateGameUI() {
        if (!gameState) return;
       
        const myIndex = gameState.players.findIndex(p => p.id === currentUserId);
        const myMovesGrid = (myIndex === -1) ? createEmptyGrid() : ((myIndex === 0) ? gameState.p1Moves : gameState.p2Moves);
       
        isMyTurn = gameState.currentTurnId === currentUserId;
       
        document.getElementById('message-box').textContent = gameState.message;
       
        const container = document.getElementById('battleship-container');
        container.innerHTML = '';

        container.appendChild(createCell('bs-header', ''));

        // Заголовки столбцов (A, B, C...)
        for (let c = 0; c < GRID_SIZE; c++) {
            container.appendChild(createCell('bs-header', coordsToLabel(0, c)[0]));
        }

        // Ячейки
        for (let r = 0; r < GRID_SIZE; r++) {
            container.appendChild(createCell('bs-header', r + 1)); // Заголовок строки (1, 2, 3...)
           
            for (let c = 0; c < GRID_SIZE; c++) {
                const cell = createCell('bs-cell', '');
                const moveStatus = myMovesGrid[r][c];
               
                let isTargetable = gameState.status === 'active' && isMyTurn && moveStatus === 0;

                if (moveStatus === 2) {
                    cell.textContent = '��';
                    cell.classList.add('bs-hit');
                } else if (moveStatus === 1) {
                    cell.textContent = '��';
                    cell.classList.add('bs-miss');
                } else if (isTargetable) {
                    cell.textContent = '';
                    cell.classList.add('bs-target-cell');
                    cell.onclick = () => handleShot(r, c, cell);
                } else {
                    cell.textContent = ' ';
                    cell.style.cursor = 'default';
                }

                container.appendChild(cell);
            }
        }
    }
   
    function createCell(className, content) {
        const cell = document.createElement('div');
        cell.className = 'bs-cell ' + className;
        cell.textContent = content;
        return cell;
    }

    async function initializeGame() {
        console.log("Шаг 1: Начало инициализации. Установка ID пользователя.");
       
        // 1. Устанавливаем ID игры и пользователя немедленно
        setupUserIdSynchronously(); // СИНХРОННЫЙ ВЫЗОВ
       
        const gameIdElement = document.getElementById('game-id');
        if(gameIdElement) {
             gameIdElement.textContent = GAME_ID_PLACEHOLDER;
        }
       
        document.getElementById('message-box').textContent = "Шаг 2: Поиск существующей игры...";
       
        // Заменяем window.confirm на простой console.log и прямое выполнение
        const customConfirm = (message) => {
            console.warn(`[ATTENTION]: Требуется подтверждение: ${message}. Возвращено true.`);
            return true;
        };

        try {
            // 2. Попытка загрузить существующую игру
            const loaded = await loadGameStateFromForum();

            // 3. Если игра не загружена, начать новую
            if (!loaded) {
                const p1Id = currentUserId;
                const p2Id = 'WAITING_FOR_PLAYER_2';
                gameState = getInitialState(p1Id, p2Id);
               
                // Сохраняем начальное состояние
                await saveGameStateToForum(gameState);
               
                document.getElementById('message-box').textContent = "Шаг 3: Новая игра создана. Ждем соперника. Поделитесь этой страницей!";
            } else if (gameState && gameState.players[1].id === 'WAITING_FOR_PLAYER_2' && gameState.players[0].id !== currentUserId) {
                 document.getElementById('message-box').textContent = "Вы присоединились! Ожидайте первого хода Игрока 1.";
            }
           
            updateGameUI();
        } catch (e) {
            console.error("Критическая ошибка при инициализации игры:", e);
            document.getElementById('message-box').textContent = `КРИТИЧЕСКАЯ ОШИБКА! Игра не запустилась: ${e.message}. Проверьте консоль браузера.`;
        }
    }

    // --- СЛУШАТЕЛИ СОБЫТИЙ ---

    document.getElementById('reset-button').onclick = async () => {
        // Убрал confirm(), так как он заблокирован. Просто перезапускаем.
        const p1Id = currentUserId;
        const p2Id = 'WAITING_FOR_PLAYER_2';
        gameState = getInitialState(p1Id, p2Id);
        await saveGameStateToForum(gameState);
        updateGameUI();
        document.getElementById('message-box').textContent = "Новая игра создана. Сделайте первый выстрел!";
    };
   
    document.getElementById('refresh-button').onclick = async () => {
        await loadGameStateFromForum();
       
        if (gameState) {
            if (isMyTurn) {
                 document.getElementById('message-box').textContent = "Ваш ход! Стреляйте!";
            } else {
                 document.getElementById('message-box').textContent = "Ход соперника. Обновлено. Ожидайте.";
            }
        }
    };

    window.onload = initializeGame;

</script>
</body>
</html>[/html]

0

10

[html]
<script src="https://cdn.jsdelivr.net/npm/@quadrosystems/mybb-sdk@0.9.7/lib/index.min.js"></script>

<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 {number} COOLDOWN_MINUTES - Время кулдауна в минутах.
* Установите 0 для отключения.
*/
const COOLDOWN_MINUTES = 0; // <-- УСТАНОВИТЬ ВРУЧНУЮ
const COOLDOWN_MS = COOLDOWN_MINUTES * 60000;

/* Символы (Картинки) - ВСЕГДА HTTPS */
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 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 {
        // Получаем ник из SDK
        const userDetails = await MYBB.user.getDetails();
        userName = userDetails.username || userName || "Игрок";
       
        // Получаем ID темы
        const threadDetails = await MYBB.thread.getDetails();
        threadID = threadDetails.tid;
console.log("Thread ID установлен:", threadID); // <--- ДОБАВИТЬ ЭТУ СТРОКУ

    } catch (e) {
        console.warn("MyBB SDK: Не удалось получить детали пользователя/темы.", e);
        if (!userName) userName = "Игрок"; // Fallback, если SDK не сработал
    }
  }

  // Запрашиваем ник у гостя, если не удалось получить его с форума
  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)) {
    // 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)) {
    // КОМБО: Кристалл (0), Корона (1), Сердце (3) - нет Черепа
    reward = 300;
    winText = "�� КОМБО! Кристалл, Корона, Сердце!";
    color = "#ff6a9a";
    winDescription = `�� Комбо (0, 1, 3)! Выпало: -- -- -- — ВЫИГРЫШ: ${reward}.`;
  } else if (!results.includes(s2)) {
    // КОМБО: НЕТ ЧЕРЕПА (2)
    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 && threadID && typeof MYBB !== 'undefined' && MYBB.thread) {
    // Используем и [img] для BB-кода, чтобы пост был красивым
    const postContent = `
[center][b]Я только что крутил Слот-Машину и выиграл ${reward} монет!

${winDescription}
Мой текущий баланс: ${userCoins}
[/center]
`;
    try {
      // Отправка сообщения
      await MYBB.thread.reply(threadID, postContent);
    } catch (error) {
      console.error("Ошибка при отправке сообщения:", error);
      // Если не удалось отправить, сообщаем пользователю
      alert(`Выигрыш ${reward} получен, но не удалось отправить сообщение на форум. Проверьте, авторизованы ли вы.`);
    }
  }
};

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

0

11

[html]
<script src="https://cdn.jsdelivr.net/npm/@quadrosystems/mybb-sdk@0.9.7/lib/index.min.js"></script>

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

0

12

[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;">Инициализация...</p>
</div>

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

    // Функция инициализации, которая теперь гарантированно видит MYBB
    async function initializeTest() {
        if (typeof MYBB === 'undefined' || typeof MYBB.thread === 'undefined') {
             statusDisplay.textContent = "Критическая ошибка: MYBB не определен после внедрения.";
             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);
        }
    }

    // Функция отправки сообщения (без изменений)
    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]

После прямого внедрения, вы должны увидеть либо **"Готово к тесту!"** (и кнопка станет активной), либо новую ошибку (например, `403 Forbidden` при попытке постинга), что будет означать, что вы наконец-то установили связь с SDK.

0

13

[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

14

[html]
<div id="lotteryMachine" style="width:90%; max-width:350px; 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);">

  <div style="font-size:0.9em; color:#ffdd8a; text-align:right;">
    �� <span id="userName">Гость</span> | �� <span id="userCoins">0</span>
  </div>

  <b style="font-size:1.3em;">�� Лотерейные Ячейки</b>

  <div style="margin-top:10px; font-size:0.9em; color:#d9c7a1;">
    ⏳ Новый ход через: <span id="timer" style="font-weight:bold; color:#ffdd8a;"></span>
  </div>

  <table id="lotteryGrid" style="width:100%; border-collapse:collapse; margin:15px 0;">
    <tbody>
      <tr>
        <td data-id="1" class="lot-cell">1</td>
        <td data-id="2" class="lot-cell">2</td>
        <td data-id="3" class="lot-cell">3</td>
      </tr>
      <tr>
        <td data-id="4" class="lot-cell">4</td>
        <td data-id="5" class="lot-cell">5</td>
        <td data-id="6" class="lot-cell">6</td>
      </tr>
      <tr>
        <td data-id="7" class="lot-cell">7</td>
        <td data-id="8" class="lot-cell">8</td>
        <td data-id="9" class="lot-cell">9</td>
      </tr>
    </tbody>
  </table>

  <div id="resultDisplay" style="margin-top:12px; font-size:1.1em; font-weight:bold; color:#ffdd00; min-height:2.2em;">Нажмите на ячейку!</div>
</div>

<style>
#lotteryGrid td {
  width: 33.33%;
  aspect-ratio: 1; /* Делает ячейки квадратными */
  border: 3px solid #7c653f;
  background: #282828;
  color: #fff;
  font-size: 1.5em;
  font-weight: bold;
  cursor: pointer;
  transition: background 0.2s, transform 0.1s;
  user-select: none;
}
#lotteryGrid td:hover:not(.disabled) {
  background: #3a3a3a;
}
#lotteryGrid td.disabled {
  cursor: not-allowed;
  opacity: 0.5;
}
.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 REWARD_AMOUNT = 500;
/* ----------------- */

const timer = document.getElementById("timer");
const resultDisplay = document.getElementById("resultDisplay");
const lotteryGrid = document.getElementById("lotteryGrid");
const cells = document.querySelectorAll('#lotteryGrid td');

let threadID = null;
let isUserLoggedIn = false;
let userName = localStorage.getItem("slotUserName");
let userCoins = parseInt(localStorage.getItem("slotCoins") || "0");
let winningCellID = 0; // Новая переменная для хранения выигрышной ячейки

// Функция для установки класса "disabled"
const setCellsDisabled = (isDisabled) => {
    cells.forEach(cell => {
        if (isDisabled) {
            cell.classList.add('disabled');
            cell.onclick = null;
        } else {
            cell.classList.remove('disabled');
            cell.onclick = () => handleLotteryClick(parseInt(cell.dataset.id));
        }
    });
};

       
    } else if (isWinner) {
        resultDisplay.innerHTML += `<br><span style="color:#f00; font-size:0.8em;">(Войдите, чтобы запостить результат!)</span>`;
    }

// Асинхронная инициализация пользователя и получение ID темы
async function initialize() {
    // ... (весь ваш код инициализации из прошлого ответа) ...
    if (typeof MYBB !== 'undefined' && MYBB.user && MYBB.thread) {
      try {
          const userDetails = await MYBB.user.getDetails();
          isUserLoggedIn = userDetails.uid && userDetails.uid > 0;
          userName = userDetails.username || userName || (isUserLoggedIn ? "Игрок" : "Гость");
          const threadDetails = await MYBB.thread.getDetails();
          threadID = threadDetails.tid;
      } 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;
   
    // Определяем выигрышную ячейку (только один раз за сессию/ход)
    winningCellID = Math.floor(Math.random() * 9) + 1;
    console.log("Winning Cell ID:", winningCellID); // Для проверки в консоли
   
    updateTimer();
}

/* --- Функции Кулдауна --- */
const canClick = () => {
    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 = "готово ✅";
        setCellsDisabled(false); // Активируем ячейки
        return;
    }
   
    let minutes = Math.floor(diff/60000);
    let seconds = Math.floor((diff%60000)/1000);
    timer.textContent = `${minutes}м ${seconds}с`;
    setCellsDisabled(true); // Отключаем ячейки
};
setInterval(updateTimer, 1000);

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

    const isWinner = cellId === winningCellID;
    let reward = isWinner ? REWARD_AMOUNT : 0;
   
    // Визуальное отображение результата
    cells.forEach(cell => {
        cell.style.background = '#404040'; // Закрашиваем все
    });
    const clickedCell = document.querySelector(`#lotteryGrid td[data-id="${cellId}"]`);
    clickedCell.style.background = isWinner ? '#0f0' : '#f00'; // Выделяем результат
    clickedCell.innerHTML = isWinner ? '��' : '❌';
   
    resultDisplay.innerHTML = isWinner ? `<span class="winGlow">ПОБЕДА! +${reward}</span>` : 'Провал! Попробуйте еще.';

    // Обновление баланса и кулдауна
    userCoins += reward;
    localStorage.setItem("slotCoins", userCoins);
    localStorage.setItem("slotLastSpin", Date.now());
    document.getElementById("userCoins").textContent = userCoins;
    updateTimer(); // Запускаем кулдаун
   
    // 5. Постинг на форум
    if (isUserLoggedIn && threadID) {
        const statusText = isWinner ? `�� ВЫИГРЫШ! Я нашел сокровище в ячейке №${cellId} и выиграл ${reward} монет!` : `�� ПРОИГРЫШ. Я выбрал ячейку №${cellId}, но ничего не нашел.`;

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

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

0

15

https://upforme.ru/uploads/001c/80/ee/3/22789.gif   https://upforme.ru/uploads/001c/80/ee/3/591404.gif

0


Вы здесь » Тест оформления » Короткие зарисовки » морской бой


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