요리와 음식 효과
01 게임
이 문서 안에서
부엌에서 나온 요리 19종의 효과와, 틱 없는 소급 계산 구조에서 지속형 버프가 어떻게 정직하게 적분되는지를 정합니다. 요리는 주민 상세 카드에서 대상을 지정해 먹이는 소비품이고 먹으면 사라집니다.
한 줄 요약 — 버프는 "지금의 배율"이 아니라 "구간에 대한 배율의 적분"입니다 — 그래야 먹기 전 시간이 늘지 않습니다.
1. 왜 구간 적분이 필요했나
주머니 적립량은 min(capacity, floor((now - gatheredAt) / msPerUnit))로 파생됩니다. 일시 배율을 단순히 곱하면 먹기 전 구간까지 소급 팽창합니다. 해법: 버프가 from·until을 들고, 경과 구간을 버프 경계로 잘라 배율을 적분해 "유효 작업 시간"을 구합니다. msPerUnit은 건드리지 않고 시계를 빨리 돌리는 모델입니다.
성질 셋: 소급 없음(from 이전은 ×1), 단조성(파생값은 감소 불가), 오프라인 정확성(만료 버프는 살아 있던 만큼만 기여).
2. 적분의 실제 계산
buffedWorkMs(from, to, buffs, kind, role) 절차: kind·창 겹침·역할 필터 → 빈 목록이면 to - from 반환 → 모든 경계 정렬해 구간 생성 → 구간마다 배율 곱 × 길이 합산. 활성 창은 반열림 [from, until).
계산 예 — Lv.1 농부 8,000ms/개, 용량 100, 3분 시점에 포자 수프(×1.25, 30분) 투여, 8분에 확인:
| 구간 | 길이 | 배율 | 유효 시간 |
|---|---|---|---|
| 0~3분 | 3분 | ×1 | 3분 |
| 3~8분 | 5분 | ×1.25 | 6.25분 |
| 합계 | 8분 | 9.25분 = 555,000ms |
floor(555,000/8,000) = 69개. 버프 없으면 60개, 소급 방식이면 75개. 차이 6개가 이 문서의 이유입니다.
pouch_cap만 적분이 아니라 구간 최댓값(maxBuffMultiplier)을 씁니다. 만료 시 용량이 줄어 보이는 문제를 막기 위함입니다.
전리품 보너스(loot_bonus)는 커버리지 비례: buffCoverageMs / elapsed 비율만큼 추가 전리품 인정. 버프 없으면 rng 미접촉.
3. 효과 7종
| kind | 파라미터 | 지속/즉시 | 저장 위치 | 읽는 곳 |
|---|---|---|---|---|
gather_ | mul, minutes, roles? | 지속 | 주민 | gather.ts gatheredAmount (적분) |
pouch_cap | mul, minutes | 지속 | 주민 | gather.ts villagerPouchCapacity (구간 최댓값) |
warrior_atk | mul, minutes | 지속 | 주민(전사) | battle- 피해 누적 (적분) |
loot_ | extra, minutes | 지속 | 주민(전사) | battle- 전리품 (커버리지 비례) |
damage_cut | pct, minutes | 지속 | 주민(전사) | battle- 피격 감소 |
harvest | minutes | 즉시 | — | 인벤토리 직접 가산 |
battle_ | seconds | 즉시 | — | 전사 본인의 battle. |
heal | pct | 즉시 | — | 전사 본인의 hp 복원 |
loot_bonus의 extra는 TimedBuff.mul 칸에 담기며 그 kind에서는 추가 드랍 개수(정수)를 뜻합니다. 버프의 source 필드에는 음식 키가 들어가 칩 아이콘·행 id에 쓰입니다.
저장 위치: 모든 버프는 `VillagerState.buffs`에 저장됩니다(개인). 왕국 단위 버프(GameSave.buffs)는 삭제되었습니다 — 파티가 사라지면서 버프를 소유할 왕국 스코프가 없어졌기 때문입니다. INTEGRATED_BUFF_KINDS는 gather_rate·pouch_cap·warrior_atk·loot_bonus 넷입니다.
4. 요리 19종
두 축(자원·힘)으로 편성되며 같은 축의 두 요리가 크기만 다른 같은 일을 하지 않습니다. 경험치 축은 v26에서 삭제 — 10n(n+10) 골드 곡선과 60n 경험치의 비용 차이가 끝없이 벌어지는 구조였고, 경험치 바를 보여 주는 UI도 없었습니다. 레벨은 골드로만 오릅니다(성장과 레벨).
연회 모둠(feast_platter)·장터 파이(market_pie)·보물 타르트(treasure_tart)는 부엌에서 만들지만 먹는 요리가 아니라 축제 공물입니다. 효과를 한 사람에게 돌릴 수 없는 왕국 단위 콘텐츠이므로 개인 음식 카탈로그에서 빠졌고, data/item-effects.json에 효과가 없습니다. 판매는 가능합니다.
티어3(herb_tea·dragon_roast·dragon_broth)은 인장 요리사 전용, 재료도 인장 주민 주머니에서만 나옵니다.
즉시형 계산:
harvest:max(1, floor(minutes × 60000 / villagerGatherMs(target)))—gatheredAt미변경, 장비·작업지 배율 포함,gather_rate버프만 제외.battle_damage:round(warriorAtkOf(eater) × seconds × 1000 / BATTLE_HIT_MS)—BATTLE_HIT_MS = 4,000이므로 돌격 건량(60초) = 그 전사의 ATK ×15.heal: 전사 본인의maxHp대비pct%를 복원하고, 쓰러진 전사(defeatedAt존재)를 일으킵니다.harvest는resourcesGathered카운터를 밀어 미션 진행. 어떤 요리든foodEaten+1.
5. 대상과 거절 사유
| target | 뜻 | 해당 요리 |
|---|---|---|
villager | 아무 주민 1명 | 없음 (경험치와 함께 비었음) |
gatherer | 주머니를 채우는 주민 | 빵, 생선 스튜, 꽉찬 도시락, 포자 수프, 마녀의 약차, 용의 탕약, 달걀 타르트, 우유 푸딩, 추수 죽, 사냥꾼 전골, 약초꾼 샐러드 |
miner | 광부 | 젤리 푸딩, 광부 꼬치 |
warrior | 전사 1명 | 불꽃 전골, 돌격 건량, 독초 절임, 용린 스테이크, 뼈 육수, 수호 전골 |
모든 요리는 주민 상세 카드에서 먹입니다. 가방(인벤토리) 화면에 사용 버튼은 없습니다 — 대상이 사람이므로 사람의 화면에서 고르는 것이 올바른 진입점이고, 가방은 물건이 있는 곳이지 물건을 쓰는 곳이 아닙니다.
consumeFood는 소비 전에 자격을 전부 판정하고 거절 시 가방 불변. 0이 된 키는 삭제합니다. 주민 상세의 음식 목록도 같은 조건으로 거절될 행을 미리 숨깁니다.
6. 중첩과 만료
슬롯 = kind + roles 정렬 문자열. 같은 슬롯에 최대 하나.
| 상황 | 결과 |
|---|---|
| 같은 슬롯 없음 | 새로 추가. from = now, until = now + min(duration, cap) |
| 더 강한 배율 | 교체. from = now, until = now + min(max(duration, remaining), cap) |
| 같거나 약한 배율 | 시간만 연장. until = now + min(remaining + duration, cap) |
MAX_BUFF_STACK_FACTOR = 2 — 재섭취 최대 지속은 기본의 2배.
| 요리 | 기본 지속 | 스택 상한 |
|---|---|---|
| 달걀 타르트 | 1,440분 | 2,880분 |
| 꽉찬 도시락 | 480분 | 960분 |
| 우유 푸딩 | 120분 | 240분 |
| 마녀의 약차 | 45분 | 90분 |
| 포자 수프 | 30분 | 60분 |
| 독초 절임 | 30분 | 60분 |
| 용린 스테이크 | 30분 | 60분 |
| 추수 죽 | 30분 | 60분 |
| 사냥꾼 전골 | 30분 | 60분 |
| 약초꾼 샐러드 | 30분 | 60분 |
| 수호 전골 | 30분 | 60분 |
| 광부 꼬치 | 25분 | 50분 |
| 불꽃 전골 | 20분 | 40분 |
정리(prune): 적분 kind → until > min(now, settledUpTo), 즉시 kind → until > now. 수거 함수가 적립을 가방에 넣은 뒤에 prune을 부르는 순서 자체가 규칙입니다.
7. 밸런스 상한
채집: 광부는 광부 꼬치(×1.6)와 마녀의 약차(×1.5)가 다른 슬롯이라 동시 적용, 음식만 ×2.4. 장비까지 ×3.168, 각인 3단계까지 ×4.22. 이 빌드에서 도달 가능한 값은 ×2.88(장비) / ×3.456(각인). villagerGatherMs 2,000ms 하한은 장비·특성만 붙잡고 요리는 붙잡지 않습니다.
공격력: warrior_atk 두 요리(×1.25, ×1.6)는 roles 없어 같은 슬롯 — 상한 정확히 ×1.6, 최대 60분. 버프는 전사 개인의 villager.buffs에 저장되므로 전사마다 따로 먹여야 합니다.
전리품: loot_bonus 독초 절임 하나(+1, 전사 개인).
8. 화면
주민 상세 칩: 본인 버프 전부. 라벨은 loot_bonus만 +{개수}, 나머지 +{(mul-1)×100}%. 남은 시간은 ≥60초면 분(올림), <60초면 초(올림, 최소 1).
먹은 뒤의 보고는 어디로 들어갔는지가 정합니다. harvest만 가방에 물건을 넣으므로 가방으로 들어오는 다른 모든 문과 같은 것을 씁니다 — 획득 팝업(openRewardPopup, 아이콘·개수·나온 곳). 나머지 일곱은 토스트 한 줄입니다(버프는 남은 분, 회복은 생명력, battle_damage는 피해량, gold는 이미 금화 연출이 따로 돕니다). 즉시 채집에는 토스트를 겹치지 않습니다 — 팝업이 이름과 개수를 이미 말하므로, 제작 §10이 제작 연출을 걷어낸 것과 같은 이유입니다.
효능 문구는 {n}분어치 즉시 채집입니다. "즉시 채집 5분"은 5개인지 5분인지 읽는 사람이 고르게 두었고, 실제로 나오는 개수는 그 주민의 ms/개에 달려 있어(기본 8,000ms면 37개) 문구가 개수를 약속할 수도 없습니다. 약속할 수 있는 것은 시간이고, 개수는 팝업이 답합니다.
9. 세이브와 마이그레이션
v23→v24에서 버프 배열 도입. 빈 []로 시드 — buffedWorkMs가 빈 목록에 경과 시간을 그대로 돌리므로 이전과 동일하게 동작합니다. 요리 셋(spore_soup·herb_tea·venom_pickle)의 즉시→지속 전환은 별도 마이그레이션 없음 — 소비 시점에 카탈로그에서 읽으므로 자연 적용.
어긋남과 미구현 항목은 구현 상태에 있습니다.
이어서 읽기
- 제작 — 19종의 재료와 레퍼토리·레벨 게이트
- 채집과 주머니 — 적분이 꽂히는 자리, 용량 공식과 2,000ms 하한
- 전투 —
warrior_atk적분과loot_bonus추첨이 도는 정산 루프 - 인벤토리와 판매 — 요리 판매가 사다리
- 성장과 레벨 — 요리가 살 수 없는 것, 골드 단독 레벨업 곡선