From 7bf027b495144c305863e2ba300fb4e2953017e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C4=BCebs=20Cvetkovs?= Date: Fri, 12 Apr 2024 07:48:16 +0000 Subject: [PATCH] Upload files to "/" --- drawing.py | 189 +++++++++++++++++++++++++++++++++++++++++++++ interaction.py | 121 +++++++++++++++++++++++++++++ main.py | 46 +++++++++++ map.py | 44 +++++++++++ object_renderer.py | 22 ++++++ 5 files changed, 422 insertions(+) create mode 100644 drawing.py create mode 100644 interaction.py create mode 100644 main.py create mode 100644 map.py create mode 100644 object_renderer.py diff --git a/drawing.py b/drawing.py new file mode 100644 index 0000000..ea2a737 --- /dev/null +++ b/drawing.py @@ -0,0 +1,189 @@ +import pygame +from settings import * +from ray_casting import ray_casting +from map import mini_map +from collections import deque +from random import randrange +import sys + + +class Drawing: + def __init__(self, sc, sc_map, player, clock): + self.sc = sc + self.sc_map = sc_map + self.player = player + self.clock = clock + self.digits_images = [pygame.image.load(f'digits/{i}.png') for i in range(11)] + self.font = pygame.font.SysFont('Arial', 36, bold=True) + self.font_win = pygame.font.Font('font/font.ttf', 144) + self.textures = {1: pygame.image.load('img/wall3.png').convert(), + 2: pygame.image.load('img/wall4.png').convert(), + 3: pygame.image.load('img/wall5.png').convert(), + 4: pygame.image.load('img/wall6.png').convert(), + 'S': pygame.image.load('img/sky2.png').convert() + } + # menu + self.menu_trigger = True + self.menu_trigger = True + self.menu_picture = pygame.image.load('img/bg.jpg').convert() + # weapon parameters + self.weapon_base_sprite = pygame.image.load('sprites/weapons/shotgun/base/0.png').convert_alpha() + self.weapon_shot_animation = deque([pygame.image.load(f'sprites/weapons/shotgun/shot/{i}.png').convert_alpha() + for i in range(20)]) + self.weapon_rect = self.weapon_base_sprite.get_rect() + self.weapon_pos = (HALF_WIDTH - self.weapon_rect.width // 2, HEIGHT - self.weapon_rect.height) + self.shot_length = len(self.weapon_shot_animation) + self.shot_length_count = 0 + self.shot_animation_speed = 3 + self.shot_animation_count = 0 + self.shot_animation_trigger = True + #self.shot_sound = pygame.mixer.Sound('sound/shotgun.wav') + # sfx parameters + self.sfx = deque([pygame.image.load(f'sprites/weapons/sfx/{i}.png').convert_alpha() for i in range(9)]) + self.sfx_length_count = 0 + self.sfx_length = len(self.sfx) + + def background(self, angle): + sky_offset = -10 * math.degrees(angle) % WIDTH + self.sc.blit(self.textures['S'], (sky_offset, 0)) + self.sc.blit(self.textures['S'], (sky_offset - WIDTH, 0)) + self.sc.blit(self.textures['S'], (sky_offset + WIDTH, 0)) + pygame.draw.rect(self.sc, DARKGRAY, (0, HALF_HEIGHT, WIDTH, HALF_HEIGHT)) + + def world(self, world_objects): + for obj in sorted(world_objects, key=lambda n: n[0], reverse=True): + if obj[0]: + _, object, object_pos = obj + self.sc.blit(object, object_pos) + + def fps(self, clock): + display_fps = str(int(clock.get_fps())) + render = self.font.render(display_fps, 0, DARKORANGE) + self.sc.blit(render, FPS_POS) + + def mini_map(self, player): + self.sc_map.fill(BLACK) + map_x, map_y = player.x // MAP_SCALE, player.y // MAP_SCALE + pygame.draw.line(self.sc_map, YELLOW, (map_x, map_y), (map_x + 12 * math.cos(player.angle), + map_y + 12 * math.sin(player.angle)), 2) + pygame.draw.circle(self.sc_map, RED, (int(map_x), int(map_y)), 5) + for x, y in mini_map: + pygame.draw.rect(self.sc_map, DARKBROWN, (x, y, MAP_TILE, MAP_TILE)) + self.sc.blit(self.sc_map, MAP_POS) + + def draw_player_health(self): + health = str(self.player.health) + for i, char in enumerate(health): + self.sc.blit(self.digits_images[int(char[0])], (i * 60, 0)) + self.sc.blit(self.digits_images[10], (190, 0)) + + def player_weapon(self, shots): + if self.player.shot: + #if not self.shot_length_count: + #self.shot_sound.play() + self.shot_projection = min(shots)[1] // 2 + self.bullet_sfx() + shot_sprite = self.weapon_shot_animation[0] + self.sc.blit(shot_sprite, self.weapon_pos) + self.shot_animation_count += 1 + if self.shot_animation_count == self.shot_animation_speed: + self.weapon_shot_animation.rotate(-1) + self.shot_animation_count = 0 + self.shot_length_count += 1 + self.shot_animation_trigger = False + if self.shot_length_count == self.shot_length: + self.player.shot = False + self.shot_length_count = 0 + self.sfx_length_count = 0 + self.shot_animation_trigger = True + else: + self.sc.blit(self.weapon_base_sprite, self.weapon_pos) + + def bullet_sfx(self): + if self.sfx_length_count < self.sfx_length: + sfx = pygame.transform.scale(self.sfx[0], (self.shot_projection, self.shot_projection)) + sfx_rect = sfx.get_rect() + self.sc.blit(sfx, (HALF_WIDTH - sfx_rect.w // 2, HALF_HEIGHT - sfx_rect.h // 2)) + self.sfx_length_count += 1 + self.sfx.rotate(-1) + + def win(self): + render = self.font_win.render('YOU WIN!!!', 1, (randrange(40, 120), 0, 0)) + rect = pygame.Rect(0, 0, 1000, 300) + rect.center = HALF_WIDTH, HALF_HEIGHT + pygame.draw.rect(self.sc, BLACK, rect, border_radius=50) + self.sc.blit(render, (rect.centerx - 430, rect.centery - 140)) + pygame.display.flip() + self.clock.tick(15) + + + def lose(self): + render = self.font_win.render('Game Over!!!', 1, (randrange(40, 120), 0, 0)) + rect = pygame.Rect(0, 0, 1250, 300) + rect.center = HALF_WIDTH, HALF_HEIGHT + pygame.draw.rect(self.sc, BLACK, rect, border_radius=50) + self.sc.blit(render, (rect.centerx - 430, rect.centery - 140)) + pygame.display.flip() + self.sc.blit(self.digits_images[int(0)], (0, 0)) + self.sc.blit(self.digits_images[10], (190, 0)) + self.clock.tick(15) + + def menu(self): + x = 0 + button_font = pygame.font.Font('font/font.ttf', 72) + label_font = pygame.font.Font('font/font1.otf', 300) + label_name_font = pygame.font.Font('font/font1.otf', 50) + start = button_font.render('START', 1, pygame.Color('lightgray')) + button_start = pygame.Rect(0, 0, 400, 150) + button_start.center = HALF_WIDTH, HALF_HEIGHT + exit = button_font.render('EXIT', 1, pygame.Color('lightgray')) + button_exit = pygame.Rect(0, 0, 400, 150) + button_exit.center = HALF_WIDTH, HALF_HEIGHT + 200 + + while self.menu_trigger: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + + self.sc.blit(self.menu_picture, (0, 0), (x % WIDTH, HALF_HEIGHT, WIDTH, HEIGHT)) + x += 1 + + pygame.draw.rect(self.sc, BLACK, button_start, border_radius=25, width=10) + self.sc.blit(start, (button_start.centerx - 130, button_start.centery - 70)) + + pygame.draw.rect(self.sc, BLACK, button_exit, border_radius=25, width=10) + self.sc.blit(exit, (button_exit.centerx - 85, button_exit.centery - 70)) + + color = randrange(40) + label = label_font.render('PAPAPIKA', 1, (color, color, color)) + self.sc.blit(label, (15, -30)) + + gleb = label_name_font.render('Glebs Cvetkovs', 1, (color, color, color)) + self.sc.blit(gleb, (button_exit.centerx - 580, button_exit.centery + 140)) + + vlad = label_name_font.render('Vladislavs Pozdnaks', 1, (color, color, color)) + self.sc.blit(vlad, (button_exit.centerx - 580, button_exit.centery + 100)) + + maks = label_name_font.render('Maksims Rubins', 1, (color, color, color)) + self.sc.blit(maks, (button_exit.centerx - 580, button_exit.centery + 60)) + + + + + mouse_pos = pygame.mouse.get_pos() + mouse_click = pygame.mouse.get_pressed() + if button_start.collidepoint(mouse_pos): + pygame.draw.rect(self.sc, BLACK, button_start, border_radius=25) + self.sc.blit(start, (button_start.centerx - 130, button_start.centery - 70)) + if mouse_click[0]: + self.menu_trigger = False + elif button_exit.collidepoint(mouse_pos): + pygame.draw.rect(self.sc, BLACK, button_exit, border_radius=25) + self.sc.blit(exit, (button_exit.centerx - 85, button_exit.centery - 70)) + if mouse_click[0]: + pygame.quit() + sys.exit() + + pygame.display.flip() + self.clock.tick(20) \ No newline at end of file diff --git a/interaction.py b/interaction.py new file mode 100644 index 0000000..9399d40 --- /dev/null +++ b/interaction.py @@ -0,0 +1,121 @@ +import random +from settings import * +from map import world_map +from ray_casting import mapping +import math +import pygame +from numba import njit +from random import randrange + +@njit(fastmath=True, cache=True) +def ray_casting_npc_player(npc_x, npc_y, blocked_doors, world_map, player_pos): + ox, oy = player_pos + xm, ym = mapping(ox, oy) + delta_x, delta_y = ox - npc_x, oy - npc_y + cur_angle = math.atan2(delta_y, delta_x) + cur_angle += math.pi + + sin_a = math.sin(cur_angle) + sin_a = sin_a if sin_a else 0.000001 + cos_a = math.cos(cur_angle) + cos_a = cos_a if cos_a else 0.000001 + + # verticals + x, dx = (xm + TILE, 1) if cos_a >= 0 else (xm, -1) + for i in range(0, int(abs(delta_x)) // TILE): + depth_v = (x - ox) / cos_a + yv = oy + depth_v * sin_a + tile_v = mapping(x + dx, yv) + if tile_v in world_map or tile_v in blocked_doors: + return False + x += dx * TILE + + # horizontals + y, dy = (ym + TILE, 1) if sin_a >= 0 else (ym, -1) + for i in range(0, int(abs(delta_y)) // TILE): + depth_h = (y - oy) / sin_a + xh = ox + depth_h * cos_a + tile_h = mapping(xh, y + dy) + if tile_h in world_map or tile_h in blocked_doors: + return False + y += dy * TILE + return True + + +class Interaction: + def __init__(self, player, sprites, drawing): + self.player = player + self.sprites = sprites + self.drawing = drawing + #self.pain_sound = pygame.mixer.Sound('sound/pain.wav') + + def interaction_objects(self): + if self.player.shot and self.drawing.shot_animation_trigger: + for obj in sorted(self.sprites.list_of_objects, key=lambda obj: obj.distance_to_sprite): + if obj.is_on_fire[1]: + if obj.is_dead != 'immortal' and not obj.is_dead: + if ray_casting_npc_player(obj.x, obj.y, + self.sprites.blocked_doors, + world_map, self.player.pos): + #if obj.flag == 'npc': + #self.pain_sound.play() + obj.is_dead = True + obj.blocked = None + self.drawing.shot_animation_trigger = False + break + + def attack(self): + for obj in self.sprites.list_of_objects: + if obj.sprite_shot and 1 > randrange(0, 100): + self.player.get_damage(obj.damage) + + + + def npc_action(self): + for obj in self.sprites.list_of_objects: + if obj.flag == 'npc' and not obj.is_dead: + if ray_casting_npc_player(obj.x, obj.y, + self.sprites.blocked_doors, + world_map, self.player.pos): + obj.npc_action_trigger = True + self.npc_move(obj) + else: + obj.npc_action_trigger = False + + def npc_move(self, obj): + if abs(obj.distance_to_sprite) > TILE: + dx = obj.x - self.player.pos[0] + dy = obj.y - self.player.pos[1] + obj.x = obj.x + 1 if dx < 0 else obj.x - 1 + obj.y = obj.y + 1 if dy < 0 else obj.y - 1 + + + def clear_world(self): + deleted_objects = self.sprites.list_of_objects[:] + [self.sprites.list_of_objects.remove(obj) for obj in deleted_objects if obj.delete] + + #def play_music(self): + #pygame.mixer.pre_init(44100, -16, 2, 2048) + #pygame.mixer.init() + #pygame.mixer.music.load('sound/theme.mp3') + # pygame.mixer.music.play(10) + + def check_win(self): + if not len([obj for obj in self.sprites.list_of_objects if obj.flag == 'npc' and not obj.is_dead]): + #pygame.mixer.music.stop() + #pygame.mixer.music.load('sound/win.mp3') + #pygame.mixer.music.play() + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + exit() + self.drawing.win() + + def check_lose(self): + print(self.player.health) + if self.player.health <= 0: + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + exit() + self.drawing.lose() diff --git a/main.py b/main.py new file mode 100644 index 0000000..af92500 --- /dev/null +++ b/main.py @@ -0,0 +1,46 @@ +from player import Player +from sprite_objects import * +from ray_casting import ray_casting_walls +from drawing import Drawing +from interaction import Interaction +import time +from random import randrange + +pygame.init() +sc = pygame.display.set_mode((WIDTH, HEIGHT)) +sc_map = pygame.Surface(MINIMAP_RES) + +sprites = Sprites() +clock = pygame.time.Clock() +player = Player(sprites) +drawing = Drawing(sc, sc_map, player, clock) +interaction = Interaction(player, sprites, drawing) + + +drawing.menu() +pygame.mouse.set_visible(False) +starttime = time.time() +#interaction.play_music() + +while True: + player.movement() + drawing.background(player.angle) + walls, wall_shot = ray_casting_walls(player, drawing.textures) + drawing.world(walls + [obj.object_locate(player) for obj in sprites.list_of_objects]) + drawing.fps(clock) + drawing.mini_map(player) + drawing.player_weapon([wall_shot, sprites.sprite_shot]) + drawing.draw_player_health() + + interaction.interaction_objects() + interaction.npc_action() + endtime = time.time() + if endtime-starttime > 2: + if randrange(1, 100) < 20: + interaction.attack() + interaction.clear_world() + interaction.check_win() + interaction.check_lose() + + pygame.display.flip() + clock.tick() \ No newline at end of file diff --git a/map.py b/map.py new file mode 100644 index 0000000..ec1570b --- /dev/null +++ b/map.py @@ -0,0 +1,44 @@ +from settings import * +import pygame +from numba.core import types +from numba.typed import Dict +from numba import int32 + +_ = False +matrix_map = [ + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, _, _, _, _, _, 2, _, _, _, _, _, _, _, _, _, _, 4, _, _, _, _, _, 1], + [1, _, 2, 2, _, _, _, _, _, 2, 2, 2, _, _, _, 3, _, _, _, _, 4, _, _, 1], + [1, _, _, _, _, _, _, _, _, _, _, 2, 2, _, _, _, 3, _, _, _, _, _, _, 1], + [1, _, 2, 2, _, _, _, _, _, _, _, _, 2, _, 4, _, _, 3, _, _, _, 4, _, 1], + [1, _, _, _, _, _, 4, _, _, 2, 2, _, 2, _, _, _, _, _, _, 4, _, _, _, 1], + [1, _, 3, _, _, _, 2, _, _, 2, _, _, 2, _, _, _, 4, _, _, _, _, 4, _, 1], + [1, _, _, 3, _, _, 2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 1], + [1, _, 3, _, _, _, _, _, _, _, 3, _, _, 3, 3, _, _, _, _, 3, 3, _, _, 1], + [1, _, 3, _, _, _, 3, 3, _, 3, _, _, _, 3, 3, _, _, _, _, 2, 3, _, _, 1], + [1, _, _, _, _, 3, _, 3, _, _, 3, _, _, _, _, _, _, _, _, _, _, _, _, 1], + [1, _, 4, _, 3, _, _, _, _, 3, _, _, 2, _, _, _, _, _, _, _, _, 2, _, 1], + [1, _, _, _, _, _, 4, _, _, _, _, _, 2, 2, _, _, _, _, _, _, 2, 2, _, 1], + [1, _, _, 4, _, _, _, _, 4, _, _, _, _, 2, 2, 2, 2, 2, 2, 2, 2, _, _, 1], + [1, _, _, _, _, _, _, _, _, _, 4, _, _, _, _, _, _, _, _, _, _, _, _, 1], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] +] + +WORLD_WIDTH = len(matrix_map[0]) * TILE +WORLD_HEIGHT = len(matrix_map) * TILE +world_map = Dict.empty(key_type=types.UniTuple(int32, 2), value_type=int32) +mini_map = set() +collision_walls = [] +for j, row in enumerate(matrix_map): + for i, char in enumerate(row): + if char: + mini_map.add((i * MAP_TILE, j * MAP_TILE)) + collision_walls.append(pygame.Rect(i * TILE, j * TILE, TILE, TILE)) + if char == 1: + world_map[(i * TILE, j * TILE)] = 1 + elif char == 2: + world_map[(i * TILE, j * TILE)] = 2 + elif char == 3: + world_map[(i * TILE, j * TILE)] = 3 + elif char == 4: + world_map[(i * TILE, j * TILE)] = 4 \ No newline at end of file diff --git a/object_renderer.py b/object_renderer.py new file mode 100644 index 0000000..de3f486 --- /dev/null +++ b/object_renderer.py @@ -0,0 +1,22 @@ +import pygame as pg +from settings import * + +class ObjectRenderer: + def __init__(self, game): + self.game = game + self.screen = game.screen + self.wall_textures = self.load_wall_textures() + + @staticmethod + def get_texture(path, res=(TEXTURE_SIZE, TEXTURE_SIZE)): + texture = pg.image.load(path).convert_alpha() + return pg.transform.scale(texture, res) + + def load_wall_textures(self): + return { + 1: self.get_texture('resources/textures/wall1.png'), + 2: self.get_texture('resources/textures/wall2.png'), + 3: self.get_texture('resources/textures/wall3.png'), + 4: self.get_texture('resources/textures/wall4.png'), + 5: self.get_texture('resources/textures/wall5.png'), + }