微件:GGLScratchGame

来自Limbo Wiki Mirror
Gaoice留言 | 贡献2026年2月1日 (日) 19:35的版本
   <button class="ggl-start-btn">开始抽奖</button>
     点击灰色区域揭示内容,全部揭示后才能扫描。
     剩余彩票:
   <img class="ggl-bg"
        src="180px-%E5%9B%BE%E7%89%871.png">
     <button id="ggl-scan">扫描结果</button>

<style> .ggl-frame {

 max-width: 420px;
 margin: 20px auto;
 padding: 10px;
 border: 2px solid #333;
 background: #111;
 color: #eee;
 font-family: sans-serif;

}

/* 顶部 */ .ggl-top {

 position: absolute;
 top: 0;
 left: 0;
 width: 100%;
 height: 30%;
 padding: 10px;
 box-sizing: border-box;

}

.ggl-rules {

 font-size: 14px;
 opacity: .8;

}

.ggl-remaining {

 margin-top: 6px;
 font-size: 16px;
 font-weight: bold;

}

.ggl-tickets-left {

 font-size: 14px;
 margin-top: 4px;

}

/* 彩票 */ .ggl-root {

 position: relative;
 width: 100%;
 aspect-ratio: 1075 / 1911;

}

.ggl-bg {

 width: 100%;
 display: block;

}

/* 刮奖区 */ .ggl-scratch-area {

 position: absolute;
 left: 10%;
 top: 35%;
 width: 80%;
 height: 53%;

}

.ggl-grid {

 display: grid;
 grid-template-columns: repeat(5, 1fr);
 grid-template-rows: repeat(6, 1fr);
 gap: 4px;
 width: 100%;
 height: 100%;

}

/* 单格 */ .ggl-cell {

 position: relative;
 cursor: pointer;

}

.ggl-cover, .ggl-reveal {

 position: absolute;
 inset: 0;
 width: 100%;
 height: 100%;
 pointer-events: none;

}

.ggl-cover {

 z-index: 3;

} .ggl-reveal {

 z-index: 1;

}

/* 奖项层 */ .ggl-prize {

 position: absolute;
 inset: 0;
 z-index: 2;
 display: flex;
 align-items: center;
 justify-content: center;
 font-size: 12px;
 color: #000;
 text-align: center;
 pointer-events: none;

}

/* 吉祥物 & 气泡 */ .ggl-mascot {

 position: absolute;
 left: 6%;
 top: 105%;
 bottom: 8%;
 font-size: 20px;
 color: #000;

}

.ggl-bubble {

 position: absolute;
 left: 6%;
 bottom: 14%;
 top: 100%;
 width: 60%;
 background: rgba(255,255,255,.9);
 color: #000;
 padding: 6px;
 font-size: 13px;

}

/* 扫描 */ .ggl-controls {

 position: absolute;
 right: 6%;
 bottom: 8%;

}

  1. ggl-scan {
 opacity: .3;

}

  1. ggl-scan.active {
 opacity: 1;

}

/* 遮罩 */ .ggl-overlay {

 position: absolute;
 inset: 0;
 background: rgba(0,0,0,.9);
 z-index: 50;
 display: flex;
 align-items: center;
 justify-content: center;

}

