Rebalance item effects for segmented status model
Tune item stat values so basic items restore roughly 1 baby segment (+25), medium items provide meaningful upgrades (+35-50), and premium items have strong multi-stat effects (+70-75). Food: Apple +15→+25 hunger, Burger +40→+45, Pizza +35→+40, Sushi +30→+35, Cake +20→+25. Toys: Teddy +40→+45 happiness and -15→-5 energy (premium = low cost). Medicine: Bandage +15→+25, Vitamins +20→+25 health and add +5 energy, Elixir +80→+75 health. Hygiene: Soap +30→+25, Bubble Bath +60→+70 hygiene and +20→+25 happiness. Direct actions unchanged (play_music +15, sing +20). Adds 20 focused tests covering item catalog values, applyItemEffects clamping, tier relationships, and direct action effects. No changes to decay rates, sleep behaviour, careState mapping, segmented UI, item previews, level unlocks, or Nostr persistence.
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import { getShopItemById } from './blobbi-shop-items';
|
||||
import { applyItemEffects } from '@/blobbi/actions/lib/blobbi-action-utils';
|
||||
import { DIRECT_ACTION_HAPPINESS_EFFECTS } from '@/blobbi/actions/hooks/useBlobbiDirectAction';
|
||||
import type { BlobbiStats } from '@/blobbi/core/lib/blobbi';
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
const FULL: BlobbiStats = { hunger: 100, happiness: 100, health: 100, hygiene: 100, energy: 100 };
|
||||
const LOW: BlobbiStats = { hunger: 25, happiness: 25, health: 25, hygiene: 25, energy: 25 };
|
||||
|
||||
function effectOf(id: string) {
|
||||
const item = getShopItemById(id);
|
||||
if (!item?.effect) throw new Error(`Item or effect not found: ${id}`);
|
||||
return item.effect;
|
||||
}
|
||||
|
||||
// ─── Food items ───────────────────────────────────────────────────────────────
|
||||
|
||||
describe('food item effects', () => {
|
||||
it('Apple restores +25 hunger (1 baby segment)', () => {
|
||||
const effect = effectOf('food_apple');
|
||||
expect(effect.hunger).toBe(25);
|
||||
expect(effect.energy).toBe(5);
|
||||
expect(effect.hygiene).toBe(-2);
|
||||
});
|
||||
|
||||
it('Burger restores +45 hunger with multi-stat bonus', () => {
|
||||
const effect = effectOf('food_burger');
|
||||
expect(effect.hunger).toBe(45);
|
||||
expect(effect.happiness).toBe(10);
|
||||
expect(effect.energy).toBe(8);
|
||||
expect(effect.hygiene).toBe(-8);
|
||||
});
|
||||
|
||||
it('Sushi restores +35 hunger and +10 health', () => {
|
||||
const effect = effectOf('food_sushi');
|
||||
expect(effect.hunger).toBe(35);
|
||||
expect(effect.health).toBe(10);
|
||||
expect(effect.hygiene).toBe(-5);
|
||||
});
|
||||
|
||||
it('Cake is happiness-focused food (+25 hunger, +30 happiness)', () => {
|
||||
const effect = effectOf('food_cake');
|
||||
expect(effect.hunger).toBe(25);
|
||||
expect(effect.happiness).toBe(30);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Toy items ────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('toy item effects', () => {
|
||||
it('Ball is basic toy (+25 happiness, costs energy and hygiene)', () => {
|
||||
const effect = effectOf('toy_ball');
|
||||
expect(effect.happiness).toBe(25);
|
||||
expect(effect.energy).toBe(-10);
|
||||
expect(effect.hygiene).toBe(-5);
|
||||
});
|
||||
|
||||
it('Teddy Bear is premium toy with low energy cost', () => {
|
||||
const effect = effectOf('toy_teddy');
|
||||
expect(effect.happiness).toBe(45);
|
||||
expect(effect.energy).toBe(-5);
|
||||
});
|
||||
|
||||
it('Teddy costs less energy than Ball', () => {
|
||||
const teddy = effectOf('toy_teddy');
|
||||
const ball = effectOf('toy_ball');
|
||||
// Teddy is premium — more happiness, less energy cost
|
||||
expect(teddy.happiness!).toBeGreaterThan(ball.happiness!);
|
||||
expect(teddy.energy!).toBeGreaterThan(ball.energy!); // -5 > -10
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Medicine items ───────────────────────────────────────────────────────────
|
||||
|
||||
describe('medicine item effects', () => {
|
||||
it('Bandage restores +25 health (1 baby segment)', () => {
|
||||
expect(effectOf('med_bandage').health).toBe(25);
|
||||
});
|
||||
|
||||
it('Vitamins restore health and energy', () => {
|
||||
const effect = effectOf('med_vitamins');
|
||||
expect(effect.health).toBe(25);
|
||||
expect(effect.energy).toBe(5);
|
||||
});
|
||||
|
||||
it('Health Elixir is premium (+75 health, +20 happiness, +10 energy)', () => {
|
||||
const effect = effectOf('med_elixir');
|
||||
expect(effect.health).toBe(75);
|
||||
expect(effect.happiness).toBe(20);
|
||||
expect(effect.energy).toBe(10);
|
||||
});
|
||||
|
||||
it('Super Medicine has happiness tradeoff', () => {
|
||||
const effect = effectOf('med_super');
|
||||
expect(effect.health).toBe(50);
|
||||
expect(effect.happiness).toBe(-10);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Hygiene items ────────────────────────────────────────────────────────────
|
||||
|
||||
describe('hygiene item effects', () => {
|
||||
it('Soap restores +25 hygiene (1 baby segment)', () => {
|
||||
const effect = effectOf('hyg_soap');
|
||||
expect(effect.hygiene).toBe(25);
|
||||
expect(effect.happiness).toBeUndefined();
|
||||
});
|
||||
|
||||
it('Bubble Bath restores +70 hygiene and +25 happiness', () => {
|
||||
const effect = effectOf('hyg_bubble');
|
||||
expect(effect.hygiene).toBe(70);
|
||||
expect(effect.happiness).toBe(25);
|
||||
});
|
||||
|
||||
it('Shampoo is mid-tier (+50 hygiene, +10 happiness)', () => {
|
||||
const effect = effectOf('hyg_shampoo');
|
||||
expect(effect.hygiene).toBe(50);
|
||||
expect(effect.happiness).toBe(10);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Direct actions ───────────────────────────────────────────────────────────
|
||||
|
||||
describe('direct action effects', () => {
|
||||
it('play_music gives +15 happiness', () => {
|
||||
expect(DIRECT_ACTION_HAPPINESS_EFFECTS.play_music).toBe(15);
|
||||
});
|
||||
|
||||
it('sing gives +20 happiness', () => {
|
||||
expect(DIRECT_ACTION_HAPPINESS_EFFECTS.sing).toBe(20);
|
||||
});
|
||||
|
||||
it('sing is stronger than play_music', () => {
|
||||
expect(DIRECT_ACTION_HAPPINESS_EFFECTS.sing).toBeGreaterThan(
|
||||
DIRECT_ACTION_HAPPINESS_EFFECTS.play_music,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── applyItemEffects clamping ────────────────────────────────────────────────
|
||||
|
||||
describe('applyItemEffects clamping', () => {
|
||||
it('stats clamp to 100 when effect would exceed', () => {
|
||||
const result = applyItemEffects(FULL, effectOf('food_apple'));
|
||||
expect(result.hunger).toBe(100);
|
||||
expect(result.energy).toBe(100);
|
||||
expect(result.hygiene).toBe(98); // 100 + (-2) = 98
|
||||
});
|
||||
|
||||
it('stats clamp to 1 when effect would go below', () => {
|
||||
const result = applyItemEffects(
|
||||
{ hunger: 1, happiness: 1, health: 1, hygiene: 1, energy: 1 },
|
||||
effectOf('toy_ball'),
|
||||
);
|
||||
// happiness: 1 + 25 = 26
|
||||
// energy: 1 + (-10) = clamped to 1
|
||||
// hygiene: 1 + (-5) = clamped to 1
|
||||
expect(result.happiness).toBe(26);
|
||||
expect(result.energy).toBe(1);
|
||||
expect(result.hygiene).toBe(1);
|
||||
});
|
||||
|
||||
it('Bubble Bath from low hygiene restores substantially', () => {
|
||||
const result = applyItemEffects(LOW, effectOf('hyg_bubble'));
|
||||
// hygiene: 25 + 70 = 95
|
||||
// happiness: 25 + 25 = 50
|
||||
expect(result.hygiene).toBe(95);
|
||||
expect(result.happiness).toBe(50);
|
||||
});
|
||||
});
|
||||
@@ -14,7 +14,7 @@ export const BLOBBI_SHOP_ITEMS: ShopItem[] = [
|
||||
type: 'food',
|
||||
price: 10,
|
||||
icon: '🍎',
|
||||
effect: { hunger: 15, hygiene: -2, energy: 5 },
|
||||
effect: { hunger: 25, hygiene: -2, energy: 5 },
|
||||
status: 'live',
|
||||
},
|
||||
{
|
||||
@@ -23,7 +23,7 @@ export const BLOBBI_SHOP_ITEMS: ShopItem[] = [
|
||||
type: 'food',
|
||||
price: 25,
|
||||
icon: '🍔',
|
||||
effect: { hunger: 40, happiness: 10, hygiene: -8, energy: 8 },
|
||||
effect: { hunger: 45, happiness: 10, hygiene: -8, energy: 8 },
|
||||
status: 'live',
|
||||
},
|
||||
{
|
||||
@@ -32,7 +32,7 @@ export const BLOBBI_SHOP_ITEMS: ShopItem[] = [
|
||||
type: 'food',
|
||||
price: 50,
|
||||
icon: '🎂',
|
||||
effect: { hunger: 20, happiness: 30, hygiene: -10, energy: 10 },
|
||||
effect: { hunger: 25, happiness: 30, hygiene: -10, energy: 10 },
|
||||
status: 'live',
|
||||
},
|
||||
{
|
||||
@@ -41,7 +41,7 @@ export const BLOBBI_SHOP_ITEMS: ShopItem[] = [
|
||||
type: 'food',
|
||||
price: 35,
|
||||
icon: '🍕',
|
||||
effect: { hunger: 35, happiness: 15, hygiene: -9, energy: 10 },
|
||||
effect: { hunger: 40, happiness: 15, hygiene: -9, energy: 10 },
|
||||
status: 'live',
|
||||
},
|
||||
{
|
||||
@@ -50,7 +50,7 @@ export const BLOBBI_SHOP_ITEMS: ShopItem[] = [
|
||||
type: 'food',
|
||||
price: 45,
|
||||
icon: '🍣',
|
||||
effect: { hunger: 30, health: 10, hygiene: -6, energy: 7 },
|
||||
effect: { hunger: 35, health: 10, hygiene: -5, energy: 7 },
|
||||
status: 'live',
|
||||
},
|
||||
|
||||
@@ -70,7 +70,7 @@ export const BLOBBI_SHOP_ITEMS: ShopItem[] = [
|
||||
type: 'toy',
|
||||
price: 60,
|
||||
icon: '🧸',
|
||||
effect: { happiness: 40, energy: -15 },
|
||||
effect: { happiness: 45, energy: -5 },
|
||||
status: 'live',
|
||||
},
|
||||
{
|
||||
@@ -90,7 +90,7 @@ export const BLOBBI_SHOP_ITEMS: ShopItem[] = [
|
||||
type: 'medicine',
|
||||
price: 40,
|
||||
icon: '💊',
|
||||
effect: { health: 20 },
|
||||
effect: { health: 25, energy: 5 },
|
||||
status: 'live',
|
||||
},
|
||||
{
|
||||
@@ -108,7 +108,7 @@ export const BLOBBI_SHOP_ITEMS: ShopItem[] = [
|
||||
type: 'medicine',
|
||||
price: 20,
|
||||
icon: '🩹',
|
||||
effect: { health: 15 },
|
||||
effect: { health: 25 },
|
||||
status: 'live',
|
||||
},
|
||||
{
|
||||
@@ -117,7 +117,7 @@ export const BLOBBI_SHOP_ITEMS: ShopItem[] = [
|
||||
type: 'medicine',
|
||||
price: 150,
|
||||
icon: '🧪',
|
||||
effect: { health: 80, happiness: 20, energy: 10 },
|
||||
effect: { health: 75, happiness: 20, energy: 10 },
|
||||
status: 'live',
|
||||
},
|
||||
{
|
||||
@@ -146,7 +146,7 @@ export const BLOBBI_SHOP_ITEMS: ShopItem[] = [
|
||||
type: 'hygiene',
|
||||
price: 15,
|
||||
icon: '🧼',
|
||||
effect: { hygiene: 30 },
|
||||
effect: { hygiene: 25 },
|
||||
status: 'live',
|
||||
},
|
||||
{
|
||||
@@ -164,7 +164,7 @@ export const BLOBBI_SHOP_ITEMS: ShopItem[] = [
|
||||
type: 'hygiene',
|
||||
price: 40,
|
||||
icon: '🛁',
|
||||
effect: { hygiene: 60, happiness: 20 },
|
||||
effect: { hygiene: 70, happiness: 25 },
|
||||
status: 'live',
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user