微件:GGLScratchGame:修订间差异

来自Limbo Wiki Mirror
Gaoice留言 | 贡献
无编辑摘要
标签(旧)WikiEditor
Gaoice留言 | 贡献
无编辑摘要
标签(旧)WikiEditor
第1行: 第1行:
<noinclude>
<!DOCTYPE html>
== 刮刮乐重构版 ==
<html>
使用方式:修改第 222-224 行的图片URL,然后保存此页面
<head>
当前配置的图片:
    <meta charset="UTF-8">
* 底图(bg):https://wm.gaoice.run/images/thumb/b/b6/%E5%9B%BE%E7%89%871.png/180px-%E5%9B%BE%E7%89%871.png
    <style>
* 覆盖图(cover):https://wm.gaoice.run/images/thumb/5/5a/%E5%88%AE%E5%BC%80%E5%89%8D.png/180px-%E5%88%AE%E5%BC%80%E5%89%8D.png
        .scratch-lottery-container {
* 底层图(revealed):https://wm.gaoice.run/images/thumb/4/4a/%E5%88%AE%E5%BC%80%E5%90%8E.jpg/180px-%E5%88%AE%E5%BC%80%E5%90%8E.jpg
            font-family: 'Arial', sans-serif;
</noinclude>
            max-width: 1100px;
            margin: 20px auto;
            background: #f5f5f5;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
 
        .scratch-lottery-header {
            text-align: center;
            margin-bottom: 20px;
        }
 
        .scratch-lottery-header h2 {
            margin: 0 0 10px 0;
            color: #333;
            font-size: 24px;
        }
 
        .scratch-lottery-status {
            display: flex;
            justify-content: space-around;
            margin: 15px 0;
            font-size: 14px;
            color: #666;
        }
 
        .status-item {
            text-align: center;
        }
 
        .status-label {
            font-weight: bold;
            color: #333;
            display: block;
            font-size: 12px;
            margin-bottom: 4px;
        }


<includeonly>
        .status-value {
<style>
            font-size: 20px;
/* === 核心容器 === */
            color: #ff6b35;
.ggl-game-root {
        }
    max-width: 400px; /* 限制最大显示宽度,防止在大屏太巨大 */
 
    margin: 20px auto;
        /* 彩票主容器 */
    font-family: 'Courier New', monospace;
        .lottery-ticket-wrapper {
    user-select: none;
            position: relative;
}
            margin: 20px auto;
            display: inline-block;
            width: 100%;
            max-width: 540px;
            background: white;
            border-radius: 4px;
            overflow: hidden;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
        }
 
        .lottery-bg-img {
            display: block;
            width: 100%;
            height: auto;
            background: #fff;
        }
 
        /* 刮奖区域容器(黄色块位置) */
        .scratchable-area {
            position: absolute;
            left: 10.6%;
            top: 23.6%;
            width: 78.6%;
            height: 53.4%;
            background: transparent;
        }
 
        /* 30 个格子网格 */
        .tile-grid {
            display: grid;
            grid-template-columns: repeat(5, 1fr);
            grid-template-rows: repeat(6, 1fr);
            gap: 0;
            width: 100%;
            height: 100%;
            padding: 0;
        }
 
        /* 单个格子 */
        .tile {
            position: relative;
            aspect-ratio: 1;
            background: #000;
            cursor: pointer;
            border: 1px solid rgba(255,255,255,0.2);
            overflow: hidden;
            transition: all 0.2s ease;
        }
 
        .tile:hover:not(.revealed) {
            background: #1a1a1a;
            box-shadow: inset 0 0 8px rgba(255,255,255,0.3);
        }
 
        .tile-cover {
            position: absolute;
            inset: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #000;
            font-size: 24px;
            font-weight: bold;
            color: #666;
            opacity: 1;
            transition: opacity 0.3s ease;
            pointer-events: none;
        }
 
        .tile-cover img {
            max-width: 100%;
            max-height: 100%;
            object-fit: contain;
        }
 
        .tile.revealed .tile-cover {
            opacity: 0;
            pointer-events: none;
        }
 
        .tile-reveal {
            position: absolute;
            inset: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #ffd700;
            opacity: 0;
            transition: opacity 0.3s ease;
            pointer-events: none;
        }
 
        .tile-reveal img {
            max-width: 100%;
            max-height: 100%;
            object-fit: contain;
        }
 
        .tile.revealed .tile-reveal {
            opacity: 1;
        }
 
        .tile-text {
            position: absolute;
            inset: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 12px;
            color: #333;
            font-weight: bold;
            text-align: center;
            padding: 4px;
            word-break: break-word;
        }
 
        /* 气泡文本 */
        .bubble {
            position: fixed;
            background: white;
            border: 2px solid #333;
            border-radius: 8px;
            padding: 12px 16px;
            font-size: 13px;
            color: #333;
            max-width: 200px;
            word-wrap: break-word;
            z-index: 1000;
            box-shadow: 0 4px 12px rgba(0,0,0,0.2);
            animation: bubbleAppear 0.3s ease;
            pointer-events: none;
        }
 
        .bubble::before {
            content: '';
            position: absolute;
            bottom: -8px;
            left: 20px;
            width: 0;
            height: 0;
            border-left: 8px solid transparent;
            border-right: 8px solid transparent;
            border-top: 8px solid #333;
        }
 
        .bubble::after {
            content: '';
            position: absolute;
            bottom: -4px;
            left: 22px;
            width: 0;
            height: 0;
            border-left: 6px solid transparent;
            border-right: 6px solid transparent;
            border-top: 6px solid white;
        }
 
        .bubble.top {
            bottom: auto;
        }
 
        .bubble.top::before {
            bottom: auto;
            top: -8px;
            border-top: none;
            border-bottom: 8px solid #333;
        }
 
        .bubble.top::after {
            bottom: auto;
            top: -4px;
            border-top: none;
            border-bottom: 6px solid white;
        }
 
        @keyframes bubbleAppear {
            from {
                opacity: 0;
                transform: scale(0.8);
            }
            to {
                opacity: 1;
                transform: scale(1);
            }
        }
 
        /* 吉祥物按钮 + 扫描按钮容器 */
        .controls-row {
            display: flex;
            gap: 10px;
            margin-top: 20px;
            width: 100%;
        }
 
        .mascot-button,
        .scan-button {
            flex: 1;
            padding: 16px 20px;
            font-size: 14px;
            font-weight: bold;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 12px;
            transition: all 0.3s ease;
        }
 
        .mascot-button {
            background: #fff3e0;
            color: #333;
            border: 2px solid #ffb74d;
        }
 
        .mascot-button:hover {
            background: #ffe0b2;
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(255, 183, 77, 0.3);
        }
 
        .mascot-icon {
            font-size: 24px;
            min-width: 28px;
        }
 
        .mascot-text {
            text-align: left;
            flex: 1;
        }
 
        .scan-button {
            background: #e0e0e0;
            color: #666;
            border: 2px solid #999;
        }
 
        .scan-button:hover {
            background: #d0d0d0;
            transform: translateY(-2px);
        }
 
        .scan-button.active {
            background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%);
            color: white;
            border-color: #ff6b35;
            box-shadow: 0 4px 16px rgba(255, 107, 53, 0.4);
            animation: pulse 1.5s infinite;
        }
 
        .scan-button.active:hover {
            box-shadow: 0 6px 20px rgba(255, 107, 53, 0.6);
        }
 
        @keyframes pulse {
            0%, 100% {
                transform: scale(1);
            }
            50% {
                transform: scale(1.02);
            }
        }
 
        /* 彩票申领按钮 */
        .draw-button {
            display: block;
            margin: 20px auto;
            padding: 12px 32px;
            font-size: 16px;
            font-weight: bold;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.3s ease;
        }
 
        .draw-button:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
        }
 
        .draw-button:disabled {
            opacity: 0.6;
            cursor: not-allowed;
        }
 
        /* 无彩票提示 */
        .no-tickets-message {
            text-align: center;
            padding: 40px 20px;
            color: #999;
            font-size: 14px;
        }
 
        /* 倒数计时器 */
        .countdown {
            display: inline-block;
            font-size: 14px;
            color: #ff6b35;
            font-weight: bold;
            margin-left: 10px;
        }
 
        /* 反色滤镜 (第11张后) */
        .invert-filter {
            filter: invert(1) hue-rotate(180deg);
        }


