Download graphwar/render.py from Foxgrad/graphwar-arena: direct link, hf CLI and curl.
- Browser
- Download file 6.55 kB
-
https://huggingface.co/Foxgrad/graphwar-arena/resolve/main/graphwar/render.py
- Command line
-
hf download hf://Foxgrad/graphwar-arena/graphwar/render.py
-
curl -L -o render.py https://huggingface.co/Foxgrad/graphwar-arena/resolve/main/graphwar/render.py
6.55 kB
| """Dessin du plateau avec pygame (vue de dessus, ou inclinée quand un tir part en 3D).""" | |
| from __future__ import annotations | |
| import math | |
| import numpy as np | |
| import pygame | |
| from . import config as C | |
| PAPER = (244, 246, 241) | |
| GRID_MINOR = (226, 232, 233) | |
| GRID_MAJOR = (196, 208, 214) | |
| AXIS = (128, 140, 150) | |
| INK = (34, 38, 50) | |
| INK_SIDE = (70, 76, 92) | |
| DEAD = (150, 150, 150) | |
| SHADOW = (175, 180, 188) | |
| MAX_TILT = math.radians(52) | |
| def font(size, bold=False): | |
| return pygame.font.SysFont("cambriamath,cambria,segoeuisymbol,dejavusans,arial", size, bold=bold) | |
| class Camera: | |
| def __init__(self, rect: pygame.Rect): | |
| self.rect = pygame.Rect(rect) | |
| self.s = min(self.rect.w / (2 * C.BOARD_W), self.rect.h / (2 * C.BOARD_H)) | |
| self.tilt = 0.0 # 0 = vue de dessus, 1 = inclinée au maximum | |
| def p(self, x, y, z=0.0): | |
| th = self.tilt * MAX_TILT | |
| cx, cy = self.rect.centerx, self.rect.centery + self.tilt * 0.12 * self.rect.h | |
| return (cx + x * self.s, cy - (y * math.cos(th) + z * math.sin(th)) * self.s) | |
| def pts(self, xs, ys, zs): | |
| th = self.tilt * MAX_TILT | |
| cx, cy = self.rect.centerx, self.rect.centery + self.tilt * 0.12 * self.rect.h | |
| X = cx + xs * self.s | |
| Y = cy - (ys * math.cos(th) + zs * math.sin(th)) * self.s | |
| return np.column_stack([X, Y]) | |
| def _cyl(surf, cam, x, y, r, h, top, side, off=(0, 0)): | |
| """Un cylindre vu en biais : flanc puis dessus (off = décalage du calque).""" | |
| th = cam.tilt * MAX_TILT | |
| rw, rh = r * cam.s, max(1.0, r * cam.s * math.cos(th)) | |
| bx, by = cam.p(x, y, 0) | |
| tx, ty = cam.p(x, y, h) | |
| bx, by, tx, ty = bx - off[0], by - off[1], tx - off[0], ty - off[1] | |
| pygame.draw.polygon(surf, side, [(bx - rw, by), (bx + rw, by), (tx + rw, ty), (tx - rw, ty)]) | |
| pygame.draw.ellipse(surf, side, (bx - rw, by - rh, 2 * rw, 2 * rh)) | |
| pygame.draw.ellipse(surf, top, (tx - rw, ty - rh, 2 * rw, 2 * rh)) | |
| def _obstacle(surf, cam, ob, craters): | |
| """Dessine un obstacle avec ses cratères (chaque trou ne creuse que son propre obstacle).""" | |
| x, y, r, h = ob | |
| tilted = cam.tilt > 0.01 | |
| th = cam.tilt * MAX_TILT | |
| rw = r * cam.s | |
| rh = max(1.0, rw * math.cos(th)) | |
| bx, by = cam.p(x, y, 0) | |
| ty = cam.p(x, y, h)[1] if tilted else by | |
| box = pygame.Rect(int(bx - rw) - 2, int(ty - rh) - 2, int(2 * rw) + 5, int(by - ty + 2 * rh) + 5) | |
| spr = pygame.Surface(box.size, pygame.SRCALPHA) | |
| off = box.topleft | |
| if tilted: | |
| _cyl(spr, cam, x, y, r, h, INK, INK_SIDE, off) | |
| else: | |
| c = cam.p(x, y) | |
| pygame.draw.circle(spr, INK, (c[0] - off[0], c[1] - off[1]), rw) | |
| clear = (0, 0, 0, 0) | |
| for cx, cy, cr in craters: | |
| if math.hypot(cx - x, cy - y) > r + cr: | |
| continue | |
| if tilted: | |
| _cyl(spr, cam, cx, cy, cr, h + 0.05, clear, clear, off) | |
| else: | |
| c = cam.p(cx, cy) | |
| pygame.draw.circle(spr, clear, (c[0] - off[0], c[1] - off[1]), cr * cam.s) | |
| surf.blit(spr, off) | |
| def draw_board(surf, cam, world, colors=((60, 90, 200), (200, 70, 60)), highlight=None): | |
| """Papier millimétré, obstacles, cratères, soldats.""" | |
| r = cam.rect | |
| pygame.draw.rect(surf, PAPER, r.inflate(4, 4)) | |
| # quadrillage | |
| for x in range(-25, 26): | |
| col = AXIS if x == 0 else GRID_MAJOR if x % 5 == 0 else GRID_MINOR | |
| pygame.draw.line(surf, col, cam.p(x, -C.BOARD_H), cam.p(x, C.BOARD_H), 2 if x == 0 else 1) | |
| for y in range(-15, 16): | |
| col = AXIS if y == 0 else GRID_MAJOR if y % 5 == 0 else GRID_MINOR | |
| pygame.draw.line(surf, col, cam.p(-C.BOARD_W, y), cam.p(C.BOARD_W, y), 2 if y == 0 else 1) | |
| corners = [cam.p(-25, -15), cam.p(25, -15), cam.p(25, 15), cam.p(-25, 15)] | |
| pygame.draw.polygon(surf, AXIS, corners, 2) | |
| # obstacles (on ne dessine pas hors du plateau en vue de dessus) | |
| clip = surf.get_clip() | |
| if cam.tilt <= 0.01: | |
| surf.set_clip(r) | |
| order = np.argsort(-world.obstacles[:, 1]) if cam.tilt > 0.01 else range(len(world.obstacles)) | |
| for i in order: | |
| _obstacle(surf, cam, world.obstacles[i], world.craters) | |
| surf.set_clip(clip) | |
| # soldats | |
| soldiers = sorted(enumerate(world.soldiers), key=lambda t: -t[1].y) | |
| for i, s in soldiers: | |
| col = colors[s.team] if s.alive else DEAD | |
| rad = C.SOLDIER_R * cam.s | |
| if cam.tilt > 0.01: | |
| b, t = cam.p(s.x, s.y, 0), cam.p(s.x, s.y, C.SOLDIER_H) | |
| pygame.draw.line(surf, col, b, t, max(3, int(rad * 0.8))) | |
| pygame.draw.circle(surf, col, t, rad * 0.7) | |
| pygame.draw.circle(surf, INK, t, rad * 0.7, 1) | |
| else: | |
| pygame.draw.circle(surf, col, cam.p(s.x, s.y), rad) | |
| pygame.draw.circle(surf, INK, cam.p(s.x, s.y), rad, 2 if i == highlight else 1) | |
| if not s.alive: | |
| c = cam.p(s.x, s.y, C.SOLDIER_H if cam.tilt > 0.01 else 0) | |
| d = rad * 0.7 | |
| pygame.draw.line(surf, INK, (c[0] - d, c[1] - d), (c[0] + d, c[1] + d), 2) | |
| pygame.draw.line(surf, INK, (c[0] - d, c[1] + d), (c[0] + d, c[1] - d), 2) | |
| if i == highlight and s.alive: | |
| pygame.draw.circle(surf, INK, cam.p(s.x, s.y), rad + 5, 1) | |
| def draw_shot(surf, cam, res, color, frac=1.0, width=3, faded=False): | |
| n = len(res.xs) | |
| if n < 2: | |
| return None | |
| k = max(2, int(n * frac)) | |
| step = max(1, k // 900) | |
| xs, ys, zs = res.xs[:k:step], res.ys[:k:step], res.zs[:k:step] | |
| xs, ys, zs = np.append(xs, res.xs[k - 1]), np.append(ys, res.ys[k - 1]), np.append(zs, res.zs[k - 1]) | |
| col = tuple(int(c + (255 - c) * 0.6) for c in color) if faded else color | |
| if cam.tilt > 0.01 and (zs > C.SHOT_Z + 0.05).any(): # ombre au sol | |
| sh = cam.pts(xs, ys, np.zeros_like(zs)) | |
| pygame.draw.lines(surf, SHADOW, False, sh.tolist(), 1) | |
| pts = cam.pts(xs, ys, zs if cam.tilt > 0.01 else np.zeros_like(zs)) | |
| pygame.draw.lines(surf, col, False, pts.tolist(), width) | |
| head = pts[-1] | |
| if not faded: | |
| pygame.draw.circle(surf, col, head, width + 3) | |
| pygame.draw.circle(surf, INK, head, width + 3, 1) | |
| return head | |
| def draw_boom(surf, center, t, color): | |
| """Petite explosion qui grossit puis s'efface (t de 0 à 1).""" | |
| r = 6 + 34 * t | |
| a = int(255 * (1 - t)) | |
| s = pygame.Surface((int(2 * r + 4), int(2 * r + 4)), pygame.SRCALPHA) | |
| pygame.draw.circle(s, (*color, a), (r + 2, r + 2), r, 4) | |
| pygame.draw.circle(s, (*INK, a // 2), (r + 2, r + 2), r * 0.55, 2) | |
| surf.blit(s, (center[0] - r - 2, center[1] - r - 2)) | |