diff --git a/scripts/generate_auto_smelter_textures.py b/scripts/generate_auto_smelter_textures.py new file mode 100644 index 0000000..1ed8cb0 --- /dev/null +++ b/scripts/generate_auto_smelter_textures.py @@ -0,0 +1,711 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Auto Smelter at 1024x1024. + +Concept: Industrial sci-fi furnace that screams "I smelt things" at +first glance. The front face dominates with a massive arched furnace +mouth showing a roaring fire when active (dark embers when idle). +The sides feature thick vertical heat pipes that glow orange when +active. The top has a heavy armored chimney/smokestack opening. +The back has an exhaust grate. The bottom is a reinforced base plate. + +Creates 12 textures: + 6 faces (north, south, east, west, up, down) x 2 states (idle, active) +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor (consistent with other Atlas blocks) +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Furnace fire / heat colors +FIRE_DEEP = (50, 12, 2, 255) +FIRE_DARK = (120, 35, 5, 255) +FIRE_MID = (220, 100, 15, 255) +FIRE_BRIGHT = (255, 170, 40, 255) +FIRE_HOT = (255, 230, 100, 255) +EMBER_DIM = (60, 20, 8, 255) +EMBER_WARM = (100, 35, 10, 255) +AMBER_GLOW = (255, 140, 30, 255) +ORANGE_GLOW = (255, 120, 20, 255) + +# Furnace mouth +MOUTH_BG = (8, 10, 14, 255) + +# Heat pipe colors +PIPE_DARK = (45, 48, 56, 255) +PIPE_MID = (60, 64, 74, 255) +PIPE_LIGHT = (78, 82, 94, 255) +PIPE_HOT = (180, 80, 20, 255) +PIPE_GLOWING = (220, 110, 25, 255) + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + """Return 6 vertices for a flat-top hexagon.""" + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + """Compute hex cell center positions covering the image.""" + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + """Draw a filled circle.""" + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + """Draw a beveled rectangle border (raised appearance).""" + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + """Create a hex armor face — the foundation for all smelter faces.""" + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + + return img + + +def add_corner_bolts(draw, inset=30): + """Add bolts at the four corners of a face.""" + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +def draw_arch(draw, cx, top_y, bottom_y, half_width, fill): + """Draw a filled arch shape (rectangle with semicircular top).""" + # Straight sides + draw.rectangle( + [cx - half_width, top_y + half_width, cx + half_width, bottom_y], + fill=fill + ) + # Semicircular arch top + draw.pieslice( + [cx - half_width, top_y, + cx + half_width, top_y + half_width * 2], + start=180, end=360, fill=fill + ) + + +def draw_arch_outline(draw, cx, top_y, bottom_y, half_width, + outline_color, width=3): + """Draw the outline of an arch shape.""" + # Left side + draw.line( + [(cx - half_width, top_y + half_width), + (cx - half_width, bottom_y)], + fill=outline_color, width=width + ) + # Right side + draw.line( + [(cx + half_width, top_y + half_width), + (cx + half_width, bottom_y)], + fill=outline_color, width=width + ) + # Bottom + draw.line( + [(cx - half_width, bottom_y), + (cx + half_width, bottom_y)], + fill=outline_color, width=width + ) + # Arch top + draw.arc( + [cx - half_width, top_y, + cx + half_width, top_y + half_width * 2], + start=180, end=360, fill=outline_color, width=width + ) + + +# --------------------------------------------------------------------------- +# NORTH (FRONT) — massive furnace mouth with arch +# --------------------------------------------------------------------------- + +def make_north(active=False): + """Front face: dominant arched furnace mouth. + + A huge arch opening takes up most of the face, making it + unmistakably a furnace. Fire/embers visible inside. + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx = S // 2 + # Arch dimensions — big and dominant + arch_half_w = int(S * 0.34) + arch_top = int(S * 0.10) + arch_bottom = int(S * 0.88) + + # Heavy outer frame (thick border around the arch) + frame_w = 24 + draw_arch(draw, cx, arch_top - frame_w, + arch_bottom + frame_w, + arch_half_w + frame_w, ARMOR_MID) + draw_arch_outline(draw, cx, arch_top - frame_w, + arch_bottom + frame_w, + arch_half_w + frame_w, + ARMOR_LIGHT, width=4) + + # Inner frame border (dark inset) + draw_arch(draw, cx, arch_top, arch_bottom, + arch_half_w, EDGE_DARK) + + # Fire interior + if active: + # Build fire from bottom up: hottest at bottom + interior_top = arch_top + arch_half_w # below the arch curve + for y in range(arch_bottom, interior_top - 1, -1): + t = (arch_bottom - y) / max(1, arch_bottom - interior_top) + if t < 0.15: + c = lerp_color(FIRE_HOT, FIRE_BRIGHT, t / 0.15) + elif t < 0.4: + c = lerp_color(FIRE_BRIGHT, FIRE_MID, + (t - 0.15) / 0.25) + elif t < 0.7: + c = lerp_color(FIRE_MID, FIRE_DARK, + (t - 0.4) / 0.3) + else: + c = lerp_color(FIRE_DARK, FIRE_DEEP, + (t - 0.7) / 0.3) + # Determine x bounds at this y level + if y <= arch_top + arch_half_w: + # In the arch curve region + dy = y - (arch_top + arch_half_w) + r_sq = arch_half_w ** 2 - dy ** 2 + if r_sq > 0: + half_x = int(math.sqrt(r_sq)) + else: + half_x = 0 + else: + half_x = arch_half_w + if half_x > 4: + draw.line( + [(cx - half_x + 4, y), (cx + half_x - 4, y)], + fill=c + ) + + # Fire tongues — brighter streaks rising from the bottom + for offset in [-80, -30, 25, 70]: + tongue_x = cx + offset + tongue_base = arch_bottom - 10 + tongue_h = 120 + (abs(offset) % 40) + for ty in range(tongue_base, tongue_base - tongue_h, -1): + tt = (tongue_base - ty) / tongue_h + tc = lerp_color(FIRE_HOT, FIRE_BRIGHT, tt) + w = max(1, int(18 * (1 - tt))) + draw.line( + [(tongue_x - w, ty), (tongue_x + w, ty)], + fill=tc + ) + else: + # Idle: dark interior with dim ember bed at bottom + # Fill the rectangular part + draw.rectangle( + [cx - arch_half_w + 3, + arch_top + arch_half_w, + cx + arch_half_w - 3, + arch_bottom - 3], + fill=MOUTH_BG + ) + # Fill the arch curve part + draw.pieslice( + [cx - arch_half_w + 3, arch_top + 3, + cx + arch_half_w - 3, arch_top + arch_half_w * 2 - 3], + start=180, end=360, fill=MOUTH_BG + ) + + # Dim ember bed at the bottom + ember_h = 40 + for y in range(arch_bottom - ember_h, arch_bottom - 3): + t = (y - (arch_bottom - ember_h)) / ember_h + c = lerp_color(MOUTH_BG, EMBER_WARM, t * 0.6) + draw.line( + [(cx - arch_half_w + 6, y), + (cx + arch_half_w - 6, y)], + fill=c + ) + # A few dim ember dots + for ex, ey in [(cx - 60, arch_bottom - 15), + (cx + 40, arch_bottom - 12), + (cx - 20, arch_bottom - 20), + (cx + 80, arch_bottom - 18), + (cx, arch_bottom - 10)]: + draw_filled_circle(draw, ex, ey, 6, EMBER_DIM) + + # Arch outline on top of everything + draw_arch_outline(draw, cx, arch_top, arch_bottom, + arch_half_w, EDGE_DARK, width=4) + + # Bolts along the frame + bolt_positions = [ + (cx - arch_half_w - frame_w + 10, arch_bottom + 10), + (cx + arch_half_w + frame_w - 10, arch_bottom + 10), + (cx - arch_half_w - frame_w + 10, + arch_top + arch_half_w + 40), + (cx + arch_half_w + frame_w - 10, + arch_top + arch_half_w + 40), + (cx - arch_half_w - frame_w + 10, + (arch_bottom + arch_top + arch_half_w) // 2 + 40), + (cx + arch_half_w + frame_w - 10, + (arch_bottom + arch_top + arch_half_w) // 2 + 40), + ] + for bx, by in bolt_positions: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + add_corner_bolts(draw) + + # Active: fire glow spilling out + if active: + add_radial_glow(img, cx, int(S * 0.55), + int(S * 0.4), AMBER_GLOW, + intensity=0.25) + add_radial_glow(img, cx, arch_bottom - 20, + int(S * 0.3), FIRE_BRIGHT, + intensity=0.15) + + return img + + +# --------------------------------------------------------------------------- +# SOUTH (BACK) — exhaust vent with heat signature +# --------------------------------------------------------------------------- + +def make_south(active=False): + """Back face: hex armor with large exhaust vent panel.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Large exhaust vent panel + vent_w = int(S * 0.50) + vent_h = int(S * 0.44) + vx1 = cx - vent_w // 2 + vy1 = cy - vent_h // 2 + vx2 = cx + vent_w // 2 + vy2 = cy + vent_h // 2 + + draw.rectangle([vx1, vy1, vx2, vy2], fill=ARMOR_MID) + add_bevel_border(draw, vx1, vy1, vx2, vy2, + EDGE_DARK, ARMOR_LIGHT, width=5) + + # Horizontal vent slats (wide, like industrial exhaust) + slat_h = 12 + slat_gap = 20 + y = vy1 + 20 + while y + slat_h < vy2 - 14: + draw.rectangle( + [vx1 + 18, y, vx2 - 18, y + slat_h], + fill=(15, 17, 22, 255) + ) + # Highlight on top of slat + draw.line( + [(vx1 + 18, y + slat_h + 1), + (vx2 - 18, y + slat_h + 1)], + fill=ARMOR_LIGHT, width=1 + ) + y += slat_h + slat_gap + + # Panel bolts + bolt_off = 18 + for bx, by in [(vx1 + bolt_off, vy1 + bolt_off), + (vx2 - bolt_off, vy1 + bolt_off), + (vx1 + bolt_off, vy2 - bolt_off), + (vx2 - bolt_off, vy2 - bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Horizontal seams + m = 30 + if active: + add_glowing_seam( + img, (m, cy), (vx1 - 10, cy), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (vx2 + 10, cy), (S - m, cy), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=8, intensity=0.15 + ) + else: + draw.line([(m, cy), (vx1 - 10, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(vx2 + 10, cy), (S - m, cy)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + + if active: + add_radial_glow(img, cx, cy, int(vent_w * 0.35), + ORANGE_GLOW, intensity=0.12) + + return img + + +# --------------------------------------------------------------------------- +# EAST / WEST (SIDES) — heat pipes running vertically +# --------------------------------------------------------------------------- + +def make_side(active=False): + """Side face: hex armor with two thick vertical heat pipes. + + The pipes run top-to-bottom and glow orange when active, + reinforcing the furnace / industrial heat aesthetic. + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Two vertical heat pipes + pipe_w = 50 + pipe_gap = int(S * 0.26) + pipe_top = 50 + pipe_bottom = S - 50 + + for pipe_cx in [cx - pipe_gap // 2, cx + pipe_gap // 2]: + px1 = pipe_cx - pipe_w // 2 + px2 = pipe_cx + pipe_w // 2 + + if active: + # Glowing hot pipe — gradient from center bright + for x in range(px1, px2 + 1): + t = abs(x - pipe_cx) / (pipe_w // 2) + c = lerp_color(PIPE_GLOWING, PIPE_HOT, t) + draw.line([(x, pipe_top), (x, pipe_bottom)], + fill=c) + else: + # Cold pipe — metallic + for x in range(px1, px2 + 1): + t = abs(x - pipe_cx) / (pipe_w // 2) + c = lerp_color(PIPE_LIGHT, PIPE_DARK, t) + draw.line([(x, pipe_top), (x, pipe_bottom)], + fill=c) + + # Pipe edge lines + draw.line([(px1, pipe_top), (px1, pipe_bottom)], + fill=EDGE_DARK, width=2) + draw.line([(px2, pipe_top), (px2, pipe_bottom)], + fill=EDGE_DARK, width=2) + + # Pipe clamp brackets (horizontal bands) + clamp_color = ARMOR_LIGHT if not active else (90, 75, 55, 255) + for clamp_y in range(pipe_top + 80, pipe_bottom - 60, 180): + clamp_h = 18 + draw.rectangle( + [px1 - 12, clamp_y, px2 + 12, clamp_y + clamp_h], + fill=clamp_color, outline=EDGE_DARK + ) + # Clamp bolts + draw_filled_circle(draw, px1 - 6, clamp_y + clamp_h // 2, + 5, RIVET_COLOR) + draw_filled_circle(draw, px2 + 6, clamp_y + clamp_h // 2, + 5, RIVET_COLOR) + + # Horizontal seam between pipes + m = 30 + if active: + add_glowing_seam( + img, (m, cy), (cx - pipe_gap // 2 - pipe_w // 2 - 16, cy), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=6, intensity=0.12 + ) + add_glowing_seam( + img, (cx + pipe_gap // 2 + pipe_w // 2 + 16, cy), + (S - m, cy), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=6, intensity=0.12 + ) + else: + draw.line( + [(m, cy), + (cx - pipe_gap // 2 - pipe_w // 2 - 16, cy)], + fill=SEAM_COLOR, width=2 + ) + draw.line( + [(cx + pipe_gap // 2 + pipe_w // 2 + 16, cy), + (S - m, cy)], + fill=SEAM_COLOR, width=2 + ) + + add_corner_bolts(draw) + + if active: + # Warm glow along each pipe + for pipe_cx in [cx - pipe_gap // 2, cx + pipe_gap // 2]: + add_radial_glow(img, pipe_cx, cy, + pipe_w + 20, AMBER_GLOW, + intensity=0.10) + + return img + + +# --------------------------------------------------------------------------- +# UP (TOP) — chimney / smokestack opening +# --------------------------------------------------------------------------- + +def make_top(active=False): + """Top face: circular chimney opening with thick armored rim. + + A large round smokestack opening dominates the top, with + heavy bolt-studded rim. Glows from within when active. + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Chimney outer rim + rim_outer = int(S * 0.36) + rim_inner = int(S * 0.28) + + # Thick metallic rim with gradient + for r in range(rim_outer, rim_inner, -1): + t = (rim_outer - r) / (rim_outer - rim_inner) + c = lerp_color(ARMOR_LIGHT, ARMOR_MID, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Rim edges + draw.ellipse( + [cx - rim_outer, cy - rim_outer, + cx + rim_outer, cy + rim_outer], + outline=EDGE_DARK + ) + draw.ellipse( + [cx - rim_inner, cy - rim_inner, + cx + rim_inner, cy + rim_inner], + outline=EDGE_DARK + ) + + # Dark chimney interior + if active: + # Warm glow from fire below + for r in range(rim_inner - 2, 0, -1): + t = r / rim_inner + c = lerp_color(FIRE_MID, FIRE_DEEP, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], + outline=c) + # Bright core + core_r = int(rim_inner * 0.3) + draw_filled_circle(draw, cx, cy, core_r, FIRE_DARK) + else: + draw_filled_circle(draw, cx, cy, rim_inner - 2, + MOUTH_BG) + + # Bolts around the rim (evenly spaced in a circle) + bolt_count = 12 + bolt_r = (rim_outer + rim_inner) // 2 + for i in range(bolt_count): + angle = math.radians(360 * i / bolt_count) + bx = int(cx + bolt_r * math.cos(angle)) + by = int(cy + bolt_r * math.sin(angle)) + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Cross seams from corners to rim + m = 30 + seam_end = rim_outer + 20 + if active: + for sx, sy, ex, ey in [ + (m, m, cx - int(seam_end * 0.7), + cy - int(seam_end * 0.7)), + (S - m, m, cx + int(seam_end * 0.7), + cy - int(seam_end * 0.7)), + (m, S - m, cx - int(seam_end * 0.7), + cy + int(seam_end * 0.7)), + (S - m, S - m, cx + int(seam_end * 0.7), + cy + int(seam_end * 0.7)), + ]: + add_glowing_seam( + img, (sx, sy), (ex, ey), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=6, intensity=0.10 + ) + else: + for sx, sy, ex, ey in [ + (m, m, cx - int(seam_end * 0.7), + cy - int(seam_end * 0.7)), + (S - m, m, cx + int(seam_end * 0.7), + cy - int(seam_end * 0.7)), + (m, S - m, cx - int(seam_end * 0.7), + cy + int(seam_end * 0.7)), + (S - m, S - m, cx + int(seam_end * 0.7), + cy + int(seam_end * 0.7)), + ]: + draw.line([(sx, sy), (ex, ey)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + + if active: + add_radial_glow(img, cx, cy, rim_inner + 30, + ORANGE_GLOW, intensity=0.18) + + return img + + +# --------------------------------------------------------------------------- +# DOWN (BOTTOM) — reinforced base plate +# --------------------------------------------------------------------------- + +def make_bottom(active=False): + """Bottom face: heavy reinforced base plate with vent grille.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Central vent grille + grille_size = 360 + g1 = cx - grille_size // 2 + g2 = cx + grille_size // 2 + draw.rectangle([g1, g1, g2, g2], fill=ARMOR_MID, + outline=ARMOR_LIGHT) + add_bevel_border(draw, g1, g1, g2, g2, ARMOR_LIGHT, EDGE_DARK, + width=3) + + # Vent slots + for vy in range(g1 + 24, g2 - 16, 22): + draw.rectangle([g1 + 18, vy, g2 - 18, vy + 8], + fill=EDGE_DARK) + draw.rectangle([g1 + 20, vy + 1, g2 - 20, vy + 7], + fill=(15, 17, 22, 255)) + + # Mounting feet in corners + foot_size = 70 + foot_inset = 55 + for fx, fy in [(foot_inset, foot_inset), + (S - foot_inset - foot_size, foot_inset), + (foot_inset, S - foot_inset - foot_size), + (S - foot_inset - foot_size, + S - foot_inset - foot_size)]: + draw.rectangle([fx, fy, fx + foot_size, fy + foot_size], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + add_bevel_border(draw, fx, fy, fx + foot_size, + fy + foot_size, + (80, 86, 100, 255), EDGE_DARK, width=2) + fcx = fx + foot_size // 2 + fcy = fy + foot_size // 2 + draw_filled_circle(draw, fcx, fcy, 12, ARMOR_MID, + outline=EDGE_DARK) + draw_filled_circle(draw, fcx, fcy, 6, RIVET_COLOR) + + # Cross seams + draw.line([(cx, foot_inset + foot_size), (cx, g1)], + fill=SEAM_COLOR, width=3) + draw.line([(cx, g2), (cx, S - foot_inset - foot_size)], + fill=SEAM_COLOR, width=3) + draw.line([(foot_inset + foot_size, cy), (g1, cy)], + fill=SEAM_COLOR, width=3) + draw.line([(g2, cy), (S - foot_inset - foot_size, cy)], + fill=SEAM_COLOR, width=3) + + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + textures = {} + + for active in (False, True): + state = "active" if active else "idle" + + textures[f"auto_smelter_{state}_north"] = make_north( + active=active + ) + textures[f"auto_smelter_{state}_south"] = make_south( + active=active + ) + textures[f"auto_smelter_{state}_east"] = make_side( + active=active + ) + textures[f"auto_smelter_{state}_west"] = make_side( + active=active + ) + textures[f"auto_smelter_{state}_up"] = make_top( + active=active + ) + textures[f"auto_smelter_{state}_down"] = make_bottom( + active=active + ) + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_cobblestone_factory_textures.py b/scripts/generate_cobblestone_factory_textures.py new file mode 100644 index 0000000..723b1e7 --- /dev/null +++ b/scripts/generate_cobblestone_factory_textures.py @@ -0,0 +1,714 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Cobblestone Factory at 1024x1024. + +Concept: Industrial stone fabricator with a heavy crushing mechanism. +The front face features a large rectangular output chute with a conveyor +of crushed stone visible inside — grinding rollers when active, dark and +still when idle. The sides show heavy mechanical pistons/rams. The top +has a reinforced intake hopper. The back has a power/coolant panel. +The bottom is a reinforced base plate. + +Creates 12 textures: + 6 faces (north, south, east, west, up, down) x 2 states (idle, active) +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor (consistent with other Atlas blocks) +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Stone / cobblestone colors +STONE_DARK = (68, 68, 68, 255) +STONE_MID = (105, 105, 105, 255) +STONE_LIGHT = (140, 140, 140, 255) +STONE_HIGHLIGHT = (170, 170, 170, 255) +COBBLE_DARK = (80, 78, 72, 255) +COBBLE_MID = (120, 118, 110, 255) +COBBLE_LIGHT = (155, 152, 145, 255) + +# Mechanical / active colors +PISTON_DARK = (50, 52, 58, 255) +PISTON_MID = (72, 76, 86, 255) +PISTON_LIGHT = (90, 95, 108, 255) +PISTON_SHAFT = (130, 135, 150, 255) + +# Active glow — orange-amber for mechanical energy +MECH_GLOW = (255, 160, 40, 255) +MECH_DIM = (180, 100, 20, 255) +AMBER_GLOW = (255, 140, 30, 255) +ORANGE_GLOW = (255, 120, 20, 255) + +# Crusher interior +INTERIOR_BG = (12, 14, 18, 255) + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + """Return 6 vertices for a flat-top hexagon.""" + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + """Compute hex cell center positions covering the image.""" + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + """Draw a filled circle.""" + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + """Draw a beveled rectangle border (raised appearance).""" + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + """Create a hex armor face — the foundation for all factory faces.""" + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + + return img + + +def add_corner_bolts(draw, inset=30): + """Add bolts at the four corners of a face.""" + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +# --------------------------------------------------------------------------- +# NORTH (FRONT) — crusher output chute with grinding rollers +# --------------------------------------------------------------------------- + +def make_north(active=False): + """Front face: large rectangular crusher chute with visible rollers. + + A wide rectangular opening shows the crushing mechanism inside. + When active, rollers spin and crushed stone particles are visible. + When idle, the mechanism is dark and still. + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx = S // 2 + + # Chute dimensions — wide rectangular opening + chute_half_w = int(S * 0.32) + chute_top = int(S * 0.14) + chute_bottom = int(S * 0.86) + + # Heavy outer frame + frame_w = 24 + draw.rectangle( + [cx - chute_half_w - frame_w, chute_top - frame_w, + cx + chute_half_w + frame_w, chute_bottom + frame_w], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - chute_half_w - frame_w, chute_top - frame_w, + cx + chute_half_w + frame_w, chute_bottom + frame_w, + ARMOR_LIGHT, EDGE_DARK, width=4 + ) + + # Inner dark opening + draw.rectangle( + [cx - chute_half_w, chute_top, + cx + chute_half_w, chute_bottom], + fill=INTERIOR_BG + ) + + # Grinding rollers — three horizontal cylinders across the chute + roller_positions = [ + chute_top + (chute_bottom - chute_top) * 1 // 4, + chute_top + (chute_bottom - chute_top) * 2 // 4, + chute_top + (chute_bottom - chute_top) * 3 // 4, + ] + roller_radius = 32 + + for ry in roller_positions: + # Draw roller as a horizontal cylinder with shading + for dy in range(-roller_radius, roller_radius + 1): + t = abs(dy) / roller_radius + if active: + c = lerp_color(PISTON_SHAFT, PISTON_DARK, t) + else: + c = lerp_color(PISTON_MID, PISTON_DARK, t) + draw.line( + [(cx - chute_half_w + 6, ry + dy), + (cx + chute_half_w - 6, ry + dy)], + fill=c + ) + + # Roller edge lines + draw.line( + [(cx - chute_half_w + 6, ry - roller_radius), + (cx + chute_half_w - 6, ry - roller_radius)], + fill=EDGE_DARK, width=2 + ) + draw.line( + [(cx - chute_half_w + 6, ry + roller_radius), + (cx + chute_half_w - 6, ry + roller_radius)], + fill=EDGE_DARK, width=2 + ) + + # Roller teeth/ridges — vertical lines across the roller + ridge_spacing = 40 + for rx in range(cx - chute_half_w + 26, cx + chute_half_w - 20, + ridge_spacing): + for dy in range(-roller_radius + 4, roller_radius - 3): + t = abs(dy) / roller_radius + ridge_c = lerp_color(ARMOR_LIGHT, EDGE_DARK, t) + draw.point((rx, ry + dy), fill=ridge_c) + draw.point((rx + 1, ry + dy), fill=ridge_c) + + # Roller bearing caps on each end + for bx in [cx - chute_half_w + 6, cx + chute_half_w - 6]: + draw_filled_circle(draw, bx, ry, roller_radius - 4, + PISTON_DARK, outline=EDGE_DARK) + draw_filled_circle(draw, bx, ry, 10, RIVET_COLOR, + outline=EDGE_DARK) + + if active: + # Crushed stone particles between rollers + import random + rng = random.Random(42) # deterministic seed + for i in range(60): + px = rng.randint(cx - chute_half_w + 20, + cx + chute_half_w - 20) + # Place particles between rollers and below the last one + zone = rng.choice([0, 1, 2, 3]) + if zone == 0: + py = rng.randint(chute_top + 8, + roller_positions[0] - roller_radius - 4) + elif zone == 1: + py = rng.randint( + roller_positions[0] + roller_radius + 4, + roller_positions[1] - roller_radius - 4) + elif zone == 2: + py = rng.randint( + roller_positions[1] + roller_radius + 4, + roller_positions[2] - roller_radius - 4) + else: + py = rng.randint( + roller_positions[2] + roller_radius + 4, + chute_bottom - 8) + size = rng.randint(4, 12) + sc = rng.choice([COBBLE_DARK, COBBLE_MID, COBBLE_LIGHT, + STONE_DARK, STONE_MID]) + draw.rectangle([px, py, px + size, py + size], fill=sc) + + # Chute outline on top + draw.rectangle( + [cx - chute_half_w, chute_top, + cx + chute_half_w, chute_bottom], + outline=EDGE_DARK + ) + + # Frame bolts + bolt_positions = [ + (cx - chute_half_w - frame_w + 10, chute_top + 10), + (cx + chute_half_w + frame_w - 10, chute_top + 10), + (cx - chute_half_w - frame_w + 10, chute_bottom - 10), + (cx + chute_half_w + frame_w - 10, chute_bottom - 10), + (cx - chute_half_w - frame_w + 10, S // 2), + (cx + chute_half_w + frame_w - 10, S // 2), + ] + for bx, by in bolt_positions: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + add_corner_bolts(draw) + + if active: + add_radial_glow(img, cx, int(S * 0.5), + int(S * 0.3), AMBER_GLOW, intensity=0.12) + + return img + + +# --------------------------------------------------------------------------- +# SOUTH (BACK) — power connection panel +# --------------------------------------------------------------------------- + +def make_south(active=False): + """Back face: hex armor with power connection panel and vents.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Large panel + panel_w = int(S * 0.50) + panel_h = int(S * 0.44) + px1 = cx - panel_w // 2 + py1 = cy - panel_h // 2 + px2 = cx + panel_w // 2 + py2 = cy + panel_h // 2 + + draw.rectangle([px1, py1, px2, py2], fill=ARMOR_MID) + add_bevel_border(draw, px1, py1, px2, py2, + EDGE_DARK, ARMOR_LIGHT, width=5) + + # Horizontal vent slats + slat_h = 10 + slat_gap = 18 + y = py1 + 20 + while y + slat_h < py2 - 14: + draw.rectangle( + [px1 + 18, y, px2 - 18, y + slat_h], + fill=(15, 17, 22, 255) + ) + draw.line( + [(px1 + 18, y + slat_h + 1), + (px2 - 18, y + slat_h + 1)], + fill=ARMOR_LIGHT, width=1 + ) + y += slat_h + slat_gap + + # Power connector circle at bottom of panel + conn_cy = py2 - 50 + conn_r = 30 + if active: + draw_filled_circle(draw, cx, conn_cy, conn_r + 4, + AMBER_GLOW, outline=EDGE_DARK) + draw_filled_circle(draw, cx, conn_cy, conn_r, + PISTON_DARK if not active else MECH_DIM, + outline=EDGE_DARK) + draw_filled_circle(draw, cx, conn_cy, 12, + RIVET_COLOR if not active else MECH_GLOW) + + # Panel bolts + bolt_off = 18 + for bx, by in [(px1 + bolt_off, py1 + bolt_off), + (px2 - bolt_off, py1 + bolt_off), + (px1 + bolt_off, py2 - bolt_off), + (px2 - bolt_off, py2 - bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Horizontal seams + m = 30 + if active: + add_glowing_seam( + img, (m, cy), (px1 - 10, cy), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (px2 + 10, cy), (S - m, cy), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=8, intensity=0.15 + ) + else: + draw.line([(m, cy), (px1 - 10, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(px2 + 10, cy), (S - m, cy)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + + if active: + add_radial_glow(img, cx, conn_cy, 50, + ORANGE_GLOW, intensity=0.15) + + return img + + +# --------------------------------------------------------------------------- +# EAST / WEST (SIDES) — hydraulic pistons / rams +# --------------------------------------------------------------------------- + +def make_side(active=False): + """Side face: hex armor with two vertical hydraulic pistons. + + Heavy piston assemblies run vertically, suggesting the crushing + force mechanism. When active, piston shafts glow with energy. + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Two vertical pistons + piston_w = 54 + piston_gap = int(S * 0.28) + piston_top = 55 + piston_bottom = S - 55 + + for piston_cx in [cx - piston_gap // 2, cx + piston_gap // 2]: + ppx1 = piston_cx - piston_w // 2 + ppx2 = piston_cx + piston_w // 2 + + # Piston housing (outer) + draw.rectangle( + [ppx1 - 8, piston_top, ppx2 + 8, piston_bottom], + fill=ARMOR_MID, outline=EDGE_DARK + ) + + # Piston shaft with gradient + if active: + for x in range(ppx1, ppx2 + 1): + t = abs(x - piston_cx) / (piston_w // 2) + c = lerp_color(PISTON_SHAFT, PISTON_MID, t) + draw.line([(x, piston_top + 10), (x, piston_bottom - 10)], + fill=c) + else: + for x in range(ppx1, ppx2 + 1): + t = abs(x - piston_cx) / (piston_w // 2) + c = lerp_color(PISTON_LIGHT, PISTON_DARK, t) + draw.line([(x, piston_top + 10), (x, piston_bottom - 10)], + fill=c) + + # Piston edge lines + draw.line([(ppx1, piston_top + 10), (ppx1, piston_bottom - 10)], + fill=EDGE_DARK, width=2) + draw.line([(ppx2, piston_top + 10), (ppx2, piston_bottom - 10)], + fill=EDGE_DARK, width=2) + + # Piston clamp brackets + clamp_color = ARMOR_LIGHT if not active else (90, 80, 55, 255) + for clamp_y in range(piston_top + 80, piston_bottom - 60, 160): + clamp_h = 20 + draw.rectangle( + [ppx1 - 14, clamp_y, ppx2 + 14, clamp_y + clamp_h], + fill=clamp_color, outline=EDGE_DARK + ) + draw_filled_circle(draw, ppx1 - 8, clamp_y + clamp_h // 2, + 5, RIVET_COLOR) + draw_filled_circle(draw, ppx2 + 8, clamp_y + clamp_h // 2, + 5, RIVET_COLOR) + + # Piston head cap (top) + draw.rectangle( + [ppx1 - 4, piston_top, ppx2 + 4, piston_top + 30], + fill=ARMOR_LIGHT, outline=EDGE_DARK + ) + add_bevel_border(draw, ppx1 - 4, piston_top, ppx2 + 4, + piston_top + 30, (85, 90, 105, 255), + EDGE_DARK, width=2) + + # Piston foot (bottom) + draw.rectangle( + [ppx1 - 4, piston_bottom - 30, ppx2 + 4, piston_bottom], + fill=ARMOR_LIGHT, outline=EDGE_DARK + ) + add_bevel_border(draw, ppx1 - 4, piston_bottom - 30, ppx2 + 4, + piston_bottom, (85, 90, 105, 255), + EDGE_DARK, width=2) + + # Horizontal seam between pistons + m = 30 + left_edge = cx - piston_gap // 2 - piston_w // 2 - 22 + right_edge = cx + piston_gap // 2 + piston_w // 2 + 22 + if active: + add_glowing_seam( + img, (m, cy), (left_edge, cy), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=6, intensity=0.12 + ) + add_glowing_seam( + img, (right_edge, cy), (S - m, cy), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=6, intensity=0.12 + ) + else: + draw.line([(m, cy), (left_edge, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(right_edge, cy), (S - m, cy)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + + if active: + for piston_cx in [cx - piston_gap // 2, cx + piston_gap // 2]: + add_radial_glow(img, piston_cx, cy, + piston_w + 20, AMBER_GLOW, + intensity=0.08) + + return img + + +# --------------------------------------------------------------------------- +# UP (TOP) — intake hopper +# --------------------------------------------------------------------------- + +def make_top(active=False): + """Top face: square intake hopper with thick armored rim. + + A large square hopper opening in the center where raw materials + could be fed in. Stone texture visible inside when idle, + glowing conveyor energy when active. + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Hopper outer rim + hopper_outer = int(S * 0.34) + hopper_inner = int(S * 0.26) + + # Thick metallic rim + draw.rectangle( + [cx - hopper_outer, cy - hopper_outer, + cx + hopper_outer, cy + hopper_outer], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - hopper_outer, cy - hopper_outer, + cx + hopper_outer, cy + hopper_outer, + ARMOR_LIGHT, EDGE_DARK, width=5 + ) + + # Inner opening + draw.rectangle( + [cx - hopper_inner, cy - hopper_inner, + cx + hopper_inner, cy + hopper_inner], + fill=INTERIOR_BG + ) + + if active: + # Warm interior glow suggesting active machinery below + for r in range(hopper_inner, 0, -1): + t = r / hopper_inner + c = lerp_color(MECH_DIM, INTERIOR_BG, t * 0.8) + draw.rectangle( + [cx - r, cy - r, cx + r, cy + r], + outline=c + ) + else: + # Visible stone rubble inside the hopper + import random + rng = random.Random(99) + for i in range(30): + sx = rng.randint(cx - hopper_inner + 10, + cx + hopper_inner - 20) + sy = rng.randint(cy - hopper_inner + 10, + cy + hopper_inner - 20) + sz = rng.randint(8, 22) + sc = rng.choice([STONE_DARK, STONE_MID, COBBLE_DARK, + COBBLE_MID]) + draw.rectangle([sx, sy, sx + sz, sy + sz], fill=sc, + outline=EDGE_DARK) + + # Inner opening outline on top + draw.rectangle( + [cx - hopper_inner, cy - hopper_inner, + cx + hopper_inner, cy + hopper_inner], + outline=EDGE_DARK + ) + + # Rim bolts + bolt_positions = [ + (cx - hopper_outer + 16, cy - hopper_outer + 16), + (cx + hopper_outer - 16, cy - hopper_outer + 16), + (cx - hopper_outer + 16, cy + hopper_outer - 16), + (cx + hopper_outer - 16, cy + hopper_outer - 16), + (cx, cy - hopper_outer + 16), + (cx, cy + hopper_outer - 16), + (cx - hopper_outer + 16, cy), + (cx + hopper_outer - 16, cy), + ] + for bx, by in bolt_positions: + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Cross seams from corners to hopper + m = 30 + h_edge = hopper_outer + 16 + if active: + for sx, sy, ex, ey in [ + (m, m, cx - h_edge, cy - h_edge), + (S - m, m, cx + h_edge, cy - h_edge), + (m, S - m, cx - h_edge, cy + h_edge), + (S - m, S - m, cx + h_edge, cy + h_edge), + ]: + add_glowing_seam( + img, (sx, sy), (ex, ey), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=6, intensity=0.10 + ) + else: + for sx, sy, ex, ey in [ + (m, m, cx - h_edge, cy - h_edge), + (S - m, m, cx + h_edge, cy - h_edge), + (m, S - m, cx - h_edge, cy + h_edge), + (S - m, S - m, cx + h_edge, cy + h_edge), + ]: + draw.line([(sx, sy), (ex, ey)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + + if active: + add_radial_glow(img, cx, cy, hopper_inner + 30, + ORANGE_GLOW, intensity=0.15) + + return img + + +# --------------------------------------------------------------------------- +# DOWN (BOTTOM) — reinforced base plate +# --------------------------------------------------------------------------- + +def make_bottom(active=False): + """Bottom face: heavy reinforced base plate with central vent.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Central vent grille + grille_size = 360 + g1 = cx - grille_size // 2 + g2 = cx + grille_size // 2 + draw.rectangle([g1, g1, g2, g2], fill=ARMOR_MID, + outline=ARMOR_LIGHT) + add_bevel_border(draw, g1, g1, g2, g2, ARMOR_LIGHT, EDGE_DARK, + width=3) + + # Vent slots + for vy in range(g1 + 24, g2 - 16, 22): + draw.rectangle([g1 + 18, vy, g2 - 18, vy + 8], + fill=EDGE_DARK) + draw.rectangle([g1 + 20, vy + 1, g2 - 20, vy + 7], + fill=(15, 17, 22, 255)) + + # Mounting feet in corners + foot_size = 70 + foot_inset = 55 + for fx, fy in [(foot_inset, foot_inset), + (S - foot_inset - foot_size, foot_inset), + (foot_inset, S - foot_inset - foot_size), + (S - foot_inset - foot_size, + S - foot_inset - foot_size)]: + draw.rectangle([fx, fy, fx + foot_size, fy + foot_size], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + add_bevel_border(draw, fx, fy, fx + foot_size, + fy + foot_size, + (80, 86, 100, 255), EDGE_DARK, width=2) + fcx = fx + foot_size // 2 + fcy = fy + foot_size // 2 + draw_filled_circle(draw, fcx, fcy, 12, ARMOR_MID, + outline=EDGE_DARK) + draw_filled_circle(draw, fcx, fcy, 6, RIVET_COLOR) + + # Cross seams + draw.line([(cx, foot_inset + foot_size), (cx, g1)], + fill=SEAM_COLOR, width=3) + draw.line([(cx, g2), (cx, S - foot_inset - foot_size)], + fill=SEAM_COLOR, width=3) + draw.line([(foot_inset + foot_size, cy), (g1, cy)], + fill=SEAM_COLOR, width=3) + draw.line([(g2, cy), (S - foot_inset - foot_size, cy)], + fill=SEAM_COLOR, width=3) + + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + textures = {} + + for active in (False, True): + state = "active" if active else "idle" + + textures[f"cobblestone_factory_{state}_north"] = make_north( + active=active + ) + textures[f"cobblestone_factory_{state}_south"] = make_south( + active=active + ) + textures[f"cobblestone_factory_{state}_east"] = make_side( + active=active + ) + textures[f"cobblestone_factory_{state}_west"] = make_side( + active=active + ) + textures[f"cobblestone_factory_{state}_up"] = make_top( + active=active + ) + textures[f"cobblestone_factory_{state}_down"] = make_bottom( + active=active + ) + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_conveyor_belt_textures.py b/scripts/generate_conveyor_belt_textures.py new file mode 100644 index 0000000..6f7639a --- /dev/null +++ b/scripts/generate_conveyor_belt_textures.py @@ -0,0 +1,621 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Conveyor Belt at 1024x1024. + +Concept: A low-profile industrial transport belt with a sci-fi aesthetic. +The block is only 6/16 tall (slab height), so side faces render only the +bottom 37.5% of the texture (UV rows 10-16 in Minecraft's 0-16 space). + +The top face is the most visible — it shows a dark metallic belt surface +with chevron ridges indicating transport direction, framed by armored +rails on the edges. The front (output) end has an amber output indicator. +The back (input) end is a darker intake. Sides show the belt mechanism +with visible rollers. + +Creates 8 textures: + - conveyor_belt_front (output end) + - conveyor_belt_back (input end) + - conveyor_belt_side (left/right sides) + - conveyor_belt_bottom (underside) + - conveyor_belt_top_north/south/east/west (directional top faces) +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor (consistent with other Atlas blocks) +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Belt surface colors +BELT_DARK = (32, 34, 40, 255) +BELT_MID = (44, 47, 55, 255) +BELT_LIGHT = (56, 60, 70, 255) + +# Chevron colors — amber/orange +CHEVRON_FILL = (140, 95, 20, 255) +CHEVRON_BRIGHT = (180, 120, 30, 255) +CHEVRON_OUTLINE = (100, 70, 15, 255) + +# Output indicator (front) +OUTPUT_AMBER = (200, 130, 25, 255) +OUTPUT_GLOW = (255, 170, 40, 255) + +# Input indicator (back) +INPUT_DIM = (50, 55, 65, 255) + +# Roller colors +ROLLER_DARK = (45, 48, 56, 255) +ROLLER_LIGHT = (85, 90, 104, 255) + +# Hex geometry +HEX_RADIUS = 28 + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _hex_vertices(cx, cy, radius): + """Return 6 vertices for a flat-top hexagon.""" + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + """Compute hex cell center positions covering the image.""" + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + """Draw a filled circle.""" + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + """Draw a beveled rectangle border (raised appearance).""" + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +def make_hex_armor_base(): + """Create a hex armor face — full 1024x1024 base.""" + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + + return img + + +def add_corner_bolts(draw, inset=30): + """Add bolts at the four corners of a face.""" + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +def draw_chevron_north(draw, tip_x, tip_y, arm_len, thickness, half_span): + """Draw a single upward-pointing chevron (^) as two polygon arms. + + Args: + tip_x, tip_y: The point of the chevron (topmost point). + arm_len: Length along each diagonal arm. + thickness: Thickness of the chevron arms. + half_span: Half the total width from tip to each arm end. + """ + # Left arm: from tip going down-left + # Right arm: from tip going down-right + # Outer V + left_out = (tip_x - half_span, tip_y + half_span) + right_out = (tip_x + half_span, tip_y + half_span) + tip = (tip_x, tip_y) + # Inner V (shifted down by thickness) + left_in = (tip_x - half_span, tip_y + half_span + thickness) + right_in = (tip_x + half_span, tip_y + half_span + thickness) + tip_in = (tip_x, tip_y + thickness) + + # Draw as a single polygon (outer V minus inner V) + # Left arm polygon + draw.polygon([ + tip, left_out, + left_in, tip_in, + ], fill=CHEVRON_FILL, outline=CHEVRON_OUTLINE) + # Right arm polygon + draw.polygon([ + tip, right_out, + right_in, tip_in, + ], fill=CHEVRON_FILL, outline=CHEVRON_OUTLINE) + + +def draw_chevron_south(draw, tip_x, tip_y, thickness, half_span): + """Draw a downward-pointing chevron (v).""" + left_out = (tip_x - half_span, tip_y - half_span) + right_out = (tip_x + half_span, tip_y - half_span) + tip = (tip_x, tip_y) + left_in = (tip_x - half_span, tip_y - half_span - thickness) + right_in = (tip_x + half_span, tip_y - half_span - thickness) + tip_in = (tip_x, tip_y - thickness) + + draw.polygon([ + tip, left_out, left_in, tip_in, + ], fill=CHEVRON_FILL, outline=CHEVRON_OUTLINE) + draw.polygon([ + tip, right_out, right_in, tip_in, + ], fill=CHEVRON_FILL, outline=CHEVRON_OUTLINE) + + +def draw_chevron_east(draw, tip_x, tip_y, thickness, half_span): + """Draw a rightward-pointing chevron (>).""" + top_out = (tip_x - half_span, tip_y - half_span) + bot_out = (tip_x - half_span, tip_y + half_span) + tip = (tip_x, tip_y) + top_in = (tip_x - half_span - thickness, tip_y - half_span) + bot_in = (tip_x - half_span - thickness, tip_y + half_span) + tip_in = (tip_x - thickness, tip_y) + + draw.polygon([ + tip, top_out, top_in, tip_in, + ], fill=CHEVRON_FILL, outline=CHEVRON_OUTLINE) + draw.polygon([ + tip, bot_out, bot_in, tip_in, + ], fill=CHEVRON_FILL, outline=CHEVRON_OUTLINE) + + +def draw_chevron_west(draw, tip_x, tip_y, thickness, half_span): + """Draw a leftward-pointing chevron (<).""" + top_out = (tip_x + half_span, tip_y - half_span) + bot_out = (tip_x + half_span, tip_y + half_span) + tip = (tip_x, tip_y) + top_in = (tip_x + half_span + thickness, tip_y - half_span) + bot_in = (tip_x + half_span + thickness, tip_y + half_span) + tip_in = (tip_x + thickness, tip_y) + + draw.polygon([ + tip, top_out, top_in, tip_in, + ], fill=CHEVRON_FILL, outline=CHEVRON_OUTLINE) + draw.polygon([ + tip, bot_out, bot_in, tip_in, + ], fill=CHEVRON_FILL, outline=CHEVRON_OUTLINE) + + +# --------------------------------------------------------------------------- +# TOP faces — belt surface with directional chevrons +# --------------------------------------------------------------------------- + +def make_top(direction="north"): + """Top face: dark belt surface with chevron arrows showing direction. + + The belt surface fills the center with armored rail edges on the sides + perpendicular to the travel direction. Clean polygon chevrons point + in the travel direction. + + TILING: The texture tiles seamlessly along the travel axis. Rails run + edge-to-edge with no border on the travel-axis edges. Chevron spacing + (128) and tread ridge spacing (32) both divide evenly into 1024, and + chevrons start at offset 0 so the pattern wraps cleanly. + + Args: + direction: "north", "south", "east", "west" + """ + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + + horizontal = direction in ("east", "west") + + # Tileable spacings — must divide evenly into 1024 + chevron_spacing = 128 # 1024 / 128 = 8 chevrons per block + ridge_spacing = 32 # 1024 / 32 = 32 ridges per block + bolt_spacing = 256 # 1024 / 256 = 4 bolts per rail per block + chevron_thickness = 22 + + rail_w = 80 + + if horizontal: + # Rails on top and bottom edges — run full width, no border on + # left/right edges so adjacent belts connect seamlessly. + draw.rectangle([0, 0, S, rail_w], fill=ARMOR_MID) + draw.line([(0, rail_w), (S, rail_w)], fill=EDGE_DARK, width=3) + draw.rectangle([0, S - rail_w, S, S], fill=ARMOR_MID) + draw.line([(0, S - rail_w), (S, S - rail_w)], + fill=EDGE_DARK, width=3) + + # Rail bolts — tileable spacing + for rx in range(bolt_spacing // 2, S, bolt_spacing): + for ry in [rail_w // 2, S - rail_w // 2]: + draw_filled_circle(draw, rx, ry, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, rx, ry, 4, RIVET_COLOR) + + # Belt surface between rails — extends to full width for tiling + belt_y1 = rail_w + 2 + belt_y2 = S - rail_w - 2 + draw.rectangle([0, belt_y1, S, belt_y2], fill=BELT_DARK) + + # Belt tread ridges — vertical lines, tileable spacing + for rx in range(0, S, ridge_spacing): + draw.line([(rx, belt_y1 + 2), (rx, belt_y2 - 2)], + fill=BELT_MID, width=2) + + # Chevrons — tileable spacing, start at half-spacing offset + belt_cy = (belt_y1 + belt_y2) // 2 + half_span = (belt_y2 - belt_y1) // 2 - 30 + + if direction == "east": + for cx in range(chevron_spacing // 2, S, chevron_spacing): + draw_chevron_east(draw, cx, belt_cy, + chevron_thickness, half_span) + else: + for cx in range(S - chevron_spacing // 2, -1, + -chevron_spacing): + draw_chevron_west(draw, cx, belt_cy, + chevron_thickness, half_span) + else: + # Rails on left and right edges — run full height, no border on + # top/bottom edges so adjacent belts connect seamlessly. + draw.rectangle([0, 0, rail_w, S], fill=ARMOR_MID) + draw.line([(rail_w, 0), (rail_w, S)], fill=EDGE_DARK, width=3) + draw.rectangle([S - rail_w, 0, S, S], fill=ARMOR_MID) + draw.line([(S - rail_w, 0), (S - rail_w, S)], + fill=EDGE_DARK, width=3) + + # Rail bolts — tileable spacing + for ry in range(bolt_spacing // 2, S, bolt_spacing): + for rx in [rail_w // 2, S - rail_w // 2]: + draw_filled_circle(draw, rx, ry, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, rx, ry, 4, RIVET_COLOR) + + # Belt surface between rails — extends to full height for tiling + belt_x1 = rail_w + 2 + belt_x2 = S - rail_w - 2 + draw.rectangle([belt_x1, 0, belt_x2, S], fill=BELT_DARK) + + # Belt tread ridges — horizontal lines, tileable spacing + for ry in range(0, S, ridge_spacing): + draw.line([(belt_x1 + 2, ry), (belt_x2 - 2, ry)], + fill=BELT_MID, width=2) + + # Chevrons — tileable spacing, start at half-spacing offset + belt_cx = (belt_x1 + belt_x2) // 2 + half_span = (belt_x2 - belt_x1) // 2 - 30 + + if direction == "north": + for cy in range(S - chevron_spacing // 2, -1, + -chevron_spacing): + draw_chevron_north(draw, belt_cx, cy, 0, + chevron_thickness, half_span) + else: + for cy in range(chevron_spacing // 2, S, chevron_spacing): + draw_chevron_south(draw, belt_cx, cy, + chevron_thickness, half_span) + + return img + + +# --------------------------------------------------------------------------- +# FRONT face — output end with amber indicator +# --------------------------------------------------------------------------- + +def make_front(): + """Front face (output end): shows belt edge and amber output indicator. + + Only the bottom 37.5% of this texture is visible (UV 10-16). + The visible region is rows 640-1024. + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx = S // 2 + vis_top = int(S * 10 / 16) # 640 + vis_bot = S + vis_h = vis_bot - vis_top # 384 + + # Armored housing strip across the visible area + draw.rectangle([10, vis_top, S - 10, vis_bot - 10], + fill=ARMOR_MID) + add_bevel_border(draw, 10, vis_top, S - 10, vis_bot - 10, + ARMOR_LIGHT, EDGE_DARK, width=4) + + # Belt opening — wider and taller for visibility + belt_w = int(S * 0.64) + belt_h = int(vis_h * 0.45) + bx1 = cx - belt_w // 2 + by1 = vis_top + int(vis_h * 0.10) + bx2 = cx + belt_w // 2 + by2 = by1 + belt_h + + draw.rectangle([bx1, by1, bx2, by2], fill=BELT_DARK) + + # Belt tread lines inside the opening + for ry in range(by1 + 8, by2 - 4, 14): + draw.line([(bx1 + 6, ry), (bx2 - 6, ry)], + fill=BELT_MID, width=2) + + draw.rectangle([bx1, by1, bx2, by2], outline=EDGE_DARK) + + # Amber output indicator — larger triangle + arrow_cy = by2 + (vis_bot - 14 - by2) // 2 + arrow_sz = 40 + + tri_pts = [ + (cx - arrow_sz, arrow_cy - arrow_sz // 2), + (cx + arrow_sz, arrow_cy - arrow_sz // 2), + (cx, arrow_cy + arrow_sz // 2), + ] + draw.polygon(tri_pts, fill=OUTPUT_AMBER, outline=EDGE_DARK) + + # Glow around the arrow + add_radial_glow(img, cx, arrow_cy, 70, OUTPUT_GLOW, intensity=0.25) + + # Bolts flanking the belt opening + for bx in [bx1 - 50, bx2 + 50]: + if 20 < bx < S - 20: + draw_filled_circle(draw, bx, (by1 + by2) // 2, 10, + ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, bx, (by1 + by2) // 2, 5, + RIVET_COLOR) + + # Bottom corner bolts + for bx in [40, S - 40]: + draw_filled_circle(draw, bx, vis_bot - 28, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, vis_bot - 28, 4, RIVET_COLOR) + + return img + + +# --------------------------------------------------------------------------- +# BACK face — input end +# --------------------------------------------------------------------------- + +def make_back(): + """Back face (input end): darker intake with no glow. + + Only bottom 37.5% visible (rows 640-1024). + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx = S // 2 + vis_top = int(S * 10 / 16) + vis_bot = S + vis_h = vis_bot - vis_top + + # Armored housing strip + draw.rectangle([10, vis_top, S - 10, vis_bot - 10], + fill=ARMOR_MID) + add_bevel_border(draw, 10, vis_top, S - 10, vis_bot - 10, + ARMOR_LIGHT, EDGE_DARK, width=4) + + # Belt intake opening + belt_w = int(S * 0.64) + belt_h = int(vis_h * 0.45) + bx1 = cx - belt_w // 2 + by1 = vis_top + int(vis_h * 0.10) + bx2 = cx + belt_w // 2 + by2 = by1 + belt_h + + draw.rectangle([bx1, by1, bx2, by2], fill=BELT_DARK) + + # Belt tread lines + for ry in range(by1 + 8, by2 - 4, 14): + draw.line([(bx1 + 6, ry), (bx2 - 6, ry)], + fill=BELT_MID, width=2) + + draw.rectangle([bx1, by1, bx2, by2], outline=EDGE_DARK) + + # Intake indicator — dimmer, inward-pointing triangle (upward) + arrow_cy = by2 + (vis_bot - 14 - by2) // 2 + arrow_sz = 40 + + tri_pts = [ + (cx - arrow_sz, arrow_cy + arrow_sz // 2), + (cx + arrow_sz, arrow_cy + arrow_sz // 2), + (cx, arrow_cy - arrow_sz // 2), + ] + draw.polygon(tri_pts, fill=INPUT_DIM, outline=EDGE_DARK) + + # Bolts flanking the belt opening + for bx in [bx1 - 50, bx2 + 50]: + if 20 < bx < S - 20: + draw_filled_circle(draw, bx, (by1 + by2) // 2, 10, + ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, bx, (by1 + by2) // 2, 5, + RIVET_COLOR) + + # Bottom corner bolts + for bx in [40, S - 40]: + draw_filled_circle(draw, bx, vis_bot - 28, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, vis_bot - 28, 4, RIVET_COLOR) + + return img + + +# --------------------------------------------------------------------------- +# SIDE face — belt mechanism with rollers +# --------------------------------------------------------------------------- + +def make_side(): + """Side face: shows belt edge and roller mechanism. + + Only bottom 37.5% visible (rows 640-1024). Rollers are drawn as + shaded circles (cross-section view). + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + vis_top = int(S * 10 / 16) + vis_bot = S + vis_h = vis_bot - vis_top + + # Armored housing strip + draw.rectangle([10, vis_top, S - 10, vis_bot - 10], + fill=ARMOR_MID) + add_bevel_border(draw, 10, vis_top, S - 10, vis_bot - 10, + ARMOR_LIGHT, EDGE_DARK, width=4) + + # Belt track — dark band at the top of the visible area + track_h = int(vis_h * 0.28) + track_y1 = vis_top + 10 + track_y2 = track_y1 + track_h + draw.rectangle([18, track_y1, S - 18, track_y2], + fill=BELT_DARK, outline=EDGE_DARK) + + # Belt tread texture on the track + for ry in range(track_y1 + 6, track_y2 - 4, 12): + draw.line([(22, ry), (S - 22, ry)], + fill=BELT_MID, width=1) + + # Rollers underneath — drawn as shaded circles (cross-section) + roller_cy = track_y2 + int(vis_h * 0.22) + roller_r = 28 + roller_spacing = 170 + for rx in range(90, S - 60, roller_spacing): + # Shaded circle for roller cross-section + for dy in range(-roller_r, roller_r + 1): + for dx in range(-roller_r, roller_r + 1): + dist = math.sqrt(dx * dx + dy * dy) + if dist <= roller_r: + t = dist / roller_r + c = lerp_color(ROLLER_LIGHT, ROLLER_DARK, t) + px, py = rx + dx, roller_cy + dy + if 0 <= px < S and 0 <= py < S: + img.putpixel((px, py), c) + # Roller outline + draw_filled_circle(draw, rx, roller_cy, roller_r, None, + outline=EDGE_DARK) + # Axle dot + draw_filled_circle(draw, rx, roller_cy, 8, RIVET_COLOR, + outline=EDGE_DARK) + + # Horizontal seam below rollers + seam_y = roller_cy + roller_r + 16 + if seam_y < vis_bot - 20: + draw.line([(18, seam_y), (S - 18, seam_y)], + fill=SEAM_COLOR, width=2) + + # Bottom corner bolts + for bx in [40, S - 40]: + draw_filled_circle(draw, bx, vis_bot - 28, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, vis_bot - 28, 4, RIVET_COLOR) + + return img + + +# --------------------------------------------------------------------------- +# BOTTOM face — reinforced base plate +# --------------------------------------------------------------------------- + +def make_bottom(): + """Bottom face: flat reinforced base plate with grid pattern.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Central grid plate + plate_inset = 60 + px1 = plate_inset + py1 = plate_inset + px2 = S - plate_inset + py2 = S - plate_inset + + draw.rectangle([px1, py1, px2, py2], fill=ARMOR_MID) + add_bevel_border(draw, px1, py1, px2, py2, ARMOR_LIGHT, EDGE_DARK, + width=3) + + # Grid lines + grid_spacing = 64 + for gx in range(px1 + grid_spacing, px2, grid_spacing): + draw.line([(gx, py1 + 6), (gx, py2 - 6)], + fill=SEAM_COLOR, width=2) + for gy in range(py1 + grid_spacing, py2, grid_spacing): + draw.line([(px1 + 6, gy), (px2 - 6, gy)], + fill=SEAM_COLOR, width=2) + + # Corner mounting bolts + bolt_inset = 40 + for bx, by in [(px1 + bolt_inset, py1 + bolt_inset), + (px2 - bolt_inset, py1 + bolt_inset), + (px1 + bolt_inset, py2 - bolt_inset), + (px2 - bolt_inset, py2 - bolt_inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + add_corner_bolts(draw) + + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + textures = { + "conveyor_belt_front": make_front(), + "conveyor_belt_back": make_back(), + "conveyor_belt_side": make_side(), + "conveyor_belt_bottom": make_bottom(), + "conveyor_belt_top_north": make_top("north"), + "conveyor_belt_top_south": make_top("south"), + "conveyor_belt_top_east": make_top("east"), + "conveyor_belt_top_west": make_top("west"), + } + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_crusher_textures.py b/scripts/generate_crusher_textures.py new file mode 100644 index 0000000..e6b0c1b --- /dev/null +++ b/scripts/generate_crusher_textures.py @@ -0,0 +1,723 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Crusher at 1024x1024. + +Concept: Heavy industrial ore crusher with visible jaw mechanism. +The front face shows interlocking crusher jaws/teeth — jagged triangular +teeth from top and bottom meeting in the center. The sides show a power +connector and heavy armor plating. The top has a square intake hopper +with a directional arrow (idle) or glowing active indicator (powered). +The back has an exhaust/output panel. The bottom is a reinforced base. + +The crusher is directional (facing) and has a powered boolean state. +Idle top faces are directional (arrow), powered top is a single shared +texture with an active glow. + +Creates 9 textures: + - crusher_front (jaw teeth) + - crusher_back (output panel) + - crusher_side (power connector) + - crusher_housing (bottom base) + - crusher_top_north/south/east/west (directional idle tops) + - crusher_top_active (powered top, shared) +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor (consistent with other Atlas blocks) +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Crusher jaw / teeth colors +JAW_DARK = (55, 58, 65, 255) +JAW_MID = (75, 80, 90, 255) +JAW_LIGHT = (95, 100, 115, 255) +JAW_EDGE = (110, 115, 130, 255) +TOOTH_TIP = (140, 145, 160, 255) + +# Interior +INTERIOR_BG = (10, 12, 16, 255) + +# Power connector — red for power input +POWER_RED = (180, 40, 40, 255) +POWER_RED_BRIGHT = (220, 60, 50, 255) +POWER_RED_DIM = (100, 30, 30, 255) + +# Active glow — orange-amber +AMBER_GLOW = (255, 140, 30, 255) +ORANGE_GLOW = (255, 120, 20, 255) +ACTIVE_FILL = (200, 120, 20, 255) + +# Arrow color for directional indicator +ARROW_DIM = (70, 75, 88, 255) + +# Hex geometry +HEX_RADIUS = 28 + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _hex_vertices(cx, cy, radius): + """Return 6 vertices for a flat-top hexagon.""" + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + """Compute hex cell center positions covering the image.""" + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + """Draw a filled circle.""" + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + """Draw a beveled rectangle border (raised appearance).""" + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +def make_hex_armor_base(): + """Create a hex armor face — full 1024x1024 base.""" + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + + return img + + +def add_corner_bolts(draw, inset=30): + """Add bolts at the four corners of a face.""" + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +# --------------------------------------------------------------------------- +# FRONT — crusher jaw teeth +# --------------------------------------------------------------------------- + +def make_front(): + """Front face: interlocking crusher jaw teeth. + + Large jagged teeth from top and bottom interlock in the center, + with dark interior visible between them. Heavy armored frame + surrounds the jaw opening. + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx = S // 2 + + # Jaw opening frame + frame_w = 28 + jaw_x1 = int(S * 0.12) + jaw_x2 = int(S * 0.88) + jaw_y1 = int(S * 0.10) + jaw_y2 = int(S * 0.90) + + # Heavy outer frame + draw.rectangle( + [jaw_x1 - frame_w, jaw_y1 - frame_w, + jaw_x2 + frame_w, jaw_y2 + frame_w], + fill=ARMOR_MID + ) + add_bevel_border( + draw, jaw_x1 - frame_w, jaw_y1 - frame_w, + jaw_x2 + frame_w, jaw_y2 + frame_w, + ARMOR_LIGHT, EDGE_DARK, width=4 + ) + + # Dark interior + draw.rectangle([jaw_x1, jaw_y1, jaw_x2, jaw_y2], fill=INTERIOR_BG) + + # Crusher teeth from top + tooth_w = 80 + tooth_h = 180 + num_teeth = (jaw_x2 - jaw_x1) // tooth_w + tooth_start_x = jaw_x1 + ((jaw_x2 - jaw_x1) - num_teeth * tooth_w) // 2 + + for i in range(num_teeth): + tx = tooth_start_x + i * tooth_w + tooth_w // 2 + # Top tooth — triangle pointing down + top_pts = [ + (tx - tooth_w // 2 + 4, jaw_y1), + (tx + tooth_w // 2 - 4, jaw_y1), + (tx, jaw_y1 + tooth_h), + ] + draw.polygon(top_pts, fill=JAW_MID, outline=EDGE_DARK) + # Highlight on left edge + draw.line([top_pts[0], top_pts[2]], fill=JAW_LIGHT, width=3) + # Dark on right edge + draw.line([top_pts[1], top_pts[2]], fill=JAW_DARK, width=3) + # Tooth tip highlight + draw_filled_circle(draw, tx, jaw_y1 + tooth_h - 8, 6, + TOOTH_TIP) + + # Crusher teeth from bottom + for i in range(num_teeth): + tx = tooth_start_x + i * tooth_w + tooth_w // 2 + # Offset by half a tooth width for interlocking + tx_bot = tx + tooth_w // 2 + if tx_bot > jaw_x2 - 10: + continue + # Bottom tooth — triangle pointing up + bot_pts = [ + (tx_bot - tooth_w // 2 + 4, jaw_y2), + (tx_bot + tooth_w // 2 - 4, jaw_y2), + (tx_bot, jaw_y2 - tooth_h), + ] + draw.polygon(bot_pts, fill=JAW_MID, outline=EDGE_DARK) + draw.line([bot_pts[0], bot_pts[2]], fill=JAW_LIGHT, width=3) + draw.line([bot_pts[1], bot_pts[2]], fill=JAW_DARK, width=3) + draw_filled_circle(draw, tx_bot, jaw_y2 - tooth_h + 8, 6, + TOOTH_TIP) + + # Also add partial bottom teeth at the edges for interlocking feel + # First bottom tooth (left-aligned) + tx_first = tooth_start_x + if tx_first >= jaw_x1 + 10: + bot_pts = [ + (tx_first - tooth_w // 2 + 4, jaw_y2), + (tx_first + tooth_w // 2 - 4, jaw_y2), + (tx_first, jaw_y2 - tooth_h), + ] + draw.polygon(bot_pts, fill=JAW_MID, outline=EDGE_DARK) + draw.line([bot_pts[0], bot_pts[2]], fill=JAW_LIGHT, width=3) + draw.line([bot_pts[1], bot_pts[2]], fill=JAW_DARK, width=3) + draw_filled_circle(draw, tx_first, jaw_y2 - tooth_h + 8, 6, + TOOTH_TIP) + + # Frame outline on top + draw.rectangle([jaw_x1, jaw_y1, jaw_x2, jaw_y2], outline=EDGE_DARK) + + # Frame bolts + for bx in [jaw_x1 - frame_w // 2, jaw_x2 + frame_w // 2]: + for by in [jaw_y1 - frame_w // 2, jaw_y2 + frame_w // 2, + S // 2]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + add_corner_bolts(draw) + + return img + + +# --------------------------------------------------------------------------- +# BACK — output panel +# --------------------------------------------------------------------------- + +def make_back(): + """Back face: hex armor with output vent panel.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Output vent panel + panel_w = int(S * 0.48) + panel_h = int(S * 0.42) + px1 = cx - panel_w // 2 + py1 = cy - panel_h // 2 + px2 = cx + panel_w // 2 + py2 = cy + panel_h // 2 + + draw.rectangle([px1, py1, px2, py2], fill=ARMOR_MID) + add_bevel_border(draw, px1, py1, px2, py2, + EDGE_DARK, ARMOR_LIGHT, width=5) + + # Horizontal vent slats + slat_h = 10 + slat_gap = 18 + y = py1 + 20 + while y + slat_h < py2 - 14: + draw.rectangle( + [px1 + 18, y, px2 - 18, y + slat_h], + fill=(15, 17, 22, 255) + ) + draw.line( + [(px1 + 18, y + slat_h + 1), + (px2 - 18, y + slat_h + 1)], + fill=ARMOR_LIGHT, width=1 + ) + y += slat_h + slat_gap + + # Panel bolts + bolt_off = 18 + for bx, by in [(px1 + bolt_off, py1 + bolt_off), + (px2 - bolt_off, py1 + bolt_off), + (px1 + bolt_off, py2 - bolt_off), + (px2 - bolt_off, py2 - bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Horizontal seams + m = 30 + draw.line([(m, cy), (px1 - 10, cy)], fill=SEAM_COLOR, width=2) + draw.line([(px2 + 10, cy), (S - m, cy)], fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + + return img + + +# --------------------------------------------------------------------------- +# SIDE — power connector and armor plating +# --------------------------------------------------------------------------- + +def make_side(): + """Side face: hex armor with red power input connector circle.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Power connector — red circle (power input) + conn_outer_r = 90 + conn_inner_r = 60 + + # Connector mounting plate + plate_r = conn_outer_r + 30 + draw_filled_circle(draw, cx, cy, plate_r, ARMOR_MID, + outline=EDGE_DARK) + + # Outer ring gradient + for r in range(conn_outer_r, conn_inner_r, -1): + t = (conn_outer_r - r) / (conn_outer_r - conn_inner_r) + c = lerp_color(POWER_RED_DIM, POWER_RED, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Outer/inner ring edges + draw.ellipse( + [cx - conn_outer_r, cy - conn_outer_r, + cx + conn_outer_r, cy + conn_outer_r], + outline=EDGE_DARK + ) + draw.ellipse( + [cx - conn_inner_r, cy - conn_inner_r, + cx + conn_inner_r, cy + conn_inner_r], + outline=EDGE_DARK + ) + + # Inner dark center + draw_filled_circle(draw, cx, cy, conn_inner_r - 2, INTERIOR_BG) + + # Center contact pin + draw_filled_circle(draw, cx, cy, 18, POWER_RED_DIM, + outline=EDGE_DARK) + draw_filled_circle(draw, cx, cy, 8, RIVET_COLOR) + + # Mounting bolts around the plate + bolt_count = 8 + bolt_r = plate_r - 14 + for i in range(bolt_count): + angle = math.radians(360 * i / bolt_count) + bx = int(cx + bolt_r * math.cos(angle)) + by = int(cy + bolt_r * math.sin(angle)) + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Horizontal seam + m = 30 + draw.line([(m, cy), (cx - plate_r - 10, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(cx + plate_r + 10, cy), (S - m, cy)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + + return img + + +# --------------------------------------------------------------------------- +# TOP — directional intake hopper (idle) and active glow +# --------------------------------------------------------------------------- + +def make_top(direction="north"): + """Top face (idle): square intake hopper with directional arrow. + + A square opening in the center for ore input, with an arrow + indicating the facing/output direction. + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Square hopper opening + hopper_size = int(S * 0.30) + hx1 = cx - hopper_size + hy1 = cy - hopper_size + hx2 = cx + hopper_size + hy2 = cy + hopper_size + + # Hopper frame + frame_w = 30 + draw.rectangle( + [hx1 - frame_w, hy1 - frame_w, hx2 + frame_w, hy2 + frame_w], + fill=ARMOR_MID + ) + add_bevel_border( + draw, hx1 - frame_w, hy1 - frame_w, + hx2 + frame_w, hy2 + frame_w, + ARMOR_LIGHT, EDGE_DARK, width=4 + ) + + # Dark hopper interior + draw.rectangle([hx1, hy1, hx2, hy2], fill=INTERIOR_BG) + + # Crusher jaw teeth visible inside the hopper (top and bottom rows) + tooth_w = 60 + tooth_h = 80 + num_teeth = (hx2 - hx1) // tooth_w + + for i in range(num_teeth): + tx = hx1 + i * tooth_w + tooth_w // 2 + # Top teeth pointing down + pts_top = [ + (tx - tooth_w // 2 + 4, hy1 + 2), + (tx + tooth_w // 2 - 4, hy1 + 2), + (tx, hy1 + tooth_h), + ] + draw.polygon(pts_top, fill=JAW_DARK, outline=EDGE_DARK) + + # Bottom teeth pointing up + pts_bot = [ + (tx - tooth_w // 2 + 4, hy2 - 2), + (tx + tooth_w // 2 - 4, hy2 - 2), + (tx, hy2 - tooth_h), + ] + draw.polygon(pts_bot, fill=JAW_DARK, outline=EDGE_DARK) + + # Hopper outline + draw.rectangle([hx1, hy1, hx2, hy2], outline=EDGE_DARK) + + # Hopper frame bolts + for bx, by in [ + (hx1 - frame_w // 2, hy1 - frame_w // 2), + (hx2 + frame_w // 2, hy1 - frame_w // 2), + (hx1 - frame_w // 2, hy2 + frame_w // 2), + (hx2 + frame_w // 2, hy2 + frame_w // 2), + (cx, hy1 - frame_w // 2), + (cx, hy2 + frame_w // 2), + (hx1 - frame_w // 2, cy), + (hx2 + frame_w // 2, cy), + ]: + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Directional arrow outside the hopper pointing toward facing direction + arrow_len = 100 + arrow_w = 50 + arrow_head_w = 80 + arrow_head_len = 60 + + if direction == "north": + # Arrow pointing up + ay = hy1 - frame_w - 30 + # Shaft + draw.rectangle( + [cx - arrow_w // 2, ay - arrow_len, + cx + arrow_w // 2, ay], + fill=ARROW_DIM, outline=EDGE_DARK + ) + # Head + draw.polygon([ + (cx - arrow_head_w, ay - arrow_len), + (cx + arrow_head_w, ay - arrow_len), + (cx, ay - arrow_len - arrow_head_len), + ], fill=ARROW_DIM, outline=EDGE_DARK) + elif direction == "south": + ay = hy2 + frame_w + 30 + draw.rectangle( + [cx - arrow_w // 2, ay, + cx + arrow_w // 2, ay + arrow_len], + fill=ARROW_DIM, outline=EDGE_DARK + ) + draw.polygon([ + (cx - arrow_head_w, ay + arrow_len), + (cx + arrow_head_w, ay + arrow_len), + (cx, ay + arrow_len + arrow_head_len), + ], fill=ARROW_DIM, outline=EDGE_DARK) + elif direction == "east": + ax = hx2 + frame_w + 30 + draw.rectangle( + [ax, cy - arrow_w // 2, + ax + arrow_len, cy + arrow_w // 2], + fill=ARROW_DIM, outline=EDGE_DARK + ) + draw.polygon([ + (ax + arrow_len, cy - arrow_head_w), + (ax + arrow_len, cy + arrow_head_w), + (ax + arrow_len + arrow_head_len, cy), + ], fill=ARROW_DIM, outline=EDGE_DARK) + elif direction == "west": + ax = hx1 - frame_w - 30 + draw.rectangle( + [ax - arrow_len, cy - arrow_w // 2, + ax, cy + arrow_w // 2], + fill=ARROW_DIM, outline=EDGE_DARK + ) + draw.polygon([ + (ax - arrow_len, cy - arrow_head_w), + (ax - arrow_len, cy + arrow_head_w), + (ax - arrow_len - arrow_head_len, cy), + ], fill=ARROW_DIM, outline=EDGE_DARK) + + # Corner seams + m = 30 + h_edge = hx2 + frame_w + 10 + for sx, sy, ex, ey in [ + (m, m, hx1 - frame_w - 10, hy1 - frame_w - 10), + (S - m, m, hx2 + frame_w + 10, hy1 - frame_w - 10), + (m, S - m, hx1 - frame_w - 10, hy2 + frame_w + 10), + (S - m, S - m, hx2 + frame_w + 10, hy2 + frame_w + 10), + ]: + draw.line([(sx, sy), (ex, ey)], fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + + return img + + +def make_top_active(): + """Top face (powered): glowing hopper indicating active crushing. + + Same hopper layout but the interior glows orange-amber to show + the crusher is actively processing. + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Square hopper opening + hopper_size = int(S * 0.30) + hx1 = cx - hopper_size + hy1 = cy - hopper_size + hx2 = cx + hopper_size + hy2 = cy + hopper_size + + # Hopper frame + frame_w = 30 + draw.rectangle( + [hx1 - frame_w, hy1 - frame_w, hx2 + frame_w, hy2 + frame_w], + fill=ARMOR_MID + ) + add_bevel_border( + draw, hx1 - frame_w, hy1 - frame_w, + hx2 + frame_w, hy2 + frame_w, + ARMOR_LIGHT, EDGE_DARK, width=4 + ) + + # Glowing hopper interior — gradient from center + draw.rectangle([hx1, hy1, hx2, hy2], fill=INTERIOR_BG) + for r in range(hopper_size, 0, -1): + t = r / hopper_size + c = lerp_color(ACTIVE_FILL, INTERIOR_BG, t * 0.7) + draw.rectangle( + [cx - r, cy - r, cx + r, cy + r], + outline=c + ) + + # Crusher jaw teeth visible inside (dimmer, backlit by glow) + tooth_w = 60 + tooth_h = 70 + num_teeth = (hx2 - hx1) // tooth_w + + for i in range(num_teeth): + tx = hx1 + i * tooth_w + tooth_w // 2 + pts_top = [ + (tx - tooth_w // 2 + 4, hy1 + 2), + (tx + tooth_w // 2 - 4, hy1 + 2), + (tx, hy1 + tooth_h), + ] + draw.polygon(pts_top, fill=JAW_DARK, outline=EDGE_DARK) + + pts_bot = [ + (tx - tooth_w // 2 + 4, hy2 - 2), + (tx + tooth_w // 2 - 4, hy2 - 2), + (tx, hy2 - tooth_h), + ] + draw.polygon(pts_bot, fill=JAW_DARK, outline=EDGE_DARK) + + # Hopper outline + draw.rectangle([hx1, hy1, hx2, hy2], outline=EDGE_DARK) + + # Hopper frame bolts + for bx, by in [ + (hx1 - frame_w // 2, hy1 - frame_w // 2), + (hx2 + frame_w // 2, hy1 - frame_w // 2), + (hx1 - frame_w // 2, hy2 + frame_w // 2), + (hx2 + frame_w // 2, hy2 + frame_w // 2), + (cx, hy1 - frame_w // 2), + (cx, hy2 + frame_w // 2), + (hx1 - frame_w // 2, cy), + (hx2 + frame_w // 2, cy), + ]: + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Glowing corner seams + m = 30 + for sx, sy, ex, ey in [ + (m, m, hx1 - frame_w - 10, hy1 - frame_w - 10), + (S - m, m, hx2 + frame_w + 10, hy1 - frame_w - 10), + (m, S - m, hx1 - frame_w - 10, hy2 + frame_w + 10), + (S - m, S - m, hx2 + frame_w + 10, hy2 + frame_w + 10), + ]: + add_glowing_seam( + img, (sx, sy), (ex, ey), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=6, intensity=0.12 + ) + + add_corner_bolts(draw) + + # Radial glow from the hopper + add_radial_glow(img, cx, cy, hopper_size + 40, + ORANGE_GLOW, intensity=0.20) + + return img + + +# --------------------------------------------------------------------------- +# BOTTOM — reinforced base plate +# --------------------------------------------------------------------------- + +def make_bottom(): + """Bottom face: heavy reinforced base plate with vent grille.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Central vent grille + grille_size = 360 + g1 = cx - grille_size // 2 + g2 = cx + grille_size // 2 + draw.rectangle([g1, g1, g2, g2], fill=ARMOR_MID, + outline=ARMOR_LIGHT) + add_bevel_border(draw, g1, g1, g2, g2, ARMOR_LIGHT, EDGE_DARK, + width=3) + + # Vent slots + for vy in range(g1 + 24, g2 - 16, 22): + draw.rectangle([g1 + 18, vy, g2 - 18, vy + 8], + fill=EDGE_DARK) + draw.rectangle([g1 + 20, vy + 1, g2 - 20, vy + 7], + fill=(15, 17, 22, 255)) + + # Mounting feet in corners + foot_size = 70 + foot_inset = 55 + for fx, fy in [(foot_inset, foot_inset), + (S - foot_inset - foot_size, foot_inset), + (foot_inset, S - foot_inset - foot_size), + (S - foot_inset - foot_size, + S - foot_inset - foot_size)]: + draw.rectangle([fx, fy, fx + foot_size, fy + foot_size], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + add_bevel_border(draw, fx, fy, fx + foot_size, + fy + foot_size, + (80, 86, 100, 255), EDGE_DARK, width=2) + fcx = fx + foot_size // 2 + fcy = fy + foot_size // 2 + draw_filled_circle(draw, fcx, fcy, 12, ARMOR_MID, + outline=EDGE_DARK) + draw_filled_circle(draw, fcx, fcy, 6, RIVET_COLOR) + + # Cross seams + draw.line([(cx, foot_inset + foot_size), (cx, g1)], + fill=SEAM_COLOR, width=3) + draw.line([(cx, g2), (cx, S - foot_inset - foot_size)], + fill=SEAM_COLOR, width=3) + draw.line([(foot_inset + foot_size, cy), (g1, cy)], + fill=SEAM_COLOR, width=3) + draw.line([(g2, cy), (S - foot_inset - foot_size, cy)], + fill=SEAM_COLOR, width=3) + + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + textures = { + "crusher_front": make_front(), + "crusher_back": make_back(), + "crusher_side": make_side(), + "crusher_housing": make_bottom(), + "crusher_top_north": make_top("north"), + "crusher_top_south": make_top("south"), + "crusher_top_east": make_top("east"), + "crusher_top_west": make_top("west"), + "crusher_top_active": make_top_active(), + } + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_experience_extractor_textures.py b/scripts/generate_experience_extractor_textures.py new file mode 100644 index 0000000..72f943c --- /dev/null +++ b/scripts/generate_experience_extractor_textures.py @@ -0,0 +1,743 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Experience Extractor at 1024x1024. + +Concept: A mystical-industrial machine that extracts XP from items and +outputs Liquid Experience fluid. The front shows a large glass vial +chamber with a prominent XP orb and a fluid output nozzle. When active, +everything glows vivid green. The sides have a red power input connector. +The top has an intake hopper with directional arrow (idle) or green glow +(active). The back has an exhaust panel. The bottom is a reinforced base. + +Creates 10 textures: + - experience_extractor_front (idle) + - experience_extractor_front_active (powered) + - experience_extractor_back + - experience_extractor_side (power connector) + - experience_extractor_housing (bottom) + - experience_extractor_top_north/south/east/west (directional idle) + - experience_extractor_top_active (powered) +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# XP green +XP_DARK = (15, 80, 10, 255) +XP_MID = (30, 150, 20, 255) +XP_BRIGHT = (57, 255, 20, 255) +XP_GLOW = (80, 255, 40, 255) +XP_DIM = (20, 100, 15, 255) + +# Vial / glass tint +GLASS_DARK = (18, 35, 22, 255) +GLASS_MID = (25, 50, 30, 255) +GLASS_EDGE = (35, 65, 40, 255) + +# Power connector +POWER_RED = (180, 40, 40, 255) +POWER_RED_DIM = (100, 30, 30, 255) + +INTERIOR_BG = (10, 12, 16, 255) + +# Arrow +ARROW_COLOR = (30, 140, 20, 255) +ARROW_OUTLINE = (15, 80, 10, 255) + +HEX_RADIUS = 28 + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _hex_vertices(cx, cy, radius): + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +def make_hex_armor_base(): + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + return img + + +def add_corner_bolts(draw, inset=30): + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +def draw_xp_orb(draw, img, cx, cy, radius, active=False): + """Draw a diamond-shaped XP orb with facets and glow.""" + r = radius + diamond = [(cx, cy - r), (cx + r, cy), + (cx, cy + r), (cx - r, cy)] + + if active: + draw.polygon(diamond, fill=XP_BRIGHT, outline=XP_MID) + inner_r = int(r * 0.50) + inner = [(cx, cy - inner_r), (cx + inner_r, cy), + (cx, cy + inner_r), (cx - inner_r, cy)] + draw.polygon(inner, fill=XP_GLOW, outline=XP_BRIGHT) + draw_filled_circle(draw, cx - int(r * 0.18), + cy - int(r * 0.18), + int(r * 0.10), XP_GLOW) + add_radial_glow(img, cx, cy, int(r * 1.8), XP_GLOW, + intensity=0.30) + else: + draw.polygon(diamond, fill=XP_MID, outline=XP_DARK) + inner_r = int(r * 0.50) + inner = [(cx, cy - inner_r), (cx + inner_r, cy), + (cx, cy + inner_r), (cx - inner_r, cy)] + draw.polygon(inner, fill=XP_BRIGHT, outline=XP_MID) + draw_filled_circle(draw, cx - int(r * 0.18), + cy - int(r * 0.18), + int(r * 0.10), XP_GLOW) + add_radial_glow(img, cx, cy, int(r * 1.2), XP_MID, + intensity=0.10) + + +def draw_vial(draw, img, cx, top_y, bottom_y, half_w, neck_half_w, + neck_h, active=False): + """Draw a glass vial/flask shape — a narrow neck leading to a wide body. + + The vial is drawn as a trapezoidal neck tapering into a rectangular + body with rounded-feeling corners (beveled edges). + """ + neck_top = top_y + neck_bottom = top_y + neck_h + body_top = neck_bottom + body_bottom = bottom_y + + # Neck + neck_pts = [ + (cx - neck_half_w, neck_top), + (cx + neck_half_w, neck_top), + (cx + half_w, body_top), + (cx - half_w, body_top), + ] + fill_c = GLASS_MID if not active else lerp_color(GLASS_MID, XP_DIM, 0.5) + draw.polygon(neck_pts, fill=fill_c, outline=GLASS_EDGE) + + # Body + draw.rectangle([cx - half_w, body_top, cx + half_w, body_bottom], + fill=GLASS_DARK if not active else + lerp_color(GLASS_DARK, XP_DARK, 0.4)) + + # Glass rim at top of neck + draw.rectangle( + [cx - neck_half_w - 8, neck_top - 8, + cx + neck_half_w + 8, neck_top + 8], + fill=ARMOR_LIGHT, outline=EDGE_DARK + ) + + # Glass sheen lines (vertical highlight on left side of body) + sheen_x = cx - half_w + 16 + for dy in range(body_top + 10, body_bottom - 10): + if (dy // 4) % 2 == 0: + draw.point((sheen_x, dy), fill=GLASS_EDGE) + draw.point((sheen_x + 1, dy), fill=GLASS_EDGE) + + # Body outline + draw.rectangle([cx - half_w, body_top, cx + half_w, body_bottom], + outline=GLASS_EDGE) + + # Fluid level inside (bottom portion of body) + if active: + fluid_top = body_top + int((body_bottom - body_top) * 0.25) + for y in range(fluid_top, body_bottom - 2): + t = (y - fluid_top) / max(1, body_bottom - 2 - fluid_top) + c = lerp_color(XP_MID, XP_DIM, t * 0.4) + draw.line([(cx - half_w + 4, y), (cx + half_w - 4, y)], + fill=c) + else: + fluid_top = body_top + int((body_bottom - body_top) * 0.65) + for y in range(fluid_top, body_bottom - 2): + t = (y - fluid_top) / max(1, body_bottom - 2 - fluid_top) + c = lerp_color(XP_DARK, (12, 50, 10, 255), t * 0.5) + draw.line([(cx - half_w + 4, y), (cx + half_w - 4, y)], + fill=c) + + +# --------------------------------------------------------------------------- +# FRONT — vial chamber with XP orb and fluid nozzle +# --------------------------------------------------------------------------- + +def make_front(active=False): + """Front face: large glass vial chamber with XP orb and output nozzle.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx = S // 2 + + # Armored chamber frame + frame_inset = int(S * 0.10) + fx1 = frame_inset + fy1 = frame_inset + fx2 = S - frame_inset + fy2 = int(S * 0.76) + + draw.rectangle([fx1, fy1, fx2, fy2], fill=ARMOR_MID) + add_bevel_border(draw, fx1, fy1, fx2, fy2, + ARMOR_LIGHT, EDGE_DARK, width=5) + + # Dark chamber interior + ch_inset = 28 + cx1 = fx1 + ch_inset + cy1 = fy1 + ch_inset + cx2 = fx2 - ch_inset + cy2 = fy2 - ch_inset + + draw.rectangle([cx1, cy1, cx2, cy2], fill=INTERIOR_BG) + + # Glass vial inside the chamber + vial_half_w = int((cx2 - cx1) * 0.30) + vial_neck_hw = int(vial_half_w * 0.40) + vial_top = cy1 + 30 + vial_bottom = cy2 - 30 + vial_neck_h = int((vial_bottom - vial_top) * 0.20) + + draw_vial(draw, img, cx, vial_top, vial_bottom, + vial_half_w, vial_neck_hw, vial_neck_h, active=active) + + # XP orb floating above the fluid in the vial + orb_cy = vial_top + vial_neck_h + int( + (vial_bottom - vial_top - vial_neck_h) * 0.35) + draw_xp_orb(draw, img, cx, orb_cy, 55, active=active) + + # Chamber outline + draw.rectangle([cx1, cy1, cx2, cy2], outline=EDGE_DARK) + + # Frame bolts + for bx, by in [(fx1 + 18, fy1 + 18), (fx2 - 18, fy1 + 18), + (fx1 + 18, fy2 - 18), (fx2 - 18, fy2 - 18), + (cx, fy1 + 18), (cx, fy2 - 18)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Fluid output nozzle below the chamber + nozzle_cy = fy2 + (S - fy2) // 2 + nozzle_r = 55 + + # Nozzle mounting plate + draw_filled_circle(draw, cx, nozzle_cy, nozzle_r + 24, + ARMOR_MID, outline=EDGE_DARK) + + # Nozzle ring + ring_color = XP_MID if active else XP_DARK + for r in range(nozzle_r, nozzle_r - 18, -1): + t = (nozzle_r - r) / 18 + c = lerp_color(ARMOR_DARK, ring_color, t) + draw.ellipse([cx - r, nozzle_cy - r, cx + r, nozzle_cy + r], + outline=c) + + draw.ellipse( + [cx - nozzle_r, nozzle_cy - nozzle_r, + cx + nozzle_r, nozzle_cy + nozzle_r], + outline=EDGE_DARK + ) + + # Nozzle center opening + inner_r = nozzle_r - 20 + draw_filled_circle(draw, cx, nozzle_cy, inner_r, + INTERIOR_BG, outline=EDGE_DARK) + + # Center fluid dot + if active: + draw_filled_circle(draw, cx, nozzle_cy, 16, XP_MID, + outline=XP_DARK) + add_radial_glow(img, cx, nozzle_cy, nozzle_r + 30, + XP_GLOW, intensity=0.18) + else: + draw_filled_circle(draw, cx, nozzle_cy, 16, XP_DARK, + outline=EDGE_DARK) + + # Nozzle mounting bolts + for i in range(6): + angle = math.radians(60 * i + 30) + bx = int(cx + (nozzle_r + 14) * math.cos(angle)) + by = int(nozzle_cy + (nozzle_r + 14) * math.sin(angle)) + draw_filled_circle(draw, bx, by, 7, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 3, RIVET_COLOR) + + # Seams flanking the chamber + m = 30 + seam_y = (fy1 + fy2) // 2 + if active: + add_glowing_seam( + img, (m, seam_y), (fx1 - 6, seam_y), + SEAM_COLOR, XP_MID, + seam_width=2, glow_width=8, intensity=0.12 + ) + add_glowing_seam( + img, (fx2 + 6, seam_y), (S - m, seam_y), + SEAM_COLOR, XP_MID, + seam_width=2, glow_width=8, intensity=0.12 + ) + else: + draw.line([(m, seam_y), (fx1 - 6, seam_y)], + fill=SEAM_COLOR, width=2) + draw.line([(fx2 + 6, seam_y), (S - m, seam_y)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + + return img + + +# --------------------------------------------------------------------------- +# BACK — exhaust/vent panel +# --------------------------------------------------------------------------- + +def make_back(): + """Back face: hex armor with vent panel.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + panel_w = int(S * 0.48) + panel_h = int(S * 0.42) + px1 = cx - panel_w // 2 + py1 = cy - panel_h // 2 + px2 = cx + panel_w // 2 + py2 = cy + panel_h // 2 + + draw.rectangle([px1, py1, px2, py2], fill=ARMOR_MID) + add_bevel_border(draw, px1, py1, px2, py2, + EDGE_DARK, ARMOR_LIGHT, width=5) + + slat_h = 10 + slat_gap = 18 + y = py1 + 20 + while y + slat_h < py2 - 14: + draw.rectangle( + [px1 + 18, y, px2 - 18, y + slat_h], + fill=(15, 17, 22, 255) + ) + draw.line( + [(px1 + 18, y + slat_h + 1), (px2 - 18, y + slat_h + 1)], + fill=ARMOR_LIGHT, width=1 + ) + y += slat_h + slat_gap + + bolt_off = 18 + for bx, by in [(px1 + bolt_off, py1 + bolt_off), + (px2 - bolt_off, py1 + bolt_off), + (px1 + bolt_off, py2 - bolt_off), + (px2 - bolt_off, py2 - bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + m = 30 + draw.line([(m, cy), (px1 - 10, cy)], fill=SEAM_COLOR, width=2) + draw.line([(px2 + 10, cy), (S - m, cy)], fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# SIDE — red power input connector +# --------------------------------------------------------------------------- + +def make_side(): + """Side face: hex armor with red power input connector.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + conn_outer_r = 90 + conn_inner_r = 60 + + plate_r = conn_outer_r + 30 + draw_filled_circle(draw, cx, cy, plate_r, ARMOR_MID, + outline=EDGE_DARK) + + for r in range(conn_outer_r, conn_inner_r, -1): + t = (conn_outer_r - r) / (conn_outer_r - conn_inner_r) + c = lerp_color(POWER_RED_DIM, POWER_RED, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + draw.ellipse( + [cx - conn_outer_r, cy - conn_outer_r, + cx + conn_outer_r, cy + conn_outer_r], + outline=EDGE_DARK + ) + draw.ellipse( + [cx - conn_inner_r, cy - conn_inner_r, + cx + conn_inner_r, cy + conn_inner_r], + outline=EDGE_DARK + ) + + draw_filled_circle(draw, cx, cy, conn_inner_r - 2, INTERIOR_BG) + draw_filled_circle(draw, cx, cy, 18, POWER_RED_DIM, + outline=EDGE_DARK) + draw_filled_circle(draw, cx, cy, 8, RIVET_COLOR) + + bolt_count = 8 + bolt_r = plate_r - 14 + for i in range(bolt_count): + angle = math.radians(360 * i / bolt_count) + bx = int(cx + bolt_r * math.cos(angle)) + by = int(cy + bolt_r * math.sin(angle)) + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + m = 30 + draw.line([(m, cy), (cx - plate_r - 10, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(cx + plate_r + 10, cy), (S - m, cy)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# TOP — directional intake hopper (idle) and active glow +# --------------------------------------------------------------------------- + +def make_top(direction="north"): + """Top face (idle): intake hopper with glass vial neck visible and + directional arrow.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + hopper_size = int(S * 0.28) + hx1 = cx - hopper_size + hy1 = cy - hopper_size + hx2 = cx + hopper_size + hy2 = cy + hopper_size + + frame_w = 28 + draw.rectangle( + [hx1 - frame_w, hy1 - frame_w, hx2 + frame_w, hy2 + frame_w], + fill=ARMOR_MID + ) + add_bevel_border( + draw, hx1 - frame_w, hy1 - frame_w, + hx2 + frame_w, hy2 + frame_w, + ARMOR_LIGHT, EDGE_DARK, width=4 + ) + + # Dark hopper interior + draw.rectangle([hx1, hy1, hx2, hy2], fill=INTERIOR_BG) + + # Circular vial opening in center of hopper + vial_r = int(hopper_size * 0.45) + draw_filled_circle(draw, cx, cy, vial_r + 10, GLASS_EDGE) + draw_filled_circle(draw, cx, cy, vial_r, GLASS_DARK, + outline=GLASS_EDGE) + + # XP orb visible deep inside + draw_xp_orb(draw, img, cx, cy, 40, active=False) + + draw.rectangle([hx1, hy1, hx2, hy2], outline=EDGE_DARK) + + # Frame bolts + for bx, by in [ + (hx1 - frame_w // 2, hy1 - frame_w // 2), + (hx2 + frame_w // 2, hy1 - frame_w // 2), + (hx1 - frame_w // 2, hy2 + frame_w // 2), + (hx2 + frame_w // 2, hy2 + frame_w // 2), + (cx, hy1 - frame_w // 2), + (cx, hy2 + frame_w // 2), + (hx1 - frame_w // 2, cy), + (hx2 + frame_w // 2, cy), + ]: + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Directional arrow + arrow_len = 90 + arrow_w = 44 + arrow_head_w = 72 + arrow_head_len = 55 + + if direction == "north": + ay = hy1 - frame_w - 28 + draw.rectangle( + [cx - arrow_w // 2, ay - arrow_len, cx + arrow_w // 2, ay], + fill=ARROW_COLOR, outline=ARROW_OUTLINE + ) + draw.polygon([ + (cx - arrow_head_w, ay - arrow_len), + (cx + arrow_head_w, ay - arrow_len), + (cx, ay - arrow_len - arrow_head_len), + ], fill=ARROW_COLOR, outline=ARROW_OUTLINE) + elif direction == "south": + ay = hy2 + frame_w + 28 + draw.rectangle( + [cx - arrow_w // 2, ay, cx + arrow_w // 2, ay + arrow_len], + fill=ARROW_COLOR, outline=ARROW_OUTLINE + ) + draw.polygon([ + (cx - arrow_head_w, ay + arrow_len), + (cx + arrow_head_w, ay + arrow_len), + (cx, ay + arrow_len + arrow_head_len), + ], fill=ARROW_COLOR, outline=ARROW_OUTLINE) + elif direction == "east": + ax = hx2 + frame_w + 28 + draw.rectangle( + [ax, cy - arrow_w // 2, ax + arrow_len, cy + arrow_w // 2], + fill=ARROW_COLOR, outline=ARROW_OUTLINE + ) + draw.polygon([ + (ax + arrow_len, cy - arrow_head_w), + (ax + arrow_len, cy + arrow_head_w), + (ax + arrow_len + arrow_head_len, cy), + ], fill=ARROW_COLOR, outline=ARROW_OUTLINE) + elif direction == "west": + ax = hx1 - frame_w - 28 + draw.rectangle( + [ax - arrow_len, cy - arrow_w // 2, ax, cy + arrow_w // 2], + fill=ARROW_COLOR, outline=ARROW_OUTLINE + ) + draw.polygon([ + (ax - arrow_len, cy - arrow_head_w), + (ax - arrow_len, cy + arrow_head_w), + (ax - arrow_len - arrow_head_len, cy), + ], fill=ARROW_COLOR, outline=ARROW_OUTLINE) + + # Corner seams + m = 30 + for sx, sy, ex, ey in [ + (m, m, hx1 - frame_w - 10, hy1 - frame_w - 10), + (S - m, m, hx2 + frame_w + 10, hy1 - frame_w - 10), + (m, S - m, hx1 - frame_w - 10, hy2 + frame_w + 10), + (S - m, S - m, hx2 + frame_w + 10, hy2 + frame_w + 10), + ]: + draw.line([(sx, sy), (ex, ey)], fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +def make_top_active(): + """Top face (powered): glowing green hopper with bright vial opening.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + hopper_size = int(S * 0.28) + hx1 = cx - hopper_size + hy1 = cy - hopper_size + hx2 = cx + hopper_size + hy2 = cy + hopper_size + + frame_w = 28 + draw.rectangle( + [hx1 - frame_w, hy1 - frame_w, hx2 + frame_w, hy2 + frame_w], + fill=ARMOR_MID + ) + add_bevel_border( + draw, hx1 - frame_w, hy1 - frame_w, + hx2 + frame_w, hy2 + frame_w, + ARMOR_LIGHT, EDGE_DARK, width=4 + ) + + # Glowing interior + draw.rectangle([hx1, hy1, hx2, hy2], fill=INTERIOR_BG) + for r in range(hopper_size, 0, -1): + t = r / hopper_size + c = lerp_color(XP_DIM, INTERIOR_BG, t * 0.55) + draw.rectangle([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Glowing vial opening + vial_r = int(hopper_size * 0.45) + draw_filled_circle(draw, cx, cy, vial_r + 10, GLASS_EDGE) + draw_filled_circle(draw, cx, cy, vial_r, XP_DARK, + outline=XP_MID) + + # Active XP orb + draw_xp_orb(draw, img, cx, cy, 45, active=True) + + draw.rectangle([hx1, hy1, hx2, hy2], outline=EDGE_DARK) + + # Frame bolts + for bx, by in [ + (hx1 - frame_w // 2, hy1 - frame_w // 2), + (hx2 + frame_w // 2, hy1 - frame_w // 2), + (hx1 - frame_w // 2, hy2 + frame_w // 2), + (hx2 + frame_w // 2, hy2 + frame_w // 2), + (cx, hy1 - frame_w // 2), + (cx, hy2 + frame_w // 2), + (hx1 - frame_w // 2, cy), + (hx2 + frame_w // 2, cy), + ]: + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Glowing corner seams + m = 30 + for sx, sy, ex, ey in [ + (m, m, hx1 - frame_w - 10, hy1 - frame_w - 10), + (S - m, m, hx2 + frame_w + 10, hy1 - frame_w - 10), + (m, S - m, hx1 - frame_w - 10, hy2 + frame_w + 10), + (S - m, S - m, hx2 + frame_w + 10, hy2 + frame_w + 10), + ]: + add_glowing_seam( + img, (sx, sy), (ex, ey), + SEAM_COLOR, XP_MID, + seam_width=2, glow_width=8, intensity=0.15 + ) + + add_corner_bolts(draw) + add_radial_glow(img, cx, cy, hopper_size + 50, + XP_GLOW, intensity=0.20) + + return img + + +# --------------------------------------------------------------------------- +# BOTTOM — reinforced base plate +# --------------------------------------------------------------------------- + +def make_bottom(): + """Bottom face: heavy reinforced base plate.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + grille_size = 360 + g1 = cx - grille_size // 2 + g2 = cx + grille_size // 2 + draw.rectangle([g1, g1, g2, g2], fill=ARMOR_MID, + outline=ARMOR_LIGHT) + add_bevel_border(draw, g1, g1, g2, g2, ARMOR_LIGHT, EDGE_DARK, + width=3) + + for vy in range(g1 + 24, g2 - 16, 22): + draw.rectangle([g1 + 18, vy, g2 - 18, vy + 8], fill=EDGE_DARK) + draw.rectangle([g1 + 20, vy + 1, g2 - 20, vy + 7], + fill=(15, 17, 22, 255)) + + foot_size = 70 + foot_inset = 55 + for fx, fy in [(foot_inset, foot_inset), + (S - foot_inset - foot_size, foot_inset), + (foot_inset, S - foot_inset - foot_size), + (S - foot_inset - foot_size, + S - foot_inset - foot_size)]: + draw.rectangle([fx, fy, fx + foot_size, fy + foot_size], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + add_bevel_border(draw, fx, fy, fx + foot_size, fy + foot_size, + (80, 86, 100, 255), EDGE_DARK, width=2) + fcx = fx + foot_size // 2 + fcy = fy + foot_size // 2 + draw_filled_circle(draw, fcx, fcy, 12, ARMOR_MID, + outline=EDGE_DARK) + draw_filled_circle(draw, fcx, fcy, 6, RIVET_COLOR) + + draw.line([(cx, foot_inset + foot_size), (cx, g1)], + fill=SEAM_COLOR, width=3) + draw.line([(cx, g2), (cx, S - foot_inset - foot_size)], + fill=SEAM_COLOR, width=3) + draw.line([(foot_inset + foot_size, cy), (g1, cy)], + fill=SEAM_COLOR, width=3) + draw.line([(g2, cy), (S - foot_inset - foot_size, cy)], + fill=SEAM_COLOR, width=3) + + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + textures = { + "experience_extractor_front": make_front(active=False), + "experience_extractor_front_active": make_front(active=True), + "experience_extractor_back": make_back(), + "experience_extractor_side": make_side(), + "experience_extractor_housing": make_bottom(), + "experience_extractor_top_north": make_top("north"), + "experience_extractor_top_south": make_top("south"), + "experience_extractor_top_east": make_top("east"), + "experience_extractor_top_west": make_top("west"), + "experience_extractor_top_active": make_top_active(), + } + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_fluid_container_textures.py b/scripts/generate_fluid_container_textures.py new file mode 100644 index 0000000..6f4749e --- /dev/null +++ b/scripts/generate_fluid_container_textures.py @@ -0,0 +1,841 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Fluid Container at 1024x1024. + +Concept: Heavy-duty industrial pressure vessel with a thick-framed glass +viewport on the front showing internal pipe structure and fluid level. +Sides feature dual vertical sight-glass gauges flanked by reinforcement +ribs. Back has twin pipe connectors with a pressure relief valve. Top +has a heavy bolted fill hatch. Bottom is a reinforced base plate. + +Creates 60 textures: + 6 faces (north, south, east, west, up, down) x 10 appearances: + empty, water_low, water_medium, water_full, + lava_low, lava_medium, lava_full, + xp_low, xp_medium, xp_full +""" + +import math +import random +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Glass / window +GLASS_DARK = (12, 16, 24, 255) +GLASS_TINT = (18, 22, 32, 255) + +# Internal tank structure colors +TANK_PIPE = (42, 46, 56, 255) +TANK_PIPE_HI = (58, 62, 74, 255) +TANK_BRACKET = (50, 54, 64, 255) +TANK_WELD = (35, 38, 46, 255) + +# Fluid colors - water (blue) +WATER_DARK = (15, 50, 110, 255) +WATER_MID = (25, 90, 170, 255) +WATER_LIGHT = (50, 140, 210, 255) +WATER_SURFACE = (70, 175, 235, 255) +WATER_GLOW = (40, 140, 220, 255) +WATER_BUBBLE = (100, 190, 245, 180) + +# Fluid colors - lava (orange/red) +LAVA_DARK = (110, 25, 5, 255) +LAVA_MID = (190, 70, 10, 255) +LAVA_LIGHT = (235, 130, 25, 255) +LAVA_SURFACE = (255, 175, 45, 255) +LAVA_GLOW = (255, 120, 20, 255) +LAVA_BUBBLE = (255, 200, 80, 200) + +# Fluid colors - experience (green) +XP_DARK = (10, 70, 15, 255) +XP_MID = (25, 130, 35, 255) +XP_LIGHT = (50, 190, 60, 255) +XP_SURFACE = (90, 235, 95, 255) +XP_GLOW = (50, 200, 60, 255) +XP_BUBBLE = (120, 240, 130, 180) + +# Gauge colors +GAUGE_BG = (10, 13, 20, 255) +GAUGE_FRAME = (55, 60, 72, 255) +GAUGE_TICK = (40, 44, 54, 255) +GAUGE_TICK_MAJOR = (65, 70, 82, 255) + +FILL_LEVELS = {"low": 0.25, "medium": 0.60, "full": 0.92} + +FLUID_COLORS = { + "water": (WATER_DARK, WATER_MID, WATER_LIGHT, WATER_SURFACE, + WATER_GLOW, WATER_BUBBLE), + "lava": (LAVA_DARK, LAVA_MID, LAVA_LIGHT, LAVA_SURFACE, + LAVA_GLOW, LAVA_BUBBLE), + "xp": (XP_DARK, XP_MID, XP_LIGHT, XP_SURFACE, + XP_GLOW, XP_BUBBLE), +} + +HEX_RADIUS = 28 + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _hex_vertices(cx, cy, radius): + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +def draw_pipe_connector(draw, img, cx, cy, r_outer, r_inner, + fluid_type=None, fill_frac=0, bolt_count=6): + """Draw a bolted pipe connector flange with optional fluid glow.""" + # Outer flange ring + draw.ellipse([cx - r_outer, cy - r_outer, + cx + r_outer, cy + r_outer], + fill=ARMOR_MID, outline=EDGE_DARK) + # Bevel on flange + for r in range(r_outer, r_outer - 6, -1): + t = (r_outer - r) / 6 + c = lerp_color(ARMOR_LIGHT, ARMOR_MID, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Inner ring groove + groove_r = r_inner + 6 + draw.ellipse([cx - groove_r, cy - groove_r, + cx + groove_r, cy + groove_r], + outline=EDGE_DARK) + + # Pipe opening + if fluid_type is not None and fill_frac > 0: + _, _, _, _, glow, _ = FLUID_COLORS[fluid_type] + draw_filled_circle(draw, cx, cy, r_inner, glow) + draw_filled_circle(draw, cx, cy, r_inner - 8, EDGE_DARK) + # Center dot + draw_filled_circle(draw, cx, cy, 4, glow) + add_radial_glow(img, cx, cy, r_outer + 15, glow, + intensity=0.15) + else: + draw_filled_circle(draw, cx, cy, r_inner, EDGE_DARK) + draw_filled_circle(draw, cx, cy, 4, (30, 33, 40, 255)) + + # Bolts around flange + for i in range(bolt_count): + angle = math.radians(360 * i / bolt_count + 30) + bx = int(cx + (r_outer - 10) * math.cos(angle)) + by = int(cy + (r_outer - 10) * math.sin(angle)) + draw_filled_circle(draw, bx, by, 6, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 3, RIVET_COLOR) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + return img + + +def add_corner_bolts(draw, inset=30): + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +# --------------------------------------------------------------------------- +# Fluid drawing +# --------------------------------------------------------------------------- + +def draw_fluid_body(draw, img, x1, y1, x2, y2, fill_frac, + fluid_type, bubbles=True): + """Fill a region with fluid, surface line, shimmer, and bubbles.""" + if fluid_type is None or fill_frac <= 0: + return + + dark, mid, light, surface, glow, bubble_c = FLUID_COLORS[fluid_type] + total_h = y2 - y1 + fluid_h = int(total_h * fill_frac) + fluid_top = y2 - fluid_h + + # Fluid body gradient + for y in range(fluid_top, y2 + 1): + t = (y - fluid_top) / max(1, fluid_h) + c = lerp_color(light, dark, t) + draw.line([(x1, y), (x2, y)], fill=c) + + # Surface band (thicker, more visible) + surface_h = max(4, int(fluid_h * 0.04)) + for dy in range(surface_h): + t = dy / max(1, surface_h) + c = lerp_color(surface, light, t) + draw.line([(x1, fluid_top + dy), (x2, fluid_top + dy)], + fill=c) + + # Shimmer highlights on surface + rng = random.Random(42) # deterministic + w = x2 - x1 + for sx in range(x1 + 15, x2 - 40, 60): + sw = 20 + rng.randint(0, 25) + draw.line([(sx, fluid_top + 1), (sx + sw, fluid_top + 1)], + fill=(*surface[:3], 180), width=2) + + # Bubbles (deterministic positions) + if bubbles and fluid_h > 60: + for i in range(18): + bx = x1 + 20 + rng.randint(0, w - 40) + by = fluid_top + 20 + rng.randint(0, max(1, fluid_h - 50)) + br = 4 + rng.randint(0, 8) + # Only draw if within fluid + if by + br < y2 - 5 and by - br > fluid_top + surface_h: + ba = 40 + rng.randint(0, 60) + bc = (*bubble_c[:3], ba) + draw.ellipse([bx - br, by - br, bx + br, by + br], + outline=bc) + # Tiny highlight dot + draw.ellipse([bx - 1, by - br + 2, bx + 1, by - br + 4], + fill=(*surface[:3], ba)) + + # Subtle internal glow + fcx = (x1 + x2) // 2 + fcy = (fluid_top + y2) // 2 + glow_r = min(fluid_h, x2 - x1) // 3 + if glow_r > 20: + add_radial_glow(img, fcx, fcy, glow_r, glow, intensity=0.10) + + +def draw_gauge(draw, img, gx, y1, y2, gauge_w, fill_frac, + fluid_type, ticks_right=True): + """Draw a vertical sight-glass gauge with graduated markings.""" + # Recessed groove behind gauge + draw.rectangle([gx - 3, y1 - 3, gx + gauge_w + 3, y2 + 3], + fill=EDGE_DARK) + + # Gauge tube background + draw.rectangle([gx, y1, gx + gauge_w, y2], fill=GAUGE_BG) + + # Glass edge highlights + draw.line([(gx, y1), (gx, y2)], fill=GAUGE_FRAME, width=2) + draw.line([(gx + gauge_w, y1), (gx + gauge_w, y2)], + fill=GAUGE_FRAME, width=2) + draw.line([(gx, y1), (gx + gauge_w, y1)], + fill=GAUGE_FRAME, width=2) + draw.line([(gx, y2), (gx + gauge_w, y2)], + fill=GAUGE_FRAME, width=2) + + # Graduated tick marks + gauge_h = y2 - y1 + num_major = 4 + num_minor = 12 + tick_side = gx + gauge_w + 4 if ticks_right else gx - 4 + + for i in range(num_minor + 1): + ty = y1 + int(i * gauge_h / num_minor) + is_major = (i % (num_minor // num_major) == 0) + tw = 14 if is_major else 8 + tc = GAUGE_TICK_MAJOR if is_major else GAUGE_TICK + if ticks_right: + draw.line([(tick_side, ty), (tick_side + tw, ty)], + fill=tc, width=2 if is_major else 1) + else: + draw.line([(tick_side - tw, ty), (tick_side, ty)], + fill=tc, width=2 if is_major else 1) + + # Fluid fill in gauge + if fluid_type is not None and fill_frac > 0: + dark, mid, light, surface, glow, _ = FLUID_COLORS[fluid_type] + inner_h = gauge_h - 4 + fh = int(inner_h * fill_frac) + ft = y2 - 2 - fh + + for y in range(ft, y2 - 1): + t = (y - ft) / max(1, fh) + c = lerp_color(light, dark, t) + draw.line([(gx + 2, y), (gx + gauge_w - 2, y)], fill=c) + + # Surface line + draw.line([(gx + 2, ft), (gx + gauge_w - 2, ft)], + fill=surface, width=2) + + # Subtle gauge glow + gcx = gx + gauge_w // 2 + gcy = (ft + y2) // 2 + add_radial_glow(img, gcx, gcy, gauge_w, glow, + intensity=0.10) + + +# --------------------------------------------------------------------------- +# Internal tank structure (visible through glass when empty/low) +# --------------------------------------------------------------------------- + +def draw_tank_internals(draw, x1, y1, x2, y2): + """Draw internal pipes and brackets visible inside the empty tank.""" + cx = (x1 + x2) // 2 + w = x2 - x1 + h = y2 - y1 + + # Intake pipe coming from top center + pipe_w = 28 + pipe_x = cx - pipe_w // 2 + draw.rectangle([pipe_x, y1, pipe_x + pipe_w, y1 + int(h * 0.25)], + fill=TANK_PIPE) + # Pipe highlight + draw.line([(pipe_x + 4, y1), (pipe_x + 4, y1 + int(h * 0.25))], + fill=TANK_PIPE_HI, width=2) + # Pipe outline + draw.rectangle([pipe_x, y1, pipe_x + pipe_w, y1 + int(h * 0.25)], + outline=TANK_WELD) + + # Pipe nozzle at bottom of intake + noz_y = y1 + int(h * 0.25) + draw.rectangle([pipe_x - 6, noz_y, pipe_x + pipe_w + 6, noz_y + 10], + fill=TANK_BRACKET, outline=TANK_WELD) + + # Horizontal bracing bars + for frac in (0.40, 0.70): + bar_y = y1 + int(h * frac) + draw.rectangle([x1 + 8, bar_y, x2 - 8, bar_y + 6], + fill=TANK_BRACKET, outline=TANK_WELD) + # Bracket mounting points + for bx in [x1 + 20, x2 - 20]: + draw.rectangle([bx - 5, bar_y - 4, bx + 5, bar_y + 10], + fill=TANK_PIPE, outline=TANK_WELD) + + # Drain outlet at bottom center + drain_y = y2 - int(h * 0.08) + draw.rectangle([pipe_x - 2, drain_y, pipe_x + pipe_w + 2, y2], + fill=TANK_PIPE, outline=TANK_WELD) + draw.rectangle([pipe_x + 4, drain_y + 2, + pipe_x + pipe_w - 4, y2 - 2], + fill=GLASS_DARK) + + # Weld seam lines along tank edges + draw.line([(x1 + 4, y1 + 10), (x1 + 4, y2 - 10)], + fill=TANK_WELD, width=2) + draw.line([(x2 - 4, y1 + 10), (x2 - 4, y2 - 10)], + fill=TANK_WELD, width=2) + + # Measurement scale on right inside wall + for i in range(0, 11): + my = y2 - int(h * 0.05) - int((h * 0.88) * i / 10) + mw = 16 if i % 5 == 0 else 8 + mc = (45, 50, 62, 255) if i % 5 == 0 else (35, 38, 48, 255) + draw.line([(x2 - 10, my), (x2 - 10 - mw, my)], + fill=mc, width=2 if i % 5 == 0 else 1) + + +# --------------------------------------------------------------------------- +# NORTH (FRONT) - Heavy viewport with internal structure +# --------------------------------------------------------------------------- + +def make_north(fluid_type=None, fill_level=None): + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + cx = S // 2 + fill_frac = FILL_LEVELS.get(fill_level, 0) + + # Thick outer frame + frame_outer = 18 + win_x1 = int(S * 0.12) + win_y1 = int(S * 0.08) + win_x2 = int(S * 0.88) + win_y2 = int(S * 0.78) + + # Heavy frame surround + fx1 = win_x1 - frame_outer + fy1 = win_y1 - frame_outer + fx2 = win_x2 + frame_outer + fy2 = win_y2 + frame_outer + + draw.rectangle([fx1, fy1, fx2, fy2], fill=ARMOR_MID) + add_bevel_border(draw, fx1, fy1, fx2, fy2, + ARMOR_LIGHT, EDGE_DARK, width=5) + + # Recessed inner frame + draw.rectangle([win_x1 - 4, win_y1 - 4, win_x2 + 4, win_y2 + 4], + fill=EDGE_DARK) + draw.rectangle([win_x1, win_y1, win_x2, win_y2], fill=GLASS_DARK) + draw.rectangle([win_x1 + 2, win_y1 + 2, win_x2 - 2, win_y2 - 2], + fill=GLASS_TINT) + + # Internal tank structure (visible behind glass) + draw_tank_internals(draw, win_x1 + 3, win_y1 + 3, + win_x2 - 3, win_y2 - 3) + + # Fluid on top of internals + draw_fluid_body(draw, img, win_x1 + 3, win_y1 + 3, + win_x2 - 3, win_y2 - 3, fill_frac, fluid_type) + + # Glass sheen overlay (two subtle diagonal strips) + sheen = Image.new("RGBA", (S, S), (0, 0, 0, 0)) + sd = ImageDraw.Draw(sheen) + # Narrow bright strip + sx1 = win_x1 + int((win_x2 - win_x1) * 0.08) + sx2 = win_x1 + int((win_x2 - win_x1) * 0.12) + sd.polygon([ + (sx1, win_y1 + 6), (sx2, win_y1 + 6), + (sx2 - 20, win_y2 - 6), (sx1 - 20, win_y2 - 6) + ], fill=(200, 220, 240, 18)) + # Wider faint strip + sx3 = sx2 + 12 + sx4 = sx3 + 20 + sd.polygon([ + (sx3, win_y1 + 6), (sx4, win_y1 + 6), + (sx4 - 20, win_y2 - 6), (sx3 - 20, win_y2 - 6) + ], fill=(200, 220, 240, 10)) + img = Image.alpha_composite(img, sheen) + draw = ImageDraw.Draw(img) + + # Window frame inner border line + draw.rectangle([win_x1, win_y1, win_x2, win_y2], outline=EDGE_DARK) + + # Heavy bolts around frame (corners + midpoints) + cy_win = (win_y1 + win_y2) // 2 + bolt_spots = [ + (fx1 + 14, fy1 + 14), (fx2 - 14, fy1 + 14), + (fx1 + 14, fy2 - 14), (fx2 - 14, fy2 - 14), + (cx, fy1 + 14), (cx, fy2 - 14), + (fx1 + 14, cy_win), (fx2 - 14, cy_win), + ] + for bx, by in bolt_spots: + draw_filled_circle(draw, bx, by, 11, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Output nozzle at bottom center + draw_pipe_connector(draw, img, cx, int(S * 0.90), 52, 30, + fluid_type, fill_frac) + + add_corner_bolts(draw) + + return img + + +# --------------------------------------------------------------------------- +# SOUTH (BACK) - Twin pipe connectors + pressure panel +# --------------------------------------------------------------------------- + +def make_south(fluid_type=None, fill_level=None): + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + cx, cy = S // 2, S // 2 + fill_frac = FILL_LEVELS.get(fill_level, 0) + + # Pressure data panel (upper center) + px1 = int(S * 0.25) + py1 = int(S * 0.12) + px2 = int(S * 0.75) + py2 = int(S * 0.42) + + draw.rectangle([px1, py1, px2, py2], fill=ARMOR_MID) + add_bevel_border(draw, px1, py1, px2, py2, + EDGE_DARK, ARMOR_LIGHT, width=4) + + # Panel contents: horizontal data lines (like a readout) + line_y = py1 + 22 + while line_y + 10 < py2 - 16: + lw = 40 + (hash(line_y) % 80) + lx = px1 + 22 + line_c = (35, 40, 50, 255) + draw.rectangle([lx, line_y, lx + lw, line_y + 6], + fill=line_c) + # Status dot + if fluid_type and fill_frac > 0: + _, _, _, _, glow, _ = FLUID_COLORS[fluid_type] + draw_filled_circle(draw, px2 - 24, line_y + 3, 4, glow) + else: + draw_filled_circle(draw, px2 - 24, line_y + 3, 4, + (40, 44, 54, 255)) + line_y += 24 + + # Panel bolts + for bx, by in [(px1 + 14, py1 + 14), (px2 - 14, py1 + 14), + (px1 + 14, py2 - 14), (px2 - 14, py2 - 14)]: + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Twin pipe connectors (lower half, spaced apart) + pipe_y = int(S * 0.66) + for pipe_cx in [int(S * 0.32), int(S * 0.68)]: + draw_pipe_connector(draw, img, pipe_cx, pipe_y, 56, 32, + fluid_type, fill_frac, bolt_count=8) + + # Pressure relief valve (small, between the pipes) + valve_cx = cx + valve_cy = int(S * 0.88) + # Valve body + draw.rectangle([valve_cx - 20, valve_cy - 14, + valve_cx + 20, valve_cy + 14], + fill=ARMOR_MID, outline=EDGE_DARK) + add_bevel_border(draw, valve_cx - 20, valve_cy - 14, + valve_cx + 20, valve_cy + 14, + ARMOR_LIGHT, EDGE_DARK, width=2) + # Valve handle (T-shape) + draw.rectangle([valve_cx - 3, valve_cy - 28, + valve_cx + 3, valve_cy - 14], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + draw.rectangle([valve_cx - 14, valve_cy - 32, + valve_cx + 14, valve_cy - 26], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + + # Horizontal seam + m = 30 + draw.line([(m, cy), (px1 - 10, cy)], fill=SEAM_COLOR, width=2) + draw.line([(px2 + 10, cy), (S - m, cy)], fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# EAST / WEST (SIDES) - Reinforcement ribs + dual sight gauges +# --------------------------------------------------------------------------- + +def make_side(fluid_type=None, fill_level=None): + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + cx, cy = S // 2, S // 2 + fill_frac = FILL_LEVELS.get(fill_level, 0) + + # Vertical reinforcement ribs (pressure vessel look) + rib_w = 30 + rib_top = int(S * 0.07) + rib_bot = int(S * 0.93) + rib_positions = [int(S * 0.18), int(S * 0.82) - rib_w] + + for rx in rib_positions: + # Rib body + draw.rectangle([rx, rib_top, rx + rib_w, rib_bot], + fill=ARMOR_MID) + # Rib highlight (center bright strip) + draw.rectangle([rx + rib_w // 2 - 3, rib_top, + rx + rib_w // 2 + 3, rib_bot], + fill=ARMOR_LIGHT) + # Rib edges + draw.line([(rx, rib_top), (rx, rib_bot)], + fill=EDGE_DARK, width=2) + draw.line([(rx + rib_w, rib_top), (rx + rib_w, rib_bot)], + fill=EDGE_DARK, width=2) + + # Clamp brackets along rib + for clamp_y in range(rib_top + 60, rib_bot - 40, 160): + ch = 14 + draw.rectangle([rx - 10, clamp_y, rx + rib_w + 10, + clamp_y + ch], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, rx - 5, clamp_y + ch // 2, + 4, RIVET_COLOR) + draw_filled_circle(draw, rx + rib_w + 5, clamp_y + ch // 2, + 4, RIVET_COLOR) + + # Dual sight-glass gauges (between the ribs) + gauge_w = 44 + gauge_y1 = int(S * 0.12) + gauge_y2 = int(S * 0.88) + gauge_left_x = int(S * 0.34) + gauge_right_x = int(S * 0.58) + + draw_gauge(draw, img, gauge_left_x, gauge_y1, gauge_y2, + gauge_w, fill_frac, fluid_type, ticks_right=True) + draw_gauge(draw, img, gauge_right_x, gauge_y1, gauge_y2, + gauge_w, fill_frac, fluid_type, ticks_right=False) + + # Connecting bar between gauges at top and bottom + bar_h = 10 + for by in [gauge_y1 - 8, gauge_y2 + 2]: + draw.rectangle([gauge_left_x - 3, by, + gauge_right_x + gauge_w + 3, by + bar_h], + fill=ARMOR_MID, outline=EDGE_DARK) + + # Central label plate between gauges + lp_x1 = gauge_left_x + gauge_w + 8 + lp_x2 = gauge_right_x - 8 + lp_y1 = cy - 30 + lp_y2 = cy + 30 + if lp_x2 > lp_x1 + 10: + draw.rectangle([lp_x1, lp_y1, lp_x2, lp_y2], + fill=ARMOR_MID, outline=EDGE_DARK) + add_bevel_border(draw, lp_x1, lp_y1, lp_x2, lp_y2, + ARMOR_LIGHT, EDGE_DARK, width=2) + # Two small lines suggesting text + draw.rectangle([lp_x1 + 6, cy - 8, lp_x2 - 6, cy - 3], + fill=(40, 44, 54, 255)) + draw.rectangle([lp_x1 + 6, cy + 3, lp_x2 - 6, cy + 8], + fill=(40, 44, 54, 255)) + + # Horizontal seams + m = 30 + draw.line([(m, cy), (rib_positions[0] - 4, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(rib_positions[1] + rib_w + 4, cy), (S - m, cy)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + + return img + + +# --------------------------------------------------------------------------- +# UP (TOP) - Heavy fill hatch +# --------------------------------------------------------------------------- + +def make_top(fluid_type=None, fill_level=None): + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + cx, cy = S // 2, S // 2 + fill_frac = FILL_LEVELS.get(fill_level, 0) + + # Reinforcement cross-plates (X pattern under the hatch) + plate_w = 24 + m = 60 + for sx, sy, ex, ey in [ + (m, m, S - m, S - m), (S - m, m, m, S - m) + ]: + draw.line([(sx, sy), (ex, ey)], fill=ARMOR_MID, + width=plate_w) + draw.line([(sx, sy), (ex, ey)], fill=EDGE_DARK, width=2) + + # Hatch rim (large circular opening) + rim_outer = int(S * 0.36) + rim_inner = int(S * 0.26) + + # Thick metallic rim + for r in range(rim_outer, rim_inner, -1): + t = (rim_outer - r) / (rim_outer - rim_inner) + c = lerp_color(ARMOR_LIGHT, ARMOR_MID, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Rim edges + draw.ellipse([cx - rim_outer, cy - rim_outer, + cx + rim_outer, cy + rim_outer], + outline=EDGE_DARK) + draw.ellipse([cx - rim_inner, cy - rim_inner, + cx + rim_inner, cy + rim_inner], + outline=EDGE_DARK) + + # Hatch interior + if fluid_type is not None and fill_frac > 0: + dark, mid, light, surface, glow, _ = FLUID_COLORS[fluid_type] + # Fluid surface seen from above + for r in range(rim_inner - 2, 0, -1): + t = r / (rim_inner - 2) + c = lerp_color(surface, mid, t * 0.5) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], + outline=c) + # Bright center + core_r = int(rim_inner * 0.25) + draw_filled_circle(draw, cx, cy, core_r, surface) + # Concentric ripples + for ring_r in range(core_r + 30, rim_inner - 10, 40): + draw.ellipse([cx - ring_r, cy - ring_r, + cx + ring_r, cy + ring_r], + outline=(*surface[:3], 50)) + add_radial_glow(img, cx, cy, rim_inner + 25, glow, + intensity=0.15) + else: + draw_filled_circle(draw, cx, cy, rim_inner - 2, GLASS_DARK) + # Visible internal braces when empty + draw.line([(cx - rim_inner + 15, cy), + (cx + rim_inner - 15, cy)], + fill=TANK_BRACKET, width=6) + draw.line([(cx, cy - rim_inner + 15), + (cx, cy + rim_inner - 15)], + fill=TANK_BRACKET, width=6) + + # Bolts around rim + bolt_count = 12 + bolt_r = (rim_outer + rim_inner) // 2 + for i in range(bolt_count): + angle = math.radians(360 * i / bolt_count) + bx = int(cx + bolt_r * math.cos(angle)) + by = int(cy + bolt_r * math.sin(angle)) + draw_filled_circle(draw, bx, by, 9, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Corner seams + seam_end = rim_outer + 20 + for sx, sy, ex, ey in [ + (m, m, cx - int(seam_end * 0.7), + cy - int(seam_end * 0.7)), + (S - m, m, cx + int(seam_end * 0.7), + cy - int(seam_end * 0.7)), + (m, S - m, cx - int(seam_end * 0.7), + cy + int(seam_end * 0.7)), + (S - m, S - m, cx + int(seam_end * 0.7), + cy + int(seam_end * 0.7)), + ]: + if fluid_type is not None and fill_frac > 0: + _, _, _, _, glow, _ = FLUID_COLORS[fluid_type] + add_glowing_seam( + img, (sx, sy), (ex, ey), SEAM_COLOR, glow, + seam_width=2, glow_width=6, intensity=0.08 + ) + else: + draw.line([(sx, sy), (ex, ey)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# DOWN (BOTTOM) - Reinforced base plate with drain +# --------------------------------------------------------------------------- + +def make_bottom(fluid_type=None, fill_level=None): + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + cx, cy = S // 2, S // 2 + + # Central drain grille + grille_size = 360 + g1 = cx - grille_size // 2 + g2 = cx + grille_size // 2 + draw.rectangle([g1, g1, g2, g2], fill=ARMOR_MID, + outline=ARMOR_LIGHT) + add_bevel_border(draw, g1, g1, g2, g2, ARMOR_LIGHT, EDGE_DARK, + width=3) + + # Vent/drain slots + for vy in range(g1 + 24, g2 - 16, 22): + draw.rectangle([g1 + 18, vy, g2 - 18, vy + 8], + fill=EDGE_DARK) + draw.rectangle([g1 + 20, vy + 1, g2 - 20, vy + 7], + fill=(15, 17, 22, 255)) + + # Mounting feet in corners + foot_size = 70 + foot_inset = 55 + for fx, fy in [(foot_inset, foot_inset), + (S - foot_inset - foot_size, foot_inset), + (foot_inset, S - foot_inset - foot_size), + (S - foot_inset - foot_size, + S - foot_inset - foot_size)]: + draw.rectangle([fx, fy, fx + foot_size, fy + foot_size], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + add_bevel_border(draw, fx, fy, fx + foot_size, + fy + foot_size, + (80, 86, 100, 255), EDGE_DARK, width=2) + fcx = fx + foot_size // 2 + fcy = fy + foot_size // 2 + draw_filled_circle(draw, fcx, fcy, 12, ARMOR_MID, + outline=EDGE_DARK) + draw_filled_circle(draw, fcx, fcy, 6, RIVET_COLOR) + + # Cross seams + draw.line([(cx, foot_inset + foot_size), (cx, g1)], + fill=SEAM_COLOR, width=3) + draw.line([(cx, g2), (cx, S - foot_inset - foot_size)], + fill=SEAM_COLOR, width=3) + draw.line([(foot_inset + foot_size, cy), (g1, cy)], + fill=SEAM_COLOR, width=3) + draw.line([(g2, cy), (S - foot_inset - foot_size, cy)], + fill=SEAM_COLOR, width=3) + + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + textures = {} + + textures["fluid_container_north"] = make_north() + textures["fluid_container_south"] = make_south() + textures["fluid_container_east"] = make_side() + textures["fluid_container_west"] = make_side() + textures["fluid_container_up"] = make_top() + textures["fluid_container_down"] = make_bottom() + + for fluid in ("water", "lava", "xp"): + for level in ("low", "medium", "full"): + suffix = f"_{fluid}_{level}" + textures[f"fluid_container_north{suffix}"] = make_north( + fluid_type=fluid, fill_level=level + ) + textures[f"fluid_container_south{suffix}"] = make_south( + fluid_type=fluid, fill_level=level + ) + textures[f"fluid_container_east{suffix}"] = make_side( + fluid_type=fluid, fill_level=level + ) + textures[f"fluid_container_west{suffix}"] = make_side( + fluid_type=fluid, fill_level=level + ) + textures[f"fluid_container_up{suffix}"] = make_top( + fluid_type=fluid, fill_level=level + ) + textures[f"fluid_container_down{suffix}"] = make_bottom( + fluid_type=fluid, fill_level=level + ) + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_fluid_merger_textures.py b/scripts/generate_fluid_merger_textures.py new file mode 100644 index 0000000..bee3373 --- /dev/null +++ b/scripts/generate_fluid_merger_textures.py @@ -0,0 +1,584 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Fluid Merger at 1024x1024. + +Concept: Same design language as Power Merger — dark armored housing with +honeycomb grid, centered connector port in a recessed panel. Color coding: + - Input faces (back/side/top/bottom): always RED (matches power merger) + - Output face (front): GREY when empty, fluid-colored when active + - water: blue + - lava: orange + - experience: green + +Creates 20 textures: + 5 faces (front, back, side, top, bottom) x 4 fluid states +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor (consistent with other Atlas blocks) +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Connector hole +CONNECTOR_HOLE = (12, 14, 18, 255) + +# Front (output) — grey when empty (no fluid flowing) +FRONT_RING = (120, 125, 135, 255) +FRONT_BRIGHT = (160, 165, 175, 255) +FRONT_GLOW = (140, 145, 155, 255) + +# Input faces — always red (matches power merger input convention) +INPUT_RING = (200, 50, 40, 255) +INPUT_BRIGHT = (255, 70, 50, 255) +INPUT_GLOW = (255, 60, 40, 255) + +# Output fluid overrides — when a fluid is active, the output face +# changes to the fluid's color. Input faces stay red always. +OUTPUT_FLUID_COLORS = { + "none": None, + "water": { + "ring": (30, 100, 220, 255), + "bright": (50, 140, 255, 255), + "glow": (40, 120, 255, 255), + }, + "lava": { + "ring": (220, 120, 30, 255), + "bright": (255, 160, 50, 255), + "glow": (255, 140, 40, 255), + }, + "xp": { + "ring": (50, 200, 50, 255), + "bright": (80, 255, 80, 255), + "glow": (60, 240, 60, 255), + }, +} + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + return img + + +def add_corner_bolts(draw, inset=30): + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +def draw_connector_port(img, draw, cx, cy, outer_r, inner_r, + ring_color, bright_color, glow_color, + active=False): + """Draw a color-coded circular connector port with contact pins.""" + active_ring = ring_color if not active else bright_color + + for r in range(outer_r, inner_r, -1): + t = (outer_r - r) / (outer_r - inner_r) + c = lerp_color(active_ring, ARMOR_MID, t * 0.6) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + draw_filled_circle(draw, cx, cy, inner_r, CONNECTOR_HOLE, + outline=EDGE_DARK) + + pin_r = int(inner_r * 0.5) + pin_size = max(4, inner_r // 12) + pin_color = ring_color if not active else bright_color + for angle_deg in [0, 90, 180, 270]: + angle = math.radians(angle_deg) + px = int(cx + pin_r * math.cos(angle)) + py = int(cy + pin_r * math.sin(angle)) + draw_filled_circle(draw, px, py, pin_size, pin_color) + + if active: + add_glow_ring(img, cx, cy, outer_r, + outer_r + int(outer_r * 0.35), glow_color) + add_radial_glow(img, cx, cy, inner_r, bright_color, + intensity=0.3) + + +# --------------------------------------------------------------------------- +# Fluid-specific details (differentiate from power merger) +# --------------------------------------------------------------------------- + +PIPE_COLOR = (60, 65, 78, 255) +PIPE_HIGHLIGHT = (80, 85, 98, 255) +PIPE_SHADOW = (30, 33, 40, 255) +FLANGE_COLOR = (90, 95, 108, 255) +GAUGE_BG = (18, 20, 26, 255) +GAUGE_RIM = (85, 90, 102, 255) +GAUGE_TICK = (110, 115, 128, 255) +GAUGE_NEEDLE = (200, 50, 40, 255) + + +def draw_pipe_stub(draw, x1, y1, x2, y2, horizontal=True): + """Draw a pipe stub with highlight and flange ends.""" + pw = 18 # pipe width + if horizontal: + draw.rectangle([x1, y1 - pw // 2, x2, y1 + pw // 2], fill=PIPE_COLOR) + draw.line([(x1, y1 - pw // 2), (x2, y1 - pw // 2)], + fill=PIPE_HIGHLIGHT, width=2) + draw.line([(x1, y1 + pw // 2), (x2, y1 + pw // 2)], + fill=PIPE_SHADOW, width=2) + # Flanges at each end + fw = pw + 10 + for fx in [x1, x2]: + draw.rectangle([fx - 4, y1 - fw // 2, fx + 4, y1 + fw // 2], + fill=FLANGE_COLOR, outline=EDGE_DARK) + else: + draw.rectangle([x1 - pw // 2, y1, x1 + pw // 2, y2], fill=PIPE_COLOR) + draw.line([(x1 - pw // 2, y1), (x1 - pw // 2, y2)], + fill=PIPE_HIGHLIGHT, width=2) + draw.line([(x1 + pw // 2, y1), (x1 + pw // 2, y2)], + fill=PIPE_SHADOW, width=2) + fw = pw + 10 + for fy in [y1, y2]: + draw.rectangle([x1 - fw // 2, fy - 4, x1 + fw // 2, fy + 4], + fill=FLANGE_COLOR, outline=EDGE_DARK) + + +def draw_pressure_gauge(draw, gx, gy, radius, fluid, glow_color=None): + """Draw a small circular pressure gauge.""" + # Gauge body + draw_filled_circle(draw, gx, gy, radius + 3, FLANGE_COLOR, + outline=EDGE_DARK) + draw_filled_circle(draw, gx, gy, radius, GAUGE_BG) + draw_filled_circle(draw, gx, gy, radius, None, outline=GAUGE_RIM) + + # Tick marks around the gauge (from 7 o'clock to 5 o'clock) + for i in range(7): + angle = math.radians(225 - i * 30) + tx1 = int(gx + (radius - 3) * math.cos(angle)) + ty1 = int(gy - (radius - 3) * math.sin(angle)) + tx2 = int(gx + (radius - 7) * math.cos(angle)) + ty2 = int(gy - (radius - 7) * math.sin(angle)) + draw.line([(tx1, ty1), (tx2, ty2)], fill=GAUGE_TICK, width=1) + + # Needle — points higher when fluid is active + if fluid == "none": + needle_angle = math.radians(210) # low / empty + elif fluid in ("water", "lava", "xp"): + needle_angle = math.radians(330) # high / full + else: + needle_angle = math.radians(270) # mid + + needle_color = glow_color if glow_color else GAUGE_NEEDLE + nx = int(gx + (radius - 8) * math.cos(needle_angle)) + ny = int(gy - (radius - 8) * math.sin(needle_angle)) + draw.line([(gx, gy), (nx, ny)], fill=needle_color, width=2) + draw_filled_circle(draw, gx, gy, 3, needle_color) + + +def add_fluid_details(draw, cx, cy, panel_r, port_outer, fluid, + glow_color, seam_style): + """Add pipe stubs and pressure gauge to differentiate from power merger.""" + # Pipe stubs from panel edge toward the connector port + pipe_end = port_outer + 15 + + if seam_style in ("horizontal", "cross", "none"): + # Left pipe stub + draw_pipe_stub(draw, cx - panel_r + 8, cy, + cx - pipe_end, cy, horizontal=True) + # Right pipe stub + draw_pipe_stub(draw, cx + pipe_end, cy, + cx + panel_r - 8, cy, horizontal=True) + + if seam_style in ("cross",): + # Top pipe stub + draw_pipe_stub(draw, cx, cy - panel_r + 8, + cx, cy - pipe_end, horizontal=False) + # Bottom pipe stub + draw_pipe_stub(draw, cx, cy + pipe_end, + cx, cy + panel_r - 8, horizontal=False) + + # Pressure gauge in upper-right corner of panel + gauge_r = 22 + gauge_x = cx + panel_r - 45 + gauge_y = cy - panel_r + 45 + draw_pressure_gauge(draw, gauge_x, gauge_y, gauge_r, fluid, + glow_color=glow_color if fluid != "none" else None) + + +# --------------------------------------------------------------------------- +# Face builder +# --------------------------------------------------------------------------- + +def _get_colors(fluid, default_ring, default_bright, default_glow, + is_output=False): + """Return (ring, bright, glow, is_active) for a given fluid state. + + Input faces always use their default colors (red) regardless of fluid. + Output face switches to the fluid's color when active. + """ + if not is_output: + # Input faces: always red, glow when fluid is active + active = fluid != "none" + return default_ring, default_bright, default_glow, active + fc = OUTPUT_FLUID_COLORS[fluid] + if fc is None: + return default_ring, default_bright, default_glow, False + return fc["ring"], fc["bright"], fc["glow"], True + + +def make_face(fluid, default_ring, default_bright, default_glow, + seam_style="horizontal", is_output=False): + """Build a face with a single centered connector in a recessed panel.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + port_outer = int(S * 0.22) + port_inner = int(S * 0.12) + + ring, bright, glow, active = _get_colors( + fluid, default_ring, default_bright, default_glow, + is_output=is_output + ) + + # Recessed panel + panel_r = int(S * 0.30) + draw.rectangle( + [cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Connector port + draw_connector_port(img, draw, cx, cy, port_outer, port_inner, + ring, bright, glow, active=active) + + # Fluid-specific details (pipe stubs + pressure gauge) + add_fluid_details(draw, cx, cy, panel_r, port_outer, fluid, + glow, seam_style) + + # Panel bolts + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Seams + m = 30 + seam_glow = glow if active else None + if seam_style in ("horizontal", "cross"): + if active: + add_glowing_seam( + img, (m, cy), (cx - panel_r - 10, cy), + SEAM_COLOR, seam_glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (cx + panel_r + 10, cy), (S - m, cy), + SEAM_COLOR, seam_glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + else: + draw.line([(m, cy), (cx - panel_r - 10, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(cx + panel_r + 10, cy), (S - m, cy)], + fill=SEAM_COLOR, width=2) + + if seam_style == "cross": + if active: + add_glowing_seam( + img, (cx, m), (cx, cy - panel_r - 10), + SEAM_COLOR, seam_glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (cx, cy + panel_r + 10), (cx, S - m), + SEAM_COLOR, seam_glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + else: + draw.line([(cx, m), (cx, cy - panel_r - 10)], + fill=SEAM_COLOR, width=2) + draw.line([(cx, cy + panel_r + 10), (cx, S - m)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# Public face functions +# --------------------------------------------------------------------------- + +def make_front(fluid="none"): + """Front face (output): bright cyan connector.""" + return make_face( + fluid, FRONT_RING, FRONT_BRIGHT, FRONT_GLOW, + seam_style="none", is_output=True + ) + + +def make_back(fluid="none"): + """Back face (input): deep blue connector.""" + return make_face( + fluid, INPUT_RING, INPUT_BRIGHT, INPUT_GLOW, + seam_style="none" + ) + + +def make_side(fluid="none"): + """Side face: input deep blue connector with horizontal seams.""" + return make_face( + fluid, INPUT_RING, INPUT_BRIGHT, INPUT_GLOW, + seam_style="horizontal" + ) + + +def make_top(fluid="none"): + """Top face: large gauge showing fluid fill level.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Recessed panel + panel_r = int(S * 0.38) + draw.rectangle( + [cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Determine fluid colors and needle angle + active = fluid != "none" + fc = OUTPUT_FLUID_COLORS[fluid] + if fc is None: + needle_color = GAUGE_TICK + zone_color = (60, 65, 78, 255) + needle_angle_deg = 225 # low / empty + else: + needle_color = fc["bright"] + zone_color = fc["ring"] + needle_angle_deg = 315 # high / full + + # Large gauge circle + gauge_r = int(S * 0.28) + # Outer bezel ring + draw_filled_circle(draw, cx, cy, gauge_r + 8, FLANGE_COLOR, + outline=EDGE_DARK) + draw_filled_circle(draw, cx, cy, gauge_r + 4, GAUGE_RIM) + # Gauge face + draw_filled_circle(draw, cx, cy, gauge_r, GAUGE_BG) + + # Colored arc zone (from 7 o'clock to 1 o'clock, showing capacity) + arc_inner = gauge_r - 30 + arc_outer = gauge_r - 10 + for deg in range(225, 314): + angle = math.radians(deg) + for r in range(arc_inner, arc_outer): + ax = int(cx + r * math.cos(angle)) + ay = int(cy - r * math.sin(angle)) + if 0 <= ax < S and 0 <= ay < S: + t = 0.3 if not active else 0.7 + base = img.getpixel((ax, ay)) + img.putpixel((ax, ay), blend_over(base, zone_color, t)) + + # Re-get draw after putpixel operations + draw = ImageDraw.Draw(img) + + # Major tick marks (7 o'clock to 1 o'clock, 9 ticks) + for i in range(9): + angle = math.radians(225 - i * 11.25) # ~90 degree sweep + # Outer tick + tx1 = int(cx + (gauge_r - 6) * math.cos(angle)) + ty1 = int(cy - (gauge_r - 6) * math.sin(angle)) + tx2 = int(cx + (gauge_r - 20) * math.cos(angle)) + ty2 = int(cy - (gauge_r - 20) * math.sin(angle)) + w = 3 if i % 4 == 0 else 1 + draw.line([(tx1, ty1), (tx2, ty2)], fill=GAUGE_TICK, width=w) + + # Minor tick marks + for i in range(33): + angle = math.radians(225 - i * 2.8125) + tx1 = int(cx + (gauge_r - 6) * math.cos(angle)) + ty1 = int(cy - (gauge_r - 6) * math.sin(angle)) + tx2 = int(cx + (gauge_r - 12) * math.cos(angle)) + ty2 = int(cy - (gauge_r - 12) * math.sin(angle)) + draw.line([(tx1, ty1), (tx2, ty2)], fill=EDGE_DARK, width=1) + + # Labels: "E" (empty) near 7 o'clock, "F" (full) near 1 o'clock + e_angle = math.radians(220) + e_x = int(cx + (gauge_r - 45) * math.cos(e_angle)) + e_y = int(cy - (gauge_r - 45) * math.sin(e_angle)) + # Draw E as lines + draw.line([(e_x - 6, e_y - 8), (e_x - 6, e_y + 8)], + fill=GAUGE_TICK, width=2) + draw.line([(e_x - 6, e_y - 8), (e_x + 5, e_y - 8)], + fill=GAUGE_TICK, width=2) + draw.line([(e_x - 6, e_y), (e_x + 3, e_y)], + fill=GAUGE_TICK, width=2) + draw.line([(e_x - 6, e_y + 8), (e_x + 5, e_y + 8)], + fill=GAUGE_TICK, width=2) + + f_angle = math.radians(320) + f_x = int(cx + (gauge_r - 45) * math.cos(f_angle)) + f_y = int(cy - (gauge_r - 45) * math.sin(f_angle)) + # Draw F as lines + draw.line([(f_x - 6, f_y - 8), (f_x - 6, f_y + 8)], + fill=GAUGE_TICK, width=2) + draw.line([(f_x - 6, f_y - 8), (f_x + 5, f_y - 8)], + fill=GAUGE_TICK, width=2) + draw.line([(f_x - 6, f_y), (f_x + 3, f_y)], + fill=GAUGE_TICK, width=2) + + # Needle + needle_angle = math.radians(needle_angle_deg) + needle_len = gauge_r - 35 + nx = int(cx + needle_len * math.cos(needle_angle)) + ny = int(cy - needle_len * math.sin(needle_angle)) + draw.line([(cx, cy), (nx, ny)], fill=needle_color, width=4) + # Needle hub + draw_filled_circle(draw, cx, cy, 12, FLANGE_COLOR, outline=EDGE_DARK) + draw_filled_circle(draw, cx, cy, 6, needle_color) + + # Glow effect when active + if active: + add_glow_ring(img, cx, cy, gauge_r, + gauge_r + int(gauge_r * 0.4), fc["glow"]) + add_radial_glow(img, cx, cy, gauge_r + 30, + fc["glow"], intensity=0.45) + draw = ImageDraw.Draw(img) + + # Panel bolts + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + add_corner_bolts(draw) + return img + + +def make_bottom(fluid="none"): + """Bottom face: input deep blue connector with cross seams.""" + return make_face( + fluid, INPUT_RING, INPUT_BRIGHT, INPUT_GLOW, + seam_style="cross" + ) + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +FLUID_SUFFIXES = {"none": "", "water": "_water", "lava": "_lava", + "xp": "_xp"} + +FACES = { + "front": make_front, + "back": make_back, + "side": make_side, + "top": make_top, + "bottom": make_bottom, +} + + +def main(): + textures = {} + for face_name, make_fn in FACES.items(): + for fluid, suffix in FLUID_SUFFIXES.items(): + tex_name = f"fluid_merger_{face_name}{suffix}" + textures[tex_name] = make_fn(fluid=fluid) + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_fluid_pipe_textures.py b/scripts/generate_fluid_pipe_textures.py new file mode 100644 index 0000000..ed5edcc --- /dev/null +++ b/scripts/generate_fluid_pipe_textures.py @@ -0,0 +1,453 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Fluid Pipe at 1024x1024. + +Concept: Dark armored pipe housing with honeycomb grid. Front shows a +circular pipe opening, back shows a sealed cap, sides show the pipe +body with a directional flow arrow. When fluid is active, the pipe +interior and arrow glow with the fluid's color. + +Creates 24 textures: + front, back, side_{up,down,left,right} × 4 fluid states (none, water, lava, xp) +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Pipe interior +PIPE_HOLE = (12, 14, 18, 255) +PIPE_RIM = (80, 85, 98, 255) +PIPE_RIM_LIGHT = (100, 105, 118, 255) + +# Pipe body (for side faces) +PIPE_BODY = (55, 60, 72, 255) +PIPE_BODY_LIGHT = (70, 75, 88, 255) +PIPE_BODY_DARK = (35, 38, 48, 255) + +# Arrow +ARROW_COLOR = (140, 148, 165, 255) +ARROW_BRIGHT = (180, 188, 205, 255) + +# Cap (back face) +CAP_COLOR = (65, 70, 82, 255) +CAP_BOLT = (95, 100, 115, 255) + +# Fluid colors +FLUID_COLORS = { + "none": None, + "water": { + "fill": (33, 150, 243, 255), + "fill_dark": (25, 118, 200, 255), + "glow": (40, 120, 255, 255), + "bright": (50, 140, 255, 255), + }, + "lava": { + "fill": (230, 90, 20, 255), + "fill_dark": (180, 60, 10, 255), + "glow": (255, 140, 40, 255), + "bright": (255, 160, 50, 255), + }, + "xp": { + "fill": (50, 200, 50, 255), + "fill_dark": (30, 140, 30, 255), + "glow": (60, 240, 60, 255), + "bright": (80, 255, 80, 255), + }, +} + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + return img + + +def add_corner_bolts(draw, inset=30): + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +# --------------------------------------------------------------------------- +# Front face — pipe opening +# --------------------------------------------------------------------------- + +def make_front(fluid="none"): + """Front face: circular pipe opening with connector ring.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + outer_r = int(S * 0.28) + mid_r = int(S * 0.22) + inner_r = int(S * 0.16) + + fc = FLUID_COLORS[fluid] + active = fc is not None + + # Outer flange ring + draw_filled_circle(draw, cx, cy, outer_r, PIPE_RIM, outline=EDGE_DARK) + + # Gradient ring from rim to mid + for r in range(outer_r - 2, mid_r, -1): + t = (outer_r - r) / (outer_r - mid_r) + c = lerp_color(PIPE_RIM_LIGHT, ARMOR_MID, t * 0.7) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Pipe wall (between mid and inner) + for r in range(mid_r, inner_r, -1): + t = (mid_r - r) / (mid_r - inner_r) + c = lerp_color(PIPE_BODY, PIPE_BODY_DARK, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Pipe interior + if active: + # Fluid fill + draw_filled_circle(draw, cx, cy, inner_r, fc["fill_dark"]) + # Brighter center + center_r = int(inner_r * 0.6) + for r in range(inner_r, 0, -1): + t = 1.0 - (r / inner_r) + c = lerp_color(fc["fill_dark"], fc["fill"], t * 0.8) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + else: + draw_filled_circle(draw, cx, cy, inner_r, PIPE_HOLE, + outline=EDGE_DARK) + + # Flange bolts (8 bolts around the ring) + bolt_r = int((outer_r + mid_r) / 2) + for i in range(8): + angle = math.radians(45 * i) + bx = int(cx + bolt_r * math.cos(angle)) + by = int(cy + bolt_r * math.sin(angle)) + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Glow when active + if active: + add_glow_ring(img, cx, cy, inner_r, + inner_r + int(inner_r * 0.4), fc["glow"]) + add_radial_glow(img, cx, cy, inner_r, fc["bright"], + intensity=0.25) + + add_corner_bolts(ImageDraw.Draw(img)) + return img + + +# --------------------------------------------------------------------------- +# Back face — sealed cap +# --------------------------------------------------------------------------- + +def make_back(fluid="none"): + """Back face: sealed pipe cap with bolts.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + outer_r = int(S * 0.28) + cap_r = int(S * 0.20) + + fc = FLUID_COLORS[fluid] + active = fc is not None + + # Outer flange ring + draw_filled_circle(draw, cx, cy, outer_r, PIPE_RIM, outline=EDGE_DARK) + + # Cap plate + draw_filled_circle(draw, cx, cy, cap_r, CAP_COLOR, outline=EDGE_DARK) + + # Bevel effect on cap + for r in range(cap_r, cap_r - 6, -1): + t = (cap_r - r) / 6 + c = lerp_color(ARMOR_LIGHT, CAP_COLOR, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Center bolt cluster + draw_filled_circle(draw, cx, cy, 20, ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, cx, cy, 12, CAP_BOLT) + draw_filled_circle(draw, cx, cy, 6, RIVET_COLOR) + + # Cap bolts in a ring + bolt_r = int(cap_r * 0.7) + for i in range(6): + angle = math.radians(60 * i) + bx = int(cx + bolt_r * math.cos(angle)) + by = int(cy + bolt_r * math.sin(angle)) + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Flange bolts + flange_r = int((outer_r + cap_r) / 2) + 5 + for i in range(8): + angle = math.radians(45 * i) + bx = int(cx + flange_r * math.cos(angle)) + by = int(cy + flange_r * math.sin(angle)) + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Subtle glow around flange when active + if active: + add_glow_ring(img, cx, cy, outer_r, + outer_r + int(outer_r * 0.25), fc["glow"]) + + add_corner_bolts(ImageDraw.Draw(img)) + return img + + +# --------------------------------------------------------------------------- +# Side face — pipe body with directional arrow (pointing up) +# --------------------------------------------------------------------------- + +def _draw_arrow_up(draw, cx, cy, arrow_color): + """Draw a flow direction arrow pointing up.""" + # Arrow head (triangle) + head_top = cy - int(S * 0.28) + head_base = cy - int(S * 0.14) + head_half_w = int(S * 0.12) + + draw.polygon([ + (cx, head_top), + (cx - head_half_w, head_base), + (cx + head_half_w, head_base), + ], fill=arrow_color) + + # Arrow shaft + shaft_top = head_base + shaft_bottom = cy + int(S * 0.28) + shaft_half_w = int(S * 0.04) + + draw.rectangle([ + cx - shaft_half_w, shaft_top, + cx + shaft_half_w, shaft_bottom + ], fill=arrow_color) + + +def make_side_up(fluid="none"): + """Side face with flow arrow pointing up (base for rotation).""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + fc = FLUID_COLORS[fluid] + active = fc is not None + + # Pipe body band (central horizontal strip) + pipe_half = int(S * 0.18) + pipe_y1 = 0 + pipe_y2 = S + + # Left and right pipe walls + wall_left = cx - pipe_half + wall_right = cx + pipe_half + draw.rectangle([wall_left, pipe_y1, wall_right, pipe_y2], + fill=PIPE_BODY) + + # Pipe wall highlights + draw.line([(wall_left, pipe_y1), (wall_left, pipe_y2)], + fill=PIPE_BODY_LIGHT, width=3) + draw.line([(wall_right, pipe_y1), (wall_right, pipe_y2)], + fill=PIPE_BODY_DARK, width=3) + + # Pipe wall bevel + for i in range(6): + t = i / 6 + c = lerp_color(ARMOR_DARK, PIPE_BODY, t) + draw.line([(wall_left - 6 + i, pipe_y1), + (wall_left - 6 + i, pipe_y2)], fill=c) + c2 = lerp_color(PIPE_BODY, ARMOR_DARK, t) + draw.line([(wall_right + 1 + i, pipe_y1), + (wall_right + 1 + i, pipe_y2)], fill=c2) + + # Fluid fill inside pipe + if active: + fill_half = int(S * 0.10) + fill_left = cx - fill_half + fill_right = cx + fill_half + draw.rectangle([fill_left, 0, fill_right, S], + fill=fc["fill_dark"]) + # Brighter center stripe + center_half = int(S * 0.04) + draw.rectangle([cx - center_half, 0, cx + center_half, S], + fill=fc["fill"]) + + # Joint flanges (horizontal bands across the pipe) + for flange_y in [int(S * 0.15), int(S * 0.85)]: + flange_h = 12 + draw.rectangle([wall_left - 8, flange_y - flange_h, + wall_right + 8, flange_y + flange_h], + fill=PIPE_RIM, outline=EDGE_DARK) + # Flange bolts + for fbx in [wall_left + 20, wall_right - 20]: + draw_filled_circle(draw, fbx, flange_y, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, fbx, flange_y, 4, RIVET_COLOR) + + # Flow direction arrow + arrow_c = fc["bright"] if active else ARROW_COLOR + _draw_arrow_up(draw, cx, cy, arrow_c) + + # Arrow outline for visibility + if active: + # Glow along pipe edges + for x in range(max(0, wall_left - 15), min(S, wall_right + 16)): + for y_off in range(8): + dl = abs(x - wall_left) + dr = abs(x - wall_right) + d = min(dl, dr) + if d < 8: + t = 0.2 * (1.0 - d / 8) + for y in range(0, S, 4): + if 0 <= y < S: + base = img.getpixel((x, y)) + img.putpixel((x, y), + blend_over(base, fc["glow"], t)) + break # only need outer loop once per x + + # Seam lines on armor sections + m = 30 + seam_y = cy + if not active: + draw = ImageDraw.Draw(img) + draw.line([(m, seam_y), (wall_left - 10, seam_y)], + fill=SEAM_COLOR, width=2) + draw.line([(wall_right + 10, seam_y), (S - m, seam_y)], + fill=SEAM_COLOR, width=2) + else: + add_glowing_seam( + img, (m, seam_y), (wall_left - 10, seam_y), + SEAM_COLOR, fc["glow"], + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (wall_right + 10, seam_y), (S - m, seam_y), + SEAM_COLOR, fc["glow"], + seam_width=2, glow_width=8, intensity=0.15 + ) + + draw = ImageDraw.Draw(img) + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# Public face functions + rotation +# --------------------------------------------------------------------------- + +def make_side_down(fluid="none"): + return make_side_up(fluid).rotate(180) + + +def make_side_left(fluid="none"): + return make_side_up(fluid).rotate(90) + + +def make_side_right(fluid="none"): + return make_side_up(fluid).rotate(-90) + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +FLUID_SUFFIXES = {"none": "", "water": "_filled", "lava": "_filled_lava", + "xp": "_filled_xp"} + + +def main(): + textures = {} + + for fluid, suffix in FLUID_SUFFIXES.items(): + textures[f"fluid_pipe_front{suffix}"] = make_front(fluid) + textures[f"fluid_pipe_back{suffix}"] = make_back(fluid) + textures[f"fluid_pipe_side{suffix}_up"] = make_side_up(fluid) + textures[f"fluid_pipe_side{suffix}_down"] = make_side_down(fluid) + textures[f"fluid_pipe_side{suffix}_left"] = make_side_left(fluid) + textures[f"fluid_pipe_side{suffix}_right"] = make_side_right(fluid) + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_fluid_pump_textures.py b/scripts/generate_fluid_pump_textures.py new file mode 100644 index 0000000..c6be5ad --- /dev/null +++ b/scripts/generate_fluid_pump_textures.py @@ -0,0 +1,491 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Fluid Pump at 1024x1024. + +Concept: Heavy industrial pump housing with honeycomb grid. Side face +shows a large circular pump mechanism with pipe connectors. Top has a +pressure gauge and exhaust port. Bottom has ventilation grille and +mounting feet. Active states glow with fluid color. + +Creates 9 textures: + top, bottom, side × 3 states (inactive, active water, active lava) +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Pump mechanism +MECH_OUTER = (75, 80, 95, 255) +MECH_MID = (60, 65, 78, 255) +MECH_INNER = (45, 48, 58, 255) +MECH_BOLT = (90, 95, 110, 255) +MECH_HUB = (30, 33, 40, 255) + +# Connector / pipe elements +PIPE_RIM = (80, 85, 98, 255) +HOLE_DARK = (12, 14, 18, 255) + +# Gauge +GAUGE_BG = (18, 20, 26, 255) +GAUGE_RIM = (85, 90, 102, 255) +GAUGE_TICK = (110, 115, 128, 255) + +# Vent grille +VENT_SLOT = (15, 18, 24, 255) + +# Fluid colors +FLUID_COLORS = { + "inactive": None, + "water": { + "accent": (0, 212, 255, 255), + "glow": (0, 229, 255, 255), + "dim": (0, 150, 200, 255), + "fill": (33, 150, 243, 255), + }, + "lava": { + "accent": (255, 140, 0, 255), + "glow": (255, 100, 20, 255), + "dim": (200, 80, 10, 255), + "fill": (230, 90, 20, 255), + }, +} + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + return img + + +def add_corner_bolts(draw, inset=30): + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +# --------------------------------------------------------------------------- +# Side face — pump mechanism +# --------------------------------------------------------------------------- + +def make_side(state="inactive"): + """Side face: pump housing with large circular mechanism and connectors.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + fc = FLUID_COLORS[state] + active = fc is not None + + # Upper panel strip + panel_h = int(S * 0.10) + draw.rectangle([9, 9, S - 10, panel_h], fill=ARMOR_MID) + add_bevel_border(draw, 9, 9, S - 10, panel_h, ARMOR_LIGHT, EDGE_DARK, + width=2) + + # Indicator window in upper panel + win_x = int(S * 0.08) + win_w = int(S * 0.12) + win_h = int(panel_h * 0.5) + win_y = int(panel_h * 0.25) + indicator_color = fc["accent"] if active else EDGE_DARK + draw.rectangle([win_x, win_y, win_x + win_w, win_y + win_h], + fill=indicator_color, outline=EDGE_DARK) + + # Rivets along upper panel + for rx in range(int(S * 0.3), S - 30, int(S * 0.12)): + draw_filled_circle(draw, rx, panel_h // 2 + 5, 5, RIVET_COLOR) + + # Lower base strip + base_top = S - panel_h + draw.rectangle([9, base_top, S - 10, S - 10], fill=ARMOR_MID) + add_bevel_border(draw, 9, base_top, S - 10, S - 10, ARMOR_LIGHT, + EDGE_DARK, width=2) + # Rivets along base + for rx in range(50, S - 30, int(S * 0.1)): + draw_filled_circle(draw, rx, base_top + panel_h // 2, 5, + RIVET_COLOR) + + # Main pump mechanism — large circle + mech_r = int(S * 0.25) + # Outer housing ring + draw_filled_circle(draw, cx, cy, mech_r + 15, MECH_OUTER, + outline=EDGE_DARK) + # Gradient ring + for r in range(mech_r + 12, mech_r - 5, -1): + t = (mech_r + 12 - r) / 17 + c = lerp_color(MECH_OUTER, MECH_MID, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Inner mechanism + draw_filled_circle(draw, cx, cy, mech_r - 5, MECH_MID, + outline=EDGE_DARK) + + # Concentric rings inside mechanism + inner_r = int(mech_r * 0.6) + draw_filled_circle(draw, cx, cy, inner_r, MECH_INNER, + outline=EDGE_DARK) + + # Center hub + hub_r = int(mech_r * 0.25) + hub_color = fc["accent"] if active else MECH_HUB + draw_filled_circle(draw, cx, cy, hub_r, hub_color, outline=EDGE_DARK) + + # Hub cross pattern + cross_color = fc["dim"] if active else EDGE_DARK + draw.line([(cx - hub_r + 5, cy), (cx + hub_r - 5, cy)], + fill=cross_color, width=4) + draw.line([(cx, cy - hub_r + 5), (cx, cy + hub_r - 5)], + fill=cross_color, width=4) + + # Center bolt + bolt_color = fc["accent"] if active else RIVET_COLOR + draw_filled_circle(draw, cx, cy, 12, ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, cx, cy, 6, bolt_color) + + # Mechanism bolts (around inner ring) + bolt_ring_r = int((mech_r - 5 + inner_r) / 2) + for i in range(8): + angle = math.radians(45 * i + 22.5) + bx = int(cx + bolt_ring_r * math.cos(angle)) + by = int(cy + bolt_ring_r * math.sin(angle)) + draw_filled_circle(draw, bx, by, 8, MECH_BOLT, outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Pressure line from mechanism to upper panel + line_x = cx + draw.line([(line_x - 4, panel_h), (line_x - 4, cy - mech_r - 15)], + fill=PIPE_RIM, width=3) + draw.line([(line_x + 4, panel_h), (line_x + 4, cy - mech_r - 15)], + fill=PIPE_RIM, width=3) + if active: + draw.line([(line_x, panel_h + 2), (line_x, cy - mech_r - 13)], + fill=fc["dim"], width=3) + + # Pipe connectors on left and right edges + conn_h = int(S * 0.08) + conn_w = int(S * 0.06) + for side_x, align in [(9, "left"), (S - 10 - conn_w, "right")]: + draw.rectangle([side_x, cy - conn_h, side_x + conn_w, cy + conn_h], + fill=PIPE_RIM, outline=EDGE_DARK) + # Pipe opening + hole_margin = 8 + hole_color = fc["dim"] if active else HOLE_DARK + draw.rectangle([side_x + hole_margin, cy - conn_h + hole_margin, + side_x + conn_w - hole_margin, + cy + conn_h - hole_margin], + fill=hole_color, outline=EDGE_DARK) + + # Glow effects when active + if active: + add_glow_ring(img, cx, cy, inner_r, + inner_r + int(inner_r * 0.5), fc["glow"]) + add_radial_glow(img, cx, cy, hub_r + 10, fc["accent"], + intensity=0.3) + + draw = ImageDraw.Draw(img) + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# Top face — pressure gauge and exhaust +# --------------------------------------------------------------------------- + +def make_top(state="inactive"): + """Top face: tech plate with pressure gauge, vent, and panel seams.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + fc = FLUID_COLORS[state] + active = fc is not None + + # Panel seam cross + m = 30 + seam_points = [ + ((m, cy), (S - m, cy)), + ((cx, m), (cx, S - m)), + ] + for start, end in seam_points: + if active: + add_glowing_seam(img, start, end, SEAM_COLOR, fc["glow"], + seam_width=3, glow_width=10, intensity=0.15) + else: + draw = ImageDraw.Draw(img) + draw.line([start, end], fill=SEAM_COLOR, width=3) + + draw = ImageDraw.Draw(img) + + # Upper-left quadrant: Pressure gauge + gauge_cx = int(S * 0.28) + gauge_cy = int(S * 0.28) + gauge_r = int(S * 0.14) + + # Gauge bezel + draw_filled_circle(draw, gauge_cx, gauge_cy, gauge_r + 6, GAUGE_RIM, + outline=EDGE_DARK) + draw_filled_circle(draw, gauge_cx, gauge_cy, gauge_r, GAUGE_BG) + + # Tick marks + for i in range(9): + angle = math.radians(225 - i * 11.25) + tx1 = int(gauge_cx + (gauge_r - 4) * math.cos(angle)) + ty1 = int(gauge_cy - (gauge_r - 4) * math.sin(angle)) + tx2 = int(gauge_cx + (gauge_r - 14) * math.cos(angle)) + ty2 = int(gauge_cy - (gauge_r - 14) * math.sin(angle)) + w = 3 if i % 4 == 0 else 1 + draw.line([(tx1, ty1), (tx2, ty2)], fill=GAUGE_TICK, width=w) + + # Needle + if active: + needle_angle = math.radians(315) + needle_color = fc["accent"] + else: + needle_angle = math.radians(225) + needle_color = GAUGE_TICK + + nx = int(gauge_cx + (gauge_r - 18) * math.cos(needle_angle)) + ny = int(gauge_cy - (gauge_r - 18) * math.sin(needle_angle)) + draw.line([(gauge_cx, gauge_cy), (nx, ny)], fill=needle_color, width=3) + draw_filled_circle(draw, gauge_cx, gauge_cy, 6, needle_color) + + # Upper-right quadrant: Vent grille + vent_x1 = int(S * 0.56) + vent_y1 = int(S * 0.14) + vent_x2 = int(S * 0.86) + vent_y2 = int(S * 0.42) + draw.rectangle([vent_x1, vent_y1, vent_x2, vent_y2], + fill=ARMOR_MID, outline=EDGE_DARK) + # Vent slots + slot_spacing = (vent_y2 - vent_y1 - 12) // 7 + for i in range(7): + sy = vent_y1 + 8 + i * slot_spacing + draw.line([(vent_x1 + 8, sy), (vent_x2 - 8, sy)], + fill=VENT_SLOT, width=4) + if active: + draw.line([(vent_x1 + 10, sy + 1), (vent_x2 - 10, sy + 1)], + fill=fc["dim"], width=2) + + # Lower-left quadrant: ID plate + plate_x1 = int(S * 0.12) + plate_y1 = int(S * 0.58) + plate_x2 = int(S * 0.42) + plate_y2 = int(S * 0.82) + draw.rectangle([plate_x1, plate_y1, plate_x2, plate_y2], + fill=ARMOR_MID, outline=EDGE_DARK) + add_bevel_border(draw, plate_x1, plate_y1, plate_x2, plate_y2, + ARMOR_LIGHT, EDGE_DARK, width=2) + # Etched lines on plate + for ly in range(plate_y1 + 20, plate_y2 - 15, 18): + draw.line([(plate_x1 + 15, ly), (plate_x2 - 15, ly)], + fill=SEAM_COLOR, width=1) + + # Lower-right quadrant: Exhaust port + exhaust_cx = int(S * 0.72) + exhaust_cy = int(S * 0.72) + exhaust_r = int(S * 0.12) + draw_filled_circle(draw, exhaust_cx, exhaust_cy, exhaust_r + 6, + MECH_OUTER, outline=EDGE_DARK) + draw_filled_circle(draw, exhaust_cx, exhaust_cy, exhaust_r, + EDGE_DARK, outline=MECH_MID) + exhaust_inner = int(exhaust_r * 0.55) + exhaust_color = fc["dim"] if active else HOLE_DARK + draw_filled_circle(draw, exhaust_cx, exhaust_cy, exhaust_inner, + exhaust_color, outline=EDGE_DARK) + + if active: + add_radial_glow(img, exhaust_cx, exhaust_cy, exhaust_r, + fc["glow"], intensity=0.2) + + # Corner bolts for each quadrant + quad_bolt_positions = [ + (int(S * 0.08), int(S * 0.08)), + (int(S * 0.92), int(S * 0.08)), + (int(S * 0.08), int(S * 0.92)), + (int(S * 0.92), int(S * 0.92)), + ] + draw = ImageDraw.Draw(img) + for bx, by in quad_bolt_positions: + draw_filled_circle(draw, bx, by, 12, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 6, RIVET_COLOR) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# Bottom face — heavy base plate with ventilation +# --------------------------------------------------------------------------- + +def make_bottom(state="inactive"): + """Bottom face: base plate with ventilation grille and mounting feet.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + fc = FLUID_COLORS[state] + active = fc is not None + + # Raised base plate + plate_margin = int(S * 0.08) + draw.rectangle([plate_margin, plate_margin, + S - plate_margin, S - plate_margin], + fill=ARMOR_MID, outline=EDGE_DARK) + add_bevel_border(draw, plate_margin, plate_margin, + S - plate_margin, S - plate_margin, + ARMOR_LIGHT, EDGE_DARK, width=3) + + # Central ventilation grille + grille_margin = int(S * 0.2) + grille_x1 = grille_margin + grille_x2 = S - grille_margin + slot_y_start = grille_margin + 20 + slot_y_end = S - grille_margin - 20 + num_slots = 12 + slot_spacing = (slot_y_end - slot_y_start) // num_slots + + for i in range(num_slots): + sy = slot_y_start + i * slot_spacing + draw.rectangle([grille_x1 + 10, sy, + grille_x2 - 10, sy + slot_spacing - 8], + fill=VENT_SLOT, outline=EDGE_DARK) + if active: + draw.rectangle([grille_x1 + 14, sy + 2, + grille_x2 - 14, sy + slot_spacing - 10], + fill=fc["dim"]) + + # Mounting feet (4 corners of the base plate) + foot_size = int(S * 0.08) + foot_positions = [ + (plate_margin + 10, plate_margin + 10), + (S - plate_margin - foot_size - 10, plate_margin + 10), + (plate_margin + 10, S - plate_margin - foot_size - 10), + (S - plate_margin - foot_size - 10, + S - plate_margin - foot_size - 10), + ] + for fx, fy in foot_positions: + draw.rectangle([fx, fy, fx + foot_size, fy + foot_size], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + add_bevel_border(draw, fx, fy, fx + foot_size, fy + foot_size, + RIVET_COLOR, EDGE_DARK, width=2) + foot_cx = fx + foot_size // 2 + foot_cy = fy + foot_size // 2 + draw_filled_circle(draw, foot_cx, foot_cy, 12, ARMOR_MID, + outline=EDGE_DARK) + draw_filled_circle(draw, foot_cx, foot_cy, 6, RIVET_COLOR) + + if active: + add_radial_glow(img, cx, cy, int(S * 0.3), fc["glow"], + intensity=0.1) + + draw = ImageDraw.Draw(img) + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +STATES = { + "inactive": "", + "water": "_active", + "lava": "_active_lava", +} + + +def main(): + textures = {} + + for state, suffix in STATES.items(): + textures[f"fluid_pump_top{suffix}"] = make_top(state) + textures[f"fluid_pump_bottom{suffix}"] = make_bottom(state) + textures[f"fluid_pump_side{suffix}"] = make_side(state) + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_fluid_splitter_textures.py b/scripts/generate_fluid_splitter_textures.py new file mode 100644 index 0000000..fb44ac4 --- /dev/null +++ b/scripts/generate_fluid_splitter_textures.py @@ -0,0 +1,585 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Fluid Splitter at 1024x1024. + +Concept: Inverse of Fluid Merger — one input splits to many outputs. +Same design language with input/output reversed. Color coding: + - Input face (front): always RED + - Output faces (back/side/bottom): GREY when empty, fluid-colored when active + - water: blue + - lava: orange + - experience: green + - Top face: gauge showing fill level + +Creates 20 textures: + 5 faces (front, back, side, top, bottom) x 4 fluid states +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor (consistent with other Atlas blocks) +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Connector hole +CONNECTOR_HOLE = (12, 14, 18, 255) + +# Front (output) — grey when empty (no fluid flowing) +FRONT_RING = (120, 125, 135, 255) +FRONT_BRIGHT = (160, 165, 175, 255) +FRONT_GLOW = (140, 145, 155, 255) + +# Input faces — always red (matches power merger input convention) +INPUT_RING = (200, 50, 40, 255) +INPUT_BRIGHT = (255, 70, 50, 255) +INPUT_GLOW = (255, 60, 40, 255) + +# Output fluid overrides — when a fluid is active, the output face +# changes to the fluid's color. Input faces stay red always. +OUTPUT_FLUID_COLORS = { + "none": None, + "water": { + "ring": (30, 100, 220, 255), + "bright": (50, 140, 255, 255), + "glow": (40, 120, 255, 255), + }, + "lava": { + "ring": (220, 120, 30, 255), + "bright": (255, 160, 50, 255), + "glow": (255, 140, 40, 255), + }, + "xp": { + "ring": (50, 200, 50, 255), + "bright": (80, 255, 80, 255), + "glow": (60, 240, 60, 255), + }, +} + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + return img + + +def add_corner_bolts(draw, inset=30): + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +def draw_connector_port(img, draw, cx, cy, outer_r, inner_r, + ring_color, bright_color, glow_color, + active=False): + """Draw a color-coded circular connector port with contact pins.""" + active_ring = ring_color if not active else bright_color + + for r in range(outer_r, inner_r, -1): + t = (outer_r - r) / (outer_r - inner_r) + c = lerp_color(active_ring, ARMOR_MID, t * 0.6) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + draw_filled_circle(draw, cx, cy, inner_r, CONNECTOR_HOLE, + outline=EDGE_DARK) + + pin_r = int(inner_r * 0.5) + pin_size = max(4, inner_r // 12) + pin_color = ring_color if not active else bright_color + for angle_deg in [0, 90, 180, 270]: + angle = math.radians(angle_deg) + px = int(cx + pin_r * math.cos(angle)) + py = int(cy + pin_r * math.sin(angle)) + draw_filled_circle(draw, px, py, pin_size, pin_color) + + if active: + add_glow_ring(img, cx, cy, outer_r, + outer_r + int(outer_r * 0.35), glow_color) + add_radial_glow(img, cx, cy, inner_r, bright_color, + intensity=0.3) + + +# --------------------------------------------------------------------------- +# Fluid-specific details (differentiate from power merger) +# --------------------------------------------------------------------------- + +PIPE_COLOR = (60, 65, 78, 255) +PIPE_HIGHLIGHT = (80, 85, 98, 255) +PIPE_SHADOW = (30, 33, 40, 255) +FLANGE_COLOR = (90, 95, 108, 255) +GAUGE_BG = (18, 20, 26, 255) +GAUGE_RIM = (85, 90, 102, 255) +GAUGE_TICK = (110, 115, 128, 255) +GAUGE_NEEDLE = (200, 50, 40, 255) + + +def draw_pipe_stub(draw, x1, y1, x2, y2, horizontal=True): + """Draw a pipe stub with highlight and flange ends.""" + pw = 18 # pipe width + if horizontal: + draw.rectangle([x1, y1 - pw // 2, x2, y1 + pw // 2], fill=PIPE_COLOR) + draw.line([(x1, y1 - pw // 2), (x2, y1 - pw // 2)], + fill=PIPE_HIGHLIGHT, width=2) + draw.line([(x1, y1 + pw // 2), (x2, y1 + pw // 2)], + fill=PIPE_SHADOW, width=2) + # Flanges at each end + fw = pw + 10 + for fx in [x1, x2]: + draw.rectangle([fx - 4, y1 - fw // 2, fx + 4, y1 + fw // 2], + fill=FLANGE_COLOR, outline=EDGE_DARK) + else: + draw.rectangle([x1 - pw // 2, y1, x1 + pw // 2, y2], fill=PIPE_COLOR) + draw.line([(x1 - pw // 2, y1), (x1 - pw // 2, y2)], + fill=PIPE_HIGHLIGHT, width=2) + draw.line([(x1 + pw // 2, y1), (x1 + pw // 2, y2)], + fill=PIPE_SHADOW, width=2) + fw = pw + 10 + for fy in [y1, y2]: + draw.rectangle([x1 - fw // 2, fy - 4, x1 + fw // 2, fy + 4], + fill=FLANGE_COLOR, outline=EDGE_DARK) + + +def draw_pressure_gauge(draw, gx, gy, radius, fluid, glow_color=None): + """Draw a small circular pressure gauge.""" + # Gauge body + draw_filled_circle(draw, gx, gy, radius + 3, FLANGE_COLOR, + outline=EDGE_DARK) + draw_filled_circle(draw, gx, gy, radius, GAUGE_BG) + draw_filled_circle(draw, gx, gy, radius, None, outline=GAUGE_RIM) + + # Tick marks around the gauge (from 7 o'clock to 5 o'clock) + for i in range(7): + angle = math.radians(225 - i * 30) + tx1 = int(gx + (radius - 3) * math.cos(angle)) + ty1 = int(gy - (radius - 3) * math.sin(angle)) + tx2 = int(gx + (radius - 7) * math.cos(angle)) + ty2 = int(gy - (radius - 7) * math.sin(angle)) + draw.line([(tx1, ty1), (tx2, ty2)], fill=GAUGE_TICK, width=1) + + # Needle — points higher when fluid is active + if fluid == "none": + needle_angle = math.radians(210) # low / empty + elif fluid in ("water", "lava", "xp"): + needle_angle = math.radians(330) # high / full + else: + needle_angle = math.radians(270) # mid + + needle_color = glow_color if glow_color else GAUGE_NEEDLE + nx = int(gx + (radius - 8) * math.cos(needle_angle)) + ny = int(gy - (radius - 8) * math.sin(needle_angle)) + draw.line([(gx, gy), (nx, ny)], fill=needle_color, width=2) + draw_filled_circle(draw, gx, gy, 3, needle_color) + + +def add_fluid_details(draw, cx, cy, panel_r, port_outer, fluid, + glow_color, seam_style): + """Add pipe stubs and pressure gauge to differentiate from power merger.""" + # Pipe stubs from panel edge toward the connector port + pipe_end = port_outer + 15 + + if seam_style in ("horizontal", "cross", "none"): + # Left pipe stub + draw_pipe_stub(draw, cx - panel_r + 8, cy, + cx - pipe_end, cy, horizontal=True) + # Right pipe stub + draw_pipe_stub(draw, cx + pipe_end, cy, + cx + panel_r - 8, cy, horizontal=True) + + if seam_style in ("cross",): + # Top pipe stub + draw_pipe_stub(draw, cx, cy - panel_r + 8, + cx, cy - pipe_end, horizontal=False) + # Bottom pipe stub + draw_pipe_stub(draw, cx, cy + pipe_end, + cx, cy + panel_r - 8, horizontal=False) + + # Pressure gauge in upper-right corner of panel + gauge_r = 22 + gauge_x = cx + panel_r - 45 + gauge_y = cy - panel_r + 45 + draw_pressure_gauge(draw, gauge_x, gauge_y, gauge_r, fluid, + glow_color=glow_color if fluid != "none" else None) + + +# --------------------------------------------------------------------------- +# Face builder +# --------------------------------------------------------------------------- + +def _get_colors(fluid, default_ring, default_bright, default_glow, + is_output=False): + """Return (ring, bright, glow, is_active) for a given fluid state. + + Input faces always use their default colors (red) regardless of fluid. + Output face switches to the fluid's color when active. + """ + if not is_output: + # Input faces: always red, glow when fluid is active + active = fluid != "none" + return default_ring, default_bright, default_glow, active + fc = OUTPUT_FLUID_COLORS[fluid] + if fc is None: + return default_ring, default_bright, default_glow, False + return fc["ring"], fc["bright"], fc["glow"], True + + +def make_face(fluid, default_ring, default_bright, default_glow, + seam_style="horizontal", is_output=False): + """Build a face with a single centered connector in a recessed panel.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + port_outer = int(S * 0.22) + port_inner = int(S * 0.12) + + ring, bright, glow, active = _get_colors( + fluid, default_ring, default_bright, default_glow, + is_output=is_output + ) + + # Recessed panel + panel_r = int(S * 0.30) + draw.rectangle( + [cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Connector port + draw_connector_port(img, draw, cx, cy, port_outer, port_inner, + ring, bright, glow, active=active) + + # Fluid-specific details (pipe stubs + pressure gauge) + add_fluid_details(draw, cx, cy, panel_r, port_outer, fluid, + glow, seam_style) + + # Panel bolts + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Seams + m = 30 + seam_glow = glow if active else None + if seam_style in ("horizontal", "cross"): + if active: + add_glowing_seam( + img, (m, cy), (cx - panel_r - 10, cy), + SEAM_COLOR, seam_glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (cx + panel_r + 10, cy), (S - m, cy), + SEAM_COLOR, seam_glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + else: + draw.line([(m, cy), (cx - panel_r - 10, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(cx + panel_r + 10, cy), (S - m, cy)], + fill=SEAM_COLOR, width=2) + + if seam_style == "cross": + if active: + add_glowing_seam( + img, (cx, m), (cx, cy - panel_r - 10), + SEAM_COLOR, seam_glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (cx, cy + panel_r + 10), (cx, S - m), + SEAM_COLOR, seam_glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + else: + draw.line([(cx, m), (cx, cy - panel_r - 10)], + fill=SEAM_COLOR, width=2) + draw.line([(cx, cy + panel_r + 10), (cx, S - m)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# Public face functions +# --------------------------------------------------------------------------- + +def make_front(fluid="none"): + """Front face (input): always red connector.""" + return make_face( + fluid, INPUT_RING, INPUT_BRIGHT, INPUT_GLOW, + seam_style="none" + ) + + +def make_back(fluid="none"): + """Back face (output): grey when empty, fluid-colored when active.""" + return make_face( + fluid, FRONT_RING, FRONT_BRIGHT, FRONT_GLOW, + seam_style="none", is_output=True + ) + + +def make_side(fluid="none"): + """Side face: output connector with horizontal seams.""" + return make_face( + fluid, FRONT_RING, FRONT_BRIGHT, FRONT_GLOW, + seam_style="horizontal", is_output=True + ) + + +def make_top(fluid="none"): + """Top face: large gauge showing fluid fill level.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Recessed panel + panel_r = int(S * 0.38) + draw.rectangle( + [cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Determine fluid colors and needle angle + active = fluid != "none" + fc = OUTPUT_FLUID_COLORS[fluid] + if fc is None: + needle_color = GAUGE_TICK + zone_color = (60, 65, 78, 255) + needle_angle_deg = 225 # low / empty + else: + needle_color = fc["bright"] + zone_color = fc["ring"] + needle_angle_deg = 315 # high / full + + # Large gauge circle + gauge_r = int(S * 0.28) + # Outer bezel ring + draw_filled_circle(draw, cx, cy, gauge_r + 8, FLANGE_COLOR, + outline=EDGE_DARK) + draw_filled_circle(draw, cx, cy, gauge_r + 4, GAUGE_RIM) + # Gauge face + draw_filled_circle(draw, cx, cy, gauge_r, GAUGE_BG) + + # Colored arc zone (from 7 o'clock to 1 o'clock, showing capacity) + arc_inner = gauge_r - 30 + arc_outer = gauge_r - 10 + for deg in range(225, 314): + angle = math.radians(deg) + for r in range(arc_inner, arc_outer): + ax = int(cx + r * math.cos(angle)) + ay = int(cy - r * math.sin(angle)) + if 0 <= ax < S and 0 <= ay < S: + t = 0.3 if not active else 0.7 + base = img.getpixel((ax, ay)) + img.putpixel((ax, ay), blend_over(base, zone_color, t)) + + # Re-get draw after putpixel operations + draw = ImageDraw.Draw(img) + + # Major tick marks (7 o'clock to 1 o'clock, 9 ticks) + for i in range(9): + angle = math.radians(225 - i * 11.25) # ~90 degree sweep + # Outer tick + tx1 = int(cx + (gauge_r - 6) * math.cos(angle)) + ty1 = int(cy - (gauge_r - 6) * math.sin(angle)) + tx2 = int(cx + (gauge_r - 20) * math.cos(angle)) + ty2 = int(cy - (gauge_r - 20) * math.sin(angle)) + w = 3 if i % 4 == 0 else 1 + draw.line([(tx1, ty1), (tx2, ty2)], fill=GAUGE_TICK, width=w) + + # Minor tick marks + for i in range(33): + angle = math.radians(225 - i * 2.8125) + tx1 = int(cx + (gauge_r - 6) * math.cos(angle)) + ty1 = int(cy - (gauge_r - 6) * math.sin(angle)) + tx2 = int(cx + (gauge_r - 12) * math.cos(angle)) + ty2 = int(cy - (gauge_r - 12) * math.sin(angle)) + draw.line([(tx1, ty1), (tx2, ty2)], fill=EDGE_DARK, width=1) + + # Labels: "E" (empty) near 7 o'clock, "F" (full) near 1 o'clock + e_angle = math.radians(220) + e_x = int(cx + (gauge_r - 45) * math.cos(e_angle)) + e_y = int(cy - (gauge_r - 45) * math.sin(e_angle)) + # Draw E as lines + draw.line([(e_x - 6, e_y - 8), (e_x - 6, e_y + 8)], + fill=GAUGE_TICK, width=2) + draw.line([(e_x - 6, e_y - 8), (e_x + 5, e_y - 8)], + fill=GAUGE_TICK, width=2) + draw.line([(e_x - 6, e_y), (e_x + 3, e_y)], + fill=GAUGE_TICK, width=2) + draw.line([(e_x - 6, e_y + 8), (e_x + 5, e_y + 8)], + fill=GAUGE_TICK, width=2) + + f_angle = math.radians(320) + f_x = int(cx + (gauge_r - 45) * math.cos(f_angle)) + f_y = int(cy - (gauge_r - 45) * math.sin(f_angle)) + # Draw F as lines + draw.line([(f_x - 6, f_y - 8), (f_x - 6, f_y + 8)], + fill=GAUGE_TICK, width=2) + draw.line([(f_x - 6, f_y - 8), (f_x + 5, f_y - 8)], + fill=GAUGE_TICK, width=2) + draw.line([(f_x - 6, f_y), (f_x + 3, f_y)], + fill=GAUGE_TICK, width=2) + + # Needle + needle_angle = math.radians(needle_angle_deg) + needle_len = gauge_r - 35 + nx = int(cx + needle_len * math.cos(needle_angle)) + ny = int(cy - needle_len * math.sin(needle_angle)) + draw.line([(cx, cy), (nx, ny)], fill=needle_color, width=4) + # Needle hub + draw_filled_circle(draw, cx, cy, 12, FLANGE_COLOR, outline=EDGE_DARK) + draw_filled_circle(draw, cx, cy, 6, needle_color) + + # Glow effect when active + if active: + add_glow_ring(img, cx, cy, gauge_r, + gauge_r + int(gauge_r * 0.4), fc["glow"]) + add_radial_glow(img, cx, cy, gauge_r + 30, + fc["glow"], intensity=0.45) + draw = ImageDraw.Draw(img) + + # Panel bolts + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + add_corner_bolts(draw) + return img + + +def make_bottom(fluid="none"): + """Bottom face: output connector with cross seams.""" + return make_face( + fluid, FRONT_RING, FRONT_BRIGHT, FRONT_GLOW, + seam_style="cross", is_output=True + ) + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +FLUID_SUFFIXES = {"none": "", "water": "_water", "lava": "_lava", + "xp": "_xp"} + +FACES = { + "front": make_front, + "back": make_back, + "side": make_side, + "top": make_top, + "bottom": make_bottom, +} + + +def main(): + textures = {} + for face_name, make_fn in FACES.items(): + for fluid, suffix in FLUID_SUFFIXES.items(): + tex_name = f"fluid_splitter_{face_name}{suffix}" + textures[tex_name] = make_fn(fluid=fluid) + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_lava_generator_textures.py b/scripts/generate_lava_generator_textures.py new file mode 100644 index 0000000..06d47fb --- /dev/null +++ b/scripts/generate_lava_generator_textures.py @@ -0,0 +1,419 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Lava Generator at 1024x1024. + +Concept: Heavy industrial lava generation unit. Side face shows a large +heat window/chamber with containment grid. Top has a magma core output +port with heat vents. Bottom is a heavy base plate with ventilation. +Active state glows with orange/lava colors. + +Creates 5 textures: + top, top_active, bottom, side, side_active +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Lava / heat colors +LAVA_BRIGHT = (255, 160, 30, 255) +LAVA_MID = (230, 90, 20, 255) +LAVA_DIM = (180, 60, 10, 255) +LAVA_DARK = (120, 30, 5, 255) +LAVA_GLOW = (255, 140, 40, 255) +EMBER = (200, 50, 10, 255) + +# Heat chamber +CHAMBER_DARK = (15, 12, 10, 255) +CHAMBER_GRID = (55, 50, 45, 255) +CHAMBER_FRAME = (65, 60, 55, 255) + +# Inactive window +WINDOW_DARK = (20, 22, 28, 255) +WINDOW_GRID = (40, 42, 50, 255) + +# Vent / exhaust +VENT_SLOT = (15, 18, 24, 255) + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + return img + + +def add_corner_bolts(draw, inset=30): + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +# --------------------------------------------------------------------------- +# Side face — heat chamber window +# --------------------------------------------------------------------------- + +def make_side(active=False): + """Side face: heat chamber window with containment grid.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Upper panel strip + panel_h = int(S * 0.08) + draw.rectangle([9, 9, S - 10, panel_h + 9], fill=ARMOR_MID) + add_bevel_border(draw, 9, 9, S - 10, panel_h + 9, ARMOR_LIGHT, + EDGE_DARK, width=2) + # Rivets along upper panel + for rx in range(60, S - 40, int(S * 0.08)): + draw_filled_circle(draw, rx, 9 + panel_h // 2, 5, RIVET_COLOR) + + # Lower panel strip + base_top = S - panel_h - 9 + draw.rectangle([9, base_top, S - 10, S - 10], fill=ARMOR_MID) + add_bevel_border(draw, 9, base_top, S - 10, S - 10, ARMOR_LIGHT, + EDGE_DARK, width=2) + for rx in range(60, S - 40, int(S * 0.08)): + draw_filled_circle(draw, rx, base_top + panel_h // 2, 5, + RIVET_COLOR) + + # Main heat chamber window + win_margin_x = int(S * 0.10) + win_y1 = panel_h + 9 + 20 + win_y2 = base_top - 20 + win_x1 = win_margin_x + win_x2 = S - win_margin_x + + # Window frame + frame_w = 8 + draw.rectangle([win_x1 - frame_w, win_y1 - frame_w, + win_x2 + frame_w, win_y2 + frame_w], + fill=CHAMBER_FRAME if active else ARMOR_MID, + outline=EDGE_DARK) + add_bevel_border(draw, win_x1 - frame_w, win_y1 - frame_w, + win_x2 + frame_w, win_y2 + frame_w, + ARMOR_LIGHT, EDGE_DARK, width=3) + + # Window interior + if active: + # Lava glow background + draw.rectangle([win_x1, win_y1, win_x2, win_y2], fill=LAVA_DARK) + # Gradient from dark edges to bright center + win_cx = (win_x1 + win_x2) // 2 + win_cy = (win_y1 + win_y2) // 2 + win_w = win_x2 - win_x1 + win_h = win_y2 - win_y1 + for x in range(win_x1, win_x2 + 1): + for y in range(win_y1, win_y2 + 1): + dx = abs(x - win_cx) / (win_w / 2) + dy = abs(y - win_cy) / (win_h / 2) + d = min(1.0, math.sqrt(dx * dx + dy * dy)) + t = (1.0 - d) * 0.7 + base = img.getpixel((x, y)) + img.putpixel((x, y), blend_over(base, LAVA_BRIGHT, t)) + draw = ImageDraw.Draw(img) + else: + draw.rectangle([win_x1, win_y1, win_x2, win_y2], fill=WINDOW_DARK) + + # Containment grid bars (vertical) + num_vbars = 8 + bar_spacing = (win_x2 - win_x1) // (num_vbars + 1) + bar_color = CHAMBER_GRID if active else WINDOW_GRID + for i in range(1, num_vbars + 1): + bx = win_x1 + i * bar_spacing + draw.line([(bx, win_y1), (bx, win_y2)], fill=bar_color, width=4) + + # Containment grid bars (horizontal) + num_hbars = 4 + hbar_spacing = (win_y2 - win_y1) // (num_hbars + 1) + for i in range(1, num_hbars + 1): + by = win_y1 + i * hbar_spacing + draw.line([(win_x1, by), (win_x2, by)], fill=bar_color, width=4) + + # Status indicator light (bottom-right of lower panel) + ind_cx = S - int(S * 0.12) + ind_cy = base_top + panel_h // 2 + ind_r = 16 + if active: + draw_filled_circle(draw, ind_cx, ind_cy, ind_r, LAVA_BRIGHT, + outline=EDGE_DARK) + add_radial_glow(img, ind_cx, ind_cy, ind_r + 8, LAVA_GLOW, + intensity=0.3) + draw = ImageDraw.Draw(img) + else: + draw_filled_circle(draw, ind_cx, ind_cy, ind_r, ARMOR_MID, + outline=EDGE_DARK) + draw_filled_circle(draw, ind_cx, ind_cy, ind_r - 4, EDGE_DARK) + + # Glow seams when active + if active: + # Glow around window frame + add_glow_ring(img, (win_x1 + win_x2) // 2, (win_y1 + win_y2) // 2, + max(win_w, win_h) // 2 - 20, + max(win_w, win_h) // 2 + 30, LAVA_GLOW) + draw = ImageDraw.Draw(img) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# Top face — magma core output with heat vents +# --------------------------------------------------------------------------- + +def make_top(active=False): + """Top face: magma core output port with radial heat vents.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Panel seams (cross pattern) + m = 30 + if active: + add_glowing_seam(img, (m, cy), (S - m, cy), SEAM_COLOR, LAVA_GLOW, + seam_width=3, glow_width=10, intensity=0.15) + add_glowing_seam(img, (cx, m), (cx, S - m), SEAM_COLOR, LAVA_GLOW, + seam_width=3, glow_width=10, intensity=0.15) + draw = ImageDraw.Draw(img) + else: + draw.line([(m, cy), (S - m, cy)], fill=SEAM_COLOR, width=3) + draw.line([(cx, m), (cx, S - m)], fill=SEAM_COLOR, width=3) + + # Central core port + core_outer = int(S * 0.25) + core_inner = int(S * 0.15) + + # Outer housing ring + draw_filled_circle(draw, cx, cy, core_outer + 10, ARMOR_LIGHT, + outline=EDGE_DARK) + for r in range(core_outer + 8, core_outer - 2, -1): + t = (core_outer + 8 - r) / 10 + c = lerp_color(ARMOR_LIGHT, ARMOR_MID, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Core interior + if active: + # Molten lava core + draw_filled_circle(draw, cx, cy, core_inner, LAVA_DARK) + for r in range(core_inner, 0, -1): + t = 1.0 - (r / core_inner) + c = lerp_color(LAVA_DARK, LAVA_BRIGHT, t ** 0.8) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + add_glow_ring(img, cx, cy, core_inner, + core_inner + int(core_inner * 0.5), LAVA_GLOW) + add_radial_glow(img, cx, cy, core_outer + 20, LAVA_GLOW, + intensity=0.25) + draw = ImageDraw.Draw(img) + else: + draw_filled_circle(draw, cx, cy, core_inner, EDGE_DARK, + outline=ARMOR_MID) + # Dark cross pattern on inactive core + draw.line([(cx - core_inner + 8, cy), (cx + core_inner - 8, cy)], + fill=ARMOR_MID, width=4) + draw.line([(cx, cy - core_inner + 8), (cx, cy + core_inner - 8)], + fill=ARMOR_MID, width=4) + + # Gradient ring between outer and inner + for r in range(core_outer - 2, core_inner, -1): + t = (core_outer - r) / (core_outer - core_inner) + if active: + c = lerp_color(ARMOR_MID, LAVA_DIM, t * 0.5) + else: + c = lerp_color(ARMOR_MID, EDGE_DARK, t * 0.4) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Core bolts + bolt_r = int((core_outer + core_inner) / 2) + 5 + for i in range(8): + angle = math.radians(45 * i + 22.5) + bx = int(cx + bolt_r * math.cos(angle)) + by = int(cy + bolt_r * math.sin(angle)) + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Heat vent slots in each quadrant + vent_r_start = core_outer + 30 + vent_r_end = int(S * 0.42) + for quadrant_angle in [45, 135, 225, 315]: + for i in range(4): + angle = math.radians(quadrant_angle + (i - 1.5) * 6) + x1 = int(cx + vent_r_start * math.cos(angle)) + y1 = int(cy + vent_r_start * math.sin(angle)) + x2 = int(cx + vent_r_end * math.cos(angle)) + y2 = int(cy + vent_r_end * math.sin(angle)) + if active: + draw.line([(x1, y1), (x2, y2)], fill=EMBER, width=3) + else: + draw.line([(x1, y1), (x2, y2)], fill=SEAM_COLOR, width=2) + + # Corner bolts + quad_positions = [ + (int(S * 0.12), int(S * 0.12)), + (int(S * 0.88), int(S * 0.12)), + (int(S * 0.12), int(S * 0.88)), + (int(S * 0.88), int(S * 0.88)), + ] + for bx, by in quad_positions: + draw_filled_circle(draw, bx, by, 12, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 6, RIVET_COLOR) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# Bottom face — heavy base plate +# --------------------------------------------------------------------------- + +def make_bottom(): + """Bottom face: heavy base plate with ventilation grille.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Raised base plate + plate_margin = int(S * 0.08) + draw.rectangle([plate_margin, plate_margin, + S - plate_margin, S - plate_margin], + fill=ARMOR_MID, outline=EDGE_DARK) + add_bevel_border(draw, plate_margin, plate_margin, + S - plate_margin, S - plate_margin, + ARMOR_LIGHT, EDGE_DARK, width=3) + + # Central ventilation grille + grille_margin = int(S * 0.2) + slot_y_start = grille_margin + 20 + slot_y_end = S - grille_margin - 20 + num_slots = 12 + slot_spacing = (slot_y_end - slot_y_start) // num_slots + + for i in range(num_slots): + sy = slot_y_start + i * slot_spacing + draw.rectangle([grille_margin + 10, sy, + S - grille_margin - 10, sy + slot_spacing - 8], + fill=VENT_SLOT, outline=EDGE_DARK) + + # Mounting feet + foot_size = int(S * 0.08) + foot_positions = [ + (plate_margin + 10, plate_margin + 10), + (S - plate_margin - foot_size - 10, plate_margin + 10), + (plate_margin + 10, S - plate_margin - foot_size - 10), + (S - plate_margin - foot_size - 10, + S - plate_margin - foot_size - 10), + ] + for fx, fy in foot_positions: + draw.rectangle([fx, fy, fx + foot_size, fy + foot_size], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + add_bevel_border(draw, fx, fy, fx + foot_size, fy + foot_size, + RIVET_COLOR, EDGE_DARK, width=2) + foot_cx = fx + foot_size // 2 + foot_cy = fy + foot_size // 2 + draw_filled_circle(draw, foot_cx, foot_cy, 12, ARMOR_MID, + outline=EDGE_DARK) + draw_filled_circle(draw, foot_cx, foot_cy, 6, RIVET_COLOR) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + textures = { + "lava_generator_side": make_side(active=False), + "lava_generator_side_active": make_side(active=True), + "lava_generator_top": make_top(active=False), + "lava_generator_top_active": make_top(active=True), + "lava_generator_bottom": make_bottom(), + } + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_obsidian_factory_textures.py b/scripts/generate_obsidian_factory_textures.py new file mode 100644 index 0000000..13ab340 --- /dev/null +++ b/scripts/generate_obsidian_factory_textures.py @@ -0,0 +1,584 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Obsidian Factory at 1024x1024. + +Concept: Heavy industrial obsidian production unit with dark purple +accent color. North face has control panel with gauges. South has an +output port. East/West are side panels with cooling vents. Top has a +mixing chamber viewport. Bottom has a heavy base plate. + +Active state adds purple glow effects throughout. + +Creates 12 textures: + 6 faces (north, south, east, west, up, down) × 2 states (idle, active) +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Obsidian / purple accent +OBSIDIAN_DARK = (26, 10, 46, 255) +OBSIDIAN_MID = (55, 20, 90, 255) +OBSIDIAN_BRIGHT = (75, 0, 130, 255) +OBSIDIAN_GLOW = (120, 40, 200, 255) +OBSIDIAN_ACCENT = (150, 60, 255, 255) + +# Gauge +GAUGE_BG = (18, 20, 26, 255) +GAUGE_RIM = (85, 90, 102, 255) +GAUGE_TICK = (110, 115, 128, 255) + +# Port / connector +HOLE_DARK = (12, 14, 18, 255) +PORT_RIM = (80, 85, 98, 255) + +# Vent +VENT_SLOT = (15, 18, 24, 255) + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +def make_hex_armor_base(): + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + return img + + +def add_corner_bolts(draw, inset=30): + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +def draw_small_gauge(draw, gx, gy, radius, active=False): + """Draw a small circular gauge.""" + draw_filled_circle(draw, gx, gy, radius + 3, GAUGE_RIM, + outline=EDGE_DARK) + draw_filled_circle(draw, gx, gy, radius, GAUGE_BG) + + for i in range(7): + angle = math.radians(225 - i * 30) + tx1 = int(gx + (radius - 3) * math.cos(angle)) + ty1 = int(gy - (radius - 3) * math.sin(angle)) + tx2 = int(gx + (radius - 7) * math.cos(angle)) + ty2 = int(gy - (radius - 7) * math.sin(angle)) + draw.line([(tx1, ty1), (tx2, ty2)], fill=GAUGE_TICK, width=1) + + if active: + needle_angle = math.radians(315) + needle_color = OBSIDIAN_ACCENT + else: + needle_angle = math.radians(225) + needle_color = GAUGE_TICK + + nx = int(gx + (radius - 8) * math.cos(needle_angle)) + ny = int(gy - (radius - 8) * math.sin(needle_angle)) + draw.line([(gx, gy), (nx, ny)], fill=needle_color, width=2) + draw_filled_circle(draw, gx, gy, 3, needle_color) + + +# --------------------------------------------------------------------------- +# North face — control panel with gauges +# --------------------------------------------------------------------------- + +def make_north(active=False): + """North face: control panel with gauges and status indicators.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + cx, cy = S // 2, S // 2 + + # Upper control panel + panel_x1 = int(S * 0.08) + panel_y1 = int(S * 0.10) + panel_x2 = S - int(S * 0.08) + panel_y2 = int(S * 0.55) + draw.rectangle([panel_x1, panel_y1, panel_x2, panel_y2], + fill=ARMOR_MID, outline=EDGE_DARK) + add_bevel_border(draw, panel_x1, panel_y1, panel_x2, panel_y2, + ARMOR_LIGHT, EDGE_DARK, width=3) + + # Three gauges across the panel + gauge_r = int(S * 0.07) + gauge_y = int(S * 0.30) + for i, gx in enumerate([int(S * 0.25), cx, int(S * 0.75)]): + draw_small_gauge(draw, gx, gauge_y, gauge_r, active=active) + + # Status indicator strip below gauges + strip_y1 = int(S * 0.42) + strip_y2 = int(S * 0.50) + draw.rectangle([panel_x1 + 20, strip_y1, panel_x2 - 20, strip_y2], + fill=EDGE_DARK, outline=ARMOR_MID) + + # Status LEDs + num_leds = 8 + led_spacing = (panel_x2 - panel_x1 - 60) // (num_leds + 1) + for i in range(num_leds): + lx = panel_x1 + 40 + (i + 1) * led_spacing + ly = (strip_y1 + strip_y2) // 2 + if active: + # Light up progressively in purple + led_color = OBSIDIAN_ACCENT if i < 6 else OBSIDIAN_MID + draw_filled_circle(draw, lx, ly, 8, led_color) + else: + draw_filled_circle(draw, lx, ly, 8, SEAM_COLOR) + + # Lower output slot + slot_x1 = int(S * 0.30) + slot_y1 = int(S * 0.62) + slot_x2 = int(S * 0.70) + slot_y2 = int(S * 0.78) + draw.rectangle([slot_x1, slot_y1, slot_x2, slot_y2], + fill=ARMOR_MID, outline=EDGE_DARK) + add_bevel_border(draw, slot_x1, slot_y1, slot_x2, slot_y2, + EDGE_DARK, ARMOR_LIGHT, width=3) + # Slot opening + slot_inner_margin = 15 + slot_color = OBSIDIAN_DARK if active else HOLE_DARK + draw.rectangle([slot_x1 + slot_inner_margin, slot_y1 + slot_inner_margin, + slot_x2 - slot_inner_margin, slot_y2 - slot_inner_margin], + fill=slot_color, outline=EDGE_DARK) + + if active: + # Purple glow from slot + add_radial_glow(img, (slot_x1 + slot_x2) // 2, + (slot_y1 + slot_y2) // 2, + int((slot_x2 - slot_x1) * 0.6), OBSIDIAN_GLOW, + intensity=0.2) + draw = ImageDraw.Draw(img) + + # Panel rivets + for rx in [panel_x1 + 15, panel_x2 - 15]: + for ry in [panel_y1 + 15, panel_y2 - 15]: + draw_filled_circle(draw, rx, ry, 8, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, rx, ry, 4, RIVET_COLOR) + + # Seams + m = 30 + if active: + add_glowing_seam(img, (m, int(S * 0.88)), (S - m, int(S * 0.88)), + SEAM_COLOR, OBSIDIAN_GLOW, + seam_width=2, glow_width=8, intensity=0.15) + draw = ImageDraw.Draw(img) + else: + draw.line([(m, int(S * 0.88)), (S - m, int(S * 0.88))], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# South face — output port +# --------------------------------------------------------------------------- + +def make_south(active=False): + """South face: obsidian output port.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + cx, cy = S // 2, S // 2 + + # Recessed panel + panel_r = int(S * 0.30) + draw.rectangle([cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID) + add_bevel_border(draw, cx - panel_r, cy - panel_r, + cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4) + + # Output port — square with beveled edges (obsidian shaped) + port_r = int(S * 0.16) + draw.rectangle([cx - port_r, cy - port_r, cx + port_r, cy + port_r], + fill=HOLE_DARK, outline=EDGE_DARK) + add_bevel_border(draw, cx - port_r, cy - port_r, + cx + port_r, cy + port_r, + ARMOR_MID, EDGE_DARK, width=5) + + # Inner obsidian color when active + if active: + inner_margin = 12 + draw.rectangle([cx - port_r + inner_margin, + cy - port_r + inner_margin, + cx + port_r - inner_margin, + cy + port_r - inner_margin], + fill=OBSIDIAN_DARK) + add_radial_glow(img, cx, cy, port_r, OBSIDIAN_GLOW, intensity=0.3) + add_glow_ring(img, cx, cy, port_r, port_r + int(port_r * 0.4), + OBSIDIAN_GLOW) + draw = ImageDraw.Draw(img) + + # Panel bolts + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Horizontal seams + m = 30 + if active: + add_glowing_seam(img, (m, cy), (cx - panel_r - 10, cy), + SEAM_COLOR, OBSIDIAN_GLOW, + seam_width=2, glow_width=8, intensity=0.15) + add_glowing_seam(img, (cx + panel_r + 10, cy), (S - m, cy), + SEAM_COLOR, OBSIDIAN_GLOW, + seam_width=2, glow_width=8, intensity=0.15) + draw = ImageDraw.Draw(img) + else: + draw.line([(m, cy), (cx - panel_r - 10, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(cx + panel_r + 10, cy), (S - m, cy)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# East/West — side panels with cooling vents +# --------------------------------------------------------------------------- + +def make_side(active=False): + """Side face (east/west): cooling vent panel.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + cx, cy = S // 2, S // 2 + + # Upper panel strip + panel_h = int(S * 0.08) + draw.rectangle([9, 9, S - 10, panel_h + 9], fill=ARMOR_MID) + add_bevel_border(draw, 9, 9, S - 10, panel_h + 9, ARMOR_LIGHT, + EDGE_DARK, width=2) + for rx in range(60, S - 40, int(S * 0.08)): + draw_filled_circle(draw, rx, 9 + panel_h // 2, 5, RIVET_COLOR) + + # Lower panel strip + base_top = S - panel_h - 9 + draw.rectangle([9, base_top, S - 10, S - 10], fill=ARMOR_MID) + add_bevel_border(draw, 9, base_top, S - 10, S - 10, ARMOR_LIGHT, + EDGE_DARK, width=2) + for rx in range(60, S - 40, int(S * 0.08)): + draw_filled_circle(draw, rx, base_top + panel_h // 2, 5, + RIVET_COLOR) + + # Central cooling vent section + vent_x1 = int(S * 0.12) + vent_y1 = panel_h + 9 + 20 + vent_x2 = S - int(S * 0.12) + vent_y2 = base_top - 20 + + draw.rectangle([vent_x1, vent_y1, vent_x2, vent_y2], + fill=ARMOR_MID, outline=EDGE_DARK) + add_bevel_border(draw, vent_x1, vent_y1, vent_x2, vent_y2, + ARMOR_LIGHT, EDGE_DARK, width=3) + + # Vent slots + num_slots = 10 + slot_margin = 20 + slot_spacing = (vent_y2 - vent_y1 - 2 * slot_margin) // num_slots + for i in range(num_slots): + sy = vent_y1 + slot_margin + i * slot_spacing + draw.rectangle([vent_x1 + slot_margin, sy, + vent_x2 - slot_margin, sy + slot_spacing - 8], + fill=VENT_SLOT, outline=EDGE_DARK) + if active: + # Purple glow through vents + draw.rectangle([vent_x1 + slot_margin + 4, sy + 2, + vent_x2 - slot_margin - 4, + sy + slot_spacing - 10], + fill=OBSIDIAN_DARK) + + # Circular side port + port_cx = cx + port_cy = cy + port_outer = int(S * 0.12) + port_inner = int(S * 0.07) + + draw_filled_circle(draw, port_cx, port_cy, port_outer, PORT_RIM, + outline=EDGE_DARK) + for r in range(port_outer - 2, port_inner, -1): + t = (port_outer - r) / (port_outer - port_inner) + c = lerp_color(PORT_RIM, ARMOR_MID, t * 0.6) + draw.ellipse([port_cx - r, port_cy - r, port_cx + r, port_cy + r], + outline=c) + + if active: + draw_filled_circle(draw, port_cx, port_cy, port_inner, + OBSIDIAN_DARK, outline=EDGE_DARK) + add_glow_ring(img, port_cx, port_cy, port_inner, + port_inner + int(port_inner * 0.5), OBSIDIAN_GLOW) + add_radial_glow(img, port_cx, port_cy, port_inner, + OBSIDIAN_ACCENT, intensity=0.25) + draw = ImageDraw.Draw(img) + else: + draw_filled_circle(draw, port_cx, port_cy, port_inner, + HOLE_DARK, outline=EDGE_DARK) + + # Port bolts + bolt_r = int((port_outer + port_inner) / 2) + 3 + for i in range(6): + angle = math.radians(60 * i) + bx = int(port_cx + bolt_r * math.cos(angle)) + by = int(port_cy + bolt_r * math.sin(angle)) + draw_filled_circle(draw, bx, by, 6, ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 3, RIVET_COLOR) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# Top face — mixing chamber viewport +# --------------------------------------------------------------------------- + +def make_top(active=False): + """Top face: mixing chamber with central viewport.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + cx, cy = S // 2, S // 2 + + # Cross seams + m = 30 + if active: + add_glowing_seam(img, (m, cy), (S - m, cy), SEAM_COLOR, + OBSIDIAN_GLOW, seam_width=3, glow_width=10, + intensity=0.15) + add_glowing_seam(img, (cx, m), (cx, S - m), SEAM_COLOR, + OBSIDIAN_GLOW, seam_width=3, glow_width=10, + intensity=0.15) + draw = ImageDraw.Draw(img) + else: + draw.line([(m, cy), (S - m, cy)], fill=SEAM_COLOR, width=3) + draw.line([(cx, m), (cx, S - m)], fill=SEAM_COLOR, width=3) + + # Central mixing chamber viewport + chamber_outer = int(S * 0.25) + chamber_inner = int(S * 0.18) + + # Outer housing ring + draw_filled_circle(draw, cx, cy, chamber_outer + 10, ARMOR_LIGHT, + outline=EDGE_DARK) + for r in range(chamber_outer + 8, chamber_outer - 2, -1): + t = (chamber_outer + 8 - r) / 10 + c = lerp_color(ARMOR_LIGHT, ARMOR_MID, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Gradient ring + for r in range(chamber_outer - 2, chamber_inner, -1): + t = (chamber_outer - r) / (chamber_outer - chamber_inner) + if active: + c = lerp_color(ARMOR_MID, OBSIDIAN_DARK, t * 0.5) + else: + c = lerp_color(ARMOR_MID, EDGE_DARK, t * 0.4) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Chamber interior + if active: + # Swirling obsidian/lava mixture + draw_filled_circle(draw, cx, cy, chamber_inner, OBSIDIAN_DARK) + for r in range(chamber_inner, 0, -1): + t = 1.0 - (r / chamber_inner) + c = lerp_color(OBSIDIAN_DARK, OBSIDIAN_MID, t ** 0.6) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + # Bright core + core_r = int(chamber_inner * 0.3) + for r in range(core_r, 0, -1): + t = 1.0 - (r / core_r) + c = lerp_color(OBSIDIAN_MID, OBSIDIAN_BRIGHT, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + add_glow_ring(img, cx, cy, chamber_inner, + chamber_inner + int(chamber_inner * 0.5), + OBSIDIAN_GLOW) + add_radial_glow(img, cx, cy, chamber_outer + 20, + OBSIDIAN_GLOW, intensity=0.2) + draw = ImageDraw.Draw(img) + else: + draw_filled_circle(draw, cx, cy, chamber_inner, EDGE_DARK, + outline=ARMOR_MID) + + # Chamber bolts + bolt_r = int((chamber_outer + chamber_inner) / 2) + 5 + for i in range(8): + angle = math.radians(45 * i + 22.5) + bx = int(cx + bolt_r * math.cos(angle)) + by = int(cy + bolt_r * math.sin(angle)) + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Corner quadrant bolts + for bx, by in [(int(S * 0.12), int(S * 0.12)), + (int(S * 0.88), int(S * 0.12)), + (int(S * 0.12), int(S * 0.88)), + (int(S * 0.88), int(S * 0.88))]: + draw_filled_circle(draw, bx, by, 12, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 6, RIVET_COLOR) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# Bottom face — heavy base plate +# --------------------------------------------------------------------------- + +def make_bottom(active=False): + """Bottom face: heavy base plate with ventilation.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + cx, cy = S // 2, S // 2 + + plate_margin = int(S * 0.08) + draw.rectangle([plate_margin, plate_margin, + S - plate_margin, S - plate_margin], + fill=ARMOR_MID, outline=EDGE_DARK) + add_bevel_border(draw, plate_margin, plate_margin, + S - plate_margin, S - plate_margin, + ARMOR_LIGHT, EDGE_DARK, width=3) + + # Central vent grille + grille_margin = int(S * 0.2) + slot_y_start = grille_margin + 20 + slot_y_end = S - grille_margin - 20 + num_slots = 12 + slot_spacing = (slot_y_end - slot_y_start) // num_slots + + for i in range(num_slots): + sy = slot_y_start + i * slot_spacing + draw.rectangle([grille_margin + 10, sy, + S - grille_margin - 10, sy + slot_spacing - 8], + fill=VENT_SLOT, outline=EDGE_DARK) + if active: + draw.rectangle([grille_margin + 14, sy + 2, + S - grille_margin - 14, + sy + slot_spacing - 10], + fill=OBSIDIAN_DARK) + + # Mounting feet + foot_size = int(S * 0.08) + foot_positions = [ + (plate_margin + 10, plate_margin + 10), + (S - plate_margin - foot_size - 10, plate_margin + 10), + (plate_margin + 10, S - plate_margin - foot_size - 10), + (S - plate_margin - foot_size - 10, + S - plate_margin - foot_size - 10), + ] + for fx, fy in foot_positions: + draw.rectangle([fx, fy, fx + foot_size, fy + foot_size], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + add_bevel_border(draw, fx, fy, fx + foot_size, fy + foot_size, + RIVET_COLOR, EDGE_DARK, width=2) + foot_cx = fx + foot_size // 2 + foot_cy = fy + foot_size // 2 + draw_filled_circle(draw, foot_cx, foot_cy, 12, ARMOR_MID, + outline=EDGE_DARK) + draw_filled_circle(draw, foot_cx, foot_cy, 6, RIVET_COLOR) + + if active: + add_radial_glow(img, cx, cy, int(S * 0.25), OBSIDIAN_GLOW, + intensity=0.1) + draw = ImageDraw.Draw(img) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + textures = {} + + for active_state, prefix in [(False, "idle"), (True, "active")]: + textures[f"obsidian_factory_{prefix}_north"] = make_north(active_state) + textures[f"obsidian_factory_{prefix}_south"] = make_south(active_state) + textures[f"obsidian_factory_{prefix}_east"] = make_side(active_state) + textures[f"obsidian_factory_{prefix}_west"] = make_side(active_state) + textures[f"obsidian_factory_{prefix}_up"] = make_top(active_state) + textures[f"obsidian_factory_{prefix}_down"] = make_bottom(active_state) + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_power_cable_textures.py b/scripts/generate_power_cable_textures.py index 0489f97..d82dc1e 100644 --- a/scripts/generate_power_cable_textures.py +++ b/scripts/generate_power_cable_textures.py @@ -1,146 +1,435 @@ #!/usr/bin/env python3 -"""Generate directional arrow textures for power cables. +"""Generate sci-fi hex-armor textures for Power Cable at 1024x1024. -Creates 12 textures (16x16 pixel art): -- power_cable_front.png: Gold connector circle on dark gray -- power_cable_back.png: Darker inset square on dark gray -- power_cable_side_{up,down,left,right}.png: Gold arrow pointing in direction -- power_cable_cap_{up,down,left,right}.png: Gold arrow for top/bottom faces +Concept: Dark armored housing with honeycomb grid on all faces. +Directional gold arrows indicate flow direction. Powered variant +adds glowing amber/orange energy effects to arrows and seams. + +Creates 20 textures: + Front (2): power_cable_front, _front_powered + Back (2): power_cable_back, _back_powered + Side (8): power_cable_side_{up,down,left,right} + _powered + Cap (8): power_cable_cap_{up,down,left,right} + _powered """ -import os +import math from PIL import Image, ImageDraw -OUTPUT_DIR = os.path.join( - os.path.dirname(__file__), "..", - "src", "main", "resources", "atlas", "resourcepack", - "assets", "minecraft", "textures", "block", "custom" +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + draw_hex_grid_lines_only, add_radial_glow, add_glow_ring, + add_glowing_seam, save_textures, ) -# Colors -DARK_GRAY = (58, 58, 58, 255) # #3a3a3a - base -DARKER_GRAY = (40, 40, 40, 255) # #282828 - back inset -GOLD = (255, 215, 0, 255) # #FFD700 - arrows/connector -GOLD_DIM = (200, 168, 0, 255) # dimmer gold for border accents -OUTLINE = (30, 30, 30, 255) # near-black outline +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor (consistent with other Atlas blocks) +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) +FRAME_DARK = (28, 30, 38, 255) +FRAME_MID = (58, 62, 74, 255) +FRAME_LIGHT = (80, 86, 100, 255) + +# Energy colors — gold/amber for cable direction arrows +GOLD = (210, 175, 40, 255) +GOLD_DIM = (160, 130, 30, 255) +GOLD_BRIGHT = (255, 220, 60, 255) +AMBER_GLOW = (255, 180, 40, 255) +ORANGE_GLOW = (255, 140, 30, 255) + +# Connector port colors +CONNECTOR_RING = (180, 150, 50, 255) +CONNECTOR_HOLE = (12, 14, 18, 255) + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + """Return 6 vertices for a flat-top hexagon.""" + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + """Compute hex cell center positions covering the image.""" + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + """Draw a filled circle.""" + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + """Draw a beveled rectangle border (raised appearance).""" + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + """Create a hex armor face — the foundation for all cable faces.""" + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + + # Outer border + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + + # Hex cells + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + return img + + +def add_corner_bolts(draw, inset=30): + """Add bolts at the four corners of a face.""" + positions = [ + (inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset), + ] + for bx, by in positions: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) -def new_img(): - return Image.new("RGBA", (16, 16), DARK_GRAY) +# --------------------------------------------------------------------------- +# Arrow drawing — chevron + shaft pointing UP at 1024 scale +# --------------------------------------------------------------------------- -def make_front(): - """Front face: gold connector circle centered.""" - img = new_img() +def draw_arrow_up(img, color, shaft_color=None, thick=False): + """Draw a directional arrow/chevron pointing UP, centered. + + The arrow has a V-shaped chevron head and a straight shaft below. + If thick=True, uses wider strokes (for powered glow underlay). + """ + draw = ImageDraw.Draw(img) + cx = S // 2 + if shaft_color is None: + shaft_color = color + + # Arrow geometry + tip_y = int(S * 0.18) + wing_y = int(S * 0.40) + shaft_top = wing_y - int(S * 0.04) + shaft_bottom = int(S * 0.82) + wing_half = int(S * 0.28) + shaft_half = int(S * 0.045) + stroke = 28 if thick else 18 + + # Chevron head — two angled lines forming a V pointing up + # Left wing + draw.line( + [(cx, tip_y), (cx - wing_half, wing_y)], + fill=color, width=stroke + ) + # Right wing + draw.line( + [(cx, tip_y), (cx + wing_half, wing_y)], + fill=color, width=stroke + ) + + # Shaft + draw.rectangle( + [cx - shaft_half, shaft_top, cx + shaft_half, shaft_bottom], + fill=shaft_color + ) + + +def draw_arrow_glow(img, glow_color, intensity=0.25): + """Add a soft glow along the arrow path (for powered state).""" + cx = S // 2 + # Glow along the shaft + add_radial_glow( + img, cx, int(S * 0.50), int(S * 0.18), + glow_color, intensity=intensity + ) + # Glow at the chevron tip + add_radial_glow( + img, cx, int(S * 0.25), int(S * 0.22), + glow_color, intensity=intensity * 0.8 + ) + + +# --------------------------------------------------------------------------- +# FRONT FACE — connector port +# --------------------------------------------------------------------------- + +def make_front(powered=False): + """Front face: hex armor with a central circular connector port.""" + img = make_hex_armor_base() draw = ImageDraw.Draw(img) - # Outer circle (gold ring) - draw.ellipse([5, 5, 10, 10], fill=GOLD, outline=OUTLINE) - # Inner dark center (connector hole) - draw.rectangle([7, 7, 8, 8], fill=DARKER_GRAY) + + cx, cy = S // 2, S // 2 + port_outer = int(S * 0.22) + port_inner = int(S * 0.12) + + # Recessed panel behind the port + panel_r = int(S * 0.30) + draw.rectangle( + [cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Connector ring + ring_color = CONNECTOR_RING if not powered else GOLD_BRIGHT + for r in range(port_outer, port_inner, -1): + t = (port_outer - r) / (port_outer - port_inner) + c = lerp_color(ring_color, ARMOR_MID, t * 0.6) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Connector hole + draw_filled_circle(draw, cx, cy, port_inner, CONNECTOR_HOLE, + outline=EDGE_DARK) + + # Contact pins inside the hole (small bright dots) + pin_r = int(S * 0.06) + pin_size = 6 + pin_color = GOLD_DIM if not powered else GOLD_BRIGHT + for angle_deg in [0, 90, 180, 270]: + angle = math.radians(angle_deg) + px = int(cx + pin_r * math.cos(angle)) + py = int(cy + pin_r * math.sin(angle)) + draw_filled_circle(draw, px, py, pin_size, pin_color) + + # Bolts around the port panel + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + add_corner_bolts(draw) + + # Powered: glow around connector + if powered: + add_glow_ring(img, cx, cy, port_outer, port_outer + int(S * 0.08), + AMBER_GLOW) + add_radial_glow(img, cx, cy, port_inner, GOLD_BRIGHT, + intensity=0.3) + return img -def make_back(): - """Back face: darker inset square.""" - img = new_img() +# --------------------------------------------------------------------------- +# BACK FACE — recessed output panel +# --------------------------------------------------------------------------- + +def make_back(powered=False): + """Back face: hex armor with a dark recessed square.""" + img = make_hex_armor_base() draw = ImageDraw.Draw(img) - # Inset square - draw.rectangle([5, 5, 10, 10], fill=DARKER_GRAY, outline=OUTLINE) + + cx, cy = S // 2, S // 2 + recess_r = int(S * 0.18) + + # Recessed square + draw.rectangle( + [cx - recess_r, cy - recess_r, cx + recess_r, cy + recess_r], + fill=(18, 20, 26, 255) + ) + add_bevel_border( + draw, + cx - recess_r, cy - recess_r, cx + recess_r, cy + recess_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Inner grate lines + for offset in range(-recess_r + 20, recess_r, 24): + draw.line( + [(cx - recess_r + 10, cy + offset), + (cx + recess_r - 10, cy + offset)], + fill=(28, 30, 36, 255), width=2 + ) + + add_corner_bolts(draw) + + # Horizontal seam + draw.line([(12, S // 2), (cx - recess_r - 10, S // 2)], + fill=SEAM_COLOR, width=2) + draw.line([(cx + recess_r + 10, S // 2), (S - 12, S // 2)], + fill=SEAM_COLOR, width=2) + + if powered: + add_radial_glow(img, cx, cy, recess_r + int(S * 0.06), + AMBER_GLOW, intensity=0.15) + return img -def make_side_up(): - """Side face with gold arrow pointing UP and a gold border strip at top edge.""" - img = new_img() +# --------------------------------------------------------------------------- +# SIDE FACE — hex armor with directional arrow +# --------------------------------------------------------------------------- + +def make_side_up(powered=False): + """Side face with hex armor and a gold arrow pointing UP. + + The arrow indicates energy flow direction. A gold border strip + at the top edge marks the side facing the front connector. + """ + img = make_hex_armor_base() draw = ImageDraw.Draw(img) - # Gold border strip at top (the edge toward the front face) - draw.rectangle([0, 0, 15, 1], fill=GOLD_DIM) - - # Arrow/chevron pointing up, centered horizontally - # Tip of arrow at y=3 - # Row by row (symmetric chevron) - arrow_pixels = [ - (7, 3), (8, 3), # tip - (6, 4), (7, 4), (8, 4), (9, 4), # row 2 - (5, 5), (6, 5), (9, 5), (10, 5), # row 3 (hollow) - (4, 6), (5, 6), (10, 6), (11, 6), # row 4 (hollow) - # Shaft below chevron - (7, 7), (8, 7), - (7, 8), (8, 8), - (7, 9), (8, 9), - (7, 10), (8, 10), - (7, 11), (8, 11), - (7, 12), (8, 12), - ] - for x, y in arrow_pixels: - img.putpixel((x, y), GOLD) + # Gold border strip at top edge (toward front face) + strip_h = 14 + strip_color = GOLD_DIM if not powered else GOLD_BRIGHT + draw.rectangle([10, 10, S - 10, 10 + strip_h], fill=strip_color) + + # Arrow + if powered: + draw_arrow_up(img, GOLD_BRIGHT, GOLD_BRIGHT, thick=True) + draw_arrow_glow(img, AMBER_GLOW, intensity=0.3) + else: + draw_arrow_up(img, GOLD, GOLD_DIM) + + add_corner_bolts(draw, inset=30) + + # Horizontal seam below the arrow shaft + seam_y = int(S * 0.90) + if powered: + add_glowing_seam( + img, (30, seam_y), (S - 30, seam_y), + SEAM_COLOR, AMBER_GLOW, + seam_width=2, glow_width=8, intensity=0.2 + ) + else: + draw.line([(30, seam_y), (S - 30, seam_y)], + fill=SEAM_COLOR, width=2) return img -def make_cap_up(): - """Cap face (top/bottom) with gold arrow pointing UP. Slightly different border style.""" - img = new_img() +# --------------------------------------------------------------------------- +# CAP FACE — hex armor with directional arrow (no border strip) +# --------------------------------------------------------------------------- + +def make_cap_up(powered=False): + """Cap face (top/bottom of cable) with arrow pointing UP. + + Similar to side but with corner accent marks instead of a + full border strip. + """ + img = make_hex_armor_base() draw = ImageDraw.Draw(img) - # Subtle corner accents instead of full border strip - draw.rectangle([0, 0, 2, 0], fill=GOLD_DIM) - draw.rectangle([13, 0, 15, 0], fill=GOLD_DIM) - - # Arrow/chevron pointing up - same design as side but slightly smaller shaft - arrow_pixels = [ - (7, 3), (8, 3), # tip - (6, 4), (7, 4), (8, 4), (9, 4), # row 2 - (5, 5), (6, 5), (9, 5), (10, 5), # row 3 (hollow) - (4, 6), (5, 6), (10, 6), (11, 6), # row 4 (hollow) - # Shaft - (7, 7), (8, 7), - (7, 8), (8, 8), - (7, 9), (8, 9), - (7, 10), (8, 10), - (7, 11), (8, 11), - ] - for x, y in arrow_pixels: - img.putpixel((x, y), GOLD) + # Corner accent marks + accent_color = GOLD_DIM if not powered else GOLD_BRIGHT + accent_len = int(S * 0.08) + m = 12 # margin from edge + draw.rectangle([m, m, m + accent_len, m + 6], fill=accent_color) + draw.rectangle([S - m - accent_len, m, S - m, m + 6], + fill=accent_color) + + # Arrow + if powered: + draw_arrow_up(img, GOLD_BRIGHT, GOLD_BRIGHT, thick=True) + draw_arrow_glow(img, AMBER_GLOW, intensity=0.25) + else: + draw_arrow_up(img, GOLD, GOLD_DIM) + + add_corner_bolts(draw, inset=30) return img -def rotate_variants(base_img, prefix): - """Generate up/down/left/right variants by rotating the base (which points up).""" +# --------------------------------------------------------------------------- +# Rotation helpers +# --------------------------------------------------------------------------- + +def rotate_variants(make_fn, prefix, powered=False): + """Generate up/down/left/right variants by rotating the UP base.""" + base = make_fn(powered=powered) + suffix = "_powered" if powered else "" imgs = {} - imgs[f"{prefix}_up"] = base_img.copy() - imgs[f"{prefix}_down"] = base_img.rotate(180) - imgs[f"{prefix}_left"] = base_img.rotate(90) # 90 CCW - imgs[f"{prefix}_right"] = base_img.rotate(-90) # 90 CW (= 270 CCW) + imgs[f"{prefix}_up{suffix}"] = base.copy() + imgs[f"{prefix}_down{suffix}"] = base.rotate(180) + imgs[f"{prefix}_left{suffix}"] = base.rotate(90) + imgs[f"{prefix}_right{suffix}"] = base.rotate(-90) return imgs -def main(): - os.makedirs(OUTPUT_DIR, exist_ok=True) +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- +def main(): textures = {} - # Front and back - textures["power_cable_front"] = make_front() - textures["power_cable_back"] = make_back() - - # Side variants (rotated from base pointing up) - side_base = make_side_up() - textures.update(rotate_variants(side_base, "power_cable_side")) - - # Cap variants (rotated from base pointing up) - cap_base = make_cap_up() - textures.update(rotate_variants(cap_base, "power_cable_cap")) - - # Save all - for name, img in textures.items(): - path = os.path.join(OUTPUT_DIR, f"{name}.png") - img.save(path) - print(f" Created {name}.png") - - print(f"\nGenerated {len(textures)} textures in {OUTPUT_DIR}") + # Front and back (unpowered + powered) + textures["power_cable_front"] = make_front(powered=False) + textures["power_cable_front_powered"] = make_front(powered=True) + textures["power_cable_back"] = make_back(powered=False) + textures["power_cable_back_powered"] = make_back(powered=True) + + # Side variants (unpowered + powered) + textures.update(rotate_variants( + make_side_up, "power_cable_side", powered=False + )) + textures.update(rotate_variants( + make_side_up, "power_cable_side", powered=True + )) + + # Cap variants (unpowered + powered) + textures.update(rotate_variants( + make_cap_up, "power_cable_cap", powered=False + )) + textures.update(rotate_variants( + make_cap_up, "power_cable_cap", powered=True + )) + + save_textures(textures) if __name__ == "__main__": diff --git a/scripts/generate_power_merger_textures.py b/scripts/generate_power_merger_textures.py new file mode 100644 index 0000000..7b791ae --- /dev/null +++ b/scripts/generate_power_merger_textures.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Power Merger at 1024x1024. + +Concept: Dark armored housing with honeycomb grid. Each face has a +single centered connector circle. Color coding matches the power +splitter: red = input, yellow = output, gold = neutral housing. +The merger is the inverse of the splitter (many inputs -> one output). + +Creates 10 textures: + 5 faces (front, back, side, top, bottom) x 2 states (off, powered) +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor (consistent with other Atlas blocks) +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) +FRAME_DARK = (28, 30, 38, 255) + +# Connector hole +CONNECTOR_HOLE = (12, 14, 18, 255) + +# Front (output) — yellow +FRONT_RING = (200, 180, 40, 255) +FRONT_BRIGHT = (255, 240, 60, 255) +FRONT_GLOW = (255, 230, 50, 255) + +# Back (input) — red +BACK_RING = (200, 50, 40, 255) +BACK_BRIGHT = (255, 70, 50, 255) +BACK_GLOW = (255, 60, 40, 255) + +# Side/top/bottom — gold +NEUTRAL_RING = (200, 170, 50, 255) +NEUTRAL_BRIGHT = (255, 220, 60, 255) +NEUTRAL_GLOW = (255, 200, 50, 255) + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + """Return 6 vertices for a flat-top hexagon.""" + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + """Compute hex cell center positions covering the image.""" + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + """Draw a filled circle.""" + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + """Draw a beveled rectangle border (raised appearance).""" + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + """Create a hex armor face — the foundation for all merger faces.""" + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + + return img + + +def add_corner_bolts(draw, inset=30): + """Add bolts at the four corners of a face.""" + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +def draw_connector_port(img, draw, cx, cy, outer_r, inner_r, + ring_color, bright_color, glow_color, + powered=False): + """Draw a color-coded circular connector port with contact pins.""" + active_ring = ring_color if not powered else bright_color + + for r in range(outer_r, inner_r, -1): + t = (outer_r - r) / (outer_r - inner_r) + c = lerp_color(active_ring, ARMOR_MID, t * 0.6) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + draw_filled_circle(draw, cx, cy, inner_r, CONNECTOR_HOLE, + outline=EDGE_DARK) + + pin_r = int(inner_r * 0.5) + pin_size = max(4, inner_r // 12) + pin_color = ring_color if not powered else bright_color + for angle_deg in [0, 90, 180, 270]: + angle = math.radians(angle_deg) + px = int(cx + pin_r * math.cos(angle)) + py = int(cy + pin_r * math.sin(angle)) + draw_filled_circle(draw, px, py, pin_size, pin_color) + + if powered: + add_glow_ring(img, cx, cy, outer_r, + outer_r + int(outer_r * 0.35), glow_color) + add_radial_glow(img, cx, cy, inner_r, bright_color, + intensity=0.3) + + +# --------------------------------------------------------------------------- +# Face builder +# --------------------------------------------------------------------------- + +def make_face(ring, bright, glow, powered=False, + seam_style="horizontal"): + """Build a face with a single centered connector in a recessed panel.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + port_outer = int(S * 0.22) + port_inner = int(S * 0.12) + + # Recessed panel + panel_r = int(S * 0.30) + draw.rectangle( + [cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Connector port + draw_connector_port(img, draw, cx, cy, port_outer, port_inner, + ring, bright, glow, powered=powered) + + # Panel bolts + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Seams + m = 30 + if seam_style in ("horizontal", "cross"): + if powered: + add_glowing_seam( + img, (m, cy), (cx - panel_r - 10, cy), + SEAM_COLOR, glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (cx + panel_r + 10, cy), (S - m, cy), + SEAM_COLOR, glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + else: + draw.line([(m, cy), (cx - panel_r - 10, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(cx + panel_r + 10, cy), (S - m, cy)], + fill=SEAM_COLOR, width=2) + + if seam_style == "cross": + if powered: + add_glowing_seam( + img, (cx, m), (cx, cy - panel_r - 10), + SEAM_COLOR, glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (cx, cy + panel_r + 10), (cx, S - m), + SEAM_COLOR, glow, + seam_width=2, glow_width=8, intensity=0.15 + ) + else: + draw.line([(cx, m), (cx, cy - panel_r - 10)], + fill=SEAM_COLOR, width=2) + draw.line([(cx, cy + panel_r + 10), (cx, S - m)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + textures = {} + + for powered in (False, True): + sfx = "_powered" if powered else "" + + # Front (output) — yellow + textures[f"power_merger_front{sfx}"] = make_face( + FRONT_RING, FRONT_BRIGHT, FRONT_GLOW, + powered=powered, seam_style="none" + ) + # Back (input) — red + textures[f"power_merger_back{sfx}"] = make_face( + BACK_RING, BACK_BRIGHT, BACK_GLOW, + powered=powered, seam_style="none" + ) + # Side (input) — red with horizontal seams + textures[f"power_merger_side{sfx}"] = make_face( + BACK_RING, BACK_BRIGHT, BACK_GLOW, + powered=powered, seam_style="horizontal" + ) + # Top (input) — red with cross seams + textures[f"power_merger_top{sfx}"] = make_face( + BACK_RING, BACK_BRIGHT, BACK_GLOW, + powered=powered, seam_style="cross" + ) + # Bottom (input) — red with cross seams + textures[f"power_merger_bottom{sfx}"] = make_face( + BACK_RING, BACK_BRIGHT, BACK_GLOW, + powered=powered, seam_style="cross" + ) + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_power_splitter_textures.py b/scripts/generate_power_splitter_textures.py new file mode 100644 index 0000000..78d15f0 --- /dev/null +++ b/scripts/generate_power_splitter_textures.py @@ -0,0 +1,441 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Power Splitter at 1024x1024. + +Concept: Dark armored housing with honeycomb grid. The splitter's +function is communicated through color-coded connector ports: + - Red = input (one large port on the front) + - Yellow = output (two ports on the back) +Side and cap faces show one red circle on the left (input) splitting +into two yellow circles on the right (output) connected by lines. +Powered variants add glow effects in matching colors. + +Creates 8 textures: + Front (2): power_splitter_front, _front_powered + Back (2): power_splitter_back, _back_powered + Side (2): power_splitter_side, _side_powered + Cap (2): power_splitter_cap, _cap_powered +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor (consistent with other Atlas blocks) +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) +FRAME_DARK = (28, 30, 38, 255) +FRAME_MID = (58, 62, 74, 255) +FRAME_LIGHT = (80, 86, 100, 255) + +# Input colors — red +RED = (200, 50, 40, 255) +RED_RING = (180, 45, 35, 255) +RED_BRIGHT = (255, 70, 50, 255) +RED_GLOW = (255, 60, 40, 255) + +# Output colors — yellow +YELLOW = (220, 200, 40, 255) +YELLOW_RING = (200, 180, 40, 255) +YELLOW_BRIGHT = (255, 240, 60, 255) +YELLOW_GLOW = (255, 230, 50, 255) + +# Connector +CONNECTOR_HOLE = (12, 14, 18, 255) + +# Side/cap neutral color — gold/amber +GOLD_RING = (200, 170, 50, 255) +GOLD_BRIGHT = (255, 220, 60, 255) +GOLD_GLOW = (255, 200, 50, 255) + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + """Return 6 vertices for a flat-top hexagon.""" + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + """Compute hex cell center positions covering the image.""" + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + """Draw a filled circle.""" + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def draw_thick_circle(draw, cx, cy, radius, color, width): + """Draw a circle outline with a given line width.""" + for w in range(width): + r = radius - w + if r > 0: + draw.ellipse([cx - r, cy - r, cx + r, cy + r], + outline=color) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + """Draw a beveled rectangle border (raised appearance).""" + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + """Create a hex armor face — the foundation for all splitter faces.""" + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + + return img + + +def add_corner_bolts(draw, inset=30): + """Add bolts at the four corners of a face.""" + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +def draw_connector_port(img, draw, cx, cy, outer_r, inner_r, + ring_color, bright_color, glow_color, + powered=False): + """Draw a color-coded circular connector port with contact pins.""" + active_ring = ring_color if not powered else bright_color + + # Connector ring gradient + for r in range(outer_r, inner_r, -1): + t = (outer_r - r) / (outer_r - inner_r) + c = lerp_color(active_ring, ARMOR_MID, t * 0.6) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Connector hole + draw_filled_circle(draw, cx, cy, inner_r, CONNECTOR_HOLE, + outline=EDGE_DARK) + + # Contact pins + pin_r = int(inner_r * 0.5) + pin_size = max(4, inner_r // 12) + pin_color = ring_color if not powered else bright_color + for angle_deg in [0, 90, 180, 270]: + angle = math.radians(angle_deg) + px = int(cx + pin_r * math.cos(angle)) + py = int(cy + pin_r * math.sin(angle)) + draw_filled_circle(draw, px, py, pin_size, pin_color) + + if powered: + add_glow_ring(img, cx, cy, outer_r, + outer_r + int(outer_r * 0.35), glow_color) + add_radial_glow(img, cx, cy, inner_r, bright_color, + intensity=0.3) + + +# --------------------------------------------------------------------------- +# FRONT FACE — single red input connector +# --------------------------------------------------------------------------- + +def make_front(powered=False): + """Front face: single large red input connector port.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + port_outer = int(S * 0.22) + port_inner = int(S * 0.12) + + # Recessed panel + panel_r = int(S * 0.30) + draw.rectangle( + [cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Red input connector + draw_connector_port(img, draw, cx, cy, port_outer, port_inner, + RED_RING, RED_BRIGHT, RED_GLOW, + powered=powered) + + # Panel bolts + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# BACK FACE — single yellow output connector +# --------------------------------------------------------------------------- + +def make_back(powered=False): + """Back face: single large yellow output connector port.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + port_outer = int(S * 0.22) + port_inner = int(S * 0.12) + + # Recessed panel + panel_r = int(S * 0.30) + draw.rectangle( + [cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Yellow output connector + draw_connector_port(img, draw, cx, cy, port_outer, port_inner, + YELLOW_RING, YELLOW_BRIGHT, YELLOW_GLOW, + powered=powered) + + # Panel bolts + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# SIDE FACE — single gold connector circle +# --------------------------------------------------------------------------- + +def make_side(powered=False): + """Side face: hex armor with a single centered gold connector.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + port_outer = int(S * 0.18) + port_inner = int(S * 0.10) + + # Recessed panel + panel_r = int(S * 0.26) + draw.rectangle( + [cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Gold connector + draw_connector_port(img, draw, cx, cy, port_outer, port_inner, + GOLD_RING, GOLD_BRIGHT, GOLD_GLOW, + powered=powered) + + # Panel bolts + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Horizontal seam + seam_margin = 30 + if powered: + add_glowing_seam( + img, (seam_margin, cy), (cx - panel_r - 10, cy), + SEAM_COLOR, GOLD_GLOW, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (cx + panel_r + 10, cy), (S - seam_margin, cy), + SEAM_COLOR, GOLD_GLOW, + seam_width=2, glow_width=8, intensity=0.15 + ) + else: + draw.line([(seam_margin, cy), (cx - panel_r - 10, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(cx + panel_r + 10, cy), (S - seam_margin, cy)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# CAP FACE — single gold connector circle +# --------------------------------------------------------------------------- + +def make_cap(powered=False): + """Cap face (top/bottom): hex armor with a single centered gold + connector, matching the side face style. + """ + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + port_outer = int(S * 0.18) + port_inner = int(S * 0.10) + + # Recessed panel + panel_r = int(S * 0.26) + draw.rectangle( + [cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Gold connector + draw_connector_port(img, draw, cx, cy, port_outer, port_inner, + GOLD_RING, GOLD_BRIGHT, GOLD_GLOW, + powered=powered) + + # Panel bolts + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Cross seams + seam_margin = 30 + if powered: + add_glowing_seam( + img, (seam_margin, cy), (cx - panel_r - 10, cy), + SEAM_COLOR, GOLD_GLOW, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (cx + panel_r + 10, cy), (S - seam_margin, cy), + SEAM_COLOR, GOLD_GLOW, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (cx, seam_margin), (cx, cy - panel_r - 10), + SEAM_COLOR, GOLD_GLOW, + seam_width=2, glow_width=8, intensity=0.15 + ) + add_glowing_seam( + img, (cx, cy + panel_r + 10), (cx, S - seam_margin), + SEAM_COLOR, GOLD_GLOW, + seam_width=2, glow_width=8, intensity=0.15 + ) + else: + draw.line([(seam_margin, cy), (cx - panel_r - 10, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(cx + panel_r + 10, cy), (S - seam_margin, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(cx, seam_margin), (cx, cy - panel_r - 10)], + fill=SEAM_COLOR, width=2) + draw.line([(cx, cy + panel_r + 10), (cx, S - seam_margin)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + textures = {} + + textures["power_splitter_front"] = make_front(powered=False) + textures["power_splitter_front_powered"] = make_front(powered=True) + textures["power_splitter_back"] = make_back(powered=False) + textures["power_splitter_back_powered"] = make_back(powered=True) + textures["power_splitter_side"] = make_side(powered=False) + textures["power_splitter_side_powered"] = make_side(powered=True) + textures["power_splitter_cap"] = make_cap(powered=False) + textures["power_splitter_cap_powered"] = make_cap(powered=True) + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_small_drill_textures.py b/scripts/generate_small_drill_textures.py new file mode 100644 index 0000000..ae179f5 --- /dev/null +++ b/scripts/generate_small_drill_textures.py @@ -0,0 +1,412 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Small Drill at 1024x1024. + +Concept: Compact industrial drilling unit. Front face shows a large drill bit +with concentric cutting rings. Back face is a motor housing with exhaust port. +Side faces show directional flow arrows indicating drill direction, with +industrial panel details. + +Creates 6 textures: + small_drill_front, small_drill, small_drill_arrow_up, + small_drill_arrow_down, small_drill_arrow_left, small_drill_arrow_right +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) + +# Drill / mechanical colors +DRILL_STEEL = (160, 165, 175, 255) +DRILL_STEEL_DARK = (110, 115, 125, 255) +DRILL_STEEL_LIGHT = (190, 195, 205, 255) +DRILL_TIP = (200, 190, 140, 255) # carbide/gold tip +DRILL_TIP_BRIGHT = (230, 215, 160, 255) +DRILL_ACCENT = (200, 170, 50, 255) # gold/yellow accent +DRILL_GLOW = (220, 200, 80, 255) + +# Motor / power +MOTOR_DARK = (30, 32, 38, 255) +MOTOR_COIL = (80, 60, 40, 255) + +# Exhaust / vent +VENT_SLOT = (15, 18, 24, 255) + +# Arrow color +ARROW_COLOR = (200, 170, 50, 255) +ARROW_OUTLINE = (140, 120, 40, 255) + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +# --------------------------------------------------------------------------- +# Hex armor base +# --------------------------------------------------------------------------- + +def make_hex_armor_base(): + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + return img + + +def add_corner_bolts(draw, inset=30): + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +# --------------------------------------------------------------------------- +# Front face — drill bit +# --------------------------------------------------------------------------- + +def make_front(): + """Front face: concentric drill bit with cutting edges and carbide tip.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Outer drill housing ring + housing_r = int(S * 0.42) + draw_filled_circle(draw, cx, cy, housing_r, ARMOR_MID, outline=EDGE_DARK) + add_bevel_border(draw, cx - housing_r, cy - housing_r, + cx + housing_r, cy + housing_r, ARMOR_LIGHT, EDGE_DARK, + width=3) + + # Housing bolts around perimeter + for i in range(12): + angle = math.radians(30 * i) + bx = int(cx + (housing_r - 18) * math.cos(angle)) + by = int(cy + (housing_r - 18) * math.sin(angle)) + draw_filled_circle(draw, bx, by, 8, ARMOR_LIGHT, outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 4, RIVET_COLOR) + + # Outer cutting ring + cut_outer = int(S * 0.35) + cut_inner = int(S * 0.28) + draw_filled_circle(draw, cx, cy, cut_outer, DRILL_STEEL_DARK, + outline=EDGE_DARK) + # Gradient ring + for r in range(cut_outer, cut_inner, -1): + t = (cut_outer - r) / (cut_outer - cut_inner) + c = lerp_color(DRILL_STEEL_DARK, DRILL_STEEL, t) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Cutting teeth on outer ring (radial lines) + num_teeth = 16 + for i in range(num_teeth): + angle = math.radians(360 / num_teeth * i) + x1 = int(cx + cut_inner * math.cos(angle)) + y1 = int(cy + cut_inner * math.sin(angle)) + x2 = int(cx + cut_outer * math.cos(angle)) + y2 = int(cy + cut_outer * math.sin(angle)) + draw.line([(x1, y1), (x2, y2)], fill=EDGE_DARK, width=4) + + # Inner cutting ring + inner_ring_r = int(S * 0.22) + draw_filled_circle(draw, cx, cy, inner_ring_r, DRILL_STEEL, + outline=EDGE_DARK) + + # Spiral cutting grooves (3 spiral arms) + for arm in range(3): + base_angle = math.radians(120 * arm) + points = [] + for step in range(50): + t = step / 49 + r = inner_ring_r * (1.0 - t * 0.6) + a = base_angle + t * math.pi * 1.5 + px = int(cx + r * math.cos(a)) + py = int(cy + r * math.sin(a)) + points.append((px, py)) + if len(points) > 1: + draw.line(points, fill=DRILL_STEEL_DARK, width=6) + + # Center hub + hub_r = int(S * 0.08) + draw_filled_circle(draw, cx, cy, hub_r + 8, DRILL_STEEL_DARK, + outline=EDGE_DARK) + draw_filled_circle(draw, cx, cy, hub_r, DRILL_TIP, outline=EDGE_DARK) + # Carbide tip gradient + for r in range(hub_r, 0, -1): + t = 1.0 - (r / hub_r) + c = lerp_color(DRILL_TIP, DRILL_TIP_BRIGHT, t ** 0.6) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + # Center cross mark on tip + draw.line([(cx - hub_r + 10, cy), (cx + hub_r - 10, cy)], + fill=DRILL_STEEL_DARK, width=3) + draw.line([(cx, cy - hub_r + 10), (cx, cy + hub_r - 10)], + fill=DRILL_STEEL_DARK, width=3) + + # Subtle glow on the drill tip + add_radial_glow(img, cx, cy, hub_r + 20, DRILL_GLOW, intensity=0.15) + draw = ImageDraw.Draw(img) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# Back face — motor housing +# --------------------------------------------------------------------------- + +def make_back(): + """Back face: motor housing with exhaust port and power connection.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Upper panel — ID plate area + panel_h = int(S * 0.08) + draw.rectangle([9, 9, S - 10, panel_h + 9], fill=ARMOR_MID) + add_bevel_border(draw, 9, 9, S - 10, panel_h + 9, ARMOR_LIGHT, + EDGE_DARK, width=2) + for rx in range(60, S - 40, int(S * 0.08)): + draw_filled_circle(draw, rx, 9 + panel_h // 2, 5, RIVET_COLOR) + + # Lower panel + base_top = S - panel_h - 9 + draw.rectangle([9, base_top, S - 10, S - 10], fill=ARMOR_MID) + add_bevel_border(draw, 9, base_top, S - 10, S - 10, ARMOR_LIGHT, + EDGE_DARK, width=2) + for rx in range(60, S - 40, int(S * 0.08)): + draw_filled_circle(draw, rx, base_top + panel_h // 2, 5, + RIVET_COLOR) + + # Central exhaust port + exhaust_r = int(S * 0.18) + draw_filled_circle(draw, cx, cy, exhaust_r + 12, ARMOR_MID, + outline=EDGE_DARK) + draw_filled_circle(draw, cx, cy, exhaust_r, VENT_SLOT, + outline=EDGE_DARK) + + # Exhaust grille (concentric rings) + for i in range(1, 5): + r = int(exhaust_r * (i / 5)) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], + outline=ARMOR_MID) + + # Cross bars in exhaust + draw.line([(cx - exhaust_r + 5, cy), (cx + exhaust_r - 5, cy)], + fill=ARMOR_MID, width=6) + draw.line([(cx, cy - exhaust_r + 5), (cx, cy + exhaust_r - 5)], + fill=ARMOR_MID, width=6) + + # Exhaust bolts + for i in range(8): + angle = math.radians(45 * i) + bx = int(cx + (exhaust_r + 6) * math.cos(angle)) + by = int(cy + (exhaust_r + 6) * math.sin(angle)) + draw_filled_circle(draw, bx, by, 6, ARMOR_LIGHT, outline=EDGE_DARK) + + # Ventilation slots on sides + vent_w = int(S * 0.12) + vent_margin = int(S * 0.12) + for side_x in [vent_margin, S - vent_margin - vent_w]: + vy_start = panel_h + 40 + vy_end = base_top - 40 + num_slots = 8 + slot_h = (vy_end - vy_start) // num_slots + for i in range(num_slots): + sy = vy_start + i * slot_h + draw.rectangle([side_x, sy, side_x + vent_w, sy + slot_h - 6], + fill=VENT_SLOT, outline=EDGE_DARK) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# Side face — with directional arrow (base, then rotated) +# --------------------------------------------------------------------------- + +def make_side_arrow_up(): + """Side face with upward-pointing arrow indicating drill direction.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Panel seams (horizontal dividers) + seam_y1 = int(S * 0.15) + seam_y2 = int(S * 0.85) + draw.line([(30, seam_y1), (S - 30, seam_y1)], fill=SEAM_COLOR, width=3) + draw.line([(30, seam_y2), (S - 30, seam_y2)], fill=SEAM_COLOR, width=3) + + # Side panel details — small vent grille on left + grille_x = int(S * 0.06) + grille_w = int(S * 0.10) + grille_y1 = int(S * 0.25) + grille_y2 = int(S * 0.75) + draw.rectangle([grille_x, grille_y1, grille_x + grille_w, grille_y2], + fill=ARMOR_MID, outline=EDGE_DARK) + num_slots = 6 + slot_h = (grille_y2 - grille_y1) // num_slots + for i in range(num_slots): + sy = grille_y1 + i * slot_h + 4 + draw.rectangle([grille_x + 4, sy, + grille_x + grille_w - 4, sy + slot_h - 8], + fill=VENT_SLOT) + + # Small vent grille on right (mirror) + grille_x2 = S - grille_x - grille_w + draw.rectangle([grille_x2, grille_y1, grille_x2 + grille_w, grille_y2], + fill=ARMOR_MID, outline=EDGE_DARK) + for i in range(num_slots): + sy = grille_y1 + i * slot_h + 4 + draw.rectangle([grille_x2 + 4, sy, + grille_x2 + grille_w - 4, sy + slot_h - 8], + fill=VENT_SLOT) + + # Central directional arrow (pointing up) + arrow_w = int(S * 0.22) + arrow_h = int(S * 0.35) + arrow_top = cy - int(arrow_h * 0.55) + arrow_bot = cy + int(arrow_h * 0.45) + shaft_w = int(arrow_w * 0.4) + head_w = arrow_w + + # Arrow shaft + shaft_x1 = cx - shaft_w // 2 + shaft_x2 = cx + shaft_w // 2 + shaft_top = cy - int(arrow_h * 0.1) + draw.rectangle([shaft_x1, shaft_top, shaft_x2, arrow_bot], + fill=ARROW_COLOR, outline=ARROW_OUTLINE) + # Inner highlight on shaft + draw.rectangle([shaft_x1 + 6, shaft_top + 6, shaft_x2 - 6, arrow_bot - 6], + outline=DRILL_TIP_BRIGHT) + + # Arrow head (triangle) + head_top = arrow_top + head_bot = shaft_top + 2 + head_points = [ + (cx, head_top), + (cx - head_w // 2, head_bot), + (cx + head_w // 2, head_bot), + ] + draw.polygon(head_points, fill=ARROW_COLOR, outline=ARROW_OUTLINE) + # Inner highlight triangle + inner_margin = 10 + inner_points = [ + (cx, head_top + inner_margin * 2), + (cx - head_w // 2 + inner_margin + 6, head_bot - inner_margin), + (cx + head_w // 2 - inner_margin - 6, head_bot - inner_margin), + ] + draw.polygon(inner_points, outline=DRILL_TIP_BRIGHT) + + # Subtle glow around arrow + add_radial_glow(img, cx, cy - int(arrow_h * 0.1), int(S * 0.22), + DRILL_GLOW, intensity=0.1) + draw = ImageDraw.Draw(img) + + # Rivets along seams + for rx in range(80, S - 60, int(S * 0.1)): + draw_filled_circle(draw, rx, seam_y1, 5, RIVET_COLOR) + draw_filled_circle(draw, rx, seam_y2, 5, RIVET_COLOR) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + front = make_front() + back = make_back() + side_up = make_side_arrow_up() + + # Rotate for other arrow directions + side_down = side_up.rotate(180) + side_left = side_up.rotate(90, expand=False) + side_right = side_up.rotate(-90, expand=False) + + textures = { + "small_drill_front": front, + "small_drill": back, + "small_drill_arrow_up": side_up, + "small_drill_arrow_down": side_down, + "small_drill_arrow_left": side_left, + "small_drill_arrow_right": side_right, + } + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_small_solar_panel_textures.py b/scripts/generate_small_solar_panel_textures.py new file mode 100644 index 0000000..bbb708a --- /dev/null +++ b/scripts/generate_small_solar_panel_textures.py @@ -0,0 +1,462 @@ +#!/usr/bin/env python3 +"""Generate sci-fi hex-armor textures for Small Solar Panel at 1024x1024. + +Concept: Dark armored housing with honeycomb grid. The top face is the +showcase — a grid of blue photovoltaic cells in a beveled frame. The +"full" variant has brighter cells and a green status LED. Side faces +have a yellow output connector (the panel generates power). Bottom +is a heavy base plate with vents. + +Creates 4 textures: + small_solar_panel.png — top face (inactive) + small_solar_panel_full.png — top face (full/active) + small_solar_panel_side.png — side face (shared) + small_solar_panel_bottom.png — bottom face (shared) +""" + +import math +from PIL import Image, ImageDraw + +from texture_lib import ( + new_img, add_border, lerp_color, blend_over, + add_radial_glow, add_glow_ring, add_glowing_seam, + save_textures, +) + +S = 1024 # texture size + +# --------------------------------------------------------------------------- +# Color Palette +# --------------------------------------------------------------------------- + +# Housing / armor +ARMOR_DARK = (38, 40, 48, 255) +ARMOR_MID = (52, 56, 66, 255) +ARMOR_LIGHT = (70, 75, 88, 255) +HEX_LINE = (48, 52, 62, 255) +EDGE_DARK = (22, 24, 30, 255) +SEAM_COLOR = (30, 33, 40, 255) +RIVET_COLOR = (100, 108, 125, 255) +FRAME_DARK = (28, 30, 38, 255) + +# Connector hole +CONNECTOR_HOLE = (12, 14, 18, 255) + +# Output connector — yellow (produces power) +OUTPUT_RING = (200, 180, 40, 255) +OUTPUT_BRIGHT = (255, 240, 60, 255) +OUTPUT_GLOW = (255, 230, 50, 255) + +# Solar cell colors +CELL_DARK = (15, 40, 120, 255) +CELL_MID = (25, 60, 160, 255) +CELL_LIGHT = (40, 85, 200, 255) +CELL_HIGHLIGHT = (60, 110, 230, 255) +CELL_GRID = (10, 25, 70, 255) + +# Active/full solar cell colors (brighter) +CELL_FULL_DARK = (25, 60, 160, 255) +CELL_FULL_MID = (40, 90, 210, 255) +CELL_FULL_LIGHT = (60, 120, 245, 255) +CELL_FULL_HIGHLIGHT = (100, 160, 255, 255) + +# Status LED +LED_RED = (200, 50, 40, 255) +LED_RED_GLOW = (255, 60, 40, 255) +LED_GREEN = (50, 220, 50, 255) +LED_GREEN_GLOW = (80, 255, 80, 255) + +# Hex geometry +HEX_RADIUS = 28 + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _hex_vertices(cx, cy, radius): + """Return 6 vertices for a flat-top hexagon.""" + verts = [] + for i in range(6): + angle = math.radians(60 * i) + verts.append((cx + radius * math.cos(angle), + cy + radius * math.sin(angle))) + return verts + + +def _compute_hex_centers(size, radius, inset=12): + """Compute hex cell center positions covering the image.""" + centers = [] + col_step = radius * 1.5 + row_step = radius * math.sqrt(3) + cols = int((size - 2 * inset) / col_step) + 3 + rows = int((size - 2 * inset) / row_step) + 3 + for col in range(-1, cols + 1): + for row in range(-1, rows + 1): + cx = inset + col * col_step + cy = inset + row * row_step + (col % 2) * (row_step / 2) + if -radius < cx < size + radius and -radius < cy < size + radius: + centers.append((cx, cy)) + return centers + + +HEX_CENTERS = _compute_hex_centers(S, HEX_RADIUS) + + +def draw_filled_circle(draw, cx, cy, radius, fill, outline=None): + """Draw a filled circle.""" + draw.ellipse( + [cx - radius, cy - radius, cx + radius, cy + radius], + fill=fill, outline=outline + ) + + +def add_bevel_border(draw, x1, y1, x2, y2, light, dark, width=2): + """Draw a beveled rectangle border (raised appearance).""" + for i in range(width): + draw.line([(x1 + i, y1 + i), (x2 - i, y1 + i)], fill=light) + draw.line([(x1 + i, y1 + i), (x1 + i, y2 - i)], fill=light) + draw.line([(x1 + i, y2 - i), (x2 - i, y2 - i)], fill=dark) + draw.line([(x2 - i, y1 + i), (x2 - i, y2 - i)], fill=dark) + + +def make_hex_armor_base(): + """Create a hex armor face.""" + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + + for cx, cy in HEX_CENTERS: + verts = _hex_vertices(cx, cy, HEX_RADIUS - 1) + draw.polygon(verts, fill=ARMOR_DARK, outline=HEX_LINE) + + return img + + +def add_corner_bolts(draw, inset=30): + """Add bolts at the four corners.""" + for bx, by in [(inset, inset), (S - inset, inset), + (inset, S - inset), (S - inset, S - inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + +def draw_connector_port(img, draw, cx, cy, outer_r, inner_r, + ring_color, bright_color, glow_color, + powered=False): + """Draw a color-coded circular connector port.""" + active_ring = ring_color if not powered else bright_color + + for r in range(outer_r, inner_r, -1): + t = (outer_r - r) / (outer_r - inner_r) + c = lerp_color(active_ring, ARMOR_MID, t * 0.6) + draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=c) + + draw_filled_circle(draw, cx, cy, inner_r, CONNECTOR_HOLE, + outline=EDGE_DARK) + + pin_r = int(inner_r * 0.5) + pin_size = max(4, inner_r // 12) + pin_color = ring_color if not powered else bright_color + for angle_deg in [0, 90, 180, 270]: + angle = math.radians(angle_deg) + px = int(cx + pin_r * math.cos(angle)) + py = int(cy + pin_r * math.sin(angle)) + draw_filled_circle(draw, px, py, pin_size, pin_color) + + if powered: + add_glow_ring(img, cx, cy, outer_r, + outer_r + int(outer_r * 0.35), glow_color) + add_radial_glow(img, cx, cy, inner_r, bright_color, + intensity=0.3) + + +# --------------------------------------------------------------------------- +# TOP FACE — solar cell grid +# --------------------------------------------------------------------------- + +def make_top(full=False): + """Top face: realistic solar panel — many small photovoltaic cells + with thin silver bus bars, in a metallic aluminum frame. + + A real solar panel has: + - Many small rectangular cells in a tight grid + - Thin silver bus bars (vertical connectors between cells) + - Fine horizontal finger lines across each cell + - Slight color variation between cells + - Aluminum/silver frame + """ + img = new_img(S, ARMOR_DARK) + draw = ImageDraw.Draw(img) + + # Outer border + add_border(draw, S, EDGE_DARK, width=6) + add_bevel_border(draw, 6, 6, S - 7, S - 7, ARMOR_LIGHT, EDGE_DARK, + width=3) + + # Aluminum frame + frame_inset = 40 + fx1 = frame_inset + fy1 = frame_inset + fx2 = S - frame_inset + fy2 = S - frame_inset + + # Silver/aluminum frame color + frame_color = (140, 148, 160, 255) + frame_inner = (110, 118, 130, 255) + draw.rectangle([fx1, fy1, fx2, fy2], fill=frame_color) + add_bevel_border(draw, fx1, fy1, fx2, fy2, + (170, 178, 190, 255), (90, 96, 108, 255), + width=4) + + # Dark backing behind the cells + cell_margin = 18 + cx1 = fx1 + cell_margin + cy1 = fy1 + cell_margin + cx2 = fx2 - cell_margin + cy2 = fy2 - cell_margin + draw.rectangle([cx1, cy1, cx2, cy2], fill=(5, 10, 25, 255)) + + cell_area_w = cx2 - cx1 + cell_area_h = cy2 - cy1 + + # Cell grid: 6 columns x 10 rows for realistic proportions + cols = 6 + rows = 10 + bus_bar_w = 3 # silver bus bar width between columns + cell_gap_h = 2 # tiny gap between rows + cell_w = (cell_area_w - (cols - 1) * bus_bar_w) // cols + cell_h = (cell_area_h - (rows - 1) * cell_gap_h) // rows + + # Pick cell colors based on state + if full: + c_base = CELL_FULL_DARK + c_top = CELL_FULL_MID + c_hi = CELL_FULL_HIGHLIGHT + else: + c_base = CELL_DARK + c_top = CELL_MID + c_hi = CELL_HIGHLIGHT + + # Silver bus bar color + bus_color = (160, 170, 185, 255) + finger_color = (80, 100, 150, 255) + + for row in range(rows): + for col in range(cols): + # Cell position + x1 = cx1 + col * (cell_w + bus_bar_w) + y1 = cy1 + row * (cell_h + cell_gap_h) + x2 = x1 + cell_w + y2 = y1 + cell_h + + # Slight random-ish color variation per cell + variation = ((row * 7 + col * 13) % 5) - 2 + cell_base = ( + max(0, min(255, c_base[0] + variation * 3)), + max(0, min(255, c_base[1] + variation * 3)), + max(0, min(255, c_base[2] + variation * 4)), + 255, + ) + cell_top = ( + max(0, min(255, c_top[0] + variation * 3)), + max(0, min(255, c_top[1] + variation * 3)), + max(0, min(255, c_top[2] + variation * 4)), + 255, + ) + + # Cell gradient (lighter at top, darker at bottom) + for y in range(y1, min(y2, cy2)): + t = (y - y1) / max(1, cell_h) + c = lerp_color(cell_top, cell_base, t) + draw.line([(max(x1, cx1), y), + (min(x2, cx2), y)], fill=c) + + # Fine horizontal finger lines (thin silver lines + # for current collection, every ~12px) + for fy in range(y1 + 8, y2 - 4, 12): + if cy1 < fy < cy2: + draw.line( + [(max(x1 + 1, cx1), fy), + (min(x2 - 1, cx2), fy)], + fill=finger_color, width=1 + ) + + # Vertical bus bars between cell columns (silver connectors) + for col in range(1, cols): + bx = cx1 + col * cell_w + (col - 1) * bus_bar_w + draw.rectangle( + [bx, cy1, bx + bus_bar_w, cy2], + fill=bus_color + ) + + # Two thicker horizontal bus bars (main current collectors) + # positioned at 1/3 and 2/3 of the panel height + main_bus_w = 3 + for frac in (1 / 3, 2 / 3): + by = int(cy1 + cell_area_h * frac) + draw.rectangle( + [cx1, by - main_bus_w // 2, cx2, by + main_bus_w // 2], + fill=bus_color + ) + + # Corner bolts on the frame + bolt_inset = frame_inset // 2 + 2 + for bx, by in [(bolt_inset, bolt_inset), + (S - bolt_inset, bolt_inset), + (bolt_inset, S - bolt_inset), + (S - bolt_inset, S - bolt_inset)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Status LED in the bottom-right of the frame + led_x = fx2 - 16 + led_y = fy2 - 16 + if full: + draw_filled_circle(draw, led_x, led_y, 10, LED_GREEN, + outline=EDGE_DARK) + add_radial_glow(img, led_x, led_y, 25, LED_GREEN_GLOW, + intensity=0.5) + else: + draw_filled_circle(draw, led_x, led_y, 10, LED_RED, + outline=EDGE_DARK) + + # Full state: subtle blue glow across the panel + if full: + mid_x = (cx1 + cx2) // 2 + mid_y = (cy1 + cy2) // 2 + add_radial_glow(img, mid_x, mid_y, + cell_area_w // 2, c_hi, intensity=0.06) + + return img + + +# --------------------------------------------------------------------------- +# SIDE FACE — hex armor with yellow output connector +# --------------------------------------------------------------------------- + +def make_side(): + """Side face: hex armor with a yellow output connector circle.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + port_outer = int(S * 0.22) + port_inner = int(S * 0.12) + + # Recessed panel + panel_r = int(S * 0.30) + draw.rectangle( + [cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r], + fill=ARMOR_MID + ) + add_bevel_border( + draw, + cx - panel_r, cy - panel_r, cx + panel_r, cy + panel_r, + EDGE_DARK, ARMOR_LIGHT, width=4 + ) + + # Yellow output connector + draw_connector_port(img, draw, cx, cy, port_outer, port_inner, + OUTPUT_RING, OUTPUT_BRIGHT, OUTPUT_GLOW, + powered=False) + + # Panel bolts + bolt_off = panel_r - 16 + for bx, by in [(cx - bolt_off, cy - bolt_off), + (cx + bolt_off, cy - bolt_off), + (cx - bolt_off, cy + bolt_off), + (cx + bolt_off, cy + bolt_off)]: + draw_filled_circle(draw, bx, by, 10, ARMOR_LIGHT, + outline=EDGE_DARK) + draw_filled_circle(draw, bx, by, 5, RIVET_COLOR) + + # Horizontal seams + m = 30 + draw.line([(m, cy), (cx - panel_r - 10, cy)], + fill=SEAM_COLOR, width=2) + draw.line([(cx + panel_r + 10, cy), (S - m, cy)], + fill=SEAM_COLOR, width=2) + + add_corner_bolts(draw) + return img + + +# --------------------------------------------------------------------------- +# BOTTOM FACE — hex armor base plate +# --------------------------------------------------------------------------- + +def make_bottom(): + """Bottom face: hex armor with vent grille and mounting feet.""" + img = make_hex_armor_base() + draw = ImageDraw.Draw(img) + + cx, cy = S // 2, S // 2 + + # Central vent grille + grille_size = 360 + g1 = cx - grille_size // 2 + g2 = cx + grille_size // 2 + draw.rectangle([g1, g1, g2, g2], fill=ARMOR_MID, outline=ARMOR_LIGHT) + add_bevel_border(draw, g1, g1, g2, g2, ARMOR_LIGHT, EDGE_DARK, + width=3) + + # Vent slots + for vy in range(g1 + 24, g2 - 16, 22): + draw.rectangle([g1 + 18, vy, g2 - 18, vy + 8], fill=EDGE_DARK) + draw.rectangle([g1 + 20, vy + 1, g2 - 20, vy + 7], + fill=(15, 17, 22, 255)) + + # Mounting feet in corners + foot_size = 70 + foot_inset = 55 + for fx, fy in [(foot_inset, foot_inset), + (S - foot_inset - foot_size, foot_inset), + (foot_inset, S - foot_inset - foot_size), + (S - foot_inset - foot_size, + S - foot_inset - foot_size)]: + draw.rectangle([fx, fy, fx + foot_size, fy + foot_size], + fill=ARMOR_LIGHT, outline=EDGE_DARK) + add_bevel_border(draw, fx, fy, fx + foot_size, fy + foot_size, + (80, 86, 100, 255), EDGE_DARK, width=2) + fcx = fx + foot_size // 2 + fcy = fy + foot_size // 2 + draw_filled_circle(draw, fcx, fcy, 12, ARMOR_MID, + outline=EDGE_DARK) + draw_filled_circle(draw, fcx, fcy, 6, RIVET_COLOR) + + # Cross seams + draw.line([(cx, foot_inset + foot_size), (cx, g1)], + fill=SEAM_COLOR, width=3) + draw.line([(cx, g2), (cx, S - foot_inset - foot_size)], + fill=SEAM_COLOR, width=3) + draw.line([(foot_inset + foot_size, cy), (g1, cy)], + fill=SEAM_COLOR, width=3) + draw.line([(g2, cy), (S - foot_inset - foot_size, cy)], + fill=SEAM_COLOR, width=3) + + return img + + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- + +def main(): + textures = {} + + textures["small_solar_panel"] = make_top(full=False) + textures["small_solar_panel_full"] = make_top(full=True) + textures["small_solar_panel_side"] = make_side() + textures["small_solar_panel_bottom"] = make_bottom() + + save_textures(textures) + + +if __name__ == "__main__": + main() diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_down.png index 850438a..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_east.png index 571828d..5f82165 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_east.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_east.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_north.png index 7fc200f..e3f05f3 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_north.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_north.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_south.png index 3e8d12f..e3f05f3 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_south.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_south.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_up.png index 3091bb7..f5949cb 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_west.png index 0da09ef..5f82165 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_west.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_active_west.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_back.png deleted file mode 100644 index 48b6b84..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_back.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_fire.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_fire.png deleted file mode 100644 index 8b7b27d..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_fire.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_fire_off.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_fire_off.png deleted file mode 100644 index b788c34..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_fire_off.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_front.png deleted file mode 100644 index 379176a..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_front.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_housing.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_housing.png deleted file mode 100644 index 79edad8..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_housing.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_down.png index b832919..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_east.png index 35baa4d..5c5ff60 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_east.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_east.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_north.png index f8c6060..1654bfc 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_north.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_north.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_south.png index 4f1ce18..1654bfc 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_south.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_south.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_up.png index 121ec91..aab6903 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_west.png index 95d8d7f..5c5ff60 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_west.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_idle_west.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_side.png deleted file mode 100644 index 24165d2..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_side.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_east.png deleted file mode 100644 index d974460..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_east.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_north.png deleted file mode 100644 index 73c8321..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_north.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_south.png deleted file mode 100644 index 88ae177..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_south.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_west.png deleted file mode 100644 index da66567..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/auto_smelter_top_west.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_down.png index de5cd86..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_east.png index 50dab6b..8a54ed4 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_east.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_east.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_north.png index 7864a9c..cfa7099 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_north.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_north.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_south.png index ba2159d..4f57600 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_south.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_south.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_up.png index a36c281..7737bff 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_west.png index 34672b0..8a54ed4 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_west.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_active_west.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_down.png index f710ba0..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_east.png index fb3b773..5aab604 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_east.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_east.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_north.png index 524bf85..512a59a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_north.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_north.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_south.png index 686a3ac..1f495dd 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_south.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_south.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_up.png index 1dfb090..3eca03b 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_west.png index ac72971..5aab604 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_west.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/cobblestone_factory_idle_west.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_back.png index 3afc14f..f117067 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_back.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_back.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_bottom.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_bottom.png index 5d67cb3..3d81d39 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_bottom.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_bottom.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_front.png index 01037cc..11f1bb5 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_front.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_front.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_side.png index c445ab9..92e4a29 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_side.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_side.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_east.png index 38a528c..722fc62 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_east.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_east.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_north.png index 57df28d..1565ced 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_north.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_north.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_south.png index b319caa..274100d 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_south.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_south.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_west.png index 4b9ca78..5857c59 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_west.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/conveyor_belt_top_west.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_back.png index c9afab4..aa3f5a1 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_back.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_back.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_front.png index 9a0b0d9..56d3a8c 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_front.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_front.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_housing.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_housing.png index f4e9df5..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_housing.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_housing.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_side.png index f8779dd..4fc1fda 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_side.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_side.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_active.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_active.png index 8bf7c36..d9ee4c4 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_active.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_active.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_east.png index 8ca070c..27c77c1 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_east.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_east.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_north.png index 8ecbc32..fc74378 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_north.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_north.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_south.png index 9b4088e..2f7cc30 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_south.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_south.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_west.png index b296b9b..8f698e3 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_west.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/crusher_top_west.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_back.png index 9f40192..aa3f5a1 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_back.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_back.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_front.png index c1f373c..c742e05 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_front.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_front.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_front_active.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_front_active.png index e306553..0b845b1 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_front_active.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_front_active.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_housing.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_housing.png index 0b3c6c8..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_housing.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_housing.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_side.png index 9860b81..4fc1fda 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_side.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_side.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_active.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_active.png index 3bfc60e..da548cb 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_active.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_active.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_east.png index 6459df2..0fae204 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_east.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_east.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_north.png index c6bf4ba..3e20612 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_north.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_north.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_south.png index 9ac196f..a408948 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_south.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_south.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_west.png index 4f153ea..2773351 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_west.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/experience_extractor_top_west.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back.png deleted file mode 100644 index 40c60ea..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_lava_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_lava_full.png deleted file mode 100644 index aa2514c..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_lava_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_lava_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_lava_low.png deleted file mode 100644 index 520bf0d..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_lava_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_lava_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_lava_medium.png deleted file mode 100644 index bb802b1..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_lava_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_water_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_water_full.png deleted file mode 100644 index 81971f8..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_water_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_water_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_water_low.png deleted file mode 100644 index 9c99165..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_water_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_water_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_water_medium.png deleted file mode 100644 index 0d8a782..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_water_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_xp_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_xp_full.png deleted file mode 100644 index 35ee71d..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_xp_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_xp_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_xp_low.png deleted file mode 100644 index 40c48a4..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_xp_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_xp_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_xp_medium.png deleted file mode 100644 index f8e7933..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_back_xp_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down.png index b13b61e..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_full.png index 93990fc..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_low.png index 7881828..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_medium.png index ac1e2cc..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_lava_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_full.png index ff2d0ef..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_low.png index 636d3e9..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_medium.png index f5f098e..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_water_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_full.png index 5c4e269..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_low.png index 8b239db..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_medium.png index 07aef0d..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_down_xp_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east.png index 643632f..bef393a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_full.png index b378233..157a8fd 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_low.png index 122532a..2777212 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_medium.png index 0e3cde0..d674cb8 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_lava_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_full.png index a9f0b42..da8b79a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_low.png index b4a6c4a..f421486 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_medium.png index c78d157..d61233b 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_water_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_full.png index 7f8f3f6..8a9bc6e 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_low.png index 82e57b8..8d3dff6 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_medium.png index 8a05b3f..dbebb14 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_east_xp_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front.png deleted file mode 100644 index a6e37ad..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_lava_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_lava_full.png deleted file mode 100644 index 16d8611..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_lava_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_lava_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_lava_low.png deleted file mode 100644 index 0b24532..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_lava_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_lava_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_lava_medium.png deleted file mode 100644 index 90646d8..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_lava_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_water_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_water_full.png deleted file mode 100644 index 3ae7c34..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_water_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_water_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_water_low.png deleted file mode 100644 index f737cbe..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_water_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_water_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_water_medium.png deleted file mode 100644 index f0fecfe..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_water_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_xp_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_xp_full.png deleted file mode 100644 index 5143b03..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_xp_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_xp_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_xp_low.png deleted file mode 100644 index c0000a6..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_xp_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_xp_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_xp_medium.png deleted file mode 100644 index a4083ac..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_front_xp_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north.png index 4438be3..43b67e7 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_full.png index d91df2e..c9ee2f1 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_low.png index 29e4e99..41be4e2 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_medium.png index bd477ea..4330837 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_lava_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_full.png index b844e85..441c84d 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_low.png index fb0cbcb..e02d545 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_medium.png index 76b478f..34c48e6 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_water_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_full.png index ec80703..a631a85 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_low.png index 4f00f46..f385be2 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_medium.png index ead70bd..18daca4 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_north_xp_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side.png deleted file mode 100644 index 2f79c8e..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_lava_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_lava_full.png deleted file mode 100644 index 986586e..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_lava_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_lava_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_lava_low.png deleted file mode 100644 index 2e15365..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_lava_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_lava_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_lava_medium.png deleted file mode 100644 index b87aeaf..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_lava_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_water_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_water_full.png deleted file mode 100644 index cd36d45..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_water_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_water_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_water_low.png deleted file mode 100644 index c3ff069..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_water_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_water_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_water_medium.png deleted file mode 100644 index cf13a0a..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_water_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_xp_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_xp_full.png deleted file mode 100644 index 1a1aca7..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_xp_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_xp_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_xp_low.png deleted file mode 100644 index 6bcb8d1..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_xp_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_xp_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_xp_medium.png deleted file mode 100644 index 2986b06..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_side_xp_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south.png index 4887500..b8f75d1 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_full.png index c773b0c..29ebda7 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_low.png index 7da659f..29ebda7 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_medium.png index c779885..29ebda7 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_lava_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_full.png index dab2a97..b77458e 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_low.png index 5d5e325..b77458e 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_medium.png index fb88c5e..b77458e 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_water_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_full.png index 72dcc8e..1307ff8 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_low.png index a0fcd9c..1307ff8 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_medium.png index 2b1ba1a..1307ff8 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_south_xp_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top.png deleted file mode 100644 index 5f45dbf..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_lava_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_lava_full.png deleted file mode 100644 index 18107eb..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_lava_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_lava_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_lava_low.png deleted file mode 100644 index a344191..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_lava_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_lava_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_lava_medium.png deleted file mode 100644 index 817d515..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_lava_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_water_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_water_full.png deleted file mode 100644 index b78ad84..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_water_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_water_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_water_low.png deleted file mode 100644 index ffe5537..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_water_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_water_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_water_medium.png deleted file mode 100644 index 31c57ef..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_water_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_xp_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_xp_full.png deleted file mode 100644 index a6626ee..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_xp_full.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_xp_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_xp_low.png deleted file mode 100644 index feb4f4f..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_xp_low.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_xp_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_xp_medium.png deleted file mode 100644 index f10f345..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_top_xp_medium.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up.png index 6ba7f87..651bc1f 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_full.png index e60cb08..7734356 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_low.png index 8f6e419..7734356 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_medium.png index 51684f7..7734356 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_lava_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_full.png index 49387e3..8ea78ee 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_low.png index 2501c63..8ea78ee 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_medium.png index 85fb1fe..8ea78ee 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_water_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_full.png index 7fccaa2..411e6ec 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_low.png index 9de8f6a..411e6ec 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_medium.png index 3396db3..411e6ec 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_up_xp_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west.png index 301c912..bef393a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_full.png index 0c652f0..157a8fd 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_low.png index 29fa8f4..2777212 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_medium.png index 991cbc0..d674cb8 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_lava_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_full.png index 808ced5..da8b79a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_low.png index e6b617c..f421486 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_medium.png index 3f63220..d61233b 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_water_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_full.png index d257b70..8a9bc6e 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_low.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_low.png index f96066f..8d3dff6 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_low.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_low.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_medium.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_medium.png index e60b3ba..dbebb14 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_medium.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_container_west_xp_medium.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back.png index 932b467..81da2c7 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_lava.png index 4d5addc..feb4f34 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_water.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_water.png index 8f7ba12..feb4f34 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_water.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_water.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_xp.png index eb0c210..feb4f34 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_back_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom.png index 932b467..c41dd06 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_lava.png index 4d5addc..fae46a3 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_water.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_water.png index 8f7ba12..fae46a3 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_water.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_water.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_xp.png index eb0c210..fae46a3 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_bottom_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front.png index e2100d8..059d5b2 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_lava.png index d16f831..d0cee3b 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_water.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_water.png index 0d6d43a..3074698 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_water.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_water.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_xp.png index e3712d9..5c959ca 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_front_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side.png index 932b467..d97f8ac 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_lava.png index 4d5addc..5692882 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_water.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_water.png index 8f7ba12..5692882 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_water.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_water.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_xp.png index eb0c210..5692882 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_side_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top.png index 932b467..8817bb0 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_lava.png index 4d5addc..d2ee4dd 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_water.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_water.png index 8f7ba12..c0c7b45 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_water.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_water.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_xp.png index eb0c210..bf83391 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_merger_top_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back.png index c2aef23..a4b72e5 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled.png index 5fbfb1c..f83e3ad 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled_lava.png index c55634c..c701f5d 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled_xp.png index 344940a..abf79d1 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_back_filled_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front.png index 051db15..1f328b7 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled.png index 55e26e5..3f0e81a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled_lava.png index 060a071..7961ced 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled_xp.png index 1d48901..8cf9ca5 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_front_filled_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_down.png index c85290b..9f58e37 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_down.png index 6b01f7a..22200f1 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_down.png index fb3852a..1299aaa 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_left.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_left.png index 1f921d1..9f314fd 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_left.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_left.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_right.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_right.png index 8ebe58e..bd24a98 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_right.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_right.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_up.png index 338636a..2dbd953 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_lava_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_left.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_left.png index c34412d..79765db 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_left.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_left.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_right.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_right.png index 723be55..fc0cc06 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_right.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_right.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_up.png index 4d77c76..717c8f0 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_down.png index 722f825..48eac55 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_left.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_left.png index 4b79fe2..e41809a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_left.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_left.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_right.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_right.png index dac8135..42e409b 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_right.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_right.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_up.png index 4fe15c5..af8fa87 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_filled_xp_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_left.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_left.png index 892ca70..dc17a7e 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_left.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_left.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_right.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_right.png index 8113ca1..f2d14f5 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_right.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_right.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_up.png index 0916364..3ccf4c3 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pipe_side_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom.png index b4ce767..1c7472e 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom_active.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom_active.png index a6da686..68a8495 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom_active.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom_active.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom_active_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom_active_lava.png index bb9a76e..a46a367 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom_active_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_bottom_active_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side.png index 453f5e7..977520d 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side_active.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side_active.png index 0358534..83731cc 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side_active.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side_active.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side_active_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side_active_lava.png index a7ec3a8..e7cb88b 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side_active_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_side_active_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top.png index 147c5e7..b1042eb 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top_active.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top_active.png index 1eb49a6..ba046b0 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top_active.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top_active.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top_active_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top_active_lava.png index 17dbaa0..f6e6003 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top_active_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_pump_top_active_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back.png index 932b467..059d5b2 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_lava.png index 4d5addc..d0cee3b 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_water.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_water.png index 8f7ba12..3074698 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_water.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_water.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_xp.png index eb0c210..5c959ca 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_back_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom.png index 2af3b39..61fb612 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_lava.png index 2af3b39..e09ca68 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_water.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_water.png index 2af3b39..3fcfb19 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_water.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_water.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_xp.png index 11ad606..ed3dcbd 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_bottom_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front.png index e2100d8..81da2c7 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_lava.png index d16f831..feb4f34 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_water.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_water.png index 0d6d43a..feb4f34 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_water.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_water.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_xp.png index e3712d9..feb4f34 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_front_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side.png index e2100d8..bad6b45 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_lava.png index d16f831..b7b4d36 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_water.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_water.png index 0d6d43a..af4b831 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_water.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_water.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_xp.png index e3712d9..0dd0a09 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_side_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top.png index e2100d8..8817bb0 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_lava.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_lava.png index d16f831..d2ee4dd 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_lava.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_lava.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_water.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_water.png index 0d6d43a..c0c7b45 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_water.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_water.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_xp.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_xp.png index e3712d9..bf83391 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_xp.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/fluid_splitter_top_xp.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/guide_fluid_container.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/guide_fluid_container.png deleted file mode 100644 index 448aaed..0000000 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/guide_fluid_container.png and /dev/null differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_bottom.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_bottom.png index 1eec457..1c7472e 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_bottom.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_bottom.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_side.png index c045caf..5d13aa6 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_side.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_side.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_side_active.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_side_active.png index dedb720..501e0f9 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_side_active.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_side_active.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_top.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_top.png index 94e7de6..7485bee 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_top.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_top.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_top_active.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_top_active.png index 60040e7..1e942a3 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_top_active.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/lava_generator_top_active.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_down.png index 32177b3..d60c54d 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_east.png index 891e187..ffc675a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_east.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_east.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_north.png index 13c3165..fbef63f 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_north.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_north.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_south.png index 85933e3..7f04183 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_south.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_south.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_up.png index e7ab3b3..ad83c88 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_west.png index f292cda..ffc675a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_west.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_active_west.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_down.png index 5c57947..1c7472e 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_east.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_east.png index b3b789d..0a8735e 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_east.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_east.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_north.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_north.png index ca56be7..c6053a2 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_north.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_north.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_south.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_south.png index 85f405f..f33bd76 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_south.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_south.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_up.png index 0fdbc3a..72818f6 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_west.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_west.png index 0a87b96..0a8735e 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_west.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/obsidian_factory_idle_west.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_back.png index 99d9039..e75070c 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_back.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_back.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_back_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_back_powered.png index 99d9039..2e6396f 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_back_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_back_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_down.png index bcf815d..88efe56 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_down_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_down_powered.png index 467844d..7d7cf91 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_down_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_down_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_left.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_left.png index 2fb826e..a0cb2b6 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_left.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_left.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_left_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_left_powered.png index b1caf2a..708bc47 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_left_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_left_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_right.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_right.png index f0e26ae..d5f9a06 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_right.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_right.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_right_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_right_powered.png index e0bb96e..bfe74fd 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_right_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_right_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_up.png index 228611e..fbfce72 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_up_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_up_powered.png index b2b79b6..38e4a54 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_up_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_cap_up_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_front.png index 27962d6..0035c42 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_front.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_front.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_front_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_front_powered.png index 4d3e6e8..165c774 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_front_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_front_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_down.png index 184e33c..74a0b81 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_down_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_down_powered.png index bac282b..7007ce0 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_down_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_down_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_left.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_left.png index b77bec6..ccbd4c5 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_left.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_left.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_left_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_left_powered.png index ea22321..51b4f87 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_left_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_left_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_right.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_right.png index a5a38c7..0ae9c64 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_right.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_right.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_right_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_right_powered.png index da835a3..b35dc86 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_right_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_right_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_up.png index b2dad00..560f726 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_up_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_up_powered.png index 9fd7965..bff1752 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_up_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_cable_side_up_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_back.png index e3995e1..1e89587 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_back.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_back.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_back_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_back_powered.png index 937c7dd..2d9fc93 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_back_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_back_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_bottom.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_bottom.png index e3995e1..cc76c2a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_bottom.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_bottom.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_bottom_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_bottom_powered.png index 937c7dd..084218a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_bottom_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_bottom_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_front.png index 4b03417..b9ffaae 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_front.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_front.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_front_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_front_powered.png index 32d927d..c626656 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_front_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_front_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_side.png index e3995e1..59b06c5 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_side.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_side.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_side_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_side_powered.png index 937c7dd..9284045 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_side_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_side_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_top.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_top.png index e3995e1..cc76c2a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_top.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_top.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_top_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_top_powered.png index 937c7dd..084218a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_top_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_merger_top_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_back.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_back.png index e3995e1..b9ffaae 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_back.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_back.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_back_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_back_powered.png index 937c7dd..c626656 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_back_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_back_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_cap.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_cap.png index 4b03417..d9f3afb 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_cap.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_cap.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_cap_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_cap_powered.png index 32d927d..aa207f8 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_cap_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_cap_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_front.png index 4b03417..97cfe39 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_front.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_front.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_front_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_front_powered.png index 32d927d..2d9fc93 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_front_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_front_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_side.png index 4b03417..5287f2a 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_side.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_side.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_side_powered.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_side_powered.png index 32d927d..dabc93b 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_side_powered.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/power_splitter_side_powered.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill.png index 598c08f..6673f66 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_down.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_down.png index 58acfb0..175e187 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_down.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_down.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_left.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_left.png index 2ccca2c..8dd9a1f 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_left.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_left.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_right.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_right.png index a88b61f..1575858 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_right.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_right.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_up.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_up.png index 7ae83da..6e093a0 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_up.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_arrow_up.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_front.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_front.png index 76294dc..722bc0c 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_front.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_drill_front.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel.png index 3966c81..3360b2d 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_bottom.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_bottom.png index 38cdb4c..40d3b65 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_bottom.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_bottom.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_full.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_full.png index 3966c81..afba3a5 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_full.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_full.png differ diff --git a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_side.png b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_side.png index ad7f90a..e5f50b7 100644 Binary files a/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_side.png and b/src/main/resources/atlas/resourcepack/assets/minecraft/textures/block/custom/small_solar_panel_side.png differ