.game-header {
        /* 调试模式 */
    text-align: center;
        .debug-panel {
    color: #555;
            position: fixed;
    margin-bottom: 10px;
            bottom: 20px;
    font-weight: bold;
            right: 20px;
}
            background: rgba(0,0,0,0.85);
            color: #0f0;
            padding: 12px 16px;
            border-radius: 4px;
            font-family: 'Courier New', monospace;
            font-size: 12px;
            z-index: 999;
            max-height: 200px;
            overflow-y: auto;
            display: none;
        }


/* === 彩票视觉部分 === */
        .debug-panel.active {
.ticket-wrapper {
            display: block;
    position: relative;
        }
    width: 100%;
    /* 核心:保持 1075:1911 的宽高比 */
    padding-bottom: 177.76%;
    background-size: 100% 100%;
    background-repeat: no-repeat;
    box-shadow: 0 10px 20px rgba(0,0,0,0.3);
    border-radius: 8px;
}


/* === 刮奖区定位 (关键) === */
        .debug-line {
.scratch-grid-overlay {
            margin: 4px 0;
    position: absolute;
        }
    /* 以下百分比是根据 1075x1911 图和 846x1020 区域计算得出 */
    width: 78.7%;  /* 846 / 1075 */
    height: 53.4%;  /* 1020 / 1911 */
    left: 10.65%;  /* (100% - 78.7%) / 2 */
    top: 34%;      /* 估算黄色区域距离顶部的距离,可根据实际效果微调 */
   
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    grid-template-rows: repeat(6, 1fr);
    /* 消除格子间隙,因为你的图是连续的 */
    gap: 0;  
}


/* === 格子样式 === */
        /* 响应式设计 */
