Improve sixty-fifth battle map detail
This commit is contained in:
@@ -658,6 +658,8 @@ MAP_CONFIGS = {
|
||||
"before": BATTLE_IMAGE_DIR / "sixty-fifth-battle-map.svg",
|
||||
"seed": 241344,
|
||||
"tile_size": VAST_LARGE_MAP_TILE_SIZE,
|
||||
"output_tile_size": DESKTOP_RUNTIME_MAP_TILE_SIZE,
|
||||
"detail_profile": "weishui-northbank",
|
||||
},
|
||||
"sixty-sixth": {
|
||||
"slug": "sixty-sixth-battle-map",
|
||||
@@ -1377,6 +1379,205 @@ def add_weishui_camps_details(canvas: Image.Image, terrain: list[list[str]]) ->
|
||||
canvas.alpha_composite(sharp.filter(ImageFilter.GaussianBlur(0.04)))
|
||||
|
||||
|
||||
def draw_northbank_ferry(draw: ImageDraw.ImageDraw, x: int, y: int) -> None:
|
||||
cx, cy = center(x, y)
|
||||
plank_w = int(TILE * 0.58)
|
||||
plank_h = int(TILE * 0.12)
|
||||
for row, offset_y in enumerate((-int(TILE * 0.18), 0, int(TILE * 0.18))):
|
||||
left = cx - plank_w + row * 5
|
||||
top = cy + offset_y - plank_h
|
||||
right = cx + plank_w + row * 5
|
||||
bottom = cy + offset_y + plank_h
|
||||
draw.rounded_rectangle(
|
||||
(left, top, right, bottom),
|
||||
radius=max(3, TILE // 36),
|
||||
fill=(116, 78, 43, 132),
|
||||
)
|
||||
draw.line((left + 8, top + 4, right - 10, bottom - 4), fill=(218, 176, 99, 78), width=2)
|
||||
for offset in (-int(TILE * 0.45), int(TILE * 0.46)):
|
||||
draw.line(
|
||||
(cx + offset, cy - int(TILE * 0.38), cx + offset + int(TILE * 0.08), cy + int(TILE * 0.4)),
|
||||
fill=(65, 45, 32, 138),
|
||||
width=max(3, TILE // 34),
|
||||
)
|
||||
draw.arc(
|
||||
(cx - int(TILE * 0.7), cy - int(TILE * 0.34), cx + int(TILE * 0.7), cy + int(TILE * 0.38)),
|
||||
195,
|
||||
345,
|
||||
fill=(189, 212, 205, 64),
|
||||
width=max(3, TILE // 34),
|
||||
)
|
||||
|
||||
|
||||
def draw_northbank_barricade(draw: ImageDraw.ImageDraw, x: int, y: int) -> None:
|
||||
cx, cy = center(x, y)
|
||||
for offset in (-int(TILE * 0.36), -int(TILE * 0.12), int(TILE * 0.12), int(TILE * 0.36)):
|
||||
draw.line(
|
||||
(cx + offset - int(TILE * 0.16), cy + int(TILE * 0.22), cx + offset + int(TILE * 0.14), cy - int(TILE * 0.24)),
|
||||
fill=(66, 43, 27, 146),
|
||||
width=max(4, TILE // 30),
|
||||
)
|
||||
draw.line(
|
||||
(cx + offset - int(TILE * 0.14), cy - int(TILE * 0.2), cx + offset + int(TILE * 0.16), cy + int(TILE * 0.2)),
|
||||
fill=(91, 61, 36, 130),
|
||||
width=max(3, TILE // 36),
|
||||
)
|
||||
draw.line(
|
||||
(cx - int(TILE * 0.58), cy + int(TILE * 0.12), cx + int(TILE * 0.58), cy + int(TILE * 0.08)),
|
||||
fill=(39, 31, 23, 88),
|
||||
width=max(3, TILE // 32),
|
||||
)
|
||||
|
||||
|
||||
def draw_northbank_command_post(draw: ImageDraw.ImageDraw, x: int, y: int) -> None:
|
||||
cx, cy = center(x, y)
|
||||
base_y = cy + int(TILE * 0.24)
|
||||
tent_w = int(TILE * 0.58)
|
||||
tent_h = int(TILE * 0.42)
|
||||
draw.polygon(
|
||||
[(cx - tent_w, base_y), (cx - int(TILE * 0.04), cy - tent_h), (cx + tent_w, base_y)],
|
||||
fill=(116, 79, 42, 152),
|
||||
)
|
||||
draw.polygon(
|
||||
[(cx - int(tent_w * 0.62), base_y - 3), (cx - int(TILE * 0.04), cy - int(tent_h * 0.7)), (cx + int(tent_w * 0.2), base_y - 3)],
|
||||
fill=(204, 158, 78, 132),
|
||||
)
|
||||
draw.line((cx - tent_w, base_y, cx + tent_w, base_y), fill=(56, 38, 25, 138), width=max(3, TILE // 34))
|
||||
for crate_x in (cx - int(TILE * 0.44), cx + int(TILE * 0.42)):
|
||||
crate_y = cy + int(TILE * 0.38)
|
||||
draw.rectangle(
|
||||
(crate_x - int(TILE * 0.14), crate_y - int(TILE * 0.1), crate_x + int(TILE * 0.14), crate_y + int(TILE * 0.1)),
|
||||
fill=(132, 85, 42, 132),
|
||||
)
|
||||
draw.line(
|
||||
(crate_x - int(TILE * 0.14), crate_y - int(TILE * 0.09), crate_x + int(TILE * 0.14), crate_y + int(TILE * 0.09)),
|
||||
fill=(217, 172, 91, 78),
|
||||
width=2,
|
||||
)
|
||||
pole_x = cx + int(TILE * 0.52)
|
||||
pole_y = cy - int(TILE * 0.04)
|
||||
draw.line((pole_x, pole_y + int(TILE * 0.42), pole_x, pole_y - int(TILE * 0.4)), fill=(65, 43, 27, 154), width=max(3, TILE // 40))
|
||||
draw.polygon(
|
||||
[(pole_x + 4, pole_y - int(TILE * 0.39)), (pole_x + int(TILE * 0.4), pole_y - int(TILE * 0.27)), (pole_x + 4, pole_y - int(TILE * 0.13))],
|
||||
fill=(176, 55, 42, 144),
|
||||
)
|
||||
|
||||
|
||||
def add_weishui_northbank_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 == "river":
|
||||
for _ in range(5):
|
||||
sx = RNG.randint(left + 12, max(left + 13, right - 72))
|
||||
sy = RNG.randint(top + 14, bottom - 14)
|
||||
span = RNG.randint(max(38, TILE // 3), max(48, TILE))
|
||||
draw.arc(
|
||||
(sx, sy - RNG.randint(8, 20), sx + span, sy + RNG.randint(10, 24)),
|
||||
190,
|
||||
352,
|
||||
fill=(*jitter(PALETTE["water_light"], 12), RNG.randint(42, 86)),
|
||||
width=RNG.choice([2, 2, 3]),
|
||||
)
|
||||
if terrain_at(terrain, x, y - 1) != "river" or terrain_at(terrain, x, y + 1) != "river":
|
||||
bank_y = top + RNG.randint(8, TILE - 8)
|
||||
for _ in range(5):
|
||||
px = RNG.randint(left + 8, right - 8)
|
||||
reed = RNG.choice([(88, 111, 58, 82), (157, 134, 75, 72), (42, 82, 51, 72)])
|
||||
draw.line((px, bank_y + RNG.randint(-5, 6), px + RNG.randint(-9, 8), bank_y - RNG.randint(10, 26)), fill=reed, width=2)
|
||||
|
||||
if kind == "road" and (y >= 47 or x >= 76) and RNG.random() < 0.64:
|
||||
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=(93, 65, 39, RNG.randint(42, 68)),
|
||||
width=RNG.choice([2, 3]),
|
||||
)
|
||||
if RNG.random() < 0.3:
|
||||
for step in range(3):
|
||||
px = left + 22 + 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=(57, 43, 31, 54))
|
||||
|
||||
if kind == "forest" and (y >= 60 or x >= 88):
|
||||
for _ in range(3):
|
||||
bx = cx + RNG.randint(-54, 56)
|
||||
by = cy + RNG.randint(-46, 46)
|
||||
draw.polygon(
|
||||
organic_ellipse_points(bx, by, RNG.randint(18, 42), RNG.randint(12, 30), 14, 0.34),
|
||||
fill=(13, 43, 28, RNG.randint(42, 74)),
|
||||
)
|
||||
if RNG.random() < 0.4:
|
||||
draw.line(
|
||||
(cx + RNG.randint(-22, 22), cy + RNG.randint(8, 38), cx + RNG.randint(-20, 24), cy + RNG.randint(34, 58)),
|
||||
fill=(59, 39, 24, 88),
|
||||
width=RNG.randint(4, 7),
|
||||
)
|
||||
|
||||
if kind in {"hill", "cliff"} and RNG.random() < 0.55:
|
||||
for _ in range(2):
|
||||
px = RNG.randint(left + 12, right - 12)
|
||||
py = RNG.randint(top + 12, bottom - 12)
|
||||
draw.line(
|
||||
(px - RNG.randint(12, 38), py + RNG.randint(-5, 8), px + RNG.randint(22, 54), py + RNG.randint(-9, 9)),
|
||||
fill=(72, 62, 46, RNG.randint(30, 56)),
|
||||
width=RNG.choice([2, 2, 3]),
|
||||
)
|
||||
|
||||
if kind in {"camp", "fort"} and RNG.random() < 0.52:
|
||||
for _ in range(2):
|
||||
px = RNG.randint(left + 24, right - 26)
|
||||
py = RNG.randint(top + 26, bottom - 24)
|
||||
draw.rectangle((px - 10, py - 7, px + 12, py + 8), fill=(140, 91, 45, RNG.randint(86, 126)))
|
||||
if (x + y) % 8 == 0:
|
||||
pole_x = cx + RNG.randint(-38, 38)
|
||||
pole_y = cy + RNG.randint(-34, 24)
|
||||
sharp_draw.line((pole_x, pole_y + 36, pole_x, pole_y - 38), fill=(80, 52, 31, 156), width=max(3, TILE // 38))
|
||||
sharp_draw.polygon(
|
||||
[(pole_x + 4, pole_y - 37), (pole_x + 42, pole_y - 25), (pole_x + 4, pole_y - 13)],
|
||||
fill=(190, 150, 49, 150),
|
||||
)
|
||||
|
||||
if kind == "village" and RNG.random() < 0.48:
|
||||
lx = cx + RNG.randint(-36, 36)
|
||||
ly = cy + RNG.randint(-24, 28)
|
||||
draw.ellipse((lx - 9, ly - 9, lx + 9, ly + 9), fill=(229, 168, 76, 84))
|
||||
draw.arc((lx - 18, ly - 42, lx + 28, ly + 10), 205, 330, fill=(84, 74, 55, 42), width=2)
|
||||
|
||||
for start, end, width in [
|
||||
((20, 95), (43, 92), 116),
|
||||
((43, 92), (63, 84), 112),
|
||||
((63, 84), (90, 72), 106),
|
||||
((90, 72), (112, 66), 98),
|
||||
((104, 58), (122, 47), 92),
|
||||
]:
|
||||
paint_path_segment(draw, center(*start), center(*end), width, (198, 162, 94), 48, 14)
|
||||
paint_path_segment(draw, center(*start), center(*end), max(34, width // 2), (230, 194, 126), 28, 9)
|
||||
|
||||
for ferry in [(28, 39), (58, 39), (88, 40), (105, 48)]:
|
||||
draw_northbank_ferry(sharp_draw, *ferry)
|
||||
|
||||
for post in [(31, 96), (63, 83), (82, 55), (99, 69), (113, 61)]:
|
||||
draw_northbank_command_post(sharp_draw, *post)
|
||||
|
||||
for barrier in [(91, 74), (103, 63), (113, 52), (121, 47), (124, 38), (126, 28)]:
|
||||
draw_northbank_barricade(sharp_draw, *barrier)
|
||||
|
||||
for beacon in [(97, 72), (112, 66), (121, 55), (124, 42), (126, 31)]:
|
||||
draw_weishui_beacon(sharp_draw, *beacon)
|
||||
|
||||
canvas.alpha_composite(details.filter(ImageFilter.GaussianBlur(0.16)))
|
||||
canvas.alpha_composite(sharp.filter(ImageFilter.GaussianBlur(0.035)))
|
||||
|
||||
|
||||
def draw_wuzhang_ledger_tent(draw: ImageDraw.ImageDraw, x: int, y: int) -> None:
|
||||
cx, cy = center(x, y)
|
||||
tent_w = int(TILE * 0.62)
|
||||
@@ -1589,6 +1790,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") == "weishui-northbank":
|
||||
add_weishui_northbank_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