에셋 정리 + 원본 보관 + 건물 일러스트 적용
- 미사용 에셋 삭제: 구 오렌지 태비 4프레임(품종 시트로 대체), create-next-app svg - assets-src/: AI 생성 원본(품종 시트 8장·잔디·강물·도구·건물 시트) 프로젝트로 이동 - build.png 슬라이스: 어두운 글로우 배경을 밝기 기반 플러드필로 제거 → 부두(선착장+보트)·벌목장·채석장(광산)·부엌(모닥불) 4종 - 마을 캔버스·상점·패널 아이콘에 건물 일러스트 적용 (도구 아이콘은 폴백) - 배치 고스트도 건물 일러스트, 히트 영역 확장, 삭제된 루트 프레임 404 폴백 제거 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
이 커밋은 다음에 포함됨:
@@ -14,6 +14,7 @@ import {
|
||||
APTITUDE_LABEL,
|
||||
BREEDS,
|
||||
BUILD_DEFS,
|
||||
BUILDING_IMG,
|
||||
catSprite,
|
||||
HALL_LEVELS,
|
||||
KITCHEN_UPGRADE_COST,
|
||||
@@ -199,7 +200,7 @@ export default function Game() {
|
||||
{openedBuilding && (
|
||||
<SidePanel
|
||||
title={`${openedBuilding.name} Lv${openedBuilding.level}`}
|
||||
icon={TOOL_IMG[openedBuilding.aptitude]}
|
||||
icon={BUILDING_IMG[openedBuilding.type] ?? TOOL_IMG[openedBuilding.aptitude]}
|
||||
onClose={() => setPanel(null)}
|
||||
>
|
||||
<BuildingPanel
|
||||
@@ -399,9 +400,9 @@ function ShopPanel({
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={TOOL_IMG[def.aptitude]}
|
||||
src={BUILDING_IMG[def.type] ?? TOOL_IMG[def.aptitude]}
|
||||
alt={def.name}
|
||||
className={`h-12 w-12 shrink-0 object-contain ${built || !hallOk ? "opacity-40" : ""}`}
|
||||
className={`h-14 w-14 shrink-0 object-contain ${built || !hallOk ? "opacity-40" : ""}`}
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="font-jua text-[15px] text-[#3f2c14]">{def.name}</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { engine, catRate, canPlaceAt } from "@/lib/game/engine";
|
||||
import {
|
||||
BOARD_COLS,
|
||||
BOARD_ROWS,
|
||||
BUILDING_IMG,
|
||||
catSprite,
|
||||
HALL_TILE,
|
||||
TOOL_IMG,
|
||||
@@ -536,22 +537,29 @@ export default function VillageView({
|
||||
const stored = Math.floor(b.stored);
|
||||
const isKitchen = b.type === "kitchen";
|
||||
// 건물 본체 → 정보 패널
|
||||
hitRectsRef.current.push({ id: b.id, kind: "open", x: x - 30, y: y - 38, w: 60, h: 66 });
|
||||
hitRectsRef.current.push({ id: b.id, kind: "open", x: x - 44, y: y - 52, w: 88, h: 80 });
|
||||
// 배지 → 빠른 회수 (생산 건물만)
|
||||
if (!isKitchen && stored > 0)
|
||||
hitRectsRef.current.push({ id: b.id, kind: "collect", x: x - 40, y: y - 62, w: 80, h: 26 });
|
||||
sprites.push({
|
||||
y,
|
||||
draw: () => {
|
||||
shadow(x, y + 6, 24);
|
||||
const img = toolImagesRef.current.get(b.aptitude);
|
||||
if (img) {
|
||||
// 세로가 긴 일러스트 — 높이 56px 기준으로 비율 유지
|
||||
const h = 56;
|
||||
const w = (img.width / img.height) * h;
|
||||
ctx.drawImage(img, x - w / 2, y + 8 - h, w, h);
|
||||
const bImg = getImage(BUILDING_IMG[b.type]);
|
||||
if (bImg) {
|
||||
// 건물 일러스트 (자체 잔디 베이스 포함 — 그림자 불필요)
|
||||
const w = 96;
|
||||
const h = (bImg.height / bImg.width) * w;
|
||||
ctx.drawImage(bImg, x - w / 2, y + 14 - h, w, h);
|
||||
} else {
|
||||
drawEmoji(b.emoji, x, y + 8, 42);
|
||||
shadow(x, y + 6, 24);
|
||||
const img = toolImagesRef.current.get(b.aptitude);
|
||||
if (img) {
|
||||
const h = 56;
|
||||
const w = (img.width / img.height) * h;
|
||||
ctx.drawImage(img, x - w / 2, y + 8 - h, w, h);
|
||||
} else {
|
||||
drawEmoji(b.emoji, x, y + 8, 42);
|
||||
}
|
||||
}
|
||||
ctx.font = `13px ${juaFamily}`;
|
||||
ctx.textAlign = "center";
|
||||
@@ -597,14 +605,10 @@ export default function VillageView({
|
||||
? "walk1"
|
||||
: "walk2"
|
||||
: "idle";
|
||||
// 품종 스프라이트 → (cook 없으면 work) → 기본 스프라이트 순 폴백
|
||||
// 품종 스프라이트 → (cook 없으면 work) → 이모지 순 폴백
|
||||
let sprite = getImage(catSprite(cat.breed, frameName));
|
||||
if (!sprite && frameName === "cook")
|
||||
sprite = getImage(catSprite(cat.breed, "work"));
|
||||
if (!sprite)
|
||||
sprite = getImage(
|
||||
catSprite("", frameName === "cook" ? "work" : frameName),
|
||||
);
|
||||
if (sprite) {
|
||||
const sh = 42;
|
||||
const sw = (sprite.width / sprite.height) * sh;
|
||||
@@ -658,12 +662,14 @@ export default function VillageView({
|
||||
// 고스트 건물
|
||||
const gx = isoX(hover.i, hover.j);
|
||||
const gy = isoY(hover.i, hover.j) + TH / 2;
|
||||
const img = toolImagesRef.current.get(placing.aptitude);
|
||||
const gImg =
|
||||
getImage(BUILDING_IMG[placing.type]) ??
|
||||
toolImagesRef.current.get(placing.aptitude);
|
||||
ctx.globalAlpha = 0.75;
|
||||
if (img) {
|
||||
const h = 56;
|
||||
const w = (img.width / img.height) * h;
|
||||
ctx.drawImage(img, gx - w / 2, gy + 8 - h, w, h);
|
||||
if (gImg) {
|
||||
const w = 96;
|
||||
const h = (gImg.height / gImg.width) * w;
|
||||
ctx.drawImage(gImg, gx - w / 2, gy + 14 - h, w, h);
|
||||
} else {
|
||||
drawEmoji("🏗️", gx, gy + 8, 40);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,14 @@ export function catSprite(breed: string, frame: CatFrame): string {
|
||||
return dir ? `/assets/cats/${dir}/${frame}.png` : `/assets/cats/${frame}.png`;
|
||||
}
|
||||
|
||||
// 건물 일러스트 (public/assets/buildings/)
|
||||
export const BUILDING_IMG: Record<string, string> = {
|
||||
dock: "/assets/buildings/dock.png",
|
||||
lumberyard: "/assets/buildings/lumberyard.png",
|
||||
quarry: "/assets/buildings/quarry.png",
|
||||
kitchen: "/assets/buildings/kitchen.png",
|
||||
};
|
||||
|
||||
// 적성별 도구 일러스트 (public/assets/tools/)
|
||||
export const TOOL_IMG: Record<AptitudeKey, string> = {
|
||||
fishing: "/assets/tools/fishing.png",
|
||||
|
||||
새 이슈에서 참조
사용자 차단