diff --git a/autoload/save_system.gd b/autoload/save_system.gd index 115d730..b5dfe76 100644 --- a/autoload/save_system.gd +++ b/autoload/save_system.gd @@ -407,6 +407,8 @@ func _spawn_item(world_scene: Node, d: Dictionary) -> void: Vector2i(int(d.get("tile_x", 0)), int(d.get("tile_y", 0))) ) ent.quality = int(d.get("quality", 1)) as Item.Quality + ent.subtype = StringName(d.get("subtype", "")) + ent.queue_redraw() func _spawn_wall(world_scene: Node, d: Dictionary) -> void: diff --git a/scenes/entities/crop.gd b/scenes/entities/crop.gd index f47ed74..494d585 100644 --- a/scenes/entities/crop.gd +++ b/scenes/entities/crop.gd @@ -109,6 +109,11 @@ func on_harvest_tick() -> void: var it: Item = ITEM_SCENE.instantiate() get_parent().add_child(it) it.setup(item_type, 1, tile) + # Carry the crop_kind through as a visual subtype so wheat / corn / + # potato / strawberry each render distinctly, while item_type stays + # generic (TYPE_GRAIN / TYPE_VEGETABLE) for stockpile filter purposes. + it.subtype = crop_kind + it.queue_redraw() stage = Stage.TILLED stage_progress = 0 _refresh_sprite_region() diff --git a/scenes/entities/item.gd b/scenes/entities/item.gd index 5476e53..465d151 100644 --- a/scenes/entities/item.gd +++ b/scenes/entities/item.gd @@ -84,6 +84,13 @@ enum Quality { SHODDY, NORMAL, EXCELLENT, MASTERWORK, LEGENDARY } @export var stack_size: int = 1 @export var quality: Quality = Quality.NORMAL +## Visual subtype within the broader item_type. Lets multiple harvested produce +## share one storage-filter category (TYPE_GRAIN matches both wheat AND corn) +## while still rendering distinctly. Empty string = use item_type's default +## shape. Set by the spawning code (Crop.on_harvest_tick assigns "wheat"/"corn" +## /"potato"/"strawberry" based on crop_kind). +@export var subtype: StringName = &"" + var tile: Vector2i = Vector2i.ZERO ## When true the on-floor visual is suppressed; the carrying pawn renders the @@ -154,6 +161,7 @@ func to_dict() -> Dictionary: return { "class_id": &"item", "type": String(item_type), + "subtype": String(subtype), "stack_size": stack_size, "tile_x": tile.x, "tile_y": tile.y, @@ -167,6 +175,7 @@ func to_dict() -> Dictionary: static func from_dict(d: Dictionary) -> Dictionary: return { "type": StringName(d.get("type", "wood")), + "subtype": StringName(d.get("subtype", "")), "stack_size": int(d.get("stack_size", 1)), "tile_x": int(d.get("tile_x", 0)), "tile_y": int(d.get("tile_y", 0)), @@ -187,8 +196,86 @@ static func from_dict(d: Dictionary) -> Dictionary: ## etc. Atlas-backed types (wood / stone / plank / iron_ore / gold) also have ## a shape here so the carry indicator works for them — the on-floor visual ## still uses the bundle icon via the Sprite2D child path. -static func draw_item_shape(target: CanvasItem, t: StringName) -> bool: +static func draw_item_shape(target: CanvasItem, t: StringName, sub: StringName = &"") -> bool: var dark := Color(0.10, 0.07, 0.05, 0.85) # shared outline + # Subtype dispatch — lets wheat / corn / potato / strawberry render + # distinctly even though they share TYPE_GRAIN or TYPE_VEGETABLE for + # storage-filter purposes. Falls through to the type dispatch if subtype + # is unrecognised so existing items don't blank out. + match sub: + &"wheat": + # Yellow stalks with grain heads — same as default TYPE_GRAIN shape. + var stalk := Color(0.85, 0.70, 0.20) + var tie := Color(0.55, 0.35, 0.10) + target.draw_rect(Rect2(-4.0, -6.0, 1.5, 12.0), stalk) + target.draw_rect(Rect2(-0.75, -6.0, 1.5, 12.0), stalk) + target.draw_rect(Rect2(2.5, -6.0, 1.5, 12.0), stalk) + target.draw_circle(Vector2(-3.2, -5.0), 1.2, Color(0.95, 0.78, 0.25)) + target.draw_circle(Vector2(0.0, -5.5), 1.2, Color(0.95, 0.78, 0.25)) + target.draw_circle(Vector2(3.2, -5.0), 1.2, Color(0.95, 0.78, 0.25)) + target.draw_rect(Rect2(-5.0, 0.0, 10.0, 2.0), tie) + return true + &"corn": + # Corn cob — yellow body with rows of kernel dots + green husk. + var husk := Color(0.30, 0.65, 0.20) + var cob := Color(0.95, 0.82, 0.30) + var kernel_dark := Color(0.70, 0.55, 0.15) + # Husk leaves splayed out at the top. + var husk_l: PackedVector2Array = PackedVector2Array([Vector2(-4.0, -5.0), Vector2(-2.0, -2.0), Vector2(-5.0, -1.0)]) + var husk_r: PackedVector2Array = PackedVector2Array([Vector2(4.0, -5.0), Vector2(2.0, -2.0), Vector2(5.0, -1.0)]) + target.draw_colored_polygon(husk_l, husk) + target.draw_colored_polygon(husk_r, husk) + # Cob body — vertical oval/rect with rounded corners. + target.draw_rect(Rect2(-3.0, -4.0, 6.0, 10.0), cob) + target.draw_circle(Vector2(0.0, 5.5), 3.0, cob) + # Kernel dots — 2 columns × 3 rows for texture. + for y in [-2.0, 0.0, 2.0, 4.0]: + target.draw_rect(Rect2(-2.0, y, 1.5, 1.5), kernel_dark) + target.draw_rect(Rect2(0.5, y, 1.5, 1.5), kernel_dark) + # Cob outline (open at top because of husk). + target.draw_arc(Vector2(0.0, 5.5), 3.0, 0.0, PI, 12, dark, 1.0) + return true + &"potato": + # Two brown lumps with sprout-eye dots — pile of potatoes. + var skin := Color(0.62, 0.45, 0.25) + var skin_dark := Color(0.42, 0.28, 0.15) + var eye := Color(0.25, 0.15, 0.05) + # Two overlapping potato ovals. + target.draw_circle(Vector2(-2.0, 1.0), 4.0, skin) + target.draw_circle(Vector2(2.5, -0.5), 3.5, skin) + # Outline. + target.draw_arc(Vector2(-2.0, 1.0), 4.0, 0.0, TAU, 16, skin_dark, 1.0) + target.draw_arc(Vector2(2.5, -0.5), 3.5, 0.0, TAU, 12, skin_dark, 1.0) + # Eye dots. + target.draw_circle(Vector2(-3.0, 0.0), 0.7, eye) + target.draw_circle(Vector2(-0.5, 2.0), 0.6, eye) + target.draw_circle(Vector2(3.5, -1.5), 0.7, eye) + return true + &"strawberry": + # Classic red strawberry — heart-shape body with green calyx on top + # and tiny yellow seed dots scattered on the surface. + var berry := Color(0.88, 0.18, 0.20) + var berry_dark := Color(0.62, 0.10, 0.10) + var leaf := Color(0.25, 0.60, 0.20) + var seed := Color(0.95, 0.85, 0.30) + # Body — wider top tapering to a point at the bottom. + var body: PackedVector2Array = PackedVector2Array([ + Vector2(-5.0, -1.0), Vector2(-3.5, -3.0), Vector2(3.5, -3.0), + Vector2(5.0, -1.0), Vector2(3.0, 4.0), Vector2(0.0, 6.0), Vector2(-3.0, 4.0), + ]) + target.draw_colored_polygon(body, berry) + # Body outline. + target.draw_polyline(body + PackedVector2Array([body[0]]), berry_dark, 1.0) + # Green calyx (leaves) on top. + target.draw_rect(Rect2(-3.0, -5.0, 6.0, 2.0), leaf) + var leaf_top: PackedVector2Array = PackedVector2Array([Vector2(-2.0, -5.0), Vector2(0.0, -7.0), Vector2(2.0, -5.0)]) + target.draw_colored_polygon(leaf_top, leaf) + # Seed speckles. + target.draw_circle(Vector2(-2.0, 1.0), 0.5, seed) + target.draw_circle(Vector2(2.0, 1.0), 0.5, seed) + target.draw_circle(Vector2(0.0, 3.0), 0.5, seed) + return true + # Fall through to type dispatch. match t: TYPE_BREAD: var crust := Color(0.62, 0.40, 0.18) @@ -375,7 +462,7 @@ func _draw() -> void: var square := Rect2(Vector2(-half, -half), Vector2(half * 2, half * 2)) if not has_sprite: - if not Item.draw_item_shape(self, item_type): + if not Item.draw_item_shape(self, item_type, subtype): var hue := float(item_type.hash() % 360) / 360.0 var fill := Color.from_hsv(hue, 0.6, 0.85) draw_rect(square, fill) diff --git a/scenes/pawn/pawn.gd b/scenes/pawn/pawn.gd index 5b6acd0..c3a838a 100644 --- a/scenes/pawn/pawn.gd +++ b/scenes/pawn/pawn.gd @@ -1184,7 +1184,7 @@ func _draw() -> void: var ci_center := Vector2(7.0, -12.0) var ci_scale := 0.55 draw_set_transform(ci_center, 0.0, Vector2(ci_scale, ci_scale)) - if not Item.draw_item_shape(self, carried_item.item_type): + if not Item.draw_item_shape(self, carried_item.item_type, carried_item.subtype): var ci_hue := float(carried_item.item_type.hash() % 360) / 360.0 var ci_color := Color.from_hsv(ci_hue, 0.6, 0.85) draw_rect(Rect2(-6, -6, 12, 12), ci_color)