diff --git a/assets/audio/attack/claw.wav b/assets/audio/attack/claw.wav new file mode 100644 index 0000000..1a98889 Binary files /dev/null and b/assets/audio/attack/claw.wav differ diff --git a/assets/audio/attack/fireball.wav b/assets/audio/attack/fireball.wav new file mode 100644 index 0000000..dc2c852 Binary files /dev/null and b/assets/audio/attack/fireball.wav differ diff --git a/assets/audio/attack/slash.wav b/assets/audio/attack/slash.wav new file mode 100644 index 0000000..942aeb0 Binary files /dev/null and b/assets/audio/attack/slash.wav differ diff --git a/assets/audio/death.wav b/assets/audio/death.wav new file mode 100644 index 0000000..ef03ab1 Binary files /dev/null and b/assets/audio/death.wav differ diff --git a/assets/audio/flame.wav b/assets/audio/flame.wav new file mode 100644 index 0000000..97bb0e4 Binary files /dev/null and b/assets/audio/flame.wav differ diff --git a/assets/audio/heal.wav b/assets/audio/heal.wav new file mode 100644 index 0000000..e94bab0 Binary files /dev/null and b/assets/audio/heal.wav differ diff --git a/assets/audio/hit.wav b/assets/audio/hit.wav new file mode 100644 index 0000000..435b317 Binary files /dev/null and b/assets/audio/hit.wav differ diff --git a/assets/audio/main.ogg b/assets/audio/main.ogg new file mode 100644 index 0000000..da5d252 Binary files /dev/null and b/assets/audio/main.ogg differ diff --git a/assets/audio/sword.wav b/assets/audio/sword.wav new file mode 100644 index 0000000..2e962d0 Binary files /dev/null and b/assets/audio/sword.wav differ diff --git a/game/configs/game/monster_config.py b/game/configs/game/monster_config.py index b497e75..a8863fe 100644 --- a/game/configs/game/monster_config.py +++ b/game/configs/game/monster_config.py @@ -1,5 +1,12 @@ +import os + + +script_dir = os.path.dirname(os.path.abspath(__file__)) +asset_path = os.path.join( + script_dir, '../../..', 'assets') + monster_data = { - 'squid': {'health': 100,'exp':100,'damage':20,'attack_type': 'slash', 'attack_sound':'../Graphics/audio/attack/slash.wav', 'speed': 3, 'resistance': 3, 'attack_radius': 80, 'notice_radius': 360}, - 'raccoon': {'health': 300,'exp':250,'damage':40,'attack_type': 'claw', 'attack_sound':'../Graphics/audio/attack/claw.wav','speed': 2, 'resistance': 3, 'attack_radius': 120, 'notice_radius': 400}, - 'spirit': {'health': 100,'exp':110,'damage':8,'attack_type': 'thunder', 'attack_sound':'../Graphics/audio/attack/fireball.wav', 'speed': 4, 'resistance': 3, 'attack_radius': 60, 'notice_radius': 350}, - 'bamboo': {'health': 70,'exp':120,'damage':6,'attack_type': 'leaf_attack', 'attack_sound':'../Graphics/audio/attack/slash.wav', 'speed': 3, 'resistance': 3, 'attack_radius': 50, 'notice_radius': 300}} + 'squid': {'health': 100, 'exp': 100, 'attack': 20, 'attack_type': 'slash', 'attack_sound': f'{asset_path}/audio/attack/slash.wav', 'speed': 3, 'knockback': 20, 'attack_radius': 80, 'notice_radius': 360}, + 'raccoon': {'health': 300, 'exp': 250, 'attack': 40, 'attack_type': 'claw', 'attack_sound': f'{asset_path}/audio/attack/claw.wav', 'speed': 2, 'knockback': 20, 'attack_radius': 120, 'notice_radius': 400}, + 'spirit': {'health': 100, 'exp': 110, 'attack': 8, 'attack_type': 'thunder', 'attack_sound': f'{asset_path}/audio/attack/fireball.wav', 'speed': 4, 'knockback': 20, 'attack_radius': 60, 'notice_radius': 350}, + 'bamboo': {'health': 70, 'exp': 120, 'attack': 6, 'attack_type': 'leaf_attack', 'attack_sound': f'{asset_path}/audio/attack/slash.wav', 'speed': 3, 'knockback': 20, 'attack_radius': 50, 'notice_radius': 300}} diff --git a/game/configs/game/player_config.py b/game/configs/game/player_config.py new file mode 100644 index 0000000..b98eb74 --- /dev/null +++ b/game/configs/game/player_config.py @@ -0,0 +1,23 @@ +stats = { + 'health': 100, + 'energy': 60, + 'attack': 10, + 'magic': 4, + 'speed': 5 +} + +max_stats = { + 'health': 300, + 'energy': 150, + 'attack': 20, + 'magic': 10, + 'speed': 10 +} + +upgrade_costs = { + 'health': 100, + 'energy': 100, + 'attack': 100, + 'magic': 100, + 'speed': 100 +} diff --git a/game/configs/system/window_config.py b/game/configs/system/window_config.py index f403747..5854f0a 100644 --- a/game/configs/system/window_config.py +++ b/game/configs/system/window_config.py @@ -13,37 +13,3 @@ HITBOX_OFFSET = { # general colors WATER_COLOR = '#71ddee' -UI_BG_COLOR = '#222222' -UI_BORDER_COLOR = '#111111' -TEXT_COLOR = '#EEEEEE' - -# ui colors -HEALTH_COLOR = 'red' -ENERGY_COLOR = 'blue' -UI_BORDER_COLOR_ACTIVE = 'gold' - -# Upgrade menu -TEXT_COLOR_SELECTED = '#111111' -BAR_COLOR = '#EEEEEE' -BAR_COLOR_SELECTED = '#111111' -UPGRADE_BG_COLOR_SELECTED = '#EEEEEE' - -# # weapons -# weapon_data = { -# 'sword': {'cooldown': 100, 'damage': 15,'graphic':'../Graphics/graphics/weapons/sword/full.png'}, -# 'lance': {'cooldown': 400, 'damage': 30,'graphic':'../Graphics/graphics/weapons/lance/full.png'}, -# 'axe': {'cooldown': 300, 'damage': 20, 'graphic':'../Graphics/graphics/weapons/axe/full.png'}, -# 'rapier':{'cooldown': 50, 'damage': 8, 'graphic':'../Graphics/graphics/weapons/rapier/full.png'}, -# 'sai':{'cooldown': 80, 'damage': 10, 'graphic':'../Graphics/graphics/weapons/sai/full.png'}} -# -# # magic -# magic_data = { -# 'flame': {'strength': 5,'cost': 20,'graphic':'../Graphics/graphics/particles/flame/fire.png'}, -# 'heal' : {'strength': 20,'cost': 10,'graphic':'../Graphics/graphics/particles/heal/heal.png'}} -# -# # enemy -# monster_data = { -# 'squid': {'health': 100,'exp':100,'damage':20,'attack_type': 'slash', 'attack_sound':'../Graphics/audio/attack/slash.wav', 'speed': 3, 'resistance': 3, 'attack_radius': 80, 'notice_radius': 360}, -# 'raccoon': {'health': 300,'exp':250,'damage':40,'attack_type': 'claw', 'attack_sound':'../Graphics/audio/attack/claw.wav','speed': 2, 'resistance': 3, 'attack_radius': 120, 'notice_radius': 400}, -# 'spirit': {'health': 100,'exp':110,'damage':8,'attack_type': 'thunder', 'attack_sound':'../Graphics/audio/attack/fireball.wav', 'speed': 4, 'resistance': 3, 'attack_radius': 60, 'notice_radius': 350}, -# 'bamboo': {'health': 70,'exp':120,'damage':6,'attack_type': 'leaf_attack', 'attack_sound':'../Graphics/audio/attack/slash.wav', 'speed': 3, 'resistance': 3, 'attack_radius': 50, 'notice_radius': 300}} diff --git a/game/effects/magic_effects.py b/game/effects/magic_effects.py index 63d2803..11ba981 100644 --- a/game/effects/magic_effects.py +++ b/game/effects/magic_effects.py @@ -1,24 +1,26 @@ +import os import pygame from random import randint from configs.system.window_config import TILESIZE -from configs.game.spell_config import * - class MagicPlayer: def __init__(self, animation_player): self.animation_player = animation_player + script_dir = os.path.dirname(os.path.abspath(__file__)) + asset_path = os.path.join( + script_dir, '../..', 'assets') # Sound Setup - # self.sounds = { - # 'heal': pygame.mixer.Sound('../Graphics/audio/heal.wav'), - # 'flame': pygame.mixer.Sound('../Graphics/audio/flame.wav') - # } + self.sounds = { + 'heal': pygame.mixer.Sound(f'{asset_path}/audio/heal.wav'), + 'flame': pygame.mixer.Sound(f'{asset_path}/audio/flame.wav') + } def heal(self, player, strength, cost, groups): if player.stats.energy >= cost: - # self.sounds['heal'].play() + self.sounds['heal'].play() player.stats.health += strength player.stats.energy -= cost if player.stats.health >= player.stats.stats['health']: @@ -31,13 +33,13 @@ class MagicPlayer: def flame(self, player, cost, groups): if player.stats.energy >= cost: player.stats.energy -= cost - # self.sounds['flame'].play() + self.sounds['flame'].play() - if player.status.split('_')[0] == 'right': + if player._input.status.split('_')[0] == 'right': direction = pygame.math.Vector2(1, 0) - elif player.status.split('_')[0] == 'left': + elif player._input.status.split('_')[0] == 'left': direction = pygame.math.Vector2(-1, 0) - elif player.status.split('_')[0] == 'up': + elif player._input.status.split('_')[0] == 'up': direction = pygame.math.Vector2(0, -1) else: direction = pygame.math.Vector2(0, 1) diff --git a/game/effects/weapon_effects.py b/game/effects/weapon_effects.py index 893e011..5f709b1 100644 --- a/game/effects/weapon_effects.py +++ b/game/effects/weapon_effects.py @@ -12,7 +12,7 @@ class Weapon(pygame.sprite.Sprite): script_dir, '../..', 'assets') self.sprite_type = 'weapon' - direction = player.status.split('_')[0] + direction = player._input.status.split('_')[0] # Graphic full_path = f"{asset_path}/graphics/weapons/{player._input.combat.weapon}/{direction}.png" diff --git a/game/entities/components/_input.py b/game/entities/components/_input.py index be387c5..0a89ad6 100644 --- a/game/entities/components/_input.py +++ b/game/entities/components/_input.py @@ -10,21 +10,22 @@ from .combat import CombatHandler class InputHandler: - def __init__(self, sprite_type, status): - self.status = status + # , status): + def __init__(self, sprite_type, animation_player): + self.status = 'down' + self.sprite_type = sprite_type # Setup Movement - self.movement = MovementHandler(sprite_type) + self.movement = MovementHandler(self.sprite_type) self.move_cooldown = 15 self.can_move = True self.move_time = None # Setup Combat - self.combat = CombatHandler() + self.combat = CombatHandler(animation_player) self.attacking = False self.attack_cooldown = 400 self.attack_time = None - self.can_move = True # Setup Special Actions self.can_rotate_weapon = True @@ -37,28 +38,30 @@ class InputHandler: def check_input(self, speed, hitbox, obstacle_sprites, rect, player): if not self.attacking and self.can_move: + keys = pygame.key.get_pressed() - button = randint(0, 7) + + # button = randint(0, 5) self.move_time = pygame.time.get_ticks() # Movement Input - if button == 0: # keys[pygame.K_w]: + if keys[pygame.K_w]: # button == 0: # keys[pygame.K_w]: self.movement.direction.y = -1 self.status = 'up' self.can_move = False - elif button == 1: # keys[pygame.K_s]: + elif keys[pygame.K_s]: # button == 1: # keys[pygame.K_s]: self.movement.direction.y = 1 self.status = 'down' self.can_move = False else: self.movement.direction.y = 0 - if button == 2: # keys[pygame.K_a]: + if keys[pygame.K_a]: # button == 2: # keys[pygame.K_a]: self.movement.direction.x = -1 self.status = 'left' self.can_move = False - elif button == 3: # keys[pygame.K_d]: + elif keys[pygame.K_d]: # keys[pygame.K_d]: self.movement.direction.x = 1 self.status = 'right' self.can_move = False @@ -67,53 +70,56 @@ class InputHandler: self.movement.move(speed, hitbox, obstacle_sprites, rect) - # Combat Input - if button == 4 and not self.attacking: # keys[pygame.K_e] - self.attacking = True - self.attack_time = pygame.time.get_ticks() - self.combat.create_attack_sprite(player) - # self.weapon_attack_sound.play() + if self.sprite_type == 'player': + # Combat Input + if keys[pygame.K_e] and not self.attacking: # keys[pygame.K_e] + self.attacking = True + self.attack_time = pygame.time.get_ticks() + self.combat.create_attack_sprite(player) + self.combat.weapon_attack_sound.play() - # Magic Input - if button == 5: # keys[pygame.K_q]: - self.attacking = True - self.attack_time = pygame.time.get_ticks() + # Magic Input + if keys[pygame.K_q]: # keys[pygame.K_q]: + self.attacking = True + self.attack_time = pygame.time.get_ticks() - self.combat.magic = list(magic_data.keys())[ - self.combat.magic_index] + self.combat.magic = list(magic_data.keys())[ + self.combat.magic_index] - strength = list(magic_data.values())[ - self.combat.magic_index]['strength'] + player.stats.magic + strength = list(magic_data.values())[ + self.combat.magic_index]['strength'] + player.stats.magic - cost = list(magic_data.values())[ - self.combat.magic_index]['cost'] - print(self.combat.magic) - self.combat.create_magic_sprite( - player, self.combat.magic, strength, cost) + cost = list(magic_data.values())[ + self.combat.magic_index]['cost'] + self.combat.create_magic_sprite( + player, self.combat.magic, strength, cost) - # Rotating Weapons - if button == 6 and self.can_rotate_weapon: # keys[pygame.K_LSHIFT] - self.can_rotate_weapon = False - self.weapon_rotation_time = pygame.time.get_ticks() - if self.combat.weapon_index < len(list(weapon_data.keys())) - 1: - self.combat.weapon_index += 1 - else: - self.combat.weapon_index = 0 + # Rotating Weapons + # keys[pygame.K_LSHIFT] + if keys[pygame.K_LSHIFT] and self.can_rotate_weapon: + self.can_rotate_weapon = False + self.weapon_rotation_time = pygame.time.get_ticks() + if self.combat.weapon_index < len(list(weapon_data.keys())) - 1: + self.combat.weapon_index += 1 + else: + self.combat.weapon_index = 0 - self.combat.weapon = list(weapon_data.keys())[ - self.combat.weapon_index] + self.combat.weapon = list(weapon_data.keys())[ + self.combat.weapon_index] - # Swap Spells - if button == 7 and self.can_swap_magic: # keys[pygame.K_LCTRL] : - self.can_swap_magic = False - self.magic_swap_time = pygame.time.get_ticks() - if self.combat.magic_index < len(list(magic_data.keys())) - 1: - self.combat.magic_index += 1 - else: - self.combat.magic_index = 0 + # Swap Spells + # keys[pygame.K_LCTRL] : + if keys[pygame.K_LCTRL] and self.can_swap_magic: + self.can_swap_magic = False + self.magic_swap_time = pygame.time.get_ticks() + if self.combat.magic_index < len(list(magic_data.keys())) - 1: + self.combat.magic_index += 1 + else: + self.combat.magic_index = 0 def cooldowns(self, vulnerable): current_time = pygame.time.get_ticks() + self.vulnerable = vulnerable if self.attacking: if current_time - self.attack_time > self.attack_cooldown + weapon_data[self.combat.weapon]['cooldown']: @@ -130,8 +136,8 @@ class InputHandler: self.can_swap_magic = True if not vulnerable: - if current_time - self.hurt_time >= self.invulnerability_duration: - vulnerable = True + if current_time - self.combat.hurt_time >= self.combat.invulnerability_duration: + self.combat.vulnerable = True if not self.can_move: if current_time - self.move_time >= self.move_cooldown: diff --git a/game/entities/components/animaton.py b/game/entities/components/animaton.py index 0fa0cff..efc0d03 100644 --- a/game/entities/components/animaton.py +++ b/game/entities/components/animaton.py @@ -3,20 +3,25 @@ import pygame from math import sin from utils.resource_loader import import_folder + from configs.system.window_config import HITBOX_OFFSET class AnimationHandler: - def __init__(self): + def __init__(self, sprite_type, name=None): self.frame_index = 0 self.animation_speed = 0.15 - def import_assets(self, sprite_type, position, name=None): + self.sprite_type = sprite_type + self.name = name + + def import_assets(self, position): script_dir = os.path.dirname(os.path.abspath(__file__)) asset_path = os.path.join( script_dir, '../../..', 'assets', 'graphics') - if sprite_type == 'player': + + if self.sprite_type == 'player': character_path = f"{asset_path}/player" @@ -24,7 +29,8 @@ class AnimationHandler: self.image = pygame.image.load( f"{character_path}/down/down_0.png").convert_alpha() self.rect = self.image.get_rect(topleft=position) - self.hitbox = self.rect.inflate(HITBOX_OFFSET[sprite_type]) + self.hitbox = self.rect.inflate(HITBOX_OFFSET[self.sprite_type]) + self.animations = { 'up': [], 'down': [], 'left': [], 'right': [], 'up_idle': [], 'down_idle': [], 'left_idle': [], 'right_idle': [], @@ -34,21 +40,30 @@ class AnimationHandler: full_path = f"{character_path}/{animation}" self.animations[animation] = import_folder(full_path) - elif sprite_type == 'enemy': + elif self.sprite_type == 'enemy': + + self.status = 'idle' + + character_path = f"{asset_path}/monsters/{self.name}" - character_path = f"{asset_path}/monsters/{name}" self.animations = {'idle': [], 'move': [], 'attack': []} - main_path = f"{asset_path}/monsters/{name}" for animation in self.animations.keys(): self.animations[animation] = import_folder( - f"{main_path}/{animation}") + f"{character_path}/{animation}") + + self.image = self.animations[self.status][self.frame_index] + self.rect = self.image.get_rect(topleft=position) + self.hitbox = self.rect.inflate(0, -10) + + def animate(self, status, vulnerable=True, can_attack=False): - def animate(self, status, vulnerable): animation = self.animations[status] - self.frame_index += self.animation_speed if self.frame_index >= len(animation): + if self.sprite_type == 'enemy': + if status == 'attack': + self.can_attack = False self.frame_index = 0 # Set the image @@ -61,6 +76,10 @@ class AnimationHandler: else: self.image.set_alpha(255) + def trigger_death_particles(self, animation_player, position, particle_type, groups): + animation_player.generate_particles( + particle_type, position, groups) + def wave_value(self): value = sin(pygame.time.get_ticks()) if value >= 0: diff --git a/game/entities/components/audio.py b/game/entities/components/audio.py new file mode 100644 index 0000000..f8a41b5 --- /dev/null +++ b/game/entities/components/audio.py @@ -0,0 +1,27 @@ +import os +import pygame + +from configs.game.monster_config import monster_data + + +class AudioHandler: + + def __init__(self, sprite_type, monster_name=None): + script_dir = os.path.dirname(os.path.abspath(__file__)) + asset_path = os.path.join( + script_dir, '../../..', 'assets', 'audio') + + if sprite_type == 'player': + pass + + elif sprite_type == 'enemy': + + # Sounds + self.attack_sound = pygame.mixer.Sound( + monster_data[monster_name]['attack_sound']) + self.death_sound = pygame.mixer.Sound( + f'{asset_path}/death.wav') + self.hit_sound = pygame.mixer.Sound(f'{asset_path}/hit.wav') + self.death_sound.set_volume(0.2) + self.hit_sound.set_volume(0.2) + self.attack_sound.set_volume(0.2) diff --git a/game/entities/components/combat.py b/game/entities/components/combat.py index 0f99653..e7babd6 100644 --- a/game/entities/components/combat.py +++ b/game/entities/components/combat.py @@ -3,7 +3,6 @@ import pygame from effects.weapon_effects import Weapon from effects.magic_effects import MagicPlayer -from effects.particle_effects import AnimationPlayer from configs.game.weapon_config import weapon_data from configs.game.spell_config import magic_data @@ -11,11 +10,12 @@ from configs.game.spell_config import magic_data class CombatHandler: - def __init__(self): + def __init__(self, animation_player): + + self.animation_player = animation_player # Setup Combat - self.animation_player = AnimationPlayer() - self.magic_player = MagicPlayer(self.animation_player) + self.magic_player = MagicPlayer(animation_player) self.current_attack = None # Spell and Weapon Rotation @@ -31,13 +31,13 @@ class CombatHandler: self.invulnerability_duration = 300 # Import Sounds -# script_dir = os.path.dirname(os.path.abspath(__file__)) -# asset_path = os.path.join( -# script_dir, '../../..', 'assets', 'audio') -# -# self.weapon_attack_sound = pygame.mixer.Sound( -# f"{asset_path}/sword.wav") -# self.weapon_attack_sound.set_volume(0.2) + script_dir = os.path.dirname(os.path.abspath(__file__)) + asset_path = os.path.join( + script_dir, '../../..', 'assets', 'audio') + + self.weapon_attack_sound = pygame.mixer.Sound( + f"{asset_path}/sword.wav") + self.weapon_attack_sound.set_volume(0.2) def create_attack_sprite(self, player): self.current_attack = Weapon( @@ -49,7 +49,6 @@ class CombatHandler: self.current_attack = None def create_magic_sprite(self, player, style, strength, cost): - print(style) if style == 'heal': self.magic_player.heal(player, strength, cost, [ player.visible_sprites]) diff --git a/game/entities/components/stats.py b/game/entities/components/stats.py index c383017..edd0b5e 100644 --- a/game/entities/components/stats.py +++ b/game/entities/components/stats.py @@ -1,40 +1,41 @@ +from configs.game.player_config import stats, max_stats, upgrade_costs +from configs.game.monster_config import monster_data + + class StatsHandler: - def __init__(self): - self.stats = { - 'health': 100, - 'energy': 60, - 'attack': 10, - 'magic': 4, - 'speed': 5 - } + def __init__(self, sprite_type, monster_name=None): - self.max_stats = { - 'health': 300, - 'energy': 150, - 'attack': 20, - 'magic': 10, - 'speed': 10 - } + if sprite_type == 'player': - self.upgrade_costs = { - 'health': 100, - 'energy': 100, - 'attack': 100, - 'magic': 100, - 'speed': 100 - } + self.stats = stats - self.health = self.stats['health'] - self.energy = self.stats['energy'] - self.attack = self.stats['attack'] - self.magic = self.stats['magic'] - self.speed = self.stats['speed'] - self.exp = 10000 + self.max_stats = max_stats + + self.upgrade_costs = upgrade_costs + + self.health = self.stats['health'] + self.energy = self.stats['energy'] + self.attack = self.stats['attack'] + self.magic = self.stats['magic'] + self.speed = self.stats['speed'] + self.exp = 10000 + + if sprite_type == 'enemy': + + self.monster_info = monster_data[monster_name] + self.health = self.monster_info['health'] + self.attack = self.monster_info['attack'] + self.attack_type = self.monster_info['attack_type'] + self.attack_radius = self.monster_info['attack_radius'] + self.speed = self.monster_info['speed'] + self.knockback = self.monster_info['knockback'] + self.notice_radius = self.monster_info['notice_radius'] + self.exp = self.monster_info['exp'] def energy_recovery(self): if self.energy < self.stats['energy']: - self.energy += 0.01 * self.stats['magic'] + self.energy += 0.01 * self.magic else: self.energy = self.stats['energy'] diff --git a/game/entities/enemy.py b/game/entities/enemy.py new file mode 100644 index 0000000..8aa0d65 --- /dev/null +++ b/game/entities/enemy.py @@ -0,0 +1,92 @@ +import pygame + +from .components.animaton import AnimationHandler +from .components.stats import StatsHandler +from .components._input import InputHandler + +from effects.particle_effects import AnimationPlayer + +from .components.audio import AudioHandler + + +class Enemy(pygame.sprite.Sprite): + + def __init__(self, name, position, groups, visible_sprites, obstacle_sprites): + super().__init__(groups) + self.sprite_type = "enemy" + self.name = name + self.visible_sprites = visible_sprites + + # Setup Graphics + self.audio = AudioHandler(self.sprite_type, self.name) + self.animation_player = AnimationPlayer() + self.animation = AnimationHandler(self.sprite_type, self.name) + self.animation.import_assets(position) + self.status = self.animation.status + self.image = self.animation.image + self.rect = self.animation.rect + + # Setup Inputs + self._input = InputHandler( + self.sprite_type, self.animation_player) + + # Setup Stats + self.stats = StatsHandler(self.sprite_type, self.name) + self.obstacle_sprites = obstacle_sprites + + self.distance_direction_from_player = None + + self.kills = 0 + + def get_action(self): + player_distance = sorted( + self.distance_direction_from_player, key=lambda x: x[0])[0] + + if player_distance[0] < self.stats.notice_radius and player_distance[0] >= self.stats.attack_radius: + self._input.movement.direction = player_distance[1] + self.animation.status = "move" + self._input.movement.move( + self.stats.speed, self.animation.hitbox, self.obstacle_sprites, self.animation.rect) + elif player_distance[0] <= self.stats.attack_radius: + self.animation.status = 'attack' + else: + self.animation.status = 'idle' + + def add_exp(self, player): + player.stats.exp += self.stats.exp + + def check_death(self, player): + if self.stats.health <= 0: + self.add_exp(player) + self.animation.trigger_death_particles( + self.animation_player, self.rect.center, self.name, self.visible_sprites) + self.audio.death_sound.play() + self.kill() + + def get_damaged(self, player, attack_type): + if self._input.combat.vulnerable: + self.audio.hit_sound.play() + for _, direction, attacking_player in self.distance_direction_from_player: + if attacking_player == player: + self._input.movement.direction = -direction + self._input.movement.move( + self.stats.speed * self.stats.knockback, self.animation.hitbox, self.obstacle_sprites, self.animation.rect) + break + if attack_type == 'weapon': + self.stats.health -= player.get_full_weapon_damage() + else: + self.stats.health -= player.get_full_magic_damage() + self.check_death(player) + self._input.combat.hurt_time = pygame.time.get_ticks() + self._input.combat.vulnerable = False + + def update(self): + + self.get_action() + self.status = self.animation.status + + self.animation.animate(self.status, self._input.combat.vulnerable) + self.image = self.animation.image + self.rect = self.animation.rect + + self._input.cooldowns(self._input.combat.vulnerable) diff --git a/game/entities/player.py b/game/entities/player.py index f660eb0..585d19b 100644 --- a/game/entities/player.py +++ b/game/entities/player.py @@ -1,14 +1,19 @@ import pygame +from random import randint + +from configs.game.weapon_config import weapon_data +from configs.game.spell_config import magic_data -from .components.combat import CombatHandler from .components.stats import StatsHandler from .components._input import InputHandler from .components.animaton import AnimationHandler +from effects.particle_effects import AnimationPlayer + class Player(pygame.sprite.Sprite): - def __init__(self, position, groups, obstacle_sprites, visible_sprites, attack_sprites): + def __init__(self, position, groups, obstacle_sprites, visible_sprites, attack_sprites, attackable_sprites): super().__init__(groups) # Setup Sprites @@ -16,32 +21,33 @@ class Player(pygame.sprite.Sprite): self.visible_sprites = visible_sprites self.attack_sprites = attack_sprites self.obstacle_sprites = obstacle_sprites - self.status = 'down' + self.attackable_sprites = attackable_sprites + + # Setup Graphics + self.animation_player = AnimationPlayer() + self.animation = AnimationHandler(self.sprite_type) + self.animation.import_assets(position) + self.image = self.animation.image + self.rect = self.animation.rect # Setup Inputs self._input = InputHandler( - self.sprite_type, self.status) - - # Setup Graphics - self.animation = AnimationHandler() - self.animation.import_assets(self.sprite_type, position) - self.animate = self.animation.animate - self.image = self.animation.image - self.animate(self.status, self._input.combat.vulnerable) - self.rect = self.animation.rect + self.sprite_type, self.animation_player) # , self.status) # Setup Stats - self.stats = StatsHandler() + self.stats = StatsHandler(self.sprite_type) + + self.distance_direction_from_enemy = None def get_status(self): if self._input.movement.direction.x == 0 and self._input.movement.direction.y == 0: - if not 'idle' in self.status and not 'attack' in self.status: + if 'idle' not in self.status and 'attack' not in self.status: self.status += '_idle' if self._input.attacking: self._input.movement.direction.x = 0 self._input.movement.direction.y = 0 - if not 'attack' in self.status: + if 'attack' not in self.status: if 'idle' in self.status: self.status = self.status.replace('idle', 'attack') else: @@ -50,10 +56,38 @@ class Player(pygame.sprite.Sprite): if 'attack' in self.status: self.status = self.status.replace('_attack', '') + def attack_logic(self): + if self.attack_sprites: + for attack_sprite in self.attack_sprites: + collision_sprites = pygame.sprite.spritecollide( + attack_sprite, self.attackable_sprites, False) + if collision_sprites: + for target_sprite in collision_sprites: + if target_sprite.sprite_type == 'grass': + pos = target_sprite.rect.center + offset = pygame.math.Vector2(0, 75) + for leaf in range(randint(3, 6)): + self.animation_player.create_grass_particles( + position=pos - offset, groups=[self.visible_sprites]) + target_sprite.kill() + else: + target_sprite.get_damaged( + self, attack_sprite.sprite_type) + + def get_full_weapon_damage(self): + base_damage = self.stats.attack + weapon_damage = weapon_data[self._input.combat.weapon]['damage'] + return (base_damage + weapon_damage) + + def get_full_magic_damage(self): + base_damage = self.stats.magic + spell_damage = magic_data[self._input.combat.magic]['strength'] + return (base_damage + spell_damage) + def update(self): # Refresh objects based on input self._input.check_input( - self.stats.stats['speed'], self.animation.hitbox, self.obstacle_sprites, self.animation.rect, self) + self.stats.speed, self.animation.hitbox, self.obstacle_sprites, self.animation.rect, self) self.status = self._input.status # Animate diff --git a/game/interface/upgrade.py b/game/interface/upgrade.py index 9cfb46f..d27a857 100644 --- a/game/interface/upgrade.py +++ b/game/interface/upgrade.py @@ -1,6 +1,6 @@ import pygame -from .ui_settings import * +from .ui_settings import UI_FONT, UI_FONT_SIZE, TEXT_COLOR, TEXT_COLOR_SELECTED, UPGRADE_BG_COLOR_SELECTED, UI_BORDER_COLOR, UI_BG_COLOR, BAR_COLOR_SELECTED, BAR_COLOR class Upgrade: diff --git a/game/level/level.py b/game/level/level.py index be975e7..cb1ccd6 100644 --- a/game/level/level.py +++ b/game/level/level.py @@ -1,11 +1,8 @@ import os import pygame -from random import choice, randint +from random import choice -from configs.game.spell_config import magic_data -from configs.game.weapon_config import weapon_data -from configs.game.monster_config import monster_data from configs.system.window_config import TILESIZE from utils.debug import debug @@ -14,12 +11,9 @@ from utils.resource_loader import import_csv_layout, import_folder from interface.ui import UI from interface.upgrade import Upgrade -from effects.magic_effects import MagicPlayer -from effects.particle_effects import AnimationPlayer -from effects.weapon_effects import Weapon - from entities.observer import Observer from entities.player import Player +from entities.enemy import Enemy from .terrain import Tile from .camera import Camera @@ -48,6 +42,9 @@ class Level: self.ui = UI() self.upgrade = Upgrade(self.player) + self.get_players_enemies() + self.get_distance_direction() + def create_map(self): script_dir = os.path.dirname(os.path.abspath(__file__)) asset_path = os.path.join( @@ -92,29 +89,86 @@ class Level: elif col == '394': # Player Generation self.player = Player( - (x, y), [self.visible_sprites], self.obstacle_sprites, self.visible_sprites, self.attack_sprites) + (x, y), [self.visible_sprites], self.obstacle_sprites, self.visible_sprites, self.attack_sprites, self.attackable_sprites) else: - pass - # monster generation + # Monster Generation + if col == '390': + monster_name = 'bamboo' + elif col == '391': + monster_name = 'spirit' + elif col == '392': + monster_name = 'raccoon' + else: + monster_name = 'squid' + + Enemy(monster_name, (x, y), [ + self.visible_sprites, self.attackable_sprites], self.visible_sprites, self.obstacle_sprites) + + def get_players_enemies(self): + self.player_sprites = [sprite for sprite in self.visible_sprites.sprites( + ) if hasattr(sprite, 'sprite_type') and sprite.sprite_type in ('player')] + + self.enemy_sprites = [sprite for sprite in self.visible_sprites.sprites( + ) if hasattr(sprite, 'sprite_type') and sprite.sprite_type in ('enemy')] + + def get_distance_direction(self): + for player in self.player_sprites: + player.distance_direction_from_enemy = [] + + for enemy in self.enemy_sprites: + enemy.distance_direction_from_player = [] + + for player in self.player_sprites: + player_vector = pygame.math.Vector2(player.rect.center) + for enemy in self.enemy_sprites: + enemy_vector = pygame.math.Vector2(enemy.rect.center) + distance = (player_vector - enemy_vector).magnitude() + + if distance > 0: + direction = (player_vector - enemy_vector).normalize() + else: + direction = pygame.math.Vector2() + + enemy.distance_direction_from_player.append( + (distance, direction, player)) + player.distance_direction_from_enemy.append( + (distance, -direction, enemy)) + + def apply_damage_to_player(self): + for enemy in self.enemy_sprites: + for distance, _, player in enemy.distance_direction_from_player: + if distance < enemy.stats.attack_radius and player._input.combat.vulnerable: + player.stats.health -= enemy.stats.attack + player._input.combat.vulnerable = False + player._input.combat.hurt_time = pygame.time.get_ticks() def toggle_menu(self): self.game_paused = not self.game_paused - def run(self): + def run(self, who='observer'): # Draw the game - self.visible_sprites.custom_draw(self.player) - self.ui.display(self.player) - debug(self.player.status) + if who == 'observer': + self.visible_sprites.custom_draw(self.observer) + self.ui.display(self.observer) + elif who == 'player': + self.visible_sprites.custom_draw(self.player) + self.ui.display(self.player) + debug('v0.5') if not self.game_paused: # Update the game - # self.player.distance_direction_to_player = self.get_state() + for player in self.player_sprites: + player.attack_logic() + + self.get_players_enemies() + self.get_distance_direction() + self.apply_damage_to_player() self.visible_sprites.update() + # self.visible_sprites.enemy_update(self.player) # self.player_attack_logic() else: - if self.visible_sprites.sprite_type == 'player': - self.upgrade.display() + self.upgrade.display() if self.player.stats.health <= 0: self.__init__() diff --git a/game/main.py b/game/main.py index 83fd427..3b5a43e 100644 --- a/game/main.py +++ b/game/main.py @@ -2,10 +2,12 @@ import pygame import sys from configs.system.window_config import WIDTH, HEIGHT, WATER_COLOR, FPS -from utils.debug import debug from level.level import Level +import os +import psutil + class Game: @@ -13,16 +15,17 @@ class Game: pygame.init() - self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) + self.screen = pygame.display.set_mode( + (WIDTH, HEIGHT)) pygame.display.set_caption('Pneuma') self.clock = pygame.time.Clock() self.level = Level() - # # Sound - # main_sound = pygame.mixer.Sound('../assets/audio/main.ogg') - # main_sound.set_volume(0.4) - # main_sound.play(loops=-1) + # Sound + main_sound = pygame.mixer.Sound('../assets/audio/main.ogg') + main_sound.set_volume(0.4) + main_sound.play(loops=-1) def run(self): @@ -35,7 +38,7 @@ class Game: self.level.toggle_menu() self.screen.fill(WATER_COLOR) - self.level.run() + self.level.run('player') pygame.display.update() self.clock.tick(FPS) diff --git a/old/Game/main.py b/old/Game/main.py index 954c2fb..293e4f4 100644 --- a/old/Game/main.py +++ b/old/Game/main.py @@ -1,5 +1,6 @@ import pygame import sys +import os from utils.settings import * from utils.debug import debug @@ -7,13 +8,15 @@ from utils.debug import debug from objects.level import Level + class Game: def __init__(self): pygame.init() - self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) + self.screen = pygame.display.set_mode( + (WIDTH, HEIGHT)) pygame.display.set_caption('Pneuma') self.clock = pygame.time.Clock() @@ -43,6 +46,5 @@ class Game: if __name__ == '__main__': game = Game() - figure_file = 'rl/plots/pneuma.png' while True: game.run()