Files
Ascent-of-Man/generate_rivers.py
T
2026-11-10 19:54:00 +01:00

190 lines
7.7 KiB
Python

#!/usr/bin/env python3
"""
River generator and map builder for tiletest.
Encodes rivers onto images/rivermap.png such that rivers STRICTLY pass through
the exact midpoints of the 6 hexagon edges, ensuring 100% seamless connections.
"""
from PIL import Image, ImageDraw
import numpy as np
WIDTH = 3600
HEIGHT = 1500
SCALE_X = 8.0
SCALE_Y = 9.24045
LOD_0_SIZE_Y = 0.4328799197461722
# 6 edge midpoints in local tile space (radius R = 0.5)
MIDPOINTS = [
(-0.375, 0.21650635), # Edge 0 (Top-Left)
( 0.0, 0.43301270), # Edge 1 (Top)
( 0.375, 0.21650635), # Edge 2 (Top-Right)
( 0.375, -0.21650635), # Edge 3 (Bottom-Right)
( 0.0, -0.43301270), # Edge 4 (Bottom)
(-0.375, -0.21650635) # Edge 5 (Bottom-Left)
]
def tile_world_pos(tx, ty):
wx = tx * 0.75
wy = ty * (LOD_0_SIZE_Y * 2.0) + (0.0 if tx % 2 == 0 else LOD_0_SIZE_Y)
return wx, wy
def world_to_pixel(wx, wy):
px = int(round(wx * SCALE_X))
py = int(round(wy * SCALE_Y))
return px, HEIGHT - 1 - py
def get_hex_neighbor(x, y, edge):
even = (x % 2 == 0)
if edge == 0: return (x - 1, y if even else y + 1)
if edge == 1: return (x, y + 1)
if edge == 2: return (x + 1, y if even else y + 1)
if edge == 3: return (x + 1, y - 1 if even else y)
if edge == 4: return (x, y - 1)
if edge == 5: return (x - 1, y - 1 if even else y)
return (x, y)
def trace_river(start_tile, edge_steps):
"""Traces a river from a start tile following a sequence of exit edges."""
path = []
curr = start_tile
in_edge = None
for out_edge in edge_steps:
path.append((curr, in_edge, out_edge))
curr = get_hex_neighbor(curr[0], curr[1], out_edge)
in_edge = (out_edge + 3) % 6
path.append((curr, in_edge, None))
return path
def build_rivermap(output_path="/home/bart/git/Ascent-of-Man/images/rivermap.png", preview_path="/home/bart/git/Ascent-of-Man/images/rivermap_preview.png"):
river_img = Image.new('RGB', (WIDTH, HEIGHT), (0, 0, 0))
draw = ImageDraw.Draw(river_img)
# 1. Main River: starts high on mountain (36, 31) in snow, flows down to South Ocean (32, 14)
main_river_steps = [5, 5, 5, 5] + [4] * 15
main_river = trace_river((36, 31), main_river_steps)
# 2. Mountain Tributary: starts on ridge (36, 28) and merges with main river at (32, 26)
trib_river_steps = [5, 5, 5, 5]
tributary = trace_river((36, 28), trib_river_steps)
# 3. Canyon River in Prairie (Red Sand & Red Rock)
prairie_steps = [4, 4, 5, 4, 4, 3, 4, 4, 4]
prairie_river = trace_river((110, 32), prairie_steps)
rivers = [main_river, tributary, prairie_river]
river_color = (0, 180, 255)
for r in rivers:
for (tx, ty), e_in, e_out in r:
wx, wy = tile_world_pos(tx, ty)
cx, cy = world_to_pixel(wx, wy)
edges = [e for e in (e_in, e_out) if e is not None]
for e in edges:
mx, my = MIDPOINTS[e]
mpx, mpy = world_to_pixel(wx + mx, wy + my)
# Line between midpoint and tile center
draw.line([(mpx, mpy), (cx, cy)], fill=river_color, width=2)
# Small marker at midpoint for detection
draw.rectangle([mpx - 1, mpy - 1, mpx + 1, mpy + 1], fill=river_color)
river_img.save(output_path)
print(f"Saved rivermap to {output_path}")
# Build an overlay preview on top of typemap.png
try:
type_img = Image.open("/home/bart/git/Ascent-of-Man/images/typemap.png").convert("RGBA")
r_arr = np.array(river_img)
t_arr = np.array(type_img)
mask = (r_arr[:, :, 2] > 120) & (r_arr[:, :, 0] < 100)
t_arr[mask] = [0, 220, 255, 255]
preview = Image.fromarray(t_arr)
preview.save(preview_path)
print(f"Saved preview to {preview_path}")
# Build orthographic hex-tile minimap.png
water_threshold = 6
try:
with open("/home/bart/git/Ascent-of-Man/config/terrain_movement.yaml", "r") as yf:
for line in yf:
if line.strip().startswith("water_height_threshold:"):
val_str = line.split(":", 1)[1].split("#", 1)[0].strip()
water_threshold = int(val_str)
break
except Exception:
pass
h_arr = np.array(Image.open("/home/bart/git/Ascent-of-Man/images/heightmap.jpg"))
R = 8.0
W = 3608
H = 1740
hex_img = Image.new('RGB', (W, H), (15, 20, 30))
hdraw = ImageDraw.Draw(hex_img)
angles = [np.pi * 60.0 * i / 180.0 for i in range(6)]
corner_dx = [-R * np.cos(a) for a in angles]
corner_dy = [ R * np.sin(a) for a in angles]
for ty in range(125):
for tx in range(300):
wx = tx * 0.75
wy = ty * (LOD_0_SIZE_Y * 2.0) + (0.0 if tx % 2 == 0 else LOD_0_SIZE_Y)
cx = (wx + 0.5) * 16.0
cy = H - 1 - (wy + LOD_0_SIZE_Y) * 16.0
px = min(3599, max(0, int(round(wx * SCALE_X))))
py = min(1499, max(0, int(round(wy * SCALE_Y))))
h = int(h_arr[1500 - 1 - py, px, 0]) if h_arr.ndim == 3 else int(h_arr[1500 - 1 - py, px])
tp = t_arr[1500 - 1 - py, px]
dist_pole = abs((wy / 108.22) - 0.5) * 2.0
if h < water_threshold:
color = (220, 235, 250) if dist_pole > 0.85 else (28, 95, 175) # Water (blue) / polar ice
elif h > 182 or (dist_pole > 0.80 and h > 20):
color = (245, 250, 255) # Snow (white) above 2500m
elif h > 73:
# Mountain rock (between 1000m and 2500m)
r, g, b = int(tp[0]), int(tp[1]), int(tp[2])
if r > 150 and g < 100:
color = (175, 75, 55) # Red rock mountain in prairie
else:
color = (125, 130, 135) # Gray alpine mountain rock
else:
# Biome ground below 1000m
r, g, b = int(tp[0]), int(tp[1]), int(tp[2])
sat = max(r, g, b) - min(r, g, b)
if r > 150 and g < 100:
color = (195, 75, 45) # Prairie / Red Rock
elif r > 150 and g > 150 and b < 100:
color = (225, 185, 65) # Sahara yellow
elif b > 50 and r < 50:
color = (35, 115, 50) # Forest green
elif sat < 40 and g < 140:
color = (110, 125, 95) # Tundra slate
else:
color = (85, 175, 55) # Grassland green
poly = [(cx + corner_dx[i], cy + corner_dy[i]) for i in range(6)]
hdraw.polygon(poly, fill=color, outline=(0, 0, 0, 45))
# Draw rivers on hex map
for r in rivers:
for (tx, ty), e_in, e_out in r:
wx, wy = tile_world_pos(tx, ty)
cx = (wx + 0.5) * 16.0
cy = H - 1 - (wy + LOD_0_SIZE_Y) * 16.0
edges = [e for e in (e_in, e_out) if e is not None]
for e in edges:
mx, my = MIDPOINTS[e]
mpx = (wx + mx + 0.5) * 16.0
mpy = H - 1 - (wy + my + LOD_0_SIZE_Y) * 16.0
hdraw.line([(mpx, mpy), (cx, cy)], fill=(0, 220, 255), width=3)
hdraw.ellipse([mpx - 2, mpy - 2, mpx + 2, mpy + 2], fill=(0, 240, 255))
hex_img.save("/home/bart/git/Ascent-of-Man/images/minimap.png")
print("Saved orthographic hex minimap to /home/bart/git/Ascent-of-Man/images/minimap.png")
except Exception as ex:
print(f"Minimap/preview generation skipped: {ex}")
if __name__ == "__main__":
build_rivermap()