Doompart1

main
Vladislavs Pozdņaks 2024-03-28 06:41:00 +00:00
commit 81a9243277
5 changed files with 442 additions and 0 deletions

156
drawing.py 100644
View File

@ -0,0 +1,156 @@
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.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_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 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 menu(self):
x = 0
button_font = pygame.font.Font('font/font.ttf', 72)
label_font = pygame.font.Font('font/font1.otf', 400)
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('DOOMPy', 1, (color, color, color))
self.sc.blit(label, (15, -30))
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)

105
interaction.py 100644
View File

@ -0,0 +1,105 @@
from settings import *
from map import world_map
from ray_casting import mapping
import math
import pygame
from numba import njit
@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
if obj.flag in {'door_h', 'door_v'} and obj.distance_to_sprite < TILE:
obj.door_open_trigger = True
obj.blocked = None
break
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()

37
main.py 100644
View File

@ -0,0 +1,37 @@
from player import Player
from sprite_objects import *
from ray_casting import ray_casting_walls
from drawing import Drawing
from interaction import Interaction
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)
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])
interaction.interaction_objects()
interaction.npc_action()
interaction.clear_world()
interaction.check_win()
pygame.display.flip()
clock.tick()

44
map.py 100644
View File

@ -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

100
player.py 100644
View File

@ -0,0 +1,100 @@
from settings import *
import pygame
import math
from map import collision_walls
class Player:
def __init__(self, sprites):
self.x, self.y = player_pos
self.sprites = sprites
self.angle = player_angle
self.sensitivity = 0.004
# collision parameters
self.side = 50
self.rect = pygame.Rect(*player_pos, self.side, self.side)
# weapon
self.shot = False
@property
def pos(self):
return (self.x, self.y)
@property
def collision_list(self):
return collision_walls + [pygame.Rect(*obj.pos, obj.side, obj.side) for obj in
self.sprites.list_of_objects if obj.blocked]
def detect_collision(self, dx, dy):
next_rect = self.rect.copy()
next_rect.move_ip(dx, dy)
hit_indexes = next_rect.collidelistall(self.collision_list)
if len(hit_indexes):
delta_x, delta_y = 0, 0
for hit_index in hit_indexes:
hit_rect = self.collision_list[hit_index]
if dx > 0:
delta_x += next_rect.right - hit_rect.left
else:
delta_x += hit_rect.right - next_rect.left
if dy > 0:
delta_y += next_rect.bottom - hit_rect.top
else:
delta_y += hit_rect.bottom - next_rect.top
if abs(delta_x - delta_y) < 10:
dx, dy = 0, 0
elif delta_x > delta_y:
dy = 0
elif delta_y > delta_x:
dx = 0
self.x += dx
self.y += dy
def movement(self):
self.keys_control()
self.mouse_control()
self.rect.center = self.x, self.y
self.angle %= DOUBLE_PI
def keys_control(self):
sin_a = math.sin(self.angle)
cos_a = math.cos(self.angle)
keys = pygame.key.get_pressed()
if keys[pygame.K_ESCAPE]:
exit()
if keys[pygame.K_w]:
dx = player_speed * cos_a
dy = player_speed * sin_a
self.detect_collision(dx, dy)
if keys[pygame.K_s]:
dx = -player_speed * cos_a
dy = -player_speed * sin_a
self.detect_collision(dx, dy)
if keys[pygame.K_a]:
dx = player_speed * sin_a
dy = -player_speed * cos_a
self.detect_collision(dx, dy)
if keys[pygame.K_d]:
dx = -player_speed * sin_a
dy = player_speed * cos_a
self.detect_collision(dx, dy)
if keys[pygame.K_LEFT]:
self.angle -= 0.02
if keys[pygame.K_RIGHT]:
self.angle += 0.02
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1 and not self.shot:
self.shot = True
def mouse_control(self):
if pygame.mouse.get_focused():
difference = pygame.mouse.get_pos()[0] - HALF_WIDTH
pygame.mouse.set_pos((HALF_WIDTH, HALF_HEIGHT))
self.angle += difference * self.sensitivity