/
GraphTreeHeap
/
NeuroFighter
Обзор
Документация
Войти
/
GraphTreeHeap
/
NeuroFighter
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/graphics.py
390 строк
19 KB
FedMam
moving to the new project version
11 окт 2025, 04:16
11 окт 2025, 04:16
00409b0
Код
Авторство
О чём код?
from mechanics import * import pygame import pygame.mixer import random import math import sys import os WORKING_DIR = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) MARGIN = 8 GROUND_HEIGHT = 32 MOUNT_BASE = ROOM_HEIGHT // 3 MOUNT_HEIGHT = ROOM_HEIGHT // 3 EYES_CLR = (0xff, 0xff, 0xff) BULLET_CLR = (0xff, 0xc0, 0x00) BULLET_SIZE = 4 BULLET_HALFSIZE = BULLET_SIZE // 2 SCALE = 3 WINDOW_WIDTH = ROOM_WIDTH + MARGIN * 2 WINDOW_HEIGHT = ROOM_HEIGHT + GROUND_HEIGHT + CHR_HITBOX_HALFSIZE + MARGIN * 2 ENABLE_MUSIC = True FONT_FILENAME = f'{WORKING_DIR}/assets/fonts/PublicPixel.ttf' # character-specific properties SKY_CLRS = [ (0x80, 0x00, 0x00), # Zhan Shi (0x00, 0x00, 0x80), # Vortex (0x40, 0x00, 0x00), # Murasaki (0x80, 0xc0, 0xff), # Viper (0x60, 0x00, 0xc0), # Raio (0x20, 0x40, 0x80), # Metelitsa (0xff, 0x60, 0x00), # Ironfist (0xc0, 0x90, 0x60), # Raptor (0xa0, 0xd0, 0xff), # Tecolotl (0xd0, 0xe0, 0xff), # Zephyr (0x80, 0xc0, 0xff), # Dreadnought (0x40, 0x00, 0x00), # Crusher (0x00, 0x40, 0x00), # Oxide (0x70, 0x00, 0x70) # Dr.Trainor ] MOUNT_CLRS = [ (0x80, 0x60, 0x60), # Zhan Shi (0x00, 0x00, 0x60), # Vortex (0x40, 0x00, 0x40), # Murasaki (0xa0, 0xa0, 0xa0), # Viper (0x00, 0x00, 0x00), # Raio (0x80, 0xc0, 0xff), # Metelitsa (0x00, 0x60, 0x00), # Ironfist (0x00, 0xb0, 0x00), # Raptor (0x60, 0x30, 0x00), # Tecolotl (0xff, 0xff, 0xff), # Zephyr (0x00, 0x50, 0x70), # Dreadnought (0x80, 0x50, 0x20), # Crusher (0x00, 0x80, 0x00), # Oxide (0xc0, 0xc0, 0xc0), # Dr.Trainor ] MOUNT_FUNCS = [ lambda x: x * 10 - math.floor(x * 10), # Zhan Shi lambda x: math.sin(x * math.pi * 10) / 2 + 0.5, # Vortex lambda x: (1 if math.sin(x * math.pi * 40) >= 0 else 0), # Murasaki lambda x: (1 if math.sin(x * math.pi * 8) >= 0 else 0), # Viper lambda x: x * 15 - math.floor(x * 15), # Raio lambda x: abs((x * 10 - math.floor(x * 10)) - 0.5) * 2, # Metelitsa lambda x: abs((x * 10 - math.floor(x * 10)) - 0.5) * 2, # Ironfist lambda x: abs((x * 6 - math.floor(x * 6)) - 0.5) * 2, # Raptor lambda x: min(1, abs((x * 6 - math.floor(x * 6)) - 0.5) * 4), # Tecolotl lambda x: math.sqrt(1 - (((x * 4 - math.floor(x * 4)) - 0.5) * 2) ** 2), # Zephyr lambda _: 0.0, # Dreadnought lambda x: abs((x * 3 - math.floor(x * 3)) - 0.5) * 2, # Crusher lambda x: math.sin(x * math.pi * 20) / 2 + 0.5, # Oxide lambda x: max(0, min(1, abs((x * 4 - math.floor(x * 4)) - 0.5) * 6 - 1)), # Dr.Trainor ] GROUND_CLRS = [ (0xff, 0x80, 0x00), # Zhan Shi (0x80, 0x80, 0x80), # Vortex (0xff, 0xc0, 0x60), # Murasaki (0x5f, 0x5f, 0x5f), # Viper (0xc0, 0xc0, 0x00), # Raio (0xff, 0xff, 0xff), # Metelitsa (0x00, 0x00, 0x00), # Ironfist (0x00, 0x80, 0x00), # Raptor (0xff, 0xff, 0x80), # Tecolotl (0xc0, 0x80, 0xff), # Zephyr (0xc0, 0x80, 0x40), # Dreadnought (0xc0, 0x80, 0x00), # Crusher (0xd0, 0xd0, 0xd0), # Oxide (0x90, 0x90, 0x90) # Dr.Trainor ] CHR_MUSICS = [ f'{WORKING_DIR}/assets/music/0-zhan-shi.wav', f'{WORKING_DIR}/assets/music/1-vortex.wav', f'{WORKING_DIR}/assets/music/2-murasaki.wav', f'{WORKING_DIR}/assets/music/3-cobra.wav', f'{WORKING_DIR}/assets/music/4-raio.wav', f'{WORKING_DIR}/assets/music/5-metelitsa.wav', f'{WORKING_DIR}/assets/music/6-ironclad.wav', f'{WORKING_DIR}/assets/music/7-raptor.wav', f'{WORKING_DIR}/assets/music/8-tecolotl.wav', f'{WORKING_DIR}/assets/music/9-brise.wav', f'{WORKING_DIR}/assets/music/10-dreadnought.wav', f'{WORKING_DIR}/assets/music/11-crusher.wav', f'{WORKING_DIR}/assets/music/12-dr-oxide.wav', f'{WORKING_DIR}/assets/music/13-the-agent.wav', ] screen = None clock = None font = None graphics_initialized = False def init_graphics(): global graphics_initialized, screen, clock, font if not graphics_initialized: graphics_initialized = True pygame.init() pygame.mixer.init() screen = pygame.display.set_mode((WINDOW_WIDTH * SCALE, WINDOW_HEIGHT * SCALE)) pygame.display.set_caption('NeuroFighter') clock = pygame.time.Clock() font = pygame.font.Font(FONT_FILENAME, 8 * SCALE) def deinit_graphics(): global graphics_initialized if graphics_initialized: graphics_initialized = False pygame.mixer.quit() pygame.quit() SINGLE_PLAYER_KEYS = [pygame.K_LEFT, pygame.K_RIGHT, pygame.K_z, pygame.K_x] MULTIPLAYER_P1_KEYS = [pygame.K_a, pygame.K_d, pygame.K_LCTRL, pygame.K_LSHIFT] MULTIPLAYER_P2_KEYS = [pygame.K_LEFT, pygame.K_RIGHT, pygame.K_LEFTBRACKET, pygame.K_RIGHTBRACKET] class GameTV: def __init__(self, env: Environment, player_controlled: bool=False, opponent_controlled: bool=False, opponent_background: bool=True): self.env = env self.player_controlled = player_controlled self.player_keys_held = [0] * N_BUTTONS self.opponent_controlled = opponent_controlled self.opponent_keys_held = [0] * N_BUTTONS self.player_keys = None self.opponent_keys = None if self.player_controlled and not self.opponent_controlled: self.player_keys = SINGLE_PLAYER_KEYS elif self.opponent_controlled and not self.player_controlled: self.opponent_keys = SINGLE_PLAYER_KEYS elif self.player_controlled and self.opponent_controlled: if opponent_background: # kind of a kostyl self.player_keys = MULTIPLAYER_P1_KEYS self.opponent_keys = MULTIPLAYER_P2_KEYS else: self.player_keys = MULTIPLAYER_P2_KEYS self.opponent_keys = MULTIPLAYER_P1_KEYS self.opponent_background = opponent_background if ENABLE_MUSIC: pygame.mixer.music.load(CHR_MUSICS[env.opponent.character_id if opponent_background else env.player.character_id]) pygame.mixer.music.play(loops=40) def step(self, ready_round: int=0): clock.tick(FPS) render(self.env, opponent_background=self.opponent_background, ready_round=ready_round) events = pygame.event.get() for event in events: if event.type in [pygame.KEYDOWN, pygame.KEYUP]: keydown = event.type == pygame.KEYDOWN if self.player_controlled: for btn_i in range(N_BUTTONS): if event.key == self.player_keys[btn_i]: self.player_keys_held[btn_i] = keydown if self.opponent_controlled: for btn_i in range(N_BUTTONS): if event.key == self.opponent_keys[btn_i]: self.opponent_keys_held[btn_i] = keydown elif event.type == pygame.QUIT: deinit_graphics() exit() for btn_i in range(N_BUTTONS): if self.player_controlled: self.env.player.buttons_held[btn_i] = self.player_keys_held[btn_i] if self.opponent_controlled: self.env.opponent.buttons_held[btn_i] = self.opponent_keys_held[btn_i] if self.player_controlled and self.env.player.buttons_held[0] and self.env.player.buttons_held[1]: self.env.player.buttons_held[0] = self.env.player.buttons_held[1] = False if self.opponent_controlled and self.env.opponent.buttons_held[0] and self.env.opponent.buttons_held[1]: self.env.opponent.buttons_held[0] = self.env.opponent.buttons_held[1] = False def render(env: Environment, opponent_background: bool=True, ready_round: int=0): background_id = env.opponent.character_id if opponent_background else env.player.character_id screen.fill(SKY_CLRS[background_id]) # mountains for x in range(0, WINDOW_WIDTH+1): mt_pixel_height = round(MOUNT_FUNCS[background_id](x / (WINDOW_WIDTH+1)) * MOUNT_HEIGHT) pygame.draw.rect(screen, MOUNT_CLRS[background_id], (x * SCALE, (WINDOW_HEIGHT - GROUND_HEIGHT - MOUNT_BASE - mt_pixel_height) * SCALE, SCALE, (mt_pixel_height + MOUNT_BASE) * SCALE)) # ground pygame.draw.rect(screen, GROUND_CLRS[background_id], (0, (MARGIN + ROOM_HEIGHT + CHR_HITBOX_SIZE) * SCALE, WINDOW_WIDTH * SCALE, (GROUND_HEIGHT - CHR_HITBOX_HALFSIZE + MARGIN) * SCALE)) # opponent pygame.draw.rect(screen, tuple(map(lambda rgb: rgb // 2, env.opponent.color)), ((MARGIN + env.opponent.pos[0] - CHR_HITBOX_HALFSIZE) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - env.opponent.pos[1] - CHR_HITBOX_HALFSIZE) * SCALE, CHR_HITBOX_SIZE * SCALE, CHR_HITBOX_SIZE * SCALE)) pygame.draw.rect(screen, env.opponent.color, ((MARGIN + env.opponent.pos[0] - CHR_HITBOX_HALFSIZE + 1) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - env.opponent.pos[1] - CHR_HITBOX_HALFSIZE + 1) * SCALE, (CHR_HITBOX_SIZE - 2) * SCALE, (CHR_HITBOX_SIZE - 2) * SCALE)) pygame.draw.rect(screen, EYES_CLR, ((MARGIN + env.opponent.pos[0] + (0 if env.opponent.facing_right else -2)) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - env.opponent.pos[1] - 4) * SCALE, 2 * SCALE, 4 * SCALE)) pygame.draw.rect(screen, EYES_CLR, ((MARGIN + env.opponent.pos[0] + (4 if env.opponent.facing_right else -6)) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - env.opponent.pos[1] - 4) * SCALE, 2 * SCALE, 4 * SCALE)) # player pygame.draw.rect(screen, tuple(map(lambda rgb: rgb // 2, env.player.color)), ((MARGIN + env.player.pos[0] - CHR_HITBOX_HALFSIZE) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - env.player.pos[1] - CHR_HITBOX_HALFSIZE) * SCALE, CHR_HITBOX_SIZE * SCALE, CHR_HITBOX_SIZE * SCALE)) pygame.draw.rect(screen, env.player.color, ((MARGIN + env.player.pos[0] - CHR_HITBOX_HALFSIZE + 1) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - env.player.pos[1] - CHR_HITBOX_HALFSIZE + 1) * SCALE, (CHR_HITBOX_SIZE - 2) * SCALE, (CHR_HITBOX_SIZE - 2) * SCALE)) pygame.draw.rect(screen, EYES_CLR, ((MARGIN + env.player.pos[0] + (0 if env.player.facing_right else -2)) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - env.player.pos[1] - 4) * SCALE, 2 * SCALE, 4 * SCALE)) pygame.draw.rect(screen, EYES_CLR, ((MARGIN + env.player.pos[0] + (4 if env.player.facing_right else -6)) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - env.player.pos[1] - 4) * SCALE, 2 * SCALE, 4 * SCALE)) # projectiles for proj in env.player_projectiles: pygame.draw.rect(screen, tuple(map(lambda rgb: rgb // 2, env.player.color)), ((MARGIN + proj.pos[0] - BULLET_HALFSIZE - 1) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - proj.pos[1] - BULLET_HALFSIZE - 1) * SCALE, (BULLET_SIZE + 2) * SCALE, (BULLET_SIZE + 2) * SCALE)) pygame.draw.rect(screen, (0xff, 0xff, 0xff), ((MARGIN + proj.pos[0] - BULLET_HALFSIZE) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - proj.pos[1] - BULLET_HALFSIZE) * SCALE, BULLET_SIZE * SCALE, BULLET_SIZE * SCALE)) pygame.draw.rect(screen, env.player.color, ((MARGIN + proj.pos[0] - BULLET_HALFSIZE + 1) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - proj.pos[1] - BULLET_HALFSIZE + 1) * SCALE, (BULLET_SIZE - 2) * SCALE, (BULLET_SIZE - 2) * SCALE)) for proj in env.opponent_projectiles: pygame.draw.rect(screen, tuple(map(lambda rgb: rgb // 2, env.opponent.color)), ((MARGIN + proj.pos[0] - BULLET_HALFSIZE - 1) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - proj.pos[1] - BULLET_HALFSIZE - 1) * SCALE, (BULLET_SIZE + 2) * SCALE, (BULLET_SIZE + 2) * SCALE)) pygame.draw.rect(screen, (0xff, 0xff, 0xff), ((MARGIN + proj.pos[0] - BULLET_HALFSIZE) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - proj.pos[1] - BULLET_HALFSIZE) * SCALE, BULLET_SIZE * SCALE, BULLET_SIZE * SCALE)) pygame.draw.rect(screen, env.opponent.color, ((MARGIN + proj.pos[0] - BULLET_HALFSIZE + 1) * SCALE, (WINDOW_HEIGHT - MARGIN - GROUND_HEIGHT - proj.pos[1] - BULLET_HALFSIZE + 1) * SCALE, (BULLET_SIZE - 2) * SCALE, (BULLET_SIZE - 2) * SCALE)) # HP & cooldown pygame.draw.rect(screen, env.player.color, (4 * SCALE, 4 * SCALE, (env.player.max_hp // 2 + 2) * SCALE, 8 * SCALE)) pygame.draw.rect(screen, (0x00, 0x00, 0x00), ((4 + 1 + env.player.hp // 2) * SCALE, 5 * SCALE, (env.player.max_hp // 2 - env.player.hp // 2) * SCALE, 6 * SCALE)) player_cooldown_width = math.ceil((env.player.max_hp // 2) * env.player.shoot_cooldown_ctr / env.player.shoot_cooldown) pygame.draw.rect(screen, tuple(map(lambda rgb: 255 - (255 - rgb) // 2, env.player.color)), (4 * SCALE, 16 * SCALE, (env.player.max_hp // 2 + 2) * SCALE, 8 * SCALE)) pygame.draw.rect(screen, (0x00, 0x00, 0x00), ((4 + 1 + env.player.max_hp // 2 - player_cooldown_width) * SCALE, 17 * SCALE, player_cooldown_width * SCALE, 6 * SCALE)) pygame.draw.rect(screen, env.opponent.color, ((WINDOW_WIDTH - 4 - env.opponent.max_hp // 2 - 2) * SCALE, 4 * SCALE, (env.opponent.max_hp // 2 + 2) * SCALE, 8 * SCALE)) pygame.draw.rect(screen, (0x00, 0x00, 0x00), ((WINDOW_WIDTH - 4 - env.opponent.max_hp // 2 - 1) * SCALE, 5 * SCALE, (env.opponent.max_hp // 2 - env.opponent.hp // 2) * SCALE, 6 * SCALE)) opponent_cooldown_width = math.ceil((env.opponent.max_hp // 2) * env.opponent.shoot_cooldown_ctr / env.opponent.shoot_cooldown) pygame.draw.rect(screen, tuple(map(lambda rgb: 255 - (255 - rgb) // 2, env.opponent.color)), ((WINDOW_WIDTH - 4 - env.opponent.max_hp // 2 - 2) * SCALE, 16 * SCALE, (env.opponent.max_hp // 2 + 2) * SCALE, 8 * SCALE)) pygame.draw.rect(screen, (0x00, 0x00, 0x00), ((WINDOW_WIDTH - 4 - env.opponent.max_hp // 2 - 1) * SCALE, 17 * SCALE, opponent_cooldown_width * SCALE, 6 * SCALE)) # names player_name_surf = font.render(env.player.name.upper(), False, (0xff, 0xff, 0xff)) screen.blit(player_name_surf, (4 * SCALE, 28 * SCALE)) opponent_name_surf = font.render(env.opponent.name.upper(), False, (0xff, 0xff, 0xff)) screen.blit(opponent_name_surf, ((WINDOW_WIDTH - 4 - 8 * len(env.opponent.name)) * SCALE, 28 * SCALE)) # time time_str = f'{env.time_left // FPS // 60}:{str((env.time_left // FPS) % 60).rjust(2, '0')}' time_surf = font.render(time_str, False, GROUND_CLRS[background_id]) screen.blit(time_surf, ((WINDOW_WIDTH // 2 - 4 * len(time_str)) * SCALE, 4 * SCALE)) ''' # buttons (debug) for btn_i, btn_chr in enumerate('<>ZX'): player_btn_surf = font.render(btn_chr, False, env.player.color if env.player.buttons_held[btn_i] else (0xff, 0xff, 0xff)) opponent_btn_surf = font.render(btn_chr, False, env.opponent.color if env.opponent.buttons_held[btn_i] else (0xff, 0xff, 0xff)) screen.blit(player_btn_surf, ((4 + 8 * btn_i) * SCALE, 48 * SCALE)) screen.blit(opponent_btn_surf, ((WINDOW_WIDTH - 36 + 8 * btn_i) * SCALE, 48 * SCALE)) # rewards (debug) player_reward_str = "%.3f" % (env.total_player_reward,) player_reward_surf = font.render(player_reward_str, False, SKY_CLRS[background_id]) opponent_reward_str = "%.3f" % (env.total_opponent_reward,) opponent_reward_surf = font.render(opponent_reward_str, False, SKY_CLRS[background_id]) screen.blit(player_reward_surf, (4 * SCALE, (WINDOW_HEIGHT - 4 - 8) * SCALE)) screen.blit(opponent_reward_surf, ((WINDOW_WIDTH - 4 - 8 * len(opponent_reward_str)) * SCALE, (WINDOW_HEIGHT - 4 - 8) * SCALE)) ''' if ready_round != 0: # ready round round_str = f'ROUND {ready_round}' round_bright_surf = font.render(round_str, False, (0xff, 0xff, 0xff)) round_shadow_surf = font.render(round_str, False, (0x00, 0x00, 0x00)) screen.blit(round_shadow_surf, ((WINDOW_WIDTH // 2 - 4 * len(round_str) + 1) * SCALE, (WINDOW_HEIGHT // 2 - 4 + 1) * SCALE)) screen.blit(round_bright_surf, ((WINDOW_WIDTH // 2 - 4 * len(round_str)) * SCALE, (WINDOW_HEIGHT // 2 - 4) * SCALE)) if env.done(): if env.player.hp <= 0 and env.opponent.hp <= 0: double_ko_str = 'DOUBLE KO!' double_ko_bright_surf = font.render(double_ko_str, False, (0xff, 0xff, 0xff)) double_ko_shadow_surf = font.render(double_ko_str, False, (0x00, 0x00, 0x00)) screen.blit(double_ko_shadow_surf, ((WINDOW_WIDTH // 2 - 4 * len(double_ko_str) + 1) * SCALE, (WINDOW_HEIGHT // 2 - 4 + 1) * SCALE)) screen.blit(double_ko_bright_surf, ((WINDOW_WIDTH // 2 - 4 * len(double_ko_str)) * SCALE, (WINDOW_HEIGHT // 2 - 4) * SCALE)) elif env.player.hp <= 0 or env.opponent.hp <= 0: winner = env.player if env.opponent.hp <= 0 else env.opponent winner_str = f'{winner.name.upper()} WINS!' winner_bright_surf = font.render(winner_str, False, winner.color) winner_shadow_surf = font.render(winner_str, False, (0x00, 0x00, 0x00)) screen.blit(winner_shadow_surf, ((WINDOW_WIDTH // 2 - 4 * len(winner_str) + 1) * SCALE, (WINDOW_HEIGHT // 2 - 4 + 1) * SCALE)) screen.blit(winner_bright_surf, ((WINDOW_WIDTH // 2 - 4 * len(winner_str)) * SCALE, (WINDOW_HEIGHT // 2 - 4) * SCALE)) else: time_up_str = 'TIME OVER!' time_up_bright_surf = font.render(time_up_str, False, (0xff, 0xff, 0xff)) time_up_shadow_surf = font.render(time_up_str, False, (0x00, 0x00, 0x00)) screen.blit(time_up_shadow_surf, ((WINDOW_WIDTH // 2 - 4 * len(time_up_str) + 1) * SCALE, (WINDOW_HEIGHT // 2 - 4 + 1) * SCALE)) screen.blit(time_up_bright_surf, ((WINDOW_WIDTH // 2 - 4 * len(time_up_str)) * SCALE, (WINDOW_HEIGHT // 2 - 4) * SCALE)) pygame.display.flip()