Improve sixty-sixth battle map detail
This commit is contained in:
@@ -9,6 +9,8 @@ from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageChops, ImageDraw, ImageEnhance, ImageFilter, ImageOps
|
||||
|
||||
Image.MAX_IMAGE_PIXELS = None
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCENARIO_FILE = ROOT / "src" / "game" / "data" / "scenario.ts"
|
||||
@@ -666,6 +668,8 @@ MAP_CONFIGS = {
|
||||
"before": BATTLE_IMAGE_DIR / "sixty-sixth-battle-map.svg",
|
||||
"seed": 241354,
|
||||
"tile_size": VAST_LARGE_MAP_TILE_SIZE,
|
||||
"output_tile_size": DESKTOP_RUNTIME_MAP_TILE_SIZE,
|
||||
"detail_profile": "wuzhang-final",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1373,6 +1377,176 @@ def add_weishui_camps_details(canvas: Image.Image, terrain: list[list[str]]) ->
|
||||
canvas.alpha_composite(sharp.filter(ImageFilter.GaussianBlur(0.04)))
|
||||
|
||||
|
||||
def draw_wuzhang_ledger_tent(draw: ImageDraw.ImageDraw, x: int, y: int) -> None:
|
||||
cx, cy = center(x, y)
|
||||
tent_w = int(TILE * 0.62)
|
||||
tent_h = int(TILE * 0.44)
|
||||
base_y = cy + int(TILE * 0.24)
|
||||
draw.polygon(
|
||||
[(cx - tent_w, base_y), (cx - int(TILE * 0.08), cy - tent_h), (cx + tent_w, base_y)],
|
||||
fill=(118, 86, 47, 154),
|
||||
)
|
||||
draw.polygon(
|
||||
[(cx - int(tent_w * 0.7), base_y - 3), (cx - int(TILE * 0.08), cy - int(tent_h * 0.72)), (cx + int(tent_w * 0.18), base_y - 3)],
|
||||
fill=(205, 162, 86, 136),
|
||||
)
|
||||
draw.line((cx - tent_w, base_y, cx + tent_w, base_y), fill=(58, 39, 25, 142), width=max(3, TILE // 32))
|
||||
table_y = cy + int(TILE * 0.38)
|
||||
draw.rounded_rectangle(
|
||||
(cx - int(TILE * 0.36), table_y - int(TILE * 0.11), cx + int(TILE * 0.38), table_y + int(TILE * 0.12)),
|
||||
radius=max(4, TILE // 28),
|
||||
fill=(92, 56, 30, 144),
|
||||
)
|
||||
draw.rectangle(
|
||||
(cx - int(TILE * 0.28), table_y - int(TILE * 0.07), cx + int(TILE * 0.22), table_y + int(TILE * 0.05)),
|
||||
fill=(222, 194, 129, 132),
|
||||
)
|
||||
for offset in (-int(TILE * 0.18), 0, int(TILE * 0.18)):
|
||||
draw.line(
|
||||
(cx + offset, table_y - int(TILE * 0.05), cx + offset + int(TILE * 0.16), table_y + int(TILE * 0.04)),
|
||||
fill=(99, 68, 42, 98),
|
||||
width=2,
|
||||
)
|
||||
for lx in (cx - int(TILE * 0.48), cx + int(TILE * 0.5)):
|
||||
ly = cy - int(TILE * 0.08)
|
||||
draw.ellipse((lx - 8, ly - 8, lx + 8, ly + 8), fill=(238, 177, 76, 132))
|
||||
draw.arc((lx - 22, ly - 36, lx + 28, ly + 20), 205, 330, fill=(93, 75, 48, 52), width=2)
|
||||
|
||||
|
||||
def draw_wuzhang_supply_cart(draw: ImageDraw.ImageDraw, x: int, y: int) -> None:
|
||||
cx, cy = center(x, y)
|
||||
cart_w = int(TILE * 0.5)
|
||||
cart_h = int(TILE * 0.22)
|
||||
body_y = cy + int(TILE * 0.14)
|
||||
draw.rectangle((cx - cart_w, body_y - cart_h, cx + cart_w, body_y + cart_h), fill=(111, 71, 39, 138))
|
||||
draw.line((cx - cart_w - 10, body_y - cart_h - 6, cx + cart_w + 8, body_y + cart_h + 6), fill=(213, 168, 86, 76), width=3)
|
||||
for wx in (cx - int(TILE * 0.38), cx + int(TILE * 0.38)):
|
||||
draw.ellipse((wx - 12, body_y + cart_h - 4, wx + 12, body_y + cart_h + 20), outline=(42, 29, 21, 138), width=3)
|
||||
for offset in (-int(TILE * 0.22), 0, int(TILE * 0.23)):
|
||||
draw.rectangle(
|
||||
(cx + offset - int(TILE * 0.12), body_y - cart_h - int(TILE * 0.14), cx + offset + int(TILE * 0.1), body_y - cart_h + int(TILE * 0.02)),
|
||||
fill=(153, 107, 55, 124),
|
||||
)
|
||||
|
||||
|
||||
def draw_wuzhang_beacon(draw: ImageDraw.ImageDraw, x: int, y: int) -> None:
|
||||
cx, cy = center(x, y)
|
||||
base_y = cy + int(TILE * 0.28)
|
||||
top_y = cy - int(TILE * 0.28)
|
||||
draw.line((cx - int(TILE * 0.22), base_y, cx - 5, top_y), fill=(61, 43, 30, 150), width=max(4, TILE // 28))
|
||||
draw.line((cx + int(TILE * 0.22), base_y, cx + 5, top_y), fill=(61, 43, 30, 150), width=max(4, TILE // 28))
|
||||
draw.line((cx - int(TILE * 0.28), cy, cx + int(TILE * 0.28), cy - 5), fill=(128, 88, 48, 132), width=max(4, TILE // 34))
|
||||
draw.rectangle((cx - 18, top_y - 8, cx + 18, top_y + 14), fill=(88, 55, 32, 166))
|
||||
draw.polygon(
|
||||
[(cx - 18, top_y - 8), (cx - 6, top_y - 38), (cx + 4, top_y - 14), (cx + 16, top_y - 42), (cx + 18, top_y - 4)],
|
||||
fill=(222, 103, 33, 170),
|
||||
)
|
||||
draw.polygon([(cx - 6, top_y - 5), (cx + 1, top_y - 26), (cx + 8, top_y - 5)], fill=(244, 181, 70, 152))
|
||||
for offset in (-26, -2, 24):
|
||||
draw.arc((cx + offset - 18, top_y - 74, cx + offset + 48, top_y - 18), 205, 340, fill=(77, 72, 58, 54), width=2)
|
||||
|
||||
|
||||
def add_wuzhang_final_details(canvas: Image.Image, terrain: list[list[str]]) -> None:
|
||||
details = Image.new("RGBA", canvas.size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(details)
|
||||
sharp = Image.new("RGBA", canvas.size, (0, 0, 0, 0))
|
||||
sharp_draw = ImageDraw.Draw(sharp)
|
||||
|
||||
for y, row in enumerate(terrain):
|
||||
for x, kind in enumerate(row):
|
||||
left, top, right, bottom = tile_box(x, y)
|
||||
cx, cy = center(x, y)
|
||||
|
||||
if kind in {"hill", "cliff"} and RNG.random() < 0.7:
|
||||
for _ in range(3):
|
||||
px = RNG.randint(left + 10, right - 12)
|
||||
py = RNG.randint(top + 12, bottom - 12)
|
||||
draw.line(
|
||||
(px - RNG.randint(12, 42), py + RNG.randint(-6, 8), px + RNG.randint(22, 58), py + RNG.randint(-10, 10)),
|
||||
fill=(73, 63, 46, RNG.randint(34, 62)),
|
||||
width=RNG.choice([2, 2, 3]),
|
||||
)
|
||||
|
||||
if kind == "road" and (y >= 78 or x <= 70) and RNG.random() < 0.62:
|
||||
for _ in range(2):
|
||||
py = cy + RNG.randint(-30, 30)
|
||||
draw.line(
|
||||
(left + RNG.randint(10, 26), py, right - RNG.randint(10, 26), py + RNG.randint(-7, 7)),
|
||||
fill=(91, 67, 44, RNG.randint(40, 68)),
|
||||
width=RNG.choice([2, 3]),
|
||||
)
|
||||
if RNG.random() < 0.28:
|
||||
for step in range(3):
|
||||
px = left + 24 + step * int(TILE * 0.24) + RNG.randint(-6, 6)
|
||||
py = cy + RNG.randint(-18, 22)
|
||||
draw.ellipse((px - 4, py - 2, px + 5, py + 3), fill=(59, 45, 33, 54))
|
||||
|
||||
if kind == "river":
|
||||
for _ in range(3):
|
||||
sx = RNG.randint(left + 12, max(left + 13, right - 58))
|
||||
sy = RNG.randint(top + 16, bottom - 16)
|
||||
draw.arc(
|
||||
(sx, sy - RNG.randint(6, 16), sx + RNG.randint(38, 86), sy + RNG.randint(10, 22)),
|
||||
192,
|
||||
350,
|
||||
fill=(150, 178, 181, RNG.randint(32, 62)),
|
||||
width=RNG.choice([2, 2, 3]),
|
||||
)
|
||||
|
||||
if kind == "forest" and (x >= 76 or y >= 82 or RNG.random() < 0.2):
|
||||
for _ in range(2):
|
||||
bx = cx + RNG.randint(-52, 52)
|
||||
by = cy + RNG.randint(-44, 44)
|
||||
draw.polygon(organic_ellipse_points(bx, by, RNG.randint(22, 46), RNG.randint(14, 32), 12, 0.32), fill=(15, 44, 30, RNG.randint(40, 70)))
|
||||
if RNG.random() < 0.38:
|
||||
draw.line(
|
||||
(cx + RNG.randint(-22, 20), cy + RNG.randint(8, 42), cx + RNG.randint(-18, 24), cy + RNG.randint(34, 62)),
|
||||
fill=(58, 39, 25, 86),
|
||||
width=RNG.randint(4, 7),
|
||||
)
|
||||
|
||||
if kind in {"camp", "fort"} and RNG.random() < 0.45:
|
||||
for _ in range(2):
|
||||
px = RNG.randint(left + 22, right - 24)
|
||||
py = RNG.randint(top + 26, bottom - 22)
|
||||
draw.rectangle((px - 10, py - 7, px + 12, py + 8), fill=(137, 91, 47, RNG.randint(84, 122)))
|
||||
if (x + y) % 7 == 0:
|
||||
pole_x = cx + RNG.randint(-36, 36)
|
||||
pole_y = cy + RNG.randint(-34, 22)
|
||||
sharp_draw.line((pole_x, pole_y + 34, pole_x, pole_y - 36), fill=(70, 47, 29, 152), width=max(3, TILE // 40))
|
||||
sharp_draw.polygon(
|
||||
[(pole_x + 4, pole_y - 34), (pole_x + 42, pole_y - 22), (pole_x + 4, pole_y - 10)],
|
||||
fill=(174, 58, 45, 148),
|
||||
)
|
||||
|
||||
if kind == "village" and RNG.random() < 0.58:
|
||||
lx = cx + RNG.randint(-36, 36)
|
||||
ly = cy + RNG.randint(-22, 28)
|
||||
draw.ellipse((lx - 9, ly - 9, lx + 9, ly + 9), fill=(229, 168, 76, 90))
|
||||
draw.arc((lx - 18, ly - 42, lx + 28, ly + 10), 205, 330, fill=(84, 74, 55, 44), width=2)
|
||||
|
||||
for start, end, width in [
|
||||
((14, 103), (25, 101), 120),
|
||||
((25, 101), (46, 98), 118),
|
||||
((36, 104), (64, 96), 108),
|
||||
((64, 96), (90, 84), 96),
|
||||
]:
|
||||
paint_path_segment(draw, center(*start), center(*end), width, (190, 154, 91), 52, 14)
|
||||
paint_path_segment(draw, center(*start), center(*end), max(34, width // 2), (226, 190, 123), 30, 9)
|
||||
|
||||
for marker in [(20, 102), (38, 96), (46, 98), (64, 64), (86, 74)]:
|
||||
draw_wuzhang_ledger_tent(sharp_draw, *marker)
|
||||
|
||||
for marker in [(14, 103), (25, 101), (36, 104), (57, 101), (63, 106)]:
|
||||
draw_wuzhang_supply_cart(sharp_draw, *marker)
|
||||
|
||||
for beacon in [(84, 78), (90, 84), (103, 33), (121, 34), (124, 52)]:
|
||||
draw_wuzhang_beacon(sharp_draw, *beacon)
|
||||
|
||||
canvas.alpha_composite(details.filter(ImageFilter.GaussianBlur(0.16)))
|
||||
canvas.alpha_composite(sharp.filter(ImageFilter.GaussianBlur(0.035)))
|
||||
|
||||
|
||||
def add_subtle_grid_and_grade(canvas: Image.Image, terrain: list[list[str]]) -> Image.Image:
|
||||
width, height = canvas.size
|
||||
overlay = Image.new("RGBA", canvas.size, (0, 0, 0, 0))
|
||||
@@ -1415,6 +1589,8 @@ def build_map(config: dict[str, object]) -> Image.Image:
|
||||
add_route_and_battlefield_details(canvas, terrain, units)
|
||||
if config.get("detail_profile") == "weishui-camps":
|
||||
add_weishui_camps_details(canvas, terrain)
|
||||
elif config.get("detail_profile") == "wuzhang-final":
|
||||
add_wuzhang_final_details(canvas, terrain)
|
||||
return add_subtle_grid_and_grade(canvas, terrain)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user