import type { AptitudeKey } from "./types"; // 적성 레벨별 초당 작업량 (기획서 4.4) — BASE_RATE로 전체 속도 스케일 조정 export const APTITUDE_RATE = [0, 1.0, 2.2, 4.8, 10.0, 21.0, 45.0]; export const BASE_RATE = 0.2; // 품종별 스프라이트 폴더 (public/assets/cats/{폴더}/). 미등록 품종은 기본(오렌지 태비) 사용 export const CAT_SPRITE_DIR: Record = { korean_shorthair: "korean_shorthair", munchkin: "munchkin", norwegian_forest: "norwegian_forest", bengal: "bengal", russian_blue: "russian_blue", persian: "persian", sphynx: "sphynx", maine_coon: "maine_coon", }; export type CatFrame = "idle" | "walk1" | "walk2" | "work" | "cook" | "sleep"; export function catSprite(breed: string, frame: CatFrame): string { const dir = CAT_SPRITE_DIR[breed]; return dir ? `/assets/cats/${dir}/${frame}.png` : `/assets/cats/${frame}.png`; } // 건물 일러스트 (public/assets/buildings/) export const BUILDING_IMG: Record = { construction: "/assets/buildings/construction.png", hall: "/assets/buildings/hall.png", dock: "/assets/buildings/dock.png", lumberyard: "/assets/buildings/lumberyard.png", quarry: "/assets/buildings/quarry.png", kitchen: "/assets/buildings/kitchen.png", cat_house: "/assets/buildings/cat_house.png", sun_deck: "/assets/buildings/sun_deck.png", market: "/assets/buildings/market.png", warehouse: "/assets/buildings/warehouse.png", }; // 자연 소품 일러스트 (public/assets/nature/) export const NATURE_IMG: Record = { pine: "/assets/nature/pine.png", leafy: "/assets/nature/leafy.png", pine_small: "/assets/nature/pine_small.png", daisy: "/assets/nature/daisy.png", tulip: "/assets/nature/tulip.png", wheat: "/assets/nature/wheat.png", mushroom: "/assets/nature/mushroom.png", rock: "/assets/nature/rock.png", bush: "/assets/nature/bush.png", }; // 아이콘 일러스트 — 배지·버블·HUD (public/assets/icons/) export const ICON_IMG: Record = { fish: "/assets/icons/fish.png", wood: "/assets/icons/wood.png", stone: "/assets/icons/stone.png", meal: "/assets/icons/meal.png", coin: "/assets/icons/coin.png", hammer: "/assets/icons/hammer.png", sweat: "/assets/icons/sweat.png", plate: "/assets/icons/plate.png", sleep: "/assets/icons/sleep.png", anger: "/assets/icons/anger.png", pan: "/assets/icons/pan.png", heart: "/assets/icons/heart.png", }; // 적성별 도구 일러스트 (public/assets/tools/) export const TOOL_IMG: Record = { fishing: "/assets/tools/fishing.png", logging: "/assets/tools/logging.png", mining: "/assets/tools/mining.png", foraging: "/assets/tools/foraging.png", cooking: "/assets/tools/cooking.png", crafting: "/assets/tools/crafting.png", grooming: "/assets/tools/grooming.png", }; export const APTITUDE_LABEL: Record = { fishing: "낚시", logging: "벌목", mining: "채굴", foraging: "채집", cooking: "요리", crafting: "제작", grooming: "손질", }; export interface BreedDef { key: string; name: string; emoji: string; rarity: number; // 별 개수 weight: number; // 추첨 가중치 bias: AptitudeKey[]; // 적성 편향 aptitudeCap: number; appetiteMod: number; } // 기획서 4.2 품종표 export const BREEDS: BreedDef[] = [ { key: "korean_shorthair", name: "코리안 숏헤어", emoji: "🐱", rarity: 1, weight: 35, bias: [], aptitudeCap: 5, appetiteMod: -1 }, { key: "munchkin", name: "먼치킨", emoji: "😺", rarity: 1, weight: 25, bias: ["foraging", "grooming"], aptitudeCap: 5, appetiteMod: 0 }, { key: "norwegian_forest", name: "노르웨이 숲", emoji: "🐈", rarity: 2, weight: 15, bias: ["logging", "mining"], aptitudeCap: 5, appetiteMod: 1 }, { key: "bengal", name: "뱅갈", emoji: "🐆", rarity: 2, weight: 10, bias: ["fishing", "foraging"], aptitudeCap: 5, appetiteMod: 0 }, { key: "russian_blue", name: "러시안 블루", emoji: "🐈‍⬛", rarity: 2, weight: 8, bias: ["crafting", "cooking"], aptitudeCap: 5, appetiteMod: 0 }, { key: "persian", name: "페르시안", emoji: "😸", rarity: 3, weight: 4, bias: ["cooking", "grooming"], aptitudeCap: 5, appetiteMod: 0 }, { key: "sphynx", name: "스핑크스", emoji: "🐯", rarity: 3, weight: 2, bias: ["crafting", "mining"], aptitudeCap: 5, appetiteMod: 0 }, { key: "maine_coon", name: "메인쿤", emoji: "🦁", rarity: 4, weight: 1, bias: ["logging", "mining"], aptitudeCap: 6, appetiteMod: 2 }, ]; export interface PassiveDef { key: string; name: string; kind: "positive" | "negative" | "tradeoff"; desc: string; speedMul: number; // 1단계에서는 작업 속도 효과만 적용 } // 기획서 5장 중 1단계에서 의미 있는 패시브 위주 export const PASSIVES: PassiveDef[] = [ { key: "diligent", name: "부지런함", kind: "positive", desc: "작업 속도 +25%", speedMul: 1.25 }, { key: "sturdy", name: "튼튼함", kind: "positive", desc: "체력 소모 -30%", speedMul: 1 }, { key: "small_eater", name: "소식가", kind: "positive", desc: "식량 소모 -30%", speedMul: 1 }, { key: "lucky", name: "복덩이", kind: "positive", desc: "확률적으로 냥코인 획득", speedMul: 1 }, { key: "lazy", name: "게으름뱅이", kind: "negative", desc: "작업 속도 -30%", speedMul: 0.7 }, { key: "big_eater", name: "대식가", kind: "negative", desc: "식량 소모 +50%", speedMul: 1 }, { key: "workaholic", name: "일중독", kind: "tradeoff", desc: "작업 속도 +60%, 행복도 감소 +40%", speedMul: 1.6 }, { key: "optimist", name: "낙천적", kind: "positive", desc: "행복도 감소 속도 -40%", speedMul: 1 }, { key: "picky", name: "까다로움", kind: "negative", desc: "행복도 감소 속도 +50%", speedMul: 1 }, { key: "popular", name: "인기쟁이", kind: "positive", desc: "같은 건물 동료의 행복도가 서서히 상승", speedMul: 1 }, ]; // ---------- 마을 보드 ---------- import type { BuildingType } from "./types"; export const BOARD_COLS = 15; export const BOARD_ROWS = 15; export const BOARD_VERSION = 2; // 보드 개편 시 증가 → 세이브 위치 리셋 export const HALL_TILE = { i: 6, j: 3 }; export const TREE_TILES: Array<{ i: number; j: number }> = [ { i: 0, j: 9 }, { i: 1, j: 11 }, { i: 3, j: 12 }, { i: 0, j: 13 }, { i: 1, j: 8 }, { i: 4, j: 13 }, { i: 11, j: 1 }, { i: 2, j: 0 }, ]; // 강은 동쪽 2열 export const isWaterTile = (i: number) => i >= BOARD_COLS - 2; // 건물 풋프린트 (타일 단위, pos는 좌상단 앵커) export const FOOTPRINTS: Record = { hall: { w: 3, h: 3 }, dock: { w: 2, h: 2 }, lumberyard: { w: 2, h: 2 }, quarry: { w: 2, h: 2 }, kitchen: { w: 2, h: 2 }, cat_house: { w: 2, h: 2 }, sun_deck: { w: 2, h: 2 }, market: { w: 2, h: 2 }, warehouse: { w: 2, h: 2 }, }; export const footprintOf = (type: string) => FOOTPRINTS[type] ?? { w: 1, h: 1 }; // 배치 규칙: 부두는 물 위에만 export type PlacementRule = "land" | "onWater"; export const PLACEMENT_RULES: Partial> = { dock: "onWater", }; // 보드 v2 기본 위치 (마이그레이션 시 재배치) export const DEFAULT_BUILDING_POS: Record = { dock_1: { i: 13, j: 6 }, lumberyard_1: { i: 2, j: 9 }, kitchen_1: { i: 8, j: 8 }, quarry_1: { i: 1, j: 2 }, }; // ---------- 성장 ---------- export const EXP_PER_LEVEL = 90; // 레벨업 필요 경험치 = 레벨 × 90 export const APT_POINT_EVERY = 3; // 3레벨마다 적성 포인트 1 // ---------- 건설 가능한 건물 ---------- export interface BuildDef { id: string; // 건물 인스턴스 id (단일 건설) type: | "dock" | "lumberyard" | "quarry" | "kitchen" | "cat_house" | "sun_deck" | "market" | "warehouse"; kind?: "work" | "passive"; // passive: 고양이 배치 없음 (기본 work) name: string; emoji: string; aptitude: AptitudeKey; resource?: "fish" | "wood" | "stone"; maxSlots: number; cost: Partial>; reqHallLv: number; buildTime: number; // 건설 소요 시간 (초) desc: string; } export const BUILD_DEFS: BuildDef[] = [ { id: "dock_1", type: "dock", name: "부두", emoji: "⚓", aptitude: "fishing", resource: "fish", maxSlots: 3, cost: {}, reqHallLv: 1, buildTime: 15, desc: "낚시 적성 고양이가 생선을 잡습니다. 강물 위에 지어요. 첫 건물이라 무료!", }, { id: "lumberyard_1", type: "lumberyard", name: "벌목장", emoji: "🪓", aptitude: "logging", resource: "wood", maxSlots: 3, cost: { fish: 30 }, reqHallLv: 1, buildTime: 30, desc: "벌목 적성 고양이가 목재를 모읍니다. 목재는 모든 건설의 기본!", }, { id: "kitchen_1", type: "kitchen", name: "부엌", emoji: "🍳", aptitude: "cooking", maxSlots: 2, cost: { wood: 60 }, reqHallLv: 1, buildTime: 60, desc: "요리 적성 고양이가 생선으로 요리를 만들고, 배고픈 고양이들이 알아서 먹으러 옵니다.", }, { id: "quarry_1", type: "quarry", name: "채석장", emoji: "⛏️", aptitude: "mining", resource: "stone", maxSlots: 3, cost: { wood: 120 }, reqHallLv: 2, buildTime: 120, desc: "채굴 적성 고양이가 돌을 캡니다. 돌은 회관 증축과 중급 건물에 필요합니다.", }, { id: "cat_house_1", type: "cat_house", name: "고양이 집", emoji: "🛖", kind: "passive", aptitude: "grooming", maxSlots: 0, cost: { wood: 100, stone: 20 }, reqHallLv: 3, buildTime: 90, desc: "지친 고양이가 푹 쉬는 보금자리. 휴식 회복이 빨라지고(분당 +30) 행복도 하락을 막아줍니다.", }, { id: "sun_deck_1", type: "sun_deck", name: "햇볕 데크", emoji: "🌞", kind: "passive", aptitude: "grooming", maxSlots: 0, cost: { wood: 120, stone: 40 }, reqHallLv: 3, buildTime: 90, desc: "따끈한 햇살 아래 뒹굴뒹굴. 설치하면 마을에 💗 행복도가 눈뜨고, 회복 속도도 1.5배!", }, { id: "market_1", type: "market", name: "장터", emoji: "🏪", kind: "passive", aptitude: "crafting", maxSlots: 0, cost: { wood: 120 }, reqHallLv: 2, buildTime: 60, desc: "자원과 요리를 냥코인으로 바꿔줍니다. 판매 품목을 정하면 분당 5개씩 자동 판매!", }, { id: "warehouse_1", type: "warehouse", name: "창고", emoji: "📦", kind: "passive", aptitude: "crafting", maxSlots: 0, cost: { wood: 200, stone: 100 }, reqHallLv: 3, buildTime: 120, desc: "자원 보관 상한을 +500 늘려줍니다. 꽉 찬 창고는 생산을 멈추게 해요.", }, ]; // ---------- 꾸미기 (바닥 장식) ---------- export interface DecoDef { key: string; name: string; cost: Partial>; img: string; } export const DECO_DEFS: DecoDef[] = [ { key: "dirt", name: "흙마당", cost: { wood: 10 }, img: "/assets/deco/dirt.png" }, { key: "boulders", name: "바위 터", cost: { stone: 15 }, img: "/assets/deco/boulders.png" }, { key: "pavement", name: "돌바닥", cost: { stone: 25 }, img: "/assets/deco/pavement.png" }, { key: "deck", name: "나무 데크", cost: { wood: 30 }, img: "/assets/deco/deck.png" }, { key: "pebbles", name: "자갈길", cost: { stone: 12 }, img: "/assets/deco/pebbles.png" }, { key: "stump", name: "그루터기 쉼터", cost: { wood: 20 }, img: "/assets/deco/stump.png" }, { key: "flower_grass", name: "꽃잔디", cost: { wood: 15 }, img: "/assets/deco/flower_grass.png" }, { key: "plaza", name: "돌광장", cost: { stone: 30 }, img: "/assets/deco/plaza.png" }, { key: "boardwalk", name: "나무길", cost: { wood: 25 }, img: "/assets/deco/boardwalk.png" }, ]; // ---------- 요리 (부엌) ---------- export interface RecipeDef { key: string; name: string; emoji: string; input: Partial>; // 재료 (추후 획득처 확장) work: number; // 요리 1개당 필요 작업량 fullness: number; // 먹었을 때 포만도 회복량 reqHallLv: number; // 해금 조건: 회관 레벨 reqKitchenLv: number; // 해금 조건: 부엌 레벨 } export const RECIPES: RecipeDef[] = [ { key: "grilled_fish", name: "물고기 구이", emoji: "🍖", input: { fish: 2 }, work: 3, fullness: 40, reqHallLv: 1, reqKitchenLv: 1, }, { key: "smoked_fish", name: "훈제 생선", emoji: "🐟", input: { fish: 3 }, work: 6, fullness: 70, reqHallLv: 3, reqKitchenLv: 2, // 아직 잠김 — 회관·부엌 증축으로 해금 예정 }, ]; export const KITCHEN_MEAL_CAPS = [10, 15]; // 부엌 레벨별 선반 보관 수 export const KITCHEN_UPGRADE_COST = { wood: 150, stone: 60 }; // Lv1→2 export const mealCap = (kitchenLv: number) => KITCHEN_MEAL_CAPS[kitchenLv - 1] ?? KITCHEN_MEAL_CAPS[KITCHEN_MEAL_CAPS.length - 1]; export const EAT_THRESHOLD = 65; // 이 포만도 이하면 먹으러 감 export const FULLNESS_DECAY_PER_SEC = 0.03; // × 식욕 수치 // ---------- 경제 (Phase C) ---------- // 판매가 (개당 냥코인) export const SELL_PRICES: Record = { fish: 1, wood: 2, stone: 3, }; // 요리 판매가 (레시피 키 기준) export const MEAL_PRICES: Record = { grilled_fish: 4, smoked_fish: 10, }; export const MARKET_RATE_PER_SEC = 5 / 60; // 분당 5개 판매 export const BASE_STORAGE = 500; // 자원별 기본 보관 상한 export const WAREHOUSE_STORAGE = 500; // 창고 1채당 추가 상한 export const BUILDING_STORE_CAP = 400; // 건물별 산출물 적재 한도 (가득 차면 생산 정지) // ↑ 150이던 시절: 고레벨 3마리 배치 시 1~2분 만에 가득 차 상시 수거 노동이 됨. // 400이면 최대 편성 기준 ~10분 여유, 오프라인 복귀 보상도 의미 있는 양이 됨. export const ADOPT_FREE_CATS = 2; // 처음 2마리는 무료 export const ADOPT_BASE_COST = 100; export const ADOPT_COST_PER_CAT = 50; export const REROLL_COST = 30; // 입양 재추첨 export const RUSH_COST_PER_10S = 1; // 즉시 완공: 남은 10초당 1코인 // ---------- 체력·행복도 (Phase B) ---------- export const ENERGY_DRAIN_PER_SEC = 2 / 60; // 작업 중 체력 소모 (분당 2) export const REST_RECOVER_PER_SEC = 20 / 60; // 휴식 회복 (분당 20) export const HOUSE_RECOVER_PER_SEC = 30 / 60; // 고양이 집 휴식 회복 (분당 30) export const IDLE_RECOVER_PER_SEC = 10 / 60; // 배회 중 자연 회복 (분당 10) export const CAT_HOUSE_CAPACITY = 4; // 고양이 집 1채당 휴식 정원 export const STRIKE_THRESHOLD = 20; // 행복도가 이 밑이면 파업 export const STRIKE_RESUME = 40; // 이 이상 회복되면 복귀 // ---------- 마을 회관 증축 ---------- export interface HallLevelDef { capacity: number; cost: Partial>; reqCat?: { level: number; count: number }; reqBuildings?: Array<{ type: "quarry" | "kitchen"; level?: number; label: string }>; locked?: string; // 아직 구현 안 된 선행 요소 안내 } export const HALL_LEVELS: Record = { 2: { capacity: 5, cost: { wood: 100, fish: 80 }, reqCat: { level: 3, count: 1 } }, 3: { capacity: 8, cost: { wood: 250, stone: 150 }, reqCat: { level: 5, count: 2 }, reqBuildings: [ { type: "quarry", label: "채석장 건설" }, { type: "kitchen", level: 2, label: "부엌 Lv2" }, ], }, 4: { capacity: 12, cost: { stone: 400 }, reqCat: { level: 8, count: 3 }, locked: "목공소(목판)가 추가된 뒤 해금됩니다", }, }; export const CAT_NAMES = [ "치즈", "나비", "콩이", "보리", "두부", "마루", "모찌", "호두", "감자", "앙꼬", "밤이", "구름", "초코", "우유", "깨비", "솜이", "달이", "별이", "쿠키", "만두", "생강", "참깨", "누룽지", "조약돌", ];