情绪泡泡龙——ABC消消乐大作战(优化版)
by LaserPirate421002 lines37.4 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>情绪泡泡龙——ABC消消乐大作战(优化版)</title>
<style>
:root {
--bg: #fdf6f0;
--card-bg: #fffaf5;
--text: #5a4a3a;
--text-light: #7a6a5a;
--golden: #f2c94c;
--gray-bubble: #c4b5d4;
--gray-emotion: #d4b5b5;
--positive-b: #f5d78c;
--positive-c: #f9e4a0;
--shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
--shadow-lg: 0 8px 30px rgba(0, 0, 0, 0.12);
--radius: 18px;
--font: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans SC', sans-serif;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font);
background: linear-gradient(135deg, #fef5ed 0%, #fde9df 30%, #fdf0f5 60%, #fef8f3 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
-webkit-tap-highlight-color: transparent;
user-select: none;
-webkit-user-select: none;
-webkit-touch-callout: none;
}
.game-container {
width: 100%;
max-width: 480px;
background: var(--card-bg);
border-radius: 24px;
box-shadow: var(--shadow-lg);
padding: 14px 14px 16px;
display: flex;
flex-direction: column;
gap: 10px;
position: relative;
overflow: hidden;
}
.game-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 6px;
background: linear-gradient(90deg, #f9c5a7, #f7d98c, #a7d4c5, #c4b5d4, #f9c5a7);
border-radius: 24px 24px 0 0;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 6px;
padding: 6px 8px;
background: #fffefb;
border-radius: 12px;
box-shadow: var(--shadow);
margin-top: 2px;
}
.header-item {
display: flex;
align-items: center;
gap: 4px;
font-size: 0.85rem;
font-weight: 600;
color: var(--text);
white-space: nowrap;
}
.header-item .icon {
font-size: 1.2rem;
}
.header-item .value {
color: #d4843a;
font-weight: 700;
font-size: 0.95rem;
min-width: 24px;
text-align: center;
}
.timer-warning {
color: #e05a5a !important;
animation: pulse-warning 0.6s ease-in-out infinite;
}
@keyframes pulse-warning {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.15);
}
}
.event-display {
background: linear-gradient(135deg, #fffef9, #fef9f2);
border: 2px dashed #e8d5c4;
border-radius: 12px;
padding: 8px 12px;
text-align: center;
}
.event-label {
font-size: 0.7rem;
color: #b8957a;
letter-spacing: 1px;
margin-bottom: 2px;
}
.event-text {
font-size: 1rem;
font-weight: 700;
color: #5a3e2b;
line-height: 1.4;
}
.guide-text {
font-size: 0.75rem;
color: #a08060;
margin-top: 4px;
}
/* 4x4 网格 */
.bubble-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, 1fr);
gap: 8px;
padding: 6px;
background: #fefcf9;
border-radius: var(--radius);
box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.04);
min-height: 280px;
position: relative;
}
@media (max-width: 400px) {
.bubble-grid {
gap: 6px;
padding: 4px;
}
.bubble {
font-size: 0.7rem !important;
min-height: 58px !important;
}
.game-container {
padding: 10px 8px 12px;
}
.event-text {
font-size: 0.9rem;
}
}
.bubble {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
border-radius: 50%;
cursor: pointer;
font-weight: 600;
font-size: 0.8rem;
line-height: 1.25;
padding: 6px 3px;
transition: transform 0.15s ease, box-shadow 0.15s ease;
position: relative;
word-break: break-all;
min-height: 64px;
aspect-ratio: 1 / 1;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.12), inset 0 2px 0 rgba(255, 255, 255, 0.5);
letter-spacing: 0.3px;
color: #3a2a1a;
}
.bubble:active {
transform: scale(0.9);
}
.bubble.selected {
transform: scale(1.08);
box-shadow: 0 0 0 5px rgba(100, 160, 220, 0.6), 0 6px 20px rgba(0, 0, 0, 0.25) !important;
z-index: 5;
animation: gentle-bounce 0.5s ease-in-out infinite;
}
@keyframes gentle-bounce {
0%,
100% {
transform: scale(1.08);
}
50% {
transform: scale(1.14);
}
}
.bubble.hint-glow {
animation: hint-pulse 0.7s ease-in-out infinite !important;
box-shadow: 0 0 0 6px rgba(255, 180, 50, 0.8), 0 6px 22px rgba(255, 150, 30, 0.5) !important;
z-index: 4;
}
@keyframes hint-pulse {
0%,
100% {
box-shadow: 0 0 0 6px rgba(255, 180, 50, 0.8), 0 6px 22px rgba(255, 150, 30, 0.5);
}
50% {
box-shadow: 0 0 0 12px rgba(255, 180, 50, 0.2), 0 8px 28px rgba(255, 150, 30, 0.6);
}
}
/* 灰色泡泡 - 不合理信念 */
.bubble.belief-negative {
background: linear-gradient(145deg, #d5c8e8, #bfaed6);
box-shadow: 0 4px 10px rgba(150, 120, 170, 0.35), inset 0 2px 0 rgba(255, 255, 255, 0.4);
}
/* 灰色泡泡 - 负性情绪 */
.bubble.emotion-negative {
background: linear-gradient(145deg, #e4cdcd, #d4b5b5);
box-shadow: 0 4px 10px rgba(170, 130, 130, 0.35), inset 0 2px 0 rgba(255, 255, 255, 0.4);
}
/* 金色泡泡 - 合理信念 */
.bubble.belief-positive {
background: linear-gradient(145deg, #f8df90, #f0c94c);
box-shadow: 0 4px 12px rgba(200, 150, 40, 0.4), inset 0 2px 0 rgba(255, 255, 255, 0.5);
color: #5a3a0a;
}
/* 金色泡泡 - 正性情绪 */
.bubble.emotion-positive {
background: linear-gradient(145deg, #fceba8, #f7d96a);
box-shadow: 0 4px 12px rgba(200, 150, 30, 0.4), inset 0 2px 0 rgba(255, 255, 255, 0.5);
color: #5a3a0a;
}
/* 动画 */
.bubble.bursting {
animation: burst-out 0.5s ease-out forwards;
}
@keyframes burst-out {
0% {
transform: scale(1);
opacity: 1;
}
30% {
transform: scale(1.3);
opacity: 0.7;
}
100% {
transform: scale(0);
opacity: 0;
}
}
.bubble.collecting {
animation: collect-gold 0.6s ease-out forwards;
}
@keyframes collect-gold {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.4);
opacity: 0.9;
box-shadow: 0 0 30px 20px rgba(255, 200, 50, 0.7);
}
100% {
transform: scale(0);
opacity: 0;
}
}
.bubble.dropping {
animation: drop-in 0.55s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
@keyframes drop-in {
0% {
transform: translateY(-60px) scale(0.3);
opacity: 0;
}
60% {
transform: translateY(6px) scale(1.05);
opacity: 1;
}
100% {
transform: translateY(0) scale(1);
opacity: 1;
}
}
.bubble.shaking {
animation: shake 0.4s ease-in-out;
}
@keyframes shake {
0%,
100% {
transform: translateX(0);
}
25% {
transform: translateX(-6px);
}
50% {
transform: translateX(6px);
}
75% {
transform: translateX(-3px);
}
}
.floating-tip {
position: fixed;
pointer-events: none;
z-index: 100;
font-weight: 700;
font-size: 0.9rem;
color: #5a3a0a;
background: #fef9e8;
border: 2px solid #f0c94c;
border-radius: 20px;
padding: 8px 16px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
animation: float-up 2.5s ease-out forwards;
white-space: nowrap;
text-align: center;
}
@keyframes float-up {
0% {
opacity: 0;
transform: translateY(0) scale(0.6);
}
20% {
opacity: 1;
transform: translateY(-20px) scale(1.05);
}
80% {
opacity: 0.8;
transform: translateY(-50px) scale(1);
}
100% {
opacity: 0;
transform: translateY(-80px) scale(0.8);
}
}
.btn-row {
display: flex;
gap: 10px;
justify-content: center;
flex-wrap: wrap;
}
.btn {
padding: 10px 18px;
border-radius: 25px;
border: none;
font-size: 0.9rem;
font-weight: 700;
cursor: pointer;
letter-spacing: 0.5px;
transition: all 0.2s;
font-family: var(--font);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 6px;
}
.btn:active {
transform: scale(0.94);
}
.btn-hint {
background: #fff3d6;
color: #8b6914;
border: 2px solid #f0c94c;
}
.btn-hint:disabled {
opacity: 0.4;
cursor: not-allowed;
pointer-events: none;
}
.btn-reset {
background: #f5f0eb;
color: #7a6a5a;
border: 2px solid #d5c8b5;
}
/* 结算弹窗 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.55);
z-index: 200;
display: flex;
align-items: center;
justify-content: center;
animation: fade-in 0.3s ease;
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.modal {
background: #fffefb;
border-radius: 24px;
padding: 24px 20px;
max-width: 420px;
width: 90%;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
text-align: center;
max-height: 85vh;
overflow-y: auto;
animation: slide-up 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes slide-up {
from {
transform: translateY(60px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.modal h2 {
font-size: 1.4rem;
color: #5a3e2b;
margin-bottom: 8px;
}
.title-badge {
display: inline-block;
font-size: 1rem;
font-weight: 700;
padding: 8px 20px;
border-radius: 25px;
margin: 8px 0 14px;
letter-spacing: 1px;
}
.badge-diamond {
background: #e8f8ff;
color: #2b6f9a;
border: 2px solid #6bc4e8;
}
.badge-gold {
background: #fff8e8;
color: #8b6914;
border: 2px solid #f0c94c;
}
.badge-bronze {
background: #fdf5f0;
color: #8b5a3a;
border: 2px solid #d4a080;
}
.score-display {
font-size: 2.5rem;
font-weight: 900;
color: #d4843a;
}
.stars-display {
font-size: 1.8rem;
letter-spacing: 4px;
margin: 4px 0 10px;
}
.quotes-list {
text-align: left;
background: #fefcf8;
border-radius: 14px;
padding: 14px;
margin: 10px 0;
border: 1px solid #f0e8d8;
}
.quotes-list p {
padding: 6px 0;
border-bottom: 1px dotted #e8d8c4;
font-size: 0.8rem;
color: #5a4a3a;
line-height: 1.5;
}
.quotes-list p:last-child {
border-bottom: none;
}
.btn-close {
margin-top: 12px;
background: #f0c94c;
color: #5a3a0a;
font-size: 1rem;
padding: 12px 30px;
border-radius: 25px;
border: none;
font-weight: 700;
cursor: pointer;
box-shadow: 0 4px 14px rgba(200, 150, 30, 0.3);
font-family: var(--font);
}
</style>
</head>
<body>
<div class="game-container" id="gameContainer">
<div class="header">
<div class="header-item"><span class="icon">🎯</span>关卡:<span class="value" id="levelDisplay">1/5</span></div>
<div class="header-item"><span class="icon">⭐</span>星星:<span class="value" id="starDisplay">0</span></div>
<div class="header-item"><span class="icon">💯</span>分数:<span class="value" id="scoreDisplay">0</span></div>
<div class="header-item"><span class="icon">⏱️</span>时间:<span class="value" id="timerDisplay">120</span>秒</div>
</div>
<div class="event-display" id="eventDisplay">
<div class="event-label">📋 当前事件 A</div>
<div class="event-text" id="eventText">小组合作作业,我的建议没被采纳。</div>
<div class="guide-text" id="guideText">👆 先找到两个<strong>灰色泡泡</strong>配对,再看会出现什么神奇变化!</div>
</div>
<div class="bubble-grid" id="bubbleGrid"></div>
<div class="btn-row">
<button class="btn btn-hint" id="btnHint">💡 提示(1次)</button>
<button class="btn btn-reset" id="btnReset">🔄 重置本关</button>
</div>
</div>
<div class="modal-overlay" id="modalOverlay" style="display:none;">
<div class="modal" id="modalContent"></div>
</div>
<script>
(function() {
// 游戏数据
const eventsData = [
{ id: 0, eventA: '小组合作作业,我的建议没被采纳。', beliefNegative: '他们肯定看不起我,觉得我蠢。',
emotionNegative: '委屈😭、愤怒😡', beliefPositive: '方案有取舍很正常,不代表我不好。', emotionPositive: '平静😊、有动力💪',
encouragement: '太棒了!你的想法很有价值,不被采纳只是方案选择不同而已✨' },
{ id: 1, eventA: '手机被老师暂时保管了。', beliefNegative: '老师故意针对我,让我丢脸!', emotionNegative: '暴怒😡、想顶撞',
beliefPositive: '规则对大家都一样,老师是在帮我。', emotionPositive: '释然😌、专心听课', encouragement: '没错!规则面前人人平等,专注听课才是对自己负责💪' },
{ id: 2, eventA: '好朋友突然不回我消息。', beliefNegative: '他肯定讨厌我了。', emotionNegative: '焦虑😰、难过😢',
beliefPositive: '他可能在忙,我可以等等再问。', emotionPositive: '理解🤗、耐心', encouragement: '理解万岁!朋友可能只是暂时忙碌,耐心是友谊的试金石🤗' },
{ id: 3, eventA: '考试没考好。', beliefNegative: '我太笨了,永远学不好。', emotionNegative: '沮丧😞、想放弃',
beliefPositive: '这次没考好说明有提升空间。', emotionPositive: '反思🤔、制定计划✍️', encouragement: '成长型思维!考试是检验学习的机会,不是定义能力的标签✍️' },
{ id: 4, eventA: '被同学当众开了玩笑。', beliefNegative: '他在羞辱我,所有人都笑我。', emotionNegative: '羞耻😳、愤怒😡',
beliefPositive: '他只是开玩笑,没有恶意。', emotionPositive: '一笑了之😄、大度', encouragement: '大度是一种力量!一笑了之,你的心胸比玩笑更大😄' }
];
const globalQuotes = [
'💡 想法一变,心情就亮了!',
'🌈 事件无法改变,但信念可以选择。',
'🧠 做情绪的主人,从转念开始。',
'🫧 灰色泡泡不可怕,转念就能变金色!',
'🎨 你的想法,决定你的心情颜色。',
];
let currentLevel = 0;
let score = 0;
let stars = 0;
let timeLeft = 120;
let timerInterval = null;
let hintUsed = false;
let selectedBubble = null;
let isProcessing = false;
let collectedGoldenPairs = 0;
let goldenPairsNeeded = 1;
let allBubbles = [];
let allEncouragements = [];
// DOM
const bubbleGrid = document.getElementById('bubbleGrid');
const eventText = document.getElementById('eventText');
const guideText = document.getElementById('guideText');
const levelDisplay = document.getElementById('levelDisplay');
const starDisplay = document.getElementById('starDisplay');
const scoreDisplay = document.getElementById('scoreDisplay');
const timerDisplay = document.getElementById('timerDisplay');
const btnHint = document.getElementById('btnHint');
const btnReset = document.getElementById('btnReset');
const modalOverlay = document.getElementById('modalOverlay');
const modalContent = document.getElementById('modalContent');
function generateId() {
return 'b_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6);
}
function createBubble(eventId, type, chain, text) {
return { id: generateId(), eventId, type, chain, text, htmlEl: null, gridIndex: -1 };
}
function buildBubblePool(level) {
const current = eventsData[level];
const pool = [];
// 当前事件的4个泡泡 (2灰+2金)
pool.push(createBubble(level, 'belief', 'negative', current.beliefNegative));
pool.push(createBubble(level, 'emotion', 'negative', current.emotionNegative));
pool.push(createBubble(level, 'belief', 'positive', current.beliefPositive));
pool.push(createBubble(level, 'emotion', 'positive', current.emotionPositive));
// 干扰项:从其他事件中取部分泡泡,但总泡泡数控制在12-14个,网格为4x4=16,剩余用重复当前事件的金色泡泡填充(降低难度)
const otherEvents = eventsData.filter((_, i) => i !== level);
const interference = [];
otherEvents.forEach(ev => {
interference.push(createBubble(ev.id, 'belief', 'negative', ev.beliefNegative));
interference.push(createBubble(ev.id, 'emotion', 'negative', ev.emotionNegative));
});
// 打乱并取前6个作为干扰
shuffleArray(interference);
const selectedInterference = interference.slice(0, 6);
pool.push(...selectedInterference);
// 如果总数不到16,用当前事件的金色泡泡复制填充(让学生更容易看到正确选项)
while (pool.length < 16) {
pool.push(createBubble(level, 'belief', 'positive', current.beliefPositive));
if (pool.length < 16) pool.push(createBubble(level, 'emotion', 'positive', current.emotionPositive));
}
// 确保恰好16个
const finalPool = pool.slice(0, 16);
shuffleArray(finalPool);
return finalPool;
}
function shuffleArray(arr) {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
}
function renderGrid() {
bubbleGrid.innerHTML = '';
allBubbles.forEach((b, index) => {
const el = document.createElement('div');
el.className = 'bubble';
el.setAttribute('data-index', index);
el.textContent = b.text;
if (b.chain === 'negative') {
el.classList.add(b.type === 'belief' ? 'belief-negative' : 'emotion-negative');
} else {
el.classList.add(b.type === 'belief' ? 'belief-positive' : 'emotion-positive');
}
el.addEventListener('click', () => onBubbleClick(index, el));
el.addEventListener('touchend', (e) => {
e.preventDefault();
onBubbleClick(index, el);
});
bubbleGrid.appendChild(el);
b.htmlEl = el;
b.gridIndex = index;
});
updateGoldenTracking();
}
function updateGoldenTracking() {
const goldenBeliefs = allBubbles.filter(b => b.eventId === currentLevel && b.chain === 'positive' && b
.type === 'belief').length;
goldenPairsNeeded = goldenBeliefs;
collectedGoldenPairs = 0;
}
function onBubbleClick(index, el) {
if (isProcessing) return;
if (el.classList.contains('bursting') || el.classList.contains('collecting') || el.classList.contains(
'dropping')) return;
const bubble = allBubbles[index];
if (!bubble) return;
if (selectedBubble && selectedBubble.id === bubble.id) {
deselectBubble();
return;
}
if (!selectedBubble) {
selectBubble(bubble, el);
return;
}
const first = selectedBubble;
const firstEl = first.htmlEl;
const second = bubble;
const secondEl = el;
if (first.eventId === second.eventId && first.type !== second.type && first.chain === second.chain) {
isProcessing = true;
deselectBubbleSilent();
if (first.chain === 'negative') {
handleNegativePair(first, second, firstEl, secondEl);
} else {
handlePositivePair(first, second, firstEl, secondEl);
}
} else {
firstEl.classList.add('shaking');
secondEl.classList.add('shaking');
deselectBubbleSilent();
setTimeout(() => {
firstEl.classList.remove('shaking');
secondEl.classList.remove('shaking');
}, 400);
}
}
function selectBubble(bubble, el) {
deselectBubbleSilent();
selectedBubble = bubble;
el.classList.add('selected');
}
function deselectBubble() {
if (selectedBubble && selectedBubble.htmlEl) {
selectedBubble.htmlEl.classList.remove('selected');
}
selectedBubble = null;
}
function deselectBubbleSilent() {
if (selectedBubble && selectedBubble.htmlEl) {
selectedBubble.htmlEl.classList.remove('selected');
}
selectedBubble = null;
}
function handleNegativePair(b1, b2, el1, el2) {
el1.classList.add('bursting');
el2.classList.add('bursting');
const midX = (el1.getBoundingClientRect().left + el2.getBoundingClientRect().right) / 2;
const midY = (el1.getBoundingClientRect().top + el2.getBoundingClientRect().bottom) / 2;
showFloatingTip(midX, midY, '💡 试试换个想法,心情会不一样哦!');
const current = eventsData[currentLevel];
setTimeout(() => {
removeBubbleById(b1.id);
removeBubbleById(b2.id);
const newB = createBubble(currentLevel, 'belief', 'positive', current.beliefPositive);
const newE = createBubble(currentLevel, 'emotion', 'positive', current.emotionPositive);
allBubbles = allBubbles.filter(b => b !== null);
allBubbles.push(newB);
allBubbles.push(newE);
// 保持总数不超过16
while (allBubbles.length > 16) {
const idx = allBubbles.findIndex(b => b.eventId !== currentLevel && b.chain === 'negative');
if (idx >= 0) allBubbles.splice(idx, 1);
else {
const ri = allBubbles.findIndex(b => b.eventId !== currentLevel);
if (ri >= 0) allBubbles.splice(ri, 1);
else break;
}
}
renderGrid();
setTimeout(() => {
allBubbles.forEach(b => {
if (b.htmlEl && b.eventId === currentLevel && b.chain === 'positive' && !b.htmlEl
.classList.contains('collecting')) {
b.htmlEl.classList.add('dropping');
}
});
updateGoldenTracking();
checkLevelComplete();
isProcessing = false;
}, 80);
}, 500);
}
function handlePositivePair(b1, b2, el1, el2) {
el1.classList.add('collecting');
el2.classList.add('collecting');
const current = eventsData[currentLevel];
const midX = (el1.getBoundingClientRect().left + el2.getBoundingClientRect().right) / 2;
const midY = (el1.getBoundingClientRect().top + el2.getBoundingClientRect().bottom) / 2;
showFloatingTip(midX, midY, current.encouragement);
score += 10;
stars += 1;
collectedGoldenPairs++;
if (!allEncouragements.includes(current.encouragement)) allEncouragements.push(current.encouragement);
updateUI();
setTimeout(() => {
removeBubbleById(b1.id);
removeBubbleById(b2.id);
allBubbles = allBubbles.filter(b => b !== null);
renderGrid();
updateGoldenTracking();
checkLevelComplete();
isProcessing = false;
}, 550);
}
function removeBubbleById(id) {
const idx = allBubbles.findIndex(b => b && b.id === id);
if (idx >= 0) allBubbles[idx] = null;
}
function showFloatingTip(x, y, text) {
const tip = document.createElement('div');
tip.className = 'floating-tip';
tip.textContent = text;
tip.style.left = x + 'px';
tip.style.top = y + 'px';
tip.style.transform = 'translate(-50%, -50%)';
document.body.appendChild(tip);
setTimeout(() => { if (tip.parentNode) tip.remove(); }, 2600);
}
function checkLevelComplete() {
updateGoldenTracking();
const remainingGolden = allBubbles.filter(b => b && b.eventId === currentLevel && b.chain === 'positive');
if (remainingGolden.length === 0 && collectedGoldenPairs >= goldenPairsNeeded) {
clearInterval(timerInterval);
if (currentLevel < 4) {
setTimeout(() => { currentLevel++;
startLevel(currentLevel); }, 700);
} else {
setTimeout(() => showFinalModal(), 600);
}
}
}
function startTimer() {
clearInterval(timerInterval);
timeLeft = 120;
updateTimerDisplay();
timerInterval = setInterval(() => {
timeLeft--;
updateTimerDisplay();
if (timeLeft <= 0) {
clearInterval(timerInterval);
handleTimeUp();
}
}, 1000);
}
function updateTimerDisplay() {
timerDisplay.textContent = timeLeft;
timerDisplay.classList.toggle('timer-warning', timeLeft <= 20);
}
function handleTimeUp() {
isProcessing = true;
clearInterval(timerInterval);
const rect = bubbleGrid.getBoundingClientRect();
showFloatingTip(rect.left + rect.width / 2, rect.top + rect.height / 2, '⏰ 时间到!重新挑战本关吧~');
setTimeout(() => { isProcessing = false;
startLevel(currentLevel); }, 1500);
}
function startLevel(level) {
currentLevel = level;
clearInterval(timerInterval);
isProcessing = false;
selectedBubble = null;
hintUsed = false;
collectedGoldenPairs = 0;
btnHint.disabled = false;
btnHint.textContent = '💡 提示(1次)';
const ev = eventsData[level];
eventText.textContent = ev.eventA;
guideText.innerHTML = '👆 先找到两个<strong>灰色泡泡</strong>配对,再看会出现什么神奇变化!';
levelDisplay.textContent = (level + 1) + '/5';
updateUI();
allBubbles = buildBubblePool(level);
renderGrid();
updateGoldenTracking();
timerDisplay.classList.remove('timer-warning');
updateTimerDisplay();
startTimer();
}
function updateUI() {
scoreDisplay.textContent = score;
starDisplay.textContent = stars;
}
btnHint.addEventListener('click', () => {
if (hintUsed || isProcessing) return;
hintUsed = true;
btnHint.disabled = true;
btnHint.textContent = '💡 已使用';
const gb = allBubbles.find(b => b && b.eventId === currentLevel && b.chain === 'positive' && b.type ===
'belief');
const ge = allBubbles.find(b => b && b.eventId === currentLevel && b.chain === 'positive' && b.type ===
'emotion');
if (gb && gb.htmlEl) gb.htmlEl.classList.add('hint-glow');
if (ge && ge.htmlEl) ge.htmlEl.classList.add('hint-glow');
setTimeout(() => {
if (gb && gb.htmlEl) gb.htmlEl.classList.remove('hint-glow');
if (ge && ge.htmlEl) ge.htmlEl.classList.remove('hint-glow');
}, 3000);
});
btnReset.addEventListener('click', () => {
if (isProcessing) return;
startLevel(currentLevel);
});
function showFinalModal() {
clearInterval(timerInterval);
isProcessing = true;
let badgeClass, titleName;
if (score >= 80) { badgeClass = 'badge-diamond';
titleName = '💎 钻石心灵大师'; } else if (score >= 50) { badgeClass = 'badge-gold';
titleName = '🥇 黄金炼心师'; } else { badgeClass = 'badge-bronze';
titleName = '🥉 青铜净化师'; }
const quotes = [...new Set([...allEncouragements, ...globalQuotes])];
modalContent.innerHTML = `
<h2>🎉 通关成功!</h2>
<div class="title-badge ${badgeClass}">${titleName}</div>
<div class="score-display">${score}</div>
<div style="color:#7a6a5a;">总分</div>
<div class="stars-display">${'⭐'.repeat(Math.min(stars, 20))}</div>
<div style="color:#7a6a5a;font-size:0.85rem;">共获得 ${stars} 颗星星</div>
<div class="quotes-list">
<strong>📝 心灵金句合集:</strong>
${quotes.map(q => `<p>${q}</p>`).join('')}
</div>
<p style="font-size:0.8rem;color:#b8957a;margin-top:8px;">
记住:<strong>事件A只是间接原因,信念B才是情绪C的直接原因</strong>。<br>
做情绪的主人,从调整想法开始!🌈
</p>
<button class="btn-close" id="btnCloseModal">🔄 重新挑战</button>
`;
modalOverlay.style.display = 'flex';
document.getElementById('btnCloseModal').addEventListener('click', () => {
modalOverlay.style.display = 'none';
resetGame();
});
}
function resetGame() {
currentLevel = 0;
score = 0;
stars = 0;
allEncouragements = [];
isProcessing = false;
selectedBubble = null;
hintUsed = false;
btnHint.disabled = false;
btnHint.textContent = '💡 提示(1次)';
clearInterval(timerInterval);
updateUI();
startLevel(0);
modalOverlay.style.display = 'none';
}
modalOverlay.addEventListener('click', (e) => {
if (e.target === modalOverlay) resetGame();
});
function initGame() {
resetGame();
}
initGame();
console.log('🫧 情绪泡泡龙优化版已就绪!网格4x4,更容易找到配对。');
})();
</script>
</body>
</html>Game Source: 情绪泡泡龙——ABC消消乐大作战(优化版)
Creator: LaserPirate42
Libraries: none
Complexity: complex (1002 lines, 37.4 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: abc-laserpirate42-mpcrtsj2" to link back to the original. Then publish at arcadelab.ai/publish.