import pygame import os import time import csv pygame.init() screen_width = 600 screen_height = int(screen_width * 0.8) screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption('SAMURAI') # set fps CLOCK = pygame.time.Clock() FPS = 60 # define game variables camera_x = 0 camera_y = 0 GRAVITY = 0.90 LEVEL = 0 ROWS = 16 COLS = 150 tile_size = screen_width // ROWS TILE_TYPES = 21 JUMP_STRENGTH = 15 level = 0 # define player action variables moving_left = False moving_right = False # define colors BG = (144, 201, 120) RED = (255, 0, 0) BLACK = (0, 0, 0) WHITE = (255, 255, 255) score = 0 start_time = time.time() img_list = [] for x in range(TILE_TYPES): img = pygame.image.load(f'img/background/tile/{x}.png') img = pygame.transform.scale(img, (tile_size, tile_size)) img_list.append(img) def load_and_resize_images(directory, max_size): images = [] for filename in os.listdir(directory): if filename.endswith('.png'): image = pygame.image.load(os.path.join(directory, filename)) img_width, img_height = image.get_size() aspect_ratio = img_width / img_height # Calculate new dimensions while maintaining aspect ratio if aspect_ratio > 1: new_width = max_size[0] new_height = int(new_width / aspect_ratio) else: new_height = max_size[1] new_width = int(new_height * aspect_ratio) image = pygame.transform.scale(image, (new_width, new_height)) images.append(image) return images image_size = (150, 100) # width, height # Load images for each animation state idle_images = load_and_resize_images('img/player/idle', image_size) run_images = load_and_resize_images('img/player/run', image_size) jump_images = load_and_resize_images('img/player/jump', image_size) attack_images = load_and_resize_images('img/player/attack', image_size) def draw_bg(): screen.fill(BG) class Samurai(pygame.sprite.Sprite): def __init__(self, char_type, x, y, scale, speed): pygame.sprite.Sprite.__init__(self) self.alive = True # type: ignore self.char_type = char_type self.speed = speed self.max_health = 1 self.health = self.max_health self.direction = 1 self.vel_y = 0 self.scored = False self.Run = False self.jump = False self.in_air = True self.flip = False self.target = None self.attacking = False self.attack_has_hit = False self.attack_cooldown = 1000 self.last_attack_time = 0 self.animation_list = [] animation_types = ['idle', 'run', 'jump', 'attack'] for animation in animation_types: temp_list = [] animation_folder = f'img/{self.char_type}/{animation}' if os.path.exists(animation_folder): files = [f for f in os.listdir(animation_folder) if os.path.isfile(os.path.join(animation_folder, f))] files.sort() for filename in files: if filename.endswith('.png'): img = pygame.image.load( os.path.join(animation_folder, filename)) img = pygame.transform.scale( img, (int(img.get_width() * scale), int(img.get_height() * scale)) ) temp_list.append(img) self.animation_list.append(temp_list) self.frame_index = 0 self.action = 0 self.update_time = pygame.time.get_ticks() self.image = self.animation_list[self.action][self.frame_index] self.rect = self.image.get_rect() self.rect.center = (x, y) self.width = self.image.get_width() self.height = self.image.get_height() def move(self, moving_left, moving_right, target=None): dx = 0 dy = 0 self.rect.x += dx if target and self.rect.colliderect(target.rect): self.rect.x -= dx # undo horizontal move dx = 0 # 2. Vertical movement + collision check -------------------------------------------------- self.rect.y += dy if target and self.rect.colliderect(target.rect): self.rect.y -= dy # undo vertical move dy = 0 if self.char_type == 'enemy' and target: if abs(self.rect.centerx - target.rect.centerx) > 40: if self.rect.centerx < target.rect.centerx: dx = self.speed self.flip = False self.direction = 1 else: dx = -self.speed self.flip = True self.direction = -1 else: self.attacking = True if self.char_type == 'player': if moving_left: dx = -self.speed self.flip = True self.direction = -1 if moving_right: dx = self.speed self.flip = False self.direction = 1 if self.jump and not self.in_air: self.vel_y = -JUMP_STRENGTH self.jump = False self.in_air = True self.vel_y += GRAVITY if self.vel_y > 10: self.vel_y = 10 dy += self.vel_y #check for collision for tile in world.obstacle_list: if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height): dx = 0 if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height): if self.vel_y < 0: self.vel_y = 0 dy = tile[1].bottom - self.rect.top if self.vel_y >= 0: self.vel_y = 0 dy = tile[1].bottom - self.rect.top if self.rect.bottom + dy > 300: dy = 300 - self.rect.bottom self.in_air = False self.rect.x += dx self.rect.y += dy def update_animation(self): animation_cooldown = 100 self.image = self.animation_list[self.action][self.frame_index] if pygame.time.get_ticks() - self.update_time > animation_cooldown: self.update_time = pygame.time.get_ticks() self.frame_index += 1 if self.frame_index >= len(self.animation_list[self.action]): self.frame_index = 0 if self.attacking: self.attacking = False self.attack_has_hit = False def update_action(self, new_action): if new_action != self.action: self.action = new_action self.frame_index = 0 self.update_time = pygame.time.get_ticks() def draw(self): screen.blit( pygame.transform.flip(self.image, self.flip, False), self.rect ) def attack_hitbox(self, enemy_group): if self.attacking and not self.attack_has_hit: attack_rect = pygame.Rect(0, 0, 50, 20) if self.direction == 1: attack_rect.topleft = (self.rect.right, self.rect.centery - 10) else: attack_rect.topright = (self.rect.left, self.rect.centery - 10) for target in enemy_group: if attack_rect.colliderect(target.rect): target.health -= 1 if target.health <= 0: target.alive = False self.attack_has_hit = True class World(): def __init__(self): self.obstacle_list = [] def process_data(self, data): #iterate through each value in level data file for y, row in enumerate(data): for x, tile in enumerate(row): if tile >= 0: img = img_list[tile] img_rect = img.get_rect() img_rect.x = x * tile_size img_rect.y = y * tile_size tile_data = (img, img_rect) if tile >= 0 and tile <= 13: self.obstacle_list.append(tile_data) elif tile == 14:#create player player = Samurai('player', x * tile_size, y * tile_size, 2, 3,) health_bar = HealthBar(10, 10, player.health, player.health) elif tile == 15:#create enemies enemy = Samurai('enemy', x * tile_size, y * tile_size, 2 , 3,) enemy_group.add(enemy) return player, health_bar def draw(self): for img, rect in world.obstacle_list: screen.blit(img, (rect.x - int(camera_x), rect.y)) class HealthBar: def __init__(self, x, y, health, max_health): self.x = x self.y = y self.health = health self.max_health = max_health def draw(self, health): self.health = health ratio = self.health / self.max_health pygame.draw.rect(screen, WHITE, (self.x - 2, self.y - 2, 154, 24)) pygame.draw.rect(screen, BLACK, (self.x, self.y, 150, 20)) pygame.draw.rect(screen, RED, (self.x, self.y, 150 * ratio, 20)) # Create game objects player = Samurai('player', 200, 200, 3, 5) enemy = Samurai('enemy', 3000, 200, 3, 5) health_bar_player = HealthBar(10, 10, player.health, player.max_health) enemy_group = pygame.sprite.Group() def enemy_ai(enemy, player): if enemy.alive and player.alive: dist = abs(enemy.rect.centerx - player.rect.centerx) if dist < 400 and not enemy.attacking: if enemy.rect.centerx > player.rect.centerx: enemy.direction = -1 enemy.flip = True enemy.Run = True enemy.rect.x -= enemy.speed - 2 else: enemy.direction = 1 enemy.flip = False enemy.Run = True enemy.rect.x += enemy.speed - 2 if dist < 60: now = pygame.time.get_ticks() enemy.rect.x += enemy.speed if now - enemy.last_attack_time > enemy.attack_cooldown: enemy.attacking = True enemy.update_action(3) enemy.frame_index = 0 enemy.last_attack_time = now #create empty tile list world_data = [] for row in range(ROWS): r = [-1] * COLS world_data.append(r) # load the levels and create the world with open(f'level{level}_data.csv', newline='') as csvfile: reader = csv.reader(csvfile, delimiter=',') for x, row in enumerate(reader): for y, tile in enumerate(row): world_data[x][y] = int(tile) world = World() player, health_bar = world.process_data(world_data) run = True while run: CLOCK.tick(FPS) draw_bg() for img, rect in world.obstacle_list: screen.blit(img, (rect.x - camera_x, rect.y - camera_y)) # Player if player.alive: if player.attacking: player.update_action(3) elif player.in_air: player.update_action(2) elif moving_left: player.flip = True player.update_action(1) elif moving_right: player.flip = False player.update_action(1) else: player.update_action(0) player.attack_hitbox([enemy]) player.update_animation() player.move(moving_left, moving_right, target = enemy) if player.rect.centerx - camera_x > screen_width * 0.6: camera_x += (player.rect.centerx - camera_x - screen_width * 0.6) # Scroll left elif player.rect.centerx - camera_x < screen_width * 0.4: camera_x += (player.rect.centerx - camera_x - screen_width * 0.4) screen.blit(player.image, (player.rect.x - int(camera_x), player.rect.y)) # Enemy if enemy.alive: enemy_ai(enemy, player) if enemy.attacking: enemy.update_action(3) elif enemy.in_air: enemy.update_action(2) elif enemy.Run == True: enemy.update_action(1) else: enemy.update_action(0) enemy.move(False, False, target = player) enemy.attack_hitbox([player]) enemy.update_animation() else: if not enemy.scored: score += 1 enemy.scored = True for img, rect in world.obstacle_list: screen.blit(img, (rect.x - int(camera_x), rect.y)) # Draw health bars health_bar_player.draw(player.health) # Display score and time font = pygame.font.SysFont(None, 30) elapsed_time = int(time.time() - start_time) screen.blit(font.render(f'Score: {score}', True, BLACK), (10, 40)) screen.blit(font.render(f'Time: {elapsed_time}s', True, BLACK), (10, 70)) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_a: moving_left = True elif event.key == pygame.K_d: moving_right = True elif event.key == pygame.K_w and player.alive: player.jump = True elif event.key == pygame.K_l and player.alive: now = pygame.time.get_ticks() if now - player.last_attack_time > player.attack_cooldown: player.attacking = True player.update_action(3) player.frame_index = 0 player.last_attack_time = now elif event.key == pygame.K_ESCAPE: run = False elif event.type == pygame.KEYUP: if event.key == pygame.K_a: moving_left = False elif event.key == pygame.K_d: moving_right = False pygame.display.update() pygame.quit()