diff --git a/scenes/entities/item.gd b/scenes/entities/item.gd index 0ae136d..5476e53 100644 --- a/scenes/entities/item.gd +++ b/scenes/entities/item.gd @@ -176,169 +176,186 @@ static func from_dict(d: Dictionary) -> Dictionary: # ── render ──────────────────────────────────────────────────────────────────── -## Procedural shape per item type. All shapes draw inside a -6..+6 box so the -## quality border (half=6) wraps the result. Returns true if the type was -## handled; false sends the caller to the hue-hashed fallback. +## Procedural shape per item type, drawn onto an arbitrary CanvasItem. All +## shapes draw inside a -6..+6 box so the quality border (half=6) wraps the +## result, and the carry indicator (Pawn._draw) can scale the same shapes down +## via draw_set_transform. Returns true if the type was handled; false sends +## the caller to its hue-hashed fallback. ## ## Shape language is loose silhouette + a category-appropriate colour: bread = ## brown loaf, grain = wheat-amber stalks, vegetable = green disc, meal = bowl, -## etc. Better than rendering 18 different magenta-pink squares; later art pass -## can lift these to atlas crops where the bundle has a fitting icon. -func _draw_item_shape(t: StringName) -> bool: +## 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: var dark := Color(0.10, 0.07, 0.05, 0.85) # shared outline match t: TYPE_BREAD: - # Two stacked brown loaves with a slash on the upper crust. var crust := Color(0.62, 0.40, 0.18) var glaze := Color(0.82, 0.58, 0.30) - draw_rect(Rect2(-6.0, -1.0, 12.0, 5.0), crust) - draw_rect(Rect2(-5.0, -5.0, 10.0, 4.0), glaze) - draw_line(Vector2(-3.0, -4.0), Vector2(0.0, -2.0), dark, 1.0) - draw_line(Vector2(0.0, -4.0), Vector2(3.0, -2.0), dark, 1.0) - draw_rect(Rect2(-6.0, -5.0, 12.0, 9.0), dark, false, 1.0) + target.draw_rect(Rect2(-6.0, -1.0, 12.0, 5.0), crust) + target.draw_rect(Rect2(-5.0, -5.0, 10.0, 4.0), glaze) + target.draw_line(Vector2(-3.0, -4.0), Vector2(0.0, -2.0), dark, 1.0) + target.draw_line(Vector2(0.0, -4.0), Vector2(3.0, -2.0), dark, 1.0) + target.draw_rect(Rect2(-6.0, -5.0, 12.0, 9.0), dark, false, 1.0) return true TYPE_GRAIN: - # Three vertical wheat stalks tied in the middle. var stalk := Color(0.85, 0.70, 0.20) var tie := Color(0.55, 0.35, 0.10) - draw_rect(Rect2(-4.0, -6.0, 1.5, 12.0), stalk) - draw_rect(Rect2(-0.75, -6.0, 1.5, 12.0), stalk) - draw_rect(Rect2(2.5, -6.0, 1.5, 12.0), stalk) - # Grain heads (small dots near top). - draw_circle(Vector2(-3.2, -5.0), 1.2, Color(0.95, 0.78, 0.25)) - draw_circle(Vector2(0.0, -5.5), 1.2, Color(0.95, 0.78, 0.25)) - draw_circle(Vector2(3.2, -5.0), 1.2, Color(0.95, 0.78, 0.25)) - draw_rect(Rect2(-5.0, 0.0, 10.0, 2.0), tie) + 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 TYPE_FLOUR: - # Cream-white sack with a darker drawstring at the neck. var sack := Color(0.93, 0.90, 0.78) var shadow := Color(0.78, 0.74, 0.60) - draw_rect(Rect2(-5.0, -3.0, 10.0, 8.0), sack) - draw_rect(Rect2(3.0, -3.0, 2.0, 8.0), shadow) - draw_rect(Rect2(-4.0, -5.0, 8.0, 2.0), Color(0.45, 0.30, 0.10)) # drawstring band - draw_circle(Vector2(0.0, -5.5), 1.0, sack) # gathered top puff - draw_rect(Rect2(-5.0, -6.0, 10.0, 11.0), dark, false, 1.0) + target.draw_rect(Rect2(-5.0, -3.0, 10.0, 8.0), sack) + target.draw_rect(Rect2(3.0, -3.0, 2.0, 8.0), shadow) + target.draw_rect(Rect2(-4.0, -5.0, 8.0, 2.0), Color(0.45, 0.30, 0.10)) + target.draw_circle(Vector2(0.0, -5.5), 1.0, sack) + target.draw_rect(Rect2(-5.0, -6.0, 10.0, 11.0), dark, false, 1.0) return true TYPE_VEGETABLE: - # Green-leafed root vegetable (think turnip). var leaf := Color(0.25, 0.65, 0.20) var root := Color(0.92, 0.88, 0.70) - draw_circle(Vector2(0.0, 1.0), 5.0, root) - draw_arc(Vector2(0.0, 1.0), 5.0, 0.0, TAU, 16, dark, 1.0) - # Leaves - draw_rect(Rect2(-3.0, -5.0, 2.0, 3.0), leaf) - draw_rect(Rect2(-1.0, -6.0, 2.0, 4.0), leaf) - draw_rect(Rect2( 1.0, -5.0, 2.0, 3.0), leaf) + target.draw_circle(Vector2(0.0, 1.0), 5.0, root) + target.draw_arc(Vector2(0.0, 1.0), 5.0, 0.0, TAU, 16, dark, 1.0) + target.draw_rect(Rect2(-3.0, -5.0, 2.0, 3.0), leaf) + target.draw_rect(Rect2(-1.0, -6.0, 2.0, 4.0), leaf) + target.draw_rect(Rect2( 1.0, -5.0, 2.0, 3.0), leaf) return true TYPE_MEAL: - # Wooden bowl with a steamy meal heap. var bowl := Color(0.50, 0.30, 0.12) var bowl_rim := Color(0.30, 0.18, 0.08) var food := Color(0.85, 0.55, 0.20) - # Bowl as a half-disc. - draw_circle(Vector2(0.0, 1.0), 6.0, bowl) - draw_rect(Rect2(-6.0, -5.0, 12.0, 6.0), Color(0, 0, 0, 0)) # cover top half (no-op, rely on z) - draw_arc(Vector2(0.0, 1.0), 6.0, 0.0, PI, 16, bowl_rim, 1.0) - draw_line(Vector2(-6.0, 1.0), Vector2(6.0, 1.0), bowl_rim, 1.0) - # Food mound on top. - draw_circle(Vector2(0.0, 0.0), 3.5, food) - # Two steam wisps. - draw_line(Vector2(-2.0, -5.0), Vector2(-1.0, -3.0), Color(0.9, 0.9, 0.9, 0.7), 1.0) - draw_line(Vector2(2.0, -5.0), Vector2(1.0, -3.0), Color(0.9, 0.9, 0.9, 0.7), 1.0) + target.draw_circle(Vector2(0.0, 1.0), 6.0, bowl) + target.draw_arc(Vector2(0.0, 1.0), 6.0, 0.0, PI, 16, bowl_rim, 1.0) + target.draw_line(Vector2(-6.0, 1.0), Vector2(6.0, 1.0), bowl_rim, 1.0) + target.draw_circle(Vector2(0.0, 0.0), 3.5, food) + target.draw_line(Vector2(-2.0, -5.0), Vector2(-1.0, -3.0), Color(0.9, 0.9, 0.9, 0.7), 1.0) + target.draw_line(Vector2(2.0, -5.0), Vector2(1.0, -3.0), Color(0.9, 0.9, 0.9, 0.7), 1.0) return true TYPE_MEAT: - # Raw red steak with a pale fat marbling line. var meat := Color(0.78, 0.20, 0.20) var fat := Color(0.95, 0.85, 0.70) - draw_rect(Rect2(-5.0, -3.0, 10.0, 7.0), meat) - draw_line(Vector2(-5.0, 0.0), Vector2(5.0, 0.5), fat, 1.0) - draw_rect(Rect2(-5.0, -3.0, 10.0, 7.0), dark, false, 1.0) + target.draw_rect(Rect2(-5.0, -3.0, 10.0, 7.0), meat) + target.draw_line(Vector2(-5.0, 0.0), Vector2(5.0, 0.5), fat, 1.0) + target.draw_rect(Rect2(-5.0, -3.0, 10.0, 7.0), dark, false, 1.0) return true TYPE_CLOTH: - # Folded cloth bolt — light blue with horizontal pleats. var cloth := Color(0.50, 0.65, 0.85) var pleat := Color(0.32, 0.42, 0.58) - draw_rect(Rect2(-5.0, -4.0, 10.0, 8.0), cloth) - draw_line(Vector2(-5.0, -1.5), Vector2(5.0, -1.5), pleat, 1.0) - draw_line(Vector2(-5.0, 1.5), Vector2(5.0, 1.5), pleat, 1.0) - draw_rect(Rect2(-5.0, -4.0, 10.0, 8.0), dark, false, 1.0) + target.draw_rect(Rect2(-5.0, -4.0, 10.0, 8.0), cloth) + target.draw_line(Vector2(-5.0, -1.5), Vector2(5.0, -1.5), pleat, 1.0) + target.draw_line(Vector2(-5.0, 1.5), Vector2(5.0, 1.5), pleat, 1.0) + target.draw_rect(Rect2(-5.0, -4.0, 10.0, 8.0), dark, false, 1.0) return true TYPE_MEDICINE: - # White phial with a red cross — medieval-ish but reads instantly. var phial := Color(0.95, 0.95, 0.95) var cross := Color(0.80, 0.15, 0.15) - draw_rect(Rect2(-4.0, -5.0, 8.0, 10.0), phial) - draw_rect(Rect2(-1.0, -3.0, 2.0, 6.0), cross) - draw_rect(Rect2(-3.0, -1.0, 6.0, 2.0), cross) - draw_rect(Rect2(-4.0, -5.0, 8.0, 10.0), dark, false, 1.0) + target.draw_rect(Rect2(-4.0, -5.0, 8.0, 10.0), phial) + target.draw_rect(Rect2(-1.0, -3.0, 2.0, 6.0), cross) + target.draw_rect(Rect2(-3.0, -1.0, 6.0, 2.0), cross) + target.draw_rect(Rect2(-4.0, -5.0, 8.0, 10.0), dark, false, 1.0) return true TYPE_TOOL: - # Brown-handled hammer. var handle := Color(0.50, 0.30, 0.12) var head := Color(0.45, 0.45, 0.48) - draw_rect(Rect2(-1.0, -2.0, 2.0, 8.0), handle) - draw_rect(Rect2(-5.0, -5.0, 10.0, 4.0), head) - draw_rect(Rect2(-5.0, -5.0, 10.0, 4.0), dark, false, 1.0) + target.draw_rect(Rect2(-1.0, -2.0, 2.0, 8.0), handle) + target.draw_rect(Rect2(-5.0, -5.0, 10.0, 4.0), head) + target.draw_rect(Rect2(-5.0, -5.0, 10.0, 4.0), dark, false, 1.0) return true TYPE_WEAPON: - # Sword: triangular blade + brown grip + crossguard. var blade := Color(0.78, 0.80, 0.85) var guard := Color(0.45, 0.30, 0.10) - # Blade (pointing up) var pts: PackedVector2Array = PackedVector2Array([Vector2(0.0, -6.0), Vector2(2.5, 1.0), Vector2(-2.5, 1.0)]) - draw_colored_polygon(pts, blade) - # Crossguard - draw_rect(Rect2(-4.0, 1.0, 8.0, 2.0), guard) - # Grip - draw_rect(Rect2(-1.0, 3.0, 2.0, 3.0), guard) + target.draw_colored_polygon(pts, blade) + target.draw_rect(Rect2(-4.0, 1.0, 8.0, 2.0), guard) + target.draw_rect(Rect2(-1.0, 3.0, 2.0, 3.0), guard) return true TYPE_ARMOR: - # Helmet silhouette — rounded grey dome with a nose-guard slit. var steel := Color(0.65, 0.65, 0.70) var visor := Color(0.20, 0.20, 0.25) - draw_circle(Vector2(0.0, 0.0), 5.5, steel) - draw_rect(Rect2(-1.0, -2.0, 2.0, 4.0), visor) - draw_rect(Rect2(-6.0, 4.0, 12.0, 2.0), steel) - draw_arc(Vector2(0.0, 0.0), 5.5, 0.0, TAU, 16, dark, 1.0) + target.draw_circle(Vector2(0.0, 0.0), 5.5, steel) + target.draw_rect(Rect2(-1.0, -2.0, 2.0, 4.0), visor) + target.draw_rect(Rect2(-6.0, 4.0, 12.0, 2.0), steel) + target.draw_arc(Vector2(0.0, 0.0), 5.5, 0.0, TAU, 16, dark, 1.0) return true TYPE_STONE_BLOCK: - # Cleaned-up stone brick: pale grey rect with a chiseled corner. var stone := Color(0.62, 0.60, 0.58) var stone_hi := Color(0.78, 0.76, 0.72) - draw_rect(Rect2(-6.0, -4.0, 12.0, 8.0), stone) - draw_rect(Rect2(-6.0, -4.0, 12.0, 2.0), stone_hi) - draw_line(Vector2(-6.0, 0.0), Vector2(6.0, 0.0), Color(0.42, 0.40, 0.38), 1.0) - draw_rect(Rect2(-6.0, -4.0, 12.0, 8.0), dark, false, 1.0) + target.draw_rect(Rect2(-6.0, -4.0, 12.0, 8.0), stone) + target.draw_rect(Rect2(-6.0, -4.0, 12.0, 2.0), stone_hi) + target.draw_line(Vector2(-6.0, 0.0), Vector2(6.0, 0.0), Color(0.42, 0.40, 0.38), 1.0) + target.draw_rect(Rect2(-6.0, -4.0, 12.0, 8.0), dark, false, 1.0) return true TYPE_COPPER_ORE: - # Copper chunks — warm brown with bright highlights. var copper := Color(0.65, 0.35, 0.18) var hi := Color(0.92, 0.55, 0.20) - draw_circle(Vector2(-2.0, 1.0), 3.5, copper) - draw_circle(Vector2(2.0, -1.0), 2.8, copper) - draw_circle(Vector2(-2.0, 1.0), 1.5, hi) - draw_circle(Vector2(2.0, -1.0), 1.0, hi) + target.draw_circle(Vector2(-2.0, 1.0), 3.5, copper) + target.draw_circle(Vector2(2.0, -1.0), 2.8, copper) + target.draw_circle(Vector2(-2.0, 1.0), 1.5, hi) + target.draw_circle(Vector2(2.0, -1.0), 1.0, hi) return true TYPE_SILVER: - # Silver nugget — cool grey with a white highlight. var silver := Color(0.78, 0.80, 0.85) var hi := Color(0.98, 0.98, 1.00) - draw_circle(Vector2(-2.0, 1.0), 3.5, silver) - draw_circle(Vector2(2.0, -1.0), 2.8, silver) - draw_circle(Vector2(-2.0, 0.5), 1.2, hi) + target.draw_circle(Vector2(-2.0, 1.0), 3.5, silver) + target.draw_circle(Vector2(2.0, -1.0), 2.8, silver) + target.draw_circle(Vector2(-2.0, 0.5), 1.2, hi) return true TYPE_ASH: - # Grey pile with faint smoke wisps. var ash := Color(0.55, 0.55, 0.55) var ash_hi := Color(0.78, 0.78, 0.78) - # Heap (low triangle) var pts: PackedVector2Array = PackedVector2Array([Vector2(-6.0, 4.0), Vector2(6.0, 4.0), Vector2(0.0, -2.0)]) - draw_colored_polygon(pts, ash) - draw_line(Vector2(-3.0, 1.0), Vector2(3.0, 1.0), ash_hi, 1.0) - # Smoke wisps - draw_line(Vector2(-1.0, -3.0), Vector2(0.0, -5.0), Color(0.85, 0.85, 0.85, 0.6), 1.0) - draw_line(Vector2(1.5, -3.0), Vector2(2.5, -5.0), Color(0.85, 0.85, 0.85, 0.6), 1.0) + target.draw_colored_polygon(pts, ash) + target.draw_line(Vector2(-3.0, 1.0), Vector2(3.0, 1.0), ash_hi, 1.0) + target.draw_line(Vector2(-1.0, -3.0), Vector2(0.0, -5.0), Color(0.85, 0.85, 0.85, 0.6), 1.0) + target.draw_line(Vector2(1.5, -3.0), Vector2(2.5, -5.0), Color(0.85, 0.85, 0.85, 0.6), 1.0) + return true + # Atlas-backed types — the on-floor visual is the bundle icon (Sprite2D + # child), but the carry indicator needs a simple shape since pawns can't + # draw an AtlasTexture inline. Shapes below approximate the atlas look. + TYPE_WOOD: + var wood := Color(0.55, 0.35, 0.18) + var wood_hi := Color(0.75, 0.50, 0.25) + target.draw_rect(Rect2(-6.0, -3.0, 12.0, 6.0), wood) + target.draw_line(Vector2(-6.0, -1.0), Vector2(6.0, -1.0), wood_hi, 1.0) + target.draw_line(Vector2(-6.0, 1.0), Vector2(6.0, 1.0), Color(0.40, 0.25, 0.10), 1.0) + target.draw_rect(Rect2(-6.0, -3.0, 12.0, 6.0), dark, false, 1.0) + return true + TYPE_PLANK: + var plank := Color(0.80, 0.60, 0.35) + var plank_grain := Color(0.55, 0.38, 0.20) + target.draw_rect(Rect2(-6.0, -3.0, 12.0, 6.0), plank) + target.draw_line(Vector2(-5.0, -1.0), Vector2(5.0, -1.0), plank_grain, 1.0) + target.draw_line(Vector2(-5.0, 1.0), Vector2(5.0, 1.0), plank_grain, 1.0) + target.draw_rect(Rect2(-6.0, -3.0, 12.0, 6.0), dark, false, 1.0) + return true + TYPE_STONE: + var stone := Color(0.60, 0.58, 0.55) + var stone_hi := Color(0.78, 0.76, 0.72) + target.draw_circle(Vector2(-1.5, 1.0), 4.0, stone) + target.draw_circle(Vector2(2.0, -0.5), 3.0, stone) + target.draw_circle(Vector2(-1.5, 1.0), 1.5, stone_hi) + return true + TYPE_IRON_ORE: + var ore := Color(0.42, 0.42, 0.50) + var ore_hi := Color(0.60, 0.62, 0.72) + target.draw_circle(Vector2(-1.5, 1.0), 4.0, ore) + target.draw_circle(Vector2(2.0, -0.5), 3.0, ore) + target.draw_circle(Vector2(2.0, -0.5), 1.2, ore_hi) + return true + TYPE_GOLD: + var gold := Color(0.92, 0.78, 0.20) + var gold_hi := Color(1.00, 0.95, 0.55) + target.draw_circle(Vector2(-1.5, 1.0), 4.0, gold) + target.draw_circle(Vector2(2.0, -0.5), 3.0, gold) + target.draw_circle(Vector2(-1.5, 0.5), 1.5, gold_hi) return true _: return false @@ -358,7 +375,7 @@ func _draw() -> void: var square := Rect2(Vector2(-half, -half), Vector2(half * 2, half * 2)) if not has_sprite: - if not _draw_item_shape(item_type): + if not Item.draw_item_shape(self, item_type): 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 2f4565e..5b6acd0 100644 --- a/scenes/pawn/pawn.gd +++ b/scenes/pawn/pawn.gd @@ -1176,12 +1176,20 @@ func _draw() -> void: if _selected: draw_arc(Vector2.ZERO, 10.0, 0.0, TAU, 32, Color(1.0, 0.9, 0.2, 0.85), 2.0) - # Phase 4 — carry indicator: small coloured square at upper-right of body. + # Carry indicator — draw a shrunk version of the actual item shape at the + # pawn's upper-right ("held out at chest height"). Uses Item.draw_item_shape + # at 0.55× scale (the shapes are authored for a 12×12 box; 0.55× → ~7×7). + # Falls back to a hue-hashed square if the item type isn't shape-registered. if carried_item != null: - 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, -10, 7, 7), ci_color) - draw_rect(Rect2(6, -10, 7, 7), Color(0, 0, 0, 0.7), false, 1.0) + 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): + 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) + draw_rect(Rect2(-6, -6, 12, 12), Color(0, 0, 0, 0.7), false, 1.0) + draw_set_transform(Vector2.ZERO, 0.0, Vector2.ONE) # ── helpers ─────────────────────────────────────────────────────────────────