good version

This commit is contained in:
王性驊 2025-11-22 15:27:42 +08:00
parent 687a83922f
commit 11c01fb1ea
4 changed files with 877 additions and 47 deletions

View File

@ -0,0 +1,617 @@
<template>
<div class="game-overlay" @click.self="handleClose">
<div class="game-container">
<h2 class="game-title">猜拳遊戲</h2>
<div v-if="!gameStarted" class="game-intro">
<p>3回合制</p>
</div>
<div v-if="gameStarted" class="game-status">
<div class="score">
<span>: {{ playerScore }}</span>
<span>{{ currentRound }}/3</span>
<span>電腦: {{ cpuScore }}</span>
</div>
</div>
<div v-if="result" class="result-display" ref="resultDisplayRef">
<div class="hands" :class="{ 'shake': waiting }">
<div class="hand player-hand" :class="{ 'slide-in-left': !waiting }">
<div class="hand-icon" :class="`icon-${playerChoice}`"></div>
</div>
<div class="vs">VS</div>
<div class="hand cpu-hand" :class="{ 'slide-in-right': !waiting }">
<div class="hand-icon" :class="`icon-${cpuChoice}`"></div>
</div>
</div>
<div class="result-text" :class="[result, { 'bounce-in': !waiting }]">{{ resultText }}</div>
</div>
<div v-if="!gameOver" class="choices">
<button
v-for="choice in choices"
:key="choice.id"
class="choice-btn"
@click="play(choice.id)"
:disabled="waiting"
>
<div class="choice-icon" :class="`icon-${choice.id}`"></div>
<span>{{ choice.name }}</span>
</button>
</div>
<div v-if="gameOver" class="game-over">
<div class="final-result" :class="finalResult">
<div class="result-icon" :class="`icon-${finalResult}`"></div>
<h3>{{ finalResultText }}</h3>
</div>
<button class="action-btn" @click="playAgain">再玩一次</button>
</div>
<button class="close-btn" @click="handleClose">
{{ gameOver ? '完成' : '取消' }}
</button>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const emit = defineEmits(['close', 'complete']);
const choices = [
{ id: 'rock', name: '石頭' },
{ id: 'paper', name: '布' },
{ id: 'scissors', name: '剪刀' }
];
const gameStarted = ref(false);
const gameOver = ref(false);
const currentRound = ref(1);
const playerScore = ref(0);
const cpuScore = ref(0);
const playerChoice = ref('');
const cpuChoice = ref('');
const result = ref('');
const waiting = ref(false);
const resultText = computed(() => {
if (result.value === 'win') return '你贏了!';
if (result.value === 'lose') return '你輸了!';
return '平手!';
});
const finalResult = computed(() => {
if (playerScore.value > cpuScore.value) return 'win';
if (playerScore.value < cpuScore.value) return 'lose';
return 'draw';
});
const finalResultText = computed(() => {
if (finalResult.value === 'win') return '勝利!';
if (finalResult.value === 'lose') return '失敗...';
return '平手';
});
function play(choice) {
if (waiting.value || gameOver.value) return;
gameStarted.value = true;
waiting.value = true;
playerChoice.value = choice;
// CPU makes a random choice
const cpuIndex = Math.floor(Math.random() * 3);
cpuChoice.value = choices[cpuIndex].id;
// Determine winner
setTimeout(() => {
const outcome = determineWinner(choice, cpuChoice.value);
result.value = outcome;
if (outcome === 'win') {
playerScore.value++;
} else if (outcome === 'lose') {
cpuScore.value++;
}
// Check if game is over
setTimeout(() => {
if (currentRound.value >= 3) {
gameOver.value = true;
} else {
currentRound.value++;
result.value = '';
waiting.value = false;
}
}, 1500);
}, 500);
}
function determineWinner(player, cpu) {
if (player === cpu) return 'draw';
if (
(player === 'rock' && cpu === 'scissors') ||
(player === 'paper' && cpu === 'rock') ||
(player === 'scissors' && cpu === 'paper')
) {
return 'win';
}
return 'lose';
}
function playAgain() {
gameStarted.value = false;
gameOver.value = false;
currentRound.value = 1;
playerScore.value = 0;
cpuScore.value = 0;
result.value = '';
waiting.value = false;
}
function handleClose() {
if (gameOver.value && finalResult.value === 'win') {
emit('complete', true); // Won the game
} else {
emit('complete', false); // Didn't win
}
emit('close');
}
</script>
<style scoped>
.game-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
justify-content: center;
z-index: 150;
font-family: 'DotGothic16', sans-serif;
overflow: auto;
}
.game-container {
background: #f5f5dc;
border: 4px solid #8b4513;
border-radius: 8px;
padding: 8px;
width: 90%;
max-width: 280px;
max-height: 90vh;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
animation: slide-up 0.3s ease-out;
overflow-y: auto;
display: flex;
flex-direction: column;
}
.game-title {
text-align: center;
font-size: 13px;
margin: 0 0 6px 0;
color: #8b4513;
flex-shrink: 0;
}
.game-intro {
text-align: center;
margin-bottom: 6px;
color: #8b4513;
font-size: 10px;
flex-shrink: 0;
}
.game-status {
text-align: center;
margin-bottom: 5px;
flex-shrink: 0;
}
.score {
display: flex;
justify-content: space-between;
font-size: 10px;
font-weight: bold;
color: #8b4513;
padding: 0 5px;
}
.result-display {
margin: 6px 0;
flex-shrink: 0;
}
.hands {
display: flex;
align-items: center;
justify-content: space-around;
margin-bottom: 5px;
}
.hands.shake {
animation: shake-hands 0.5s ease-in-out;
}
@keyframes shake-hands {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px) rotate(-5deg); }
75% { transform: translateX(5px) rotate(5deg); }
}
.hand {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
}
.hand.slide-in-left {
animation: slide-in-left 0.5s ease-out;
}
.hand.slide-in-right {
animation: slide-in-right 0.5s ease-out;
}
@keyframes slide-in-left {
from {
transform: translateX(-50px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slide-in-right {
from {
transform: translateX(50px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.hand span {
font-size: 10px;
color: #8b4513;
}
.hand-icon {
width: 24px;
height: 24px;
position: relative;
}
.vs {
font-size: 12px;
font-weight: bold;
color: #8b4513;
}
.result-text {
text-align: center;
font-size: 11px;
font-weight: bold;
padding: 5px;
border-radius: 4px;
}
.result-text.bounce-in {
animation: bounce-in 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
@keyframes bounce-in {
0% {
transform: scale(0);
opacity: 0;
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
opacity: 1;
}
}
.result-text.win {
background: #90EE90;
color: #006400;
}
.result-text.lose {
background: #FFB6C1;
color: #8b0000;
}
.result-text.draw {
background: #FFE4B5;
color: #8b4513;
}
.choices {
display: flex;
gap: 5px;
margin-bottom: 6px;
flex-shrink: 0;
}
.choice-btn {
flex: 1;
background: #fff;
border: 3px solid #8b4513;
border-radius: 5px;
padding: 6px 3px;
cursor: pointer;
transition: all 0.2s;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
font-family: 'DotGothic16', sans-serif;
}
.choice-btn:hover:not(:disabled) {
background: #fffacd;
transform: scale(1.05);
animation: pulse 0.6s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1.05); }
50% { transform: scale(1.1); }
}
.choice-btn:active:not(:disabled) {
transform: scale(0.95);
}
.choice-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.choice-btn span {
font-size: 9px;
color: #8b4513;
}
.choice-icon, .hand-icon {
width: 20px;
height: 20px;
position: relative;
}
/* Pixel Art Icons */
.icon-rock::before {
content: '';
position: absolute;
width: 1px;
height: 1px;
background: transparent;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(2);
box-shadow:
-2px -2px 0 #888, -1px -2px 0 #888, 0 -2px 0 #888, 1px -2px 0 #888,
-3px -1px 0 #888, -2px -1px 0 #aaa, -1px -1px 0 #aaa, 0 -1px 0 #aaa, 1px -1px 0 #aaa, 2px -1px 0 #888,
-3px 0 0 #888, -2px 0 0 #aaa, -1px 0 0 #666, 0 0 0 #666, 1px 0 0 #aaa, 2px 0 0 #888,
-2px 1px 0 #888, -1px 1px 0 #aaa, 0 1px 0 #aaa, 1px 1px 0 #888,
-1px 2px 0 #888, 0 2px 0 #888;
}
.icon-paper::before {
content: '';
position: absolute;
width: 1px;
height: 1px;
background: transparent;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(2);
box-shadow:
-2px -3px 0 #ffcc99, -1px -3px 0 #ffcc99, 0 -3px 0 #ffcc99, 1px -3px 0 #ffcc99, 2px -3px 0 #ffcc99,
-3px -2px 0 #ffcc99, -2px -2px 0 #ffcc99, -1px -2px 0 #ffcc99, 0 -2px 0 #ffcc99, 1px -2px 0 #ffcc99, 2px -2px 0 #ffcc99, 3px -2px 0 #ffcc99,
-3px -1px 0 #ffcc99, -2px -1px 0 #ffcc99, -1px -1px 0 #ffcc99, 0 -1px 0 #ffcc99, 1px -1px 0 #ffcc99, 2px -1px 0 #ffcc99, 3px -1px 0 #ffcc99,
-3px 0 0 #ffcc99, -2px 0 0 #ffcc99, -1px 0 0 #ffaa77, 0 0 0 #ffaa77, 1px 0 0 #ffaa77, 2px 0 0 #ffcc99, 3px 0 0 #ffcc99,
-3px 1px 0 #ffcc99, -2px 1px 0 #ffcc99, -1px 1px 0 #ffcc99, 0 1px 0 #ffcc99, 1px 1px 0 #ffcc99, 2px 1px 0 #ffcc99, 3px 1px 0 #ffcc99;
}
.icon-scissors::before {
content: '';
position: absolute;
width: 1px;
height: 1px;
background: transparent;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(2);
box-shadow:
-3px -2px 0 #888, -2px -2px 0 #888,
-3px -1px 0 #888, -2px -1px 0 #888,
-2px 0 0 #888, -1px 0 0 #888, 0 0 0 #888, 1px 0 0 #888, 2px 0 0 #888,
-1px 1px 0 #888, 0 1px 0 #888, 1px 1px 0 #888,
2px -2px 0 #888, 3px -2px 0 #888,
2px -1px 0 #888, 3px -1px 0 #888;
}
.game-over {
text-align: center;
margin-bottom: 6px;
flex-shrink: 0;
}
.final-result {
font-size: 13px;
margin: 6px 0;
padding: 8px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.result-icon {
width: 24px;
height: 24px;
position: relative;
flex-shrink: 0;
}
/* Trophy icon for win */
.icon-win::before {
content: '';
position: absolute;
width: 1px;
height: 1px;
background: transparent;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(2);
box-shadow:
/* Cup top */
-3px -4px 0 #ffd700, -2px -4px 0 #ffd700, -1px -4px 0 #ffd700, 0 -4px 0 #ffd700, 1px -4px 0 #ffd700, 2px -4px 0 #ffd700, 3px -4px 0 #ffd700,
/* Cup body */
-2px -3px 0 #ffd700, -1px -3px 0 #ffed4e, 0 -3px 0 #ffed4e, 1px -3px 0 #ffed4e, 2px -3px 0 #ffd700,
-2px -2px 0 #ffd700, -1px -2px 0 #ffed4e, 0 -2px 0 #ffed4e, 1px -2px 0 #ffed4e, 2px -2px 0 #ffd700,
-2px -1px 0 #ffd700, -1px -1px 0 #ffed4e, 0 -1px 0 #ffed4e, 1px -1px 0 #ffed4e, 2px -1px 0 #ffd700,
-2px 0 0 #ffd700, -1px 0 0 #ffd700, 0 0 0 #ffd700, 1px 0 0 #ffd700, 2px 0 0 #ffd700,
/* Base */
-1px 1px 0 #b8860b, 0 1px 0 #b8860b, 1px 1px 0 #b8860b,
-2px 2px 0 #b8860b, -1px 2px 0 #b8860b, 0 2px 0 #b8860b, 1px 2px 0 #b8860b, 2px 2px 0 #b8860b;
}
/* Broken heart icon for lose */
.icon-lose::before {
content: '';
position: absolute;
width: 1px;
height: 1px;
background: transparent;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(2);
box-shadow:
/* Left side of heart */
-3px -2px 0 #ff6b6b, -2px -2px 0 #ff6b6b,
-4px -1px 0 #ff6b6b, -3px -1px 0 #ff6b6b, -2px -1px 0 #ff6b6b, -1px -1px 0 #ff6b6b,
-4px 0 0 #ff6b6b, -3px 0 0 #ff6b6b, -2px 0 0 #ff6b6b, -1px 0 0 #ff6b6b,
/* Right side of heart */
1px -2px 0 #ff6b6b, 2px -2px 0 #ff6b6b,
0 -1px 0 #ff6b6b, 1px -1px 0 #ff6b6b, 2px -1px 0 #ff6b6b, 3px -1px 0 #ff6b6b,
0 0 0 #ff6b6b, 1px 0 0 #ff6b6b, 2px 0 0 #ff6b6b, 3px 0 0 #ff6b6b,
/* Bottom crack (broken) */
-3px 1px 0 #ff6b6b, -1px 1px 0 #ff6b6b, 1px 1px 0 #ff6b6b,
-2px 2px 0 #ff6b6b, 0 2px 0 #ff6b6b,
-1px 3px 0 #ff6b6b;
}
/* Handshake icon for draw */
.icon-draw::before {
content: '';
position: absolute;
width: 1px;
height: 1px;
background: transparent;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(2);
box-shadow:
/* Left hand */
-4px -1px 0 #ffcc99, -3px -1px 0 #ffcc99, -2px -1px 0 #ffcc99,
-4px 0 0 #ffcc99, -3px 0 0 #ffcc99, -2px 0 0 #ffcc99, -1px 0 0 #ffcc99,
-4px 1px 0 #ffcc99, -3px 1px 0 #ffcc99, -2px 1px 0 #ffcc99,
/* Right hand */
1px -1px 0 #ffcc99, 2px -1px 0 #ffcc99, 3px -1px 0 #ffcc99,
0 0 0 #ffcc99, 1px 0 0 #ffcc99, 2px 0 0 #ffcc99, 3px 0 0 #ffcc99,
1px 1px 0 #ffcc99, 2px 1px 0 #ffcc99, 3px 1px 0 #ffcc99;
}
.final-result h3 {
margin: 0;
font-size: 13px;
}
.final-result.win {
background: #90EE90;
color: #006400;
}
.final-result.lose {
background: #FFB6C1;
color: #8b0000;
}
.final-result.draw {
background: #FFE4B5;
color: #8b4513;
}
.action-btn {
width: 100%;
padding: 5px;
background: #4CAF50;
border: 3px solid #2e7d32;
border-radius: 4px;
color: white;
font-size: 10px;
font-weight: bold;
cursor: pointer;
font-family: 'DotGothic16', sans-serif;
margin-bottom: 5px;
}
.action-btn:hover {
background: #45a049;
}
.action-btn:active {
transform: scale(0.95);
}
.close-btn {
width: 100%;
padding: 5px;
background: #cd853f;
border: 3px solid #8b4513;
border-radius: 4px;
color: white;
font-size: 10px;
font-weight: bold;
cursor: pointer;
font-family: 'DotGothic16', sans-serif;
flex-shrink: 0;
}
.close-btn:hover {
background: #d2691e;
}
.close-btn:active {
transform: scale(0.95);
}
@keyframes slide-up {
from {
transform: translateY(50px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
</style>

View File

@ -5,7 +5,7 @@
:disabled="stage === 'egg'"
@info="showPetInfo = !showPetInfo"
@feed="$emit('action', 'feed')"
@play="$emit('action', 'play')"
@playMenu="showPlayMenu = true"
@sleep="$emit('action', 'sleep')"
/>
@ -199,6 +199,20 @@
@close="showPetInfo = false"
/>
<!-- Play Menu -->
<PlayMenu
v-if="showPlayMenu"
@select="handlePlaySelect"
@close="showPlayMenu = false"
/>
<!-- Mini Games -->
<GuessingGame
v-if="currentGame === 'guessing'"
@close="currentGame = ''"
@complete="handleGameComplete"
/>
<!-- Inventory Screen -->
<InventoryScreen
v-if="showInventory"
@ -239,6 +253,8 @@ import FortuneStickAnimation from './FortuneStickAnimation.vue';
import FortuneResult from './FortuneResult.vue';
import PetInfoScreen from './PetInfoScreen.vue';
import InventoryScreen from './InventoryScreen.vue';
import PlayMenu from './PlayMenu.vue';
import GuessingGame from './GuessingGame.vue';
import guanyinLots from '../assets/guanyin_100_lots.json';
const props = defineProps({
@ -285,6 +301,8 @@ const currentLotNumber = ref(null);
const consecutiveSaintCount = ref(0);
const showPetInfo = ref(false);
const showInventory = ref(false);
const showPlayMenu = ref(false);
const currentGame = ref(''); // '', 'training', 'guessing', 'ball'
const inventory = ref(new Array(16).fill(null));
// Initialize some items
inventory.value[0] = { id: 'cookie', name: '幸運餅乾', description: '增加一點快樂值', count: 5, iconClass: 'icon-cookie' };
@ -343,6 +361,25 @@ function handleJiaobeiClose() {
consecutiveSaintCount.value = 0;
}
// --- Play Game Selection ---
function handlePlaySelect(gameType) {
showPlayMenu.value = false;
console.log('Selected game:', gameType);
// Show the selected game
currentGame.value = gameType;
}
function handleGameComplete(won) {
console.log('Game completed, won:', won);
currentGame.value = '';
if (won) {
// Reward: increase happiness
emit('action', 'play');
}
}
// --- ---
function handleStickComplete(number) {
@ -914,58 +951,22 @@ async function startFeeding(type) {
foodVisible.value = true;
console.log('Food visible:', foodVisible.value);
// Calculate food position: in front of pet, at mouth height
// Food drops in front (not directly at mouth)
// Calculate food position: directly above pet's mouth
const foodSize = 10 * pixelSize;
const frontOffsetX = isFacingRight.value ? -foodSize - 5 : width + 5; // In front of pet
const mouthY = 8.5; // Mouth is at row 8-9
// Set horizontal position (in front of pet)
let targetFoodX = petX.value + frontOffsetX;
// Only avoid poop areas if there's actual poop
const areas = getPoopAreas();
if (areas.length > 0) {
const maxPoopRight = Math.max(...areas.map(a => a.right));
if (targetFoodX < maxPoopRight + 10) {
// Move food to the right of poop areas
targetFoodX = maxPoopRight + 15;
}
}
// Ensure food stays within bounds
const cw = containerRef.value?.clientWidth || 300;
targetFoodX = Math.max(10, Math.min(cw - 40, targetFoodX));
foodX.value = targetFoodX;
foodX.value = targetFoodX;
// Food drops right in front of pet's mouth (don't move the pet)
// Position food based on which way pet is facing
const mouthOffsetX = isFacingRight.value ? -foodSize - 2 : width + 2;
foodX.value = petX.value + mouthOffsetX;
foodY.value = 0; // Start from top of screen
// Calculate target Y (at mouth level)
const targetY = petY.value + (mouthY * pixelSize) - (foodSize / 2);
// Only avoid poop areas vertically if there's actual poop
let safeTargetY = targetY;
if (areas.length > 0) {
const maxPoopBottom = Math.max(...areas.map(a => a.bottom));
safeTargetY = Math.min(targetY, maxPoopBottom - foodSize - 5);
}
console.log('Food dropping to:', foodX.value, targetY, 'Pet at:', petX.value, petY.value);
// Move pet to food
const targetPetX = isFacingRight.value ? targetFoodX - width - 5 : targetFoodX + (10 * pixelSize) + 5;
// Force pet to be at the correct position to eat
// This ensures that even if the food was moved due to bounds/poop, the pet is there
petX.value = targetPetX;
// Simple animation sequence
// 1. Drop food
// 2. Pet moves to food
// 3. Pet eats (mouth open/close)
// ... (Existing feeding logic would go here, but we rely on state='eating' triggers from parent)
// Animate falling to front of pet
// Animate falling to pet's mouth
const duration = 800;
const startTime = performance.now();
@ -975,7 +976,7 @@ async function startFeeding(type) {
const progress = Math.min(elapsed / duration, 1);
// Ease out for smoother landing
const eased = 1 - Math.pow(1 - progress, 3);
foodY.value = eased * safeTargetY;
foodY.value = eased * targetY;
if (progress < 1) {
requestAnimationFrame(animateFall);

212
src/components/PlayMenu.vue Normal file
View File

@ -0,0 +1,212 @@
<template>
<div class="play-menu-overlay" @click.self="$emit('close')">
<div class="play-menu-container">
<h2 class="menu-title">選擇遊戲</h2>
<div class="game-options">
<button
class="game-option"
@click="selectGame('training')"
>
<div class="option-icon icon-training"></div>
<div class="option-name">訓練</div>
<div class="option-desc">攻擊訓練</div>
</button>
<button
class="game-option"
@click="selectGame('guessing')"
>
<div class="option-icon icon-rps"></div>
<div class="option-name">猜拳</div>
<div class="option-desc">剪刀石頭布</div>
</button>
<button
class="game-option"
@click="selectGame('ball')"
>
<div class="option-icon icon-ball"></div>
<div class="option-name">接球</div>
<div class="option-desc">反應小遊戲</div>
</button>
</div>
<button class="close-btn" @click="$emit('close')">取消</button>
</div>
</div>
</template>
<script setup>
const emit = defineEmits(['close', 'select']);
function selectGame(gameType) {
emit('select', gameType);
}
</script>
<style scoped>
.play-menu-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
font-family: 'DotGothic16', sans-serif;
overflow: auto;
}
.play-menu-container {
background: #f5f5dc;
border: 4px solid #8b4513;
border-radius: 12px;
padding: 15px;
min-width: 200px;
max-width: 90%;
max-height: 85%;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
overflow-y: auto;
margin: 10px;
}
.menu-title {
text-align: center;
font-size: 14px;
margin: 0 0 12px 0;
color: #8b4513;
}
.game-options {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 10px;
}
.game-option {
background: #fff;
border: 3px solid #8b4513;
border-radius: 8px;
padding: 10px;
cursor: pointer;
transition: all 0.2s;
text-align: center;
font-family: 'DotGothic16', sans-serif;
}
.game-option:hover {
background: #fffacd;
transform: scale(1.05);
}
.game-option:active {
transform: scale(0.95);
}
.option-icon {
width: 24px;
height: 24px;
margin: 0 auto 5px;
position: relative;
}
/* Pixel Art Icons */
.icon-training::before {
content: '';
position: absolute;
width: 1px;
height: 1px;
background: transparent;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(2);
box-shadow:
/* Explosion/Impact shape */
0 0 0 #ff0000,
-2px -2px 0 #ff4400, 2px -2px 0 #ff4400,
-3px 0 0 #ff6600, -2px 0 0 #ff4400, 0 0 0 #ff0000, 2px 0 0 #ff4400, 3px 0 0 #ff6600,
-2px 2px 0 #ff4400, 2px 2px 0 #ff4400,
/* Outer glow */
-4px -1px 0 #ffaa00, 4px -1px 0 #ffaa00,
-4px 1px 0 #ffaa00, 4px 1px 0 #ffaa00,
-1px -4px 0 #ffaa00, 1px -4px 0 #ffaa00,
-1px 4px 0 #ffaa00, 1px 4px 0 #ffaa00;
}
.icon-rps::before {
content: '';
position: absolute;
width: 1px;
height: 1px;
background: transparent;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(2);
box-shadow:
/* Fist shape */
-1px -3px 0 #ffcc99, 0 -3px 0 #ffcc99, 1px -3px 0 #ffcc99,
-2px -2px 0 #ffcc99, -1px -2px 0 #ffcc99, 0 -2px 0 #ffcc99, 1px -2px 0 #ffcc99, 2px -2px 0 #ffcc99,
-2px -1px 0 #ffcc99, -1px -1px 0 #ffcc99, 0 -1px 0 #ffcc99, 1px -1px 0 #ffcc99, 2px -1px 0 #ffcc99,
-2px 0 0 #ffcc99, -1px 0 0 #ffcc99, 0 0 0 #ffaa77, 1px 0 0 #ffcc99, 2px 0 0 #ffcc99,
-2px 1px 0 #ffcc99, -1px 1px 0 #ffcc99, 0 1px 0 #ffcc99, 1px 1px 0 #ffcc99, 2px 1px 0 #ffcc99,
-1px 2px 0 #ffcc99, 0 2px 0 #ffcc99, 1px 2px 0 #ffcc99;
}
.icon-ball::before {
content: '';
position: absolute;
width: 1px;
height: 1px;
background: transparent;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(2);
box-shadow:
/* Ball shape */
-1px -3px 0 #000, 0 -3px 0 #000, 1px -3px 0 #000,
-2px -2px 0 #000, -1px -2px 0 #fff, 0 -2px 0 #fff, 1px -2px 0 #fff, 2px -2px 0 #000,
-3px -1px 0 #000, -2px -1px 0 #fff, -1px -1px 0 #fff, 0 -1px 0 #000, 1px -1px 0 #fff, 2px -1px 0 #fff, 3px -1px 0 #000,
-3px 0 0 #000, -2px 0 0 #fff, -1px 0 0 #000, 0 0 0 #000, 1px 0 0 #000, 2px 0 0 #fff, 3px 0 0 #000,
-3px 1px 0 #000, -2px 1px 0 #fff, -1px 1px 0 #fff, 0 1px 0 #000, 1px 1px 0 #fff, 2px 1px 0 #fff, 3px 1px 0 #000,
-2px 2px 0 #000, -1px 2px 0 #fff, 0 2px 0 #fff, 1px 2px 0 #fff, 2px 2px 0 #000,
-1px 3px 0 #000, 0 3px 0 #000, 1px 3px 0 #000;
}
.option-name {
font-size: 13px;
font-weight: bold;
color: #8b4513;
margin-bottom: 2px;
}
.option-desc {
font-size: 10px;
color: #a0522d;
}
.close-btn {
width: 100%;
padding: 8px;
background: #cd853f;
border: 3px solid #8b4513;
border-radius: 6px;
color: white;
font-size: 12px;
font-weight: bold;
cursor: pointer;
font-family: 'DotGothic16', sans-serif;
}
.close-btn:hover {
background: #d2691e;
}
.close-btn:active {
transform: scale(0.95);
}
</style>

View File

@ -2,7 +2,7 @@
<div class="top-menu">
<button class="icon-btn icon-stats" @click="$emit('info')" title="Status"></button>
<button class="icon-btn icon-feed" @click="$emit('feed')" :disabled="disabled" title="Feed"></button>
<button class="icon-btn icon-play" @click="$emit('play')" :disabled="disabled" title="Play"></button>
<button class="icon-btn icon-play" @click="$emit('playMenu')" :disabled="disabled" title="Play"></button>
<button class="icon-btn icon-sleep" @click="$emit('sleep')" :disabled="disabled" title="Sleep"></button>
</div>
</template>
@ -15,7 +15,7 @@ const props = defineProps({
}
});
defineEmits(['info', 'feed', 'play', 'sleep']);
defineEmits(['info', 'feed', 'playMenu', 'sleep']);
</script>
<style scoped>