.ggl-start-btn {

 padding: 12px 24px;
 font-size: 16px;

} </style> <script> (function () {

 /********************
  * 全局状态
  ********************/
 const gameState = {
   totalTickets: 11,
   currentTicket: 1,
   totalCells: 30,
   revealedCells: 0,
   started: false,
   data: 0
 };
 /********************
  * DOM
  ********************/
 const grid = document.querySelector('.ggl-grid');
 const bubble = document.querySelector('.ggl-bubble');
 const mascot = document.querySelector('.ggl-mascot');
 const scanBtn = document.getElementById('ggl-scan');
 const ticketLeftEl = document.getElementById('ggl-ticket-left');
 const startMask = document.getElementById('ggl-start-mask');
 mascot.style.color = '#000';
 /********************
  * 彩票剧情表(核心)
  ********************/
 const TICKETS = {
   1: {
     mascot: '(^▽^)',
     text: '欢迎,先来三张试试吧。',
     rewards: ['DATA', 'EMPTY', 'EMPTY']
   },
   2: {
     mascot: '(ノ◕ヮ◕)ノ',
     text: '四个字可以连在一起。',
     rewards: ['DATA','DATA','DATA','DATA']
   },
   3: {
     mascot: '(・∀・)',
     text: '中了刮刮乐!+1 Data',
     rewards: ['DATA']
   },
   4: {
     mascot: '(・_・)',
     text: '什么都没中。',
     rewards: []
   },
   5: {
     mascot: '(;゚Д゚)',
     text: '不对劲……又中了 Data。',
     rewards: ['DATA']
   },
   6: {
     mascot: ,
     text: '什么都没有。',
     rewards: []
   },
   7: {
     mascot: '...',
     text: '▒▓░▒▓░▒▓',
     rewards: ['DATA']
   },
   8: {
     mascot: '(^▽^)',
     text: '中了两张 Data!',
     rewards: ['DATA','DATA']
   },
   9: {
     mascot: '(・_・)',
     text: '……全部都是 Data?',
     rewards: Array(30).fill('DATA')
   },
   10: {
     mascot: '(;゚Д゚)',
     text: '停下来。',
     rewards: [],
     lock: true
   },
   11: {
     mascot: '平安喜乐',
     text: '你知道的太多了。',
     rewards: [],
     end: true
   }
 };
 /********************
  * 奖项坐标生成
  ********************/
 function buildRewardMap(ticket) {
   const map = Array(30).fill('EMPTY');
   const rewards = TICKETS[ticket].rewards || [];
   rewards.forEach((r, i) => map[i] = r);
   return map;
 }
 /********************
  * UI 刷新
  ********************/
 function updateTopUI() {
   if (!ticketLeftEl) return;
   ticketLeftEl.textContent =
     gameState.totalTickets - gameState.currentTicket + 1;
 }
 function updateMascot(ticket) {
   mascot.textContent = TICKETS[ticket].mascot || ;
   bubble.textContent = TICKETS[ticket].text || ;
 }
 /********************
  * 构建 30 格
  ********************/
 function buildGrid(ticket) {
   grid.innerHTML = ;
   gameState.revealedCells = 0;
   scanBtn.classList.remove('active');
   const rewardMap = buildRewardMap(ticket);
   const locked = TICKETS[ticket].lock;
   for (let i = 0; i < 30; i++) {
     const cell = document.createElement('div');
     cell.className = 'ggl-cell';
     cell.innerHTML = `
       <img src="180px-%E5%88%AE%E5%BC%80%E5%90%8E.jpg">
${rewardMap[i] === 'DATA' ? 'DATA' : }
     `;
     const cover = cell.querySelector('.ggl-cover');
     if (!locked) {
       cover.addEventListener('click', () => {
         if (cell.dataset.done) return;
         cell.dataset.done = '1';
         cover.style.opacity = '0';
         gameState.revealedCells++;
         if (rewardMap[i] === 'DATA') {
           gameState.data++;
         }
         if (gameState.revealedCells >= gameState.totalCells) {
           scanBtn.classList.add('active');
           bubble.textContent = '可以扫描了。';
         }
       });
     }
     grid.appendChild(cell);
   }
 }
 /********************
  * 下一张彩票
  ********************/
 function nextTicket() {
   gameState.currentTicket++;
   updateTopUI();
   if (gameState.currentTicket > gameState.totalTickets) return;
   if (gameState.currentTicket === 11) {
     document.body.style.filter = 'invert(1)';
     bubble.style.background = '#000';
     bubble.style.color = 'red';
   }
   updateMascot(gameState.currentTicket);
   buildGrid(gameState.currentTicket);
 }
 /********************
  * 扫描按钮
  ********************/
 scanBtn.addEventListener('click', () => {
   if (!scanBtn.classList.contains('active')) return;
   bubble.textContent = '扫描中……';
   setTimeout(() => {
     if (TICKETS[gameState.currentTicket].end) {
       location.href = '/index.php?title=六世恶言之一';
     } else {
       nextTicket();
     }
   }, 800);
 });
 /********************
  * 初始化遮罩
  ********************/
 if (startMask) {
   startMask.addEventListener('click', () => {
     startMask.remove();
     gameState.started = true;
     updateTopUI();
     updateMascot(1);
     buildGrid(1);
   });
 } else {
   updateTopUI();
   updateMascot(1);
   buildGrid(1);
 }

})(); </script>