|
|
|
@ -1,6 +1,7 @@
|
|
|
|
|
from __future__ import annotations |
|
|
|
|
import math |
|
|
|
|
from copy import deepcopy |
|
|
|
|
import time |
|
|
|
|
from random import choice, uniform, randrange as rnd |
|
|
|
|
from dataclasses import dataclass, field |
|
|
|
|
|
|
|
|
@ -23,6 +24,9 @@ GAME_COLORS = [RED, BLUE, YELLOW, GREEN, MAGENTA, CYAN]
|
|
|
|
|
|
|
|
|
|
WIDTH = 800 |
|
|
|
|
HEIGHT = 600 |
|
|
|
|
pygame.init() |
|
|
|
|
font = pygame.font.Font('IntroRust.otf', 25) |
|
|
|
|
font2 = pygame.font.Font('IntroRust.otf', 16) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass |
|
|
|
@ -33,6 +37,7 @@ class Ball:
|
|
|
|
|
color: int = field(default_factory=lambda: choice(GAME_COLORS)) |
|
|
|
|
time_to_split: typing.Optional[int] = field(default_factory=lambda: choice([None, rnd(5, 25)])) |
|
|
|
|
live: int = 30 |
|
|
|
|
source: int = 0 |
|
|
|
|
|
|
|
|
|
def move(self): |
|
|
|
|
"""Переместить мяч по прошествии единицы времени. |
|
|
|
@ -92,6 +97,7 @@ class Ball:
|
|
|
|
|
|
|
|
|
|
@dataclass |
|
|
|
|
class Gun: |
|
|
|
|
gun_id: int = field(default_factory=lambda: rnd(1, 1000000)) |
|
|
|
|
bullet: bool = 0 |
|
|
|
|
f2_power: int = 10 |
|
|
|
|
f2_on: bool = 0 |
|
|
|
@ -118,14 +124,15 @@ class Gun:
|
|
|
|
|
new_ball.r += 5 |
|
|
|
|
new_ball.v[0] = self.f2_power * math.cos(self.an) |
|
|
|
|
new_ball.v[1] = - self.f2_power * math.sin(self.an) |
|
|
|
|
new_ball.source = self.gun_id |
|
|
|
|
game.balls.append(new_ball) |
|
|
|
|
self.f2_on = False |
|
|
|
|
self.f2_power = 10 |
|
|
|
|
|
|
|
|
|
def targetting(self, event): |
|
|
|
|
"""Прицеливание. Зависит от положения мыши.""" |
|
|
|
|
if event and event.pos[0] != 20: |
|
|
|
|
self.an = math.atan((event.pos[1] - self.y0) / (event.pos[0] - self.x0)) |
|
|
|
|
if event and event.pos[0] != self.x0: |
|
|
|
|
self.an = math.atan2((event.pos[1] - self.y0), (event.pos[0] - self.x0)) |
|
|
|
|
if self.f2_on: |
|
|
|
|
self.color = RED |
|
|
|
|
else: |
|
|
|
@ -179,6 +186,13 @@ class Gun:
|
|
|
|
|
else: |
|
|
|
|
self.color = GREY |
|
|
|
|
|
|
|
|
|
def hittest(self, obj): |
|
|
|
|
if self.x0 - 50 - obj.r < obj.position[0] < self.x0 + 50 + obj.r \ |
|
|
|
|
and self.y0 - obj.r < obj.position[1] < self.y0 + 35 + obj.r \ |
|
|
|
|
and hasattr(obj, 'source') and obj.source != self.gun_id: |
|
|
|
|
return True |
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass |
|
|
|
|
class Target: |
|
|
|
@ -188,7 +202,6 @@ class Target:
|
|
|
|
|
vx: float = field(default_factory=lambda: rnd(-4, 6)) |
|
|
|
|
vy: float = field(default_factory=lambda: rnd(-4, 6)) |
|
|
|
|
color: int = RED |
|
|
|
|
points: int = 0 |
|
|
|
|
live: int = 1 |
|
|
|
|
ax: float = 0 |
|
|
|
|
ay: float = 0 |
|
|
|
@ -197,9 +210,9 @@ class Target:
|
|
|
|
|
oscillation_ampl: float = field(default_factory=lambda: rnd(0, 6) * choice([0, 0, 1])) |
|
|
|
|
oscillation_phase: float = field(default_factory=lambda: uniform(0, 2 * np.pi)) |
|
|
|
|
|
|
|
|
|
def hit(self, points=1): |
|
|
|
|
def hit(self, game: Game, points=1): |
|
|
|
|
"""Попадание шарика в цель.""" |
|
|
|
|
self.points += points |
|
|
|
|
game.points += points |
|
|
|
|
|
|
|
|
|
def draw(self, screen): |
|
|
|
|
pygame.draw.circle( |
|
|
|
@ -264,6 +277,8 @@ class Game:
|
|
|
|
|
finished: bool = False |
|
|
|
|
clock: pygame.time.Clock = field(default_factory=pygame.time.Clock) |
|
|
|
|
fps: int = FPS |
|
|
|
|
points: int = 0 |
|
|
|
|
screen: pygame.Surface = field(default_factory=lambda: pygame.display.set_mode((WIDTH, HEIGHT))) |
|
|
|
|
|
|
|
|
|
def move(self): |
|
|
|
|
for target in self.targets: |
|
|
|
@ -276,11 +291,14 @@ class Game:
|
|
|
|
|
ball1, ball2 = ball.get_split() |
|
|
|
|
self.balls.pop([i for i in range(len(self.balls)) if self.balls[i].position is ball.position][0]) |
|
|
|
|
self.balls.extend([ball1, ball2]) |
|
|
|
|
for gun in self.guns: |
|
|
|
|
if gun.hittest(ball): |
|
|
|
|
self.guns.remove(gun) |
|
|
|
|
for target in self.targets: |
|
|
|
|
if ball.hittest(target) and target.live: |
|
|
|
|
target.live = 0 |
|
|
|
|
ball.live = -1 |
|
|
|
|
target.hit() |
|
|
|
|
target.hit(self) |
|
|
|
|
self.targets.remove(target) |
|
|
|
|
self.targets.append(Target()) |
|
|
|
|
for gun in self.guns: |
|
|
|
@ -294,6 +312,8 @@ class Game:
|
|
|
|
|
target.draw(screen) |
|
|
|
|
for ball in self.balls: |
|
|
|
|
ball.draw(screen) |
|
|
|
|
score_text = font.render(str(self.points), 1, BLACK) |
|
|
|
|
screen.blit(score_text, (10, 10)) |
|
|
|
|
pygame.display.update() |
|
|
|
|
|
|
|
|
|
def process_event(self, event): |
|
|
|
@ -307,9 +327,8 @@ class Game:
|
|
|
|
|
gun.handle_event(event, self) |
|
|
|
|
|
|
|
|
|
def main_loop(self): |
|
|
|
|
screen = pygame.display.set_mode((WIDTH, HEIGHT)) |
|
|
|
|
while not self.finished: |
|
|
|
|
self.draw(screen) |
|
|
|
|
self.draw(self.screen) |
|
|
|
|
self.clock.tick(self.fps) |
|
|
|
|
for event in pygame.event.get(): |
|
|
|
|
self.process_event(event) |
|
|
|
@ -317,6 +336,28 @@ class Game:
|
|
|
|
|
pygame.quit() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pygame.init() |
|
|
|
|
rules_text = ''' |
|
|
|
|
Управление левой пушкой: |
|
|
|
|
a - влево, d - вправо, qe - наведение, s - стрелять |
|
|
|
|
Управление правой: |
|
|
|
|
jl - движение, uo - наведение, k - стрелять |
|
|
|
|
|
|
|
|
|
Некоторые снаряды разрывные - разбиваются на 2. |
|
|
|
|
Цели бывают обычные (те, которые полностью красные) - они |
|
|
|
|
движутся, просто отражаясь от стенок, - |
|
|
|
|
а также те, которые движутся случайно, и те, которые вращаются |
|
|
|
|
|
|
|
|
|
Кликните, чтобы начать |
|
|
|
|
''' |
|
|
|
|
game = Game() |
|
|
|
|
y = 100 |
|
|
|
|
game.screen.fill(0x2e2646) |
|
|
|
|
for line in rules_text.split('\n'): |
|
|
|
|
surface = font2.render(line, True, WHITE) |
|
|
|
|
game.screen.blit(surface, (WIDTH / 2 - surface.get_width() / 2, y)) |
|
|
|
|
y += 5 + surface.get_height() |
|
|
|
|
rules_surface = font2.render(rules_text, True, WHITE) |
|
|
|
|
pygame.display.update() |
|
|
|
|
while not any(ev.type == pygame.MOUSEBUTTONUP or ev.type == pygame.QUIT for ev in pygame.event.get()): |
|
|
|
|
pass |
|
|
|
|
game.main_loop() |
|
|
|
|