.grid-cell {
        @media (max-width: 600px) {
    width: 100%;
            .scratch-lottery-container {
    height: 100%;
                padding: 12px;
    background-size: 100% 100%; /* 强制图片拉伸填满格子 */
            }
    background-repeat: no-repeat;
   
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
   
    /* 初始文字透明 (刮开前看不到字) */
    color: transparent;
    font-weight: bold;
    font-size: 14px;
    /* 文字描边,防止在复杂背景看不清 */
    text-shadow: 1px 1px 0 #fff, -1px -1px 0 #fff;  
}


/* 刮开后的状态 */
            .lottery-ticket-wrapper {
.grid-cell.revealed {
                max-width: 100%;
    cursor: default;
            }
    color: #d32f2f; /* 刮开后文字颜色 */
}


/* === 底部控制区 (重写布局) === */
            .controls-row {
.controls-area {
                flex-direction: column;
    margin-top: 15px;
            }
    display: flex;
    justify-content: space-between; /* 左右分布 */
    align-items: flex-end;
}


/* 左侧:气泡+颜文字 */
            .scratch-lottery-status {
.mascot-box {
                flex-direction: column;
    flex: 1;
                gap: 10px;
    text-align: left;
            }
    margin-right: 10px;
        }
}
.speech-bubble {
    background: #fff;
    border: 1px solid #333;
    border-radius: 8px 8px 8px 0;
    padding: 8px;
    font-size: 12px;
    margin-bottom: 5px;
    box-shadow: 2px 2px 0 rgba(0,0,0,0.1);
    min-height: 20px;
    line-height: 1.3;
}
.mascot-face {
    font-size: 18px;
    font-weight: bold;
    color: #333;
    margin-left: 5px;
}


/* 右侧:按钮 */
        /* 整页反色效果 (第11张) */
