good version

This commit is contained in:
王性驊 2025-11-22 12:53:14 +08:00
parent 3c9a7f1e7b
commit 687a83922f
2 changed files with 104 additions and 11 deletions

View File

@ -51,10 +51,19 @@ function handleAction(action) {
sleep(); sleep();
break; break;
case 'medicine': case 'medicine':
// Heal the pet // Heal the pet with animation
if (state.value === 'sick') { if (state.value === 'sick') {
stats.value.health = 100; if (petGameRef.value) {
state.value = 'idle'; // Trigger medicine animation
petGameRef.value.startFeeding('medicine').then(() => {
stats.value.health = 100;
state.value = 'idle';
});
} else {
// Fallback if ref not ready
stats.value.health = 100;
state.value = 'idle';
}
} }
break; break;
case 'stats': case 'stats':

View File

@ -62,7 +62,7 @@
<!-- 食物 --> <!-- 食物 -->
<div <div
v-if="state === 'eating' && foodVisible" v-if="foodVisible"
class="food-item" class="food-item"
:style="{ :style="{
left: foodX + 'px', left: foodX + 'px',
@ -681,6 +681,8 @@ const foodX = ref(0);
const foodY = ref(0); const foodY = ref(0);
const foodStage = ref(0); // 0, 1, 2 const foodStage = ref(0); // 0, 1, 2
const foodVisible = ref(false); const foodVisible = ref(false);
const currentFoodType = ref('food'); // 'food' or 'medicine'
const isMedicineActive = ref(false);
// Playing State // Playing State
const ballX = ref(0); const ballX = ref(0);
@ -725,6 +727,56 @@ const currentPixels = computed(() => {
}); });
const currentFoodPixels = computed(() => { const currentFoodPixels = computed(() => {
if (currentFoodType.value === 'medicine') {
// Diagonal Pill Pixel Art
const pillPalette = {
'B': '#1a1a1a', // Black Outline
'R': '#e74c3c', // Red
'L': '#ff7979', // Light Red (Highlight)
'W': '#ecf0f1', // White
'S': '#bdc3c7', // Shadow (Grey)
'H': '#ffffff', // Pure White Highlight
'0': 'transparent'
};
// 12x12 Grid
const pillSprite = [
"0000000BBB00",
"000000BRRRB0",
"00000BRHLRB0",
"0000BRRRRRB0",
"000BWRRRRB00",
"00BWWRRRB000",
"0BWWWWRB0000",
"0BSWWWWB0000",
"00BSSWB00000",
"000BBB000000",
"000000000000",
"000000000000"
];
// Apply stage (bite)
const stage = foodStage.value;
const pxs = [];
pillSprite.forEach((row, y) => {
[...row].forEach((ch, x) => {
if (ch === '0') return;
// Bite logic: Eat from bottom-left/bottom
// Stage 1: Remove bottom chunk
if (stage === 1 && (y > 7 || (y > 5 && x < 4))) return;
// Stage 2: Remove most of it
if (stage === 2 && y > 4) return;
pxs.push({
x, y,
color: pillPalette[ch]
});
});
});
return pxs;
}
const sprite = FOOD_SPRITES[foodStage.value]; const sprite = FOOD_SPRITES[foodStage.value];
const pxs = []; const pxs = [];
if(!sprite) return pxs; if(!sprite) return pxs;
@ -774,7 +826,7 @@ function moveRandomly() {
} }
console.log('moveRandomly called. State:', props.state); console.log('moveRandomly called. State:', props.state);
if (props.state === 'sleep' || props.state === 'dead' || props.state === 'eating') { if (props.state === 'sleep' || props.state === 'dead' || props.state === 'eating' || isMedicineActive.value) {
updateHeadIconsPosition(); updateHeadIconsPosition();
return; return;
} }
@ -841,10 +893,26 @@ function moveRandomly() {
} }
// Feeding Logic // Feeding Logic
async function startFeeding() {
// Reset food async function startFeeding(type) {
console.log('Starting feeding:', type);
if (type === 'medicine') {
currentFoodType.value = 'medicine';
isMedicineActive.value = true;
} else {
// If default feeding, ensure we don't override medicine
if (isMedicineActive.value) {
console.log('Skipping default feeding because medicine is active');
return;
}
currentFoodType.value = 'food';
isMedicineActive.value = false;
}
foodStage.value = 0; foodStage.value = 0;
foodVisible.value = true; foodVisible.value = true;
console.log('Food visible:', foodVisible.value);
// Calculate food position: in front of pet, at mouth height // Calculate food position: in front of pet, at mouth height
// Food drops in front (not directly at mouth) // Food drops in front (not directly at mouth)
@ -886,6 +954,10 @@ async function startFeeding() {
// Move pet to food // Move pet to food
const targetPetX = isFacingRight.value ? targetFoodX - width - 5 : targetFoodX + (10 * pixelSize) + 5; 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 // Simple animation sequence
// 1. Drop food // 1. Drop food
// 2. Pet moves to food // 2. Pet moves to food
@ -938,6 +1010,7 @@ async function startFeeding() {
// Finish eating // Finish eating
foodVisible.value = false; foodVisible.value = false;
isMedicineActive.value = false;
emit('update:state', 'idle'); emit('update:state', 'idle');
} }
@ -1030,11 +1103,21 @@ onUnmounted(() => {
watch(() => props.state, (newState) => { watch(() => props.state, (newState) => {
updateHeadIconsPosition(); updateHeadIconsPosition();
if (newState === 'eating') { if (newState === 'eating') {
startFeeding(); // Only start default feeding if we aren't already doing medicine
if (!isMedicineActive.value) {
startFeeding();
}
} else { } else {
// Reset feeding state if interrupted // Reset feeding state if interrupted
isMouthOpen.value = false; if (newState !== 'eating') {
foodVisible.value = false; isMouthOpen.value = false;
foodVisible.value = false;
isMedicineActive.value = false;
// Reset type back to food when done
if (!foodVisible.value) {
currentFoodType.value = 'food';
}
}
} }
}); });
@ -1065,7 +1148,8 @@ async function shakeHead() {
// Expose shakeHead function to parent component // Expose shakeHead function to parent component
defineExpose({ defineExpose({
shakeHead shakeHead,
startFeeding
}); });
</script> </script>