diff --git a/scripts/generate-action-sprites.py b/scripts/generate-action-sprites.py index 5498d1b..b7e7283 100644 --- a/scripts/generate-action-sprites.py +++ b/scripts/generate-action-sprites.py @@ -1,11 +1,13 @@ from __future__ import annotations +import math from pathlib import Path from PIL import Image, ImageChops, ImageDraw, ImageEnhance, ImageFilter FRAME_SIZE = 313 +PALETTE_COLORS = 192 ROOT = Path(__file__).resolve().parents[1] UNIT_DIR = ROOT / "src" / "assets" / "images" / "units" @@ -47,12 +49,13 @@ def main() -> None: continue sheet = Image.open(source).convert("RGBA") output = Image.new("RGBA", (FRAME_SIZE * len(ACTION_ORDER), FRAME_SIZE * len(DIRECTIONS)), (0, 0, 0, 0)) + profile = action_profile(source.stem) for row, direction in enumerate(DIRECTIONS): for col, action in enumerate(ACTION_ORDER): frame = crop_frame(sheet, row, FRAME_BY_ACTION[action]) if action == "attack": - action_frame = make_attack(frame, direction) + action_frame = make_attack(frame, direction, profile) elif action == "strategy": action_frame = make_strategy(frame, direction) elif action == "item": @@ -61,7 +64,26 @@ def main() -> None: action_frame = make_hurt(frame, direction) output.alpha_composite(action_frame, (col * FRAME_SIZE, row * FRAME_SIZE)) - output.save(source.with_name(f"{source.stem}-actions.png")) + save_unit_png(output, source.with_name(f"{source.stem}-actions.png")) + + +def save_unit_png(image: Image.Image, path: Path) -> None: + image = image.convert("RGBA") + indexed = image.quantize(colors=PALETTE_COLORS, method=Image.Quantize.FASTOCTREE, dither=Image.Dither.NONE) + indexed.save(path, optimize=True) + + +def action_profile(stem: str) -> str: + lower = stem.lower() + if any(token in lower for token in ("archer", "crossbow", "longbow")): + return "ranged" + if any(token in lower for token in ("strategist", "shaman", "zhuge", "sima", "fa-zheng", "ma-liang", "yi-ji")): + return "strategy" + if any(token in lower for token in ("cavalry", "ma-chao", "ma-dai", "lu-bu")): + return "cavalry" + if any(token in lower for token in ("spearman", "guan-yu", "zhang-fei", "jiang-wei", "huang-zhong", "zhao-yun")): + return "polearm" + return "blade" def crop_frame(sheet: Image.Image, row: int, col: int) -> Image.Image: @@ -91,7 +113,17 @@ def underlay(frame: Image.Image, color: tuple[int, int, int, int], blur: int, ex return result -def make_attack(frame: Image.Image, direction: str) -> Image.Image: +def make_attack(frame: Image.Image, direction: str, profile: str) -> Image.Image: + if profile == "ranged": + return make_ranged_attack(frame, direction) + if profile == "strategy": + return make_strategy_attack(frame, direction) + if profile == "cavalry": + return make_charge_attack(frame, direction) + return make_blade_attack(frame, direction, profile) + + +def make_blade_attack(frame: Image.Image, direction: str, profile: str) -> Image.Image: dx, dy = FORWARD[direction] body = offset_subject(frame, dx, dy) body = underlay(body, (255, 224, 120, 92), 4, 1) @@ -107,9 +139,10 @@ def make_attack(frame: Image.Image, direction: str) -> Image.Image: else: points = [(74, 202), (166, 112), (248, 198)] - draw.line(points, fill=(255, 247, 214, 218), width=15, joint="curve") - draw.line(points, fill=(98, 210, 255, 178), width=6, joint="curve") - draw.line(points, fill=(255, 178, 86, 158), width=2, joint="curve") + width = 18 if profile == "polearm" else 15 + draw.line(points, fill=(255, 247, 214, 224), width=width, joint="curve") + draw.line(points, fill=(98, 210, 255, 184), width=7 if profile == "polearm" else 6, joint="curve") + draw.line(points, fill=(255, 178, 86, 166), width=3 if profile == "polearm" else 2, joint="curve") slash = slash.filter(ImageFilter.GaussianBlur(0.45)) result = Image.new("RGBA", frame.size, (0, 0, 0, 0)) @@ -118,6 +151,105 @@ def make_attack(frame: Image.Image, direction: str) -> Image.Image: return result +def make_ranged_attack(frame: Image.Image, direction: str) -> Image.Image: + dx, dy = FORWARD[direction] + body = underlay(offset_subject(frame, round(dx * 0.45), round(dy * 0.45)), (255, 221, 96, 78), 4, 1) + effect = Image.new("RGBA", frame.size, (0, 0, 0, 0)) + draw = ImageDraw.Draw(effect) + + if direction == "east": + shaft = (134, 124, 282, 102) + tip = [(282, 102), (266, 94), (269, 111)] + fletch = ((132, 124), (111, 118), (115, 132)) + bow = (84, 74, 158, 198, -54, 62) + elif direction == "west": + shaft = (179, 124, 31, 102) + tip = [(31, 102), (47, 94), (44, 111)] + fletch = ((181, 124), (202, 118), (198, 132)) + bow = (155, 74, 229, 198, 172, 286) + elif direction == "south": + shaft = (140, 118, 176, 274) + tip = [(176, 274), (163, 259), (184, 257)] + fletch = ((140, 118), (131, 97), (150, 101)) + bow = (91, 80, 213, 176, 24, 145) + else: + shaft = (172, 190, 136, 34) + tip = [(136, 34), (129, 51), (148, 48)] + fletch = ((172, 190), (182, 211), (163, 207)) + bow = (91, 128, 213, 224, 205, 326) + + draw.arc(bow[:4], start=bow[4], end=bow[5], fill=(250, 214, 102, 190), width=5) + draw.line(shaft, fill=(255, 252, 225, 224), width=7) + draw.line(shaft, fill=(92, 210, 255, 150), width=3) + draw.polygon(tip, fill=(255, 245, 207, 230)) + draw.polygon(fletch, fill=(255, 196, 88, 196)) + for offset in (-14, 14): + if direction in ("east", "west"): + draw.line((shaft[0], shaft[1] + offset, shaft[2], shaft[3] + offset), fill=(255, 230, 128, 70), width=2) + else: + draw.line((shaft[0] + offset, shaft[1], shaft[2] + offset, shaft[3]), fill=(255, 230, 128, 70), width=2) + + result = Image.new("RGBA", frame.size, (0, 0, 0, 0)) + result.alpha_composite(effect.filter(ImageFilter.GaussianBlur(0.25))) + result.alpha_composite(body) + return result + + +def make_strategy_attack(frame: Image.Image, direction: str) -> Image.Image: + dx, dy = FORWARD[direction] + body = underlay(offset_subject(frame, round(dx * 0.35), round(dy * 0.35)), (92, 180, 255, 92), 6, 2) + effect = Image.new("RGBA", frame.size, (0, 0, 0, 0)) + draw = ImageDraw.Draw(effect) + center = (156 + round(dx * 0.45), 150 + round(dy * 0.45)) + + for radius, alpha in ((78, 82), (48, 126), (22, 180)): + draw.ellipse( + (center[0] - radius, center[1] - radius, center[0] + radius, center[1] + radius), + outline=(108, 210, 255, alpha), + width=5, + ) + for angle in range(0, 360, 45): + x = center[0] + int(74 * math.cos(math.radians(angle))) + y = center[1] + int(74 * math.sin(math.radians(angle))) + draw.polygon([(x, y - 8), (x + 5, y), (x, y + 8), (x - 5, y)], fill=(255, 230, 132, 156)) + + result = Image.new("RGBA", frame.size, (0, 0, 0, 0)) + result.alpha_composite(effect.filter(ImageFilter.GaussianBlur(0.35))) + result.alpha_composite(body) + return result + + +def make_charge_attack(frame: Image.Image, direction: str) -> Image.Image: + dx, dy = FORWARD[direction] + body = underlay(offset_subject(frame, round(dx * 1.2), round(dy * 1.2)), (255, 188, 84, 88), 5, 2) + effect = Image.new("RGBA", frame.size, (0, 0, 0, 0)) + draw = ImageDraw.Draw(effect) + + if direction == "east": + lines = [((38, 96), (188, 82)), ((28, 154), (214, 146)), ((56, 222), (180, 204))] + spear = (128, 132, 288, 98) + elif direction == "west": + lines = [((275, 96), (125, 82)), ((285, 154), (99, 146)), ((257, 222), (133, 204))] + spear = (185, 132, 25, 98) + elif direction == "south": + lines = [((86, 28), (98, 188)), ((154, 24), (148, 214)), ((226, 52), (205, 180))] + spear = (138, 120, 180, 286) + else: + lines = [((86, 285), (98, 125)), ((154, 289), (148, 99)), ((226, 261), (205, 133))] + spear = (175, 192, 133, 26) + + for line in lines: + draw.line(line, fill=(255, 204, 102, 118), width=8) + draw.line(line, fill=(255, 245, 210, 120), width=3) + draw.line(spear, fill=(255, 250, 218, 218), width=8) + draw.line(spear, fill=(255, 152, 58, 166), width=3) + + result = Image.new("RGBA", frame.size, (0, 0, 0, 0)) + result.alpha_composite(effect.filter(ImageFilter.GaussianBlur(0.3))) + result.alpha_composite(body) + return result + + def make_strategy(frame: Image.Image, direction: str) -> Image.Image: aura = Image.new("RGBA", frame.size, (0, 0, 0, 0)) draw = ImageDraw.Draw(aura) diff --git a/scripts/harmonize-unit-sprites.py b/scripts/harmonize-unit-sprites.py new file mode 100644 index 0000000..3a91a84 --- /dev/null +++ b/scripts/harmonize-unit-sprites.py @@ -0,0 +1,436 @@ +from __future__ import annotations + +import sys +from dataclasses import dataclass +from pathlib import Path + +from PIL import Image, ImageChops, ImageEnhance, ImageFilter, ImageOps + + +FRAME_SIZE = 313 +GRID_SIZE = 4 +PALETTE_COLORS = 192 +ROOT = Path(__file__).resolve().parents[1] +UNIT_DIR = ROOT / "src" / "assets" / "images" / "units" +REFERENCE_SHEET = UNIT_DIR / "unit-cao-cao.png" + + +@dataclass(frozen=True) +class FrameTarget: + center_x: float + bottom_y: int + width: int + height: int + + +@dataclass(frozen=True) +class UnitStyle: + scale: float + saturation: float + contrast: float + brightness: float + edge_warmth: float + rim_color: tuple[int, int, int] + + +@dataclass(frozen=True) +class TemplateVariant: + prefix: str + template: str + tint: tuple[int, int, int] + strength: float + saturation: float = 1.0 + brightness: float = 1.0 + + +TEMPLATE_VARIANTS = ( + TemplateVariant("unit-shu-archer", "unit-rebel-archer", (54, 132, 82), 0.2, 1.05), + TemplateVariant("unit-shu-cavalry", "unit-ma-chao", (58, 126, 78), 0.18, 1.02), + TemplateVariant("unit-shu-infantry", "unit-rebel", (56, 132, 78), 0.24, 1.06), + TemplateVariant("unit-shu-officer", "unit-guan-yu", (58, 132, 82), 0.16, 1.02), + TemplateVariant("unit-shu-spearman", "unit-zhang-fei", (70, 122, 86), 0.14, 1.02), + TemplateVariant("unit-shu-strategist", "unit-zhuge-liang", (74, 138, 102), 0.16, 1.03, 1.02), + TemplateVariant("unit-wei-archer", "unit-rebel-archer", (70, 84, 118), 0.24, 1.02, 0.98), + TemplateVariant("unit-wei-cavalry", "unit-ma-chao", (72, 78, 114), 0.24, 1.0, 0.98), + TemplateVariant("unit-wei-infantry", "unit-rebel", (68, 78, 112), 0.26, 1.0, 0.98), + TemplateVariant("unit-wei-officer", "unit-cao-cao", (76, 74, 116), 0.18, 1.0, 0.98), + TemplateVariant("unit-wei-strategist", "unit-zhuge-liang", (78, 92, 140), 0.22, 1.0, 0.98), + TemplateVariant("unit-wu-archer", "unit-rebel-archer", (42, 112, 120), 0.24, 1.05), + TemplateVariant("unit-wu-cavalry", "unit-ma-chao", (42, 110, 118), 0.22, 1.04), + TemplateVariant("unit-wu-infantry", "unit-rebel", (42, 116, 118), 0.26, 1.05), + TemplateVariant("unit-wu-officer", "unit-lu-meng", (46, 116, 128), 0.18, 1.02), + TemplateVariant("unit-wu-strategist", "unit-zhuge-liang", (42, 118, 132), 0.22, 1.04), + TemplateVariant("unit-nanman-infantry", "unit-rebel", (118, 112, 46), 0.28, 1.08), + TemplateVariant("unit-nanman-officer", "unit-meng-huo", (126, 100, 48), 0.2, 1.07), + TemplateVariant("unit-nanman-shaman", "unit-zhuge-liang", (118, 114, 48), 0.26, 1.08), +) + + +def main() -> None: + generic_only = "--generic-only" in sys.argv + cleanup_only = "--cleanup-only" in sys.argv + optimize_only = "--optimize-only" in sys.argv + selected_modes = sum(1 for enabled in (generic_only, cleanup_only, optimize_only) if enabled) + if selected_modes > 1: + raise ValueError("Use only one of --generic-only, --cleanup-only, or --optimize-only") + + if generic_only: + sheets = load_current_base_sheets() + write_template_variants(sheets) + return + + if cleanup_only: + cleanup_current_base_sheets() + return + + if optimize_only: + optimize_current_unit_pngs() + return + + reference = Image.open(REFERENCE_SHEET).convert("RGBA") + targets = reference_targets(reference) + generated = 0 + generated_sheets: dict[str, Image.Image] = {} + + for source in sorted(UNIT_DIR.glob("unit-*.png")): + if source.name.endswith("-actions.png"): + continue + + sheet = Image.open(source).convert("RGBA") + if sheet.size != (FRAME_SIZE * GRID_SIZE, FRAME_SIZE * GRID_SIZE): + raise ValueError(f"{source.name} has unexpected size {sheet.size}") + + if source.name == REFERENCE_SHEET.name: + output = ensure_reference_finish(sheet) + else: + output = harmonize_sheet(sheet, source.stem, targets) + + save_unit_png(output, source) + generated_sheets[source.stem] = output + generated += 1 + + write_template_variants(generated_sheets) + print(f"Harmonized {generated} unit sheets against {REFERENCE_SHEET.name}") + + +def load_current_base_sheets() -> dict[str, Image.Image]: + sheets: dict[str, Image.Image] = {} + for source in sorted(UNIT_DIR.glob("unit-*.png")): + if source.name.endswith("-actions.png"): + continue + sheets[source.stem] = Image.open(source).convert("RGBA") + return sheets + + +def cleanup_current_base_sheets() -> None: + cleaned = 0 + for source in sorted(UNIT_DIR.glob("unit-*.png")): + if source.name.endswith("-actions.png"): + continue + sheet = Image.open(source).convert("RGBA") + output = Image.new("RGBA", sheet.size, (0, 0, 0, 0)) + for row in range(GRID_SIZE): + for col in range(GRID_SIZE): + frame = crop_frame(sheet, row, col) + output.alpha_composite(remove_detached_artifacts(clean_alpha(frame)), (col * FRAME_SIZE, row * FRAME_SIZE)) + save_unit_png(output, source) + cleaned += 1 + print(f"Cleaned detached artifacts in {cleaned} unit sheets") + + +def optimize_current_unit_pngs() -> None: + optimized = 0 + for source in sorted(UNIT_DIR.glob("unit-*.png")): + image = Image.open(source).convert("RGBA") + save_unit_png(image, source) + optimized += 1 + print(f"Optimized {optimized} unit PNGs with {PALETTE_COLORS}-color palettes") + + +def write_template_variants(sheets: dict[str, Image.Image]) -> None: + written = 0 + for source in sorted(UNIT_DIR.glob("unit-*.png")): + if source.name.endswith("-actions.png"): + continue + variant = template_variant_for(source.stem) + if not variant: + continue + template = sheets.get(variant.template) + if not template: + raise ValueError(f"Missing template sheet {variant.template} for {source.stem}") + output = tint_template(template, variant) + save_unit_png(output, source) + sheets[source.stem] = output + written += 1 + print(f"Wrote {written} high-detail generic unit variants") + + +def save_unit_png(image: Image.Image, path: Path) -> None: + image = image.convert("RGBA") + indexed = image.quantize(colors=PALETTE_COLORS, method=Image.Quantize.FASTOCTREE, dither=Image.Dither.NONE) + indexed.save(path, optimize=True) + + +def template_variant_for(stem: str) -> TemplateVariant | None: + for variant in TEMPLATE_VARIANTS: + if stem.startswith(variant.prefix) and stem != variant.template: + return variant + return None + + +def tint_template(sheet: Image.Image, variant: TemplateVariant) -> Image.Image: + rgb = sheet.convert("RGB") + alpha = sheet.getchannel("A") + luma = ImageOps.grayscale(rgb) + dark = tuple(max(0, round(channel * 0.2)) for channel in variant.tint) + light = tuple(min(255, round(channel * 1.18 + 84)) for channel in variant.tint) + colorized = ImageOps.colorize(luma, black=dark, white=light, mid=variant.tint) + mask = alpha.point(lambda value: min(255, round(value * variant.strength))) + mixed = Image.composite(colorized, rgb, mask) + mixed = ImageEnhance.Color(mixed).enhance(variant.saturation) + mixed = ImageEnhance.Brightness(mixed).enhance(variant.brightness) + return Image.merge("RGBA", (*mixed.split(), alpha)) + + +def reference_targets(reference: Image.Image) -> list[list[FrameTarget]]: + rows: list[list[FrameTarget]] = [] + for row in range(GRID_SIZE): + target_row: list[FrameTarget] = [] + for col in range(GRID_SIZE): + frame = crop_frame(reference, row, col) + bbox = alpha_bbox(frame) + if not bbox: + target_row.append(FrameTarget(FRAME_SIZE / 2, FRAME_SIZE - 8, 172, 282)) + continue + left, top, right, bottom = bbox + target_row.append( + FrameTarget( + center_x=(left + right) / 2, + bottom_y=min(FRAME_SIZE - 2, bottom), + width=right - left, + height=bottom - top, + ) + ) + rows.append(target_row) + return rows + + +def harmonize_sheet(sheet: Image.Image, stem: str, targets: list[list[FrameTarget]]) -> Image.Image: + output = Image.new("RGBA", sheet.size, (0, 0, 0, 0)) + style = style_for_unit(stem) + + for row in range(GRID_SIZE): + for col in range(GRID_SIZE): + frame = crop_frame(sheet, row, col) + target = targets[row][col] + normalized = normalize_frame(frame, target, style) + output.alpha_composite(normalized, (col * FRAME_SIZE, row * FRAME_SIZE)) + + return output + + +def crop_frame(sheet: Image.Image, row: int, col: int) -> Image.Image: + return sheet.crop((col * FRAME_SIZE, row * FRAME_SIZE, (col + 1) * FRAME_SIZE, (row + 1) * FRAME_SIZE)).copy() + + +def normalize_frame(frame: Image.Image, target: FrameTarget, style: UnitStyle) -> Image.Image: + frame = remove_detached_artifacts(clean_alpha(frame)) + bbox = alpha_bbox(frame) + if not bbox: + return frame + + subject = frame.crop(bbox) + width, height = subject.size + fit_width = FRAME_SIZE - 16 + fit_height = FRAME_SIZE - 8 + target_height = min(fit_height, max(218, target.height * style.scale)) + target_width = min(fit_width, max(132, target.width * style.scale)) + + scale = 1.0 + if height > target_height * 1.06 or width > fit_width: + scale = min(target_height / height, fit_width / width) + elif height < target_height * 0.9: + scale = min(target_height / height, 1.13) + + if width * scale > target_width * 1.22: + scale = min(scale, (target_width * 1.22) / width) + + new_width = max(1, min(fit_width, round(width * scale))) + new_height = max(1, min(fit_height, round(height * scale))) + if (new_width, new_height) != subject.size: + subject = subject.resize((new_width, new_height), Image.Resampling.LANCZOS) + + subject = grade_subject(subject, style) + canvas = Image.new("RGBA", frame.size, (0, 0, 0, 0)) + left = round(target.center_x - new_width / 2) + top = round(target.bottom_y - new_height) + left = max(4, min(FRAME_SIZE - new_width - 4, left)) + top = max(0, min(FRAME_SIZE - new_height - 2, top)) + canvas.alpha_composite(subject, (left, top)) + return finish_frame(canvas, style) + + +def ensure_reference_finish(sheet: Image.Image) -> Image.Image: + style = style_for_unit("unit-cao-cao") + output = Image.new("RGBA", sheet.size, (0, 0, 0, 0)) + for row in range(GRID_SIZE): + for col in range(GRID_SIZE): + frame = crop_frame(sheet, row, col) + output.alpha_composite(finish_frame(remove_detached_artifacts(clean_alpha(frame)), style), (col * FRAME_SIZE, row * FRAME_SIZE)) + return output + + +def clean_alpha(image: Image.Image) -> Image.Image: + red, green, blue, alpha = image.split() + alpha = alpha.point(lambda value: 0 if value < 7 else (255 if value > 248 else value)) + return Image.merge("RGBA", (red, green, blue, alpha)) + + +def remove_detached_artifacts(image: Image.Image) -> Image.Image: + alpha = image.getchannel("A") + pixels = alpha.load() + width, height = alpha.size + visited = bytearray(width * height) + components: list[tuple[int, tuple[int, int, int, int], list[tuple[int, int]]]] = [] + + for y in range(height): + for x in range(width): + index = y * width + x + if visited[index] or pixels[x, y] <= 12: + continue + stack = [(x, y)] + visited[index] = 1 + points: list[tuple[int, int]] = [] + left = right = x + top = bottom = y + + while stack: + px, py = stack.pop() + points.append((px, py)) + left = min(left, px) + right = max(right, px) + top = min(top, py) + bottom = max(bottom, py) + for nx, ny in ((px + 1, py), (px - 1, py), (px, py + 1), (px, py - 1)): + if nx < 0 or nx >= width or ny < 0 or ny >= height: + continue + neighbor_index = ny * width + nx + if visited[neighbor_index] or pixels[nx, ny] <= 12: + continue + visited[neighbor_index] = 1 + stack.append((nx, ny)) + + components.append((len(points), (left, top, right + 1, bottom + 1), points)) + + if len(components) <= 1: + return image + + components.sort(key=lambda component: component[0], reverse=True) + largest_area, largest_bbox, _ = components[0] + keep_points: set[tuple[int, int]] = set(components[0][2]) + + for area, bbox, points in components[1:]: + distance = bbox_distance(largest_bbox, bbox) + if distance <= 3 or area >= largest_area * 0.18: + keep_points.update(points) + + cleaned_alpha = Image.new("L", image.size, 0) + cleaned_pixels = cleaned_alpha.load() + source_pixels = alpha.load() + for x, y in keep_points: + cleaned_pixels[x, y] = source_pixels[x, y] + + red, green, blue, _ = image.split() + return Image.merge("RGBA", (red, green, blue, cleaned_alpha)) + + +def bbox_distance(left_box: tuple[int, int, int, int], right_box: tuple[int, int, int, int]) -> int: + left_a, top_a, right_a, bottom_a = left_box + left_b, top_b, right_b, bottom_b = right_box + dx = max(left_a - right_b, left_b - right_a, 0) + dy = max(top_a - bottom_b, top_b - bottom_a, 0) + return max(dx, dy) + + +def alpha_bbox(image: Image.Image) -> tuple[int, int, int, int] | None: + alpha = image.getchannel("A") + return alpha.getbbox() + + +def grade_subject(subject: Image.Image, style: UnitStyle) -> Image.Image: + red, green, blue, alpha = subject.split() + rgb = Image.merge("RGB", (red, green, blue)) + rgb = ImageEnhance.Color(rgb).enhance(style.saturation) + rgb = ImageEnhance.Contrast(rgb).enhance(style.contrast) + rgb = ImageEnhance.Brightness(rgb).enhance(style.brightness) + poster = ImageOps.posterize(rgb, 6) + rgb = Image.blend(rgb, poster, 0.18) + rgb = rgb.filter(ImageFilter.UnsharpMask(radius=1.0, percent=120, threshold=3)) + return Image.merge("RGBA", (*rgb.split(), alpha)) + + +def finish_frame(frame: Image.Image, style: UnitStyle) -> Image.Image: + alpha = frame.getchannel("A") + outer_alpha = ImageChops.subtract(alpha.filter(ImageFilter.MaxFilter(7)), alpha) + ink_alpha = ImageChops.subtract(alpha.filter(ImageFilter.MaxFilter(3)), alpha) + glow_alpha = alpha.filter(ImageFilter.GaussianBlur(2.2)) + inner_alpha = ImageChops.subtract(alpha, alpha.filter(ImageFilter.MinFilter(3))) + + result = Image.new("RGBA", frame.size, (0, 0, 0, 0)) + result.alpha_composite(colored_mask(glow_alpha, (*style.rim_color, 34))) + result.alpha_composite(colored_mask(outer_alpha, (*style.rim_color, 160))) + result.alpha_composite(colored_mask(ink_alpha, (10, 8, 13, 185))) + + warm = colored_mask(inner_alpha, (214, 156, 58, round(34 * style.edge_warmth))) + result.alpha_composite(frame) + result.alpha_composite(warm) + return clean_alpha(result) + + +def colored_mask(alpha: Image.Image, color: tuple[int, int, int, int]) -> Image.Image: + red, green, blue, opacity = color + mask = alpha.point(lambda value: min(255, round(value * opacity / 255))) + layer = Image.new("RGBA", alpha.size, (red, green, blue, 0)) + layer.putalpha(mask) + return layer + + +def style_for_unit(stem: str) -> UnitStyle: + lower = stem.lower() + scale = 0.98 + saturation = 1.14 + contrast = 1.08 + brightness = 1.0 + edge_warmth = 1.0 + rim_color = (48, 12, 76) + + if any(token in lower for token in ("archer", "crossbow", "longbow")): + scale = 0.94 + saturation = 1.12 + elif any(token in lower for token in ("cavalry", "ma-chao", "ma-dai", "lu-bu")): + scale = 1.03 + contrast = 1.1 + elif any(token in lower for token in ("strategist", "shaman", "zhuge", "sima", "fa-zheng", "ma-liang", "yi-ji")): + scale = 0.93 + brightness = 1.03 + rim_color = (38, 22, 86) + elif any(token in lower for token in ("leader", "officer", "meng-huo", "guan-yu", "zhang-fei", "jiang-wei")): + scale = 1.01 + contrast = 1.1 + edge_warmth = 1.12 + + if "nanman" in lower or "meng-huo" in lower: + saturation = 1.18 + rim_color = (58, 18, 63) + elif "wu-" in lower or "lu-meng" in lower: + rim_color = (28, 18, 72) + elif "wei-" in lower or "cao-cao" in lower or "sima" in lower: + rim_color = (43, 10, 78) + elif "shu-" in lower or any(token in lower for token in ("liu-bei", "guan-yu", "zhang-fei", "zhao-yun")): + rim_color = (34, 24, 72) + + return UnitStyle(scale, saturation, contrast, brightness, edge_warmth, rim_color) + + +if __name__ == "__main__": + main() diff --git a/src/assets/images/units/unit-cao-cao-actions.png b/src/assets/images/units/unit-cao-cao-actions.png index fc607c6..fe04fe4 100644 Binary files a/src/assets/images/units/unit-cao-cao-actions.png and b/src/assets/images/units/unit-cao-cao-actions.png differ diff --git a/src/assets/images/units/unit-cao-cao.png b/src/assets/images/units/unit-cao-cao.png index 80fc3a1..698a429 100644 Binary files a/src/assets/images/units/unit-cao-cao.png and b/src/assets/images/units/unit-cao-cao.png differ diff --git a/src/assets/images/units/unit-fa-zheng-actions.png b/src/assets/images/units/unit-fa-zheng-actions.png index 568e7d6..35e00ac 100644 Binary files a/src/assets/images/units/unit-fa-zheng-actions.png and b/src/assets/images/units/unit-fa-zheng-actions.png differ diff --git a/src/assets/images/units/unit-fa-zheng.png b/src/assets/images/units/unit-fa-zheng.png index 124f6ec..21f4407 100644 Binary files a/src/assets/images/units/unit-fa-zheng.png and b/src/assets/images/units/unit-fa-zheng.png differ diff --git a/src/assets/images/units/unit-gong-zhi-actions.png b/src/assets/images/units/unit-gong-zhi-actions.png index 070ed8f..feee2f3 100644 Binary files a/src/assets/images/units/unit-gong-zhi-actions.png and b/src/assets/images/units/unit-gong-zhi-actions.png differ diff --git a/src/assets/images/units/unit-gong-zhi.png b/src/assets/images/units/unit-gong-zhi.png index b9c8da1..7665a65 100644 Binary files a/src/assets/images/units/unit-gong-zhi.png and b/src/assets/images/units/unit-gong-zhi.png differ diff --git a/src/assets/images/units/unit-guan-yu-actions.png b/src/assets/images/units/unit-guan-yu-actions.png index 7ab612e..2c90dd6 100644 Binary files a/src/assets/images/units/unit-guan-yu-actions.png and b/src/assets/images/units/unit-guan-yu-actions.png differ diff --git a/src/assets/images/units/unit-guan-yu.png b/src/assets/images/units/unit-guan-yu.png index 5174e73..bb7f4d0 100644 Binary files a/src/assets/images/units/unit-guan-yu.png and b/src/assets/images/units/unit-guan-yu.png differ diff --git a/src/assets/images/units/unit-huang-quan-actions.png b/src/assets/images/units/unit-huang-quan-actions.png index 43907e2..87e467d 100644 Binary files a/src/assets/images/units/unit-huang-quan-actions.png and b/src/assets/images/units/unit-huang-quan-actions.png differ diff --git a/src/assets/images/units/unit-huang-quan.png b/src/assets/images/units/unit-huang-quan.png index d8b1fd0..4c72eae 100644 Binary files a/src/assets/images/units/unit-huang-quan.png and b/src/assets/images/units/unit-huang-quan.png differ diff --git a/src/assets/images/units/unit-huang-zhong-actions.png b/src/assets/images/units/unit-huang-zhong-actions.png index 9bda6fe..58f44c9 100644 Binary files a/src/assets/images/units/unit-huang-zhong-actions.png and b/src/assets/images/units/unit-huang-zhong-actions.png differ diff --git a/src/assets/images/units/unit-huang-zhong.png b/src/assets/images/units/unit-huang-zhong.png index 3be835d..6e5f709 100644 Binary files a/src/assets/images/units/unit-huang-zhong.png and b/src/assets/images/units/unit-huang-zhong.png differ diff --git a/src/assets/images/units/unit-jiang-wei-actions.png b/src/assets/images/units/unit-jiang-wei-actions.png index 4bbe7d3..d147ba9 100644 Binary files a/src/assets/images/units/unit-jiang-wei-actions.png and b/src/assets/images/units/unit-jiang-wei-actions.png differ diff --git a/src/assets/images/units/unit-jiang-wei.png b/src/assets/images/units/unit-jiang-wei.png index ef5f5f7..45596a1 100644 Binary files a/src/assets/images/units/unit-jiang-wei.png and b/src/assets/images/units/unit-jiang-wei.png differ diff --git a/src/assets/images/units/unit-li-yan-actions.png b/src/assets/images/units/unit-li-yan-actions.png index fa7868d..a6ae7d8 100644 Binary files a/src/assets/images/units/unit-li-yan-actions.png and b/src/assets/images/units/unit-li-yan-actions.png differ diff --git a/src/assets/images/units/unit-li-yan.png b/src/assets/images/units/unit-li-yan.png index 60c1444..f5db64c 100644 Binary files a/src/assets/images/units/unit-li-yan.png and b/src/assets/images/units/unit-li-yan.png differ diff --git a/src/assets/images/units/unit-liu-bei-actions.png b/src/assets/images/units/unit-liu-bei-actions.png index 7eacb89..c1c565e 100644 Binary files a/src/assets/images/units/unit-liu-bei-actions.png and b/src/assets/images/units/unit-liu-bei-actions.png differ diff --git a/src/assets/images/units/unit-liu-bei.png b/src/assets/images/units/unit-liu-bei.png index f53c6d4..593aa76 100644 Binary files a/src/assets/images/units/unit-liu-bei.png and b/src/assets/images/units/unit-liu-bei.png differ diff --git a/src/assets/images/units/unit-lu-bu-actions.png b/src/assets/images/units/unit-lu-bu-actions.png index 240869a..f32cfe7 100644 Binary files a/src/assets/images/units/unit-lu-bu-actions.png and b/src/assets/images/units/unit-lu-bu-actions.png differ diff --git a/src/assets/images/units/unit-lu-bu.png b/src/assets/images/units/unit-lu-bu.png index f7f9124..ffcfc1c 100644 Binary files a/src/assets/images/units/unit-lu-bu.png and b/src/assets/images/units/unit-lu-bu.png differ diff --git a/src/assets/images/units/unit-lu-meng-actions.png b/src/assets/images/units/unit-lu-meng-actions.png index aee65e7..505b83f 100644 Binary files a/src/assets/images/units/unit-lu-meng-actions.png and b/src/assets/images/units/unit-lu-meng-actions.png differ diff --git a/src/assets/images/units/unit-lu-meng.png b/src/assets/images/units/unit-lu-meng.png index a6dcf74..6ae96a2 100644 Binary files a/src/assets/images/units/unit-lu-meng.png and b/src/assets/images/units/unit-lu-meng.png differ diff --git a/src/assets/images/units/unit-ma-chao-actions.png b/src/assets/images/units/unit-ma-chao-actions.png index 0af8b96..deb3b98 100644 Binary files a/src/assets/images/units/unit-ma-chao-actions.png and b/src/assets/images/units/unit-ma-chao-actions.png differ diff --git a/src/assets/images/units/unit-ma-chao.png b/src/assets/images/units/unit-ma-chao.png index ecbca99..5f3c10f 100644 Binary files a/src/assets/images/units/unit-ma-chao.png and b/src/assets/images/units/unit-ma-chao.png differ diff --git a/src/assets/images/units/unit-ma-dai-actions.png b/src/assets/images/units/unit-ma-dai-actions.png index edc9d9a..4c7260a 100644 Binary files a/src/assets/images/units/unit-ma-dai-actions.png and b/src/assets/images/units/unit-ma-dai-actions.png differ diff --git a/src/assets/images/units/unit-ma-dai.png b/src/assets/images/units/unit-ma-dai.png index 5ea849d..77fd995 100644 Binary files a/src/assets/images/units/unit-ma-dai.png and b/src/assets/images/units/unit-ma-dai.png differ diff --git a/src/assets/images/units/unit-ma-liang-actions.png b/src/assets/images/units/unit-ma-liang-actions.png index 06daf45..e370b18 100644 Binary files a/src/assets/images/units/unit-ma-liang-actions.png and b/src/assets/images/units/unit-ma-liang-actions.png differ diff --git a/src/assets/images/units/unit-ma-liang.png b/src/assets/images/units/unit-ma-liang.png index 80c4cf5..5ee5d52 100644 Binary files a/src/assets/images/units/unit-ma-liang.png and b/src/assets/images/units/unit-ma-liang.png differ diff --git a/src/assets/images/units/unit-meng-huo-actions.png b/src/assets/images/units/unit-meng-huo-actions.png index 2a12633..5e0b35e 100644 Binary files a/src/assets/images/units/unit-meng-huo-actions.png and b/src/assets/images/units/unit-meng-huo-actions.png differ diff --git a/src/assets/images/units/unit-meng-huo.png b/src/assets/images/units/unit-meng-huo.png index 79e7294..969d322 100644 Binary files a/src/assets/images/units/unit-meng-huo.png and b/src/assets/images/units/unit-meng-huo.png differ diff --git a/src/assets/images/units/unit-nanman-infantry-actions.png b/src/assets/images/units/unit-nanman-infantry-actions.png index 1a23b73..d0fb490 100644 Binary files a/src/assets/images/units/unit-nanman-infantry-actions.png and b/src/assets/images/units/unit-nanman-infantry-actions.png differ diff --git a/src/assets/images/units/unit-nanman-infantry-jungle-actions.png b/src/assets/images/units/unit-nanman-infantry-jungle-actions.png index 4ef36c6..d0fb490 100644 Binary files a/src/assets/images/units/unit-nanman-infantry-jungle-actions.png and b/src/assets/images/units/unit-nanman-infantry-jungle-actions.png differ diff --git a/src/assets/images/units/unit-nanman-infantry-jungle.png b/src/assets/images/units/unit-nanman-infantry-jungle.png index 78db7b8..b61c995 100644 Binary files a/src/assets/images/units/unit-nanman-infantry-jungle.png and b/src/assets/images/units/unit-nanman-infantry-jungle.png differ diff --git a/src/assets/images/units/unit-nanman-infantry-spear-actions.png b/src/assets/images/units/unit-nanman-infantry-spear-actions.png index 5d8bdcb..d0fb490 100644 Binary files a/src/assets/images/units/unit-nanman-infantry-spear-actions.png and b/src/assets/images/units/unit-nanman-infantry-spear-actions.png differ diff --git a/src/assets/images/units/unit-nanman-infantry-spear.png b/src/assets/images/units/unit-nanman-infantry-spear.png index 6d6491e..b61c995 100644 Binary files a/src/assets/images/units/unit-nanman-infantry-spear.png and b/src/assets/images/units/unit-nanman-infantry-spear.png differ diff --git a/src/assets/images/units/unit-nanman-infantry.png b/src/assets/images/units/unit-nanman-infantry.png index 0ec0b0d..b61c995 100644 Binary files a/src/assets/images/units/unit-nanman-infantry.png and b/src/assets/images/units/unit-nanman-infantry.png differ diff --git a/src/assets/images/units/unit-nanman-officer-actions.png b/src/assets/images/units/unit-nanman-officer-actions.png index cbf84c6..c4e2a0b 100644 Binary files a/src/assets/images/units/unit-nanman-officer-actions.png and b/src/assets/images/units/unit-nanman-officer-actions.png differ diff --git a/src/assets/images/units/unit-nanman-officer-bronze-actions.png b/src/assets/images/units/unit-nanman-officer-bronze-actions.png index 039228e..c4e2a0b 100644 Binary files a/src/assets/images/units/unit-nanman-officer-bronze-actions.png and b/src/assets/images/units/unit-nanman-officer-bronze-actions.png differ diff --git a/src/assets/images/units/unit-nanman-officer-bronze.png b/src/assets/images/units/unit-nanman-officer-bronze.png index bca5e7d..a9caa32 100644 Binary files a/src/assets/images/units/unit-nanman-officer-bronze.png and b/src/assets/images/units/unit-nanman-officer-bronze.png differ diff --git a/src/assets/images/units/unit-nanman-officer-warlord-actions.png b/src/assets/images/units/unit-nanman-officer-warlord-actions.png index 3a26e1c..c4e2a0b 100644 Binary files a/src/assets/images/units/unit-nanman-officer-warlord-actions.png and b/src/assets/images/units/unit-nanman-officer-warlord-actions.png differ diff --git a/src/assets/images/units/unit-nanman-officer-warlord.png b/src/assets/images/units/unit-nanman-officer-warlord.png index 8735f8f..a9caa32 100644 Binary files a/src/assets/images/units/unit-nanman-officer-warlord.png and b/src/assets/images/units/unit-nanman-officer-warlord.png differ diff --git a/src/assets/images/units/unit-nanman-officer.png b/src/assets/images/units/unit-nanman-officer.png index d47f0e3..a9caa32 100644 Binary files a/src/assets/images/units/unit-nanman-officer.png and b/src/assets/images/units/unit-nanman-officer.png differ diff --git a/src/assets/images/units/unit-nanman-shaman-actions.png b/src/assets/images/units/unit-nanman-shaman-actions.png index a6e75a3..2f18a1d 100644 Binary files a/src/assets/images/units/unit-nanman-shaman-actions.png and b/src/assets/images/units/unit-nanman-shaman-actions.png differ diff --git a/src/assets/images/units/unit-nanman-shaman-ember-actions.png b/src/assets/images/units/unit-nanman-shaman-ember-actions.png index 8c9e811..2f18a1d 100644 Binary files a/src/assets/images/units/unit-nanman-shaman-ember-actions.png and b/src/assets/images/units/unit-nanman-shaman-ember-actions.png differ diff --git a/src/assets/images/units/unit-nanman-shaman-ember.png b/src/assets/images/units/unit-nanman-shaman-ember.png index 1910de4..4d86a20 100644 Binary files a/src/assets/images/units/unit-nanman-shaman-ember.png and b/src/assets/images/units/unit-nanman-shaman-ember.png differ diff --git a/src/assets/images/units/unit-nanman-shaman-storm-actions.png b/src/assets/images/units/unit-nanman-shaman-storm-actions.png index bf65fc8..2f18a1d 100644 Binary files a/src/assets/images/units/unit-nanman-shaman-storm-actions.png and b/src/assets/images/units/unit-nanman-shaman-storm-actions.png differ diff --git a/src/assets/images/units/unit-nanman-shaman-storm.png b/src/assets/images/units/unit-nanman-shaman-storm.png index 369a9bc..4d86a20 100644 Binary files a/src/assets/images/units/unit-nanman-shaman-storm.png and b/src/assets/images/units/unit-nanman-shaman-storm.png differ diff --git a/src/assets/images/units/unit-nanman-shaman.png b/src/assets/images/units/unit-nanman-shaman.png index 5dbf461..4d86a20 100644 Binary files a/src/assets/images/units/unit-nanman-shaman.png and b/src/assets/images/units/unit-nanman-shaman.png differ diff --git a/src/assets/images/units/unit-pang-tong-actions.png b/src/assets/images/units/unit-pang-tong-actions.png index 210d67d..9291c61 100644 Binary files a/src/assets/images/units/unit-pang-tong-actions.png and b/src/assets/images/units/unit-pang-tong-actions.png differ diff --git a/src/assets/images/units/unit-pang-tong.png b/src/assets/images/units/unit-pang-tong.png index 79416cb..fd635e9 100644 Binary files a/src/assets/images/units/unit-pang-tong.png and b/src/assets/images/units/unit-pang-tong.png differ diff --git a/src/assets/images/units/unit-rebel-actions.png b/src/assets/images/units/unit-rebel-actions.png index 1bb20e7..b636520 100644 Binary files a/src/assets/images/units/unit-rebel-actions.png and b/src/assets/images/units/unit-rebel-actions.png differ diff --git a/src/assets/images/units/unit-rebel-archer-actions.png b/src/assets/images/units/unit-rebel-archer-actions.png index e801321..facc737 100644 Binary files a/src/assets/images/units/unit-rebel-archer-actions.png and b/src/assets/images/units/unit-rebel-archer-actions.png differ diff --git a/src/assets/images/units/unit-rebel-archer-fire-actions.png b/src/assets/images/units/unit-rebel-archer-fire-actions.png index a39f6a4..122191d 100644 Binary files a/src/assets/images/units/unit-rebel-archer-fire-actions.png and b/src/assets/images/units/unit-rebel-archer-fire-actions.png differ diff --git a/src/assets/images/units/unit-rebel-archer-fire.png b/src/assets/images/units/unit-rebel-archer-fire.png index 99b7bfd..41d55be 100644 Binary files a/src/assets/images/units/unit-rebel-archer-fire.png and b/src/assets/images/units/unit-rebel-archer-fire.png differ diff --git a/src/assets/images/units/unit-rebel-archer-veteran-actions.png b/src/assets/images/units/unit-rebel-archer-veteran-actions.png index bdf51ed..35fa336 100644 Binary files a/src/assets/images/units/unit-rebel-archer-veteran-actions.png and b/src/assets/images/units/unit-rebel-archer-veteran-actions.png differ diff --git a/src/assets/images/units/unit-rebel-archer-veteran.png b/src/assets/images/units/unit-rebel-archer-veteran.png index a842e11..74e622c 100644 Binary files a/src/assets/images/units/unit-rebel-archer-veteran.png and b/src/assets/images/units/unit-rebel-archer-veteran.png differ diff --git a/src/assets/images/units/unit-rebel-archer.png b/src/assets/images/units/unit-rebel-archer.png index 64b9e4b..d9f70bb 100644 Binary files a/src/assets/images/units/unit-rebel-archer.png and b/src/assets/images/units/unit-rebel-archer.png differ diff --git a/src/assets/images/units/unit-rebel-cavalry-actions.png b/src/assets/images/units/unit-rebel-cavalry-actions.png index 3a089aa..9b2c791 100644 Binary files a/src/assets/images/units/unit-rebel-cavalry-actions.png and b/src/assets/images/units/unit-rebel-cavalry-actions.png differ diff --git a/src/assets/images/units/unit-rebel-cavalry-veteran-actions.png b/src/assets/images/units/unit-rebel-cavalry-veteran-actions.png index accf9d6..589f00f 100644 Binary files a/src/assets/images/units/unit-rebel-cavalry-veteran-actions.png and b/src/assets/images/units/unit-rebel-cavalry-veteran-actions.png differ diff --git a/src/assets/images/units/unit-rebel-cavalry-veteran.png b/src/assets/images/units/unit-rebel-cavalry-veteran.png index 65b4f04..7ac1089 100644 Binary files a/src/assets/images/units/unit-rebel-cavalry-veteran.png and b/src/assets/images/units/unit-rebel-cavalry-veteran.png differ diff --git a/src/assets/images/units/unit-rebel-cavalry.png b/src/assets/images/units/unit-rebel-cavalry.png index f72627a..19c5133 100644 Binary files a/src/assets/images/units/unit-rebel-cavalry.png and b/src/assets/images/units/unit-rebel-cavalry.png differ diff --git a/src/assets/images/units/unit-rebel-leader-actions.png b/src/assets/images/units/unit-rebel-leader-actions.png index 2af0d7f..6f291af 100644 Binary files a/src/assets/images/units/unit-rebel-leader-actions.png and b/src/assets/images/units/unit-rebel-leader-actions.png differ diff --git a/src/assets/images/units/unit-rebel-leader-warlord-actions.png b/src/assets/images/units/unit-rebel-leader-warlord-actions.png index 18b221c..99c8afd 100644 Binary files a/src/assets/images/units/unit-rebel-leader-warlord-actions.png and b/src/assets/images/units/unit-rebel-leader-warlord-actions.png differ diff --git a/src/assets/images/units/unit-rebel-leader-warlord.png b/src/assets/images/units/unit-rebel-leader-warlord.png index a71951e..ca49961 100644 Binary files a/src/assets/images/units/unit-rebel-leader-warlord.png and b/src/assets/images/units/unit-rebel-leader-warlord.png differ diff --git a/src/assets/images/units/unit-rebel-leader.png b/src/assets/images/units/unit-rebel-leader.png index cebed8e..1a50491 100644 Binary files a/src/assets/images/units/unit-rebel-leader.png and b/src/assets/images/units/unit-rebel-leader.png differ diff --git a/src/assets/images/units/unit-rebel-ragged-actions.png b/src/assets/images/units/unit-rebel-ragged-actions.png index 3a529c5..ea6b826 100644 Binary files a/src/assets/images/units/unit-rebel-ragged-actions.png and b/src/assets/images/units/unit-rebel-ragged-actions.png differ diff --git a/src/assets/images/units/unit-rebel-ragged.png b/src/assets/images/units/unit-rebel-ragged.png index 73be787..8074350 100644 Binary files a/src/assets/images/units/unit-rebel-ragged.png and b/src/assets/images/units/unit-rebel-ragged.png differ diff --git a/src/assets/images/units/unit-rebel.png b/src/assets/images/units/unit-rebel.png index 2315dce..f28d8ca 100644 Binary files a/src/assets/images/units/unit-rebel.png and b/src/assets/images/units/unit-rebel.png differ diff --git a/src/assets/images/units/unit-shu-archer-actions.png b/src/assets/images/units/unit-shu-archer-actions.png index 9e88f88..1b5ff09 100644 Binary files a/src/assets/images/units/unit-shu-archer-actions.png and b/src/assets/images/units/unit-shu-archer-actions.png differ diff --git a/src/assets/images/units/unit-shu-archer-longbow-actions.png b/src/assets/images/units/unit-shu-archer-longbow-actions.png index 1f705ef..1b5ff09 100644 Binary files a/src/assets/images/units/unit-shu-archer-longbow-actions.png and b/src/assets/images/units/unit-shu-archer-longbow-actions.png differ diff --git a/src/assets/images/units/unit-shu-archer-longbow.png b/src/assets/images/units/unit-shu-archer-longbow.png index 0ba58e2..dad3086 100644 Binary files a/src/assets/images/units/unit-shu-archer-longbow.png and b/src/assets/images/units/unit-shu-archer-longbow.png differ diff --git a/src/assets/images/units/unit-shu-archer-veteran-actions.png b/src/assets/images/units/unit-shu-archer-veteran-actions.png index 5cf87ed..1b5ff09 100644 Binary files a/src/assets/images/units/unit-shu-archer-veteran-actions.png and b/src/assets/images/units/unit-shu-archer-veteran-actions.png differ diff --git a/src/assets/images/units/unit-shu-archer-veteran.png b/src/assets/images/units/unit-shu-archer-veteran.png index 362ec1e..dad3086 100644 Binary files a/src/assets/images/units/unit-shu-archer-veteran.png and b/src/assets/images/units/unit-shu-archer-veteran.png differ diff --git a/src/assets/images/units/unit-shu-archer.png b/src/assets/images/units/unit-shu-archer.png index ba5a67e..dad3086 100644 Binary files a/src/assets/images/units/unit-shu-archer.png and b/src/assets/images/units/unit-shu-archer.png differ diff --git a/src/assets/images/units/unit-shu-cavalry-actions.png b/src/assets/images/units/unit-shu-cavalry-actions.png index 0e1a2ec..24d541b 100644 Binary files a/src/assets/images/units/unit-shu-cavalry-actions.png and b/src/assets/images/units/unit-shu-cavalry-actions.png differ diff --git a/src/assets/images/units/unit-shu-cavalry-scout-actions.png b/src/assets/images/units/unit-shu-cavalry-scout-actions.png index 9331c25..24d541b 100644 Binary files a/src/assets/images/units/unit-shu-cavalry-scout-actions.png and b/src/assets/images/units/unit-shu-cavalry-scout-actions.png differ diff --git a/src/assets/images/units/unit-shu-cavalry-scout.png b/src/assets/images/units/unit-shu-cavalry-scout.png index f1c5570..682a304 100644 Binary files a/src/assets/images/units/unit-shu-cavalry-scout.png and b/src/assets/images/units/unit-shu-cavalry-scout.png differ diff --git a/src/assets/images/units/unit-shu-cavalry-white-actions.png b/src/assets/images/units/unit-shu-cavalry-white-actions.png index 8dbc958..24d541b 100644 Binary files a/src/assets/images/units/unit-shu-cavalry-white-actions.png and b/src/assets/images/units/unit-shu-cavalry-white-actions.png differ diff --git a/src/assets/images/units/unit-shu-cavalry-white.png b/src/assets/images/units/unit-shu-cavalry-white.png index ed124f4..682a304 100644 Binary files a/src/assets/images/units/unit-shu-cavalry-white.png and b/src/assets/images/units/unit-shu-cavalry-white.png differ diff --git a/src/assets/images/units/unit-shu-cavalry.png b/src/assets/images/units/unit-shu-cavalry.png index fd4708e..682a304 100644 Binary files a/src/assets/images/units/unit-shu-cavalry.png and b/src/assets/images/units/unit-shu-cavalry.png differ diff --git a/src/assets/images/units/unit-shu-infantry-actions.png b/src/assets/images/units/unit-shu-infantry-actions.png index 07019fd..6f76f94 100644 Binary files a/src/assets/images/units/unit-shu-infantry-actions.png and b/src/assets/images/units/unit-shu-infantry-actions.png differ diff --git a/src/assets/images/units/unit-shu-infantry-banner-actions.png b/src/assets/images/units/unit-shu-infantry-banner-actions.png index e49c878..6f76f94 100644 Binary files a/src/assets/images/units/unit-shu-infantry-banner-actions.png and b/src/assets/images/units/unit-shu-infantry-banner-actions.png differ diff --git a/src/assets/images/units/unit-shu-infantry-banner.png b/src/assets/images/units/unit-shu-infantry-banner.png index 050e902..9a62c59 100644 Binary files a/src/assets/images/units/unit-shu-infantry-banner.png and b/src/assets/images/units/unit-shu-infantry-banner.png differ diff --git a/src/assets/images/units/unit-shu-infantry-guard-actions.png b/src/assets/images/units/unit-shu-infantry-guard-actions.png index c7c24ee..6f76f94 100644 Binary files a/src/assets/images/units/unit-shu-infantry-guard-actions.png and b/src/assets/images/units/unit-shu-infantry-guard-actions.png differ diff --git a/src/assets/images/units/unit-shu-infantry-guard.png b/src/assets/images/units/unit-shu-infantry-guard.png index 1708664..9a62c59 100644 Binary files a/src/assets/images/units/unit-shu-infantry-guard.png and b/src/assets/images/units/unit-shu-infantry-guard.png differ diff --git a/src/assets/images/units/unit-shu-infantry.png b/src/assets/images/units/unit-shu-infantry.png index 75b85a4..9a62c59 100644 Binary files a/src/assets/images/units/unit-shu-infantry.png and b/src/assets/images/units/unit-shu-infantry.png differ diff --git a/src/assets/images/units/unit-shu-officer-actions.png b/src/assets/images/units/unit-shu-officer-actions.png index 19b3a8e..02cb27e 100644 Binary files a/src/assets/images/units/unit-shu-officer-actions.png and b/src/assets/images/units/unit-shu-officer-actions.png differ diff --git a/src/assets/images/units/unit-shu-officer-council-actions.png b/src/assets/images/units/unit-shu-officer-council-actions.png index 2a6e0e7..02cb27e 100644 Binary files a/src/assets/images/units/unit-shu-officer-council-actions.png and b/src/assets/images/units/unit-shu-officer-council-actions.png differ diff --git a/src/assets/images/units/unit-shu-officer-council.png b/src/assets/images/units/unit-shu-officer-council.png index de6dddd..1906d34 100644 Binary files a/src/assets/images/units/unit-shu-officer-council.png and b/src/assets/images/units/unit-shu-officer-council.png differ diff --git a/src/assets/images/units/unit-shu-officer-vanguard-actions.png b/src/assets/images/units/unit-shu-officer-vanguard-actions.png index acc7901..02cb27e 100644 Binary files a/src/assets/images/units/unit-shu-officer-vanguard-actions.png and b/src/assets/images/units/unit-shu-officer-vanguard-actions.png differ diff --git a/src/assets/images/units/unit-shu-officer-vanguard.png b/src/assets/images/units/unit-shu-officer-vanguard.png index 6e2d2fb..1906d34 100644 Binary files a/src/assets/images/units/unit-shu-officer-vanguard.png and b/src/assets/images/units/unit-shu-officer-vanguard.png differ diff --git a/src/assets/images/units/unit-shu-officer.png b/src/assets/images/units/unit-shu-officer.png index 6055700..1906d34 100644 Binary files a/src/assets/images/units/unit-shu-officer.png and b/src/assets/images/units/unit-shu-officer.png differ diff --git a/src/assets/images/units/unit-shu-spearman-actions.png b/src/assets/images/units/unit-shu-spearman-actions.png index d6ff593..3a59cb1 100644 Binary files a/src/assets/images/units/unit-shu-spearman-actions.png and b/src/assets/images/units/unit-shu-spearman-actions.png differ diff --git a/src/assets/images/units/unit-shu-spearman-guard-actions.png b/src/assets/images/units/unit-shu-spearman-guard-actions.png index 089d3ad..3a59cb1 100644 Binary files a/src/assets/images/units/unit-shu-spearman-guard-actions.png and b/src/assets/images/units/unit-shu-spearman-guard-actions.png differ diff --git a/src/assets/images/units/unit-shu-spearman-guard.png b/src/assets/images/units/unit-shu-spearman-guard.png index bfaa788..10ae4d4 100644 Binary files a/src/assets/images/units/unit-shu-spearman-guard.png and b/src/assets/images/units/unit-shu-spearman-guard.png differ diff --git a/src/assets/images/units/unit-shu-spearman-veteran-actions.png b/src/assets/images/units/unit-shu-spearman-veteran-actions.png index fdfe81c..3a59cb1 100644 Binary files a/src/assets/images/units/unit-shu-spearman-veteran-actions.png and b/src/assets/images/units/unit-shu-spearman-veteran-actions.png differ diff --git a/src/assets/images/units/unit-shu-spearman-veteran.png b/src/assets/images/units/unit-shu-spearman-veteran.png index cc49264..10ae4d4 100644 Binary files a/src/assets/images/units/unit-shu-spearman-veteran.png and b/src/assets/images/units/unit-shu-spearman-veteran.png differ diff --git a/src/assets/images/units/unit-shu-spearman.png b/src/assets/images/units/unit-shu-spearman.png index 3cd42aa..10ae4d4 100644 Binary files a/src/assets/images/units/unit-shu-spearman.png and b/src/assets/images/units/unit-shu-spearman.png differ diff --git a/src/assets/images/units/unit-shu-strategist-actions.png b/src/assets/images/units/unit-shu-strategist-actions.png index a0608b5..09c518a 100644 Binary files a/src/assets/images/units/unit-shu-strategist-actions.png and b/src/assets/images/units/unit-shu-strategist-actions.png differ diff --git a/src/assets/images/units/unit-shu-strategist-field-actions.png b/src/assets/images/units/unit-shu-strategist-field-actions.png index 917e190..09c518a 100644 Binary files a/src/assets/images/units/unit-shu-strategist-field-actions.png and b/src/assets/images/units/unit-shu-strategist-field-actions.png differ diff --git a/src/assets/images/units/unit-shu-strategist-field.png b/src/assets/images/units/unit-shu-strategist-field.png index 798c4fb..3860c53 100644 Binary files a/src/assets/images/units/unit-shu-strategist-field.png and b/src/assets/images/units/unit-shu-strategist-field.png differ diff --git a/src/assets/images/units/unit-shu-strategist-white-actions.png b/src/assets/images/units/unit-shu-strategist-white-actions.png index 48f0df5..09c518a 100644 Binary files a/src/assets/images/units/unit-shu-strategist-white-actions.png and b/src/assets/images/units/unit-shu-strategist-white-actions.png differ diff --git a/src/assets/images/units/unit-shu-strategist-white.png b/src/assets/images/units/unit-shu-strategist-white.png index 1e72f06..3860c53 100644 Binary files a/src/assets/images/units/unit-shu-strategist-white.png and b/src/assets/images/units/unit-shu-strategist-white.png differ diff --git a/src/assets/images/units/unit-shu-strategist.png b/src/assets/images/units/unit-shu-strategist.png index 4dada31..3860c53 100644 Binary files a/src/assets/images/units/unit-shu-strategist.png and b/src/assets/images/units/unit-shu-strategist.png differ diff --git a/src/assets/images/units/unit-sima-yi-actions.png b/src/assets/images/units/unit-sima-yi-actions.png index fc5bad2..e4af2c9 100644 Binary files a/src/assets/images/units/unit-sima-yi-actions.png and b/src/assets/images/units/unit-sima-yi-actions.png differ diff --git a/src/assets/images/units/unit-sima-yi.png b/src/assets/images/units/unit-sima-yi.png index bcfdbac..0ddb26e 100644 Binary files a/src/assets/images/units/unit-sima-yi.png and b/src/assets/images/units/unit-sima-yi.png differ diff --git a/src/assets/images/units/unit-wang-ping-actions.png b/src/assets/images/units/unit-wang-ping-actions.png index bc35487..d2f8fc9 100644 Binary files a/src/assets/images/units/unit-wang-ping-actions.png and b/src/assets/images/units/unit-wang-ping-actions.png differ diff --git a/src/assets/images/units/unit-wang-ping.png b/src/assets/images/units/unit-wang-ping.png index 2b0c008..563273e 100644 Binary files a/src/assets/images/units/unit-wang-ping.png and b/src/assets/images/units/unit-wang-ping.png differ diff --git a/src/assets/images/units/unit-wei-archer-actions.png b/src/assets/images/units/unit-wei-archer-actions.png index 566a84d..443442a 100644 Binary files a/src/assets/images/units/unit-wei-archer-actions.png and b/src/assets/images/units/unit-wei-archer-actions.png differ diff --git a/src/assets/images/units/unit-wei-archer-crossbow-actions.png b/src/assets/images/units/unit-wei-archer-crossbow-actions.png index ca14fc6..443442a 100644 Binary files a/src/assets/images/units/unit-wei-archer-crossbow-actions.png and b/src/assets/images/units/unit-wei-archer-crossbow-actions.png differ diff --git a/src/assets/images/units/unit-wei-archer-crossbow.png b/src/assets/images/units/unit-wei-archer-crossbow.png index 08df08e..db8a9a6 100644 Binary files a/src/assets/images/units/unit-wei-archer-crossbow.png and b/src/assets/images/units/unit-wei-archer-crossbow.png differ diff --git a/src/assets/images/units/unit-wei-archer-veteran-actions.png b/src/assets/images/units/unit-wei-archer-veteran-actions.png index 3f9f9ca..443442a 100644 Binary files a/src/assets/images/units/unit-wei-archer-veteran-actions.png and b/src/assets/images/units/unit-wei-archer-veteran-actions.png differ diff --git a/src/assets/images/units/unit-wei-archer-veteran.png b/src/assets/images/units/unit-wei-archer-veteran.png index 4115c6a..db8a9a6 100644 Binary files a/src/assets/images/units/unit-wei-archer-veteran.png and b/src/assets/images/units/unit-wei-archer-veteran.png differ diff --git a/src/assets/images/units/unit-wei-archer.png b/src/assets/images/units/unit-wei-archer.png index 3aea5e2..db8a9a6 100644 Binary files a/src/assets/images/units/unit-wei-archer.png and b/src/assets/images/units/unit-wei-archer.png differ diff --git a/src/assets/images/units/unit-wei-cavalry-actions.png b/src/assets/images/units/unit-wei-cavalry-actions.png index df2f3b1..b593886 100644 Binary files a/src/assets/images/units/unit-wei-cavalry-actions.png and b/src/assets/images/units/unit-wei-cavalry-actions.png differ diff --git a/src/assets/images/units/unit-wei-cavalry-elite-actions.png b/src/assets/images/units/unit-wei-cavalry-elite-actions.png index 24f3019..b593886 100644 Binary files a/src/assets/images/units/unit-wei-cavalry-elite-actions.png and b/src/assets/images/units/unit-wei-cavalry-elite-actions.png differ diff --git a/src/assets/images/units/unit-wei-cavalry-elite.png b/src/assets/images/units/unit-wei-cavalry-elite.png index b3cedce..e5db7fe 100644 Binary files a/src/assets/images/units/unit-wei-cavalry-elite.png and b/src/assets/images/units/unit-wei-cavalry-elite.png differ diff --git a/src/assets/images/units/unit-wei-cavalry-iron-actions.png b/src/assets/images/units/unit-wei-cavalry-iron-actions.png index 482bc61..b593886 100644 Binary files a/src/assets/images/units/unit-wei-cavalry-iron-actions.png and b/src/assets/images/units/unit-wei-cavalry-iron-actions.png differ diff --git a/src/assets/images/units/unit-wei-cavalry-iron.png b/src/assets/images/units/unit-wei-cavalry-iron.png index 271ef7e..e5db7fe 100644 Binary files a/src/assets/images/units/unit-wei-cavalry-iron.png and b/src/assets/images/units/unit-wei-cavalry-iron.png differ diff --git a/src/assets/images/units/unit-wei-cavalry.png b/src/assets/images/units/unit-wei-cavalry.png index 23967cd..e5db7fe 100644 Binary files a/src/assets/images/units/unit-wei-cavalry.png and b/src/assets/images/units/unit-wei-cavalry.png differ diff --git a/src/assets/images/units/unit-wei-infantry-actions.png b/src/assets/images/units/unit-wei-infantry-actions.png index ae53a7b..c4c11ef 100644 Binary files a/src/assets/images/units/unit-wei-infantry-actions.png and b/src/assets/images/units/unit-wei-infantry-actions.png differ diff --git a/src/assets/images/units/unit-wei-infantry-shield-actions.png b/src/assets/images/units/unit-wei-infantry-shield-actions.png index 1de27b2..c4c11ef 100644 Binary files a/src/assets/images/units/unit-wei-infantry-shield-actions.png and b/src/assets/images/units/unit-wei-infantry-shield-actions.png differ diff --git a/src/assets/images/units/unit-wei-infantry-shield.png b/src/assets/images/units/unit-wei-infantry-shield.png index bbe4893..1f1d2ff 100644 Binary files a/src/assets/images/units/unit-wei-infantry-shield.png and b/src/assets/images/units/unit-wei-infantry-shield.png differ diff --git a/src/assets/images/units/unit-wei-infantry-veteran-actions.png b/src/assets/images/units/unit-wei-infantry-veteran-actions.png index 6bbcdc3..c4c11ef 100644 Binary files a/src/assets/images/units/unit-wei-infantry-veteran-actions.png and b/src/assets/images/units/unit-wei-infantry-veteran-actions.png differ diff --git a/src/assets/images/units/unit-wei-infantry-veteran.png b/src/assets/images/units/unit-wei-infantry-veteran.png index cd9e3ec..1f1d2ff 100644 Binary files a/src/assets/images/units/unit-wei-infantry-veteran.png and b/src/assets/images/units/unit-wei-infantry-veteran.png differ diff --git a/src/assets/images/units/unit-wei-infantry.png b/src/assets/images/units/unit-wei-infantry.png index f777de8..1f1d2ff 100644 Binary files a/src/assets/images/units/unit-wei-infantry.png and b/src/assets/images/units/unit-wei-infantry.png differ diff --git a/src/assets/images/units/unit-wei-officer-actions.png b/src/assets/images/units/unit-wei-officer-actions.png index cb0a9b8..bf7e5f2 100644 Binary files a/src/assets/images/units/unit-wei-officer-actions.png and b/src/assets/images/units/unit-wei-officer-actions.png differ diff --git a/src/assets/images/units/unit-wei-officer-gate-actions.png b/src/assets/images/units/unit-wei-officer-gate-actions.png index 7d24408..bf7e5f2 100644 Binary files a/src/assets/images/units/unit-wei-officer-gate-actions.png and b/src/assets/images/units/unit-wei-officer-gate-actions.png differ diff --git a/src/assets/images/units/unit-wei-officer-gate.png b/src/assets/images/units/unit-wei-officer-gate.png index 6fa4335..e093e19 100644 Binary files a/src/assets/images/units/unit-wei-officer-gate.png and b/src/assets/images/units/unit-wei-officer-gate.png differ diff --git a/src/assets/images/units/unit-wei-officer-iron-actions.png b/src/assets/images/units/unit-wei-officer-iron-actions.png index f777f3b..bf7e5f2 100644 Binary files a/src/assets/images/units/unit-wei-officer-iron-actions.png and b/src/assets/images/units/unit-wei-officer-iron-actions.png differ diff --git a/src/assets/images/units/unit-wei-officer-iron.png b/src/assets/images/units/unit-wei-officer-iron.png index 8df04fa..e093e19 100644 Binary files a/src/assets/images/units/unit-wei-officer-iron.png and b/src/assets/images/units/unit-wei-officer-iron.png differ diff --git a/src/assets/images/units/unit-wei-officer.png b/src/assets/images/units/unit-wei-officer.png index 292d313..e093e19 100644 Binary files a/src/assets/images/units/unit-wei-officer.png and b/src/assets/images/units/unit-wei-officer.png differ diff --git a/src/assets/images/units/unit-wei-strategist-actions.png b/src/assets/images/units/unit-wei-strategist-actions.png index 91e543f..8aa16fc 100644 Binary files a/src/assets/images/units/unit-wei-strategist-actions.png and b/src/assets/images/units/unit-wei-strategist-actions.png differ diff --git a/src/assets/images/units/unit-wei-strategist-black-actions.png b/src/assets/images/units/unit-wei-strategist-black-actions.png index 1a4ae52..8aa16fc 100644 Binary files a/src/assets/images/units/unit-wei-strategist-black-actions.png and b/src/assets/images/units/unit-wei-strategist-black-actions.png differ diff --git a/src/assets/images/units/unit-wei-strategist-black.png b/src/assets/images/units/unit-wei-strategist-black.png index 0744b66..97a5c8c 100644 Binary files a/src/assets/images/units/unit-wei-strategist-black.png and b/src/assets/images/units/unit-wei-strategist-black.png differ diff --git a/src/assets/images/units/unit-wei-strategist-blue-actions.png b/src/assets/images/units/unit-wei-strategist-blue-actions.png index 41724c0..8aa16fc 100644 Binary files a/src/assets/images/units/unit-wei-strategist-blue-actions.png and b/src/assets/images/units/unit-wei-strategist-blue-actions.png differ diff --git a/src/assets/images/units/unit-wei-strategist-blue.png b/src/assets/images/units/unit-wei-strategist-blue.png index c701a16..97a5c8c 100644 Binary files a/src/assets/images/units/unit-wei-strategist-blue.png and b/src/assets/images/units/unit-wei-strategist-blue.png differ diff --git a/src/assets/images/units/unit-wei-strategist.png b/src/assets/images/units/unit-wei-strategist.png index 62b834b..97a5c8c 100644 Binary files a/src/assets/images/units/unit-wei-strategist.png and b/src/assets/images/units/unit-wei-strategist.png differ diff --git a/src/assets/images/units/unit-wei-yan-actions.png b/src/assets/images/units/unit-wei-yan-actions.png index 8061771..f294a4b 100644 Binary files a/src/assets/images/units/unit-wei-yan-actions.png and b/src/assets/images/units/unit-wei-yan-actions.png differ diff --git a/src/assets/images/units/unit-wei-yan.png b/src/assets/images/units/unit-wei-yan.png index 8d01933..ae8a2e8 100644 Binary files a/src/assets/images/units/unit-wei-yan.png and b/src/assets/images/units/unit-wei-yan.png differ diff --git a/src/assets/images/units/unit-wu-archer-actions.png b/src/assets/images/units/unit-wu-archer-actions.png index c6c6014..f0112d4 100644 Binary files a/src/assets/images/units/unit-wu-archer-actions.png and b/src/assets/images/units/unit-wu-archer-actions.png differ diff --git a/src/assets/images/units/unit-wu-archer-naval-actions.png b/src/assets/images/units/unit-wu-archer-naval-actions.png index d17dc71..f0112d4 100644 Binary files a/src/assets/images/units/unit-wu-archer-naval-actions.png and b/src/assets/images/units/unit-wu-archer-naval-actions.png differ diff --git a/src/assets/images/units/unit-wu-archer-naval.png b/src/assets/images/units/unit-wu-archer-naval.png index 88b1302..1c50fee 100644 Binary files a/src/assets/images/units/unit-wu-archer-naval.png and b/src/assets/images/units/unit-wu-archer-naval.png differ diff --git a/src/assets/images/units/unit-wu-archer-river-actions.png b/src/assets/images/units/unit-wu-archer-river-actions.png index 2209897..f0112d4 100644 Binary files a/src/assets/images/units/unit-wu-archer-river-actions.png and b/src/assets/images/units/unit-wu-archer-river-actions.png differ diff --git a/src/assets/images/units/unit-wu-archer-river.png b/src/assets/images/units/unit-wu-archer-river.png index 2567f3b..1c50fee 100644 Binary files a/src/assets/images/units/unit-wu-archer-river.png and b/src/assets/images/units/unit-wu-archer-river.png differ diff --git a/src/assets/images/units/unit-wu-archer.png b/src/assets/images/units/unit-wu-archer.png index 8b39b47..1c50fee 100644 Binary files a/src/assets/images/units/unit-wu-archer.png and b/src/assets/images/units/unit-wu-archer.png differ diff --git a/src/assets/images/units/unit-wu-cavalry-actions.png b/src/assets/images/units/unit-wu-cavalry-actions.png index 39947a0..bb3bd54 100644 Binary files a/src/assets/images/units/unit-wu-cavalry-actions.png and b/src/assets/images/units/unit-wu-cavalry-actions.png differ diff --git a/src/assets/images/units/unit-wu-cavalry-elite-actions.png b/src/assets/images/units/unit-wu-cavalry-elite-actions.png index 39f2fe1..bb3bd54 100644 Binary files a/src/assets/images/units/unit-wu-cavalry-elite-actions.png and b/src/assets/images/units/unit-wu-cavalry-elite-actions.png differ diff --git a/src/assets/images/units/unit-wu-cavalry-elite.png b/src/assets/images/units/unit-wu-cavalry-elite.png index 22965ae..6e0aa58 100644 Binary files a/src/assets/images/units/unit-wu-cavalry-elite.png and b/src/assets/images/units/unit-wu-cavalry-elite.png differ diff --git a/src/assets/images/units/unit-wu-cavalry-river-actions.png b/src/assets/images/units/unit-wu-cavalry-river-actions.png index e13a3b6..bb3bd54 100644 Binary files a/src/assets/images/units/unit-wu-cavalry-river-actions.png and b/src/assets/images/units/unit-wu-cavalry-river-actions.png differ diff --git a/src/assets/images/units/unit-wu-cavalry-river.png b/src/assets/images/units/unit-wu-cavalry-river.png index 8033e1b..6e0aa58 100644 Binary files a/src/assets/images/units/unit-wu-cavalry-river.png and b/src/assets/images/units/unit-wu-cavalry-river.png differ diff --git a/src/assets/images/units/unit-wu-cavalry.png b/src/assets/images/units/unit-wu-cavalry.png index f75a236..6e0aa58 100644 Binary files a/src/assets/images/units/unit-wu-cavalry.png and b/src/assets/images/units/unit-wu-cavalry.png differ diff --git a/src/assets/images/units/unit-wu-infantry-actions.png b/src/assets/images/units/unit-wu-infantry-actions.png index 6d403a8..fb3862d 100644 Binary files a/src/assets/images/units/unit-wu-infantry-actions.png and b/src/assets/images/units/unit-wu-infantry-actions.png differ diff --git a/src/assets/images/units/unit-wu-infantry-marine-actions.png b/src/assets/images/units/unit-wu-infantry-marine-actions.png index 83a50a0..fb3862d 100644 Binary files a/src/assets/images/units/unit-wu-infantry-marine-actions.png and b/src/assets/images/units/unit-wu-infantry-marine-actions.png differ diff --git a/src/assets/images/units/unit-wu-infantry-marine.png b/src/assets/images/units/unit-wu-infantry-marine.png index a3c6338..8891e65 100644 Binary files a/src/assets/images/units/unit-wu-infantry-marine.png and b/src/assets/images/units/unit-wu-infantry-marine.png differ diff --git a/src/assets/images/units/unit-wu-infantry-river-actions.png b/src/assets/images/units/unit-wu-infantry-river-actions.png index a746cb5..fb3862d 100644 Binary files a/src/assets/images/units/unit-wu-infantry-river-actions.png and b/src/assets/images/units/unit-wu-infantry-river-actions.png differ diff --git a/src/assets/images/units/unit-wu-infantry-river.png b/src/assets/images/units/unit-wu-infantry-river.png index 826c3c3..8891e65 100644 Binary files a/src/assets/images/units/unit-wu-infantry-river.png and b/src/assets/images/units/unit-wu-infantry-river.png differ diff --git a/src/assets/images/units/unit-wu-infantry.png b/src/assets/images/units/unit-wu-infantry.png index 3ec2f89..8891e65 100644 Binary files a/src/assets/images/units/unit-wu-infantry.png and b/src/assets/images/units/unit-wu-infantry.png differ diff --git a/src/assets/images/units/unit-wu-officer-actions.png b/src/assets/images/units/unit-wu-officer-actions.png index 6428ace..00f0b81 100644 Binary files a/src/assets/images/units/unit-wu-officer-actions.png and b/src/assets/images/units/unit-wu-officer-actions.png differ diff --git a/src/assets/images/units/unit-wu-officer-harbor-actions.png b/src/assets/images/units/unit-wu-officer-harbor-actions.png index b85be6e..00f0b81 100644 Binary files a/src/assets/images/units/unit-wu-officer-harbor-actions.png and b/src/assets/images/units/unit-wu-officer-harbor-actions.png differ diff --git a/src/assets/images/units/unit-wu-officer-harbor.png b/src/assets/images/units/unit-wu-officer-harbor.png index 427e8e9..e51ad44 100644 Binary files a/src/assets/images/units/unit-wu-officer-harbor.png and b/src/assets/images/units/unit-wu-officer-harbor.png differ diff --git a/src/assets/images/units/unit-wu-officer-river-actions.png b/src/assets/images/units/unit-wu-officer-river-actions.png index 2f5dc36..00f0b81 100644 Binary files a/src/assets/images/units/unit-wu-officer-river-actions.png and b/src/assets/images/units/unit-wu-officer-river-actions.png differ diff --git a/src/assets/images/units/unit-wu-officer-river.png b/src/assets/images/units/unit-wu-officer-river.png index 66ea6a3..e51ad44 100644 Binary files a/src/assets/images/units/unit-wu-officer-river.png and b/src/assets/images/units/unit-wu-officer-river.png differ diff --git a/src/assets/images/units/unit-wu-officer.png b/src/assets/images/units/unit-wu-officer.png index 544f1bc..e51ad44 100644 Binary files a/src/assets/images/units/unit-wu-officer.png and b/src/assets/images/units/unit-wu-officer.png differ diff --git a/src/assets/images/units/unit-wu-strategist-actions.png b/src/assets/images/units/unit-wu-strategist-actions.png index f1ff10b..4439cd0 100644 Binary files a/src/assets/images/units/unit-wu-strategist-actions.png and b/src/assets/images/units/unit-wu-strategist-actions.png differ diff --git a/src/assets/images/units/unit-wu-strategist-fire-actions.png b/src/assets/images/units/unit-wu-strategist-fire-actions.png index 33bc97d..4439cd0 100644 Binary files a/src/assets/images/units/unit-wu-strategist-fire-actions.png and b/src/assets/images/units/unit-wu-strategist-fire-actions.png differ diff --git a/src/assets/images/units/unit-wu-strategist-fire.png b/src/assets/images/units/unit-wu-strategist-fire.png index e5be7ae..a260327 100644 Binary files a/src/assets/images/units/unit-wu-strategist-fire.png and b/src/assets/images/units/unit-wu-strategist-fire.png differ diff --git a/src/assets/images/units/unit-wu-strategist-river-actions.png b/src/assets/images/units/unit-wu-strategist-river-actions.png index 522d176..4439cd0 100644 Binary files a/src/assets/images/units/unit-wu-strategist-river-actions.png and b/src/assets/images/units/unit-wu-strategist-river-actions.png differ diff --git a/src/assets/images/units/unit-wu-strategist-river.png b/src/assets/images/units/unit-wu-strategist-river.png index 06603c7..a260327 100644 Binary files a/src/assets/images/units/unit-wu-strategist-river.png and b/src/assets/images/units/unit-wu-strategist-river.png differ diff --git a/src/assets/images/units/unit-wu-strategist.png b/src/assets/images/units/unit-wu-strategist.png index 2c6dd5f..a260327 100644 Binary files a/src/assets/images/units/unit-wu-strategist.png and b/src/assets/images/units/unit-wu-strategist.png differ diff --git a/src/assets/images/units/unit-wu-yi-actions.png b/src/assets/images/units/unit-wu-yi-actions.png index f276998..5c21f4b 100644 Binary files a/src/assets/images/units/unit-wu-yi-actions.png and b/src/assets/images/units/unit-wu-yi-actions.png differ diff --git a/src/assets/images/units/unit-wu-yi.png b/src/assets/images/units/unit-wu-yi.png index ff16c25..46c5cd3 100644 Binary files a/src/assets/images/units/unit-wu-yi.png and b/src/assets/images/units/unit-wu-yi.png differ diff --git a/src/assets/images/units/unit-yan-yan-actions.png b/src/assets/images/units/unit-yan-yan-actions.png index 668cecc..bde7654 100644 Binary files a/src/assets/images/units/unit-yan-yan-actions.png and b/src/assets/images/units/unit-yan-yan-actions.png differ diff --git a/src/assets/images/units/unit-yan-yan.png b/src/assets/images/units/unit-yan-yan.png index ce895f7..aa6e79f 100644 Binary files a/src/assets/images/units/unit-yan-yan.png and b/src/assets/images/units/unit-yan-yan.png differ diff --git a/src/assets/images/units/unit-yi-ji-actions.png b/src/assets/images/units/unit-yi-ji-actions.png index 497f422..e37ae40 100644 Binary files a/src/assets/images/units/unit-yi-ji-actions.png and b/src/assets/images/units/unit-yi-ji-actions.png differ diff --git a/src/assets/images/units/unit-yi-ji.png b/src/assets/images/units/unit-yi-ji.png index 2a7a53f..a667e9e 100644 Binary files a/src/assets/images/units/unit-yi-ji.png and b/src/assets/images/units/unit-yi-ji.png differ diff --git a/src/assets/images/units/unit-zhang-fei-actions.png b/src/assets/images/units/unit-zhang-fei-actions.png index 3f8e478..66c1e13 100644 Binary files a/src/assets/images/units/unit-zhang-fei-actions.png and b/src/assets/images/units/unit-zhang-fei-actions.png differ diff --git a/src/assets/images/units/unit-zhang-fei.png b/src/assets/images/units/unit-zhang-fei.png index 0d46710..b440471 100644 Binary files a/src/assets/images/units/unit-zhang-fei.png and b/src/assets/images/units/unit-zhang-fei.png differ diff --git a/src/assets/images/units/unit-zhao-yun-actions.png b/src/assets/images/units/unit-zhao-yun-actions.png index b3c3cb3..025f56d 100644 Binary files a/src/assets/images/units/unit-zhao-yun-actions.png and b/src/assets/images/units/unit-zhao-yun-actions.png differ diff --git a/src/assets/images/units/unit-zhao-yun.png b/src/assets/images/units/unit-zhao-yun.png index 6f7580a..ded279f 100644 Binary files a/src/assets/images/units/unit-zhao-yun.png and b/src/assets/images/units/unit-zhao-yun.png differ diff --git a/src/assets/images/units/unit-zhuge-liang-actions.png b/src/assets/images/units/unit-zhuge-liang-actions.png index dd4e6ba..2b8a45a 100644 Binary files a/src/assets/images/units/unit-zhuge-liang-actions.png and b/src/assets/images/units/unit-zhuge-liang-actions.png differ diff --git a/src/assets/images/units/unit-zhuge-liang.png b/src/assets/images/units/unit-zhuge-liang.png index 1751b48..9dd7fdf 100644 Binary files a/src/assets/images/units/unit-zhuge-liang.png and b/src/assets/images/units/unit-zhuge-liang.png differ diff --git a/src/game/data/battles.ts b/src/game/data/battles.ts index 4195425..a17e0a2 100644 --- a/src/game/data/battles.ts +++ b/src/game/data/battles.ts @@ -3610,7 +3610,7 @@ export const fiftyFifthBattleScenario: BattleScenarioDefinition = { bonds: fiftyFifthBattleBonds, mapTextureKey: 'battle-map-fifty-fifth', leaderUnitId: 'northern-first-leader-cao-zhen', - quickVictoryTurnLimit: 110, + quickVictoryTurnLimit: 90, baseVictoryGold: 14920, objectives: [ { @@ -3651,9 +3651,9 @@ export const fiftyFifthBattleScenario: BattleScenarioDefinition = { { id: 'quick', kind: 'quick-victory', - label: '110턴 이내 출진로 확보', + label: '90턴 이내 출진로 확보', rewardGold: 3360, - maxTurn: 110 + maxTurn: 90 } ], defeatConditions: [{ kind: 'unit-defeated', unitId: 'zhuge-liang' }], @@ -4427,7 +4427,7 @@ export const sixtySixthBattleScenario: BattleScenarioDefinition = { bonds: sixtySixthBattleBonds, mapTextureKey: 'battle-map-sixty-sixth', leaderUnitId: 'northern-twelfth-leader-sima-yi', - quickVictoryTurnLimit: 132, + quickVictoryTurnLimit: 90, baseVictoryGold: 22400, objectives: [ { @@ -4468,9 +4468,9 @@ export const sixtySixthBattleScenario: BattleScenarioDefinition = { { id: 'quick', kind: 'quick-victory', - label: '132턴 이내 오장원 수습', + label: '90턴 이내 오장원 수습', rewardGold: 5000, - maxTurn: 132 + maxTurn: 90 } ], defeatConditions: [{ kind: 'unit-defeated', unitId: 'zhuge-liang' }], diff --git a/src/game/data/scenario.ts b/src/game/data/scenario.ts index 87d0f79..d71dd2b 100644 --- a/src/game/data/scenario.ts +++ b/src/game/data/scenario.ts @@ -17623,7 +17623,7 @@ const fiftyFifthBattleAllyPositions: Record = 'wang-ping': { x: 34, y: 68 } }; -export const fiftyFifthBattleUnits: UnitData[] = [ +const fiftyFifthBattleUnitsBase: UnitData[] = [ ...[ ...firstBattleUnits.filter((unit) => unit.faction === 'ally' && unit.id !== 'liu-bei'), ...xuzhouRecruitUnits, @@ -17688,6 +17688,29 @@ export const fiftyFifthBattleUnits: UnitData[] = [ createThirtyFirstEnemyUnit('northern-first-leader-cao-zhen', '조진', '위군 대장', 'rebelLeader', 178, 1240, 296, 102, 24) ]; +export const fiftyFifthBattleUnits: UnitData[] = tuneEnemyUnitGroup(fiftyFifthBattleUnitsBase, ['northern-first-'], { + hpScale: 0.5, + attackScale: 0.55, + overrides: { + 'northern-first-infantry-g': { hp: 520, attack: 160, x: 90, y: 34 }, + 'northern-first-archer-f': { hp: 420, attack: 150, x: 89, y: 31 }, + 'northern-first-cavalry-f': { hp: 540, attack: 164, x: 91, y: 41 }, + 'northern-first-ridge-guard-f': { hp: 520, attack: 158, x: 86, y: 30 }, + 'northern-first-strategist-d': { hp: 420, attack: 148, x: 88, y: 28 }, + 'northern-first-officer-fei-yao': { hp: 420, attack: 142, x: 84, y: 50 }, + 'northern-first-leader-cao-zhen': { hp: 420, attack: 148, x: 82, y: 38 } + } +}).map((unit) => + unit.faction === 'ally' + ? { + ...unit, + hp: Math.max(unit.hp, 2200), + maxHp: Math.max(unit.maxHp, 2200), + attack: Math.max(unit.attack, 340) + } + : unit +); + const fiftySixthBattleAllyPositions: Record = { 'guan-yu': { x: 16, y: 80 }, 'zhang-fei': { x: 22, y: 82 }, @@ -18698,7 +18721,7 @@ const sixtySixthEnemySpecs: ReadonlyArray< ['northern-twelfth-leader-sima-yi', '사마의', '오장원 총지휘관', 'rebelLeader', 254, 2100, 486, 124, 52] ]; -export const sixtySixthBattleUnits: UnitData[] = [ +const sixtySixthBattleUnitsBase: UnitData[] = [ ...[ ...firstBattleUnits.filter((unit) => unit.faction === 'ally' && unit.id !== 'liu-bei'), ...xuzhouRecruitUnits, @@ -18723,6 +18746,33 @@ export const sixtySixthBattleUnits: UnitData[] = [ ...sixtySixthEnemySpecs.map((spec) => createThirtyFirstEnemyUnit(...spec)) ]; +export const sixtySixthBattleUnits: UnitData[] = tuneEnemyUnitGroup(sixtySixthBattleUnitsBase, ['northern-twelfth-'], { + hpScale: 0.45, + attackScale: 0.52, + overrides: { + 'northern-twelfth-infantry-d': { hp: 420, attack: 142, x: 104, y: 60 }, + 'northern-twelfth-infantry-e': { hp: 620, attack: 176, x: 108, y: 50 }, + 'northern-twelfth-infantry-f': { hp: 620, attack: 176, x: 112, y: 42 }, + 'northern-twelfth-camp-guard-e': { hp: 560, attack: 140, x: 106, y: 54 }, + 'northern-twelfth-camp-guard-f': { hp: 560, attack: 140, x: 112, y: 46 }, + 'northern-twelfth-strategist-c': { hp: 380, attack: 132, x: 108, y: 44 }, + 'northern-twelfth-strategist-d': { hp: 380, attack: 132, x: 112, y: 62 }, + 'northern-twelfth-officer-ledger': { hp: 700, attack: 150, x: 100, y: 70 }, + 'northern-twelfth-officer-guo-huai': { hp: 760, attack: 152, x: 106, y: 68 }, + 'northern-twelfth-officer-zhang-he': { hp: 760, attack: 154, x: 92, y: 72 }, + 'northern-twelfth-leader-sima-yi': { hp: 900, attack: 156, x: 106, y: 58 } + } +}).map((unit) => + unit.faction === 'ally' + ? { + ...unit, + hp: Math.max(unit.hp, 2600), + maxHp: Math.max(unit.maxHp, 2600), + attack: Math.max(unit.attack, 420) + } + : unit +); + export const firstBattleBonds: BattleBond[] = [ { id: 'liu-bei__guan-yu', diff --git a/src/game/scenes/BattleScene.ts b/src/game/scenes/BattleScene.ts index e50595c..652820c 100644 --- a/src/game/scenes/BattleScene.ts +++ b/src/game/scenes/BattleScene.ts @@ -921,6 +921,32 @@ const unitTexture: Record = { 'menghuo-final-leader': 'unit-meng-huo' }; +const canonicalAllyNames: Record = { + 'liu-bei': '유비', + 'guan-yu': '관우', + 'zhang-fei': '장비', + 'jian-yong': '간옹', + 'mi-zhu': '미축', + 'sun-qian': '손건', + 'zhao-yun': '조운', + 'zhuge-liang': '제갈량', + 'ma-liang': '마량', + 'yi-ji': '이적', + 'gong-zhi': '공지', + 'huang-zhong': '황충', + 'wei-yan': '위연', + 'pang-tong': '방통', + 'fa-zheng': '법정', + 'wu-yi': '오의', + 'yan-yan': '엄안', + 'li-yan': '이엄', + 'huang-quan': '황권', + 'ma-chao': '마초', + 'ma-dai': '마대', + 'wang-ping': '왕평', + 'jiang-wei': '강유' +}; + const unitTextureByClass: Partial> = { lord: 'unit-liu-bei', infantry: 'unit-guan-yu', @@ -9744,7 +9770,17 @@ export class BattleScene extends Phaser.Scene { } private unitName(unitId: string) { - return battleUnits.find((unit) => unit.id === unitId)?.name ?? unitId; + const battleName = battleUnits.find((unit) => unit.id === unitId)?.name; + if (battleName) { + return battleName; + } + + const canonicalName = canonicalAllyNames[unitId]; + if (canonicalName) { + return canonicalName; + } + + return getCampaignState().roster.find((unit) => unit.id === unitId)?.name ?? unitId; } private renderRosterPanel(tab: RosterTab = this.rosterTab, message?: string) {