mirror of
https://github.com/EDeev/pixel_gamble.git
synced 2026-10-07 23:49:29 +03:00
- 10 волн, 5 видов врагов со своим поведением и босс в три фазы - меч, копьё и посох, 11 улучшений между волнами, опыт и уровни - меню, пауза, настройки, итоги партии и рекорд, русский и английский - мир 480×270 с масштабированием, интерфейс в 960×540, скорости от dt - асинхронный цикл: та же игра в браузере через pygbag - тесты без окна, автоигрок, CI, релизы exe/Linux, GitHub Pages - README на двух языках; исходная версия — релиз v1.0.0
111 lines
4.7 KiB
Python
111 lines
4.7 KiB
Python
"""Арена: пол, деревья, камни, точки появления врагов."""
|
||
|
||
import random
|
||
|
||
import pygame
|
||
|
||
from . import assets
|
||
from .settings import TILE
|
||
|
||
COLS, ROWS = 52, 34
|
||
SEED = 2022 # раскладка камней и кустов одна и та же в каждой партии
|
||
|
||
GROVES = [(9, 7), (11, 8), (40, 6), (42, 7), (8, 25), (10, 26), (41, 24), (39, 25), (25, 5), (26, 27)]
|
||
STUMPS = [(17, 12), (34, 12), (17, 22), (34, 22), (25, 16), (21, 9), (30, 25)]
|
||
|
||
|
||
class Prop(pygame.sprite.Sprite):
|
||
"""Дерево или камень: рисуется с сортировкой по глубине и мешает проходу."""
|
||
|
||
def __init__(self, image, topleft, hitbox, breakable=False):
|
||
super().__init__()
|
||
self.image = image
|
||
self.rect = image.get_rect(topleft=topleft)
|
||
self.hitbox = hitbox
|
||
self.breakable = breakable
|
||
self.sort_y = self.hitbox.bottom
|
||
|
||
|
||
class World:
|
||
def __init__(self):
|
||
self.width, self.height = COLS * TILE, ROWS * TILE
|
||
self.rect = pygame.Rect(0, 0, self.width, self.height)
|
||
self.props = pygame.sprite.Group()
|
||
edge = 2 * TILE # толщина кольца деревьев
|
||
self.walls = [pygame.Rect(0, 0, self.width, edge), pygame.Rect(0, self.height - edge, self.width, edge),
|
||
pygame.Rect(0, 0, edge, self.height), pygame.Rect(self.width - edge, 0, edge, self.height)]
|
||
self.inner = pygame.Rect(2 * TILE, 2 * TILE, self.width - 4 * TILE, self.height - 4 * TILE)
|
||
self.center = pygame.Vector2(self.width / 2, self.height / 2 + TILE)
|
||
self.floor = pygame.Surface((self.width, self.height)).convert()
|
||
self._build()
|
||
|
||
def _build(self):
|
||
rnd = random.Random(SEED)
|
||
grass = assets.tile("floor.png", 0, 12)
|
||
decor = [assets.tile("floor.png", c, r) for c, r in ((1, 12), (2, 12), (3, 12), (4, 12), (3, 11))]
|
||
bushes = [assets.tile("nature.png", c, 10) for c in range(4)]
|
||
trees = [assets.tile("nature.png", 0, 0, 2, 2), assets.tile("nature.png", 16, 0, 2, 2),
|
||
assets.tile("nature.png", 18, 0, 2, 2)]
|
||
rock = assets.tile("nature.png", 7, 12)
|
||
stump = assets.tile("nature.png", 15, 9)
|
||
|
||
for y in range(ROWS):
|
||
for x in range(COLS):
|
||
self.floor.blit(grass, (x * TILE, y * TILE))
|
||
if rnd.random() < 0.13:
|
||
self.floor.blit(rnd.choice(decor), (x * TILE, y * TILE))
|
||
|
||
taken = set()
|
||
|
||
def tree(cx, cy):
|
||
for dx in (0, 1):
|
||
for dy in (0, 1):
|
||
taken.add((cx + dx, cy + dy))
|
||
px, py = cx * TILE, cy * TILE
|
||
self.props.add(Prop(rnd.choice(trees), (px, py), pygame.Rect(px + 8, py + 20, 16, 10)))
|
||
|
||
# Кольцо деревьев по краю: стена — отдельные прямоугольники self.walls.
|
||
for x in range(0, COLS, 2):
|
||
tree(x, 0)
|
||
tree(x, ROWS - 2)
|
||
for y in range(2, ROWS - 2, 2):
|
||
tree(0, y)
|
||
tree(COLS - 2, y)
|
||
for cx, cy in GROVES:
|
||
tree(cx, cy)
|
||
|
||
def free(x, y):
|
||
near_center = abs(x - COLS / 2) < 6 and abs(y - ROWS / 2) < 5
|
||
return (x, y) not in taken and not near_center and 3 <= x < COLS - 3 and 3 <= y < ROWS - 3
|
||
|
||
for x, y in STUMPS:
|
||
taken.add((x, y))
|
||
px, py = x * TILE, y * TILE
|
||
self.props.add(Prop(stump, (px, py), pygame.Rect(px + 2, py + 5, 12, 10)))
|
||
|
||
placed = 0
|
||
while placed < 26:
|
||
x, y = rnd.randrange(3, COLS - 3), rnd.randrange(3, ROWS - 3)
|
||
if free(x, y):
|
||
taken.add((x, y))
|
||
px, py = x * TILE, y * TILE
|
||
self.props.add(Prop(rock, (px, py), pygame.Rect(px + 2, py + 4, 12, 11), breakable=True))
|
||
placed += 1
|
||
|
||
for _ in range(40):
|
||
x, y = rnd.randrange(2, COLS - 2), rnd.randrange(2, ROWS - 2)
|
||
if (x, y) not in taken:
|
||
self.floor.blit(rnd.choice(bushes), (x * TILE, y * TILE))
|
||
|
||
self.spawns = []
|
||
for x in range(4, COLS - 4, 5):
|
||
self.spawns += [pygame.Vector2(x * TILE + 8, 3 * TILE), pygame.Vector2(x * TILE + 8, (ROWS - 3) * TILE)]
|
||
for y in range(6, ROWS - 5, 5):
|
||
self.spawns += [pygame.Vector2(3 * TILE, y * TILE + 8), pygame.Vector2((COLS - 3) * TILE, y * TILE + 8)]
|
||
self.spawns = [p for p in self.spawns if not self.blocked(pygame.Rect(p.x - 8, p.y - 6, 16, 12))]
|
||
|
||
def obstacles(self):
|
||
return self.walls + [p.hitbox for p in self.props]
|
||
|
||
def blocked(self, rect):
|
||
return rect.collidelist(self.walls) != -1 or any(rect.colliderect(p.hitbox) for p in self.props)
|