.action-btn {
        .page-invert-crash {
    padding: 0 20px;
            position: fixed;
    height: 40px;
            top: 0;
    font-size: 14px;
            left: 0;
    background: #2196F3;
            width: 100%;
    color: white;
            height: 100%;
    border: none;
            background: #000;
    border-radius: 5px;
            color: #ff0000;
    cursor: pointer;
            display: flex;
    box-shadow: 2px 2px 0 #000;
            align-items: center;
    white-space: nowrap;
            justify-content: center;
}
            font-size: 32px;
.action-btn:active {
            font-weight: bold;
    transform: translate(2px, 2px);
            font-family: 'Courier New', monospace;
    box-shadow: none;
            z-index: 2000;
}
            animation: crashFlash 0.1s infinite;
.action-btn:disabled {
            pointer-events: none;
    background: #ccc;
        }
    box-shadow: none;
}
.action-btn.next { background: #4CAF50; }
.action-btn.danger { background: #000; color: red; border: 1px solid red; }


/* 结局特效 */
        @keyframes crashFlash {
.invert-filter { filter: invert(100%); background: black !important; }
            0%, 100% { opacity: 1; }
.crash-screen {
            50% { opacity: 0.8; }
    position: fixed; top: 0; left: 0; width: 100%; height: 100%;
        }
    background: black; color: red; z-index: 99999;
    display: flex; align-items: center; justify-content: center;
    font-size: 30px;
}
</style>


<!-- HTML 结构 -->
        /* 吉祥物消失效果 */
<!-- WIDGET_PARAMS: bg={{{bg}}}, cover={{{cover}}}, revealed={{{revealed}}} -->
        .mascot-hidden .mascot-button {
<div class="ggl-game-root" id="ggl-root">
            opacity: 0;
     <div class="game-header">
            pointer-events: none;
        GGL-LOTTERY | 剩余: <span id="count-val">3</span>
        }
    </div>
     </style>
</head>
<body>


    <!-- 1. 彩票底图层 -->
<div class="scratch-lottery-container" id="scratchLotteryRoot">
     <!-- 用 data-* 属性存放参数,避免style属性中的模板解析失败 -->
     <div class="scratch-lottery-header">
    <div class="ticket-wrapper" id="ticket-bg" data-bg="{{{bg|图片1.png}}}" data-cover="{{{cover|刮开前.png}}}" data-revealed="{{{revealed|刮开后.jpg}}}">
        <h2>🎰 刮刮乐抽奖</h2>
       
        <div class="scratch-lottery-status">
        <!-- 2. 刮奖区覆盖层 (绝对定位) -->
            <div class="status-item">
        <div class="scratch-grid-overlay" id="scratch-grid">
                <span class="status-label">剩余彩票</span>
             <!-- JS 生成格子 -->
                <span class="status-value" id="ticketCount">3</span>
            </div>
            <div class="status-item">
                <span class="status-label">当前票号</span>
                <span class="status-value" id="currentTicketId">-</span>
            </div>
            <div class="status-item">
                <span class="status-label">已刮格子</span>
                <span class="status-value" id="revealedCount">0/30</span>
             </div>
         </div>
         </div>
    </div>


        <!-- ID 显示 (可选,定位在右上角或哪里,这里为了简洁先隐藏或微调) -->
    <!-- 主彩票区域 -->
        <div style="position: absolute; top: 2%; right: 5%; color: white; font-weight: bold; font-size: 12px;">
    <div id="ticketContainer" style="text-align: center;">
            NO.<span id="ticket-id">001</span>
        <button class="draw-button" id="drawButton">来一张彩票</button>
        <div class="no-tickets-message" id="noTicketsMsg" style="display: none;">
            没有更多彩票了!所有剧情已解锁。
         </div>
         </div>
     </div>
     </div>


     <!-- 3. 底部控制 -->
     <!-- 控制按钮 -->
     <div class="controls-area">
     <div class="controls-row" id="controlsRow" style="display: none;">
         <div class="mascot-box">
         <div class="mascot-button" id="mascotButton">
             <div class="speech-bubble" id="mascot-text">请刮开所有区域...</div>
             <span class="mascot-icon" id="mascotIcon">🎲</span>
             <div class="mascot-face" id="mascot-face">(・ω・)</div>
             <div class="mascot-text" id="mascotText">点击格子开始</div>
         </div>
         </div>
         <button id="main-btn" class="action-btn">扫描结果</button>
         <button class="scan-button" id="scanButton" style="display: none;">
            扫描结果
        </button>
     </div>
     </div>
</div>


<!-- 伪词条 -->
    <!-- 调试面板 -->
<div id="fake-wiki-entry" style="display:none; padding:20px; background:#fff; border:1px solid #ccc; max-width:800px; margin:20px auto;">
    <div class="debug-panel" id="debugPanel"></div>
    <h1 style="border-bottom: 1px solid #aaa;">六世恶言</h1>
    <div style="margin-top:10px;">
        <p><b>描述:</b>“六世恶言”是一种具有高度认知危害的网络异常...</p>
        <hr>
        <p><b>收藏品3:</b> [回复] <b>“它是我藏在赛博刮刮乐内的,是被惩罚的人。”</b></p>
    </div>
</div>
</div>


<script>
<script>
(function() {
// ==================== 配置与数据 ====================
     var root = document.getElementById('ggl-root');
const CONFIG = {
     if (!root) return;
    bg: 'lottery-bg.png',
    area: { x: 114, y: 450, width: 846, height: 1020 },
    cols: 5,
    rows: 6,
    tileSize: 169.2,
    coverSize: 559,
    scale: 0.3027
};
 
const STATES = {
    1: { type: 'normal', text: '', emoji: '😐' },
    2: { type: 'emoji', text: '(◕‿◕) 彩票扫描中…', emoji: '😊' },
    3: { type: 'emoji', text: '(≧◡≦) 太棒了!', emoji: '🎉' },
    4: { type: 'emoji', text: '(•̀ᴗ•́) 思考中…', emoji: '🤔' },
    5: { type: 'emoji', text: '(⊙_⊙) 等等,好像不对', emoji: '😲' },
    6: { type: 'meta', text: '你看见了什么?', emoji: '👻' },
    7: { type: 'glitch', text: '…▒▒▒▒▒', emoji: '❌' },
    8: { type: 'text', text: '哦嗯嘿哈… 别挠我痒痒', emoji: '😅' },
    9: { type: 'text', text: '你是谁?', emoji: '❓' },
    10: { type: 'text', text: '停下来。', emoji: '🛑' },
    11: { type: 'text', text: '平安喜乐。你知道的太多了。', emoji: '⚫' }
};
 
// 11张预设票序数据 (每张 30 格的状态)
const TICKETS = [
    {
        id: 1,
        outcomes: [2,1,1,3,1, 1,2,1,1,1, 1,1,2,1,1, 1,1,1,2,1, 1,1,1,1,1, 1,1,1,1,1],
        notes: '第1张:介绍规则,吉祥物出场',
        extraTickets: 0,
        storyTrigger: 'intro'
    },
    {
        id: 2,
        outcomes: [3,1,1,1,3, 1,1,3,1,1, 3,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1],
        notes: '第2张:连中四次,额外发放 2 张',
        extraTickets: 2,
        storyTrigger: 'bonus'
    },
    {
        id: 3,
        outcomes: [1,2,1,1,1, 1,1,2,1,1, 1,1,1,1,2, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1],
        notes: '第3张:中两次 data',
        extraTickets: 0,
        storyTrigger: 'combo'
    },
    {
        id: 4,
        outcomes: [1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,3],
        notes: '第4张:本来没中,颜文字改了一个字后中了',
        extraTickets: 0,
        storyTrigger: 'plot_twist'
    },
    {
        id: 5,
        outcomes: [2,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1],
        notes: '第5张:中 data',
        extraTickets: 0,
        storyTrigger: 'normal'
    },
    {
        id: 6,
        outcomes: [1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1],
        notes: '第6张:什么都没有(空白)',
        extraTickets: 0,
        storyTrigger: 'empty'
    },
    {
        id: 7,
        outcomes: [1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,3,1, 1,1,1,1,1],
        notes: '第7张:中了一张',
        extraTickets: 0,
        storyTrigger: 'single'
    },
    {
        id: 8,
        outcomes: [2,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,1,1, 1,1,1,3,1, 1,1,1,3,1],
        notes: '第8张:中 data,并中两张',
        extraTickets: 0,
        storyTrigger: 'double'
    },
    {
        id: 9,
        outcomes: [2,2,2,2,2, 2,2,2,2,2, 2,2,2,2,2, 2,2,2,2,2, 2,2,2,2,2, 2,2,2,2,2],
        notes: '第9张:全页 data',
        extraTickets: 0,
        storyTrigger: 'full_page'
    },
    {
        id: 10,
        outcomes: [10,10,10,10,10, 10,10,10,10,10, 10,10,10,10,10, 10,10,10,10,10, 10,10,10,10,10, 10,10,10,10,10],
        notes: '第10张:全页停下来,扫描按钮高亮',
        extraTickets: 0,
        storyTrigger: 'scan_ready'
    },
    {
        id: 11,
        outcomes: [11,11,11,11,11, 11,11,11,11,11, 11,11,11,11,11, 11,11,11,11,11, 11,11,11,11,11, 11,11,11,11,11],
        notes: '第11张:反色滤镜,伪闪退跳转',
        extraTickets: 0,
        storyTrigger: 'final'
    }
];
 
// ==================== 游戏状态机 ====================
let gameState = {
    playerTickets: 3,
    currentTicketIndex: -1,
    currentTicket: null,
    revealedTiles: new Set(),
    isTicketOpen: false,
    isFinal: false,
    debugMode: false
};
 
// ==================== DOM 元素缓存 ====================
const elements = {
    root: null,
    ticketContainer: null,
    drawButton: null,
    noTicketsMsg: null,
    controlsRow: null,
    mascotButton: null,
    mascotIcon: null,
    mascotText: null,
    scanButton: null,
    ticketCount: null,
    currentTicketId: null,
    revealedCount: null,
    debugPanel: null
};
 
// ==================== 初始化 ====================
function init() {
     cacheElements();
    attachEventListeners();
    updateUI();
    logDebug('Game initialized');
}
 
function cacheElements() {
    elements.root = document.getElementById('scratchLotteryRoot');
    elements.ticketContainer = document.getElementById('ticketContainer');
    elements.drawButton = document.getElementById('drawButton');
    elements.noTicketsMsg = document.getElementById('noTicketsMsg');
    elements.controlsRow = document.getElementById('controlsRow');
    elements.mascotButton = document.getElementById('mascotButton');
    elements.mascotIcon = document.getElementById('mascotIcon');
    elements.mascotText = document.getElementById('mascotText');
    elements.scanButton = document.getElementById('scanButton');
    elements.ticketCount = document.getElementById('ticketCount');
    elements.currentTicketId = document.getElementById('currentTicketId');
    elements.revealedCount = document.getElementById('revealedCount');
     elements.debugPanel = document.getElementById('debugPanel');
}


    // ===== 参数配置 =====
function attachEventListeners() {
    // 直接在这里修改图片URL
     elements.drawButton.addEventListener('click', drawTicket);
     var imgBg = 'https://wm.gaoice.run/images/thumb/b/b6/%E5%9B%BE%E7%89%871.png/180px-%E5%9B%BE%E7%89%871.png';
     elements.mascotButton.addEventListener('click', mascotButtonClick);
     var imgCover = 'https://wm.gaoice.run/images/thumb/5/5a/%E5%88%AE%E5%BC%80%E5%89%8D.png/180px-%E5%88%AE%E5%BC%80%E5%89%8D.png';
     elements.scanButton.addEventListener('click', scanButtonClick);
     var imgRevealed = 'https://wm.gaoice.run/images/thumb/4/4a/%E5%88%AE%E5%BC%80%E5%90%8E.jpg/180px-%E5%88%AE%E5%BC%80%E5%90%8E.jpg';
      
      
     // 调试:打印最终获取的参数
     // 键盘快捷键
     console.log('Widget参数:', { imgBg, imgCover, imgRevealed });
     document.addEventListener('keydown', handleKeyboard);
   
}
    // 应用背景图
 
     var elTicketBg = document.getElementById('ticket-bg');
// ==================== 核心游戏函数 ====================
     if (elTicketBg && imgBg) {
function drawTicket() {
         elTicketBg.style.backgroundImage = "url('" + imgBg + "')";
     if (gameState.playerTickets <= 0) {
        showMessage('没有更多彩票了!');
        return;
    }
 
    gameState.currentTicketIndex++;
     if (gameState.currentTicketIndex >= TICKETS.length) {
         showMessage('所有剧情已完成!');
        return;
     }
     }
    var TOTAL_CARDS = 11;
    var currentIdx = 0;
    var remain = 3;
    var isScanned = false;
    var revealedCount = 0;


     // DOM
     gameState.currentTicket = TICKETS[gameState.currentTicketIndex];
    var elCount = root.querySelector('#count-val');
     gameState.playerTickets--;
     var elGrid = root.querySelector('#scratch-grid');
     gameState.revealedTiles.clear();
    var elBtn = root.querySelector('#main-btn');
     gameState.isTicketOpen = true;
     var elBubble = root.querySelector('#mascot-text');
    var elFace = root.querySelector('#mascot-face');
     var elId = root.querySelector('#ticket-id');
    var elFakeEntry = document.getElementById('fake-wiki-entry');


     // 数据
     renderTicket();
    var cards = [
    updateUI();
        { type: 'n', face: '', text: '结果:谢谢惠顾。' },
    logDebug(`Ticket ${gameState.currentTicket.id} opened`);
        { type: 'n', face: '(・ω・)', text: '检测到异常字符。' },
        { type: 'b', face: '(^▽^)', text: '恭喜!触发连击,奖励+2张!' },
        { type: 'n', face: 'o(*≧▽≦)ツ', text: '结果:1 Data 2。好耶!' },
        { type: 'c', face: '( ・_・)', text: '结果:中奖...?数据好像变了。' },
        { type: 'x', face: '', text: '对象已消失。', val: 'Data' },
        { type: 'g', face: '...', text: 'ERR_#0x00 数据损坏', val: '???' },
        { type: 'p', face: '', text: '声音检测:"好痛!"', val: 'Pain' },
        { type: 't', face: '', text: '收到文本:"你是谁?"', val: 'Info' },
        { type: 's', face: '', text: '警告:立即停止操作。', val: '停下' },
        { type: 'f', face: 'ERROR', text: '你知道的太多了。', val: '平安喜乐' }
    ];


     function setMascot(txt, face) {
     // 第10张后高亮扫描按钮
         if(txt) elBubble.innerText = txt;
    if (gameState.currentTicketIndex === 9) {
        if(face !== undefined) elFace.innerText = face;
        elements.scanButton.classList.add('active');
    }
}
 
function renderTicket() {
    const ticket = gameState.currentTicket;
   
    // 清空容器
    elements.ticketContainer.innerHTML = '';
   
    // 创建彩票HTML
    const ticketHTML = `
        <div class="lottery-ticket-wrapper">
            <canvas id="lotteryCanvas" style="display: block; width: 100%; border-radius: 4px;"></canvas>
            <div class="scratchable-area">
                <div class="tile-grid" id="tileGrid"></div>
            </div>
         </div>
    `;
   
    elements.ticketContainer.insertAdjacentHTML('beforeend', ticketHTML);
   
    const canvas = document.getElementById('lotteryCanvas');
    const ctx = canvas.getContext('2d');
   
    // 设置 canvas 尺寸(宽高比按照背景图)
    const containerWidth = elements.ticketContainer.offsetWidth - 20;
    const aspectRatio = 1075 / 1911;
    canvas.width = containerWidth;
    canvas.height = containerWidth / aspectRatio;
   
    // 绘制背景色(灰白色作为背景)
    ctx.fillStyle = '#f0f0f0';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
   
    // 绘制黄色刮奖区域
    const areaLeft = (CONFIG.area.x / 1075) * canvas.width;
    const areaTop = (CONFIG.area.y / 1911) * canvas.height;
    const areaWidth = (CONFIG.area.width / 1075) * canvas.width;
    const areaHeight = (CONFIG.area.height / 1911) * canvas.height;
   
    ctx.fillStyle = '#ffd700';
    ctx.fillRect(areaLeft, areaTop, areaWidth, areaHeight);
   
    // 绘制黑色分割线
    ctx.strokeStyle = '#ccc';
    ctx.lineWidth = 2;
    ctx.strokeRect(areaLeft, areaTop, areaWidth, areaHeight);
   
    // 创建30个格子
    const tileGrid = document.getElementById('tileGrid');
    const tileSize = areaWidth / CONFIG.cols;
   
    for (let i = 0; i < 30; i++) {
        const state = ticket.outcomes[i];
        const tile = createTile(i, state, tileSize);
        tileGrid.appendChild(tile);
     }
     }
   
    elements.controlsRow.style.display = 'flex';
    updateMascotMessage();
}


    function randVal() {
function createTile(index, stateNum, tileSize) {
        var arr = ['10mb', '50Gb', '5Kb', '1Tb', '', '寿', '', '', ''];
    const tile = document.createElement('div');
         return arr[Math.floor(Math.random() * arr.length)];
    tile.className = 'tile';
    tile.dataset.index = index;
    tile.setAttribute('aria-label', `格子 ${index + 1}`);
    tile.tabIndex = 0;
   
    const stateInfo = STATES[stateNum] || STATES[1];
   
    // 覆盖层 (黑色)
    const cover = document.createElement('div');
    cover.className = 'tile-cover';
    cover.textContent = '🔲';
    tile.appendChild(cover);
   
    // 揭示层 (黄色)
    const reveal = document.createElement('div');
    reveal.className = 'tile-reveal';
   
    if (stateInfo.type === 'emoji') {
        reveal.textContent = stateInfo.emoji;
    } else if (stateInfo.type === 'normal') {
        reveal.textContent = '';
    } else if (stateInfo.type === 'text') {
        const textDiv = document.createElement('div');
        textDiv.style.fontSize = '11px';
        textDiv.style.wordBreak = 'break-word';
         textDiv.textContent = stateInfo.text.substring(0, 8);
        reveal.appendChild(textDiv);
    } else if (stateInfo.type === 'glitch') {
        reveal.textContent = '▒▒';
        reveal.style.color = '#333';
    } else if (stateInfo.type === 'meta') {
        reveal.textContent = '?';
        reveal.style.fontSize = '32px';
     }
     }
   
    tile.appendChild(reveal);
   
    // 点击事件
    tile.addEventListener('click', () => revealTile(index, stateNum));
    tile.addEventListener('keydown', (e) => {
        if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault();
            revealTile(index, stateNum);
        }
    });
   
    return tile;
}


    function initCard(idx) {
function revealTile(index, stateNum) {
        isScanned = false;
    if (gameState.revealedTiles.has(index)) return;
        revealedCount = 0;
   
        elBtn.innerText = "扫描结果";
    gameState.revealedTiles.add(index);
         elBtn.className = "action-btn";
    const tiles = document.querySelectorAll('.tile');
        elBtn.disabled = false;
    tiles[index].classList.add('revealed');
        elBtn.style = "";
   
    // 显示气泡
    const stateInfo = STATES[stateNum] || STATES[1];
    if (stateInfo.text) {
         showBubble(index, stateInfo.text);
    }
   
    // 触发状态效果
    handleStateEffect(stateNum);
   
    // 更新UI
    updateUI();
   
    // 检查是否完成
    checkTicketCompletion();
   
    logDebug(`Tile ${index} revealed: state ${stateNum}`);
}


        elId.innerText = (idx+1).toString().padStart(3,'0');
function showBubble(tileIndex, text) {
         elCount.innerText = remain;
    // 获取格子位置
    const tiles = document.querySelectorAll('.tile');
    const tile = tiles[tileIndex];
    const rect = tile.getBoundingClientRect();
   
    const bubble = document.createElement('div');
    bubble.className = 'bubble';
    bubble.textContent = text;
    document.body.appendChild(bubble);
   
    // 自动定位(避免超出视口)
    bubble.style.left = (rect.left + rect.width / 2 - 100) + 'px';
    bubble.style.top = (rect.top - 60) + 'px';
   
    // 检查是否超出视口
    const bubbleRect = bubble.getBoundingClientRect();
    if (bubbleRect.top < 0) {
        bubble.classList.add('top');
         bubble.style.top = (rect.bottom + 20) + 'px';
    }
   
    // 3秒后移除
    setTimeout(() => bubble.remove(), 3000);
}


         var d = cards[idx];
function handleStateEffect(stateNum) {
         elGrid.innerHTML = '';
    switch (stateNum) {
         setMascot("请刮开所有区域...", d.face);
         case 2: // 播报
            updateMascotMessage();
            break;
        case 3: // 欢呼
            elements.mascotIcon.textContent = '🎉';
            break;
         case 5: // 惊讶
            elements.mascotIcon.textContent = '😲';
            break;
         case 6: // 消失
            elements.mascotButton.style.opacity = '0.3';
            break;
        case 11: // 最终
            triggerFinalSequence();
            break;
    }
}


        if(d.type === 's') { // Stop
function checkTicketCompletion() {
             elBtn.style.border = "2px solid red";
    if (gameState.revealedTiles.size === 30) {
             elBtn.style.color = "red";
        const ticket = gameState.currentTicket;
       
        logDebug(`Ticket ${ticket.id} completed - Story: ${ticket.storyTrigger}`);
       
        // 额外发放彩票
        if (ticket.extraTickets > 0) {
             gameState.playerTickets += ticket.extraTickets;
             showMessage(`✨ 额外获得 ${ticket.extraTickets} 张彩票!`);
         }
         }
       
        // 延迟后显示下一张按钮
        setTimeout(() => {
            updateUI();
        }, 1000);
    }
}


        for(var i=0; i<30; i++) {
function triggerFinalSequence() {
            var cell = document.createElement('div');
    if (gameState.isFinal) return;
            cell.className = 'grid-cell';
    gameState.isFinal = true;
           
   
            // 1. 设置刮开前的图 (覆盖层)
    // 创建反色闪退效果
            cell.style.backgroundImage = "url('" + imgCover + "')";
    const crash = document.createElement('div');
    crash.className = 'page-invert-crash';
    crash.textContent = '扫描异常…';
    document.body.appendChild(crash);
   
    // 1秒后跳转到词条页面
    setTimeout(() => {
        // 这里可以替换为实际的跳转逻辑
        showMessage('剧情结束!所有秘密已解锁。');
        crash.remove();
    }, 2000);
}


            // 决定内容
function scanButtonClick() {
            var val = d.val ? d.val : randVal();
    if (gameState.currentTicketIndex !== 9) {
            if(idx === 2 && (i===10 || i===11)) val = "大奖";
        showMessage('尚未解锁此功能');
            if(idx === 8) val = "你是谁";
        return;
    }
   
    logDebug('Scan button clicked - Releasing ticket 11');
    gameState.playerTickets = 1; // 发放最后一张票
    updateUI();
}


            cell.dataset.val = val;
function mascotButtonClick() {
    showMessage('吉祥物说:' + (elements.mascotText.textContent || '点击格子看看吧!'));
}


             // 点击事件
function updateMascotMessage() {
             (function(c, cIdx, cVal, idxI){
    if (gameState.currentTicket) {
                c.addEventListener('click', function() {
        const storyText = {
                    if(c.classList.contains('revealed') || isScanned) return;
             intro: '欢迎来到刮刮乐!点击格子开始吧!',
                   
             bonus: '哇,连中了!你很幸运呢!',
                    // 2. 切换为刮开后的图
            combo: '组合效果!再来一张?',
                    c.classList.add('revealed');
            plot_twist: '等等... 这个结果不对劲',
                    c.style.backgroundImage = "url('" + imgRevealed + "')";
            normal: '继续加油!',
                    c.innerText = cVal; // 显示文字
            empty: '哎呀,什么都没有...',
                   
            single: '再接再厉!',
                    revealedCount++;
            double: '双倍幸运!',
            full_page: '全中了!!!',
            scan_ready: '...',
            final: '你看到太多了...'
        };
       
        elements.mascotText.textContent = storyText[gameState.currentTicket.storyTrigger] || '...';
        elements.mascotIcon.textContent = STATES[2].emoji;
    }
}


                    // 剧情触发
function updateUI() {
                    if(cIdx === 5) { // 消失
    elements.ticketCount.textContent = gameState.playerTickets;
                        setMascot(["别点了","我在看你"][Math.floor(Math.random()*2)], "");
    elements.currentTicketId.textContent = gameState.currentTicket
                        c.innerText = "";  
        ? gameState.currentTicket.id
                    }
        : '-';
                    if(cIdx === 7) { // 痛
    elements.revealedCount.textContent =
                        c.innerText = ["痛!","别!"][Math.floor(Math.random()*2)];
        `${gameState.revealedTiles.size}/30`;
                        c.style.color = "red";
   
                        setMascot("别挠我!", ">_<");
    // 控制按钮显示
                    }
    if (gameState.isTicketOpen) {
                    if(cIdx === 4 && revealedCount === 15) { // 改字
        elements.drawButton.style.display = 'none';
                        setMascot("诶...?", "(;゚д゚)");
        elements.noTicketsMsg.style.display = 'none';
                        if(elGrid.children[29]) {
    } else {
                            elGrid.children[29].dataset.val = "中奖";
        elements.drawButton.style.display = 'block';
                        }
        elements.drawButton.textContent = gameState.playerTickets > 0
                    }
            ? '来一张彩票'
                });
            : '没有更多彩票了';
            })(cell, idx, val, i);
        elements.drawButton.disabled = gameState.playerTickets <= 0;
        elements.noTicketsMsg.style.display = gameState.playerTickets <= 0 ? 'block' : 'none';
    }
}


            elGrid.appendChild(cell);
function handleKeyboard(e) {
         }
    if (e.ctrlKey && e.key === 'd') {
        gameState.debugMode = !gameState.debugMode;
        elements.debugPanel.classList.toggle('active');
         logDebug('Debug mode toggled: ' + gameState.debugMode);
     }
     }
}


    elBtn.addEventListener('click', function() {
function showMessage(msg) {
        if(isScanned) {
    alert(msg);
            // Next
}
            currentIdx++;
            if(currentIdx < TOTAL_CARDS) initCard(currentIdx);
        } else {
            // Scan
            if(revealedCount < 30 && currentIdx < 9) {
                if(!confirm("还没刮完,确定扫描吗?")) return;
            }
           
            var d = cards[currentIdx];
            isScanned = true;
            elBtn.innerText = "扫描中...";
           
            setTimeout(function(){
                setMascot(d.text);
               
                if(d.type === 'b') { remain += 2; elCount.innerText = remain; }
                if(d.type === 'f') { doEnd(); return; }
                if(d.type === 's') {
                    setTimeout(function(){
                        setMascot("严重错误。排出异物...");
                        elBtn.innerText = "取出未知票据";
                        elBtn.className = "action-btn danger";
                    }, 800);
                    return;
                }


                remain--;
function logDebug(msg) {
                elCount.innerText = remain;
    if (gameState.debugMode) {
                if(remain < 0) {
        const line = document.createElement('div');
                    setMascot("票据耗尽。");
        line.className = 'debug-line';
                    elBtn.disabled = true;
        line.textContent = '[' + new Date().toLocaleTimeString() + '] ' + msg;
                    return;
        elements.debugPanel.appendChild(line);
                }
        elements.debugPanel.scrollTop = elements.debugPanel.scrollHeight;
                elBtn.innerText = "再来一张";
    }
                elBtn.classList.add('next');
     console.log('[ScratchLottery]', msg);
            }, 500);
}
        }
     });


    function doEnd() {
// ==================== 入口 ====================
        document.body.classList.add('invert-filter');
window.addEventListener('DOMContentLoaded', init);
        setTimeout(function(){
            var div = document.createElement('div');
            div.className = 'crash-screen';
            div.innerText = "FATAL ERROR";
            document.body.appendChild(div);
            setTimeout(function(){
                div.remove();
                document.body.classList.remove('invert-filter');
                root.style.display = 'none';
                if(elFakeEntry) elFakeEntry.style.display = 'block';
                window.scrollTo(0,0);
                alert("【系统提示】收到新短信。");
            }, 3000);
        }, 1500);
    }


     initCard(0);
// 如果在现有 DOM 中,立即初始化
})();
if (document.readyState !== 'loading') {
     init();
}
</script>
</script>
</includeonly>
 
</body>
</html>

2026年2月1日 (日) 15:56的版本

<!DOCTYPE html>

🎰 刮刮乐抽奖

剩余彩票 3
当前票号 -
已刮格子 0/30