游戏可以运行

This commit is contained in:
2026-05-24 08:10:22 +08:00
commit 035b2f7af9
28 changed files with 4222 additions and 0 deletions

340
card_game/ink_style.py Normal file
View File

@@ -0,0 +1,340 @@
"""Ink painting style rendering primitives for Chinese aesthetic."""
import random
import math
import pygame
from card_game.config import (
WINDOW_WIDTH, WINDOW_HEIGHT,
INK_BLACK, PAPER_WHITE, ZHU_HONG, SONGHUA_GREEN, TENG_HUANG, GOLD,
BG_COLOR, INK_WASH_1, INK_WASH_2, INK_WASH_3, INK_WASH_4, INK_WASH_5,
)
# Cached surfaces
_paper_texture = None
_mountain_layers = None
def init_cache():
"""Pre-generate cached surfaces. Call once after pygame.init()."""
global _paper_texture, _mountain_layers
_paper_texture = _generate_paper_texture(WINDOW_WIDTH, WINDOW_HEIGHT)
_mountain_layers = _generate_mountain_layers(WINDOW_WIDTH, WINDOW_HEIGHT)
def get_paper_texture():
return _paper_texture
def get_mountain_layers():
return _mountain_layers
def _generate_paper_texture(w, h):
"""Generate a rice paper (宣纸) texture surface."""
surf = pygame.Surface((w, h))
surf.fill(BG_COLOR)
# Add subtle noise
rng = random.Random(42)
for y in range(0, h, 2):
for x in range(0, w, 2):
noise = rng.gauss(0, 6)
r = min(255, max(0, int(BG_COLOR[0] + noise)))
g = min(255, max(0, int(BG_COLOR[1] + noise)))
b = min(255, max(0, int(BG_COLOR[2] + noise)))
surf.set_at((x, y), (r, g, b))
if x + 1 < w:
surf.set_at((x + 1, y), (r, g, b))
if y + 1 < h:
surf.set_at((x, y + 1), (r, g, b))
if x + 1 < w and y + 1 < h:
surf.set_at((x + 1, y + 1), (r, g, b))
# Add fiber lines
for _ in range(40):
x1 = rng.randint(0, w)
y1 = rng.randint(0, h)
length = rng.randint(30, 150)
angle = rng.uniform(0, math.pi)
color = (rng.randint(200, 220), rng.randint(185, 205), rng.randint(160, 180))
points = []
for i in range(10):
t = i / 9
px = int(x1 + length * t * math.cos(angle) + rng.gauss(0, 2))
py = int(y1 + length * t * math.sin(angle) + rng.gauss(0, 2))
points.append((px, py))
if len(points) >= 2:
pygame.draw.lines(surf, color, False, points, 1)
return surf
def _generate_mountain_layers(w, h):
"""Generate 3 layers of misty mountain silhouettes."""
rng = random.Random(123)
layers = []
layer_colors = [
(INK_WASH_1[0], INK_WASH_1[1], INK_WASH_1[2], 60),
(INK_WASH_2[0], INK_WASH_2[1], INK_WASH_2[2], 45),
(INK_WASH_3[0], INK_WASH_3[1], INK_WASH_3[2], 30),
]
base_heights = [h * 0.55, h * 0.62, h * 0.70]
for i, (color, base_y) in enumerate(zip(layer_colors, base_heights)):
surf = pygame.Surface((w, h), pygame.SRCALPHA)
points = [(0, h)]
x = 0
while x <= w:
freq1 = rng.uniform(0.003, 0.008)
freq2 = rng.uniform(0.01, 0.02)
amp1 = rng.uniform(30, 60)
amp2 = rng.uniform(10, 25)
y = base_y + amp1 * math.sin(x * freq1 + i) + amp2 * math.sin(x * freq2 + i * 2)
points.append((x, int(y)))
x += rng.randint(8, 20)
points.append((w, h))
if len(points) >= 3:
pygame.draw.polygon(surf, color, points)
layers.append(surf)
return layers
def blit_paper_background(surface):
"""Blit the cached paper texture onto the surface."""
if _paper_texture:
surface.blit(_paper_texture, (0, 0))
else:
surface.fill(BG_COLOR)
def blit_mountains(surface):
"""Blit mountain layers onto the surface."""
if _mountain_layers:
for layer in _mountain_layers:
surface.blit(layer, (0, 0))
def draw_ink_rect(surface, rect, color, alpha=255, border_radius=0):
"""Draw a rectangle with slightly wobbly brush-stroke edges."""
x, y, w, h = rect if isinstance(rect, (list, tuple)) else (rect.x, rect.y, rect.w, rect.h)
s = pygame.Surface((w, h), pygame.SRCALPHA)
base_color = (*color[:3], alpha)
# Draw base rect with slight gradient
for col in range(w):
gradient = 1.0 - 0.1 * (col / max(w, 1))
r = min(255, max(0, int(color[0] * gradient)))
g = min(255, max(0, int(color[1] * gradient)))
b = min(255, max(0, int(color[2] * gradient)))
pygame.draw.line(s, (r, g, b, alpha), (col, 0), (col, h - 1))
# Feather edges
rng = random.Random(x * 1000 + y)
for edge_x in range(min(3, w)):
for edge_y in range(h):
if rng.random() < 0.3:
s.set_at((edge_x, edge_y), (0, 0, 0, 0))
if rng.random() < 0.3:
s.set_at((w - 1 - edge_x, edge_y), (0, 0, 0, 0))
for edge_y in range(min(3, h)):
for edge_x in range(w):
if rng.random() < 0.3:
s.set_at((edge_x, edge_y), (0, 0, 0, 0))
if rng.random() < 0.3:
s.set_at((edge_x, h - 1 - edge_y), (0, 0, 0, 0))
surface.blit(s, (x, y))
def draw_ink_circle(surface, center, radius, color, alpha=200):
"""Draw a circle with irregular ink-wash edges."""
cx, cy = center
size = radius * 2 + 4
s = pygame.Surface((size, size), pygame.SRCALPHA)
scx, scy = size // 2, size // 2
rng = random.Random(cx * 100 + cy)
n = max(12, radius)
for i in range(n):
angle = 2 * math.pi * i / n
r = radius + rng.gauss(0, max(1, radius * 0.08))
px = int(scx + r * math.cos(angle))
py = int(scy + r * math.sin(angle))
pygame.draw.circle(s, (*color[:3], alpha), (px, py), max(2, int(radius * 0.4)))
# Fill center
pygame.draw.circle(s, (*color[:3], alpha), (scx, scy), max(1, radius - 2))
surface.blit(s, (cx - size // 2, cy - size // 2))
def draw_brush_stroke(surface, start, end, width, color, alpha=180):
"""Draw a thick-to-thin brush stroke line."""
x1, y1 = start
x2, y2 = end
dx, dy = x2 - x1, y2 - y1
length = math.sqrt(dx * dx + dy * dy)
if length < 1:
return
steps = max(int(length / 2), 4)
rng = random.Random(int(x1 * 100 + y1))
# Perpendicular direction for jitter
nx, ny = -dy / length, dx / length
for i in range(steps):
t = i / (steps - 1)
# Width tapers: starts thin, peaks middle, ends thin
w = width * (1 - abs(2 * t - 1) * 0.6) * (0.8 + 0.2 * rng.random())
px = x1 + dx * t + nx * rng.gauss(0, 1.5)
py = y1 + dy * t + ny * rng.gauss(0, 1.5)
a = max(50, int(alpha * (0.7 + 0.3 * rng.random())))
circle_s = pygame.Surface((int(w * 2 + 4), int(w * 2 + 4)), pygame.SRCALPHA)
pygame.draw.circle(circle_s, (*color[:3], a),
(int(w + 2), int(w + 2)), max(1, int(w)))
surface.blit(circle_s, (int(px - w - 2), int(py - w - 2)))
def draw_seal_stamp(surface, rect, text, font, color=None):
"""Draw a traditional Chinese seal (印章): red square with white text."""
if color is None:
color = ZHU_HONG
x, y, w, h = rect if isinstance(rect, (list, tuple)) else (rect.x, rect.y, rect.w, rect.h)
s = pygame.Surface((w, h), pygame.SRCALPHA)
# Red background with slight irregularity
rng = random.Random(x * 100 + y + w)
pygame.draw.rect(s, color, (0, 0, w, h))
# Feather edges slightly
for edge in range(2):
for i in range(w):
if rng.random() < 0.25:
s.set_at((i, edge), (0, 0, 0, 0))
s.set_at((i, h - 1 - edge), (0, 0, 0, 0))
for i in range(h):
if rng.random() < 0.25:
s.set_at((edge, i), (0, 0, 0, 0))
s.set_at((w - 1 - edge, i), (0, 0, 0, 0))
# White border
pygame.draw.rect(s, (255, 255, 255), (0, 0, w, h), 2)
# Text in white
text_surf = font.render(text, True, (255, 255, 255))
tx = (w - text_surf.get_width()) // 2
ty = (h - text_surf.get_height()) // 2
s.blit(text_surf, (tx, ty))
surface.blit(s, (x, y))
def draw_scroll(surface, rect, scroll_color=None):
"""Draw a scroll/卷轴 shape with wooden rollers."""
if scroll_color is None:
scroll_color = (210, 195, 170)
x, y, w, h = rect if isinstance(rect, (list, tuple)) else (rect.x, rect.y, rect.w, rect.h)
roller_h = 6
roller_color = (140, 90, 50)
# Top roller
pygame.draw.rect(surface, roller_color, (x - 4, y, w + 8, roller_h), border_radius=3)
pygame.draw.rect(surface, (100, 65, 35), (x - 4, y, w + 8, roller_h), 1, border_radius=3)
# Paper body
body_rect = pygame.Rect(x, y + roller_h, w, h - roller_h * 2)
draw_ink_rect(surface, body_rect, scroll_color, alpha=240)
# Bottom roller
by = y + h - roller_h
pygame.draw.rect(surface, roller_color, (x - 4, by, w + 8, roller_h), border_radius=3)
pygame.draw.rect(surface, (100, 65, 35), (x - 4, by, w + 8, roller_h), 1, border_radius=3)
def draw_ink_text(surface, text, pos, font, color, shadow=True):
"""Render text with a subtle ink bleed shadow."""
if shadow:
shadow_color = (max(0, color[0] - 40), max(0, color[1] - 40), max(0, color[2] - 40))
shadow_surf = font.render(text, True, shadow_color)
surface.blit(shadow_surf, (pos[0] + 1, pos[1] + 1))
text_surf = font.render(text, True, color)
surface.blit(text_surf, pos)
return text_surf
def draw_cloud_pattern(surface, rect):
"""Draw traditional Chinese cloud motifs (祥云) as decoration."""
x, y, w, h = rect if isinstance(rect, (list, tuple)) else (rect.x, rect.y, rect.w, rect.h)
color = (*INK_WASH_2[:3], 80)
s = pygame.Surface((w, h), pygame.SRCALPHA)
rng = random.Random(x * 7 + y)
n_clouds = max(1, w // 80)
for i in range(n_clouds):
cx = rng.randint(10, w - 10)
cy = rng.randint(5, h - 5)
# Simple cloud: overlapping arcs
for j in range(3):
r = rng.randint(6, 12)
ox = j * 8 - 8
oy = rng.randint(-3, 3)
pygame.draw.circle(s, color, (cx + ox, cy + oy), r)
surface.blit(s, (x, y))
def draw_zone_bg(surface, rect, base_color, accent_color=None):
"""Draw a battlefield zone background with ink wash effect."""
x, y, w, h = rect if isinstance(rect, (list, tuple)) else (rect.x, rect.y, rect.w, rect.h)
s = pygame.Surface((w, h), pygame.SRCALPHA)
# Base fill
s.fill((*base_color[:3], 120))
# Subtle horizontal brush strokes
rng = random.Random(y)
for _ in range(5):
by = rng.randint(0, h)
bx = rng.randint(0, w // 4)
bw = rng.randint(w // 3, w)
bh = rng.randint(2, 6)
bc = (*INK_WASH_2[:3], rng.randint(15, 35))
pygame.draw.rect(s, bc, (bx, by, bw, bh))
surface.blit(s, (x, y))
# Border line
if accent_color:
pygame.draw.line(surface, accent_color, (x, y + h - 1), (x + w, y + h - 1), 1)
def draw_ink_hp_bar(surface, x, y, w, h, ratio, bg_color=None):
"""Draw an HP bar with ink brush style."""
if bg_color is None:
bg_color = INK_WASH_4
pygame.draw.rect(surface, bg_color, (x, y, w, h))
if ratio > 0:
bar_w = max(1, int(w * ratio))
if ratio > 0.5:
bar_color = SONGHUA_GREEN
elif ratio > 0.25:
bar_color = TENG_HUANG
else:
bar_color = ZHU_HONG
# Brush-stroke bar
rng = random.Random(x * 100 + y)
for px in range(bar_w):
thickness = h - rng.randint(0, 1)
pygame.draw.line(surface, bar_color,
(x + px, y + (h - thickness) // 2),
(x + px, y + (h + thickness) // 2))